11import re
22from pathlib import Path
3- from typing import Protocol
3+ from typing import NamedTuple , Protocol
44
55from attrs import define
66from lsap_schema .locate import (
@@ -43,51 +43,54 @@ def _to_regex(text: str) -> str:
4343 if not tokens :
4444 return ""
4545
46- result : list [str ] = []
47- for i , token in enumerate (tokens ):
48- if token [0 ].isspace ():
49- result .append (r"\s+" )
50- else :
51- result .append (re .escape (token ))
52- if i < len (tokens ) - 1 and not tokens [i + 1 ][0 ].isspace ():
53- result .append (r"\s*" )
54- return "" .join (result )
46+ def parts ():
47+ for i , token in enumerate (tokens ):
48+ if token [0 ].isspace ():
49+ yield r"\s+"
50+ else :
51+ yield re .escape (token )
52+ if i < len (tokens ) - 1 and not tokens [i + 1 ][0 ].isspace ():
53+ yield r"\s*"
54+
55+ return "" .join (parts ())
56+
57+
58+ class ScopeInfo (NamedTuple ):
59+ range : LSPRange
60+ selection_start : LSPPosition | None
5561
5662
5763async def _get_scope_info (
5864 client : LocateClient ,
5965 file_path : Path ,
6066 scope : LineScope | SymbolScope | None ,
6167 reader : DocumentReader ,
62- ) -> tuple [ LSPRange , LSPPosition | None ] :
68+ ) -> ScopeInfo :
6369 match scope :
6470 case None :
65- return reader .full_range , None
71+ return ScopeInfo ( reader .full_range , None )
6672
6773 case LineScope (line = line ):
68- if isinstance (line , int ):
69- start , end = line - 1 , line - 1
70- else :
71- start , end = line [0 ] - 1 , line [1 ] - 1
74+ match line :
75+ case int ():
76+ start , end = line - 1 , line - 1
77+ case (s , e ):
78+ start , end = s - 1 , e - 1
7279
73- return (
80+ return ScopeInfo (
7481 LSPRange (
7582 start = LSPPosition (line = start , character = 0 ),
7683 end = LSPPosition (line = end + 1 , character = 0 ),
7784 ),
7885 None ,
7986 )
80-
8187 case SymbolScope (symbol_path = path ):
8288 symbols = await client .request_document_symbol_list (file_path )
8389 for s_path , symbol in iter_symbols (symbols or []):
8490 if s_path == path :
85- return symbol .range , symbol .selection_range .start
91+ return ScopeInfo ( symbol .range , symbol .selection_range .start )
8692 raise NotFoundError (f"Symbol { path } not found in { file_path } " )
8793
88- case _:
89- return reader .full_range , None
90-
9194
9295@define
9396class LocateCapability (Capability [LocateClient , LocateRequest , LocateResponse ]):
@@ -96,21 +99,18 @@ async def __call__(self, req: LocateRequest) -> LocateResponse | None:
9699 document = await self .client .read_file (locate .file_path )
97100 reader = DocumentReader (document )
98101
99- scope_range , selection_start = await _get_scope_info (
102+ info = await _get_scope_info (
100103 self .client , locate .file_path , locate .scope , reader
101104 )
102105
103- snippet = reader .read (scope_range )
106+ snippet = reader .read (info . range )
104107 if not snippet :
105108 return None
106109
107110 pos : LSPPosition | None = None
108111
109112 if locate .find :
110- # Auto-detect marker in the find text
111- marker_info = detect_marker (locate .find )
112-
113- if marker_info :
113+ if marker_info := detect_marker (locate .find ):
114114 marker , _ , _ = marker_info
115115 before , _ , after = locate .find .partition (marker )
116116 re_before , re_after = _to_regex (before ), _to_regex (after )
@@ -129,11 +129,17 @@ async def __call__(self, req: LocateRequest) -> LocateResponse | None:
129129 return None
130130
131131 pos = reader .offset_to_position (snippet .range .start , offset )
132- elif isinstance (locate .scope , SymbolScope ):
133- pos = selection_start
134- elif isinstance (locate .scope , LineScope ):
135- m = re .search (r"\S" , snippet .exact_content )
136- pos = reader .offset_to_position (snippet .range .start , m .start () if m else 0 )
132+ else :
133+ match locate .scope :
134+ case SymbolScope ():
135+ pos = info .selection_start
136+ case LineScope ():
137+ m = re .search (r"\S" , snippet .exact_content )
138+ pos = reader .offset_to_position (
139+ snippet .range .start , m .start () if m else 0
140+ )
141+ case _:
142+ pos = info .range .start
137143
138144 if pos :
139145 return LocateResponse (
@@ -152,20 +158,20 @@ async def __call__(self, req: LocateRangeRequest) -> LocateRangeResponse | None:
152158 document = await self .client .read_file (locate .file_path )
153159 reader = DocumentReader (document )
154160
155- scope_range , _ = await _get_scope_info (
161+ info = await _get_scope_info (
156162 self .client , locate .file_path , locate .scope , reader
157163 )
158164
159165 final_range : LSPRange | None = None
160166
161167 if locate .find :
162- snippet = reader .read (scope_range )
168+ snippet = reader .read (info . range )
163169 if not snippet :
164170 return None
165171
166172 re_find = _to_regex (locate .find )
167173 if not re_find :
168- final_range = scope_range
174+ final_range = info . range
169175 elif m := re .search (re_find , snippet .exact_content ):
170176 final_range = LSPRange (
171177 start = reader .offset_to_position (snippet .range .start , m .start ()),
@@ -174,7 +180,7 @@ async def __call__(self, req: LocateRangeRequest) -> LocateRangeResponse | None:
174180 else :
175181 return None
176182 else :
177- final_range = scope_range
183+ final_range = info . range
178184
179185 if final_range :
180186 return LocateRangeResponse (
@@ -184,3 +190,4 @@ async def __call__(self, req: LocateRangeRequest) -> LocateRangeResponse | None:
184190 end = Position .from_lsp (final_range .end ),
185191 ),
186192 )
193+ return None
0 commit comments