diff --git a/changelog.md b/changelog.md index 941f1610..1111072a 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,7 @@ Features * Add many CLI flags to startup tips. * Accept all special commands without trailing semicolons in multi-line mode. * Add prompt format strings for socket connections. +* Optionally defer auto-completions until a minimum number of characters is typed. Bug Fixes diff --git a/mycli/main.py b/mycli/main.py index 7bd1b38e..92173d04 100755 --- a/mycli/main.py +++ b/mycli/main.py @@ -31,11 +31,12 @@ import click from configobj import ConfigObj import keyring +from prompt_toolkit.application.current import get_app from prompt_toolkit.auto_suggest import AutoSuggestFromHistory from prompt_toolkit.completion import Completion, DynamicCompleter from prompt_toolkit.document import Document from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode -from prompt_toolkit.filters import HasFocus, IsDone +from prompt_toolkit.filters import Condition, HasFocus, IsDone from prompt_toolkit.formatted_text import ANSI, AnyFormattedText from prompt_toolkit.key_binding.bindings.named_commands import register as prompt_register from prompt_toolkit.key_binding.key_processor import KeyPressEvent @@ -84,6 +85,36 @@ SUPPORT_INFO = "Home: http://mycli.net\nBug tracker: https://github.com/dbcli/mycli/issues" DEFAULT_WIDTH = 80 DEFAULT_HEIGHT = 25 +MIN_COMPLETION_TRIGGER = 1 + + +@Condition +def complete_while_typing_filter() -> bool: + """Whether enough characters have been typed to trigger completion. + + Written in a verbose way, with a string slice, for efficiency.""" + if MIN_COMPLETION_TRIGGER <= 1: + return True + app = get_app() + text = app.current_buffer.text.lstrip() + text_len = len(text) + if text_len < MIN_COMPLETION_TRIGGER: + return False + last_word = text[-MIN_COMPLETION_TRIGGER:] + if len(last_word) == text_len: + return text_len >= MIN_COMPLETION_TRIGGER + if text[:6].lower() in ['source', r'\.']: + # Different word characters for paths; see comment below. + # In fact, it might be nice if paths had a different threshold. + return not bool(re.search(r'[\s!-,:-@\[-^\{\}-]', last_word)) + else: + # This is "whitespace and all punctuation except underscore and backtick" + # acting as word breaks, but it would be neat if we could complete differently + # when inside a backtick, accepting all legal characters towards the trigger + # limit. We would have to parse the statement, or at least go back more + # characters, costing performance. This still works within a backtick! So + # long as there are three trailing non-punctuation characters. + return not bool(re.search(r'[\s!-/:-@\[-^\{-~]', last_word)) class MyCli: @@ -122,6 +153,8 @@ def __init__( warn: bool | None = None, myclirc: str = "~/.myclirc", ) -> None: + global MIN_COMPLETION_TRIGGER + self.sqlexecute = sqlexecute self.logfile = logfile self.defaults_suffix = defaults_suffix @@ -222,6 +255,9 @@ def __init__( ) self._completer_lock = threading.Lock() + self.min_completion_trigger = c["main"].as_int("min_completion_trigger") + MIN_COMPLETION_TRIGGER = self.min_completion_trigger + # Register custom special commands. self.register_special_commands() @@ -1147,7 +1183,7 @@ def one_iteration(text: str | None = None) -> None: completer=DynamicCompleter(lambda: self.completer), history=history, auto_suggest=AutoSuggestFromHistory(), - complete_while_typing=True, + complete_while_typing=complete_while_typing_filter, multiline=cli_is_multiline(self), style=style_factory(self.syntax_style, self.cli_style), include_default_pygments_style=False, diff --git a/mycli/myclirc b/mycli/myclirc index 52912e5d..ab021eca 100644 --- a/mycli/myclirc +++ b/mycli/myclirc @@ -9,6 +9,10 @@ show_warnings = False # possible completions will be listed. smart_completion = True +# Minimum characters typed before offering completion suggestions. +# Suggestion: 3. +min_completion_trigger = 1 + # Multi-line mode allows breaking up the sql statements into multiple lines. If # this is set to True, then the end of the statements must have a semi-colon. # If this is set to False then sql statements can't be split into multiple diff --git a/test/myclirc b/test/myclirc index a66e6406..f3e3bbd2 100644 --- a/test/myclirc +++ b/test/myclirc @@ -9,6 +9,10 @@ show_warnings = False # possible completions will be listed. smart_completion = True +# Minimum characters typed before offering completion suggestions. +# Suggestion: 3. +min_completion_trigger = 1 + # Multi-line mode allows breaking up the sql statements into multiple lines. If # this is set to True, then the end of the statements must have a semi-colon. # If this is set to False then sql statements can't be split into multiple