diff --git a/argenta/app/autocompleter/__init__.py b/argenta/app/autocompleter/__init__.py index b8c1eb3..30bb9ce 100644 --- a/argenta/app/autocompleter/__init__.py +++ b/argenta/app/autocompleter/__init__.py @@ -1,4 +1,4 @@ -__all__ = ["Autocompleter"] +__all__ = ["AutoCompleter"] -from argenta.app.autocompleter.models import Autocompleter +from argenta.app.autocompleter.entity import AutoCompleter diff --git a/argenta/app/autocompleter/entity.py b/argenta/app/autocompleter/entity.py new file mode 100644 index 0000000..9d726c5 --- /dev/null +++ b/argenta/app/autocompleter/entity.py @@ -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)] + diff --git a/argenta/app/autocompleter/models.py b/argenta/app/autocompleter/models.py deleted file mode 100644 index 9e52bb7..0000000 --- a/argenta/app/autocompleter/models.py +++ /dev/null @@ -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() - - - - - diff --git a/argenta/app/defaults.py b/argenta/app/defaults.py index 6d29a58..d7968b4 100644 --- a/argenta/app/defaults.py +++ b/argenta/app/defaults.py @@ -5,4 +5,5 @@ from dataclasses import dataclass class PredeterminedMessages: USAGE = '[b dim]Usage[/b dim]: [i] <[green]flags[/green]>[/i]' HELP = '[b dim]Help[/b dim]: [i][/i] [b red]--help[/b red]' + AUTOCOMPLETE = '[b dim]Autocomplete[/b dim]: [i][/i] [bold]' diff --git a/argenta/app/models.py b/argenta/app/models.py index 4f9853c..eac169c 100644 --- a/argenta/app/models.py +++ b/argenta/app/models.py @@ -8,7 +8,7 @@ import re from argenta.command.models import Command, InputCommand from argenta.router import 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.command.exceptions import (UnprocessedInputFlagException, RepeatedInputFlagsException, @@ -34,7 +34,7 @@ class AppInit: dividing_line: StaticDividingLine | DynamicDividingLine = StaticDividingLine(), repeat_command_groups: bool = True, full_override_system_messages: bool = False, - autocompleter: Autocompleter = Autocompleter(), + autocompleter: AutoCompleter = AutoCompleter(), print_func: Callable[[str], None] = Console().print) -> None: self._prompt = prompt self._print_func = print_func @@ -45,6 +45,7 @@ class AppInit: self._ignore_command_register = ignore_command_register self._repeat_command_groups_description = repeat_command_groups self._full_override_system_messages = full_override_system_messages + self._autocompleter = autocompleter self._farewell_message = farewell_message self._initial_message = initial_message @@ -189,6 +190,7 @@ class AppSetups(AppValidators, AppPrinters): self._setup_system_router() self._validate_number_of_routers() self._validate_included_routers() + self._autocompleter.initial_setup() self._print_func(self._initial_message) @@ -223,6 +225,7 @@ class App(AppSetters, AppNonStandardHandlers, AppSetups): continue if self._is_exit_command(input_command): + self._autocompleter.exit_setup() return if self._is_unknown_command(input_command): diff --git a/argenta/router/entity.py b/argenta/router/entity.py index fa36cd8..c2d32e2 100644 --- a/argenta/router/entity.py +++ b/argenta/router/entity.py @@ -7,10 +7,10 @@ from argenta.router.command_handlers.entity import CommandHandlers from argenta.router.command_handler.entity import CommandHandler from argenta.command.flag.models import Flag, Flags, InputFlags from argenta.router.exceptions import (RepeatedFlagNameException, - TooManyTransferredArgsException, - RequiredArgumentNotPassedException, - IncorrectNumberOfHandlerArgsException, - TriggerCannotContainSpacesException) + TooManyTransferredArgsException, + RequiredArgumentNotPassedException, + IncorrectNumberOfHandlerArgsException, + TriggerCannotContainSpacesException) class Router: diff --git a/mock/.history b/mock/.history new file mode 100644 index 0000000..61d7296 --- /dev/null +++ b/mock/.history @@ -0,0 +1,11 @@ +test +q +P +q +S +S --host 125 +q +S --host 125.0123 +0 +U +q diff --git a/mock/local_test.py b/mock/local_test.py index d136cd3..feb0874 100644 --- a/mock/local_test.py +++ b/mock/local_test.py @@ -1,58 +1,32 @@ 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(): 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()}') +while True: try: - while True: - line = input('!("stop" to quit) Ввод текста: => ') - if line == 'stop': - 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() \ No newline at end of file + line = input('> ') + except EOFError: + break \ No newline at end of file diff --git a/mock/mock_app/handlers/routers.py b/mock/mock_app/handlers/routers.py index 1e91e3c..c2e9244 100644 --- a/mock/mock_app/handlers/routers.py +++ b/mock/mock_app/handlers/routers.py @@ -1,36 +1,32 @@ from rich.console import Console 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.router import Router from .handlers_implementation.help_command import help_command 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:') - console = Console() -flag = Flag('test') - @work_router.command(Command('0', 'Get Help')) def command_help(): 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): print(args.get_flag('test')) @settings_router.command(Command('U', 'Update WordMath')) def command_update(): - pass + print('eeeeeee') diff --git a/mock/mock_app/main.py b/mock/mock_app/main.py index b9966db..a33fd28 100644 --- a/mock/mock_app/main.py +++ b/mock/mock_app/main.py @@ -3,15 +3,19 @@ from mock.mock_app.handlers.routers import work_router, settings_router from argenta.app import App from argenta.app.defaults import PredeterminedMessages 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(): app.include_routers(work_router, settings_router) app.add_message_on_startup(PredeterminedMessages.USAGE) + app.add_message_on_startup(PredeterminedMessages.AUTOCOMPLETE) app.add_message_on_startup(PredeterminedMessages.HELP + '\n\n') app.start_polling()