auto-coder 0.1.259__py3-none-any.whl → 0.1.261__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.
Potentially problematic release.
This version of auto-coder might be problematic. Click here for more details.
- {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/METADATA +1 -1
- {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/RECORD +36 -27
- autocoder/agent/auto_review_commit.py +51 -24
- autocoder/auto_coder.py +24 -1
- autocoder/chat_auto_coder.py +377 -399
- autocoder/chat_auto_coder_lang.py +20 -0
- autocoder/commands/__init__.py +0 -0
- autocoder/commands/auto_command.py +1174 -0
- autocoder/commands/tools.py +533 -0
- autocoder/common/__init__.py +8 -0
- autocoder/common/auto_coder_lang.py +61 -8
- autocoder/common/auto_configure.py +304 -0
- autocoder/common/code_auto_merge.py +2 -2
- autocoder/common/code_auto_merge_diff.py +2 -2
- autocoder/common/code_auto_merge_editblock.py +2 -2
- autocoder/common/code_auto_merge_strict_diff.py +2 -2
- autocoder/common/code_modification_ranker.py +8 -7
- autocoder/common/command_completer.py +557 -0
- autocoder/common/conf_validator.py +245 -0
- autocoder/common/conversation_pruner.py +131 -0
- autocoder/common/git_utils.py +82 -1
- autocoder/common/index_import_export.py +101 -0
- autocoder/common/result_manager.py +115 -0
- autocoder/common/shells.py +22 -6
- autocoder/common/utils_code_auto_generate.py +2 -2
- autocoder/dispacher/actions/action.py +45 -4
- autocoder/dispacher/actions/plugins/action_regex_project.py +13 -1
- autocoder/index/filter/quick_filter.py +22 -7
- autocoder/utils/auto_coder_utils/chat_stream_out.py +13 -6
- autocoder/utils/project_structure.py +15 -0
- autocoder/utils/thread_utils.py +4 -0
- autocoder/version.py +1 -1
- {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
from prompt_toolkit.completion import Completer, Completion, CompleteEvent
|
|
2
2
|
from prompt_toolkit.document import Document
|
|
3
3
|
import pydantic
|
|
4
|
+
from typing import Callable,Dict,Any
|
|
5
|
+
from pydantic import BaseModel,SkipValidation
|
|
6
|
+
from autocoder.common import AutoCoderArgs
|
|
4
7
|
import os
|
|
5
8
|
|
|
6
9
|
COMMANDS = {
|
|
@@ -38,6 +41,8 @@ COMMANDS = {
|
|
|
38
41
|
"/speed-test": "",
|
|
39
42
|
"/input_price": "",
|
|
40
43
|
"/output_price": "",
|
|
44
|
+
},
|
|
45
|
+
"/auto": {
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
48
|
|
|
@@ -48,6 +53,24 @@ class Tag(pydantic.BaseModel):
|
|
|
48
53
|
end_tag: str
|
|
49
54
|
|
|
50
55
|
|
|
56
|
+
class FileSystemModel(pydantic.BaseModel):
|
|
57
|
+
project_root: str
|
|
58
|
+
get_all_file_names_in_project: SkipValidation[Callable]
|
|
59
|
+
get_all_file_in_project: SkipValidation[Callable]
|
|
60
|
+
get_all_dir_names_in_project: SkipValidation[Callable]
|
|
61
|
+
get_all_file_in_project_with_dot: SkipValidation[Callable]
|
|
62
|
+
get_symbol_list: SkipValidation[Callable]
|
|
63
|
+
|
|
64
|
+
class MemoryConfig(BaseModel):
|
|
65
|
+
"""
|
|
66
|
+
A model to encapsulate memory configuration and operations.
|
|
67
|
+
"""
|
|
68
|
+
memory: Dict[str, Any]
|
|
69
|
+
save_memory_func: SkipValidation[Callable]
|
|
70
|
+
|
|
71
|
+
class Config:
|
|
72
|
+
arbitrary_types_allowed = True
|
|
73
|
+
|
|
51
74
|
class CommandTextParser:
|
|
52
75
|
def __init__(self, text: str, command: str):
|
|
53
76
|
self.text = text
|
|
@@ -322,3 +345,537 @@ class CommandTextParser:
|
|
|
322
345
|
self.consume_tag()
|
|
323
346
|
else:
|
|
324
347
|
self.consume_coding_value()
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
class CommandCompleter(Completer):
|
|
351
|
+
def __init__(self, commands, file_system_model: FileSystemModel, memory_model: MemoryConfig):
|
|
352
|
+
self.commands = commands
|
|
353
|
+
self.file_system_model = file_system_model
|
|
354
|
+
self.memory_model = memory_model
|
|
355
|
+
self.all_file_names = file_system_model.get_all_file_names_in_project()
|
|
356
|
+
self.all_files = file_system_model.get_all_file_in_project()
|
|
357
|
+
self.all_dir_names = file_system_model.get_all_dir_names_in_project()
|
|
358
|
+
self.all_files_with_dot = file_system_model.get_all_file_in_project_with_dot()
|
|
359
|
+
self.symbol_list = file_system_model.get_symbol_list()
|
|
360
|
+
self.current_file_names = []
|
|
361
|
+
|
|
362
|
+
def get_completions(self, document, complete_event):
|
|
363
|
+
text = document.text_before_cursor
|
|
364
|
+
words = text.split()
|
|
365
|
+
|
|
366
|
+
if len(words) > 0:
|
|
367
|
+
if words[0] == "/mode":
|
|
368
|
+
left_word = text[len("/mode"):]
|
|
369
|
+
for mode in ["normal", "auto_detect", "voice_input"]:
|
|
370
|
+
if mode.startswith(left_word.strip()):
|
|
371
|
+
yield Completion(mode, start_position=-len(left_word.strip()))
|
|
372
|
+
|
|
373
|
+
if words[0] == "/add_files":
|
|
374
|
+
new_text = text[len("/add_files"):]
|
|
375
|
+
parser = CommandTextParser(new_text, words[0])
|
|
376
|
+
parser.add_files()
|
|
377
|
+
current_word = parser.current_word()
|
|
378
|
+
|
|
379
|
+
if parser.last_sub_command() == "/refresh":
|
|
380
|
+
return
|
|
381
|
+
|
|
382
|
+
for command in parser.get_sub_commands():
|
|
383
|
+
if command.startswith(current_word):
|
|
384
|
+
yield Completion(command, start_position=-len(current_word))
|
|
385
|
+
|
|
386
|
+
if parser.first_sub_command() == "/group" and (
|
|
387
|
+
parser.last_sub_command() == "/group"
|
|
388
|
+
or parser.last_sub_command() == "/drop"
|
|
389
|
+
):
|
|
390
|
+
group_names = self.memory_model.memory["current_files"]["groups"].keys()
|
|
391
|
+
if "," in current_word:
|
|
392
|
+
current_word = current_word.split(",")[-1]
|
|
393
|
+
|
|
394
|
+
for group_name in group_names:
|
|
395
|
+
if group_name.startswith(current_word):
|
|
396
|
+
yield Completion(
|
|
397
|
+
group_name, start_position=-len(current_word)
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
if parser.first_sub_command() != "/group":
|
|
401
|
+
if current_word and current_word.startswith("."):
|
|
402
|
+
for file_name in self.all_files_with_dot:
|
|
403
|
+
if file_name.startswith(current_word):
|
|
404
|
+
yield Completion(
|
|
405
|
+
file_name, start_position=-
|
|
406
|
+
len(current_word)
|
|
407
|
+
)
|
|
408
|
+
else:
|
|
409
|
+
for file_name in self.all_file_names:
|
|
410
|
+
if file_name.startswith(current_word):
|
|
411
|
+
yield Completion(
|
|
412
|
+
file_name, start_position=-
|
|
413
|
+
len(current_word)
|
|
414
|
+
)
|
|
415
|
+
for file_name in self.all_files:
|
|
416
|
+
if current_word and current_word in file_name:
|
|
417
|
+
yield Completion(
|
|
418
|
+
file_name, start_position=-
|
|
419
|
+
len(current_word)
|
|
420
|
+
)
|
|
421
|
+
elif words[0] == "/remove_files":
|
|
422
|
+
new_words = text[len("/remove_files"):].strip().split(",")
|
|
423
|
+
|
|
424
|
+
is_at_space = text[-1] == " "
|
|
425
|
+
last_word = new_words[-2] if len(new_words) > 1 else ""
|
|
426
|
+
current_word = new_words[-1] if new_words else ""
|
|
427
|
+
|
|
428
|
+
if is_at_space:
|
|
429
|
+
last_word = current_word
|
|
430
|
+
current_word = ""
|
|
431
|
+
|
|
432
|
+
# /remove_files /all [cursor] or /remove_files /all p[cursor]
|
|
433
|
+
if not last_word and not current_word:
|
|
434
|
+
if "/all".startswith(current_word):
|
|
435
|
+
yield Completion("/all", start_position=-len(current_word))
|
|
436
|
+
for file_name in self.current_file_names:
|
|
437
|
+
yield Completion(file_name, start_position=-len(current_word))
|
|
438
|
+
|
|
439
|
+
# /remove_files /a[cursor] or /remove_files p[cursor]
|
|
440
|
+
if current_word:
|
|
441
|
+
if "/all".startswith(current_word):
|
|
442
|
+
yield Completion("/all", start_position=-len(current_word))
|
|
443
|
+
for file_name in self.current_file_names:
|
|
444
|
+
if current_word and current_word in file_name:
|
|
445
|
+
yield Completion(
|
|
446
|
+
file_name, start_position=-len(current_word)
|
|
447
|
+
)
|
|
448
|
+
elif words[0] == "/exclude_dirs":
|
|
449
|
+
new_words = text[len("/exclude_dirs"):].strip().split(",")
|
|
450
|
+
current_word = new_words[-1]
|
|
451
|
+
|
|
452
|
+
for file_name in self.all_dir_names:
|
|
453
|
+
if current_word and current_word in file_name:
|
|
454
|
+
yield Completion(file_name, start_position=-len(current_word))
|
|
455
|
+
|
|
456
|
+
elif words[0] == "/lib":
|
|
457
|
+
new_text = text[len("/lib"):]
|
|
458
|
+
parser = CommandTextParser(new_text, words[0])
|
|
459
|
+
parser.lib()
|
|
460
|
+
current_word = parser.current_word()
|
|
461
|
+
|
|
462
|
+
for command in parser.get_sub_commands():
|
|
463
|
+
if command.startswith(current_word):
|
|
464
|
+
yield Completion(command, start_position=-len(current_word))
|
|
465
|
+
|
|
466
|
+
if parser.last_sub_command() in ["/add", "/remove", "/get"]:
|
|
467
|
+
for lib_name in self.memory_model.memory.get("libs", {}).keys():
|
|
468
|
+
if lib_name.startswith(current_word):
|
|
469
|
+
yield Completion(
|
|
470
|
+
lib_name, start_position=-len(current_word)
|
|
471
|
+
)
|
|
472
|
+
elif words[0] == "/mcp":
|
|
473
|
+
new_text = text[len("/mcp"):]
|
|
474
|
+
parser = CommandTextParser(new_text, words[0])
|
|
475
|
+
parser.lib()
|
|
476
|
+
current_word = parser.current_word()
|
|
477
|
+
for command in parser.get_sub_commands():
|
|
478
|
+
if command.startswith(current_word):
|
|
479
|
+
yield Completion(command, start_position=-len(current_word))
|
|
480
|
+
elif words[0] == "/models":
|
|
481
|
+
new_text = text[len("/models"):]
|
|
482
|
+
parser = CommandTextParser(new_text, words[0])
|
|
483
|
+
parser.lib()
|
|
484
|
+
current_word = parser.current_word()
|
|
485
|
+
for command in parser.get_sub_commands():
|
|
486
|
+
if command.startswith(current_word):
|
|
487
|
+
yield Completion(command, start_position=-len(current_word))
|
|
488
|
+
|
|
489
|
+
elif words[0] == "/conf":
|
|
490
|
+
new_words = text[len("/conf"):].strip().split()
|
|
491
|
+
is_at_space = text[-1] == " "
|
|
492
|
+
last_word = new_words[-2] if len(new_words) > 1 else ""
|
|
493
|
+
current_word = new_words[-1] if new_words else ""
|
|
494
|
+
completions = []
|
|
495
|
+
|
|
496
|
+
if is_at_space:
|
|
497
|
+
last_word = current_word
|
|
498
|
+
current_word = ""
|
|
499
|
+
|
|
500
|
+
# /conf /drop [curor] or /conf /drop p[cursor]
|
|
501
|
+
if last_word == "/drop":
|
|
502
|
+
completions = [
|
|
503
|
+
field_name
|
|
504
|
+
for field_name in self.memory_model.memory["conf"].keys()
|
|
505
|
+
if field_name.startswith(current_word)
|
|
506
|
+
]
|
|
507
|
+
# /conf [curosr]
|
|
508
|
+
elif not last_word and not current_word:
|
|
509
|
+
completions = [
|
|
510
|
+
"/drop"] if "/drop".startswith(current_word) else []
|
|
511
|
+
completions += [
|
|
512
|
+
field_name + ":"
|
|
513
|
+
for field_name in AutoCoderArgs.model_fields.keys()
|
|
514
|
+
if field_name.startswith(current_word)
|
|
515
|
+
]
|
|
516
|
+
# /conf p[cursor]
|
|
517
|
+
elif not last_word and current_word:
|
|
518
|
+
completions = [
|
|
519
|
+
"/drop"] if "/drop".startswith(current_word) else []
|
|
520
|
+
completions += [
|
|
521
|
+
field_name + ":"
|
|
522
|
+
for field_name in AutoCoderArgs.model_fields.keys()
|
|
523
|
+
if field_name.startswith(current_word)
|
|
524
|
+
]
|
|
525
|
+
|
|
526
|
+
for completion in completions:
|
|
527
|
+
yield Completion(completion, start_position=-len(current_word))
|
|
528
|
+
elif words[0] in ["/chat", "/coding","/auto"]:
|
|
529
|
+
image_extensions = (
|
|
530
|
+
".png",
|
|
531
|
+
".jpg",
|
|
532
|
+
".jpeg",
|
|
533
|
+
".gif",
|
|
534
|
+
".bmp",
|
|
535
|
+
".tiff",
|
|
536
|
+
".tif",
|
|
537
|
+
".webp",
|
|
538
|
+
".svg",
|
|
539
|
+
".ico",
|
|
540
|
+
".heic",
|
|
541
|
+
".heif",
|
|
542
|
+
".raw",
|
|
543
|
+
".cr2",
|
|
544
|
+
".nef",
|
|
545
|
+
".arw",
|
|
546
|
+
".dng",
|
|
547
|
+
".orf",
|
|
548
|
+
".rw2",
|
|
549
|
+
".pef",
|
|
550
|
+
".srw",
|
|
551
|
+
".eps",
|
|
552
|
+
".ai",
|
|
553
|
+
".psd",
|
|
554
|
+
".xcf",
|
|
555
|
+
)
|
|
556
|
+
new_text = text[len(words[0]):]
|
|
557
|
+
parser = CommandTextParser(new_text, words[0])
|
|
558
|
+
|
|
559
|
+
parser.coding()
|
|
560
|
+
current_word = parser.current_word()
|
|
561
|
+
|
|
562
|
+
if len(new_text.strip()) == 0 or new_text.strip() == "/":
|
|
563
|
+
for command in parser.get_sub_commands():
|
|
564
|
+
if command.startswith(current_word):
|
|
565
|
+
yield Completion(command, start_position=-len(current_word))
|
|
566
|
+
|
|
567
|
+
all_tags = parser.tags
|
|
568
|
+
|
|
569
|
+
if current_word.startswith("@"):
|
|
570
|
+
name = current_word[1:]
|
|
571
|
+
target_set = set()
|
|
572
|
+
|
|
573
|
+
for file_name in self.current_file_names:
|
|
574
|
+
base_file_name = os.path.basename(file_name)
|
|
575
|
+
if name in base_file_name:
|
|
576
|
+
target_set.add(base_file_name)
|
|
577
|
+
path_parts = file_name.split(os.sep)
|
|
578
|
+
display_name = (
|
|
579
|
+
os.sep.join(path_parts[-3:])
|
|
580
|
+
if len(path_parts) > 3
|
|
581
|
+
else file_name
|
|
582
|
+
)
|
|
583
|
+
relative_path = os.path.relpath(
|
|
584
|
+
file_name, self.file_system_model.project_root)
|
|
585
|
+
yield Completion(
|
|
586
|
+
relative_path,
|
|
587
|
+
start_position=-len(name),
|
|
588
|
+
display=f"{display_name} (in active files)",
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
for file_name in self.all_file_names:
|
|
592
|
+
if file_name.startswith(name) and file_name not in target_set:
|
|
593
|
+
target_set.add(file_name)
|
|
594
|
+
|
|
595
|
+
path_parts = file_name.split(os.sep)
|
|
596
|
+
display_name = (
|
|
597
|
+
os.sep.join(path_parts[-3:])
|
|
598
|
+
if len(path_parts) > 3
|
|
599
|
+
else file_name
|
|
600
|
+
)
|
|
601
|
+
relative_path = os.path.relpath(
|
|
602
|
+
file_name, self.file_system_model.project_root)
|
|
603
|
+
|
|
604
|
+
yield Completion(
|
|
605
|
+
relative_path,
|
|
606
|
+
start_position=-len(name),
|
|
607
|
+
display=f"{display_name}",
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
for file_name in self.all_files:
|
|
611
|
+
if name in file_name and file_name not in target_set:
|
|
612
|
+
path_parts = file_name.split(os.sep)
|
|
613
|
+
display_name = (
|
|
614
|
+
os.sep.join(path_parts[-3:])
|
|
615
|
+
if len(path_parts) > 3
|
|
616
|
+
else file_name
|
|
617
|
+
)
|
|
618
|
+
relative_path = os.path.relpath(
|
|
619
|
+
file_name, self.file_system_model.project_root)
|
|
620
|
+
yield Completion(
|
|
621
|
+
relative_path,
|
|
622
|
+
start_position=-len(name),
|
|
623
|
+
display=f"{display_name}",
|
|
624
|
+
)
|
|
625
|
+
|
|
626
|
+
if current_word.startswith("@@"):
|
|
627
|
+
name = current_word[2:]
|
|
628
|
+
for symbol in self.symbol_list:
|
|
629
|
+
if name in symbol.symbol_name:
|
|
630
|
+
file_name = symbol.file_name
|
|
631
|
+
path_parts = file_name.split(os.sep)
|
|
632
|
+
display_name = (
|
|
633
|
+
os.sep.join(path_parts[-3:])
|
|
634
|
+
if len(path_parts) > 3
|
|
635
|
+
else symbol.symbol_name
|
|
636
|
+
)
|
|
637
|
+
relative_path = os.path.relpath(
|
|
638
|
+
file_name, self.file_system_model.project_root)
|
|
639
|
+
yield Completion(
|
|
640
|
+
f"{symbol.symbol_name}(location: {relative_path})",
|
|
641
|
+
start_position=-len(name),
|
|
642
|
+
display=f"{symbol.symbol_name} ({display_name}/{symbol.symbol_type})",
|
|
643
|
+
)
|
|
644
|
+
|
|
645
|
+
tags = [tag for tag in parser.tags]
|
|
646
|
+
|
|
647
|
+
if current_word.startswith("<"):
|
|
648
|
+
name = current_word[1:]
|
|
649
|
+
for tag in ["<img>", "</img>"]:
|
|
650
|
+
if all_tags and all_tags[-1].start_tag == "<img>":
|
|
651
|
+
if tag.startswith(name):
|
|
652
|
+
yield Completion(
|
|
653
|
+
"</img>", start_position=-len(current_word)
|
|
654
|
+
)
|
|
655
|
+
elif tag.startswith(name):
|
|
656
|
+
yield Completion(tag, start_position=-len(current_word))
|
|
657
|
+
|
|
658
|
+
if tags and tags[-1].start_tag == "<img>" and tags[-1].end_tag == "":
|
|
659
|
+
raw_file_name = tags[0].content
|
|
660
|
+
file_name = raw_file_name.strip()
|
|
661
|
+
parent_dir = os.path.dirname(file_name)
|
|
662
|
+
file_basename = os.path.basename(file_name)
|
|
663
|
+
search_dir = parent_dir if parent_dir else "."
|
|
664
|
+
for root, dirs, files in os.walk(search_dir):
|
|
665
|
+
# 只处理直接子目录
|
|
666
|
+
if root != search_dir:
|
|
667
|
+
continue
|
|
668
|
+
|
|
669
|
+
# 补全子目录
|
|
670
|
+
for dir in dirs:
|
|
671
|
+
full_path = os.path.join(root, dir)
|
|
672
|
+
if full_path.startswith(file_name):
|
|
673
|
+
relative_path = os.path.relpath(
|
|
674
|
+
full_path, search_dir)
|
|
675
|
+
yield Completion(
|
|
676
|
+
relative_path,
|
|
677
|
+
start_position=-len(file_basename),
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
# 补全文件
|
|
681
|
+
for file in files:
|
|
682
|
+
if file.lower().endswith(
|
|
683
|
+
image_extensions
|
|
684
|
+
) and file.startswith(file_basename):
|
|
685
|
+
full_path = os.path.join(root, file)
|
|
686
|
+
relative_path = os.path.relpath(
|
|
687
|
+
full_path, search_dir)
|
|
688
|
+
yield Completion(
|
|
689
|
+
relative_path,
|
|
690
|
+
start_position=-len(file_basename),
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
# 只处理一层子目录,然后退出循环
|
|
694
|
+
break
|
|
695
|
+
|
|
696
|
+
elif not words[0].startswith("/"):
|
|
697
|
+
image_extensions = (
|
|
698
|
+
".png",
|
|
699
|
+
".jpg",
|
|
700
|
+
".jpeg",
|
|
701
|
+
".gif",
|
|
702
|
+
".bmp",
|
|
703
|
+
".tiff",
|
|
704
|
+
".tif",
|
|
705
|
+
".webp",
|
|
706
|
+
".svg",
|
|
707
|
+
".ico",
|
|
708
|
+
".heic",
|
|
709
|
+
".heif",
|
|
710
|
+
".raw",
|
|
711
|
+
".cr2",
|
|
712
|
+
".nef",
|
|
713
|
+
".arw",
|
|
714
|
+
".dng",
|
|
715
|
+
".orf",
|
|
716
|
+
".rw2",
|
|
717
|
+
".pef",
|
|
718
|
+
".srw",
|
|
719
|
+
".eps",
|
|
720
|
+
".ai",
|
|
721
|
+
".psd",
|
|
722
|
+
".xcf",
|
|
723
|
+
)
|
|
724
|
+
new_text = text
|
|
725
|
+
parser = CommandTextParser(new_text, "/auto")
|
|
726
|
+
|
|
727
|
+
parser.coding()
|
|
728
|
+
current_word = parser.current_word()
|
|
729
|
+
|
|
730
|
+
if len(new_text.strip()) == 0 or new_text.strip() == "/":
|
|
731
|
+
for command in parser.get_sub_commands():
|
|
732
|
+
if command.startswith(current_word):
|
|
733
|
+
yield Completion(command, start_position=-len(current_word))
|
|
734
|
+
|
|
735
|
+
all_tags = parser.tags
|
|
736
|
+
|
|
737
|
+
if current_word.startswith("@"):
|
|
738
|
+
name = current_word[1:]
|
|
739
|
+
target_set = set()
|
|
740
|
+
|
|
741
|
+
for file_name in self.current_file_names:
|
|
742
|
+
base_file_name = os.path.basename(file_name)
|
|
743
|
+
if name in base_file_name:
|
|
744
|
+
target_set.add(base_file_name)
|
|
745
|
+
path_parts = file_name.split(os.sep)
|
|
746
|
+
display_name = (
|
|
747
|
+
os.sep.join(path_parts[-3:])
|
|
748
|
+
if len(path_parts) > 3
|
|
749
|
+
else file_name
|
|
750
|
+
)
|
|
751
|
+
relative_path = os.path.relpath(
|
|
752
|
+
file_name, self.file_system_model.project_root)
|
|
753
|
+
yield Completion(
|
|
754
|
+
relative_path,
|
|
755
|
+
start_position=-len(name),
|
|
756
|
+
display=f"{display_name} (in active files)",
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
for file_name in self.all_file_names:
|
|
760
|
+
if file_name.startswith(name) and file_name not in target_set:
|
|
761
|
+
target_set.add(file_name)
|
|
762
|
+
|
|
763
|
+
path_parts = file_name.split(os.sep)
|
|
764
|
+
display_name = (
|
|
765
|
+
os.sep.join(path_parts[-3:])
|
|
766
|
+
if len(path_parts) > 3
|
|
767
|
+
else file_name
|
|
768
|
+
)
|
|
769
|
+
relative_path = os.path.relpath(
|
|
770
|
+
file_name, self.file_system_model.project_root)
|
|
771
|
+
|
|
772
|
+
yield Completion(
|
|
773
|
+
relative_path,
|
|
774
|
+
start_position=-len(name),
|
|
775
|
+
display=f"{display_name}",
|
|
776
|
+
)
|
|
777
|
+
|
|
778
|
+
for file_name in self.all_files:
|
|
779
|
+
if name in file_name and file_name not in target_set:
|
|
780
|
+
path_parts = file_name.split(os.sep)
|
|
781
|
+
display_name = (
|
|
782
|
+
os.sep.join(path_parts[-3:])
|
|
783
|
+
if len(path_parts) > 3
|
|
784
|
+
else file_name
|
|
785
|
+
)
|
|
786
|
+
relative_path = os.path.relpath(
|
|
787
|
+
file_name, self.file_system_model.project_root)
|
|
788
|
+
yield Completion(
|
|
789
|
+
relative_path,
|
|
790
|
+
start_position=-len(name),
|
|
791
|
+
display=f"{display_name}",
|
|
792
|
+
)
|
|
793
|
+
|
|
794
|
+
if current_word.startswith("@@"):
|
|
795
|
+
name = current_word[2:]
|
|
796
|
+
for symbol in self.symbol_list:
|
|
797
|
+
if name in symbol.symbol_name:
|
|
798
|
+
file_name = symbol.file_name
|
|
799
|
+
path_parts = file_name.split(os.sep)
|
|
800
|
+
display_name = (
|
|
801
|
+
os.sep.join(path_parts[-3:])
|
|
802
|
+
if len(path_parts) > 3
|
|
803
|
+
else symbol.symbol_name
|
|
804
|
+
)
|
|
805
|
+
relative_path = os.path.relpath(
|
|
806
|
+
file_name, self.file_system_model.project_root)
|
|
807
|
+
yield Completion(
|
|
808
|
+
f"{symbol.symbol_name}(location: {relative_path})",
|
|
809
|
+
start_position=-len(name),
|
|
810
|
+
display=f"{symbol.symbol_name} ({display_name}/{symbol.symbol_type})",
|
|
811
|
+
)
|
|
812
|
+
|
|
813
|
+
tags = [tag for tag in parser.tags]
|
|
814
|
+
|
|
815
|
+
if current_word.startswith("<"):
|
|
816
|
+
name = current_word[1:]
|
|
817
|
+
for tag in ["<img>", "</img>"]:
|
|
818
|
+
if all_tags and all_tags[-1].start_tag == "<img>":
|
|
819
|
+
if tag.startswith(name):
|
|
820
|
+
yield Completion(
|
|
821
|
+
"</img>", start_position=-len(current_word)
|
|
822
|
+
)
|
|
823
|
+
elif tag.startswith(name):
|
|
824
|
+
yield Completion(tag, start_position=-len(current_word))
|
|
825
|
+
|
|
826
|
+
if tags and tags[-1].start_tag == "<img>" and tags[-1].end_tag == "":
|
|
827
|
+
raw_file_name = tags[0].content
|
|
828
|
+
file_name = raw_file_name.strip()
|
|
829
|
+
parent_dir = os.path.dirname(file_name)
|
|
830
|
+
file_basename = os.path.basename(file_name)
|
|
831
|
+
search_dir = parent_dir if parent_dir else "."
|
|
832
|
+
for root, dirs, files in os.walk(search_dir):
|
|
833
|
+
# 只处理直接子目录
|
|
834
|
+
if root != search_dir:
|
|
835
|
+
continue
|
|
836
|
+
|
|
837
|
+
# 补全子目录
|
|
838
|
+
for dir in dirs:
|
|
839
|
+
full_path = os.path.join(root, dir)
|
|
840
|
+
if full_path.startswith(file_name):
|
|
841
|
+
relative_path = os.path.relpath(
|
|
842
|
+
full_path, search_dir)
|
|
843
|
+
yield Completion(
|
|
844
|
+
relative_path,
|
|
845
|
+
start_position=-len(file_basename),
|
|
846
|
+
)
|
|
847
|
+
|
|
848
|
+
# 补全文件
|
|
849
|
+
for file in files:
|
|
850
|
+
if file.lower().endswith(
|
|
851
|
+
image_extensions
|
|
852
|
+
) and file.startswith(file_basename):
|
|
853
|
+
full_path = os.path.join(root, file)
|
|
854
|
+
relative_path = os.path.relpath(
|
|
855
|
+
full_path, search_dir)
|
|
856
|
+
yield Completion(
|
|
857
|
+
relative_path,
|
|
858
|
+
start_position=-len(file_basename),
|
|
859
|
+
)
|
|
860
|
+
|
|
861
|
+
# 只处理一层子目录,然后退出循环
|
|
862
|
+
break
|
|
863
|
+
else:
|
|
864
|
+
for command in self.commands:
|
|
865
|
+
if command.startswith(text):
|
|
866
|
+
yield Completion(command, start_position=-len(text))
|
|
867
|
+
|
|
868
|
+
else:
|
|
869
|
+
for command in self.commands:
|
|
870
|
+
if command.startswith(text):
|
|
871
|
+
yield Completion(command, start_position=-len(text))
|
|
872
|
+
|
|
873
|
+
def update_current_files(self, files):
|
|
874
|
+
self.current_file_names = [f for f in files]
|
|
875
|
+
|
|
876
|
+
def refresh_files(self):
|
|
877
|
+
self.all_file_names = self.file_system_model.get_all_file_names_in_project()
|
|
878
|
+
self.all_files = self.file_system_model.get_all_file_in_project()
|
|
879
|
+
self.all_dir_names = self.file_system_model.get_all_dir_names_in_project()
|
|
880
|
+
self.all_files_with_dot = self.file_system_model.get_all_file_in_project_with_dot()
|
|
881
|
+
self.symbol_list = self.file_system_model.get_symbol_list()
|