Skip to content

Commit 84cc241

Browse files
authored
Merge pull request #66 from devchat-ai/feature/add-refactor-workflow
Feature: Add Code Refactoring Commands and Utility Script
2 parents d66ada9 + 91cea1b commit 84cc241

4 files changed

Lines changed: 146 additions & 0 deletions

File tree

refactor/command.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
description: rewrite selected code.
2+
hint: question
3+
input: required
4+
steps:
5+
- run: $devchat_python $command_path/rewrite.py "$input"

refactor/docstring/command.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
description: Automatically add or refine docstrings to enhance readability and clarity. Select a function or method and execute this command to optimize its documentation.
2+
steps:
3+
- run: $devchat_python $command_path/../rewrite.py "add doc comment"

refactor/names/command.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
description: Automatically rename poorly-named local variables for improved readability. Select the code segment and execute this command to optimize variable names.
2+
steps:
3+
- run: $devchat_python $command_path/../rewrite.py "Refine internal variable and function names within the code to achieve concise and meaningful identifiers that comply with English naming conventions."
4+

refactor/rewrite.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import re
2+
import sys
3+
4+
from devchat.ide.service import IDEService
5+
from devchat.ide.vscode_services import selected_lines, visible_lines
6+
from devchat.llm import chat
7+
8+
9+
def get_selected_code():
10+
"""
11+
Retrieves the selected lines of code from the user's selection.
12+
13+
This function extracts the text selected by the user in their IDE or text editor.
14+
If no text has been selected, it prints an error message to stderr and exits the
15+
program with a non-zero status indicating failure.
16+
17+
Returns:
18+
dict: A dictionary containing the key 'selectedText' with the selected text
19+
as its value. If no text is selected, the program exits.
20+
"""
21+
selected_data = IDEService().get_selected_range().dict()
22+
23+
miss_selected_error = "Please select some text."
24+
if selected_data["text"] == "":
25+
print(miss_selected_error, file=sys.stderr, flush=True)
26+
sys.exit(-1)
27+
28+
return selected_data
29+
30+
31+
def get_visible_code():
32+
"""
33+
Retrieves visible code from the visible_lines function.
34+
35+
Returns:
36+
visible_data: The visible code retrieved from the visible_lines function.
37+
"""
38+
visible_data = IDEService().get_visible_range().dict()
39+
return visible_data
40+
41+
42+
REWRITE_PROMPT = prompt = """
43+
Your task is:
44+
{question}
45+
Following the task requirements, modify only the selected portion of the code. \
46+
Please ensure that the revised code segment maintains the same indentation as the \
47+
selected code to seamlessly integrate with the existing code structure and maintain \
48+
correct syntax. Just refactor the selected code. Keep all other information as it is. \
49+
Here is the relevant context \
50+
information for your reference:
51+
1. selected code info: {selected_text}
52+
2. current visible code info: {visible_text}
53+
"""
54+
55+
56+
@chat(prompt=REWRITE_PROMPT, stream_out=True)
57+
# pylint: disable=unused-argument
58+
def ai_rewrite(question, selected_text, visible_text):
59+
"""
60+
call ai to rewrite selected code
61+
"""
62+
pass # pylint: disable=unnecessary-pass
63+
64+
65+
def extract_markdown_block(text):
66+
"""
67+
Extracts the first Markdown code block from the given text without the language specifier.
68+
69+
:param text: A string containing Markdown text
70+
:return: The content of the first Markdown code block, or None if not found
71+
"""
72+
pattern = r"```(?:\w+)?\s*\n(.*?)\n```"
73+
match = re.search(pattern, text, re.DOTALL)
74+
75+
if match:
76+
block_content = match.group(1)
77+
return block_content
78+
else:
79+
return text
80+
81+
82+
def replace_selected(new_code):
83+
selected_data = IDEService().get_selected_code().dict()
84+
select_file = selected_data["abspath"]
85+
select_range = selected_data["range"] # [start_line, start_col, end_line, end_col]
86+
87+
# Read the file
88+
with open(select_file, "r", encoding="utf-8") as file:
89+
lines = file.readlines()
90+
lines.append("\n")
91+
92+
# Modify the selected lines
93+
start_line = select_range["start"]["line"]
94+
start_col = select_range["start"]["character"]
95+
end_line = select_range["end"]["line"]
96+
end_col = select_range["end"]["character"]
97+
98+
# If the selection spans multiple lines, handle the last line and delete the lines in between
99+
if start_line != end_line:
100+
lines[start_line] = lines[start_line][:start_col] + new_code
101+
# Append the text after the selection on the last line
102+
lines[start_line] += lines[end_line][end_col:]
103+
# Delete the lines between start_line and end_line
104+
del lines[start_line + 1 : end_line + 1]
105+
else:
106+
# If the selection is within a single line, remove the selected text
107+
lines[start_line] = lines[start_line][:start_col] + new_code + lines[end_line][end_col:]
108+
109+
# Combine everything back together
110+
modified_text = "".join(lines)
111+
112+
# Write the changes back to the file
113+
with open(select_file, "w", encoding="utf-8") as file:
114+
file.write(modified_text)
115+
116+
117+
def main():
118+
question = sys.argv[1]
119+
# prepare code
120+
selected_text = get_selected_code()
121+
visible_text = get_visible_code()
122+
123+
# rewrite
124+
response = ai_rewrite(question=question, selected_text=selected_text, visible_text=visible_text)
125+
126+
# apply new code to editor
127+
new_code = extract_markdown_block(response)
128+
IDEService().diff_apply("", new_code)
129+
130+
sys.exit(0)
131+
132+
133+
if __name__ == "__main__":
134+
main()

0 commit comments

Comments
 (0)