diff --git a/README.md b/README.md index 94620f7..3b3d116 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ doesn't support: - [x] package names - [ ] `build.sh` - [x] `PKGBUILD`: by [pyalpm](https://github.com/ornitorrincos/pyalpm) - - [ ] `ebuild` + - [x] `ebuild`: by [portage](https://wiki.gentoo.org/wiki/Portage) - [ ] [Code Action](https://microsoft.github.io/language-server-protocol/specifications/specification-current#textDocument_codeAction) - [ ] `PKGBUILD` - [ ] generate a template by the name of directory containing `PKGBUILD`, the @@ -141,7 +141,7 @@ Other features: ## How Does It Work -See [here](https://github.com/neomutt/lsp-tree-sitter#usage). +See [lsp-tree-sitter documentation](https://github.com/neomutt/lsp-tree-sitter#usage). Read [![readthedocs](https://shields.io/readthedocs/termux-language-server)](https://termux-language-server.readthedocs.io) diff --git a/docs/conf.py b/docs/conf.py index 7d81290..758a494 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,13 +3,14 @@ https://www.sphinx-doc.org/en/master/usage/configuration.html """ -from termux_language_server import __version__ as version # type: ignore from termux_language_server._metainfo import ( # type: ignore author, copyright, project, ) +from termux_language_server import __version__ as version # type: ignore + __all__ = ["version", "author", "copyright", "project"] # -- Path setup -------------------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 6a0f024..ab27a92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -82,6 +82,9 @@ file = "requirements/misc.txt" [tool.setuptools.dynamic.optional-dependencies.pkgbuild] file = "requirements/pkgbuild.txt" + +[tool.setuptools.dynamic.optional-dependencies.ebuild] +file = "requirements/ebuild.txt" # end: scripts/update-pyproject.toml.pl [tool.setuptools_scm] diff --git a/src/termux_language_server/assets/jinja2/ebuild.md.j2 b/src/termux_language_server/assets/jinja2/ebuild.md.j2 new file mode 100644 index 0000000..242e506 --- /dev/null +++ b/src/termux_language_server/assets/jinja2/ebuild.md.j2 @@ -0,0 +1,7 @@ +{{ pkg.description }} + +- version: **{{ pkg.version }}** +- slot: **{{ pkg.slot }}** +- homepage: <{{ pkg.homepage }}> +- license: *{{ pkg.license }}* +- keywords: {{ pkg.keywords }} diff --git a/src/termux_language_server/assets/queries/package.scm b/src/termux_language_server/assets/queries/package.scm index dcb6324..f104f2b 100644 --- a/src/termux_language_server/assets/queries/package.scm +++ b/src/termux_language_server/assets/queries/package.scm @@ -4,6 +4,6 @@ value: (array (word) @package) ) (#match? - @variable.name "^(depends|optdepends|makedepends|conflicts|provides)$" + @variable.name "^(depends|optdepends|makedepends|conflicts|provides|DEPEND|RDEPEND|BDEPEND|IDEPEND|PDEPEND)$" ) ) diff --git a/src/termux_language_server/packages/__init__.py b/src/termux_language_server/packages/__init__.py index bdc924f..1b87d7f 100644 --- a/src/termux_language_server/packages/__init__.py +++ b/src/termux_language_server/packages/__init__.py @@ -12,7 +12,14 @@ "conflicts", "provides", "replaces", - } + }, + "ebuild": { + "DEPEND", + "RDEPEND", + "BDEPEND", + "IDEPEND", + "PDEPEND", + }, } @@ -27,6 +34,8 @@ def search_package_document(name: str, filetype: FILETYPE) -> str: """ if filetype == "PKGBUILD": from .pkgbuild import get_package_document + elif filetype == "ebuild": + from .ebuild import get_package_document else: raise NotImplementedError return get_package_document(name) @@ -43,6 +52,8 @@ def search_package_names(name: str, filetype: FILETYPE) -> dict[str, str]: """ if filetype == "PKGBUILD": from .pkgbuild import get_package_names + elif filetype == "ebuild": + from .ebuild import get_package_names else: raise NotImplementedError return get_package_names(name) diff --git a/src/termux_language_server/packages/ebuild.py b/src/termux_language_server/packages/ebuild.py new file mode 100644 index 0000000..e7f1ea8 --- /dev/null +++ b/src/termux_language_server/packages/ebuild.py @@ -0,0 +1,70 @@ +r"""Ebuild packages +================== +""" + +from concurrent.futures import ThreadPoolExecutor +from pathlib import Path +from types import SimpleNamespace + +from jinja2 import Template +from platformdirs import user_config_path +from portage import db, root + +PORTTREE = db[root]["porttree"].dbapi +_ALL_PACKAGES = PORTTREE.cp_all() +_EXECUTOR = ThreadPoolExecutor(max_workers=1) +TEMPLATE_NAME = "ebuild.md.j2" +PATH = user_config_path("portage") / TEMPLATE_NAME +if not PATH.exists(): + PATH = Path(__file__).parent.parent / "assets" / "jinja2" / TEMPLATE_NAME +TEMPLATE = PATH.read_text() + + +def _render_document(cp: str, template: str = TEMPLATE) -> str: + r"""Render document. + + :param cp: + :type cp: str + :param template: + :type template: str + :rtype: str + """ + versions = PORTTREE.cp_list(cp) + if not versions: + return "" + cpv = versions[-1] + description, homepage, license_, slot, keywords = PORTTREE.aux_get( + cpv, ["DESCRIPTION", "HOMEPAGE", "LICENSE", "SLOT", "KEYWORDS"] + ) + version = cpv[len(cp) + 1 :] + pkg = SimpleNamespace( + description=description, + version=version, + slot=slot, + homepage=homepage, + license=license_, + keywords=keywords, + ) + return Template(template).render(pkg=pkg) + + +def get_package_document(name: str, template: str = TEMPLATE) -> str: + r"""Get package document. + + :param name: + :type name: str + :param template: + :type template: str + :rtype: str + """ + return _EXECUTOR.submit(_render_document, name, template).result() + + +def get_package_names(name: str) -> dict[str, str]: + r"""Get package names. + + :param name: + :type name: str + :rtype: dict[str, str] + """ + return {cp: "" for cp in _ALL_PACKAGES if cp.startswith(name)} diff --git a/src/termux_language_server/server.py b/src/termux_language_server/server.py index 7454411..3776611 100644 --- a/src/termux_language_server/server.py +++ b/src/termux_language_server/server.py @@ -27,7 +27,9 @@ Hover, MarkupContent, MarkupKind, + Position, PublishDiagnosticsParams, + Range, TextDocumentPositionParams, TextEdit, ) @@ -47,6 +49,63 @@ ) from .utils import get_filetype, get_schema, parser +RE_PKG_START = re.compile(r"[A-Za-z_0-9/\-\.>== Hover | None: } ): if ( - parent.type == "array" + parent.type in {"array", "string"} and parent.parent is not None and parent.parent.children[0].text is not None and parent.parent.children[0].text.decode() in PACKAGE_VARIABLES.get(filetype, set()) ): - result = search_package_document(text, filetype) + result = search_package_document( + document.word_at_position( + params.position, RE_PKG_START, RE_PKG_END + ) + if parent.type == "string" + else text, + filetype, + ) if result is None: return None return Hover( @@ -239,34 +305,40 @@ def completions(params: CompletionParams) -> CompletionList: uni = PositionFinder(params.position, right_equal=True).find( document.uri, self.trees[document.uri] ) - if uni is None: - return CompletionList(False, []) + if uni is None or uni.node.type == '"': + if _is_in_dep_string( + self.trees[document.uri], + params.position, + filetype, + ): + return _package_completions( + document.word_at_position( + params.position, RE_PKG_START, RE_EMPTY + ), + filetype, + params.position, + ) + if uni is None: + return CompletionList(False, []) parent = uni.node.parent if parent is None: return CompletionList(False, []) text = uni.text if ( - parent.type == "array" + parent.type in {"array", "string"} and parent.parent is not None and parent.parent.children[0].text is not None and parent.parent.children[0].text.decode() in PACKAGE_VARIABLES.get(filetype, set()) ): - return CompletionList( - False, - [ - CompletionItem( - k, - kind=CompletionItemKind.Module, - documentation=MarkupContent( - MarkupKind.Markdown, v - ), - insert_text=k, - ) - for k, v in search_package_names( - text, filetype - ).items() - ], + return _package_completions( + document.word_at_position( + params.position, RE_PKG_START, RE_EMPTY + ) + if parent.type == "string" + else text, + filetype, + params.position, ) schema = get_schema(filetype) if ( diff --git a/tests/test_schema.py b/tests/test_schema.py index 4298d9e..6e52446 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -3,6 +3,7 @@ import os from lsp_tree_sitter.finders import SchemaFinder + from termux_language_server.schema import BashTrie from termux_language_server.utils import get_filetype, get_schema, parser