From 563c74e4fe0e4835c0a3051a650dbbde403870ab Mon Sep 17 00:00:00 2001 From: Wu Zhenyu Date: Fri, 27 Oct 2023 01:11:52 +0800 Subject: [PATCH] :fire: Move tree-sitter-lsp to a separate repo --- pyproject.toml | 9 +- requirements.txt | 6 +- requirements/colorize.txt | 2 +- requirements/misc.txt | 5 +- src/termux_language_server/__main__.py | 8 +- src/termux_language_server/finders.py | 10 +- src/termux_language_server/misc/color_map.py | 3 +- src/termux_language_server/misc/ebuild.py | 3 +- src/termux_language_server/misc/make_conf.py | 3 +- .../misc/makepkg_conf.py | 3 +- src/termux_language_server/misc/pkgbuild.py | 2 +- src/termux_language_server/misc/termux.py | 3 +- src/termux_language_server/misc/utils.py | 83 --- src/termux_language_server/schema.py | 5 +- src/termux_language_server/server.py | 6 +- .../tree_sitter_lsp/__init__.py | 438 --------------- .../tree_sitter_lsp/complete.py | 48 -- .../tree_sitter_lsp/diagnose.py | 196 ------- .../tree_sitter_lsp/finders.py | 530 ------------------ .../tree_sitter_lsp/format.py | 166 ------ .../tree_sitter_lsp/schema.py | 133 ----- .../tree_sitter_lsp/utils.py | 99 ---- tests/test_schema.py | 3 +- 23 files changed, 34 insertions(+), 1730 deletions(-) delete mode 100644 src/termux_language_server/misc/utils.py delete mode 100644 src/termux_language_server/tree_sitter_lsp/__init__.py delete mode 100644 src/termux_language_server/tree_sitter_lsp/complete.py delete mode 100644 src/termux_language_server/tree_sitter_lsp/diagnose.py delete mode 100644 src/termux_language_server/tree_sitter_lsp/finders.py delete mode 100644 src/termux_language_server/tree_sitter_lsp/format.py delete mode 100644 src/termux_language_server/tree_sitter_lsp/schema.py delete mode 100644 src/termux_language_server/tree_sitter_lsp/utils.py diff --git a/pyproject.toml b/pyproject.toml index bbd20b5..78b2b0d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,19 +31,16 @@ classifiers = [ # https://github.com/pypa/twine/issues/753 dynamic = ["version"] dependencies = [ - "colorama", "fqdn", - "jinja2", "platformdirs", - "pygls", "rfc3987", - "tree-sitter", + "tree-sitter-lsp", ] [project.optional-dependencies] -colorize = ["pygments"] +colorize = ["tree-sitter-lsp[colorize]"] dev = ["pytest-cov"] -misc = ["beautifulsoup4", "markdown-it-py", "pypandoc"] +misc = ["tree-sitter-lsp[misc]"] # pyproject.toml doesn't support git+git:// pkgbuild = ["pyalpm"] diff --git a/requirements.txt b/requirements.txt index 76e6874..08f1e44 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,10 @@ #!/usr/bin/env -S pip install -r -colorama # https://python-jsonschema.readthedocs.io/en/stable/validate/#validating-formats # hostname fqdn -jinja2 -jsonschema # get nvim-tree-sitter's parser platformdirs -pygls # uri rfc3987 -tree-sitter +tree-sitter-lsp diff --git a/requirements/colorize.txt b/requirements/colorize.txt index 0b92a1c..e3170b3 100755 --- a/requirements/colorize.txt +++ b/requirements/colorize.txt @@ -1,3 +1,3 @@ #!/usr/bin/env -S pip install -r -pygments +tree-sitter-lsp[colorize] diff --git a/requirements/misc.txt b/requirements/misc.txt index 45b42b2..ab6be67 100755 --- a/requirements/misc.txt +++ b/requirements/misc.txt @@ -1,6 +1,3 @@ #!/usr/bin/env -S pip install -r -# See . -beautifulsoup4 -markdown-it-py -pypandoc +tree-sitter-lsp[misc] diff --git a/src/termux_language_server/__main__.py b/src/termux_language_server/__main__.py index 28471c8..6d7f921 100644 --- a/src/termux_language_server/__main__.py +++ b/src/termux_language_server/__main__.py @@ -68,15 +68,17 @@ def main(): args = parser.parse_args() if args.generate_schema: + from tree_sitter_lsp.utils import pprint + from .misc import get_schema - from .tree_sitter_lsp.utils import pprint pprint(get_schema(args.generate_schema), indent=args.indent) exit() + from tree_sitter_lsp.diagnose import check + from tree_sitter_lsp.format import format + from .finders import DIAGNOSTICS_FINDER_CLASSES, FORMAT_FINDER_CLASSES from .parser import parse - from .tree_sitter_lsp.diagnose import check - from .tree_sitter_lsp.format import format from .utils import get_filetype format(args.format, parse, FORMAT_FINDER_CLASSES, get_filetype) diff --git a/src/termux_language_server/finders.py b/src/termux_language_server/finders.py index 029a69a..aa0905a 100644 --- a/src/termux_language_server/finders.py +++ b/src/termux_language_server/finders.py @@ -14,16 +14,16 @@ TextEdit, ) from tree_sitter import Tree - -from . import CSV, FILETYPE -from .schema import BashTrie -from .tree_sitter_lsp import UNI, Finder -from .tree_sitter_lsp.finders import ( +from tree_sitter_lsp import UNI, Finder +from tree_sitter_lsp.finders import ( ErrorFinder, MissingFinder, SchemaFinder, UnFixedOrderFinder, ) + +from . import CSV, FILETYPE +from .schema import BashTrie from .utils import get_schema diff --git a/src/termux_language_server/misc/color_map.py b/src/termux_language_server/misc/color_map.py index c3b361c..661b6d1 100644 --- a/src/termux_language_server/misc/color_map.py +++ b/src/termux_language_server/misc/color_map.py @@ -3,8 +3,9 @@ """ from typing import Any +from tree_sitter_lsp.misc import get_soup + from .._metainfo import SOURCE, project -from .utils import get_soup def init_schema() -> dict[str, dict[str, Any]]: diff --git a/src/termux_language_server/misc/ebuild.py b/src/termux_language_server/misc/ebuild.py index 4929531..546a305 100644 --- a/src/termux_language_server/misc/ebuild.py +++ b/src/termux_language_server/misc/ebuild.py @@ -3,8 +3,9 @@ """ from typing import Any +from tree_sitter_lsp.misc import get_soup + from .._metainfo import SOURCE, project -from .utils import get_soup def init_schema() -> dict[str, dict[str, Any]]: diff --git a/src/termux_language_server/misc/make_conf.py b/src/termux_language_server/misc/make_conf.py index 0cace15..7ace407 100644 --- a/src/termux_language_server/misc/make_conf.py +++ b/src/termux_language_server/misc/make_conf.py @@ -3,8 +3,9 @@ """ from typing import Any +from tree_sitter_lsp.misc import get_soup + from .._metainfo import SOURCE, project -from .utils import get_soup def init_schema() -> dict[str, dict[str, Any]]: diff --git a/src/termux_language_server/misc/makepkg_conf.py b/src/termux_language_server/misc/makepkg_conf.py index 9df45b1..49c1c78 100644 --- a/src/termux_language_server/misc/makepkg_conf.py +++ b/src/termux_language_server/misc/makepkg_conf.py @@ -3,8 +3,9 @@ """ from typing import Any +from tree_sitter_lsp.misc import get_soup + from .._metainfo import SOURCE, project -from .utils import get_soup def init_schema() -> dict[str, dict[str, Any]]: diff --git a/src/termux_language_server/misc/pkgbuild.py b/src/termux_language_server/misc/pkgbuild.py index d1af2e9..6679ea4 100644 --- a/src/termux_language_server/misc/pkgbuild.py +++ b/src/termux_language_server/misc/pkgbuild.py @@ -4,9 +4,9 @@ from typing import Any from markdown_it.token import Token +from tree_sitter_lsp.misc import get_md_tokens from .._metainfo import SOURCE, project -from .utils import get_md_tokens def get_content(tokens: list[Token]) -> str: diff --git a/src/termux_language_server/misc/termux.py b/src/termux_language_server/misc/termux.py index 3995c12..570c481 100644 --- a/src/termux_language_server/misc/termux.py +++ b/src/termux_language_server/misc/termux.py @@ -3,9 +3,10 @@ """ from typing import Any +from tree_sitter_lsp.misc import get_soup + from .. import CSV from .._metainfo import SOURCE, project -from .utils import get_soup URIS = { "variable": "https://github.com/termux/termux-packages/wiki/Creating-new-package", diff --git a/src/termux_language_server/misc/utils.py b/src/termux_language_server/misc/utils.py deleted file mode 100644 index 4146b69..0000000 --- a/src/termux_language_server/misc/utils.py +++ /dev/null @@ -1,83 +0,0 @@ -r"""Utils -========= -""" -from gzip import decompress -from itertools import chain -from urllib import request - -from bs4 import BeautifulSoup, FeatureNotFound -from markdown_it import MarkdownIt -from markdown_it.token import Token -from platformdirs import site_data_path, user_data_path -from pygls.uris import uri_scheme -from pypandoc import convert_text - - -def get_man(filename: str) -> str: - r"""Get man. - - :param filename: - :type filename: str - :rtype: str - """ - filename += ".5*" - text = b"" - path = "" - for path in chain( - (site_data_path("man") / "man5").glob(filename), - (user_data_path("man") / "man5").glob(filename), - ): - try: - with open(path, "rb") as f: - text = f.read() - break - except Exception: # nosec: B112 - continue - if text == b"": - raise FileNotFoundError - _, _, ext = str(path).rpartition(".") - if ext != "5": - text = decompress(text) - return text.decode() - - -def html2soup(html: str) -> BeautifulSoup: - r"""Html2soup. - - :param html: - :type html: str - :rtype: BeautifulSoup - """ - try: - soup = BeautifulSoup(html, "lxml") - except FeatureNotFound: - soup = BeautifulSoup(html, "html.parser") - return soup - - -def get_soup(uri: str) -> BeautifulSoup: - r"""Get soup. - - :param uri: - :type uri: str - :rtype: BeautifulSoup - """ - if uri_scheme(uri): - with request.urlopen(uri) as f: # nosec: B310 - html = f.read() - else: - text = get_man(uri) - html = convert_text(text, "html", "man") - return html2soup(html) - - -def get_md_tokens(filename: str) -> list[Token]: - r"""Get markdown tokens. - - :param filename: - :type filename: str - :rtype: list[Token] - """ - md = MarkdownIt("commonmark", {}) - text = get_man(filename) - return md.parse(convert_text(text, "markdown", "man")) diff --git a/src/termux_language_server/schema.py b/src/termux_language_server/schema.py index 6acc66d..6ae8190 100644 --- a/src/termux_language_server/schema.py +++ b/src/termux_language_server/schema.py @@ -6,9 +6,8 @@ from lsprotocol.types import Position, Range from tree_sitter import Node - -from .tree_sitter_lsp import UNI -from .tree_sitter_lsp.schema import Trie +from tree_sitter_lsp import UNI +from tree_sitter_lsp.schema import Trie @dataclass diff --git a/src/termux_language_server/server.py b/src/termux_language_server/server.py index 887edaf..13a4ce0 100644 --- a/src/termux_language_server/server.py +++ b/src/termux_language_server/server.py @@ -27,6 +27,9 @@ TextEdit, ) from pygls.server import LanguageServer +from tree_sitter_lsp.diagnose import get_diagnostics +from tree_sitter_lsp.finders import PositionFinder +from tree_sitter_lsp.format import get_text_edits from .finders import ( DIAGNOSTICS_FINDER_CLASSES, @@ -36,9 +39,6 @@ ) from .packages import search_package_document, search_package_names from .parser import parse -from .tree_sitter_lsp.diagnose import get_diagnostics -from .tree_sitter_lsp.finders import PositionFinder -from .tree_sitter_lsp.format import get_text_edits from .utils import get_filetype, get_schema diff --git a/src/termux_language_server/tree_sitter_lsp/__init__.py b/src/termux_language_server/tree_sitter_lsp/__init__.py deleted file mode 100644 index 3ae9daf..0000000 --- a/src/termux_language_server/tree_sitter_lsp/__init__.py +++ /dev/null @@ -1,438 +0,0 @@ -r"""Tree-sitter LSP -=================== -""" -import os -from copy import deepcopy -from dataclasses import dataclass -from typing import Any - -from jinja2 import Template -from lsprotocol.types import ( - Diagnostic, - DiagnosticSeverity, - DocumentLink, - Location, - Position, - Range, - TextEdit, -) -from pygls.uris import to_fs_path -from tree_sitter import Node, Tree, TreeCursor - -# maximum of recursive search -LEVEL = 5 - - -@dataclass -class UNI: - r"""Unified node identifier.""" - - uri: str - node: Node - - def __str__(self) -> str: - r"""Str. - - :rtype: str - """ - return f"{self.get_text()}@{self.uri}:{self.node.start_point[0] + 1}:{self.node.start_point[1] + 1}-{self.node.end_point[0] + 1}:{self.node.end_point[1]}" - - def get_text(self) -> str: - r"""Get text. - - :rtype: str - """ - return self.node2text(self.node) - - @staticmethod - def node2text(node: Node) -> str: - r"""Node2text. - - :param node: - :type node: Node - :rtype: str - """ - return node.text.decode() - - def get_location(self) -> Location: - r"""Get location. - - :rtype: Location - """ - return Location(self.uri, self.get_range()) - - def get_range(self) -> Range: - r"""Get range. - - :rtype: Range - """ - return self.node2range(self.node) - - @staticmethod - def node2range(node: Node) -> Range: - r"""Node2range. - - :param node: - :type node: Node - :rtype: Range - """ - return Range(Position(*node.start_point), Position(*node.end_point)) - - def get_path(self) -> str: - r"""Get path. - - :rtype: str - """ - return self.uri2path(self.uri) - - @staticmethod - def uri2path(uri: str) -> str: - r"""Uri2path. - - :param uri: - :type uri: str - :rtype: str - """ - if path := to_fs_path(uri): - return path - raise TypeError - - def get_diagnostic( - self, - message: str, - severity: DiagnosticSeverity, - **kwargs: Any, - ) -> Diagnostic: - r"""Get diagnostic. - - :param message: - :type message: str - :param severity: - :type severity: DiagnosticSeverity - :param kwargs: - :type kwargs: Any - :rtype: Diagnostic - """ - _range = self.get_range() - _range.end.character -= 1 - return Diagnostic( - _range, - Template(message).render(uni=self, **kwargs), - severity, - ) - - def get_text_edit(self, new_text: str) -> TextEdit: - r"""Get text edit. - - :param new_text: - :type new_text: str - :rtype: TextEdit - """ - return TextEdit(self.get_range(), new_text) - - def get_document_link(self, target: str, **kwargs) -> DocumentLink: - r"""Get document link. - - :param target: - :type target: str - :param kwargs: - :rtype: DocumentLink - """ - return DocumentLink( - self.get_range(), - Template(target).render(uni=self, **kwargs), - ) - - @staticmethod - def join(path, text) -> str: - r"""Join. - - :param path: - :param text: - :rtype: str - """ - return os.path.join(os.path.dirname(path), text) - - -@dataclass -class Finder: - r"""Finder.""" - - message: str = "" - severity: DiagnosticSeverity = DiagnosticSeverity.Error - - def __post_init__(self) -> None: - r"""Post init. - - :rtype: None - """ - self.reset() - - def __call__(self, uni: UNI) -> bool: - r"""Call. - - :param uni: - :type uni: UNI - :rtype: bool - """ - return True - - def __and__(self, second: "Finder") -> "Finder": - r"""And. - - :param second: - :type second: Finder - :rtype: "Finder" - """ - finder = deepcopy(self) - finder.__call__ = lambda uni: self(uni) and second(uni) - return finder - - def __or__(self, second: "Finder") -> "Finder": - r"""Or. - - :param second: - :type second: Finder - :rtype: "Finder" - """ - finder = deepcopy(self) - finder.__call__ = lambda uni: self(uni) or second(uni) - return finder - - def __minus__(self, second: "Finder") -> "Finder": - r"""Minus. - - :param second: - :type second: Finder - :rtype: "Finder" - """ - finder = deepcopy(self) - finder.__call__ = lambda uni: self(uni) and not second(uni) - return finder - - def is_include_node(self, node: Node) -> bool: - r"""Is include node. - - :param node: - :type node: Node - :rtype: bool - """ - return False - - def parse(self, code: bytes) -> Tree: - r"""Parse. - - :param code: - :type code: bytes - :rtype: Tree - """ - raise NotImplementedError - - def uri2tree(self, uri: str) -> Tree | None: - r"""Convert URI to tree. - - :param uri: - :type uri: str - :rtype: Tree | None - """ - path = UNI.uri2path(uri) - if not os.path.exists(path): - return None - with open(path, "rb") as f: - code = f.read() - return self.parse(code) - - def uni2uri(self, uni: UNI) -> str: - r"""Convert UNI to URI. - - :param uni: - :type uni: UNI - :rtype: str - """ - return uni.join(uni.uri, uni.get_text()) - - def uni2path(self, uni: UNI) -> str: - r"""Convert UNI to path. - - :param self: - :param uni: - :type uni: UNI - :rtype: str - """ - uri = self.uni2uri(uni) - return UNI.uri2path(uri) - - def move_cursor( - self, uri: str, cursor: TreeCursor, is_all: bool = False - ) -> str | None: - r"""Move cursor. - - :param self: - :param uri: - :type uri: str - :param cursor: - :type cursor: TreeCursor - :param is_all: - :type is_all: bool - :rtype: str | None - """ - while self(UNI(uri, cursor.node)) is False: - if self.is_include_node(cursor.node) and self.level < LEVEL: - self.level += 1 - old_uri = uri - uri = self.uni2uri(UNI(uri, cursor.node)) - tree = self.uri2tree(uri) - if tree is not None: - if is_all: - self.find_all(uri, tree, False) - else: - result = self.find(uri, tree) - if result is not None: - return - uri = old_uri - self.level -= 1 - if cursor.node.child_count > 0: - cursor.goto_first_child() - continue - while cursor.node.next_sibling is None: - cursor.goto_parent() - # when cannot find new nodes, return - if cursor.node.parent is None: - return None - cursor.goto_next_sibling() - return uri - - def reset(self) -> None: - r"""Reset. - - :rtype: None - """ - self.level = 0 - self.unis = [] - - def prepare( - self, uri: str, tree: Tree | None = None, reset: bool = True - ) -> TreeCursor: - r"""Prepare. - - :param uri: - :type uri: str - :param tree: - :type tree: Tree | None - :param reset: - :type reset: bool - :rtype: TreeCursor - """ - if reset: - self.reset() - if tree is None: - tree = self.uri2tree(uri) - if tree is None: - raise TypeError - return tree.walk() - - def find( - self, uri: str, tree: Tree | None = None, reset: bool = True - ) -> UNI | None: - r"""Find. - - :param uri: - :type uri: str - :param tree: - :type tree: Tree | None - :param reset: - :type reset: bool - :rtype: UNI | None - """ - cursor = self.prepare(uri, tree, reset) - _uri = self.move_cursor(uri, cursor, False) - if _uri is not None: - return UNI(_uri, cursor.node) - else: - return None - - def find_all( - self, uri: str, tree: Tree | None = None, reset: bool = True - ) -> list[UNI]: - r"""Find all. - - :param uri: - :type uri: str - :param tree: - :type tree: Tree | None - :param reset: - :type reset: bool - :rtype: list[UNI] - """ - cursor = self.prepare(uri, tree, reset) - while True: - _uri = self.move_cursor(uri, cursor, True) - if _uri is not None: - self.unis += [UNI(_uri, cursor.node)] - while cursor.node.next_sibling is None: - cursor.goto_parent() - # when cannot find new nodes, return - if cursor.node.parent is None: - return self.unis - cursor.goto_next_sibling() - - def uni2diagnostic(self, uni: UNI) -> Diagnostic: - r"""Uni2diagnostic. - - :param uni: - :type uni: UNI - :rtype: Diagnostic - """ - return uni.get_diagnostic(self.message, self.severity) - - def unis2diagnostics(self, unis: list[UNI]) -> list[Diagnostic]: - r"""Unis2diagnostics. - - :param unis: - :type unis: list[UNI] - :rtype: list[Diagnostic] - """ - return [self.uni2diagnostic(uni) for uni in unis] - - def get_diagnostics(self, uri: str, tree: Tree) -> list[Diagnostic]: - r"""Get diagnostics. - - :param self: - :param uri: - :type uri: str - :param tree: - :type tree: Tree - :rtype: list[Diagnostic] - """ - return self.unis2diagnostics(self.find_all(uri, tree)) - - def get_text_edits(self, uri: str, tree: Tree) -> list[TextEdit]: - r"""Get text edits. - - :param self: - :param uri: - :type uri: str - :param tree: - :type tree: Tree - :rtype: list[TextEdit] - """ - self.find_all(uri, tree) - return [] - - def get_document_links( - self, uri: str, tree: Tree, template: str - ) -> list[DocumentLink]: - r"""Get document links. - - :param uri: - :type uri: str - :param tree: - :type tree: Tree - :param template: - :type template: str - :rtype: list[DocumentLink] - """ - self.find_all(uri, tree) - return [ - uni.get_document_link(template) for uni in self.find_all(uri, tree) - ] diff --git a/src/termux_language_server/tree_sitter_lsp/complete.py b/src/termux_language_server/tree_sitter_lsp/complete.py deleted file mode 100644 index f7731a9..0000000 --- a/src/termux_language_server/tree_sitter_lsp/complete.py +++ /dev/null @@ -1,48 +0,0 @@ -r"""Complete -============ -""" -import os -from glob import glob -from pathlib import Path - -from lsprotocol.types import CompletionItem, CompletionItemKind, CompletionList - -from . import UNI - - -def get_completion_list_by_uri( - uri: str, text: str = "", expr: str = "*" -) -> CompletionList: - r"""Get completion list by uri. - - :param uri: - :type uri: str - :param text: - :type text: str - :param expr: - :type expr: str - :rtype: CompletionList - """ - dirname = os.path.dirname(UNI.uri2path(uri)) - return CompletionList( - False, - [ - CompletionItem( - x.rpartition(dirname + os.path.sep)[-1], - kind=CompletionItemKind.File - if os.path.isfile(x) - else CompletionItemKind.Folder, - documentation=Path(x).read_text() - if os.path.isfile(x) - else "\n".join(os.listdir(x)), - insert_text=x.rpartition(dirname + os.path.sep)[-1], - ) - for x in [ - file + ("" if os.path.isfile(file) else os.path.sep) - for file in glob( - os.path.join(dirname, text + f"**{os.path.sep}" + expr), - recursive=True, - ) - ] - ], - ) diff --git a/src/termux_language_server/tree_sitter_lsp/diagnose.py b/src/termux_language_server/tree_sitter_lsp/diagnose.py deleted file mode 100644 index 464b004..0000000 --- a/src/termux_language_server/tree_sitter_lsp/diagnose.py +++ /dev/null @@ -1,196 +0,0 @@ -r"""Diagnose -============ - -Wrap -``Diagnostic ``_ -to a linter. -""" -import sys -from typing import Callable, Literal - -from lsprotocol.types import Diagnostic, DiagnosticSeverity -from tree_sitter import Tree - -from . import Finder -from .utils import get_finders, get_paths - - -def get_diagnostics_by_finders( - uri: str, tree: Tree, finders: list[Finder] -) -> list[Diagnostic]: - r"""Get diagnostics by finders. - - :param uri: - :type uri: str - :param tree: - :type tree: Tree - :param finders: - :type finders: list[Finder] - :rtype: list[Diagnostic] - """ - return [ - diagnostic - for finder in finders - for diagnostic in finder.get_diagnostics(uri, tree) - ] - - -def get_diagnostics( - uri: str, - tree: Tree, - classes: list[type[Finder]] | None = None, - filetype: str | None = None, -) -> list[Diagnostic]: - r"""Get diagnostics. - - :param uri: - :type uri: str - :param tree: - :type tree: Tree - :param classes: - :type classes: list[type[Finder]] | None - :param filetype: - :type filetype: str | None - :rtype: list[Diagnostic] - """ - finders, finder_classes = get_finders(classes) - if filetype is None: - return get_diagnostics_by_finders(uri, tree, finders) - return [ - diagnostic - for diagnostic in get_diagnostics_by_finders( - uri, tree, finders + [cls(filetype) for cls in finder_classes] - ) - ] - - -def count_level( - diagnostics: list[Diagnostic], - level: DiagnosticSeverity = DiagnosticSeverity.Warning, -) -> int: - r"""Count level. - - :param diagnostics: - :type diagnostics: list[Diagnostic] - :param level: - :type level: DiagnosticSeverity - :rtype: int - """ - return len( - [ - diagnostic - for diagnostic in diagnostics - if diagnostic.severity and diagnostic.severity <= level - ] - ) - - -class _Colorama: - """Colorama.""" - - def __getattribute__(self, _: str) -> str: - """Getattribute. - - :param _: - :type _: str - :rtype: str - """ - return "" - - -def diagnostics2linter_messages( - path: str, - diagnostics: list[Diagnostic], - color: Literal["auto", "always", "never"] = "auto", - colors: list[str] | None = None, -) -> list[str]: - r"""Diagnostics2linter messages. - - :param path: - :type path: str - :param diagnostics: - :type diagnostics: list[Diagnostic] - :param color: - :type color: Literal["auto", "always", "never"] - :param colors: - :type colors: list[str] | None - :rtype: list[str] - """ - from colorama import Fore, init - - init() - if not sys.stdout.isatty() and color == "auto" or color == "never": - Fore = _Colorama() - if colors is None: - colors = [Fore.RESET, Fore.RED, Fore.YELLOW, Fore.BLUE, Fore.GREEN] - return [ - f"{Fore.MAGENTA}{path}{Fore.RESET}:{Fore.CYAN}{diagnostic.range.start.line + 1}:{diagnostic.range.start.character + 1}{Fore.RESET}-{Fore.CYAN}{diagnostic.range.end.line + 1}:{diagnostic.range.end.character + 1}{Fore.RESET}:{colors[diagnostic.severity if diagnostic.severity else 0]}{str(diagnostic.severity).split('.')[-1].lower()}{Fore.RESET}: {diagnostic.message}" - for diagnostic in diagnostics - ] - - -def check_by_finders( - paths: list[str], - parse: Callable[[bytes], Tree], - finders: list[Finder], - color: Literal["auto", "always", "never"] = "auto", -) -> int: - r"""Check by finders. - - :param paths: - :type paths: list[str] - :param parse: - :type parse: Callable[[bytes], Tree] - :param finders: - :type finders: list[Finder] - :param color: - :type color: Literal["auto", "always", "never"] - :rtype: int - """ - count = 0 - lines = [] - for path in paths: - with open(path, "rb") as f: - src = f.read() - tree = parse(src) - diagnostics = get_diagnostics_by_finders(path, tree, finders) - count += count_level(diagnostics) - lines += diagnostics2linter_messages(path, diagnostics, color) - if text := "\n".join(lines): - print(text) - return count - - -def check( - paths: list[str], - parse: Callable[[bytes], Tree], - classes: list[type[Finder]] | None = None, - get_filetype: Callable[[str], str] | None = None, - color: Literal["auto", "always", "never"] = "auto", -) -> int: - r"""Check. - - :param paths: - :type paths: list[str] - :param parse: - :type parse: Callable[[bytes], Tree] - :param classes: - :type classes: list[type[Finder]] | None - :param get_filetype: - :type get_filetype: Callable[[str], str] | None - :param color: - :type color: Literal["auto", "always", "never"] - :rtype: int - """ - finders, finder_classes = get_finders(classes) - if get_filetype is None: - return check_by_finders(paths, parse, finders, color) - return sum( - check_by_finders( - filepaths, - parse, - finders + [cls(filetype) for cls in finder_classes], - color, - ) - for filetype, filepaths in get_paths(paths, get_filetype).items() - ) diff --git a/src/termux_language_server/tree_sitter_lsp/finders.py b/src/termux_language_server/tree_sitter_lsp/finders.py deleted file mode 100644 index 6257d34..0000000 --- a/src/termux_language_server/tree_sitter_lsp/finders.py +++ /dev/null @@ -1,530 +0,0 @@ -r"""Finders -=========== -""" -import os -from copy import deepcopy -from dataclasses import dataclass -from typing import Any - -from jinja2 import Template -from jsonschema import Validator -from jsonschema.validators import validator_for -from lsprotocol.types import ( - Diagnostic, - DiagnosticSeverity, - Location, - Position, - Range, - TextEdit, -) -from tree_sitter import Node, Tree - -from . import UNI, Finder -from .schema import Trie - - -@dataclass -class MissingFinder(Finder): - r"""Missingfinder.""" - - message: str = "{{uni.get_text()}}: missing" - severity: DiagnosticSeverity = DiagnosticSeverity.Error - - def __call__(self, uni: UNI) -> bool: - r"""Call. - - :param uni: - :type uni: UNI - :rtype: bool - """ - node = uni.node - return node.is_missing and not ( - any(child.is_missing for child in node.children) - ) - - -@dataclass -class ErrorFinder(Finder): - r"""Errorfinder.""" - - message: str = "{{uni.get_text()}}: error" - severity: DiagnosticSeverity = DiagnosticSeverity.Error - - def __call__(self, uni: UNI) -> bool: - r"""Call. - - :param uni: - :type uni: UNI - :rtype: bool - """ - node = uni.node - return node.has_error and not ( - any(child.has_error for child in node.children) - ) - - -@dataclass -class NotFileFinder(Finder): - r"""NotFilefinder.""" - - message: str = "{{uni.get_text()}}: no such file or directory" - severity: DiagnosticSeverity = DiagnosticSeverity.Error - - def __call__(self, uni: UNI) -> bool: - r"""Call. - - :param uni: - :type uni: UNI - :rtype: bool - """ - path = self.uni2path(uni) - return not (os.path.isfile(path) or os.path.isdir(path)) - - -@dataclass -class RepeatedFinder(Finder): - r"""Repeatedfinder.""" - - message: str = "{{uni.get_text()}}: is repeated on {{_uni}}" - severity: DiagnosticSeverity = DiagnosticSeverity.Warning - - def reset(self) -> None: - r"""Reset. - - :rtype: None - """ - self.level = 0 - self.unis = [] - self._unis = [] - self.uni_pairs = [] - - def filter(self, uni: UNI) -> bool: - r"""Filter. - - :param uni: - :type uni: UNI - :rtype: bool - """ - return True - - def compare(self, uni: UNI, _uni: UNI) -> bool: - r"""Compare. - - :param uni: - :type uni: UNI - :param _uni: - :type _uni: UNI - :rtype: bool - """ - return uni.node.text == _uni.node.text - - def __call__(self, uni: UNI) -> bool: - r"""Call. - - :param uni: - :type uni: UNI - :rtype: bool - """ - if self.filter(uni) is False: - return False - for _uni in self._unis: - if self.compare(uni, _uni): - self.uni_pairs += [[uni, _uni]] - return True - self._unis += [uni] - return False - - def get_definitions(self, uni: UNI) -> list[Location]: - r"""Get definitions. - - :param uni: - :type uni: UNI - :rtype: list[Location] - """ - for uni_, _uni in self.uni_pairs: - # cache hit - if uni == uni_: - return [_uni.get_location()] - return [] - - def get_references(self, uni: UNI) -> list[Location]: - r"""Get references. - - :param uni: - :type uni: UNI - :rtype: list[Location] - """ - locations = [] - for uni_, _uni in self.uni_pairs: - # cache hit - if uni == _uni: - locations += [uni_.get_location()] - return locations - - def get_text_edits(self, uri: str, tree: Tree) -> list[TextEdit]: - r"""Get text edits. Only return two to avoid `Overlapping edit` - - :param self: - :param uri: - :type uri: str - :param tree: - :type tree: Tree - :rtype: list[TextEdit] - """ - self.find_all(uri, tree) - for uni, _uni in self.uni_pairs: - # swap 2 unis - return [ - uni.get_text_edit(_uni.get_text()), - _uni.get_text_edit(uni.get_text()), - ] - return [] - - def uni2diagnostic(self, uni: UNI) -> Diagnostic: - r"""Uni2diagnostic. - - :param uni: - :type uni: UNI - :rtype: Diagnostic - """ - for uni_, _uni in self.uni_pairs: - if uni == uni_: - return uni.get_diagnostic( - self.message, self.severity, _uni=_uni - ) - return uni.get_diagnostic(self.message, self.severity) - - -@dataclass -class UnsortedFinder(RepeatedFinder): - r"""Unsortedfinder.""" - - message: str = "{{uni.get_text()}}: is unsorted due to {{_uni}}" - severity: DiagnosticSeverity = DiagnosticSeverity.Warning - - def compare(self, uni: UNI, _uni: UNI) -> bool: - r"""Compare. - - :param uni: - :type uni: UNI - :param _uni: - :type _uni: UNI - :rtype: bool - """ - return uni.node.text < _uni.node.text - - -@dataclass(init=False) -class UnFixedOrderFinder(RepeatedFinder): - r"""Unfixedorderfinder.""" - - def __init__( - self, - order: list[Any], - message: str = "{{uni.get_text()}}: is unsorted due to {{_uni}}", - severity: DiagnosticSeverity = DiagnosticSeverity.Warning, - ) -> None: - r"""Init. - - :param order: - :type order: list[Any] - :param message: - :type message: str - :param severity: - :type severity: DiagnosticSeverity - :rtype: None - """ - super().__init__(message, severity) - self.order = order - - def filter(self, uni: UNI) -> bool: - r"""Filter. - - :param uni: - :type uni: UNI - :rtype: bool - """ - return uni.get_text() in self.order - - def compare(self, uni: UNI, _uni: UNI) -> bool: - r"""Compare. - - :param uni: - :type uni: UNI - :param _uni: - :type _uni: UNI - :rtype: bool - """ - return self.order.index(uni.get_text()) < self.order.index( - _uni.get_text() - ) - - -@dataclass(init=False) -class TypeFinder(Finder): - r"""Typefinder.""" - - def __init__( - self, - type: str, - message: str = "", - severity: DiagnosticSeverity = DiagnosticSeverity.Information, - ) -> None: - r"""Init. - - :param type: - :type type: str - :param message: - :type message: str - :param severity: - :type severity: DiagnosticSeverity - :rtype: None - """ - super().__init__(message, severity) - self.type = type - - def __call__(self, uni: UNI) -> bool: - r"""Call. - - :param uni: - :type uni: UNI - :rtype: bool - """ - node = uni.node - return node.type == self.type - - -@dataclass(init=False) -class PositionFinder(Finder): - r"""Positionfinder.""" - - def __init__( - self, - position: Position, - message: str = "", - severity: DiagnosticSeverity = DiagnosticSeverity.Information, - ) -> None: - r"""Init. - - :param position: - :type position: Position - :param message: - :type message: str - :param severity: - :type severity: DiagnosticSeverity - :rtype: None - """ - super().__init__(message, severity) - self.position = position - - @staticmethod - def belong(position: Position, node: Node) -> bool: - r"""Belong. - - :param position: - :type position: Position - :param node: - :type node: Node - :rtype: bool - """ - return ( - Position(*node.start_point) <= position < Position(*node.end_point) - ) - - def __call__(self, uni: UNI) -> bool: - r"""Call. - - :param uni: - :type uni: UNI - :rtype: bool - """ - node = uni.node - return node.child_count == 0 and self.belong(self.position, node) - - -@dataclass(init=False) -class RangeFinder(Finder): - r"""Rangefinder.""" - - def __init__( - self, - range: Range, - message: str = "", - severity: DiagnosticSeverity = DiagnosticSeverity.Information, - ) -> None: - r"""Init. - - :param range: - :type range: Range - :param message: - :type message: str - :param severity: - :type severity: DiagnosticSeverity - :rtype: None - """ - super().__init__(message, severity) - self.range = range - - @staticmethod - def equal(_range: Range, node: Node) -> bool: - r"""Equal. - - :param _range: - :type _range: Range - :param node: - :type node: Node - :rtype: bool - """ - return _range.start == Position( - *node.start_point - ) and _range.end == Position(*node.end_point) - - def __call__(self, uni: UNI) -> bool: - r"""Call. - - :param uni: - :type uni: UNI - :rtype: bool - """ - node = uni.node - return self.equal(self.range, node) - - -@dataclass(init=False) -class RequiresFinder(Finder): - r"""Requiresfinder.""" - - def __init__( - self, - requires: set[Any], - message: str = "{{require}}: required", - severity: DiagnosticSeverity = DiagnosticSeverity.Error, - ) -> None: - r"""Init. - - :param requires: - :type requires: set[Any] - :param message: - :type message: str - :param severity: - :type severity: DiagnosticSeverity - :rtype: None - """ - self.requires = requires - # will call reset() which will call self.requires - super().__init__(message, severity) - - def reset(self) -> None: - r"""Reset. - - :rtype: None - """ - self.level = 0 - self.unis = [] - self._requires = deepcopy(self.requires) - - def filter(self, uni: UNI, require: Any) -> bool: - r"""Filter. - - :param uni: - :type uni: UNI - :param require: - :type require: Any - :rtype: bool - """ - return False - - def __call__(self, uni: UNI) -> bool: - r"""Call. - - :param uni: - :type uni: UNI - :rtype: bool - """ - found = set() - for require in self._requires: - if self.filter(uni, require): - found |= {require} - self._requires -= found - return False - - def require2message(self, require: Any, **kwargs: Any) -> str: - r"""Require2message. - - :param require: - :type require: Any - :param kwargs: - :type kwargs: Any - :rtype: str - """ - return Template(self.message).render( - uni=self, require=require, **kwargs - ) - - def get_diagnostics(self, uri: str, tree: Tree) -> list[Diagnostic]: - r"""Get diagnostics. - - :param uri: - :type uri: str - :param tree: - :type tree: Tree - :rtype: list[Diagnostic] - """ - self.find_all(uri, tree) - return [ - Diagnostic( - # If you want to specify a range that contains a line including - # the line ending character(s) then use an end position - # denoting the start of the next line - Range(Position(0, 0), Position(1, 0)), - self.require2message(i), - self.severity, - ) - for i in self._requires - ] - - -@dataclass(init=False) -class SchemaFinder(Finder): - r"""Schemafinder.""" - - def __init__(self, schema: dict[str, Any], cls: type[Trie]) -> None: - r"""Init. - - :param schema: - :type schema: dict[str, Any] - :param cls: - :type cls: type[Trie] - :rtype: None - """ - self.validator = self.schema2validator(schema) - self.cls = cls - - @staticmethod - def schema2validator(schema: dict[str, Any]) -> Validator: - r"""Schema2validator. - - :param schema: - :type schema: dict[str, Any] - :rtype: Validator - """ - return validator_for(schema)(schema) - - def get_diagnostics(self, _: str, tree: Tree) -> list[Diagnostic]: - r"""Get diagnostics. - - :param _: - :type _: str - :param tree: - :type tree: Tree - :rtype: list[Diagnostic] - """ - trie = self.cls.from_tree(tree) - return [ - Diagnostic( - trie.from_path(error.json_path).range, - error.message, - DiagnosticSeverity.Error, - ) - for error in self.validator.iter_errors(trie.to_json()) - ] diff --git a/src/termux_language_server/tree_sitter_lsp/format.py b/src/termux_language_server/tree_sitter_lsp/format.py deleted file mode 100644 index 832159d..0000000 --- a/src/termux_language_server/tree_sitter_lsp/format.py +++ /dev/null @@ -1,166 +0,0 @@ -r"""Format -========== - -Wrap -``Document Formatting ``_ -to a formatter. -""" -from typing import Callable - -from lsprotocol.types import Position, Range, TextEdit -from tree_sitter import Tree - -from . import Finder -from .utils import get_finders, get_paths - - -def position_2d_to_1d(source: str, position: Position) -> int: - r"""Position 2d to 1d. - - :param source: - :type source: str - :param position: - :type position: Position - :rtype: int - """ - return ( - sum(len(line) + 1 for line in source.splitlines()[: position.line]) - + position.character - ) - - -def range_2d_to_1d(source: str, region: Range) -> range: - r"""Range 2d to 1d. - - :param source: - :type source: str - :param region: - :type region: Range - :rtype: range - """ - return range( - position_2d_to_1d(source, region.start), - position_2d_to_1d(source, region.end), - ) - - -def apply_text_edits(text_edits: list[TextEdit], source: str) -> str: - r"""Apply text edits. - - :param text_edits: - :type text_edits: list[TextEdit] - :param source: - :type source: str - :rtype: str - """ - for text_edit in text_edits: - region = range_2d_to_1d(source, text_edit.range) - source = ( - source[: region.start] + text_edit.new_text + source[region.stop :] - ) - return source - - -def format_by_finders( - paths: list[str], parse: Callable[[bytes], Tree], finders: list[Finder] -) -> None: - r"""Format by finders. - - :param paths: - :type paths: list[str] - :param parse: - :type parse: Callable[[bytes], Tree] - :param finders: - :type finders: list[Finder] - :rtype: None - """ - for path in paths: - with open(path, "rb") as f: - src = f.read() - tree = parse(src) - text_edits = [ - text_edit - for finder in finders - for text_edit in finder.get_text_edits(path, tree) - ] - src = apply_text_edits(text_edits, src.decode()) - with open(path, "w") as f: - f.write(src) - - -def get_text_edits_by_finders( - uri: str, tree: Tree, finders: list[Finder] -) -> list[TextEdit]: - r"""Get text edits by finders. - - :param uri: - :type uri: str - :param tree: - :type tree: Tree - :param finders: - :type finders: list[Finder] - :rtype: list[TextEdit] - """ - return [ - text_edit - for finder in finders - for text_edit in finder.get_text_edits(uri, tree) - ] - - -def get_text_edits( - uri: str, - tree: Tree, - classes: list[type[Finder]] | None = None, - filetype: str | None = None, -) -> list[TextEdit]: - r"""Get text edits. - - :param uri: - :type uri: str - :param tree: - :type tree: Tree - :param classes: - :type classes: list[type[Finder]] | None - :param filetype: - :type filetype: str | None - :rtype: list[TextEdit] - """ - finders, finder_classes = get_finders(classes) - if filetype is None: - return get_text_edits_by_finders(uri, tree, finders) - return [ - text_edit - for text_edit in get_text_edits_by_finders( - uri, tree, finders + [cls(filetype) for cls in finder_classes] - ) - ] - - -def format( - paths: list[str], - parse: Callable[[bytes], Tree], - classes: list[type[Finder]] | None = None, - get_filetype: Callable[[str], str] | None = None, -) -> None: - r"""Format. - - :param paths: - :type paths: list[str] - :param parse: - :type parse: Callable[[bytes], Tree] - :param classes: - :type classes: list[type[Finder]] | None - :param get_filetype: - :type get_filetype: Callable[[str], str] | None - :rtype: None - """ - finders, finder_classes = get_finders(classes) - if get_filetype is None: - return format_by_finders(paths, parse, finders) - for filetype, filepaths in get_paths(paths, get_filetype).items(): - format_by_finders( - filepaths, - parse, - finders + [cls(filetype) for cls in finder_classes], - ) diff --git a/src/termux_language_server/tree_sitter_lsp/schema.py b/src/termux_language_server/tree_sitter_lsp/schema.py deleted file mode 100644 index a7420f4..0000000 --- a/src/termux_language_server/tree_sitter_lsp/schema.py +++ /dev/null @@ -1,133 +0,0 @@ -r"""Schema -========== -""" -from dataclasses import dataclass -from typing import Any - -from lsprotocol.types import Position, Range -from tree_sitter import Node, Tree - -from . import UNI - - -@dataclass -class Trie: - r"""Trie.""" - - range: Range - parent: "Trie | None" = None - # can be serialized to a json - value: dict[str, "Trie"] | list["Trie"] | str | int | float | None = None - - def get_root(self) -> "Trie": - r"""Get root. - - :rtype: "Trie" - """ - node = self - while node.parent is not None: - node = node.parent - return node - - def to_path(self) -> str: - r"""To path. - - :rtype: str - """ - if self.parent is None: - return "$" - path = self.parent.to_path() - if isinstance(self.parent.value, dict): - for k, v in self.parent.value.items(): - if v is self: - return f"{path}.{k}" - raise TypeError - if isinstance(self.parent.value, list): - for k, v in enumerate(self.parent.value): - if v is self: - return f"{path}[{k}]" - raise TypeError - return path - - def from_path(self, path: str) -> "Trie": - r"""From path. - - :param path: - :type path: str - :rtype: "Trie" - """ - node = self - if path.startswith("$"): - path = path.lstrip("$") - node = self.get_root() - return node.from_relative_path(path) - - def from_relative_path(self, path: str) -> "Trie": - r"""From relative path. - - :param path: - :type path: str - :rtype: "Trie" - """ - if path == "": - return self - if path.startswith("."): - if not isinstance(self.value, dict): - raise TypeError - path = path.lstrip(".") - index, mid, path = path.partition(".") - if mid == ".": - path = mid + path - index, mid, suffix = index.partition("[") - if mid == "[": - path = mid + suffix + path - return self.value[index].from_relative_path(path) - if path.startswith("["): - if not isinstance(self.value, list): - raise TypeError - path = path.lstrip("[") - index, _, path = path.partition("]") - return self.value[int(index)].from_relative_path(path) - raise TypeError - - def to_json(self) -> dict[str, Any] | list[Any] | str | int | float | None: - r"""To json. - - :rtype: dict[str, Any] | list[Any] | str | int | float | None - """ - if isinstance(self.value, dict): - return {k: v.to_json() for k, v in self.value.items()} - if isinstance(self.value, list): - return [v.to_json() for v in self.value] - return self.value - - @classmethod - def from_tree(cls, tree: Tree) -> "Trie": - r"""From tree. - - :param tree: - :type tree: Tree - :rtype: "Trie" - """ - return cls.from_node(tree.root_node, None) - - @classmethod - def from_node(cls, node: Node, parent: "Trie | None") -> "Trie": - r"""From node. - - :param node: - :type node: Node - :param parent: - :type parent: Trie | None - :rtype: "Trie" - """ - if parent is None: - _range = Range(Position(0, 0), Position(1, 0)) - else: - _range = UNI.node2range(node) - trie = cls(_range, parent, {}) - trie.value = { - UNI.node2text(child.children[0]): cls.from_node(child, trie) - for child in node.children - } - return trie diff --git a/src/termux_language_server/tree_sitter_lsp/utils.py b/src/termux_language_server/tree_sitter_lsp/utils.py deleted file mode 100644 index 9f60fab..0000000 --- a/src/termux_language_server/tree_sitter_lsp/utils.py +++ /dev/null @@ -1,99 +0,0 @@ -r"""Utils -========= - -Some common functions used by formatters and linters. -""" -import json -import os -import sys -from typing import Any, Callable - -from . import Finder - - -def get_paths( - paths: list[str], get_filetype: Callable[[str], str] -) -> dict[str, list[str]]: - r"""Get paths. - - :param paths: - :type paths: list[str] - :param get_filetype: A function returning ``Literal["filetype1", "filetype2", ...] | Literal[""]`` - :type get_filetype: Callable[[str], str] - :rtype: dict[str, list[str]] - """ - filetype_paths = { - k: [] - for k in get_filetype.__annotations__["return"].__args__[0].__args__ - if k != "" - } - for path in paths: - filetype = get_filetype(path) - for _filetype, filepaths in filetype_paths.items(): - if filetype == _filetype: - filepaths += [path] - return filetype_paths - - -def get_finders( - classes: list[type[Finder]] | None = None, -) -> tuple[list[Finder], list[type[Finder]]]: - r"""Get finders. - - :param classes: - :type classes: list[type[Finder]] | None - :rtype: tuple[list[Finder], list[type[Finder]]] - """ - if classes is None: - from .finders import ErrorFinder, MissingFinder - - classes = [ErrorFinder, MissingFinder] - - finders = [] - finder_classes = [] - for cls in classes: - if cls.__init__.__annotations__.get("filetype"): - finder_classes += [cls] - else: - finders += [cls()] - return finders, finder_classes - - -def pprint( - obj: object, filetype: str = "json", *args: Any, **kwargs: Any -) -> None: - r"""Pprint. - - :param obj: - :type obj: object - :param filetype: - :type filetype: str - :param args: - :type args: Any - :param kwargs: - :type kwargs: Any - :rtype: None - """ - text = json.dumps(obj, *args, **kwargs) - TERM = os.getenv("TERM", "xterm") - if not sys.stdout.isatty(): - TERM = "dumb" - try: - from pygments import highlight - from pygments.formatters import get_formatter_by_name - from pygments.lexers import get_lexer_by_name - - if TERM.split("-")[-1] == "256color": - formatter_name = "terminal256" - elif TERM != "dumb": - formatter_name = "terminal" - else: - formatter_name = None - if formatter_name: - formatter = get_formatter_by_name(formatter_name) - lexer = get_lexer_by_name(filetype) - print(highlight(text, lexer, formatter), end="") - except ImportError: - TERM = "dumb" - if TERM == "dumb": - print(text) diff --git a/tests/test_schema.py b/tests/test_schema.py index 2a501da..038e92e 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -1,9 +1,10 @@ r"""Test schema.""" import os +from tree_sitter_lsp.finders import SchemaFinder + from termux_language_server.parser import parse from termux_language_server.schema import BashTrie -from termux_language_server.tree_sitter_lsp.finders import SchemaFinder from termux_language_server.utils import get_filetype, get_schema PATH = os.path.dirname(__file__)