@@ -49,7 +49,7 @@ def __init__(
49
49
WORD : bool = False ,
50
50
pattern : Optional [str ] = None ,
51
51
enable_fuzzy : FilterOrBool = True ,
52
- ):
52
+ ) -> None :
53
53
54
54
assert pattern is None or pattern .startswith ("^" )
55
55
@@ -77,7 +77,6 @@ def _get_pattern(self) -> str:
77
77
def _get_fuzzy_completions (
78
78
self , document : Document , complete_event : CompleteEvent
79
79
) -> Iterable [Completion ]:
80
-
81
80
word_before_cursor = document .get_word_before_cursor (
82
81
pattern = re .compile (self ._get_pattern ())
83
82
)
@@ -88,27 +87,35 @@ def _get_fuzzy_completions(
88
87
cursor_position = document .cursor_position - len (word_before_cursor ),
89
88
)
90
89
91
- completions = list (self .completer .get_completions (document2 , complete_event ))
90
+ inner_completions = list (
91
+ self .completer .get_completions (document2 , complete_event )
92
+ )
92
93
93
94
fuzzy_matches : List [_FuzzyMatch ] = []
94
95
95
- pat = ".*?" .join (map (re .escape , word_before_cursor ))
96
- pat = f"(?=({ pat } ))" # lookahead regex to manage overlapping matches
97
- regex = re .compile (pat , re .IGNORECASE )
98
- for compl in completions :
99
- matches = list (regex .finditer (compl .text ))
100
- if matches :
101
- # Prefer the match, closest to the left, then shortest.
102
- best = min (matches , key = lambda m : (m .start (), len (m .group (1 ))))
103
- fuzzy_matches .append (
104
- _FuzzyMatch (len (best .group (1 )), best .start (), compl )
105
- )
106
-
107
- def sort_key (fuzzy_match : "_FuzzyMatch" ) -> Tuple [int , int ]:
108
- "Sort by start position, then by the length of the match."
109
- return fuzzy_match .start_pos , fuzzy_match .match_length
110
-
111
- fuzzy_matches = sorted (fuzzy_matches , key = sort_key )
96
+ if word_before_cursor == "" :
97
+ # If word before the cursor is an empty string, consider all
98
+ # completions, without filtering everything with an empty regex
99
+ # pattern.
100
+ fuzzy_matches = [_FuzzyMatch (0 , 0 , compl ) for compl in inner_completions ]
101
+ else :
102
+ pat = ".*?" .join (map (re .escape , word_before_cursor ))
103
+ pat = f"(?=({ pat } ))" # lookahead regex to manage overlapping matches
104
+ regex = re .compile (pat , re .IGNORECASE )
105
+ for compl in inner_completions :
106
+ matches = list (regex .finditer (compl .text ))
107
+ if matches :
108
+ # Prefer the match, closest to the left, then shortest.
109
+ best = min (matches , key = lambda m : (m .start (), len (m .group (1 ))))
110
+ fuzzy_matches .append (
111
+ _FuzzyMatch (len (best .group (1 )), best .start (), compl )
112
+ )
113
+
114
+ def sort_key (fuzzy_match : "_FuzzyMatch" ) -> Tuple [int , int ]:
115
+ "Sort by start position, then by the length of the match."
116
+ return fuzzy_match .start_pos , fuzzy_match .match_length
117
+
118
+ fuzzy_matches = sorted (fuzzy_matches , key = sort_key )
112
119
113
120
for match in fuzzy_matches :
114
121
# Include these completions, but set the correct `display`
@@ -117,7 +124,8 @@ def sort_key(fuzzy_match: "_FuzzyMatch") -> Tuple[int, int]:
117
124
text = match .completion .text ,
118
125
start_position = match .completion .start_position
119
126
- len (word_before_cursor ),
120
- display_meta = match .completion .display_meta ,
127
+ # We access to private `_display_meta` attribute, because that one is lazy.
128
+ display_meta = match .completion ._display_meta ,
121
129
display = self ._get_display (match , word_before_cursor ),
122
130
style = match .completion .style ,
123
131
)
@@ -128,37 +136,41 @@ def _get_display(
128
136
"""
129
137
Generate formatted text for the display label.
130
138
"""
131
- m = fuzzy_match
132
- word = m .completion .text
133
139
134
- if m .match_length == 0 :
135
- # No highlighting when we have zero length matches (no input text).
136
- # In this case, use the original display text (which can include
137
- # additional styling or characters).
138
- return m .completion .display
140
+ def get_display () -> AnyFormattedText :
141
+ m = fuzzy_match
142
+ word = m .completion .text
139
143
140
- result : StyleAndTextTuples = []
144
+ if m .match_length == 0 :
145
+ # No highlighting when we have zero length matches (no input text).
146
+ # In this case, use the original display text (which can include
147
+ # additional styling or characters).
148
+ return m .completion .display
141
149
142
- # Text before match.
143
- result .append (("class:fuzzymatch.outside" , word [: m .start_pos ]))
150
+ result : StyleAndTextTuples = []
144
151
145
- # The match itself .
146
- characters = list ( word_before_cursor )
152
+ # Text before match .
153
+ result . append (( "class:fuzzymatch.outside" , word [: m . start_pos ]) )
147
154
148
- for c in word [m .start_pos : m .start_pos + m .match_length ]:
149
- classname = "class:fuzzymatch.inside"
150
- if characters and c .lower () == characters [0 ].lower ():
151
- classname += ".character"
152
- del characters [0 ]
155
+ # The match itself.
156
+ characters = list (word_before_cursor )
153
157
154
- result .append ((classname , c ))
158
+ for c in word [m .start_pos : m .start_pos + m .match_length ]:
159
+ classname = "class:fuzzymatch.inside"
160
+ if characters and c .lower () == characters [0 ].lower ():
161
+ classname += ".character"
162
+ del characters [0 ]
155
163
156
- # Text after match.
157
- result .append (
158
- ("class:fuzzymatch.outside" , word [m .start_pos + m .match_length :])
159
- )
164
+ result .append ((classname , c ))
165
+
166
+ # Text after match.
167
+ result .append (
168
+ ("class:fuzzymatch.outside" , word [m .start_pos + m .match_length :])
169
+ )
170
+
171
+ return result
160
172
161
- return result
173
+ return get_display ()
162
174
163
175
164
176
class FuzzyWordCompleter (Completer ):
0 commit comments