Use substring for env completion and better way to sort list (#5388)

### Motivation

It's very annoying to search env variable exactly by lprefix. The better
is to search by substring and sort results by the position of substring
and then alphabetically.

Closes #5386

### Before

```xsh
$TRA<Tab>
# nothing
```

### After
```xsh
$TRA<Tab>
# 'XONSH_TRACE_COMPLETIONS', 
# 'XONSH_TRACE_SUBPROC', 
# 'XONSH_TRACE_SUBPROC_FUNC', 
# 'XONSH_TRACEBACK_LOGFILE', 
# 'XONSH_SHOW_TRACEBACK', 
# 'VC_GIT_INCLUDE_UNTRACKED'
```

## For community
⬇️ **Please click the 👍 reaction instead of leaving a `+1` or 👍
comment**

---------

Co-authored-by: a <1@1.1>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Andy Kipp 2024-05-03 10:30:14 +02:00 committed by GitHub
parent 21da30e753
commit a3e8b1a025
Failed to generate hash of commit
5 changed files with 63 additions and 20 deletions

23
news/env_completion.rst Normal file
View file

@ -0,0 +1,23 @@
**Added:**
* <news item>
**Changed:**
* Env variables completion: now use substring for search and then sort results by the position of substring and then alphabetically. PR 5388.
**Deprecated:**
* <news item>
**Removed:**
* <news item>
**Fixed:**
* <news item>
**Security:**
* <news item>

View file

@ -12,30 +12,30 @@ def parser():
@pytest.mark.parametrize(
"cmd",
(
"ls $WO",
"ls /home/$WO",
"ls '/home/$WO'",
"ls @('hi ' + $WO",
"ls $WOW",
"ls /home/$WOW",
"ls '/home/$WOW'",
"ls @('hi ' + $WOW",
),
)
def test_simple(cmd, xession, monkeypatch, parser):
xession.env.update({"WOW": 1})
xession.env.update({"WOWZER": 1})
context = parser.parse(cmd, len(cmd))
comps, lprefix = complete_environment_vars(context)
# account for the ending quote
if cmd[-1] in "'":
assert lprefix == 4
assert lprefix == 5
else:
assert lprefix == 3
assert set(comps) == {"$WOW"}
assert lprefix == 4
assert set(comps) == {"$WOWZER"}
def test_rich_completions(xession, monkeypatch, parser):
xession.env.update({"WOW": 1})
xession.env.register("WOW", type=int, doc="Nice Docs!")
xession.env.update({"WOWZER": 1})
xession.env.register("WOWZER", type=int, doc="Nice Docs!")
context = parser.parse("$WO", 3)
context = parser.parse("$WOW", 4)
completion = next(complete_environment_vars(context)[0])
assert completion.display == "$WOW [int]"
assert completion.display == "$WOWZER [int]"
assert completion.description == "Nice Docs!"

View file

@ -155,3 +155,16 @@ def test_non_exclusive(completer, completers_mock, middle_result, exp):
completers_mock["c"] = non_exclusive_completer(lambda *a: {"c1", "c2"})
assert completer.complete("", "", 0, 0, {})[0] == exp
def test_env_completer_sort(completer, completers_mock):
@contextual_command_completer
def comp(context: CommandContext):
return {"$SUPER_WOW", "$WOW1", "$WOW0", "$MID_WOW"}
completers_mock["a"] = comp
comps = completer.complete(
"$WOW", "$WOW", 4, 0, {}, multiline_text="'$WOW'", cursor_index=4
)
assert set(comps[0]) == {"$WOW0", "$WOW1", "$MID_WOW", "$SUPER_WOW"}

View file

@ -284,9 +284,18 @@ class Completer:
)
break
prefix = None
if completion_context:
prefix = completion_context.command.prefix
if prefix.startswith("$"):
prefix = prefix[1:]
def sortkey(s):
# todo: should sort with prefix > substring > fuzzy
return s.lstrip(''''"''').lower()
"""Sort values by prefix position and then alphabetically."""
return (s.lower().find(prefix.lower()), s.lower())
else:
# Fallback sort.
sortkey = lambda s: s.lstrip(''''"''').lower()
# the last completer's lprefix is returned. other lprefix values are inside the RichCompletions.
return tuple(sorted(completions, key=sortkey)), lprefix

View file

@ -2,7 +2,6 @@ from xonsh.built_ins import XSH
from xonsh.completers.tools import (
RichCompletion,
contextual_completer,
get_filter_function,
non_exclusive_completer,
)
from xonsh.parsers.completion_context import CompletionContext
@ -27,15 +26,14 @@ def complete_environment_vars(context: CompletionContext):
lprefix = len(key) + 1
if context.command is not None and context.command.is_after_closing_quote:
lprefix += 1
filter_func = get_filter_function()
env = XSH.env
vars = [k for k, v in env.items() if key.lower() in k.lower()]
return (
RichCompletion(
"$" + k,
display=f"${k} [{type(v).__name__}]",
display=f"${k} [{type(env[k]).__name__}]",
description=env.get_docs(k).doc,
)
for k, v in env.items()
if filter_func(k, key)
for k in vars
), lprefix