Skip to content

Commit fc88a85

Browse files
committed
Add completions for Gentoo ebuilds in dependency variables
1 parent ede34f0 commit fc88a85

6 files changed

Lines changed: 117 additions & 23 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ doesn't support:
8585
- [x] package names
8686
- [ ] `build.sh`
8787
- [x] `PKGBUILD`: by [pyalpm](https://github.com/ornitorrincos/pyalpm)
88-
- [ ] `ebuild`
88+
- [x] `ebuild`: by [portage](https://wiki.gentoo.org/wiki/Portage)
8989
- [ ] [Code Action](https://microsoft.github.io/language-server-protocol/specifications/specification-current#textDocument_codeAction)
9090
- [ ] `PKGBUILD`
9191
- [ ] generate a template by the name of directory containing `PKGBUILD`, the

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ file = "requirements/misc.txt"
8282

8383
[tool.setuptools.dynamic.optional-dependencies.pkgbuild]
8484
file = "requirements/pkgbuild.txt"
85+
86+
[tool.setuptools.dynamic.optional-dependencies.ebuild]
87+
file = "requirements/ebuild.txt"
8588
# end: scripts/update-pyproject.toml.pl
8689

8790
[tool.setuptools_scm]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{{ pkg.description }}
2+
3+
- version: **{{ pkg.version }}**
4+
- slot: **{{ pkg.slot }}**
5+
- homepage: <{{ pkg.homepage }}>
6+
- license: *{{ pkg.license }}*
7+
- keywords: {{ pkg.keywords }}

src/termux_language_server/assets/queries/package.scm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
value: (array (word) @package)
55
)
66
(#match?
7-
@variable.name "^(depends|optdepends|makedepends|conflicts|provides)$"
7+
@variable.name "^(depends|optdepends|makedepends|conflicts|provides|DEPEND|RDEPEND|BDEPEND|IDEPEND|PDEPEND)$"
88
)
99
)

src/termux_language_server/packages/__init__.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,14 @@
1212
"conflicts",
1313
"provides",
1414
"replaces",
15-
}
15+
},
16+
"ebuild": {
17+
"DEPEND",
18+
"RDEPEND",
19+
"BDEPEND",
20+
"IDEPEND",
21+
"PDEPEND",
22+
},
1623
}
1724

1825

@@ -27,6 +34,8 @@ def search_package_document(name: str, filetype: FILETYPE) -> str:
2734
"""
2835
if filetype == "PKGBUILD":
2936
from .pkgbuild import get_package_document
37+
elif filetype == "ebuild":
38+
from .ebuild import get_package_document
3039
else:
3140
raise NotImplementedError
3241
return get_package_document(name)
@@ -43,6 +52,8 @@ def search_package_names(name: str, filetype: FILETYPE) -> dict[str, str]:
4352
"""
4453
if filetype == "PKGBUILD":
4554
from .pkgbuild import get_package_names
55+
elif filetype == "ebuild":
56+
from .ebuild import get_package_names
4657
else:
4758
raise NotImplementedError
4859
return get_package_names(name)

src/termux_language_server/server.py

Lines changed: 93 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
Hover,
2828
MarkupContent,
2929
MarkupKind,
30+
Position,
3031
PublishDiagnosticsParams,
32+
Range,
3133
TextDocumentPositionParams,
3234
TextEdit,
3335
)
@@ -48,6 +50,64 @@
4850
from .utils import get_filetype, get_schema, parser
4951

5052

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+
51111
class TermuxLanguageServer(LanguageServer):
52112
r"""Termux language server."""
53113

@@ -190,13 +250,20 @@ def hover(params: TextDocumentPositionParams) -> Hover | None:
190250
}
191251
):
192252
if (
193-
parent.type == "array"
253+
parent.type in {"array", "string"}
194254
and parent.parent is not None
195255
and parent.parent.children[0].text is not None
196256
and parent.parent.children[0].text.decode()
197257
in PACKAGE_VARIABLES.get(filetype, set())
198258
):
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+
)
200267
if result is None:
201268
return None
202269
return Hover(
@@ -239,34 +306,40 @@ def completions(params: CompletionParams) -> CompletionList:
239306
uni = PositionFinder(params.position, right_equal=True).find(
240307
document.uri, self.trees[document.uri]
241308
)
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, [])
244324
parent = uni.node.parent
245325
if parent is None:
246326
return CompletionList(False, [])
247327
text = uni.text
248328
if (
249-
parent.type == "array"
329+
parent.type in {"array", "string"}
250330
and parent.parent is not None
251331
and parent.parent.children[0].text is not None
252332
and parent.parent.children[0].text.decode()
253333
in PACKAGE_VARIABLES.get(filetype, set())
254334
):
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,
270343
)
271344
schema = get_schema(filetype)
272345
if (

0 commit comments

Comments
 (0)