hints kitten: Fix surrounding quotes/brackets and embedded carriage returns not being removed when using line number processing
Fixes #5170
This commit is contained in:
parent
76531d2f7a
commit
cd7751681d
@ -67,6 +67,9 @@ Detailed list of changes
|
|||||||
and :command:`kitty @ select-window` not working correctly
|
and :command:`kitty @ select-window` not working correctly
|
||||||
when using a socket (:iss:`5165`)
|
when using a socket (:iss:`5165`)
|
||||||
|
|
||||||
|
- hints kitten: Fix surrounding quotes/brackets and embedded carriage returns
|
||||||
|
not being removed when using line number processing (:iss:`5170`)
|
||||||
|
|
||||||
|
|
||||||
0.25.1 [2022-05-26]
|
0.25.1 [2022-05-26]
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|||||||
@ -9,8 +9,8 @@ from functools import lru_cache
|
|||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from itertools import repeat
|
from itertools import repeat
|
||||||
from typing import (
|
from typing import (
|
||||||
Any, Callable, Dict, Generator, Iterable, List, Optional, Pattern,
|
Any, Callable, Dict, Generator, Iterable, Iterator, List, Optional,
|
||||||
Sequence, Set, Tuple, Type, cast
|
Pattern, Sequence, Set, Tuple, Type, cast
|
||||||
)
|
)
|
||||||
|
|
||||||
from kitty.cli import parse_args
|
from kitty.cli import parse_args
|
||||||
@ -44,6 +44,7 @@ DEFAULT_HINT_ALPHABET = string.digits + string.ascii_lowercase
|
|||||||
DEFAULT_REGEX = r'(?m)^\s*(.+)\s*$'
|
DEFAULT_REGEX = r'(?m)^\s*(.+)\s*$'
|
||||||
FILE_EXTENSION = r'\.(?:[a-zA-Z0-9]{2,7}|[ahcmo])(?!\.)'
|
FILE_EXTENSION = r'\.(?:[a-zA-Z0-9]{2,7}|[ahcmo])(?!\.)'
|
||||||
PATH_REGEX = fr'(?:\S*?/[\r\S]+)|(?:\S[\r\S]*{FILE_EXTENSION})\b'
|
PATH_REGEX = fr'(?:\S*?/[\r\S]+)|(?:\S[\r\S]*{FILE_EXTENSION})\b'
|
||||||
|
DEFAULT_LINENUM_REGEX = fr'(?P<path>{PATH_REGEX}):(?P<line>\d+)'
|
||||||
|
|
||||||
|
|
||||||
class Mark:
|
class Mark:
|
||||||
@ -64,6 +65,10 @@ class Mark:
|
|||||||
self.is_hyperlink = is_hyperlink
|
self.is_hyperlink = is_hyperlink
|
||||||
self.group_id = group_id
|
self.group_id = group_id
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (f'Mark(index={self.index!r}, start={self.start!r}, end={self.end!r},'
|
||||||
|
f' text={self.text!r}, groupdict={self.groupdict!r}, is_hyperlink={self.is_hyperlink!r}, group_id={self.group_id!r})')
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=2048)
|
@lru_cache(maxsize=2048)
|
||||||
def encode_hint(num: int, alphabet: str) -> str:
|
def encode_hint(num: int, alphabet: str) -> str:
|
||||||
@ -225,14 +230,14 @@ class Hints(Handler):
|
|||||||
self.write(self.current_text)
|
self.write(self.current_text)
|
||||||
|
|
||||||
|
|
||||||
def regex_finditer(pat: 'Pattern[str]', minimum_match_length: int, text: str) -> Generator[Tuple[int, int, Dict[str, str]], None, None]:
|
def regex_finditer(pat: 'Pattern[str]', minimum_match_length: int, text: str) -> Iterator[Tuple[int, int, 're.Match[str]']]:
|
||||||
has_named_groups = bool(pat.groupindex)
|
has_named_groups = bool(pat.groupindex)
|
||||||
for m in pat.finditer(text):
|
for m in pat.finditer(text):
|
||||||
s, e = m.span(0 if has_named_groups else pat.groups)
|
s, e = m.span(0 if has_named_groups else pat.groups)
|
||||||
while e > s + 1 and text[e-1] == '\0':
|
while e > s + 1 and text[e-1] == '\0':
|
||||||
e -= 1
|
e -= 1
|
||||||
if e - s >= minimum_match_length:
|
if e - s >= minimum_match_length:
|
||||||
yield s, e, m.groupdict()
|
yield s, e, m
|
||||||
|
|
||||||
|
|
||||||
closing_bracket_map = {'(': ')', '[': ']', '{': '}', '<': '>', '*': '*', '"': '"', "'": "'", "“": "”", "‘": "’"}
|
closing_bracket_map = {'(': ')', '[': ']', '{': '}', '<': '>', '*': '*', '"': '"', "'": "'", "“": "”", "‘": "’"}
|
||||||
@ -318,16 +323,23 @@ def ip(text: str, s: int, e: int) -> Tuple[int, int]:
|
|||||||
return s, e
|
return s, e
|
||||||
|
|
||||||
|
|
||||||
def mark(pattern: str, post_processors: Iterable[PostprocessorFunc], text: str, args: HintsCLIOptions) -> Generator[Mark, None, None]:
|
def mark(pattern: str, post_processors: Iterable[PostprocessorFunc], text: str, args: HintsCLIOptions) -> Iterator[Mark]:
|
||||||
pat = re.compile(pattern)
|
pat = re.compile(pattern)
|
||||||
for idx, (s, e, groupdict) in enumerate(regex_finditer(pat, args.minimum_match_length, text)):
|
sanitize_pat = re.compile('[\r\n\0]')
|
||||||
|
for idx, (s, e, match_object) in enumerate(regex_finditer(pat, args.minimum_match_length, text)):
|
||||||
try:
|
try:
|
||||||
for func in post_processors:
|
for func in post_processors:
|
||||||
s, e = func(text, s, e)
|
s, e = func(text, s, e)
|
||||||
|
groupdict = match_object.groupdict()
|
||||||
|
for group_name in groupdict:
|
||||||
|
group_idx = pat.groupindex[group_name]
|
||||||
|
gs, ge = match_object.span(group_idx)
|
||||||
|
gs, ge = max(gs, s), min(ge, e)
|
||||||
|
groupdict[group_name] = sanitize_pat.sub('', text[gs:ge])
|
||||||
except InvalidMatch:
|
except InvalidMatch:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
mark_text = re.sub('[\r\n\0]', '', text[s:e])
|
mark_text = sanitize_pat.sub('', text[s:e])
|
||||||
yield Mark(idx, s, e, mark_text, groupdict)
|
yield Mark(idx, s, e, mark_text, groupdict)
|
||||||
|
|
||||||
|
|
||||||
@ -420,7 +432,7 @@ def parse_input(text: str) -> str:
|
|||||||
def linenum_marks(text: str, args: HintsCLIOptions, Mark: Type[Mark], extra_cli_args: Sequence[str], *a: Any) -> Generator[Mark, None, None]:
|
def linenum_marks(text: str, args: HintsCLIOptions, Mark: Type[Mark], extra_cli_args: Sequence[str], *a: Any) -> Generator[Mark, None, None]:
|
||||||
regex = args.regex
|
regex = args.regex
|
||||||
if regex == DEFAULT_REGEX:
|
if regex == DEFAULT_REGEX:
|
||||||
regex = fr'(?P<path>{PATH_REGEX}):(?P<line>\d+)'
|
regex = DEFAULT_LINENUM_REGEX
|
||||||
yield from mark(regex, [brackets, quotes], text, args)
|
yield from mark(regex, [brackets, quotes], text, args)
|
||||||
|
|
||||||
|
|
||||||
@ -699,18 +711,14 @@ def main(args: List[str]) -> Optional[Dict[str, Any]]:
|
|||||||
|
|
||||||
|
|
||||||
def linenum_process_result(data: Dict[str, Any]) -> Tuple[str, int]:
|
def linenum_process_result(data: Dict[str, Any]) -> Tuple[str, int]:
|
||||||
pat = re.compile(r':(\d+)$')
|
lnum_pat = re.compile(r'(:\d+)$')
|
||||||
for m, g in zip(data['match'], data['groupdicts']):
|
for match, g in zip(data['match'], data['groupdicts']):
|
||||||
if m:
|
path, line = g['path'], g['line']
|
||||||
path, line = g['path'], g['line']
|
if path and line:
|
||||||
# look for trailers on path of the for :number
|
m = lnum_pat.search(path)
|
||||||
while True:
|
if m is not None:
|
||||||
m = pat.search(path)
|
line = m.group(1)[1:]
|
||||||
if m is None:
|
path = path.rpartition(':')[0]
|
||||||
break
|
|
||||||
line = m.group(1)
|
|
||||||
path = path[:-len(m.group())]
|
|
||||||
|
|
||||||
return os.path.expanduser(path), int(line)
|
return os.path.expanduser(path), int(line)
|
||||||
return '', -1
|
return '', -1
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user