@@ -49,7 +49,7 @@ def __init__(
4949 WORD : bool = False ,
5050 pattern : Optional [str ] = None ,
5151 enable_fuzzy : FilterOrBool = True ,
52- ):
52+ ) -> None :
5353
5454 assert pattern is None or pattern .startswith ("^" )
5555
@@ -77,7 +77,6 @@ def _get_pattern(self) -> str:
7777 def _get_fuzzy_completions (
7878 self , document : Document , complete_event : CompleteEvent
7979 ) -> Iterable [Completion ]:
80-
8180 word_before_cursor = document .get_word_before_cursor (
8281 pattern = re .compile (self ._get_pattern ())
8382 )
@@ -88,27 +87,35 @@ def _get_fuzzy_completions(
8887 cursor_position = document .cursor_position - len (word_before_cursor ),
8988 )
9089
91- completions = list (self .completer .get_completions (document2 , complete_event ))
90+ inner_completions = list (
91+ self .completer .get_completions (document2 , complete_event )
92+ )
9293
9394 fuzzy_matches : List [_FuzzyMatch ] = []
9495
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 )
112119
113120 for match in fuzzy_matches :
114121 # Include these completions, but set the correct `display`
@@ -117,7 +124,8 @@ def sort_key(fuzzy_match: "_FuzzyMatch") -> Tuple[int, int]:
117124 text = match .completion .text ,
118125 start_position = match .completion .start_position
119126 - 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 ,
121129 display = self ._get_display (match , word_before_cursor ),
122130 style = match .completion .style ,
123131 )
@@ -128,37 +136,41 @@ def _get_display(
128136 """
129137 Generate formatted text for the display label.
130138 """
131- m = fuzzy_match
132- word = m .completion .text
133139
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
139143
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
141149
142- # Text before match.
143- result .append (("class:fuzzymatch.outside" , word [: m .start_pos ]))
150+ result : StyleAndTextTuples = []
144151
145- # The match itself .
146- characters = list ( word_before_cursor )
152+ # Text before match .
153+ result . append (( "class:fuzzymatch.outside" , word [: m . start_pos ]) )
147154
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 )
153157
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 ]
155163
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
160172
161- return result
173+ return get_display ()
162174
163175
164176class FuzzyWordCompleter (Completer ):
0 commit comments