diff --git a/aider/commands.py b/aider/commands.py index 43e73e87b..396c960b0 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -221,15 +221,18 @@ class Commands: yield Completion(fname, start_position=-len(partial)) def glob_filtered_to_repo(self, pattern): - matched_files = Path(self.coder.root).glob(pattern) - matched_files = [fn.relative_to(self.coder.root) for fn in matched_files] + raw_matched_files = Path(self.coder.root).glob(pattern) + matched_files = [] + for fn in raw_matched_files: + matched_files += expand_subdir(fn.relative_to(self.coder.root)) # if repo, filter against it if self.coder.repo: git_files = self.coder.get_tracked_files() matched_files = [fn for fn in matched_files if str(fn) in git_files] - return list(map(str, matched_files)) + res = list(map(str, matched_files)) + return res def cmd_add(self, args): "Add matching files to the chat session using glob patterns" @@ -238,6 +241,7 @@ class Commands: git_added = [] git_files = self.coder.get_tracked_files() + all_matched_files = set() for word in args.split(): matched_files = self.glob_filtered_to_repo(word) @@ -253,23 +257,25 @@ class Commands: (Path(self.coder.root) / word).touch() matched_files = [word] - for matched_file in matched_files: - abs_file_path = self.coder.abs_root_path(matched_file) + all_matched_files.update(matched_files) - if self.coder.repo and matched_file not in git_files: - self.coder.repo.git.add(abs_file_path) - git_added.append(matched_file) + for matched_file in all_matched_files: + abs_file_path = self.coder.abs_root_path(matched_file) - if abs_file_path not in self.coder.abs_fnames: - content = self.io.read_text(abs_file_path) - if content is not None: - self.coder.abs_fnames.add(abs_file_path) - self.io.tool_output(f"Added {matched_file} to the chat") - added_fnames.append(matched_file) - else: - self.io.tool_error(f"Unable to read {matched_file}") + if self.coder.repo and matched_file not in git_files: + self.coder.repo.git.add(abs_file_path) + git_added.append(matched_file) + + if abs_file_path in self.coder.abs_fnames: + self.io.tool_error(f"{matched_file} is already in the chat") + else: + content = self.io.read_text(abs_file_path) + if content is None: + self.io.tool_error(f"Unable to read {matched_file}") else: - self.io.tool_error(f"{matched_file} is already in the chat") + self.coder.abs_fnames.add(abs_file_path) + self.io.tool_output(f"Added {matched_file} to the chat") + added_fnames.append(matched_file) if self.coder.repo and git_added: git_added = " ".join(git_added) @@ -380,3 +386,14 @@ class Commands: self.io.tool_output(f"{cmd} {description}") else: self.io.tool_output(f"{cmd} No description available.") + + +def expand_subdir(file_path): + file_path = Path(file_path) + if file_path.is_file(): + yield file_path + return + + for file in file_path.rglob("*"): + if file.is_file(): + yield str(file) diff --git a/tests/test_commands.py b/tests/test_commands.py index c43826419..3590c65e5 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -79,6 +79,39 @@ class TestCommands(TestCase): # Check if no files have been added to the chat session self.assertEqual(len(coder.abs_fnames), 0) + def test_cmd_add_drop_directory(self): + # Initialize the Commands and InputOutput objects + io = InputOutput(pretty=False, yes=True) + from aider.coders import Coder + + coder = Coder.create(models.GPT35, None, io, openai_api_key="deadbeef") + commands = Commands(io, coder) + + # Create a directory and add files to it + os.mkdir("test_dir") + os.mkdir("test_dir/another_dir") + with open("test_dir/test_file1.txt", "w") as f: + f.write("Test file 1") + with open("test_dir/test_file2.txt", "w") as f: + f.write("Test file 2") + with open("test_dir/another_dir/test_file.txt", "w") as f: + f.write("Test file 3") + + # Call the cmd_add method with a directory + commands.cmd_add("test_dir test_dir/test_file2.txt") + + # Check if the files have been added to the chat session + self.assertIn(str(Path("test_dir/test_file1.txt").resolve()), coder.abs_fnames) + self.assertIn(str(Path("test_dir/test_file2.txt").resolve()), coder.abs_fnames) + self.assertIn(str(Path("test_dir/another_dir/test_file.txt").resolve()), coder.abs_fnames) + + commands.cmd_drop("test_dir/another_dir") + self.assertIn(str(Path("test_dir/test_file1.txt").resolve()), coder.abs_fnames) + self.assertIn(str(Path("test_dir/test_file2.txt").resolve()), coder.abs_fnames) + self.assertNotIn( + str(Path("test_dir/another_dir/test_file.txt").resolve()), coder.abs_fnames + ) + def test_cmd_drop_with_glob_patterns(self): # Initialize the Commands and InputOutput objects io = InputOutput(pretty=False, yes=True)