final work on autocomplete

This commit is contained in:
2025-04-09 23:32:21 +03:00
parent 592d128ef6
commit 8b496aa782
10 changed files with 100 additions and 124 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
__all__ = ["Autocompleter"] __all__ = ["AutoCompleter"]
from argenta.app.autocompleter.models import Autocompleter from argenta.app.autocompleter.entity import AutoCompleter
+45
View File
@@ -0,0 +1,45 @@
import readline
import os
class AutoCompleter:
def __init__(self, history_filename: str = False, autocomplete_button: str = 'tab'):
self.history_filename = history_filename
self.autocomplete_button = autocomplete_button
self.matches = []
def complete(self, text, state):
matches = sorted(cmd for cmd in self.get_history_items() if cmd.startswith(text))
if len(matches) > 1:
common_prefix = matches[0]
for match in matches[1:]:
i = 0
while i < len(common_prefix) and i < len(match) and common_prefix[i] == match[i]:
i += 1
common_prefix = common_prefix[:i]
if state == 0:
readline.insert_text(common_prefix[len(text):])
readline.redisplay()
return None
elif len(matches) == 1:
return matches[0] if state == 0 else None
else:
return None
def initial_setup(self):
if self.history_filename:
if os.path.exists(self.history_filename):
readline.read_history_file(self.history_filename)
readline.set_completer(self.complete)
readline.set_completer_delims(readline.get_completer_delims().replace(' ', ''))
readline.parse_and_bind(f'{self.autocomplete_button}: complete')
def exit_setup(self):
if self.history_filename:
readline.write_history_file(self.history_filename)
@staticmethod
def get_history_items():
return [readline.get_history_item(i) for i in range(1, readline.get_current_history_length() + 1)]
-58
View File
@@ -1,58 +0,0 @@
import readline
import os
class Autocompleter:
def __init__(self, history_filename: str = './completer.hist', autocomplete_button: str = 'tab'):
self.history_filename = history_filename
self.autocomplete_button = autocomplete_button
self.matches = []
def complete(self, text, state):
if state == 0:
history_values = self.get_history_items()
if text:
self.matches = sorted(h for h in history_values if h and h.startswith(text))
else:
self.matches = []
try:
response = self.matches[state]
except IndexError:
response = None
return response
def initial_setup(self):
if os.path.exists(self.history_filename):
readline.read_history_file(self.history_filename)
readline.set_completer(self.complete)
readline.parse_and_bind(f'{self.autocomplete_button}: complete')
def write_command_to_history(self):
readline.write_history_file(self.history_filename)
@staticmethod
def get_history_items():
return [readline.get_history_item(i) for i in range(1, readline.get_current_history_length() + 1)]
def inputting():
autocompleter = Autocompleter()
autocompleter.initial_setup()
print(f'Максимальная длина файла истории: {readline.get_history_length()}')
print(f'История запуска:{autocompleter.get_history_items()}')
while True:
line = input('\n!("stop" to quit) Ввод текста: => ')
if line == 'stop':
print(f'Конец записи истории: {autocompleter.get_history_items()}')
autocompleter.write_command_to_history()
break
inputting()
+1
View File
@@ -5,4 +5,5 @@ from dataclasses import dataclass
class PredeterminedMessages: class PredeterminedMessages:
USAGE = '[b dim]Usage[/b dim]: [i]<command> <[green]flags[/green]>[/i]' USAGE = '[b dim]Usage[/b dim]: [i]<command> <[green]flags[/green]>[/i]'
HELP = '[b dim]Help[/b dim]: [i]<command>[/i] [b red]--help[/b red]' HELP = '[b dim]Help[/b dim]: [i]<command>[/i] [b red]--help[/b red]'
AUTOCOMPLETE = '[b dim]Autocomplete[/b dim]: [i]<part>[/i] [bold]<tab>'
+5 -2
View File
@@ -8,7 +8,7 @@ import re
from argenta.command.models import Command, InputCommand from argenta.command.models import Command, InputCommand
from argenta.router import Router from argenta.router import Router
from argenta.router.defaults import system_router from argenta.router.defaults import system_router
from argenta.app.autocompleter import Autocompleter from argenta.app.autocompleter import AutoCompleter
from argenta.app.dividing_line.models import StaticDividingLine, DynamicDividingLine from argenta.app.dividing_line.models import StaticDividingLine, DynamicDividingLine
from argenta.command.exceptions import (UnprocessedInputFlagException, from argenta.command.exceptions import (UnprocessedInputFlagException,
RepeatedInputFlagsException, RepeatedInputFlagsException,
@@ -34,7 +34,7 @@ class AppInit:
dividing_line: StaticDividingLine | DynamicDividingLine = StaticDividingLine(), dividing_line: StaticDividingLine | DynamicDividingLine = StaticDividingLine(),
repeat_command_groups: bool = True, repeat_command_groups: bool = True,
full_override_system_messages: bool = False, full_override_system_messages: bool = False,
autocompleter: Autocompleter = Autocompleter(), autocompleter: AutoCompleter = AutoCompleter(),
print_func: Callable[[str], None] = Console().print) -> None: print_func: Callable[[str], None] = Console().print) -> None:
self._prompt = prompt self._prompt = prompt
self._print_func = print_func self._print_func = print_func
@@ -45,6 +45,7 @@ class AppInit:
self._ignore_command_register = ignore_command_register self._ignore_command_register = ignore_command_register
self._repeat_command_groups_description = repeat_command_groups self._repeat_command_groups_description = repeat_command_groups
self._full_override_system_messages = full_override_system_messages self._full_override_system_messages = full_override_system_messages
self._autocompleter = autocompleter
self._farewell_message = farewell_message self._farewell_message = farewell_message
self._initial_message = initial_message self._initial_message = initial_message
@@ -189,6 +190,7 @@ class AppSetups(AppValidators, AppPrinters):
self._setup_system_router() self._setup_system_router()
self._validate_number_of_routers() self._validate_number_of_routers()
self._validate_included_routers() self._validate_included_routers()
self._autocompleter.initial_setup()
self._print_func(self._initial_message) self._print_func(self._initial_message)
@@ -223,6 +225,7 @@ class App(AppSetters, AppNonStandardHandlers, AppSetups):
continue continue
if self._is_exit_command(input_command): if self._is_exit_command(input_command):
self._autocompleter.exit_setup()
return return
if self._is_unknown_command(input_command): if self._is_unknown_command(input_command):
+11
View File
@@ -0,0 +1,11 @@
test
q
P
q
S
S --host 125
q
S --host 125.0123
0
U
q
+23 -49
View File
@@ -1,58 +1,32 @@
import readline import readline
import os
HISTORY_FILENAME = 'completer.hist'
def completer(text, state):
matches = sorted(cmd for cmd in get_history_items() if cmd.startswith(text))
if len(matches) > 1:
common_prefix = matches[0]
for match in matches[1:]:
i = 0
while i < len(common_prefix) and i < len(match) and common_prefix[i] == match[i]:
i += 1
common_prefix = common_prefix[:i]
if state == 0:
readline.insert_text(common_prefix[len(text):])
readline.redisplay()
return None
elif len(matches) == 1:
return matches[0] if state == 0 else None
else:
return None
readline.set_completer(completer)
readline.parse_and_bind("tab: complete")
def get_history_items(): def get_history_items():
return [readline.get_history_item(i) for i in range(1, readline.get_current_history_length() + 1)] return [readline.get_history_item(i) for i in range(1, readline.get_current_history_length() + 1)]
class HistoryCompleter:
def __init__(self):
self.matches = []
return
def complete(self, text, state):
response = None
if state == 0:
history_values = get_history_items()
if text:
self.matches = sorted(h
for h in history_values
if h and h.startswith(text))
else:
self.matches = []
try:
response = self.matches[state]
except IndexError:
response = None
return response
def inputing():
if os.path.exists(HISTORY_FILENAME):
readline.read_history_file(HISTORY_FILENAME)
print(f'Максимальная длина файла истории: {readline.get_history_length()}')
print(f'История запуска:{get_history_items()}')
try:
while True: while True:
line = input('!("stop" to quit) Ввод текста: => ') try:
if line == 'stop': line = input('> ')
except EOFError:
break break
if line:
print(f'Добавление "{line}" в файл истории.')
finally:
print(f'Конец записи истории: {get_history_items()}')
readline.write_history_file(HISTORY_FILENAME)
# Регистрация класса 'HistoryCompleter'
readline.set_completer(HistoryCompleter().complete)
# Регистрация клавиши `tab` для автодополнения
readline.parse_and_bind('tab: complete')
# Запрос текста
inputing()
+3 -7
View File
@@ -1,36 +1,32 @@
from rich.console import Console from rich.console import Console
from argenta.command import Command from argenta.command import Command
from argenta.command.flag import Flags, InputFlags, Flag from argenta.command.flag import Flags, InputFlags
from argenta.command.flag.defaults import PredeterminedFlags from argenta.command.flag.defaults import PredeterminedFlags
from argenta.router import Router from argenta.router import Router
from .handlers_implementation.help_command import help_command from .handlers_implementation.help_command import help_command
work_router: Router = Router(title='Work points:') work_router: Router = Router(title='Work points:')
work_router.set_invalid_input_flag_handler(lambda flag: print(f'Invalid input flag: {flag.get_string_entity()} {flag.get_value() if flag.get_value() else ''}'))
settings_router: Router = Router(title='Settings points:') settings_router: Router = Router(title='Settings points:')
console = Console() console = Console()
flag = Flag('test')
@work_router.command(Command('0', 'Get Help')) @work_router.command(Command('0', 'Get Help'))
def command_help(): def command_help():
help_command() help_command()
@work_router.command(Command('S', 'Start Solving', Flags(PredeterminedFlags.HOST, PredeterminedFlags.PORT, flag))) @work_router.command(Command('S', 'Start Solving', Flags(PredeterminedFlags.HOST, PredeterminedFlags.PORT)))
def command_start_solving(args: InputFlags): def command_start_solving(args: InputFlags):
print(args.get_flag('test')) print(args.get_flag('test'))
@settings_router.command(Command('U', 'Update WordMath')) @settings_router.command(Command('U', 'Update WordMath'))
def command_update(): def command_update():
pass print('eeeeeee')
+5 -1
View File
@@ -3,15 +3,19 @@ from mock.mock_app.handlers.routers import work_router, settings_router
from argenta.app import App from argenta.app import App
from argenta.app.defaults import PredeterminedMessages from argenta.app.defaults import PredeterminedMessages
from argenta.app.dividing_line import DynamicDividingLine from argenta.app.dividing_line import DynamicDividingLine
from argenta.app.autocompleter import AutoCompleter
app: App = App(dividing_line=DynamicDividingLine()) autocompleter = AutoCompleter('./mock/.history')
app: App = App(dividing_line=DynamicDividingLine(),
autocompleter=autocompleter)
def main(): def main():
app.include_routers(work_router, settings_router) app.include_routers(work_router, settings_router)
app.add_message_on_startup(PredeterminedMessages.USAGE) app.add_message_on_startup(PredeterminedMessages.USAGE)
app.add_message_on_startup(PredeterminedMessages.AUTOCOMPLETE)
app.add_message_on_startup(PredeterminedMessages.HELP + '\n\n') app.add_message_on_startup(PredeterminedMessages.HELP + '\n\n')
app.start_polling() app.start_polling()