Skip to content

Commit bbafa1b

Browse files
committed
fix regression accepting pathname completion
Ensure that when accepting a pathname completion, the path as typed is accepted and not modified into an invalid path. Changes * use a Literal for the include argument to last_word() * create a separate length-measurement string for pathname completions, based on a different last-word cleanup regex * remove some todo comments from tests which are now resolved The logic of length_based_on_path assumes that if _any_ of the suggestions are pathnames, then _all_ of the suggestions are pathnames, which is currently true, and likely to remain true. It would be nice to have a test which simulated accepting the first suggestion, to check that the filled-in completion does not get mangled, as happened before.
1 parent 6439405 commit bbafa1b

File tree

4 files changed

+52
-8
lines changed

4 files changed

+52
-8
lines changed

changelog.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
Upcoming (TBD)
2+
==============
3+
4+
Bug Fixes
5+
--------
6+
* When accepting a filename completion, fill in the selection as typed.
7+
8+
19
1.54.1 (2026/02/17)
210
==============
311

mycli/packages/parseutils.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
import re
4-
from typing import Any, Generator
4+
from typing import Any, Generator, Literal
55

66
import sqlglot
77
import sqlparse
@@ -34,7 +34,15 @@ def is_valid_connection_scheme(text: str) -> tuple[bool, str | None]:
3434
return True, None
3535

3636

37-
def last_word(text: str, include: str = "alphanum_underscore") -> str:
37+
def last_word(
38+
text: str,
39+
include: Literal[
40+
'alphanum_underscore',
41+
'many_punctuations',
42+
'most_punctuations',
43+
'all_punctuations',
44+
] = 'alphanum_underscore',
45+
) -> str:
3846
r"""
3947
Find the last word in a sentence.
4048

mycli/sqlcompleter.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,7 @@ def get_completions(
10751075
word_before_cursor = document.get_word_before_cursor(WORD=True)
10761076
last_for_len = last_word(word_before_cursor, include="most_punctuations")
10771077
text_for_len = last_for_len.lower()
1078+
last_for_len_paths = last_word(word_before_cursor, include='alphanum_underscore')
10781079

10791080
if smart_completion is None:
10801081
smart_completion = self.smart_completion
@@ -1088,6 +1089,7 @@ def get_completions(
10881089
completions: list[tuple[str, int, int]] = []
10891090
suggestions = suggest_type(document.text, document.text_before_cursor)
10901091
rigid_sort = False
1092+
length_based_on_path = False
10911093

10921094
rank = 0
10931095
for suggestion in suggestions:
@@ -1196,6 +1198,7 @@ def get_completions(
11961198
completions.extend([(*x, rank) for x in file_names_m])
11971199
# for filenames we _really_ want directories to go last
11981200
rigid_sort = True
1201+
length_based_on_path = True
11991202
elif suggestion["type"] == "llm":
12001203
if not word_before_cursor:
12011204
tokens = document.text.split()[1:]
@@ -1238,7 +1241,10 @@ def completion_sort_key(item: tuple[str, int, int], text_for_len: str):
12381241
sorted_completions = sorted(completions, key=lambda item: completion_sort_key(item, text_for_len.lower()))
12391242
uniq_completions_str = dict.fromkeys(x[0] for x in sorted_completions)
12401243

1241-
return (Completion(x, -len(text_for_len)) for x in uniq_completions_str)
1244+
if length_based_on_path:
1245+
return (Completion(x, -len(last_for_len_paths)) for x in uniq_completions_str)
1246+
else:
1247+
return (Completion(x, -len(text_for_len)) for x in uniq_completions_str)
12421248

12431249
def find_files(self, word: str) -> Generator[tuple[str, int], None, None]:
12441250
"""Yield matching directory or file names.

test/test_smart_completion_public_schema_only.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -631,13 +631,11 @@ def dummy_list_path(dir_name):
631631
@patch("mycli.packages.filepaths.list_path", new=dummy_list_path)
632632
@pytest.mark.parametrize(
633633
"text,expected",
634-
# it may be that the cursor positions should be 0, but the position
635-
# info is currently being dropped in find_files()
636634
[
637635
('source ', [('/', 0), ('~', 0), ('.', 0), ('..', 0)]),
638-
("source /", [("dir1", -1), ("file1.sql", -1), ("file2.sql", -1)]),
639-
("source /dir1/", [("subdir1", -6), ("subfile1.sql", -6), ("subfile2.sql", -6)]),
640-
("source /dir1/subdir1/", [("lastfile.sql", -14)]),
636+
("source /", [("dir1", 0), ("file1.sql", 0), ("file2.sql", 0)]),
637+
("source /dir1/", [("subdir1", 0), ("subfile1.sql", 0), ("subfile2.sql", 0)]),
638+
("source /dir1/subdir1/", [("lastfile.sql", 0)]),
641639
],
642640
)
643641
def test_file_name_completion(completer, complete_event, text, expected):
@@ -697,6 +695,30 @@ def test_source_eager_completion(completer, complete_event):
697695
raise AssertionError(error)
698696

699697

698+
def test_source_leading_dot_suggestions_completion(completer, complete_event):
699+
text = "source ./sc"
700+
position = len(text)
701+
script_filename = 'script_for_test_suite.sql'
702+
f = open(script_filename, 'w')
703+
f.close()
704+
special.register_special_command(..., 'source', '\\. filename', 'Execute commands from file.', aliases=['\\.'])
705+
result = list(completer.get_completions(Document(text=text, cursor_position=position), complete_event))
706+
success = True
707+
error = 'unknown'
708+
try:
709+
assert [x.text for x in result] == [
710+
script_filename,
711+
'screenshots/',
712+
]
713+
except AssertionError as e:
714+
success = False
715+
error = e
716+
if os.path.exists(script_filename):
717+
os.remove(script_filename)
718+
if not success:
719+
raise AssertionError(error)
720+
721+
700722
def test_string_no_completion(completer, complete_event):
701723
text = 'select "json'
702724
position = len(text)

0 commit comments

Comments
 (0)