Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add better sorting of completions #1165

Closed
wants to merge 11 commits into from
74 changes: 42 additions & 32 deletions mycli/sqlcompleter.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,45 +372,55 @@ def find_matches(text, collection, start_only=False, fuzzy=True, casing=None):
last = last_word(text, include='most_punctuations')
text = last.lower()

completions = []
if casing == 'auto':
casing = 'lower' if last and last[-1].islower() else 'upper'

def apply_case(kw):
if casing is None:
return kw
if casing == 'upper':
return kw.upper()
return kw.lower()

matches = []

if fuzzy:
regex = '.*?'.join(map(escape, text))
pat = compile('(%s)' % regex)
for item in collection:
r = pat.search(item.lower())
if r:
completions.append((len(r.group()), r.start(), item))
matches.append(
(len(r.group()), r.start(), apply_case(item)))
else:
match_end_limit = len(text) if start_only else None
for item in collection:
match_point = item.lower().find(text, 0, match_end_limit)
if match_point >= 0:
completions.append((len(text), match_point, item))

if casing == 'auto':
casing = 'lower' if last and last[-1].islower() else 'upper'
matches.append((len(text), match_point, apply_case(item)))

def apply_case(kw):
if casing == 'upper':
return kw.upper()
return kw.lower()

return (Completion(z if casing is None else apply_case(z), -len(text))
for x, y, z in completions)
return matches

def get_completions(self, document, complete_event, smart_completion=None):
word_before_cursor = document.get_word_before_cursor(WORD=True)

def sorted_completions(matches):
# sort by match point, then match length, then item text
matches = sorted(matches, key=lambda m: (m[1], m[0], m[2].lower()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as the sort is casefolding it might as well also do

m[2].lower().strip('`')

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I can certainly add that. Pretty sure I've seen someone else complain in another issue about the back tick messing with sorting.

return (Completion(z, -len(word_before_cursor))
for x, y, z in matches)

if smart_completion is None:
smart_completion = self.smart_completion

# If smart_completion is off then match any word that starts with
# 'word_before_cursor'.
if not smart_completion:
return self.find_matches(word_before_cursor, self.all_completions,
start_only=True, fuzzy=False)
matches = self.find_matches(word_before_cursor, self.all_completions,
start_only=True, fuzzy=False)
return sorted_completions(matches)

completions = []
matches = []
suggestions = suggest_type(document.text, document.text_before_cursor)

for suggestion in suggestions:
Expand All @@ -431,14 +441,14 @@ def get_completions(self, document, complete_event, smart_completion=None):
]

cols = self.find_matches(word_before_cursor, scoped_cols)
completions.extend(cols)
matches.extend(cols)

elif suggestion['type'] == 'function':
# suggest user-defined functions using substring matching
funcs = self.populate_schema_objects(suggestion['schema'],
'functions')
user_funcs = self.find_matches(word_before_cursor, funcs)
completions.extend(user_funcs)
matches.extend(user_funcs)

# suggest hardcoded functions using startswith matching only if
# there is no schema qualifier. If a schema qualifier is
Expand All @@ -450,77 +460,77 @@ def get_completions(self, document, complete_event, smart_completion=None):
start_only=True,
fuzzy=False,
casing=self.keyword_casing)
completions.extend(predefined_funcs)
matches.extend(predefined_funcs)

elif suggestion['type'] == 'table':
tables = self.populate_schema_objects(suggestion['schema'],
'tables')
tables = self.find_matches(word_before_cursor, tables)
completions.extend(tables)
matches.extend(tables)

elif suggestion['type'] == 'view':
views = self.populate_schema_objects(suggestion['schema'],
'views')
views = self.find_matches(word_before_cursor, views)
completions.extend(views)
matches.extend(views)

elif suggestion['type'] == 'alias':
aliases = suggestion['aliases']
aliases = self.find_matches(word_before_cursor, aliases)
completions.extend(aliases)
matches.extend(aliases)

elif suggestion['type'] == 'database':
dbs = self.find_matches(word_before_cursor, self.databases)
completions.extend(dbs)
matches.extend(dbs)

elif suggestion['type'] == 'keyword':
keywords = self.find_matches(word_before_cursor, self.keywords,
start_only=True,
fuzzy=False,
casing=self.keyword_casing)
completions.extend(keywords)
matches.extend(keywords)

elif suggestion['type'] == 'show':
show_items = self.find_matches(word_before_cursor,
self.show_items,
start_only=False,
fuzzy=True,
casing=self.keyword_casing)
completions.extend(show_items)
matches.extend(show_items)

elif suggestion['type'] == 'change':
change_items = self.find_matches(word_before_cursor,
self.change_items,
start_only=False,
fuzzy=True)
completions.extend(change_items)
matches.extend(change_items)
elif suggestion['type'] == 'user':
users = self.find_matches(word_before_cursor, self.users,
start_only=False,
fuzzy=True)
completions.extend(users)
matches.extend(users)

elif suggestion['type'] == 'special':
special = self.find_matches(word_before_cursor,
self.special_commands,
start_only=True,
fuzzy=False)
completions.extend(special)
matches.extend(special)
elif suggestion['type'] == 'favoritequery':
queries = self.find_matches(word_before_cursor,
FavoriteQueries.instance.list(),
start_only=False, fuzzy=True)
completions.extend(queries)
matches.extend(queries)
elif suggestion['type'] == 'table_format':
formats = self.find_matches(word_before_cursor,
self.table_formats,
start_only=True, fuzzy=False)
completions.extend(formats)
matches.extend(formats)
elif suggestion['type'] == 'file_name':
file_names = self.find_files(word_before_cursor)
completions.extend(file_names)
matches.extend(file_names)

return completions
return sorted_completions(matches)

def find_files(self, word):
"""Yield matching directory or file names.
Expand Down
Loading