autocoder-nano 0.1.30__py3-none-any.whl → 0.1.34__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- autocoder_nano/agent/agent_base.py +4 -4
- autocoder_nano/agent/agentic_edit.py +1584 -0
- autocoder_nano/agent/agentic_edit_tools/__init__.py +28 -0
- autocoder_nano/agent/agentic_edit_tools/ask_followup_question_tool.py +51 -0
- autocoder_nano/agent/agentic_edit_tools/attempt_completion_tool.py +36 -0
- autocoder_nano/agent/agentic_edit_tools/base_tool_resolver.py +31 -0
- autocoder_nano/agent/agentic_edit_tools/execute_command_tool.py +65 -0
- autocoder_nano/agent/agentic_edit_tools/list_code_definition_names_tool.py +78 -0
- autocoder_nano/agent/agentic_edit_tools/list_files_tool.py +123 -0
- autocoder_nano/agent/agentic_edit_tools/list_package_info_tool.py +42 -0
- autocoder_nano/agent/agentic_edit_tools/plan_mode_respond_tool.py +35 -0
- autocoder_nano/agent/agentic_edit_tools/read_file_tool.py +73 -0
- autocoder_nano/agent/agentic_edit_tools/replace_in_file_tool.py +148 -0
- autocoder_nano/agent/agentic_edit_tools/search_files_tool.py +135 -0
- autocoder_nano/agent/agentic_edit_tools/write_to_file_tool.py +57 -0
- autocoder_nano/agent/agentic_edit_types.py +151 -0
- autocoder_nano/auto_coder_nano.py +159 -700
- autocoder_nano/git_utils.py +63 -1
- autocoder_nano/llm_client.py +170 -3
- autocoder_nano/llm_types.py +72 -16
- autocoder_nano/rules/rules_learn.py +221 -0
- autocoder_nano/templates.py +1 -1
- autocoder_nano/utils/completer_utils.py +616 -0
- autocoder_nano/utils/formatted_log_utils.py +128 -0
- autocoder_nano/utils/printer_utils.py +5 -4
- autocoder_nano/utils/shell_utils.py +85 -0
- autocoder_nano/version.py +1 -1
- {autocoder_nano-0.1.30.dist-info → autocoder_nano-0.1.34.dist-info}/METADATA +3 -2
- {autocoder_nano-0.1.30.dist-info → autocoder_nano-0.1.34.dist-info}/RECORD +34 -16
- autocoder_nano/agent/new/auto_new_project.py +0 -278
- /autocoder_nano/{agent/new → rules}/__init__.py +0 -0
- {autocoder_nano-0.1.30.dist-info → autocoder_nano-0.1.34.dist-info}/LICENSE +0 -0
- {autocoder_nano-0.1.30.dist-info → autocoder_nano-0.1.34.dist-info}/WHEEL +0 -0
- {autocoder_nano-0.1.30.dist-info → autocoder_nano-0.1.34.dist-info}/entry_points.txt +0 -0
- {autocoder_nano-0.1.30.dist-info → autocoder_nano-0.1.34.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,616 @@
|
|
1
|
+
import os
|
2
|
+
from typing import List, Any
|
3
|
+
|
4
|
+
from prompt_toolkit.completion import Completer, Completion
|
5
|
+
|
6
|
+
from autocoder_nano.llm_types import Tag, AutoCoderArgs, FileSystemModel, MemoryConfig
|
7
|
+
|
8
|
+
COMMANDS = {
|
9
|
+
"/add_files": {
|
10
|
+
"/group": {"/add": "", "/drop": "", "/reset": ""},
|
11
|
+
"/refresh": "",
|
12
|
+
},
|
13
|
+
"/remove_files": {"/all": ""},
|
14
|
+
"/coding": {"/apply": ""},
|
15
|
+
"/chat": {"/history": "", "/new": "", "/review": ""},
|
16
|
+
"/models": {"/add_model": "", "/remove": "", "/list": "", "/check": ""},
|
17
|
+
"/help": {
|
18
|
+
"/add_files": "",
|
19
|
+
"/remove_files": "",
|
20
|
+
"/chat": "",
|
21
|
+
"/coding": "",
|
22
|
+
"/commit": "",
|
23
|
+
"/conf": "",
|
24
|
+
"/mode": "",
|
25
|
+
"/models": ""
|
26
|
+
},
|
27
|
+
"/exclude_files": {"/list": "", "/drop": ""},
|
28
|
+
"/exclude_dirs": {},
|
29
|
+
"/rules": {"/list": "", "/show": "", "/remove": "", "/analyze": "", "/commit": ""}
|
30
|
+
}
|
31
|
+
|
32
|
+
|
33
|
+
class CommandTextParser:
|
34
|
+
def __init__(self, text: str, command: str):
|
35
|
+
self.text = text
|
36
|
+
self.pos = -1
|
37
|
+
self.len = len(text)
|
38
|
+
self.is_extracted = False
|
39
|
+
self.current_word_start_pos = 0
|
40
|
+
self.current_word_end_pos = 0
|
41
|
+
self.in_current_sub_command = ""
|
42
|
+
self.completions = []
|
43
|
+
self.command = command
|
44
|
+
self.current_hiararchy = COMMANDS[command]
|
45
|
+
self.sub_commands = []
|
46
|
+
self.tags = []
|
47
|
+
|
48
|
+
def first_sub_command(self):
|
49
|
+
if len(self.sub_commands) == 0:
|
50
|
+
return None
|
51
|
+
return self.sub_commands[0]
|
52
|
+
|
53
|
+
def last_sub_command(self):
|
54
|
+
if len(self.sub_commands) == 0:
|
55
|
+
return None
|
56
|
+
return self.sub_commands[-1]
|
57
|
+
|
58
|
+
def peek(self):
|
59
|
+
if self.pos + 1 < self.len:
|
60
|
+
return self.text[self.pos + 1]
|
61
|
+
return None
|
62
|
+
|
63
|
+
def peek2(self):
|
64
|
+
if self.pos + 2 < self.len:
|
65
|
+
return self.text[self.pos + 2]
|
66
|
+
return None
|
67
|
+
|
68
|
+
def peek3(self):
|
69
|
+
if self.pos + 3 < self.len:
|
70
|
+
return self.text[self.pos + 3]
|
71
|
+
return None
|
72
|
+
|
73
|
+
def next(self):
|
74
|
+
if self.pos < self.len - 1:
|
75
|
+
self.pos += 1
|
76
|
+
char = self.text[self.pos]
|
77
|
+
return char
|
78
|
+
return None
|
79
|
+
|
80
|
+
def consume_blank(self):
|
81
|
+
while self.peek() == "\n" or self.peek() == " " or self.peek() == "\t" or self.peek() == "\r":
|
82
|
+
self.next()
|
83
|
+
|
84
|
+
def is_blank(self) -> bool:
|
85
|
+
return self.peek() == "\n" or self.peek() == " " or self.peek() == "\t" or self.peek() == "\r"
|
86
|
+
|
87
|
+
def is_sub_command(self) -> bool | None:
|
88
|
+
backup_pos = self.pos
|
89
|
+
self.consume_blank()
|
90
|
+
try:
|
91
|
+
if self.peek() == "/":
|
92
|
+
current_sub_command = ""
|
93
|
+
while self.peek() is not None and self.peek() != " " and self.peek() != "\n":
|
94
|
+
current_sub_command += self.next()
|
95
|
+
|
96
|
+
if current_sub_command.count("/") > 1:
|
97
|
+
self.pos = backup_pos
|
98
|
+
return False
|
99
|
+
return True
|
100
|
+
return False
|
101
|
+
finally:
|
102
|
+
self.pos = backup_pos
|
103
|
+
|
104
|
+
def consume_sub_command(self) -> str:
|
105
|
+
# backup_pos = self.pos
|
106
|
+
self.consume_blank()
|
107
|
+
current_sub_command = ""
|
108
|
+
while self.peek() is not None and self.peek() != " " and self.peek() != "\n":
|
109
|
+
current_sub_command += self.next()
|
110
|
+
|
111
|
+
if self.peek() is None:
|
112
|
+
self.is_extracted = True
|
113
|
+
self.current_word_end_pos = self.pos + 1
|
114
|
+
self.current_word_start_pos = self.current_word_end_pos - len(
|
115
|
+
current_sub_command
|
116
|
+
)
|
117
|
+
self.in_current_sub_command = current_sub_command
|
118
|
+
else:
|
119
|
+
if current_sub_command in self.current_hiararchy:
|
120
|
+
self.current_hiararchy = self.current_hiararchy[current_sub_command]
|
121
|
+
self.sub_commands.append(current_sub_command)
|
122
|
+
|
123
|
+
return current_sub_command
|
124
|
+
|
125
|
+
def consume_command_value(self):
|
126
|
+
current_word = ""
|
127
|
+
while self.peek() is not None:
|
128
|
+
v = self.next()
|
129
|
+
if v == " ":
|
130
|
+
current_word = ""
|
131
|
+
else:
|
132
|
+
current_word += v
|
133
|
+
self.is_extracted = True
|
134
|
+
self.current_word_end_pos = self.pos + 1
|
135
|
+
self.current_word_start_pos = self.current_word_end_pos - len(current_word)
|
136
|
+
|
137
|
+
def previous(self):
|
138
|
+
if self.pos > 1:
|
139
|
+
return self.text[self.pos - 1]
|
140
|
+
return None
|
141
|
+
|
142
|
+
def is_start_tag(self) -> bool | None:
|
143
|
+
backup_pos = self.pos
|
144
|
+
tag = ""
|
145
|
+
try:
|
146
|
+
if self.peek() == "<" and self.peek2() != "/":
|
147
|
+
while (
|
148
|
+
self.peek() is not None
|
149
|
+
and self.peek() != ">"
|
150
|
+
and not self.is_blank()
|
151
|
+
):
|
152
|
+
tag += self.next()
|
153
|
+
if self.peek() == ">":
|
154
|
+
tag += self.next()
|
155
|
+
return True
|
156
|
+
else:
|
157
|
+
return False
|
158
|
+
return False
|
159
|
+
finally:
|
160
|
+
self.pos = backup_pos
|
161
|
+
|
162
|
+
def consume_tag(self):
|
163
|
+
start_tag = ""
|
164
|
+
content = ""
|
165
|
+
end_tag = ""
|
166
|
+
|
167
|
+
# consume start tag
|
168
|
+
self.current_word_start_pos = self.pos + 1
|
169
|
+
while self.peek() is not None and self.peek() != ">" and not self.is_blank():
|
170
|
+
start_tag += self.next()
|
171
|
+
if self.peek() == ">":
|
172
|
+
start_tag += self.next()
|
173
|
+
self.current_word_end_pos = self.pos + 1
|
174
|
+
tag = Tag(start_tag=start_tag, content=content, end_tag=end_tag)
|
175
|
+
self.tags.append(tag)
|
176
|
+
|
177
|
+
# consume content
|
178
|
+
self.current_word_start_pos = self.pos + 1
|
179
|
+
while self.peek() is not None and not (
|
180
|
+
self.peek() == "<" and self.peek2() == "/"
|
181
|
+
):
|
182
|
+
content += self.next()
|
183
|
+
|
184
|
+
tag.content = content
|
185
|
+
self.current_word_end_pos = self.pos + 1
|
186
|
+
|
187
|
+
# consume end tag
|
188
|
+
self.current_word_start_pos = self.pos + 1
|
189
|
+
if self.peek() == "<" and self.peek2() == "/":
|
190
|
+
while (
|
191
|
+
self.peek() is not None and self.peek() != ">" and not self.is_blank()
|
192
|
+
):
|
193
|
+
end_tag += self.next()
|
194
|
+
if self.peek() == ">":
|
195
|
+
end_tag += self.next()
|
196
|
+
tag.end_tag = end_tag
|
197
|
+
self.current_word_end_pos = self.pos + 1
|
198
|
+
|
199
|
+
# check is finished
|
200
|
+
if self.peek() is None:
|
201
|
+
self.is_extracted = True
|
202
|
+
|
203
|
+
def consume_coding_value(self):
|
204
|
+
current_word = ""
|
205
|
+
while self.peek() is not None and not self.is_start_tag():
|
206
|
+
v = self.next()
|
207
|
+
if v == " ":
|
208
|
+
current_word = ""
|
209
|
+
else:
|
210
|
+
current_word += v
|
211
|
+
if self.peek() is None:
|
212
|
+
self.is_extracted = True
|
213
|
+
|
214
|
+
self.current_word_end_pos = self.pos + 1
|
215
|
+
self.current_word_start_pos = self.current_word_end_pos - len(current_word)
|
216
|
+
|
217
|
+
def current_word(self) -> str:
|
218
|
+
return self.text[self.current_word_start_pos: self.current_word_end_pos]
|
219
|
+
|
220
|
+
def get_current_word(self) -> str:
|
221
|
+
return self.current_word()
|
222
|
+
|
223
|
+
def get_sub_commands(self) -> list[str]:
|
224
|
+
if self.get_current_word() and not self.get_current_word().startswith("/"):
|
225
|
+
return []
|
226
|
+
|
227
|
+
if isinstance(self.current_hiararchy, str):
|
228
|
+
return []
|
229
|
+
|
230
|
+
return [item for item in list(self.current_hiararchy.keys()) if item]
|
231
|
+
|
232
|
+
def add_files(self):
|
233
|
+
"""
|
234
|
+
for exmaple:
|
235
|
+
/add_files file1 file2 file3
|
236
|
+
/add_files /group/abc/cbd /group/abc/bc2
|
237
|
+
/add_files /group1 /add xxxxx
|
238
|
+
/add_files /group
|
239
|
+
/add_files /group /add <groupname>
|
240
|
+
/add_files /group /drop <groupname>
|
241
|
+
/add_files /group <groupname>,<groupname>
|
242
|
+
/add_files /refresh
|
243
|
+
"""
|
244
|
+
while True:
|
245
|
+
if self.pos == self.len - 1:
|
246
|
+
break
|
247
|
+
elif self.is_extracted:
|
248
|
+
break
|
249
|
+
elif self.is_sub_command():
|
250
|
+
self.consume_sub_command()
|
251
|
+
else:
|
252
|
+
self.consume_command_value()
|
253
|
+
return self
|
254
|
+
|
255
|
+
def coding(self):
|
256
|
+
while True:
|
257
|
+
if self.pos == self.len - 1:
|
258
|
+
break
|
259
|
+
elif self.is_extracted:
|
260
|
+
break
|
261
|
+
elif self.is_sub_command():
|
262
|
+
self.consume_sub_command()
|
263
|
+
elif self.is_start_tag():
|
264
|
+
self.consume_tag()
|
265
|
+
else:
|
266
|
+
self.consume_coding_value()
|
267
|
+
|
268
|
+
|
269
|
+
class CommandCompleter(Completer):
|
270
|
+
def __init__(self, commands: List[str], file_system_model: FileSystemModel, memory_model: MemoryConfig):
|
271
|
+
self.commands = commands
|
272
|
+
self.file_system_model = file_system_model
|
273
|
+
self.memory_model = memory_model
|
274
|
+
|
275
|
+
# self.all_file_names = get_all_file_names_in_project()
|
276
|
+
# self.all_files = get_all_file_in_project()
|
277
|
+
# self.all_dir_names = get_all_dir_names_in_project()
|
278
|
+
# self.all_files_with_dot = get_all_file_in_project_with_dot()
|
279
|
+
# self.symbol_list = get_symbol_list()
|
280
|
+
self.all_file_names: List[str] = []
|
281
|
+
self.all_files: List[str] = []
|
282
|
+
self.all_dir_names: List[str] = []
|
283
|
+
self.all_files_with_dot: List[str] = []
|
284
|
+
self.symbol_list: List[Any] = [] # Use Any for SymbolItem structure from runner
|
285
|
+
self.current_file_names: List[str] = []
|
286
|
+
|
287
|
+
self.refresh_files()
|
288
|
+
|
289
|
+
def get_completions(self, document, complete_event):
|
290
|
+
text = document.text_before_cursor
|
291
|
+
words = text.split()
|
292
|
+
|
293
|
+
if len(words) > 0:
|
294
|
+
if words[0] == "/mode":
|
295
|
+
left_word = text[len("/mode"):]
|
296
|
+
for mode in ["normal", "auto_detect"]:
|
297
|
+
if mode.startswith(left_word.strip()):
|
298
|
+
yield Completion(mode, start_position=-len(left_word.strip()))
|
299
|
+
|
300
|
+
if words[0] == "/add_files":
|
301
|
+
new_text = text[len("/add_files"):]
|
302
|
+
parser = CommandTextParser(new_text, words[0])
|
303
|
+
parser.add_files()
|
304
|
+
current_word = parser.current_word()
|
305
|
+
|
306
|
+
if parser.last_sub_command() == "/refresh":
|
307
|
+
return
|
308
|
+
|
309
|
+
for command in parser.get_sub_commands():
|
310
|
+
if command.startswith(current_word):
|
311
|
+
yield Completion(command, start_position=-len(current_word))
|
312
|
+
|
313
|
+
if parser.first_sub_command() == "/group" and (
|
314
|
+
parser.last_sub_command() == "/group"
|
315
|
+
or parser.last_sub_command() == "/drop"
|
316
|
+
):
|
317
|
+
group_names = list(self.memory_model.get_memory_func().get("current_files", {}).get("groups", {}).keys())
|
318
|
+
if "," in current_word:
|
319
|
+
current_word = current_word.split(",")[-1]
|
320
|
+
|
321
|
+
for group_name in group_names:
|
322
|
+
if group_name.startswith(current_word):
|
323
|
+
yield Completion(
|
324
|
+
group_name, start_position=-len(current_word)
|
325
|
+
)
|
326
|
+
|
327
|
+
if parser.first_sub_command() != "/group":
|
328
|
+
if current_word and current_word.startswith("."):
|
329
|
+
for file_name in self.all_files_with_dot:
|
330
|
+
if file_name.startswith(current_word):
|
331
|
+
yield Completion(file_name, start_position=-len(current_word))
|
332
|
+
else:
|
333
|
+
for file_name in self.all_file_names:
|
334
|
+
if file_name.startswith(current_word):
|
335
|
+
yield Completion(file_name, start_position=-len(current_word))
|
336
|
+
for file_name in self.all_files:
|
337
|
+
if current_word and current_word in file_name:
|
338
|
+
yield Completion(file_name, start_position=-len(current_word))
|
339
|
+
|
340
|
+
elif words[0] in ["/chat", "/coding"]:
|
341
|
+
image_extensions = (
|
342
|
+
".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".tif", ".webp", ".svg", ".ico",
|
343
|
+
".heic", ".heif", ".raw", ".cr2", ".nef", ".arw", ".dng", ".orf", ".rw2", ".pef",
|
344
|
+
".srw", ".eps", ".ai", ".psd", ".xcf",
|
345
|
+
)
|
346
|
+
new_text = text[len(words[0]):]
|
347
|
+
parser = CommandTextParser(new_text, words[0])
|
348
|
+
|
349
|
+
parser.coding()
|
350
|
+
current_word = parser.current_word()
|
351
|
+
|
352
|
+
if len(new_text.strip()) == 0 or new_text.strip() == "/":
|
353
|
+
for command in parser.get_sub_commands():
|
354
|
+
if command.startswith(current_word):
|
355
|
+
yield Completion(command, start_position=-len(current_word))
|
356
|
+
|
357
|
+
all_tags = parser.tags
|
358
|
+
|
359
|
+
if current_word.startswith("@"):
|
360
|
+
name = current_word[1:]
|
361
|
+
target_set = set()
|
362
|
+
|
363
|
+
for file_name in self.current_file_names:
|
364
|
+
base_file_name = os.path.basename(file_name)
|
365
|
+
if name in base_file_name:
|
366
|
+
target_set.add(base_file_name)
|
367
|
+
path_parts = file_name.split(os.sep)
|
368
|
+
display_name = (
|
369
|
+
os.sep.join(path_parts[-3:])
|
370
|
+
if len(path_parts) > 3
|
371
|
+
else file_name
|
372
|
+
)
|
373
|
+
relative_path = os.path.relpath(
|
374
|
+
file_name, self.file_system_model.project_root)
|
375
|
+
yield Completion(
|
376
|
+
relative_path,
|
377
|
+
start_position=-len(name),
|
378
|
+
display=f"{display_name} (in active files)",
|
379
|
+
)
|
380
|
+
|
381
|
+
for file_name in self.all_file_names:
|
382
|
+
if file_name.startswith(name) and file_name not in target_set:
|
383
|
+
target_set.add(file_name)
|
384
|
+
|
385
|
+
path_parts = file_name.split(os.sep)
|
386
|
+
display_name = (
|
387
|
+
os.sep.join(path_parts[-3:])
|
388
|
+
if len(path_parts) > 3
|
389
|
+
else file_name
|
390
|
+
)
|
391
|
+
relative_path = os.path.relpath(
|
392
|
+
file_name, self.file_system_model.project_root)
|
393
|
+
|
394
|
+
yield Completion(
|
395
|
+
relative_path,
|
396
|
+
start_position=-len(name),
|
397
|
+
display=f"{display_name}",
|
398
|
+
)
|
399
|
+
|
400
|
+
for file_name in self.all_files:
|
401
|
+
if name in file_name and file_name not in target_set:
|
402
|
+
path_parts = file_name.split(os.sep)
|
403
|
+
display_name = (
|
404
|
+
os.sep.join(path_parts[-3:])
|
405
|
+
if len(path_parts) > 3
|
406
|
+
else file_name
|
407
|
+
)
|
408
|
+
relative_path = os.path.relpath(
|
409
|
+
file_name, self.file_system_model.project_root)
|
410
|
+
yield Completion(
|
411
|
+
relative_path,
|
412
|
+
start_position=-len(name),
|
413
|
+
display=f"{display_name}",
|
414
|
+
)
|
415
|
+
|
416
|
+
if current_word.startswith("@@"):
|
417
|
+
name = current_word[2:]
|
418
|
+
for symbol in self.symbol_list:
|
419
|
+
if name in symbol.symbol_name:
|
420
|
+
file_name = symbol.file_name
|
421
|
+
path_parts = file_name.split(os.sep)
|
422
|
+
display_name = (
|
423
|
+
os.sep.join(path_parts[-3:])
|
424
|
+
if len(path_parts) > 3
|
425
|
+
else symbol.symbol_name
|
426
|
+
)
|
427
|
+
relative_path = os.path.relpath(
|
428
|
+
file_name, self.file_system_model.project_root)
|
429
|
+
yield Completion(
|
430
|
+
f"{symbol.symbol_name}(location: {relative_path})",
|
431
|
+
start_position=-len(name),
|
432
|
+
display=f"{symbol.symbol_name} ({display_name}/{symbol.symbol_type})",
|
433
|
+
)
|
434
|
+
|
435
|
+
tags = [tag for tag in parser.tags]
|
436
|
+
|
437
|
+
if current_word.startswith("<"):
|
438
|
+
name = current_word[1:]
|
439
|
+
for tag in ["<img>", "</img>"]:
|
440
|
+
if all_tags and all_tags[-1].start_tag == "<img>":
|
441
|
+
if tag.startswith(name):
|
442
|
+
yield Completion(
|
443
|
+
"</img>", start_position=-len(current_word)
|
444
|
+
)
|
445
|
+
elif tag.startswith(name):
|
446
|
+
yield Completion(tag, start_position=-len(current_word))
|
447
|
+
|
448
|
+
if tags and tags[-1].start_tag == "<img>" and tags[-1].end_tag == "":
|
449
|
+
raw_file_name = tags[0].content
|
450
|
+
file_name = raw_file_name.strip()
|
451
|
+
parent_dir = os.path.dirname(file_name)
|
452
|
+
file_basename = os.path.basename(file_name)
|
453
|
+
search_dir = parent_dir if parent_dir else "."
|
454
|
+
for root, dirs, files in os.walk(search_dir):
|
455
|
+
# 只处理直接子目录
|
456
|
+
if root != search_dir:
|
457
|
+
continue
|
458
|
+
|
459
|
+
# 补全子目录
|
460
|
+
for _dir in dirs:
|
461
|
+
full_path = os.path.join(root, _dir)
|
462
|
+
if full_path.startswith(file_name):
|
463
|
+
relative_path = os.path.relpath(full_path, search_dir)
|
464
|
+
yield Completion(relative_path, start_position=-len(file_basename))
|
465
|
+
|
466
|
+
# 补全文件
|
467
|
+
for file in files:
|
468
|
+
if file.lower().endswith(
|
469
|
+
image_extensions
|
470
|
+
) and file.startswith(file_basename):
|
471
|
+
full_path = os.path.join(root, file)
|
472
|
+
relative_path = os.path.relpath(full_path, search_dir)
|
473
|
+
yield Completion(
|
474
|
+
relative_path,
|
475
|
+
start_position=-len(file_basename),
|
476
|
+
)
|
477
|
+
|
478
|
+
# 只处理一层子目录,然后退出循环
|
479
|
+
break
|
480
|
+
|
481
|
+
elif words[0] == "/remove_files":
|
482
|
+
new_words = text[len("/remove_files"):].strip().split(",")
|
483
|
+
|
484
|
+
is_at_space = text[-1] == " "
|
485
|
+
last_word = new_words[-2] if len(new_words) > 1 else ""
|
486
|
+
current_word = new_words[-1] if new_words else ""
|
487
|
+
|
488
|
+
if is_at_space:
|
489
|
+
last_word = current_word
|
490
|
+
current_word = ""
|
491
|
+
|
492
|
+
# /remove_files /all [cursor] or /remove_files /all p[cursor]
|
493
|
+
if not last_word and not current_word:
|
494
|
+
if "/all".startswith(current_word):
|
495
|
+
yield Completion("/all", start_position=-len(current_word))
|
496
|
+
for file_name in self.current_file_names:
|
497
|
+
yield Completion(file_name, start_position=-len(current_word))
|
498
|
+
|
499
|
+
# /remove_files /a[cursor] or /remove_files p[cursor]
|
500
|
+
if current_word:
|
501
|
+
if "/all".startswith(current_word):
|
502
|
+
yield Completion("/all", start_position=-len(current_word))
|
503
|
+
for file_name in self.current_file_names:
|
504
|
+
if current_word and current_word in file_name:
|
505
|
+
yield Completion(
|
506
|
+
file_name, start_position=-len(current_word)
|
507
|
+
)
|
508
|
+
|
509
|
+
elif words[0] == "/exclude_dirs":
|
510
|
+
new_words = text[len("/exclude_dirs"):].strip().split(",")
|
511
|
+
current_word = new_words[-1]
|
512
|
+
|
513
|
+
for file_name in self.all_dir_names:
|
514
|
+
if current_word and current_word in file_name:
|
515
|
+
yield Completion(file_name, start_position=-len(current_word))
|
516
|
+
|
517
|
+
elif words[0] == "/exclude_files":
|
518
|
+
new_text = text[len("/exclude_files"):]
|
519
|
+
parser = CommandTextParser(new_text, words[0])
|
520
|
+
parser.add_files()
|
521
|
+
current_word = parser.current_word()
|
522
|
+
for command in parser.get_sub_commands():
|
523
|
+
if command.startswith(current_word):
|
524
|
+
yield Completion(command, start_position=-len(current_word))
|
525
|
+
|
526
|
+
elif words[0] == "/models":
|
527
|
+
new_text = text[len("/models"):]
|
528
|
+
parser = CommandTextParser(new_text, words[0])
|
529
|
+
parser.add_files()
|
530
|
+
current_word = parser.current_word()
|
531
|
+
for command in parser.get_sub_commands():
|
532
|
+
if command.startswith(current_word):
|
533
|
+
yield Completion(command, start_position=-len(current_word))
|
534
|
+
|
535
|
+
elif words[0] == "/help":
|
536
|
+
new_text = text[len("/help"):]
|
537
|
+
parser = CommandTextParser(new_text, words[0])
|
538
|
+
parser.add_files()
|
539
|
+
current_word = parser.current_word()
|
540
|
+
for command in parser.get_sub_commands():
|
541
|
+
if command.startswith(current_word):
|
542
|
+
yield Completion(command, start_position=-len(current_word))
|
543
|
+
|
544
|
+
elif words[0] == "/rules":
|
545
|
+
new_text = text[len("/rules"):]
|
546
|
+
parser = CommandTextParser(new_text, words[0])
|
547
|
+
parser.add_files()
|
548
|
+
current_word = parser.current_word()
|
549
|
+
for command in parser.get_sub_commands():
|
550
|
+
if command.startswith(current_word):
|
551
|
+
yield Completion(command, start_position=-len(current_word))
|
552
|
+
|
553
|
+
elif words[0] == "/conf":
|
554
|
+
new_words = text[len("/conf"):].strip().split()
|
555
|
+
is_at_space = text[-1] == " "
|
556
|
+
last_word = new_words[-2] if len(new_words) > 1 else ""
|
557
|
+
current_word = new_words[-1] if new_words else ""
|
558
|
+
completions = []
|
559
|
+
|
560
|
+
if is_at_space:
|
561
|
+
last_word = current_word
|
562
|
+
current_word = ""
|
563
|
+
|
564
|
+
# /conf /drop [curor] or /conf /drop p[cursor]
|
565
|
+
if last_word == "/drop":
|
566
|
+
completions = [
|
567
|
+
field_name
|
568
|
+
for field_name in self.memory_model.get_memory_func().get("conf", {}).keys()
|
569
|
+
if field_name.startswith(current_word)
|
570
|
+
]
|
571
|
+
# /conf [curosr]
|
572
|
+
elif not last_word and not current_word:
|
573
|
+
completions = [
|
574
|
+
"/drop"] if "/drop".startswith(current_word) else []
|
575
|
+
completions += [
|
576
|
+
field_name + ":"
|
577
|
+
for field_name in AutoCoderArgs.model_fields.keys()
|
578
|
+
if field_name.startswith(current_word)
|
579
|
+
]
|
580
|
+
# /conf p[cursor]
|
581
|
+
elif not last_word and current_word:
|
582
|
+
completions = [
|
583
|
+
"/drop"] if "/drop".startswith(current_word) else []
|
584
|
+
completions += [
|
585
|
+
field_name + ":"
|
586
|
+
for field_name in AutoCoderArgs.model_fields.keys()
|
587
|
+
if field_name.startswith(current_word)
|
588
|
+
]
|
589
|
+
|
590
|
+
for completion in completions:
|
591
|
+
yield Completion(completion, start_position=-len(current_word))
|
592
|
+
|
593
|
+
else:
|
594
|
+
for command in self.commands:
|
595
|
+
if command.startswith(text):
|
596
|
+
yield Completion(command, start_position=-len(text))
|
597
|
+
else:
|
598
|
+
for command in self.commands:
|
599
|
+
if command.startswith(text):
|
600
|
+
yield Completion(command, start_position=-len(text))
|
601
|
+
|
602
|
+
def update_current_files(self, files):
|
603
|
+
self.current_file_names = [f for f in files]
|
604
|
+
# self.current_file_names = self.memory_model.get_memory_func().get("current_files", {}).get("files", [])
|
605
|
+
|
606
|
+
def refresh_files(self):
|
607
|
+
# self.all_file_names = get_all_file_names_in_project()
|
608
|
+
# self.all_files = get_all_file_in_project()
|
609
|
+
# self.all_dir_names = get_all_dir_names_in_project()
|
610
|
+
# self.all_files_with_dot = get_all_file_in_project_with_dot()
|
611
|
+
# self.symbol_list = get_symbol_list()
|
612
|
+
self.all_file_names = self.file_system_model.get_all_file_names_in_project()
|
613
|
+
self.all_files = self.file_system_model.get_all_file_in_project()
|
614
|
+
self.all_dir_names = self.file_system_model.get_all_dir_names_in_project()
|
615
|
+
self.all_files_with_dot = self.file_system_model.get_all_file_in_project_with_dot()
|
616
|
+
self.symbol_list = self.file_system_model.get_symbol_list()
|