We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Feature: Search substring in env variable completer
```xsh
$TRA<Tab>
# Show all variables with `*TRA*`
```

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


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `xonsh/completers/environment.py`
Content:
```
1 from xonsh.built_ins import XSH
2 from xonsh.completers.tools import (
3     RichCompletion,
4     contextual_completer,
5     get_filter_function,
6     non_exclusive_completer,
7 )
8 from xonsh.parsers.completion_context import CompletionContext
9 
10 
11 @contextual_completer
12 @non_exclusive_completer
13 def complete_environment_vars(context: CompletionContext):
14     """Completes environment variables."""
15     if context.command:
16         prefix = context.command.prefix
17     elif context.python:
18         prefix = context.python.prefix
19     else:
20         return None
21 
22     dollar_location = prefix.rfind("$")
23     if dollar_location == -1:
24         return None
25 
26     key = prefix[dollar_location + 1 :]
27     lprefix = len(key) + 1
28     if context.command is not None and context.command.is_after_closing_quote:
29         lprefix += 1
30     filter_func = get_filter_function()
31     env = XSH.env
32 
33     return (
34         RichCompletion(
35             "$" + k,
36             display=f"${k} [{type(v).__name__}]",
37             description=env.get_docs(k).doc,
38         )
39         for k, v in env.items()
40         if filter_func(k, key)
41     ), lprefix
42
```

