mirror of
https://github.com/Aider-AI/aider.git
synced 2025-05-28 16:25:00 +00:00
Merge 02accb9790
into 3caab85931
This commit is contained in:
commit
29434aefb2
5 changed files with 118 additions and 8 deletions
|
@ -1764,12 +1764,26 @@ class Coder:
|
|||
added_fnames = []
|
||||
group = ConfirmGroup(new_mentions)
|
||||
for rel_fname in sorted(new_mentions):
|
||||
if self.io.confirm_ask(
|
||||
"Add file to the chat?", subject=rel_fname, group=group, allow_never=True
|
||||
):
|
||||
response = self.io.confirm_ask(
|
||||
"Add file to the chat?",
|
||||
subject=rel_fname,
|
||||
group=group,
|
||||
allow_never=True,
|
||||
show_readonly=True,
|
||||
return_string=True,
|
||||
)
|
||||
|
||||
if response == "yes":
|
||||
self.add_rel_fname(rel_fname)
|
||||
added_fnames.append(rel_fname)
|
||||
elif response == "readonly":
|
||||
abs_fname = self.abs_root_path(rel_fname)
|
||||
self.abs_read_only_fnames.add(abs_fname)
|
||||
added_fnames.append(f"{rel_fname} (read-only)")
|
||||
elif response == "never":
|
||||
self.ignore_mentions.add(rel_fname)
|
||||
else:
|
||||
# "no" response
|
||||
self.ignore_mentions.add(rel_fname)
|
||||
|
||||
if added_fnames:
|
||||
|
|
20
aider/io.py
20
aider/io.py
|
@ -801,6 +801,8 @@ class InputOutput:
|
|||
explicit_yes_required=False,
|
||||
group=None,
|
||||
allow_never=False,
|
||||
show_readonly=False,
|
||||
return_string=False,
|
||||
):
|
||||
self.num_user_asks += 1
|
||||
|
||||
|
@ -810,7 +812,7 @@ class InputOutput:
|
|||
question_id = (question, subject)
|
||||
|
||||
if question_id in self.never_prompts:
|
||||
return False
|
||||
return False if not return_string else "no"
|
||||
|
||||
if group and not group.show_group:
|
||||
group = None
|
||||
|
@ -819,6 +821,12 @@ class InputOutput:
|
|||
|
||||
valid_responses = ["yes", "no", "skip", "all"]
|
||||
options = " (Y)es/(N)o"
|
||||
|
||||
# Only add "Read-only" option when show_readonly is True
|
||||
if show_readonly:
|
||||
valid_responses.append("read")
|
||||
options += "/(R)ead-only"
|
||||
|
||||
if group:
|
||||
if not explicit_yes_required:
|
||||
options += "/(A)ll"
|
||||
|
@ -892,7 +900,13 @@ class InputOutput:
|
|||
self.never_prompts.add(question_id)
|
||||
hist = f"{question.strip()} {res}"
|
||||
self.append_chat_history(hist, linebreak=True, blockquote=True)
|
||||
return False
|
||||
return "never" if return_string else False
|
||||
|
||||
# Handle read-only option when show_readonly is True
|
||||
if show_readonly and res == "r":
|
||||
hist = f"{question.strip()} {res}"
|
||||
self.append_chat_history(hist, linebreak=True, blockquote=True)
|
||||
return "readonly" if return_string else True
|
||||
|
||||
if explicit_yes_required:
|
||||
is_yes = res == "y"
|
||||
|
@ -911,7 +925,7 @@ class InputOutput:
|
|||
hist = f"{question.strip()} {res}"
|
||||
self.append_chat_history(hist, linebreak=True, blockquote=True)
|
||||
|
||||
return is_yes
|
||||
return "yes" if return_string and is_yes else "no" if return_string else is_yes
|
||||
|
||||
@restore_multiline
|
||||
def prompt_ask(self, question, default="", subject=None):
|
||||
|
|
|
@ -135,6 +135,9 @@ class TestCoder(unittest.TestCase):
|
|||
repo.git.add(str(fname2))
|
||||
repo.git.commit("-m", "new")
|
||||
|
||||
# Make confirm_ask return "yes" by default
|
||||
mock_io.confirm_ask.return_value = "yes"
|
||||
|
||||
# Initialize the Coder object with the mocked IO and mocked repo
|
||||
coder = Coder.create(self.GPT35, None, mock_io)
|
||||
|
||||
|
@ -238,8 +241,8 @@ class TestCoder(unittest.TestCase):
|
|||
# Mock get_file_mentions to return two file names
|
||||
coder.get_file_mentions = MagicMock(return_value=set(["file1.txt", "file2.txt"]))
|
||||
|
||||
# Mock confirm_ask to return False for the first call and True for the second
|
||||
io.confirm_ask = MagicMock(side_effect=[False, True, True])
|
||||
# Mock confirm_ask to return "no" for the first call and "yes" for the second
|
||||
io.confirm_ask = MagicMock(side_effect=["no", "yes", "yes"])
|
||||
|
||||
# First call to check_for_file_mentions
|
||||
coder.check_for_file_mentions("Please check file1.txt for the info")
|
||||
|
|
|
@ -1587,6 +1587,40 @@ class TestCommands(TestCase):
|
|||
# Check if all files were removed from abs_read_only_fnames
|
||||
self.assertEqual(len(coder.abs_read_only_fnames), 0)
|
||||
|
||||
def test_check_for_file_mentions_with_readonly_option(self):
|
||||
with GitTemporaryDirectory():
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
|
||||
# Create a test file
|
||||
test_file = Path("test_file.py")
|
||||
test_file.write_text("print('Hello, world!')")
|
||||
|
||||
# Mock get_file_mentions to return our test file and confirm_ask to return "readonly"
|
||||
with (
|
||||
mock.patch.object(coder, "get_file_mentions", return_value={"test_file.py"}),
|
||||
mock.patch.object(io, "confirm_ask", return_value="readonly"),
|
||||
):
|
||||
# Call check_for_file_mentions with content mentioning the file
|
||||
result = coder.check_for_file_mentions("Let's look at test_file.py")
|
||||
|
||||
# Verify confirm_ask was called with show_readonly=True
|
||||
io.confirm_ask.assert_called_with(
|
||||
"Add file to the chat?",
|
||||
subject="test_file.py",
|
||||
group=mock.ANY,
|
||||
allow_never=True,
|
||||
show_readonly=True,
|
||||
return_string=True,
|
||||
)
|
||||
|
||||
# Verify the file was added as read-only
|
||||
self.assertEqual(len(coder.abs_fnames), 0)
|
||||
self.assertEqual(len(coder.abs_read_only_fnames), 1)
|
||||
|
||||
# Verify the return message includes "(read-only)"
|
||||
self.assertIn("(read-only)", result)
|
||||
|
||||
def test_cmd_read_only_with_tilde_path(self):
|
||||
with GitTemporaryDirectory():
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
|
|
|
@ -247,6 +247,51 @@ class TestInputOutput(unittest.TestCase):
|
|||
self.assertNotIn("(A)ll", mock_input.call_args[0][0])
|
||||
mock_input.reset_mock()
|
||||
|
||||
@patch("builtins.input")
|
||||
def test_confirm_ask_read_only_option(self, mock_input):
|
||||
io = InputOutput(pretty=False, fancy_input=False)
|
||||
|
||||
# Test case 1: User selects 'Read-only' option
|
||||
mock_input.return_value = "r"
|
||||
result = io.confirm_ask(
|
||||
"Add file to the chat?", subject="test_file.py", show_readonly=True, return_string=True
|
||||
)
|
||||
self.assertEqual(result, "readonly")
|
||||
mock_input.assert_called_once()
|
||||
mock_input.reset_mock()
|
||||
|
||||
# Test case 2: show_readonly=False should not offer read-only option
|
||||
mock_input.side_effect = ["r", "n"] # First 'r' will be invalid, then 'n' to exit the loop
|
||||
result = io.confirm_ask(
|
||||
"Add file to the chat?", subject="test_file.py", show_readonly=False, return_string=True
|
||||
)
|
||||
self.assertEqual(result, "no")
|
||||
self.assertEqual(mock_input.call_count, 2)
|
||||
mock_input.reset_mock()
|
||||
|
||||
# Test case 3: Return boolean with show_readonly=True and 'r' response
|
||||
mock_input.side_effect = None # Clear any side_effect
|
||||
mock_input.return_value = "r"
|
||||
result = io.confirm_ask(
|
||||
"Add file to the chat?", subject="test_file.py", show_readonly=True, return_string=False
|
||||
)
|
||||
self.assertTrue(result) # Should return True for backward compatibility
|
||||
mock_input.assert_called_once()
|
||||
mock_input.reset_mock()
|
||||
|
||||
# Test case 4: Verify prompt includes read-only option
|
||||
mock_input.return_value = "y"
|
||||
io.confirm_ask("Add file to the chat?", subject="test_file.py", show_readonly=True)
|
||||
call_args = mock_input.call_args[0][0]
|
||||
self.assertIn("(R)ead-only", call_args)
|
||||
mock_input.reset_mock()
|
||||
|
||||
# Test case 5: Verify prompt doesn't include read-only option when show_readonly=False
|
||||
mock_input.return_value = "y"
|
||||
io.confirm_ask("Add file to the chat?", subject="test_file.py", show_readonly=False)
|
||||
call_args = mock_input.call_args[0][0]
|
||||
self.assertNotIn("(R)ead-only", call_args)
|
||||
|
||||
@patch("builtins.input")
|
||||
def test_confirm_ask_yes_no(self, mock_input):
|
||||
io = InputOutput(pretty=False, fancy_input=False)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue