diff --git a/lsp_client/__init__.py b/lsp_client/__init__.py index eabe3fb..ca6af74 100644 --- a/lsp_client/__init__.py +++ b/lsp_client/__init__.py @@ -15,6 +15,7 @@ InitializeParams, InitializeRequest, InitializedNotification, + LanguageKind, Message, NotificationMessage, Position, @@ -68,6 +69,7 @@ "InitializeRequest", "InitializedNotification", "LSPClient", + "LanguageKind", "Message", "NotificationMessage", "PositionEncodingKind", diff --git a/lsp_client/protocol.py b/lsp_client/protocol.py index 2246cf7..1a439d0 100644 --- a/lsp_client/protocol.py +++ b/lsp_client/protocol.py @@ -267,9 +267,85 @@ def __init__(self, **kwargs: Any) -> None: # See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_synchronization # noqa: E501 +class LanguageKind(str, Enum): + """The recognised language identifiers for a :class:`TextDocumentItem`. + + The LSP types ``languageId`` simply as ``string`` and documents the set of + identifiers below. These are provided for convenience; custom identifiers + not listed here remain valid (``languageId`` accepts any string). + + See + https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentItem + """ + + ABAP = "abap" + WindowsBat = "bat" + BibTeX = "bibtex" + Clojure = "clojure" + Coffeescript = "coffeescript" + C = "c" + CPP = "cpp" + CSharp = "csharp" + CSS = "css" + Diff = "diff" + Dart = "dart" + Dockerfile = "dockerfile" + Elixir = "elixir" + Erlang = "erlang" + FSharp = "fsharp" + GitCommit = "git-commit" + GitRebase = "git-rebase" + Go = "go" + Groovy = "groovy" + Handlebars = "handlebars" + HTML = "html" + Ini = "ini" + Java = "java" + JavaScript = "javascript" + JavaScriptReact = "javascriptreact" + JSON = "json" + LaTeX = "latex" + Less = "less" + Lua = "lua" + Makefile = "makefile" + Markdown = "markdown" + ObjectiveC = "objective-c" + ObjectiveCPP = "objective-cpp" + Perl = "perl" + Perl6 = "perl6" + PHP = "php" + Powershell = "powershell" + Pug = "jade" + Python = "python" + R = "r" + Razor = "razor" + Ruby = "ruby" + Rust = "rust" + SCSS = "scss" + Sass = "sass" + Scala = "scala" + ShaderLab = "shaderlab" + ShellScript = "shellscript" + SQL = "sql" + Swift = "swift" + TypeScript = "typescript" + TypeScriptReact = "typescriptreact" + TeX = "tex" + VisualBasic = "vb" + XML = "xml" + XSL = "xsl" + YAML = "yaml" + + class TextDocumentItem(BaseModel): + """An item to transfer a text document from the client to the server. + + ``languageId`` is a free-form string per the spec; :class:`LanguageKind` + enumerates the documented identifiers for convenience. + """ + uri: str - languageId: str + languageId: LanguageKind | str version: int text: str diff --git a/tests/test_protocol.py b/tests/test_protocol.py index 56a2772..9e7944c 100644 --- a/tests/test_protocol.py +++ b/tests/test_protocol.py @@ -17,6 +17,7 @@ InitializeParams, InitializeRequest, InitializedNotification, + LanguageKind, Message, NotificationMessage, Position, @@ -32,6 +33,7 @@ TextDocumentDidCloseNotification, TextDocumentDidOpenNotification, TextDocumentIdentifier, + TextDocumentItem, TextDocumentPositionParams, WorkDoneProgressBegin, WorkDoneProgressCancelNotification, @@ -264,6 +266,56 @@ def test_response_message_null_id(): assert data["id"] is None +def test_language_kind_values(): + assert LanguageKind.Python == "python" + assert LanguageKind.CPP == "cpp" + assert LanguageKind.GitCommit == "git-commit" + assert LanguageKind.TypeScriptReact == "typescriptreact" + + +def test_text_document_item_with_language_kind(): + item = TextDocumentItem( + uri="file:///tmp/test.py", + languageId=LanguageKind.Python, + version=1, + text="print(1)", + ) + data = item.model_dump() + assert data == { + "uri": "file:///tmp/test.py", + "languageId": "python", + "version": 1, + "text": "print(1)", + } + + +def test_text_document_item_with_known_string(): + item = TextDocumentItem( + uri="file:///tmp/a.rs", languageId="rust", version=2, text="fn main(){}" + ) + assert item.model_dump()["languageId"] == "rust" + + +def test_text_document_item_allows_custom_language_id(): + # languageId is a free-form string; unlisted identifiers stay valid. + item = TextDocumentItem( + uri="file:///tmp/a.cob", languageId="cobol", version=1, text="" + ) + assert item.model_dump()["languageId"] == "cobol" + + +def test_text_document_item_serialises_over_the_wire(): + import json + + item = TextDocumentItem( + uri="file:///tmp/test.py", + languageId=LanguageKind.Python, + version=1, + text="x = 1", + ) + assert json.loads(json.dumps(item.model_dump()))["languageId"] == "python" + + def test_position_serialises(): pos = Position(line=3, character=10) assert pos.model_dump() == {"line": 3, "character": 10}