|
27 | 27 | Hover, |
28 | 28 | MarkupContent, |
29 | 29 | MarkupKind, |
| 30 | + Position, |
30 | 31 | PublishDiagnosticsParams, |
| 32 | + Range, |
31 | 33 | TextDocumentPositionParams, |
32 | 34 | TextEdit, |
33 | 35 | ) |
|
48 | 50 | from .utils import get_filetype, get_schema, parser |
49 | 51 |
|
50 | 52 |
|
| 53 | +RE_PKG_START = re.compile(r"[A-Za-z_0-9/\-\.>=<!~*]*$") |
| 54 | +RE_PKG_END = re.compile(r"^[A-Za-z_0-9/\-\.>=<!~*]*") |
| 55 | +RE_EMPTY = re.compile(r"^") |
| 56 | + |
| 57 | + |
| 58 | +def _is_in_dep_string(tree, position, filetype): |
| 59 | + r"""Check if cursor is inside a dependency variable's string. |
| 60 | +
|
| 61 | + :param tree: |
| 62 | + :param position: |
| 63 | + :type position: Position |
| 64 | + :param filetype: |
| 65 | + :type filetype: str |
| 66 | + :rtype: bool |
| 67 | + """ |
| 68 | + point = (position.line, position.character) |
| 69 | + node = tree.root_node.descendant_for_point_range(point, point) |
| 70 | + return ( |
| 71 | + node is not None |
| 72 | + and node.type == "string" |
| 73 | + and node.parent is not None |
| 74 | + and node.parent.children[0].text is not None |
| 75 | + and node.parent.children[0].text.decode() |
| 76 | + in PACKAGE_VARIABLES.get(filetype, set()) |
| 77 | + ) |
| 78 | + |
| 79 | + |
| 80 | +def _package_completions(prefix, filetype, position): |
| 81 | + r"""Build completion list for package names. |
| 82 | +
|
| 83 | + :param prefix: |
| 84 | + :type prefix: str |
| 85 | + :param filetype: |
| 86 | + :type filetype: str |
| 87 | + :param position: |
| 88 | + :type position: Position |
| 89 | + :rtype: CompletionList |
| 90 | + """ |
| 91 | + edit_range = Range( |
| 92 | + start=Position(position.line, position.character - len(prefix)), |
| 93 | + end=position, |
| 94 | + ) |
| 95 | + return CompletionList( |
| 96 | + False, |
| 97 | + [ |
| 98 | + CompletionItem( |
| 99 | + k, |
| 100 | + kind=CompletionItemKind.Module, |
| 101 | + documentation=MarkupContent(MarkupKind.Markdown, v) |
| 102 | + if v |
| 103 | + else None, |
| 104 | + text_edit=TextEdit(range=edit_range, new_text=k), |
| 105 | + ) |
| 106 | + for k, v in search_package_names(prefix, filetype).items() |
| 107 | + ], |
| 108 | + ) |
| 109 | + |
| 110 | + |
51 | 111 | class TermuxLanguageServer(LanguageServer): |
52 | 112 | r"""Termux language server.""" |
53 | 113 |
|
@@ -190,13 +250,20 @@ def hover(params: TextDocumentPositionParams) -> Hover | None: |
190 | 250 | } |
191 | 251 | ): |
192 | 252 | if ( |
193 | | - parent.type == "array" |
| 253 | + parent.type in {"array", "string"} |
194 | 254 | and parent.parent is not None |
195 | 255 | and parent.parent.children[0].text is not None |
196 | 256 | and parent.parent.children[0].text.decode() |
197 | 257 | in PACKAGE_VARIABLES.get(filetype, set()) |
198 | 258 | ): |
199 | | - result = search_package_document(text, filetype) |
| 259 | + result = search_package_document( |
| 260 | + document.word_at_position( |
| 261 | + params.position, RE_PKG_START, RE_PKG_END |
| 262 | + ) |
| 263 | + if parent.type == "string" |
| 264 | + else text, |
| 265 | + filetype, |
| 266 | + ) |
200 | 267 | if result is None: |
201 | 268 | return None |
202 | 269 | return Hover( |
@@ -239,34 +306,40 @@ def completions(params: CompletionParams) -> CompletionList: |
239 | 306 | uni = PositionFinder(params.position, right_equal=True).find( |
240 | 307 | document.uri, self.trees[document.uri] |
241 | 308 | ) |
242 | | - if uni is None: |
243 | | - return CompletionList(False, []) |
| 309 | + if uni is None or uni.node.type == '"': |
| 310 | + if _is_in_dep_string( |
| 311 | + self.trees[document.uri], |
| 312 | + params.position, |
| 313 | + filetype, |
| 314 | + ): |
| 315 | + return _package_completions( |
| 316 | + document.word_at_position( |
| 317 | + params.position, RE_PKG_START, RE_EMPTY |
| 318 | + ), |
| 319 | + filetype, |
| 320 | + params.position, |
| 321 | + ) |
| 322 | + if uni is None: |
| 323 | + return CompletionList(False, []) |
244 | 324 | parent = uni.node.parent |
245 | 325 | if parent is None: |
246 | 326 | return CompletionList(False, []) |
247 | 327 | text = uni.text |
248 | 328 | if ( |
249 | | - parent.type == "array" |
| 329 | + parent.type in {"array", "string"} |
250 | 330 | and parent.parent is not None |
251 | 331 | and parent.parent.children[0].text is not None |
252 | 332 | and parent.parent.children[0].text.decode() |
253 | 333 | in PACKAGE_VARIABLES.get(filetype, set()) |
254 | 334 | ): |
255 | | - return CompletionList( |
256 | | - False, |
257 | | - [ |
258 | | - CompletionItem( |
259 | | - k, |
260 | | - kind=CompletionItemKind.Module, |
261 | | - documentation=MarkupContent( |
262 | | - MarkupKind.Markdown, v |
263 | | - ), |
264 | | - insert_text=k, |
265 | | - ) |
266 | | - for k, v in search_package_names( |
267 | | - text, filetype |
268 | | - ).items() |
269 | | - ], |
| 335 | + return _package_completions( |
| 336 | + document.word_at_position( |
| 337 | + params.position, RE_PKG_START, RE_EMPTY |
| 338 | + ) |
| 339 | + if parent.type == "string" |
| 340 | + else text, |
| 341 | + filetype, |
| 342 | + params.position, |
270 | 343 | ) |
271 | 344 | schema = get_schema(filetype) |
272 | 345 | if ( |
|
0 commit comments