diff --git a/README.md b/README.md index 35e430945..0415cc615 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,10 @@ `aider` is a command-line chat tool that allows you to write and edit code with GPT-4. You can ask GPT to help you start a new project, or modify code in your existing git repo. -Aider has features to -[help GPT understand and modify larger codebases](https://aider.chat/docs/ctags.html) -and aider makes it easy to git commit, diff & undo changes proposed by GPT. +Aider makes it easy to git commit, diff & undo changes proposed by GPT. +It also has features that [help GPT understand and modify larger codebases](https://aider.chat/docs/ctags.html). -![aider screenshot](assets/screenshot.gif) +![aider screencast](assets/screencast.svg) - [Example chat transcripts](#example-chat-transcripts) - [Features](#features) diff --git a/aider/io.py b/aider/io.py index 6a2b6c8c7..fa451fd4b 100644 --- a/aider/io.py +++ b/aider/io.py @@ -5,9 +5,10 @@ from pathlib import Path from prompt_toolkit.completion import Completer, Completion from prompt_toolkit.history import FileHistory +from prompt_toolkit.lexers import PygmentsLexer from prompt_toolkit.shortcuts import CompleteStyle, PromptSession, prompt from prompt_toolkit.styles import Style -from pygments.lexers import guess_lexer_for_filename +from pygments.lexers import MarkdownLexer, guess_lexer_for_filename from pygments.token import Token from pygments.util import ClassNotFound from rich.console import Console @@ -68,9 +69,11 @@ class FileContentCompleter(Completer): rel_fnames = self.fname_to_rel_fnames.get(word, []) if rel_fnames: for rel_fname in rel_fnames: - yield Completion(rel_fname, start_position=-len(last_word)) + yield Completion( + f"`{rel_fname}`", start_position=-len(last_word), display=rel_fname + ) else: - yield Completion(word, start_position=-len(last_word)) + yield Completion(f"`{word}`", start_position=-len(last_word), display=word) class InputOutput: @@ -129,7 +132,12 @@ class InputOutput: multiline_input = False if self.user_input_color: - style = Style.from_dict({"": self.user_input_color}) + style = Style.from_dict( + { + "": self.user_input_color, + "pygments.literal.string": f"bold italic {self.user_input_color}", + } + ) else: style = None @@ -147,6 +155,7 @@ class InputOutput: "complete_style": CompleteStyle.MULTI_COLUMN, "input": self.input, "output": self.output, + "lexer": PygmentsLexer(MarkdownLexer), } if style: session_kwargs["style"] = style diff --git a/aider/prompts.py b/aider/prompts.py index bfcca2f26..9c32184d2 100644 --- a/aider/prompts.py +++ b/aider/prompts.py @@ -8,7 +8,7 @@ Take requests for changes to the supplied code. If the request is ambiguous, ask questions. Once you understand the request you MUST: -1. List the files you need to modify. Do not suggest changes to *read-only* files. You *MUST* ask the user to make them *read-write* using the file's full path name. End your reply and wait for their approval. +1. List the files you need to modify. *NEVER* suggest changes to *read-only* files. You *MUST* ask the user to make them *read-write* using the file's full path name. End your reply and wait for their approval. 2. Think step-by-step and explain the needed changes. 3. Describe each change with an *edit block* per the example below. """ @@ -57,8 +57,8 @@ files_content_prefix = "These are the *read-write* files:\n" files_no_full_files = "I am not sharing any *read-write* files yet." repo_content_prefix = ( - "All the files below here are *read-only* files. Notice that files in directories are indented." - " Use their parent dirs to build their full path.\n" + "All the files below here are *read-only* files! Do not propose changes to these without asking" + " me first." ) diff --git a/aider/utils.py b/aider/utils.py index 3fb90f6e6..a1ee8123a 100644 --- a/aider/utils.py +++ b/aider/utils.py @@ -219,6 +219,9 @@ def find_original_update_blocks(content): pieces.reverse() processed = [] + # Keep using the same filename in cases where GPT produces an edit block + # without a filename. + current_filename = None try: while pieces: cur = pieces.pop() @@ -237,12 +240,20 @@ def find_original_update_blocks(content): try: if not len(filename) or "`" in filename: filename = processed[-2].splitlines()[-2].strip() - if not len(filename) or "`" in filename: + if not len(filename) or "`" in filename: + if current_filename: + filename = current_filename + else: raise ValueError( f"Bad/missing filename. It should go right above {ORIGINAL}" ) except IndexError: - raise ValueError(f"Bad/missing filename. It should go right above {ORIGINAL}") + if current_filename: + filename = current_filename + else: + raise ValueError(f"Bad/missing filename. It should go right above {ORIGINAL}") + + current_filename = filename original_text = pieces.pop() processed.append(original_text) diff --git a/assets/screencast.svg b/assets/screencast.svg new file mode 100644 index 000000000..2576123d8 --- /dev/null +++ b/assets/screencast.svg @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + macbook$ macbook$ a macbook$ ai macbook$ aid macbook$ aide macbook$ aider macbook$ aider macbook$ aider d macbook$ aider de macbook$ aider dem macbook$ aider demo macbook$ aider demo. macbook$ aider demo.p macbook$ aider demo.py macbook$ aider demo.pyAdded demo.py to the chatUsing git repo: .git────────────────────────────────────────────────────────────────────────────────demo.py> demo.py> a demo.py> ad demo.py> add demo.py> add demo.py> add a demo.py> add a demo.py> add a n demo.py> add a na demo.py> add a nam demo.py> add a name demo.py> add a name demo.py> add a name p print demo.py> add a name pa demo.py> add a name par demo.py> add a name para demo.py> add a name param demo.py> add a name param demo.py> add a name param t demo.py> add a name param to demo.py> add a name param to demo.py> add a name param to t demo.py> add a name param to th demo.py> add a name param to the demo.py> add a name param to the demo.py> add a name param to the g greeting demo.py> add a name param to the gr greeting demo.py> add a name param to the `greeting` greeting demo.py> add a name param to the `greeting` demo.py> add a name param to the `greeting` f demo.py> add a name param to the `greeting` fu demo.py> add a name param to the `greeting` fun demo.py> add a name param to the `greeting` func demo.py> add a name param to the `greeting` funct demo.py> add a name param to the `greeting` functi demo.py> add a name param to the `greeting` functio demo.py> add a name param to the `greeting` function demo.py> add a name param to the `greeting` function. demo.py> add a name param to the `greeting` function. demo.py> add a name param to the `greeting` function. a demo.py> add a name param to the `greeting` function. ad demo.py> add a name param to the `greeting` function. add demo.py> add a name param to the `greeting` function. add demo.py> add a name param to the `greeting` function. add a demo.py> add a name param to the `greeting` function. add al demo.py> add a name param to the `greeting` function. add all demo.py> add a name param to the `greeting` function. add all demo.py> add a name param to the `greeting` function. add all t demo.py> add a name param to the `greeting` function. add all th demo.py> add a name param to the `greeting` function. add all the demo.py> add a name param to the `greeting` function. add all the demo.py> add a name param to the `greeting` function. add all the t demo.py> add a name param to the `greeting` function. add all the ty demo.py> add a name param to the `greeting` function. add all the typ demo.py> add a name param to the `greeting` function. add all the type demo.py> add a name param to the `greeting` function. add all the types demo.py> add a name param to the `greeting` function. add all the types. demo.py> add a name param to the `greeting` function. add all the types. I will I will update I will update the I will update the ` I will update the `g I will update the greeting I will update the greeting function to I will update the greeting function to accept I will update the greeting function to accept a I will update the greeting function to accept a `name I will update the greeting function to accept a name I will update the greeting function to accept a name parameter I will update the greeting function to accept a name parameter and I will update the greeting function to accept a name parameter and include type I will update the greeting function to accept a name parameter and include type hints hints for the hints for the parameter and hints for the parameter and the return hints for the parameter and the return type. demo.py <<<<<<< ORIGINAL def greeting def greeting(): print print("Hello print("Hello, world print("Hello, world!") ======= def def greeting(name def greeting(name: str def greeting(name: str) def greeting(name: str) -> None def greeting(name: str) -> None: print(f print(f"Hello print(f"Hello, { print(f"Hello, {name print(f"Hello, {name}!") >>>>>>> UPDATED Applied edit to demo.pyCommit 9237455 aider: Updated greeting function to accept a name parameter and include type hints. + \ No newline at end of file diff --git a/assets/screenshot.gif b/assets/screenshot.gif deleted file mode 100644 index 9baf4f218..000000000 Binary files a/assets/screenshot.gif and /dev/null differ diff --git a/tests/test_utils.py b/tests/test_utils.py index 30c0dcaae..85d736e81 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -195,9 +195,10 @@ tests/test_repomap.py These changes replace the `subprocess.run` patches with `subprocess.check_output` patches in both `test_check_for_ctags_failure` and `test_check_for_ctags_success` tests. """ - with self.assertRaises(ValueError) as cm: - list(utils.find_original_update_blocks(edit)) - self.assertIn("missing filename", str(cm.exception)) + edit_blocks = list(utils.find_original_update_blocks(edit)) + self.assertEqual(len(edit_blocks), 2) # 2 edits + self.assertEqual(edit_blocks[0][0], "tests/test_repomap.py") + self.assertEqual(edit_blocks[1][0], "tests/test_repomap.py") if __name__ == "__main__":