diff --git a/aider/args.py b/aider/args.py index a528c78d0..a6d5bbe6a 100644 --- a/aider/args.py +++ b/aider/args.py @@ -313,7 +313,7 @@ def get_parser(default_config_files, git_root): group.add_argument( "--lint", action="store_true", - help="Run the linter on all dirty files, fix problems and commit", + help="Lint and fix provided files, or dirty files if none provided", default=False, ) group.add_argument( diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 1fdf3eacf..a61c8e54d 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -84,10 +84,19 @@ class Coder: ) if not main_model: - main_model = models.Model(models.DEFAULT_MODEL_NAME) + if from_coder: + main_model = from_coder.main_model + else: + main_model = models.Model(models.DEFAULT_MODEL_NAME) if edit_format is None: - edit_format = main_model.edit_format + if from_coder: + edit_format = from_coder.edit_format + else: + edit_format = main_model.edit_format + + if not io and from_coder: + io = from_coder.io if from_coder: use_kwargs = dict(from_coder.original_kwargs) # copy orig kwargs @@ -127,6 +136,9 @@ class Coder: return res + def clone(self, **kwargs): + return Coder.create(from_coder=self, **kwargs) + def get_announcements(self): lines = [] lines.append(f"Aider v{__version__}") diff --git a/aider/commands.py b/aider/commands.py index ba72dc633..7641bebf5 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -153,40 +153,51 @@ class Commands: commit_message = args.strip() self.coder.repo.commit(message=commit_message) - def cmd_lint(self, args): - "Commit, run the linter on all dirty files, fix problems and commit again" + def cmd_lint(self, args="", fnames=None): + "Lint and fix provided files or in-chat files if none provided" if not self.coder.repo: self.io.tool_error("No git repository found.") return - if not self.coder.repo.is_dirty(): - self.io.tool_error("No more changes to commit.") - return + if not fnames: + fnames = self.coder.get_inchat_relative_files() - fnames = self.coder.repo.get_dirty_files() - linted = False + if not fnames: + self.io.tool_error("No dirty files to lint.") + return + + lint_coder = None for fname in fnames: try: - errors = self.coder.linter.lint(fname, cmd=args) - linted = True + errors = self.coder.linter.lint(fname) except FileNotFoundError as err: self.io.tool_error(f"Unable to lint {fname}") self.io.tool_error(str(err)) continue - if errors: - # Commit everything before we start fixing lint errors - if self.coder.repo.is_dirty(): - self.cmd_commit("") + if not errors: + continue - self.io.tool_error(errors) + # Commit everything before we start fixing lint errors + if self.coder.repo.is_dirty(): + self.cmd_commit("") - abs_file_path = self.coder.abs_root_path(fname) - self.coder.abs_fnames.add(abs_file_path) - self.coder.run(errors) + self.io.tool_error(errors) - if linted and self.coder.repo.is_dirty(): + if not lint_coder: + lint_coder = self.coder.clone( + # Clear the chat history, fnames + cur_messages=[], + done_messages=[], + fnames=None, + ) + + lint_coder.add_rel_fname(fname) + lint_coder.run(errors) + lint_coder.abs_fnames = set() + + if lint_coder and self.coder.repo.is_dirty(): self.cmd_commit("") def cmd_clear(self, args): diff --git a/aider/io.py b/aider/io.py index 3d0f79f2c..92462f1dd 100644 --- a/aider/io.py +++ b/aider/io.py @@ -325,7 +325,7 @@ class InputOutput: return res - def tool_error(self, message, strip=True): + def tool_error(self, message="", strip=True): self.num_error_outputs += 1 if message.strip(): diff --git a/aider/linter.py b/aider/linter.py index 4ca1ee17d..f2afe2d05 100644 --- a/aider/linter.py +++ b/aider/linter.py @@ -1,4 +1,5 @@ import os +import re import subprocess import sys import traceback @@ -13,7 +14,6 @@ from tree_sitter_languages import get_parser # noqa: E402 warnings.simplefilter("ignore", category=FutureWarning) - class Linter: def __init__(self, encoding="utf-8", root=None): self.encoding = encoding @@ -22,9 +22,14 @@ class Linter: self.languages = dict( python=self.py_lint, ) + self.all_lint_cmd = None def set_linter(self, lang, cmd): - self.languages[lang] = cmd + if lang: + self.languages[lang] = cmd + return + + self.all_lint_cmd = cmd def get_rel_fname(self, fname): if self.root: @@ -66,7 +71,10 @@ class Linter: lang = filename_to_lang(fname) if not lang: return - cmd = self.languages.get(lang) + if self.all_lint_cmd: + cmd = self.all_lint_cmd + else: + cmd = self.languages.get(lang) if callable(cmd): linkres = cmd(fname, rel_fname, code) @@ -86,7 +94,6 @@ class Linter: return res def py_lint(self, fname, rel_fname, code): - result = "" basic_res = basic_lint(rel_fname, code) compile_res = lint_python_compile(fname, code) @@ -198,12 +205,9 @@ def traverse_tree(node): return errors -import re - - def find_filenames_and_linenums(text, fnames): """ - Search text for all occurrences of :\d+ and make a list of them + Search text for all occurrences of :\\d+ and make a list of them where is one of the filenames in the list `fnames`. """ pattern = re.compile(r"(\b(?:" + "|".join(re.escape(fname) for fname in fnames) + r"):\d+\b)") diff --git a/aider/main.py b/aider/main.py index ad4c55c3d..9cf0dda10 100644 --- a/aider/main.py +++ b/aider/main.py @@ -1,5 +1,6 @@ import configparser import os +import re import sys from pathlib import Path @@ -181,14 +182,18 @@ def parse_lint_cmds(lint_cmds, io): err = False res = dict() for lint_cmd in lint_cmds: - pieces = lint_cmd.split(":") - lang = pieces[0] - cmd = lint_cmd[len(lang) + 1 :] + if re.match(r"^[a-z]+:.*", lint_cmd): + pieces = lint_cmd.split(":") + lang = pieces[0] + cmd = lint_cmd[len(lang) + 1 :] + lang = lang.strip() + else: + lang = None + cmd = lint_cmd - lang = lang.strip() cmd = cmd.strip() - if lang and cmd: + if cmd: res[lang] = cmd else: io.tool_error(f'Unable to parse --lint-cmd "{lint_cmd}"') @@ -383,11 +388,11 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F return if args.commit: - coder.commands.cmd_commit("") + coder.commands.cmd_commit() return if args.lint: - coder.commands.cmd_lint("") + coder.commands.cmd_lint(fnames=fnames) return if args.test: diff --git a/dev-requirements.txt b/dev-requirements.txt index fcbb97d26..1b97b4d8d 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -105,7 +105,7 @@ pyproject-hooks==1.1.0 # via # build # pip-tools -pytest==8.2.0 +pytest==8.2.1 # via -r dev-requirements.in python-dateutil==2.9.0.post0 # via @@ -115,7 +115,7 @@ pytz==2024.1 # via pandas pyyaml==6.0.1 # via pre-commit -requests==2.31.0 +requests==2.32.0 # via sphinx rich==13.7.1 # via typer diff --git a/requirements.txt b/requirements.txt index 90a340ccb..b5d0731f4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -96,7 +96,7 @@ googleapis-common-protos==1.63.0 # grpcio-status greenlet==3.0.3 # via playwright -grep-ast==0.3.1 +grep-ast==0.3.2 # via -r requirements.in grpcio==1.63.0 # via