99from gettext import gettext as _
1010from itertools import repeat
1111from typing import (
12- Any , Callable , Dict , Generator , Iterable , List , Optional , Pattern ,
13- Sequence , Set , Tuple , Type , cast
12+ Any , Callable , Dict , Generator , Iterable , Iterator , List , Optional ,
13+ Pattern , Sequence , Set , Tuple , Type , cast
1414)
1515
1616from kitty .cli import parse_args
@@ -44,6 +44,7 @@ def kitty_common_opts() -> KittyCommonOpts:
4444DEFAULT_REGEX = r'(?m)^\s*(.+)\s*$'
4545FILE_EXTENSION = r'\.(?:[a-zA-Z0-9]{2,7}|[ahcmo])(?!\.)'
4646PATH_REGEX = fr'(?:\S*?/[\r\S]+)|(?:\S[\r\S]*{ FILE_EXTENSION } )\b'
47+ DEFAULT_LINENUM_REGEX = fr'(?P<path>{ PATH_REGEX } ):(?P<line>\d+)'
4748
4849
4950class Mark :
@@ -64,6 +65,10 @@ def __init__(
6465 self .is_hyperlink = is_hyperlink
6566 self .group_id = group_id
6667
68+ def __repr__ (self ) -> str :
69+ return (f'Mark(index={ self .index !r} , start={ self .start !r} , end={ self .end !r} ,'
70+ f' text={ self .text !r} , groupdict={ self .groupdict !r} , is_hyperlink={ self .is_hyperlink !r} , group_id={ self .group_id !r} )' )
71+
6772
6873@lru_cache (maxsize = 2048 )
6974def encode_hint (num : int , alphabet : str ) -> str :
@@ -225,14 +230,14 @@ def draw_screen(self) -> None:
225230 self .write (self .current_text )
226231
227232
228- def regex_finditer (pat : 'Pattern[str]' , minimum_match_length : int , text : str ) -> Generator [Tuple [int , int , Dict [str , str ]], None , None ]:
233+ def regex_finditer (pat : 'Pattern[str]' , minimum_match_length : int , text : str ) -> Iterator [Tuple [int , int , 're.Match [str]' ] ]:
229234 has_named_groups = bool (pat .groupindex )
230235 for m in pat .finditer (text ):
231236 s , e = m .span (0 if has_named_groups else pat .groups )
232237 while e > s + 1 and text [e - 1 ] == '\0 ' :
233238 e -= 1
234239 if e - s >= minimum_match_length :
235- yield s , e , m . groupdict ()
240+ yield s , e , m
236241
237242
238243closing_bracket_map = {'(' : ')' , '[' : ']' , '{' : '}' , '<' : '>' , '*' : '*' , '"' : '"' , "'" : "'" , "“" : "”" , "‘" : "’" }
@@ -318,16 +323,23 @@ def ip(text: str, s: int, e: int) -> Tuple[int, int]:
318323 return s , e
319324
320325
321- def mark (pattern : str , post_processors : Iterable [PostprocessorFunc ], text : str , args : HintsCLIOptions ) -> Generator [Mark , None , None ]:
326+ def mark (pattern : str , post_processors : Iterable [PostprocessorFunc ], text : str , args : HintsCLIOptions ) -> Iterator [Mark ]:
322327 pat = re .compile (pattern )
323- for idx , (s , e , groupdict ) in enumerate (regex_finditer (pat , args .minimum_match_length , text )):
328+ sanitize_pat = re .compile ('[\r \n \0 ]' )
329+ for idx , (s , e , match_object ) in enumerate (regex_finditer (pat , args .minimum_match_length , text )):
324330 try :
325331 for func in post_processors :
326332 s , e = func (text , s , e )
333+ groupdict = match_object .groupdict ()
334+ for group_name in groupdict :
335+ group_idx = pat .groupindex [group_name ]
336+ gs , ge = match_object .span (group_idx )
337+ gs , ge = max (gs , s ), min (ge , e )
338+ groupdict [group_name ] = sanitize_pat .sub ('' , text [gs :ge ])
327339 except InvalidMatch :
328340 continue
329341
330- mark_text = re .sub ('[ \r \n \0 ]' , '' , text [s :e ])
342+ mark_text = sanitize_pat .sub ('' , text [s :e ])
331343 yield Mark (idx , s , e , mark_text , groupdict )
332344
333345
@@ -420,7 +432,7 @@ def parse_input(text: str) -> str:
420432def linenum_marks (text : str , args : HintsCLIOptions , Mark : Type [Mark ], extra_cli_args : Sequence [str ], * a : Any ) -> Generator [Mark , None , None ]:
421433 regex = args .regex
422434 if regex == DEFAULT_REGEX :
423- regex = fr'(?P<path> { PATH_REGEX } ):(?P<line>\d+)'
435+ regex = DEFAULT_LINENUM_REGEX
424436 yield from mark (regex , [brackets , quotes ], text , args )
425437
426438
@@ -699,18 +711,14 @@ def main(args: List[str]) -> Optional[Dict[str, Any]]:
699711
700712
701713def linenum_process_result (data : Dict [str , Any ]) -> Tuple [str , int ]:
702- pat = re .compile (r':(\d+)$' )
703- for m , g in zip (data ['match' ], data ['groupdicts' ]):
704- if m :
705- path , line = g ['path' ], g ['line' ]
706- # look for trailers on path of the for :number
707- while True :
708- m = pat .search (path )
709- if m is None :
710- break
711- line = m .group (1 )
712- path = path [:- len (m .group ())]
713-
714+ lnum_pat = re .compile (r'(:\d+)$' )
715+ for match , g in zip (data ['match' ], data ['groupdicts' ]):
716+ path , line = g ['path' ], g ['line' ]
717+ if path and line :
718+ m = lnum_pat .search (path )
719+ if m is not None :
720+ line = m .group (1 )[1 :]
721+ path = path .rpartition (':' )[0 ]
714722 return os .path .expanduser (path ), int (line )
715723 return '' , - 1
716724
0 commit comments