From 61e726810afbaf8d5a197f5f0a5e235730e3b986 Mon Sep 17 00:00:00 2001 From: Roland Walker Date: Wed, 21 Jan 2026 06:30:27 -0500 Subject: [PATCH] put special commands first in completions and suppress any duplication between pygments keywords and special commands, preferring special commands over pygments keywords. Example: whereas "exit" previously appeared twice, once as "EXIT" and once as "exit", it now appears only once, and at the top of the completion candidates. --- changelog.md | 5 +++++ mycli/sqlcompleter.py | 12 +++++++++--- test/test_smart_completion_public_schema_only.py | 16 +++++++++++++++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index f685262f..7f8f151b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,11 @@ TBD ============== +Features +-------- +* Place special commands first in the list of completion candidates, and remove duplicates. + + Bug Fixes -------- * Fix CamelCase fuzzy matching. diff --git a/mycli/sqlcompleter.py b/mycli/sqlcompleter.py index eee9b08c..e27fcfa6 100644 --- a/mycli/sqlcompleter.py +++ b/mycli/sqlcompleter.py @@ -14,6 +14,7 @@ from mycli.packages.parseutils import last_word from mycli.packages.special import llm from mycli.packages.special.favoritequeries import FavoriteQueries +from mycli.packages.special.main import COMMANDS as SPECIAL_COMMANDS _logger = logging.getLogger(__name__) @@ -32,14 +33,18 @@ class SQLCompleter(Completer): 'LIKE', 'LIMIT', ] - keywords = [ + keywords_raw = [ x.upper() for x in favorite_keywords + list(MYSQL_DATATYPES) + list(MYSQL_KEYWORDS) + ['ALTER TABLE', 'CHANGE MASTER TO', 'CHARACTER SET', 'FOREIGN KEY'] ] - keywords = list(dict.fromkeys(keywords)) + keywords_d = dict.fromkeys(keywords_raw) + for x in SPECIAL_COMMANDS: + if x.upper() in keywords_d: + del keywords_d[x.upper()] + keywords = list(keywords_d) tidb_keywords = [ "SELECT", @@ -1104,7 +1109,8 @@ def get_completions( elif suggestion["type"] == "special": special_m = self.find_matches(word_before_cursor, self.special_commands, start_only=True, fuzzy=False) - completions.extend(special_m) + # specials are special, and go early in the candidates, first if possible + completions = list(special_m) + completions elif suggestion["type"] == "favoritequery": if hasattr(FavoriteQueries, 'instance') and hasattr(FavoriteQueries.instance, 'list'): diff --git a/test/test_smart_completion_public_schema_only.py b/test/test_smart_completion_public_schema_only.py index c688b398..f841db49 100644 --- a/test/test_smart_completion_public_schema_only.py +++ b/test/test_smart_completion_public_schema_only.py @@ -61,7 +61,7 @@ def test_empty_string_completion(completer, complete_event): text = "" position = 0 result = list(completer.get_completions(Document(text=text, cursor_position=position), complete_event)) - assert list(map(Completion, completer.keywords + completer.special_commands)) == result + assert list(map(Completion, completer.special_commands + completer.keywords)) == result def test_select_keyword_completion(completer, complete_event): @@ -474,6 +474,20 @@ def test_grant_on_suggets_tables_and_schemata(completer, complete_event): ] +# todo: this test belongs more logically in test_naive_completion.py, but it didn't work there: +# multiple completion candidates were not suggested. +def test_deleted_keyword_completion(completer, complete_event): + text = "exi" + position = len("exi") + result = list(completer.get_completions(Document(text=text, cursor_position=position), complete_event)) + assert result == [ + Completion(text="exit", start_position=-3), + Completion(text='exists', start_position=-3), + Completion(text='expire', start_position=-3), + Completion(text='explain', start_position=-3), + ] + + def dummy_list_path(dir_name): dirs = { "/": [