Path: `xonsh/completer.py`
Content:
```
1 """A (tab-)completer for xonsh."""
2 
3 import collections.abc as cabc
4 import sys
5 import typing as tp
6 
7 from xonsh.built_ins import XSH
8 from xonsh.completers.tools import (
9     Completion,
10     RichCompletion,
11     apply_lprefix,
12     get_filter_function,
13     is_contextual_completer,
14     is_exclusive_completer,
15 )
16 from xonsh.parsers.completion_context import CompletionContext, CompletionContextParser
17 from xonsh.tools import print_exception
18 
19 
20 class Completer:
21     """This provides a list of optional completions for the xonsh shell."""
22 
23     def __init__(self):
24         self.context_parser = CompletionContextParser()
25 
26     def parse(
27         self, text: str, cursor_index: "None|int" = None, ctx=None
28     ) -> "CompletionContext":
29         """Parse the given text
30 
31         Parameters
32         ----------
33         text
34             multi-line text
35         cursor_index
36             position of the cursor. If not given, then it is considered to be at the end.
37         ctx
38             Execution context
39         """
40         cursor_index = len(text) if cursor_index is None else cursor_index
41         return self.context_parser.parse(text, cursor_index, ctx)
42 
43     def complete_line(self, text: str):
44         """Handy wrapper to build command-completion-context when cursor is at the end.
45 
46         Notes
47         -----
48         suffix is not supported; text after last space is parsed as prefix.
49         """
50         ctx = self.parse(text)
51         cmd_ctx = ctx.command
52         if not cmd_ctx:
53             raise RuntimeError("Only Command context is empty")
54         prefix = cmd_ctx.prefix
55 
56         line = text
57         begidx = text.rfind(prefix)
58         endidx = begidx + len(prefix)
59 
60         return self.complete(
61             prefix,
62             line,
63             begidx,
64             endidx,
65             cursor_index=len(line),
66             multiline_text=line,
67             completion_context=ctx,
68         )
69 
70     def complete(
71         self,
72         prefix,
73         line,
74         begidx,
75         endidx,
76         ctx=None,
77         multiline_text=None,
78         cursor_index=None,
79         completion_context=None,
80     ):
81         """Complete the string, given a possible execution context.
82 
83         Parameters
84         ----------
85         prefix : str
86             The string to match
87         line : str
88             The line that prefix appears on.
89         begidx : int
90             The index in line that prefix starts on.
91         endidx : int
92             The index in line that prefix ends on.
93         ctx : dict, optional
94             Names in the current execution context.
95         multiline_text : str
96             The complete multiline text. Needed to get completion context.
97         cursor_index : int
98             The current cursor's index in the multiline text.
99             May be ``len(multiline_text)`` for cursor at the end.
100             Needed to get completion context.
101 
102         Returns
103         -------
104         rtn : list of str
105             Possible completions of prefix, sorted alphabetically.
106         lprefix : int
107             Length of the prefix to be replaced in the completion.
108         """
109 
110         if (
111             (multiline_text is not None)
112             and (cursor_index is not None)
113             and (completion_context is None)
114         ):
115             completion_context: tp.Optional[CompletionContext] = self.parse(
116                 multiline_text,
117                 cursor_index,
118                 ctx,
119             )
120 
121         ctx = ctx or {}
122         return self.complete_from_context(
123             completion_context,
124             (prefix, line, begidx, endidx, ctx),
125         )
126 
127     @staticmethod
128     def _format_completion(
129         completion,
130         completion_context,
131         completing_contextual_command: bool,
132         lprefix: int,
133         custom_lprefix: bool,
134     ) -> tuple[Completion, int]:
135         if (
136             completing_contextual_command
137             and completion_context.command.is_after_closing_quote
138         ):
139             """
140             The cursor is appending to a closed string literal, i.e. cursor at the end of ``ls "/usr/"``.
141             1. The closing quote will be appended to all completions.
142                 I.e the completion ``/usr/bin`` will turn into ``/usr/bin"``
143                 To prevent this behavior, a completer can return a ``RichCompletion`` with ``append_closing_quote=False``.
144             2. If not specified, lprefix will cover the closing prefix.
145                 I.e for ``ls "/usr/"``, the default lprefix will be 6 to include the closing quote.
146                 To prevent this behavior, a completer can return a different lprefix or specify it inside ``RichCompletion``.
147             """
148             closing_quote = completion_context.command.closing_quote
149             if not custom_lprefix:
150                 lprefix += len(closing_quote)
151             if closing_quote:
152                 if isinstance(completion, RichCompletion):
153                     if completion.append_closing_quote:
154                         completion = completion.replace(
155                             value=completion.value + closing_quote
156                         )
157                 else:
158                     completion = completion + closing_quote
159 
160         completion = list(apply_lprefix([completion], lprefix))[0]
161 
162         if (
163             isinstance(completion, RichCompletion)
164             and completion.append_space
165             and not completion.value.endswith(" ")
166         ):
167             # append spaces AFTER appending closing quote
168             completion = completion.replace(value=completion.value + " ")
169 
170         return completion, lprefix
171 
172     @staticmethod
173     def generate_completions(
174         completion_context, old_completer_args, trace: bool
175     ) -> tp.Iterator[tuple[Completion, int]]:
176         filter_func = get_filter_function()
177 
178         for name, func in XSH.completers.items():
179             try:
180                 if is_contextual_completer(func):
181                     if completion_context is None:
182                         continue
183                     out = func(completion_context)
184                 else:
185                     if old_completer_args is None:
186                         continue
187                     out = func(*old_completer_args)
188             except StopIteration:
189                 # completer requested to stop collecting completions
190                 break
191             except Exception as e:
192                 name = func.__name__ if hasattr(func, "__name__") else str(func)
193                 print_exception(
194                     f"Completer {name} raises exception when gets "
195                     f"old_args={old_completer_args[:-1]} / completion_context={completion_context!r}:\n"
196                     f"{type(e)} - {e}"
197                 )
198                 continue
199 
200             completing_contextual_command = (
201                 is_contextual_completer(func)
202                 and completion_context is not None
203                 and completion_context.command is not None
204             )
205 
206             # -- set comp-defaults --
207 
208             # the default is that the completer function filters out as necessary
209             # we can change that once fuzzy/substring matches are added
210             is_filtered = True
211             custom_lprefix = False
212             prefix = ""
213             if completing_contextual_command:
214                 prefix = completion_context.command.prefix
215             elif old_completer_args is not None:
216                 prefix = old_completer_args[0]
217             lprefix = len(prefix)
218 
219             if isinstance(out, cabc.Sequence):
220                 # update comp-defaults from
221                 res, lprefix_filtered = out
222                 if isinstance(lprefix_filtered, bool):
223                     is_filtered = lprefix_filtered
224                 else:
225                     lprefix = lprefix_filtered
226                     custom_lprefix = True
227             else:
228                 res = out
229 
230             if res is None:
231                 continue
232 
233             items = []
234             for comp in res:
235                 if (not is_filtered) and (not filter_func(comp, prefix)):
236                     continue
237                 comp = Completer._format_completion(
238                     comp,
239                     completion_context,
240                     completing_contextual_command,
241                     lprefix or 0,
242                     custom_lprefix,
243                 )
244                 items.append(comp)
245                 yield comp
246 
247             if not items:  # empty completion
248                 continue
249 
250             if trace:
251                 print(
252                     f"TRACE COMPLETIONS: Got {len(items)} results"
253                     f" from {'' if is_exclusive_completer(func) else 'non-'}exclusive completer '{name}':"
254                 )
255                 sys.displayhook(items)
256 
257             if is_exclusive_completer(func):
258                 # we got completions for an exclusive completer
259                 break
260 
261     def complete_from_context(self, completion_context, old_completer_args=None):
262         trace = XSH.env.get("XONSH_TRACE_COMPLETIONS")
263         if trace:
264             print("\nTRACE COMPLETIONS: Getting completions with context:")
265             sys.displayhook(completion_context)
266         lprefix = 0
267 
268         # using dict to keep order py3.6+
269         completions = {}
270 
271         query_limit = XSH.env.get("COMPLETION_QUERY_LIMIT")
272 
273         for comp in self.generate_completions(
274             completion_context,
275             old_completer_args,
276             trace,
277         ):
278             completion, lprefix = comp
279             completions[completion] = None
280             if query_limit and len(completions) >= query_limit:
281                 if trace:
282                     print(
283                         "TRACE COMPLETIONS: Stopped after $COMPLETION_QUERY_LIMIT reached."
284                     )
285                 break
286 
287         def sortkey(s):
288             # todo: should sort with prefix > substring > fuzzy
289             return s.lstrip(''''"''').lower()
290 
291         # the last completer's lprefix is returned. other lprefix values are inside the RichCompletions.
292         return tuple(sorted(completions, key=sortkey)), lprefix
293
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/xonsh/completer.py b/xonsh/completer.py
--- a/xonsh/completer.py
+++ b/xonsh/completer.py
@@ -284,9 +284,18 @@
                     )
                 break
 
-        def sortkey(s):
-            # todo: should sort with prefix > substring > fuzzy
-            return s.lstrip(''''"''').lower()
+        prefix = None
+        if completion_context:
+            prefix = completion_context.command.prefix
+            if prefix.startswith("$"):
+                prefix = prefix[1:]
+
+            def sortkey(s):
+                """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
diff --git a/xonsh/completers/environment.py b/xonsh/completers/environment.py
--- a/xonsh/completers/environment.py
+++ b/xonsh/completers/environment.py
@@ -2,7 +2,6 @@
 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 @@
     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

{"golden_diff": "diff --git a/xonsh/completer.py b/xonsh/completer.py\n--- a/xonsh/completer.py\n+++ b/xonsh/completer.py\n@@ -284,9 +284,18 @@\n                     )\n                 break\n \n-        def sortkey(s):\n-            # todo: should sort with prefix > substring > fuzzy\n-            return s.lstrip(''''\"''').lower()\n+        prefix = None\n+        if completion_context:\n+            prefix = completion_context.command.prefix\n+            if prefix.startswith(\"$\"):\n+                prefix = prefix[1:]\n+\n+            def sortkey(s):\n+                \"\"\"Sort values by prefix position and then alphabetically.\"\"\"\n+                return (s.lower().find(prefix.lower()), s.lower())\n+        else:\n+            # Fallback sort.\n+            sortkey = lambda s: s.lstrip(''''\"''').lower()\n \n         # the last completer's lprefix is returned. other lprefix values are inside the RichCompletions.\n         return tuple(sorted(completions, key=sortkey)), lprefix\ndiff --git a/xonsh/completers/environment.py b/xonsh/completers/environment.py\n--- a/xonsh/completers/environment.py\n+++ b/xonsh/completers/environment.py\n@@ -2,7 +2,6 @@\n from xonsh.completers.tools import (\n     RichCompletion,\n     contextual_completer,\n-    get_filter_function,\n     non_exclusive_completer,\n )\n from xonsh.parsers.completion_context import CompletionContext\n@@ -27,15 +26,14 @@\n     lprefix = len(key) + 1\n     if context.command is not None and context.command.is_after_closing_quote:\n         lprefix += 1\n-    filter_func = get_filter_function()\n     env = XSH.env\n \n+    vars = [k for k, v in env.items() if key.lower() in k.lower()]\n     return (\n         RichCompletion(\n             \"$\" + k,\n-            display=f\"${k} [{type(v).__name__}]\",\n+            display=f\"${k} [{type(env[k]).__name__}]\",\n             description=env.get_docs(k).doc,\n         )\n-        for k, v in env.items()\n-        if filter_func(k, key)\n+        for k in vars\n     ), lprefix\n", "issue": "Feature: Search substring in env variable completer\n```xsh\r\n$TRA<Tab>\r\n# Show all variables with `*TRA*`\r\n```\r\n\r\n## For community\r\n\u2b07\ufe0f  **Please click the \ud83d\udc4d reaction instead of leaving a `+1` or \ud83d\udc4d  comment**\r\n\n", "before_files": [{"content": "from xonsh.built_ins import XSH\nfrom xonsh.completers.tools import (\n    RichCompletion,\n    contextual_completer,\n    get_filter_function,\n    non_exclusive_completer,\n)\nfrom xonsh.parsers.completion_context import CompletionContext\n\n\n@contextual_completer\n@non_exclusive_completer\ndef complete_environment_vars(context: CompletionContext):\n    \"\"\"Completes environment variables.\"\"\"\n    if context.command:\n        prefix = context.command.prefix\n    elif context.python:\n        prefix = context.python.prefix\n    else:\n        return None\n\n    dollar_location = prefix.rfind(\"$\")\n    if dollar_location == -1:\n        return None\n\n    key = prefix[dollar_location + 1 :]\n    lprefix = len(key) + 1\n    if context.command is not None and context.command.is_after_closing_quote:\n        lprefix += 1\n    filter_func = get_filter_function()\n    env = XSH.env\n\n    return (\n        RichCompletion(\n            \"$\" + k,\n            display=f\"${k} [{type(v).__name__}]\",\n            description=env.get_docs(k).doc,\n        )\n        for k, v in env.items()\n        if filter_func(k, key)\n    ), lprefix\n", "path": "xonsh/completers/environment.py"}, {"content": "\"\"\"A (tab-)completer for xonsh.\"\"\"\n\nimport collections.abc as cabc\nimport sys\nimport typing as tp\n\nfrom xonsh.built_ins import XSH\nfrom xonsh.completers.tools import (\n    Completion,\n    RichCompletion,\n    apply_lprefix,\n    get_filter_function,\n    is_contextual_completer,\n    is_exclusive_completer,\n)\nfrom xonsh.parsers.completion_context import CompletionContext, CompletionContextParser\nfrom xonsh.tools import print_exception\n\n\nclass Completer:\n    \"\"\"This provides a list of optional completions for the xonsh shell.\"\"\"\n\n    def __init__(self):\n        self.context_parser = CompletionContextParser()\n\n    def parse(\n        self, text: str, cursor_index: \"None|int\" = None, ctx=None\n    ) -> \"CompletionContext\":\n        \"\"\"Parse the given text\n\n        Parameters\n        ----------\n        text\n            multi-line text\n        cursor_index\n            position of the cursor. If not given, then it is considered to be at the end.\n        ctx\n            Execution context\n        \"\"\"\n        cursor_index = len(text) if cursor_index is None else cursor_index\n        return self.context_parser.parse(text, cursor_index, ctx)\n\n    def complete_line(self, text: str):\n        \"\"\"Handy wrapper to build command-completion-context when cursor is at the end.\n\n        Notes\n        -----\n        suffix is not supported; text after last space is parsed as prefix.\n        \"\"\"\n        ctx = self.parse(text)\n        cmd_ctx = ctx.command\n        if not cmd_ctx:\n            raise RuntimeError(\"Only Command context is empty\")\n        prefix = cmd_ctx.prefix\n\n        line = text\n        begidx = text.rfind(prefix)\n        endidx = begidx + len(prefix)\n\n        return self.complete(\n            prefix,\n            line,\n            begidx,\n            endidx,\n            cursor_index=len(line),\n            multiline_text=line,\n            completion_context=ctx,\n        )\n\n    def complete(\n        self,\n        prefix,\n        line,\n        begidx,\n        endidx,\n        ctx=None,\n        multiline_text=None,\n        cursor_index=None,\n        completion_context=None,\n    ):\n        \"\"\"Complete the string, given a possible execution context.\n\n        Parameters\n        ----------\n        prefix : str\n            The string to match\n        line : str\n            The line that prefix appears on.\n        begidx : int\n            The index in line that prefix starts on.\n        endidx : int\n            The index in line that prefix ends on.\n        ctx : dict, optional\n            Names in the current execution context.\n        multiline_text : str\n            The complete multiline text. Needed to get completion context.\n        cursor_index : int\n            The current cursor's index in the multiline text.\n            May be ``len(multiline_text)`` for cursor at the end.\n            Needed to get completion context.\n\n        Returns\n        -------\n        rtn : list of str\n            Possible completions of prefix, sorted alphabetically.\n        lprefix : int\n            Length of the prefix to be replaced in the completion.\n        \"\"\"\n\n        if (\n            (multiline_text is not None)\n            and (cursor_index is not None)\n            and (completion_context is None)\n        ):\n            completion_context: tp.Optional[CompletionContext] = self.parse(\n                multiline_text,\n                cursor_index,\n                ctx,\n            )\n\n        ctx = ctx or {}\n        return self.complete_from_context(\n            completion_context,\n            (prefix, line, begidx, endidx, ctx),\n        )\n\n    @staticmethod\n    def _format_completion(\n        completion,\n        completion_context,\n        completing_contextual_command: bool,\n        lprefix: int,\n        custom_lprefix: bool,\n    ) -> tuple[Completion, int]:\n        if (\n            completing_contextual_command\n            and completion_context.command.is_after_closing_quote\n        ):\n            \"\"\"\n            The cursor is appending to a closed string literal, i.e. cursor at the end of ``ls \"/usr/\"``.\n            1. The closing quote will be appended to all completions.\n                I.e the completion ``/usr/bin`` will turn into ``/usr/bin\"``\n                To prevent this behavior, a completer can return a ``RichCompletion`` with ``append_closing_quote=False``.\n            2. If not specified, lprefix will cover the closing prefix.\n                I.e for ``ls \"/usr/\"``, the default lprefix will be 6 to include the closing quote.\n                To prevent this behavior, a completer can return a different lprefix or specify it inside ``RichCompletion``.\n            \"\"\"\n            closing_quote = completion_context.command.closing_quote\n            if not custom_lprefix:\n                lprefix += len(closing_quote)\n            if closing_quote:\n                if isinstance(completion, RichCompletion):\n                    if completion.append_closing_quote:\n                        completion = completion.replace(\n                            value=completion.value + closing_quote\n                        )\n                else:\n                    completion = completion + closing_quote\n\n        completion = list(apply_lprefix([completion], lprefix))[0]\n\n        if (\n            isinstance(completion, RichCompletion)\n            and completion.append_space\n            and not completion.value.endswith(\" \")\n        ):\n            # append spaces AFTER appending closing quote\n            completion = completion.replace(value=completion.value + \" \")\n\n        return completion, lprefix\n\n    @staticmethod\n    def generate_completions(\n        completion_context, old_completer_args, trace: bool\n    ) -> tp.Iterator[tuple[Completion, int]]:\n        filter_func = get_filter_function()\n\n        for name, func in XSH.completers.items():\n            try:\n                if is_contextual_completer(func):\n                    if completion_context is None:\n                        continue\n                    out = func(completion_context)\n                else:\n                    if old_completer_args is None:\n                        continue\n                    out = func(*old_completer_args)\n            except StopIteration:\n                # completer requested to stop collecting completions\n                break\n            except Exception as e:\n                name = func.__name__ if hasattr(func, \"__name__\") else str(func)\n                print_exception(\n                    f\"Completer {name} raises exception when gets \"\n                    f\"old_args={old_completer_args[:-1]} / completion_context={completion_context!r}:\\n\"\n                    f\"{type(e)} - {e}\"\n                )\n                continue\n\n            completing_contextual_command = (\n                is_contextual_completer(func)\n                and completion_context is not None\n                and completion_context.command is not None\n            )\n\n            # -- set comp-defaults --\n\n            # the default is that the completer function filters out as necessary\n            # we can change that once fuzzy/substring matches are added\n            is_filtered = True\n            custom_lprefix = False\n            prefix = \"\"\n            if completing_contextual_command:\n                prefix = completion_context.command.prefix\n            elif old_completer_args is not None:\n                prefix = old_completer_args[0]\n            lprefix = len(prefix)\n\n            if isinstance(out, cabc.Sequence):\n                # update comp-defaults from\n                res, lprefix_filtered = out\n                if isinstance(lprefix_filtered, bool):\n                    is_filtered = lprefix_filtered\n                else:\n                    lprefix = lprefix_filtered\n                    custom_lprefix = True\n            else:\n                res = out\n\n            if res is None:\n                continue\n\n            items = []\n            for comp in res:\n                if (not is_filtered) and (not filter_func(comp, prefix)):\n                    continue\n                comp = Completer._format_completion(\n                    comp,\n                    completion_context,\n                    completing_contextual_command,\n                    lprefix or 0,\n                    custom_lprefix,\n                )\n                items.append(comp)\n                yield comp\n\n            if not items:  # empty completion\n                continue\n\n            if trace:\n                print(\n                    f\"TRACE COMPLETIONS: Got {len(items)} results\"\n                    f\" from {'' if is_exclusive_completer(func) else 'non-'}exclusive completer '{name}':\"\n                )\n                sys.displayhook(items)\n\n            if is_exclusive_completer(func):\n                # we got completions for an exclusive completer\n                break\n\n    def complete_from_context(self, completion_context, old_completer_args=None):\n        trace = XSH.env.get(\"XONSH_TRACE_COMPLETIONS\")\n        if trace:\n            print(\"\\nTRACE COMPLETIONS: Getting completions with context:\")\n            sys.displayhook(completion_context)\n        lprefix = 0\n\n        # using dict to keep order py3.6+\n        completions = {}\n\n        query_limit = XSH.env.get(\"COMPLETION_QUERY_LIMIT\")\n\n        for comp in self.generate_completions(\n            completion_context,\n            old_completer_args,\n            trace,\n        ):\n            completion, lprefix = comp\n            completions[completion] = None\n            if query_limit and len(completions) >= query_limit:\n                if trace:\n                    print(\n                        \"TRACE COMPLETIONS: Stopped after $COMPLETION_QUERY_LIMIT reached.\"\n                    )\n                break\n\n        def sortkey(s):\n            # todo: should sort with prefix > substring > fuzzy\n            return s.lstrip(''''\"''').lower()\n\n        # the last completer's lprefix is returned. other lprefix values are inside the RichCompletions.\n        return tuple(sorted(completions, key=sortkey)), lprefix\n", "path": "xonsh/completer.py"}], "after_files": [{"content": "from xonsh.built_ins import XSH\nfrom xonsh.completers.tools import (\n    RichCompletion,\n    contextual_completer,\n    non_exclusive_completer,\n)\nfrom xonsh.parsers.completion_context import CompletionContext\n\n\n@contextual_completer\n@non_exclusive_completer\ndef complete_environment_vars(context: CompletionContext):\n    \"\"\"Completes environment variables.\"\"\"\n    if context.command:\n        prefix = context.command.prefix\n    elif context.python:\n        prefix = context.python.prefix\n    else:\n        return None\n\n    dollar_location = prefix.rfind(\"$\")\n    if dollar_location == -1:\n        return None\n\n    key = prefix[dollar_location + 1 :]\n    lprefix = len(key) + 1\n    if context.command is not None and context.command.is_after_closing_quote:\n        lprefix += 1\n    env = XSH.env\n\n    vars = [k for k, v in env.items() if key.lower() in k.lower()]\n    return (\n        RichCompletion(\n            \"$\" + k,\n            display=f\"${k} [{type(env[k]).__name__}]\",\n            description=env.get_docs(k).doc,\n        )\n        for k in vars\n    ), lprefix\n", "path": "xonsh/completers/environment.py"}, {"content": "\"\"\"A (tab-)completer for xonsh.\"\"\"\n\nimport collections.abc as cabc\nimport sys\nimport typing as tp\n\nfrom xonsh.built_ins import XSH\nfrom xonsh.completers.tools import (\n    Completion,\n    RichCompletion,\n    apply_lprefix,\n    get_filter_function,\n    is_contextual_completer,\n    is_exclusive_completer,\n)\nfrom xonsh.parsers.completion_context import CompletionContext, CompletionContextParser\nfrom xonsh.tools import print_exception\n\n\nclass Completer:\n    \"\"\"This provides a list of optional completions for the xonsh shell.\"\"\"\n\n    def __init__(self):\n        self.context_parser = CompletionContextParser()\n\n    def parse(\n        self, text: str, cursor_index: \"None|int\" = None, ctx=None\n    ) -> \"CompletionContext\":\n        \"\"\"Parse the given text\n\n        Parameters\n        ----------\n        text\n            multi-line text\n        cursor_index\n            position of the cursor. If not given, then it is considered to be at the end.\n        ctx\n            Execution context\n        \"\"\"\n        cursor_index = len(text) if cursor_index is None else cursor_index\n        return self.context_parser.parse(text, cursor_index, ctx)\n\n    def complete_line(self, text: str):\n        \"\"\"Handy wrapper to build command-completion-context when cursor is at the end.\n\n        Notes\n        -----\n        suffix is not supported; text after last space is parsed as prefix.\n        \"\"\"\n        ctx = self.parse(text)\n        cmd_ctx = ctx.command\n        if not cmd_ctx:\n            raise RuntimeError(\"Only Command context is empty\")\n        prefix = cmd_ctx.prefix\n\n        line = text\n        begidx = text.rfind(prefix)\n        endidx = begidx + len(prefix)\n\n        return self.complete(\n            prefix,\n            line,\n            begidx,\n            endidx,\n            cursor_index=len(line),\n            multiline_text=line,\n            completion_context=ctx,\n        )\n\n    def complete(\n        self,\n        prefix,\n        line,\n        begidx,\n        endidx,\n        ctx=None,\n        multiline_text=None,\n        cursor_index=None,\n        completion_context=None,\n    ):\n        \"\"\"Complete the string, given a possible execution context.\n\n        Parameters\n        ----------\n        prefix : str\n            The string to match\n        line : str\n            The line that prefix appears on.\n        begidx : int\n            The index in line that prefix starts on.\n        endidx : int\n            The index in line that prefix ends on.\n        ctx : dict, optional\n            Names in the current execution context.\n        multiline_text : str\n            The complete multiline text. Needed to get completion context.\n        cursor_index : int\n            The current cursor's index in the multiline text.\n            May be ``len(multiline_text)`` for cursor at the end.\n            Needed to get completion context.\n\n        Returns\n        -------\n        rtn : list of str\n            Possible completions of prefix, sorted alphabetically.\n        lprefix : int\n            Length of the prefix to be replaced in the completion.\n        \"\"\"\n\n        if (\n            (multiline_text is not None)\n            and (cursor_index is not None)\n            and (completion_context is None)\n        ):\n            completion_context: tp.Optional[CompletionContext] = self.parse(\n                multiline_text,\n                cursor_index,\n                ctx,\n            )\n\n        ctx = ctx or {}\n        return self.complete_from_context(\n            completion_context,\n            (prefix, line, begidx, endidx, ctx),\n        )\n\n    @staticmethod\n    def _format_completion(\n        completion,\n        completion_context,\n        completing_contextual_command: bool,\n        lprefix: int,\n        custom_lprefix: bool,\n    ) -> tuple[Completion, int]:\n        if (\n            completing_contextual_command\n            and completion_context.command.is_after_closing_quote\n        ):\n            \"\"\"\n            The cursor is appending to a closed string literal, i.e. cursor at the end of ``ls \"/usr/\"``.\n            1. The closing quote will be appended to all completions.\n                I.e the completion ``/usr/bin`` will turn into ``/usr/bin\"``\n                To prevent this behavior, a completer can return a ``RichCompletion`` with ``append_closing_quote=False``.\n            2. If not specified, lprefix will cover the closing prefix.\n                I.e for ``ls \"/usr/\"``, the default lprefix will be 6 to include the closing quote.\n                To prevent this behavior, a completer can return a different lprefix or specify it inside ``RichCompletion``.\n            \"\"\"\n            closing_quote = completion_context.command.closing_quote\n            if not custom_lprefix:\n                lprefix += len(closing_quote)\n            if closing_quote:\n                if isinstance(completion, RichCompletion):\n                    if completion.append_closing_quote:\n                        completion = completion.replace(\n                            value=completion.value + closing_quote\n                        )\n                else:\n                    completion = completion + closing_quote\n\n        completion = list(apply_lprefix([completion], lprefix))[0]\n\n        if (\n            isinstance(completion, RichCompletion)\n            and completion.append_space\n            and not completion.value.endswith(\" \")\n        ):\n            # append spaces AFTER appending closing quote\n            completion = completion.replace(value=completion.value + \" \")\n\n        return completion, lprefix\n\n    @staticmethod\n    def generate_completions(\n        completion_context, old_completer_args, trace: bool\n    ) -> tp.Iterator[tuple[Completion, int]]:\n        filter_func = get_filter_function()\n\n        for name, func in XSH.completers.items():\n            try:\n                if is_contextual_completer(func):\n                    if completion_context is None:\n                        continue\n                    out = func(completion_context)\n                else:\n                    if old_completer_args is None:\n                        continue\n                    out = func(*old_completer_args)\n            except StopIteration:\n                # completer requested to stop collecting completions\n                break\n            except Exception as e:\n                name = func.__name__ if hasattr(func, \"__name__\") else str(func)\n                print_exception(\n                    f\"Completer {name} raises exception when gets \"\n                    f\"old_args={old_completer_args[:-1]} / completion_context={completion_context!r}:\\n\"\n                    f\"{type(e)} - {e}\"\n                )\n                continue\n\n            completing_contextual_command = (\n                is_contextual_completer(func)\n                and completion_context is not None\n                and completion_context.command is not None\n            )\n\n            # -- set comp-defaults --\n\n            # the default is that the completer function filters out as necessary\n            # we can change that once fuzzy/substring matches are added\n            is_filtered = True\n            custom_lprefix = False\n            prefix = \"\"\n            if completing_contextual_command:\n                prefix = completion_context.command.prefix\n            elif old_completer_args is not None:\n                prefix = old_completer_args[0]\n            lprefix = len(prefix)\n\n            if isinstance(out, cabc.Sequence):\n                # update comp-defaults from\n                res, lprefix_filtered = out\n                if isinstance(lprefix_filtered, bool):\n                    is_filtered = lprefix_filtered\n                else:\n                    lprefix = lprefix_filtered\n                    custom_lprefix = True\n            else:\n                res = out\n\n            if res is None:\n                continue\n\n            items = []\n            for comp in res:\n                if (not is_filtered) and (not filter_func(comp, prefix)):\n                    continue\n                comp = Completer._format_completion(\n                    comp,\n                    completion_context,\n                    completing_contextual_command,\n                    lprefix or 0,\n                    custom_lprefix,\n                )\n                items.append(comp)\n                yield comp\n\n            if not items:  # empty completion\n                continue\n\n            if trace:\n                print(\n                    f\"TRACE COMPLETIONS: Got {len(items)} results\"\n                    f\" from {'' if is_exclusive_completer(func) else 'non-'}exclusive completer '{name}':\"\n                )\n                sys.displayhook(items)\n\n            if is_exclusive_completer(func):\n                # we got completions for an exclusive completer\n                break\n\n    def complete_from_context(self, completion_context, old_completer_args=None):\n        trace = XSH.env.get(\"XONSH_TRACE_COMPLETIONS\")\n        if trace:\n            print(\"\\nTRACE COMPLETIONS: Getting completions with context:\")\n            sys.displayhook(completion_context)\n        lprefix = 0\n\n        # using dict to keep order py3.6+\n        completions = {}\n\n        query_limit = XSH.env.get(\"COMPLETION_QUERY_LIMIT\")\n\n        for comp in self.generate_completions(\n            completion_context,\n            old_completer_args,\n            trace,\n        ):\n            completion, lprefix = comp\n            completions[completion] = None\n            if query_limit and len(completions) >= query_limit:\n                if trace:\n                    print(\n                        \"TRACE COMPLETIONS: Stopped after $COMPLETION_QUERY_LIMIT reached.\"\n                    )\n                break\n\n        prefix = None\n        if completion_context:\n            prefix = completion_context.command.prefix\n            if prefix.startswith(\"$\"):\n                prefix = prefix[1:]\n\n            def sortkey(s):\n                \"\"\"Sort values by prefix position and then alphabetically.\"\"\"\n                return (s.lower().find(prefix.lower()), s.lower())\n        else:\n            # Fallback sort.\n            sortkey = lambda s: s.lstrip(''''\"''').lower()\n\n        # the last completer's lprefix is returned. other lprefix values are inside the RichCompletions.\n        return tuple(sorted(completions, key=sortkey)), lprefix\n", "path": "xonsh/completer.py"}]}