mirror of
https://github.com/Aider-AI/aider.git
synced 2025-06-13 08:05:01 +00:00
Merge branch 'main' into qwen3
This commit is contained in:
commit
024c3ed46e
2 changed files with 137 additions and 6 deletions
|
@ -1144,18 +1144,19 @@ class InputOutput:
|
||||||
ro_paths = []
|
ro_paths = []
|
||||||
for rel_path in read_only_files:
|
for rel_path in read_only_files:
|
||||||
abs_path = os.path.abspath(os.path.join(self.root, rel_path))
|
abs_path = os.path.abspath(os.path.join(self.root, rel_path))
|
||||||
ro_paths.append(abs_path if len(abs_path) < len(rel_path) else rel_path)
|
ro_paths.append(Text(abs_path if len(abs_path) < len(rel_path) else rel_path))
|
||||||
|
|
||||||
files_with_label = ["Readonly:"] + ro_paths
|
files_with_label = [Text("Readonly:")] + ro_paths
|
||||||
read_only_output = StringIO()
|
read_only_output = StringIO()
|
||||||
Console(file=read_only_output, force_terminal=False).print(Columns(files_with_label))
|
Console(file=read_only_output, force_terminal=False).print(Columns(files_with_label))
|
||||||
read_only_lines = read_only_output.getvalue().splitlines()
|
read_only_lines = read_only_output.getvalue().splitlines()
|
||||||
console.print(Columns(files_with_label))
|
console.print(Columns(files_with_label))
|
||||||
|
|
||||||
if editable_files:
|
if editable_files:
|
||||||
files_with_label = editable_files
|
text_editable_files = [Text(f) for f in editable_files]
|
||||||
|
files_with_label = text_editable_files
|
||||||
if read_only_files:
|
if read_only_files:
|
||||||
files_with_label = ["Editable:"] + editable_files
|
files_with_label = [Text("Editable:")] + text_editable_files
|
||||||
editable_output = StringIO()
|
editable_output = StringIO()
|
||||||
Console(file=editable_output, force_terminal=False).print(Columns(files_with_label))
|
Console(file=editable_output, force_terminal=False).print(Columns(files_with_label))
|
||||||
editable_lines = editable_output.getvalue().splitlines()
|
editable_lines = editable_output.getvalue().splitlines()
|
||||||
|
|
|
@ -5,6 +5,7 @@ from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from prompt_toolkit.completion import CompleteEvent
|
from prompt_toolkit.completion import CompleteEvent
|
||||||
from prompt_toolkit.document import Document
|
from prompt_toolkit.document import Document
|
||||||
|
from rich.text import Text
|
||||||
|
|
||||||
from aider.dump import dump # noqa: F401
|
from aider.dump import dump # noqa: F401
|
||||||
from aider.io import AutoCompleter, ConfirmGroup, InputOutput
|
from aider.io import AutoCompleter, ConfirmGroup, InputOutput
|
||||||
|
@ -451,8 +452,6 @@ class TestInputOutputMultilineMode(unittest.TestCase):
|
||||||
"""Test that tool_output correctly handles hex colors without # prefix"""
|
"""Test that tool_output correctly handles hex colors without # prefix"""
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from rich.text import Text
|
|
||||||
|
|
||||||
# Create IO with hex color without # for tool_output_color
|
# Create IO with hex color without # for tool_output_color
|
||||||
io = InputOutput(tool_output_color="FFA500", pretty=True)
|
io = InputOutput(tool_output_color="FFA500", pretty=True)
|
||||||
|
|
||||||
|
@ -476,5 +475,136 @@ class TestInputOutputMultilineMode(unittest.TestCase):
|
||||||
mock_print.assert_called_once()
|
mock_print.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
@patch("aider.io.is_dumb_terminal", return_value=False)
|
||||||
|
@patch.dict(os.environ, {"NO_COLOR": ""})
|
||||||
|
class TestInputOutputFormatFiles(unittest.TestCase):
|
||||||
|
def test_format_files_for_input_pretty_false(self, mock_is_dumb_terminal):
|
||||||
|
io = InputOutput(pretty=False, fancy_input=False)
|
||||||
|
rel_fnames = ["file1.txt", "file[markup].txt", "ro_file.txt"]
|
||||||
|
rel_read_only_fnames = ["ro_file.txt"]
|
||||||
|
|
||||||
|
expected_output = "file1.txt\nfile[markup].txt\nro_file.txt (read only)\n"
|
||||||
|
# Sort the expected lines because the order of editable vs read-only might vary
|
||||||
|
# depending on internal sorting, but the content should be the same.
|
||||||
|
# The method sorts editable_files and read_only_files separately.
|
||||||
|
# The final output joins sorted(read_only_files) + sorted(editable_files)
|
||||||
|
|
||||||
|
# Based on current implementation:
|
||||||
|
# read_only_files = ["ro_file.txt (read only)"]
|
||||||
|
# editable_files = ["file1.txt", "file[markup].txt"]
|
||||||
|
# output = "\n".join(read_only_files + editable_files) + "\n"
|
||||||
|
|
||||||
|
# Correct expected output based on implementation:
|
||||||
|
expected_output_lines = sorted(
|
||||||
|
[
|
||||||
|
"ro_file.txt (read only)",
|
||||||
|
"file1.txt",
|
||||||
|
"file[markup].txt",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
expected_output = "\n".join(expected_output_lines) + "\n"
|
||||||
|
|
||||||
|
actual_output = io.format_files_for_input(rel_fnames, rel_read_only_fnames)
|
||||||
|
|
||||||
|
# Normalizing actual output by splitting, sorting, and rejoining
|
||||||
|
actual_output_lines = sorted(filter(None, actual_output.splitlines()))
|
||||||
|
normalized_actual_output = "\n".join(actual_output_lines) + "\n"
|
||||||
|
|
||||||
|
self.assertEqual(normalized_actual_output, expected_output)
|
||||||
|
|
||||||
|
@patch("aider.io.Columns")
|
||||||
|
@patch("os.path.abspath")
|
||||||
|
@patch("os.path.join")
|
||||||
|
def test_format_files_for_input_pretty_true_no_files(
|
||||||
|
self, mock_join, mock_abspath, mock_columns, mock_is_dumb_terminal
|
||||||
|
):
|
||||||
|
io = InputOutput(pretty=True, root="test_root")
|
||||||
|
io.format_files_for_input([], [])
|
||||||
|
mock_columns.assert_not_called()
|
||||||
|
|
||||||
|
@patch("aider.io.Columns")
|
||||||
|
@patch("os.path.abspath")
|
||||||
|
@patch("os.path.join")
|
||||||
|
def test_format_files_for_input_pretty_true_editable_only(
|
||||||
|
self, mock_join, mock_abspath, mock_columns, mock_is_dumb_terminal
|
||||||
|
):
|
||||||
|
io = InputOutput(pretty=True, root="test_root")
|
||||||
|
rel_fnames = ["edit1.txt", "edit[markup].txt"]
|
||||||
|
|
||||||
|
io.format_files_for_input(rel_fnames, [])
|
||||||
|
|
||||||
|
mock_columns.assert_called_once()
|
||||||
|
args, _ = mock_columns.call_args
|
||||||
|
renderables = args[0]
|
||||||
|
|
||||||
|
self.assertEqual(len(renderables), 2)
|
||||||
|
self.assertIsInstance(renderables[0], Text)
|
||||||
|
self.assertEqual(renderables[0].plain, "edit1.txt")
|
||||||
|
self.assertIsInstance(renderables[1], Text)
|
||||||
|
self.assertEqual(renderables[1].plain, "edit[markup].txt")
|
||||||
|
|
||||||
|
@patch("aider.io.Columns")
|
||||||
|
@patch("os.path.abspath")
|
||||||
|
@patch("os.path.join")
|
||||||
|
def test_format_files_for_input_pretty_true_readonly_only(
|
||||||
|
self, mock_join, mock_abspath, mock_columns, mock_is_dumb_terminal
|
||||||
|
):
|
||||||
|
io = InputOutput(pretty=True, root="test_root")
|
||||||
|
|
||||||
|
# Mock path functions to ensure rel_path is chosen by the shortener logic
|
||||||
|
mock_join.side_effect = lambda *args: "/".join(args)
|
||||||
|
mock_abspath.side_effect = lambda p: "/ABS_PREFIX_VERY_LONG/" + os.path.normpath(p)
|
||||||
|
|
||||||
|
rel_read_only_fnames = ["ro1.txt", "ro[markup].txt"]
|
||||||
|
# When all files in chat are read-only
|
||||||
|
rel_fnames = list(rel_read_only_fnames)
|
||||||
|
|
||||||
|
io.format_files_for_input(rel_fnames, rel_read_only_fnames)
|
||||||
|
|
||||||
|
self.assertEqual(mock_columns.call_count, 2)
|
||||||
|
args, _ = mock_columns.call_args
|
||||||
|
renderables = args[0]
|
||||||
|
|
||||||
|
self.assertEqual(len(renderables), 3) # Readonly: + 2 files
|
||||||
|
self.assertIsInstance(renderables[0], Text)
|
||||||
|
self.assertEqual(renderables[0].plain, "Readonly:")
|
||||||
|
self.assertIsInstance(renderables[1], Text)
|
||||||
|
self.assertEqual(renderables[1].plain, "ro1.txt")
|
||||||
|
self.assertIsInstance(renderables[2], Text)
|
||||||
|
self.assertEqual(renderables[2].plain, "ro[markup].txt")
|
||||||
|
|
||||||
|
@patch("aider.io.Columns")
|
||||||
|
@patch("os.path.abspath")
|
||||||
|
@patch("os.path.join")
|
||||||
|
def test_format_files_for_input_pretty_true_mixed_files(
|
||||||
|
self, mock_join, mock_abspath, mock_columns, mock_is_dumb_terminal
|
||||||
|
):
|
||||||
|
io = InputOutput(pretty=True, root="test_root")
|
||||||
|
|
||||||
|
mock_join.side_effect = lambda *args: "/".join(args)
|
||||||
|
mock_abspath.side_effect = lambda p: "/ABS_PREFIX_VERY_LONG/" + os.path.normpath(p)
|
||||||
|
|
||||||
|
rel_fnames = ["edit1.txt", "edit[markup].txt", "ro1.txt", "ro[markup].txt"]
|
||||||
|
rel_read_only_fnames = ["ro1.txt", "ro[markup].txt"]
|
||||||
|
|
||||||
|
io.format_files_for_input(rel_fnames, rel_read_only_fnames)
|
||||||
|
|
||||||
|
self.assertEqual(mock_columns.call_count, 4)
|
||||||
|
|
||||||
|
# Check arguments for the first rendering of read-only files (call 0)
|
||||||
|
args_ro, _ = mock_columns.call_args_list[0]
|
||||||
|
renderables_ro = args_ro[0]
|
||||||
|
self.assertEqual(
|
||||||
|
renderables_ro, [Text("Readonly:"), Text("ro1.txt"), Text("ro[markup].txt")]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check arguments for the first rendering of editable files (call 2)
|
||||||
|
args_ed, _ = mock_columns.call_args_list[2]
|
||||||
|
renderables_ed = args_ed[0]
|
||||||
|
self.assertEqual(
|
||||||
|
renderables_ed, [Text("Editable:"), Text("edit1.txt"), Text("edit[markup].txt")]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue