diff --git a/requirements.txt b/requirements.txt index a888e3e..71618bd 100755 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ platformdirs pygls tree-sitter-lsp >= 0.0.5 +tree-sitter-zathurarc >= 0.0.2 webcolors diff --git a/src/zathura_language_server/finders.py b/src/zathura_language_server/finders.py new file mode 100644 index 0000000..17a8e18 --- /dev/null +++ b/src/zathura_language_server/finders.py @@ -0,0 +1,35 @@ +r"""Finders +=========== +""" +from dataclasses import dataclass + +from lsprotocol.types import DiagnosticSeverity +from tree_sitter_lsp.finders import ErrorFinder +from tree_sitter_zathurarc import language + + +@dataclass(init=False) +class ErrorZathurarcFinder(ErrorFinder): + r"""Error zathurarc finder.""" + + def __init__( + self, + message: str = "{{uni.get_text()}}: error", + severity: DiagnosticSeverity = DiagnosticSeverity.Error, + ) -> None: + r"""Init. + + :param filetype: + :type filetype: str + :param message: + :type message: str + :param severity: + :type severity: DiagnosticSeverity + :rtype: None + """ + super().__init__(language, message, severity) + + +DIAGNOSTICS_FINDER_CLASSES = [ + ErrorFinder, +] diff --git a/src/zathura_language_server/schema.py b/src/zathura_language_server/schema.py new file mode 100644 index 0000000..b6cd002 --- /dev/null +++ b/src/zathura_language_server/schema.py @@ -0,0 +1,80 @@ +r"""Schema +========== +""" +from dataclasses import dataclass +from typing import Literal + +from lsprotocol.types import Position, Range +from tree_sitter import Node +from tree_sitter_lsp import UNI +from tree_sitter_lsp.schema import Trie + + +@dataclass +class ZathurarcTrie(Trie): + r"""Zathurarc Trie.""" + + value: dict[str, "Trie"] | list["Trie"] | str | Literal[0] = 0 + + @classmethod + def from_string_node(cls, node: Node, parent: "Trie | None") -> "Trie": + r"""From string node. + + :param cls: + :param node: + :type node: Node + :param parent: + :type parent: Trie | None + :rtype: "Trie" + """ + if node.type == "string" and node.children == 3: + node = node.children[1] + text = UNI.node2text(node) + _range = UNI.node2range(node) + if node.type in {"string", "raw_string"} and node.children != 3: + text = text.strip("'\"") + _range.start.character += 1 + _range.end.character -= 1 + return cls(_range, parent, text) + + @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 node.type == "set_directive": + # node = node.children[1] + # return cls(UNI.node2range(node), parent, node.children[2]) + if node.type == "file": + trie = cls(Range(Position(0, 0), Position(1, 0)), parent, {}) + directives = { + "set_directive", + "map_directive", + "unmap_directive", + } + for directive in directives: + _type = directive.split("_")[0] + trie.value[_type] = cls( # type: ignore + Range(Position(0, 0), Position(1, 0)), trie, {} + ) + for child in node.children: + if child.type not in directives: + continue + subtrie: Trie = trie.value[child.type.split("_")[0]] # type: ignore + value: dict[str, Trie] = subtrie.value # type: ignore + if child.type == "set_directive": + value[UNI.node2text(child.children[1])] = cls.from_node( + child, subtrie + ) + else: + mode = "normal" + if child.children[1].type == "mode": + mode = child.children[1] + value[mode] = cls.from_node(child, subtrie) # type: ignore + return trie + raise NotImplementedError(node.type) diff --git a/src/zathura_language_server/server.py b/src/zathura_language_server/server.py index cb731aa..554c26b 100644 --- a/src/zathura_language_server/server.py +++ b/src/zathura_language_server/server.py @@ -6,11 +6,14 @@ from lsprotocol.types import ( TEXT_DOCUMENT_COMPLETION, + TEXT_DOCUMENT_DID_CHANGE, + TEXT_DOCUMENT_DID_OPEN, TEXT_DOCUMENT_HOVER, CompletionItem, CompletionItemKind, CompletionList, CompletionParams, + DidChangeTextDocumentParams, Hover, MarkupContent, MarkupKind, @@ -19,7 +22,10 @@ TextDocumentPositionParams, ) from pygls.server import LanguageServer +from tree_sitter_lsp.diagnose import get_diagnostics +from tree_sitter_zathurarc import parser +from .finders import DIAGNOSTICS_FINDER_CLASSES from .utils import get_schema @@ -34,6 +40,26 @@ def __init__(self, *args: Any) -> None: :rtype: None """ super().__init__(*args) + self.trees = {} + + @self.feature(TEXT_DOCUMENT_DID_OPEN) + @self.feature(TEXT_DOCUMENT_DID_CHANGE) + def did_change(params: DidChangeTextDocumentParams) -> None: + r"""Did change. + + :param params: + :type params: DidChangeTextDocumentParams + :rtype: None + """ + document = self.workspace.get_document(params.text_document.uri) + self.trees[document.uri] = parser.parse(document.source.encode()) + diagnostics = get_diagnostics( + document.uri, + self.trees[document.uri], + DIAGNOSTICS_FINDER_CLASSES, + "zathurarc", + ) + self.publish_diagnostics(params.text_document.uri, diagnostics) @self.feature(TEXT_DOCUMENT_HOVER) def hover(params: TextDocumentPositionParams) -> Hover | None: