summaryrefslogtreecommitdiffstats
path: root/examples/servers
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 20:21:34 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 20:21:34 +0000
commitfe1438b06234f8e5ecd4caa7eedfeec585b6f77c (patch)
tree5c2a9ff683189a61e0855ca3f24df319e7e03b7f /examples/servers
parentInitial commit. (diff)
downloadpygls-fe1438b06234f8e5ecd4caa7eedfeec585b6f77c.tar.xz
pygls-fe1438b06234f8e5ecd4caa7eedfeec585b6f77c.zip
Adding upstream version 1.3.0.upstream/1.3.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'examples/servers')
-rw-r--r--examples/servers/.vscode/launch.json20
-rw-r--r--examples/servers/.vscode/settings.json21
-rw-r--r--examples/servers/code_actions.py74
-rw-r--r--examples/servers/inlay_hints.py116
-rw-r--r--examples/servers/json_server.py387
-rw-r--r--examples/servers/workspace/Untitled-1.ipynb44
-rw-r--r--examples/servers/workspace/sums.txt7
-rw-r--r--examples/servers/workspace/test.json3
8 files changed, 672 insertions, 0 deletions
diff --git a/examples/servers/.vscode/launch.json b/examples/servers/.vscode/launch.json
new file mode 100644
index 0000000..f124186
--- /dev/null
+++ b/examples/servers/.vscode/launch.json
@@ -0,0 +1,20 @@
+{
+ "configurations": [
+ {
+ "name": "pygls: Debug Server",
+ "type": "python",
+ "request": "attach",
+ "connect": {
+ "host": "${config:pygls.server.debugHost}",
+ "port": "${config:pygls.server.debugPort}"
+ },
+ "pathMappings": [
+ {
+ "localRoot": "${workspaceFolder}",
+ "remoteRoot": "."
+ }
+ ],
+ "justMyCode": false
+ }
+ ]
+}
diff --git a/examples/servers/.vscode/settings.json b/examples/servers/.vscode/settings.json
new file mode 100644
index 0000000..c2b0d23
--- /dev/null
+++ b/examples/servers/.vscode/settings.json
@@ -0,0 +1,21 @@
+{
+ // Uncomment to override Python interpreter used.
+ // "pygls.server.pythonPath": "/path/to/python",
+ "pygls.server.debug": false,
+ // "pygls.server.debugHost": "localhost",
+ // "pygls.server.debugPort": 5678,
+ "pygls.server.launchScript": "json_server.py",
+ "pygls.trace.server": "off",
+ "pygls.client.documentSelector": [
+ {
+ "scheme": "file",
+ "language": "json"
+ }
+ // Uncomment to use code_actions or inlay_hints servers
+ // {
+ // "scheme": "file",
+ // "language": "plaintext"
+ // }
+ ],
+ // "pygls.jsonServer.exampleConfiguration": "some value here",
+}
diff --git a/examples/servers/code_actions.py b/examples/servers/code_actions.py
new file mode 100644
index 0000000..ec0382e
--- /dev/null
+++ b/examples/servers/code_actions.py
@@ -0,0 +1,74 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+import re
+from pygls.server import LanguageServer
+from lsprotocol.types import (
+ TEXT_DOCUMENT_CODE_ACTION,
+ CodeAction,
+ CodeActionKind,
+ CodeActionOptions,
+ CodeActionParams,
+ Position,
+ Range,
+ TextEdit,
+ WorkspaceEdit,
+)
+
+
+ADDITION = re.compile(r"^\s*(\d+)\s*\+\s*(\d+)\s*=(?=\s*$)")
+server = LanguageServer("code-action-server", "v0.1")
+
+
+@server.feature(
+ TEXT_DOCUMENT_CODE_ACTION,
+ CodeActionOptions(code_action_kinds=[CodeActionKind.QuickFix]),
+)
+def code_actions(params: CodeActionParams):
+ items = []
+ document_uri = params.text_document.uri
+ document = server.workspace.get_document(document_uri)
+
+ start_line = params.range.start.line
+ end_line = params.range.end.line
+
+ lines = document.lines[start_line : end_line + 1]
+ for idx, line in enumerate(lines):
+ match = ADDITION.match(line)
+ if match is not None:
+ range_ = Range(
+ start=Position(line=start_line + idx, character=0),
+ end=Position(line=start_line + idx, character=len(line) - 1),
+ )
+
+ left = int(match.group(1))
+ right = int(match.group(2))
+ answer = left + right
+
+ text_edit = TextEdit(range=range_, new_text=f"{line.strip()} {answer}!")
+
+ action = CodeAction(
+ title=f"Evaluate '{match.group(0)}'",
+ kind=CodeActionKind.QuickFix,
+ edit=WorkspaceEdit(changes={document_uri: [text_edit]}),
+ )
+ items.append(action)
+
+ return items
+
+
+if __name__ == "__main__":
+ server.start_io()
diff --git a/examples/servers/inlay_hints.py b/examples/servers/inlay_hints.py
new file mode 100644
index 0000000..e9924dd
--- /dev/null
+++ b/examples/servers/inlay_hints.py
@@ -0,0 +1,116 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+import re
+from typing import Optional
+
+from lsprotocol import types
+
+from pygls.server import LanguageServer
+
+NUMBER = re.compile(r"\d+")
+COMMENT = re.compile(r"^#$")
+
+
+server = LanguageServer(
+ name="inlay-hint-server",
+ version="v0.1",
+ notebook_document_sync=types.NotebookDocumentSyncOptions(
+ notebook_selector=[
+ types.NotebookDocumentSyncOptionsNotebookSelectorType2(
+ cells=[
+ types.NotebookDocumentSyncOptionsNotebookSelectorType2CellsType(
+ language="python"
+ )
+ ]
+ )
+ ]
+ ),
+)
+
+
+def parse_int(chars: str) -> Optional[int]:
+ try:
+ return int(chars)
+ except Exception:
+ return None
+
+
+@server.feature(types.TEXT_DOCUMENT_INLAY_HINT)
+def inlay_hints(params: types.InlayHintParams):
+ items = []
+ document_uri = params.text_document.uri
+ document = server.workspace.get_text_document(document_uri)
+
+ start_line = params.range.start.line
+ end_line = params.range.end.line
+
+ lines = document.lines[start_line : end_line + 1]
+ for lineno, line in enumerate(lines):
+ match = COMMENT.match(line)
+ if match is not None:
+ nb = server.workspace.get_notebook_document(cell_uri=document_uri)
+ if nb is not None:
+ idx = 0
+ for idx, cell in enumerate(nb.cells):
+ if cell.document == document_uri:
+ break
+
+ items.append(
+ types.InlayHint(
+ label=f"notebook: {nb.uri}, cell {idx+1}",
+ kind=types.InlayHintKind.Type,
+ padding_left=False,
+ padding_right=True,
+ position=types.Position(line=lineno, character=match.end()),
+ )
+ )
+
+ for match in NUMBER.finditer(line):
+ if not match:
+ continue
+
+ number = parse_int(match.group(0))
+ if number is None:
+ continue
+
+ binary_num = bin(number).split("b")[1]
+ items.append(
+ types.InlayHint(
+ label=f":{binary_num}",
+ kind=types.InlayHintKind.Type,
+ padding_left=False,
+ padding_right=True,
+ position=types.Position(line=lineno, character=match.end()),
+ )
+ )
+
+ return items
+
+
+@server.feature(types.INLAY_HINT_RESOLVE)
+def inlay_hint_resolve(hint: types.InlayHint):
+ try:
+ n = int(hint.label[1:], 2)
+ hint.tooltip = f"Binary representation of the number: {n}"
+ except Exception:
+ pass
+
+ return hint
+
+
+if __name__ == "__main__":
+ server.start_io()
diff --git a/examples/servers/json_server.py b/examples/servers/json_server.py
new file mode 100644
index 0000000..98905e4
--- /dev/null
+++ b/examples/servers/json_server.py
@@ -0,0 +1,387 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+import argparse
+import asyncio
+import json
+import re
+import time
+import uuid
+from json import JSONDecodeError
+from typing import Optional
+
+from lsprotocol import types as lsp
+
+from pygls.server import LanguageServer
+
+COUNT_DOWN_START_IN_SECONDS = 10
+COUNT_DOWN_SLEEP_IN_SECONDS = 1
+
+
+class JsonLanguageServer(LanguageServer):
+ CMD_COUNT_DOWN_BLOCKING = "countDownBlocking"
+ CMD_COUNT_DOWN_NON_BLOCKING = "countDownNonBlocking"
+ CMD_PROGRESS = "progress"
+ CMD_REGISTER_COMPLETIONS = "registerCompletions"
+ CMD_SHOW_CONFIGURATION_ASYNC = "showConfigurationAsync"
+ CMD_SHOW_CONFIGURATION_CALLBACK = "showConfigurationCallback"
+ CMD_SHOW_CONFIGURATION_THREAD = "showConfigurationThread"
+ CMD_UNREGISTER_COMPLETIONS = "unregisterCompletions"
+
+ CONFIGURATION_SECTION = "pygls.jsonServer"
+
+ def __init__(self, *args):
+ super().__init__(*args)
+
+
+json_server = JsonLanguageServer("pygls-json-example", "v0.1")
+
+
+def _validate(ls, params):
+ ls.show_message_log("Validating json...")
+
+ text_doc = ls.workspace.get_document(params.text_document.uri)
+
+ source = text_doc.source
+ diagnostics = _validate_json(source) if source else []
+
+ ls.publish_diagnostics(text_doc.uri, diagnostics)
+
+
+def _validate_json(source):
+ """Validates json file."""
+ diagnostics = []
+
+ try:
+ json.loads(source)
+ except JSONDecodeError as err:
+ msg = err.msg
+ col = err.colno
+ line = err.lineno
+
+ d = lsp.Diagnostic(
+ range=lsp.Range(
+ start=lsp.Position(line=line - 1, character=col - 1),
+ end=lsp.Position(line=line - 1, character=col),
+ ),
+ message=msg,
+ source=type(json_server).__name__,
+ )
+
+ diagnostics.append(d)
+
+ return diagnostics
+
+
+@json_server.feature(
+ lsp.TEXT_DOCUMENT_DIAGNOSTIC,
+ lsp.DiagnosticOptions(
+ identifier="jsonServer",
+ inter_file_dependencies=True,
+ workspace_diagnostics=True,
+ ),
+)
+def text_document_diagnostic(
+ params: lsp.DocumentDiagnosticParams,
+) -> lsp.DocumentDiagnosticReport:
+ """Returns diagnostic report."""
+ document = json_server.workspace.get_document(params.text_document.uri)
+ return lsp.RelatedFullDocumentDiagnosticReport(
+ items=_validate_json(document.source),
+ kind=lsp.DocumentDiagnosticReportKind.Full,
+ )
+
+
+@json_server.feature(lsp.WORKSPACE_DIAGNOSTIC)
+def workspace_diagnostic(
+ params: lsp.WorkspaceDiagnosticParams,
+) -> lsp.WorkspaceDiagnosticReport:
+ """Returns diagnostic report."""
+ documents = json_server.workspace.text_documents.keys()
+
+ if len(documents) == 0:
+ items = []
+ else:
+ first = list(documents)[0]
+ document = json_server.workspace.get_document(first)
+ items = [
+ lsp.WorkspaceFullDocumentDiagnosticReport(
+ uri=document.uri,
+ version=document.version,
+ items=_validate_json(document.source),
+ kind=lsp.DocumentDiagnosticReportKind.Full,
+ )
+ ]
+
+ return lsp.WorkspaceDiagnosticReport(items=items)
+
+
+@json_server.feature(
+ lsp.TEXT_DOCUMENT_COMPLETION,
+ lsp.CompletionOptions(trigger_characters=[","], all_commit_characters=[":"]),
+)
+def completions(params: Optional[lsp.CompletionParams] = None) -> lsp.CompletionList:
+ """Returns completion items."""
+ return lsp.CompletionList(
+ is_incomplete=False,
+ items=[
+ lsp.CompletionItem(label='"'),
+ lsp.CompletionItem(label="["),
+ lsp.CompletionItem(label="]"),
+ lsp.CompletionItem(label="{"),
+ lsp.CompletionItem(label="}"),
+ ],
+ )
+
+
+@json_server.command(JsonLanguageServer.CMD_COUNT_DOWN_BLOCKING)
+def count_down_10_seconds_blocking(ls, *args):
+ """Starts counting down and showing message synchronously.
+ It will `block` the main thread, which can be tested by trying to show
+ completion items.
+ """
+ for i in range(COUNT_DOWN_START_IN_SECONDS):
+ ls.show_message(f"Counting down... {COUNT_DOWN_START_IN_SECONDS - i}")
+ time.sleep(COUNT_DOWN_SLEEP_IN_SECONDS)
+
+
+@json_server.command(JsonLanguageServer.CMD_COUNT_DOWN_NON_BLOCKING)
+async def count_down_10_seconds_non_blocking(ls, *args):
+ """Starts counting down and showing message asynchronously.
+ It won't `block` the main thread, which can be tested by trying to show
+ completion items.
+ """
+ for i in range(COUNT_DOWN_START_IN_SECONDS):
+ ls.show_message(f"Counting down... {COUNT_DOWN_START_IN_SECONDS - i}")
+ await asyncio.sleep(COUNT_DOWN_SLEEP_IN_SECONDS)
+
+
+@json_server.feature(lsp.TEXT_DOCUMENT_DID_CHANGE)
+def did_change(ls, params: lsp.DidChangeTextDocumentParams):
+ """Text document did change notification."""
+ _validate(ls, params)
+
+
+@json_server.feature(lsp.TEXT_DOCUMENT_DID_CLOSE)
+def did_close(server: JsonLanguageServer, params: lsp.DidCloseTextDocumentParams):
+ """Text document did close notification."""
+ server.show_message("Text Document Did Close")
+
+
+@json_server.feature(lsp.TEXT_DOCUMENT_DID_OPEN)
+async def did_open(ls, params: lsp.DidOpenTextDocumentParams):
+ """Text document did open notification."""
+ ls.show_message("Text Document Did Open")
+ _validate(ls, params)
+
+
+@json_server.feature(
+ lsp.TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
+ lsp.SemanticTokensLegend(token_types=["operator"], token_modifiers=[]),
+)
+def semantic_tokens(ls: JsonLanguageServer, params: lsp.SemanticTokensParams):
+ """See https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens
+ for details on how semantic tokens are encoded."""
+
+ TOKENS = re.compile('".*"(?=:)')
+
+ uri = params.text_document.uri
+ doc = ls.workspace.get_document(uri)
+
+ last_line = 0
+ last_start = 0
+
+ data = []
+
+ for lineno, line in enumerate(doc.lines):
+ last_start = 0
+
+ for match in TOKENS.finditer(line):
+ start, end = match.span()
+ data += [(lineno - last_line), (start - last_start), (end - start), 0, 0]
+
+ last_line = lineno
+ last_start = start
+
+ return lsp.SemanticTokens(data=data)
+
+
+@json_server.feature(lsp.TEXT_DOCUMENT_INLINE_VALUE)
+def inline_value(params: lsp.InlineValueParams):
+ """Returns inline value."""
+ return [lsp.InlineValueText(range=params.range, text="Inline value")]
+
+
+@json_server.command(JsonLanguageServer.CMD_PROGRESS)
+async def progress(ls: JsonLanguageServer, *args):
+ """Create and start the progress on the client."""
+ token = str(uuid.uuid4())
+ # Create
+ await ls.progress.create_async(token)
+ # Begin
+ ls.progress.begin(
+ token,
+ lsp.WorkDoneProgressBegin(title="Indexing", percentage=0, cancellable=True),
+ )
+ # Report
+ for i in range(1, 10):
+ # Check for cancellation from client
+ if ls.progress.tokens[token].cancelled():
+ # ... and stop the computation if client cancelled
+ return
+ ls.progress.report(
+ token,
+ lsp.WorkDoneProgressReport(message=f"{i * 10}%", percentage=i * 10),
+ )
+ await asyncio.sleep(2)
+ # End
+ ls.progress.end(token, lsp.WorkDoneProgressEnd(message="Finished"))
+
+
+@json_server.command(JsonLanguageServer.CMD_REGISTER_COMPLETIONS)
+async def register_completions(ls: JsonLanguageServer, *args):
+ """Register completions method on the client."""
+ params = lsp.RegistrationParams(
+ registrations=[
+ lsp.Registration(
+ id=str(uuid.uuid4()),
+ method=lsp.TEXT_DOCUMENT_COMPLETION,
+ register_options={"triggerCharacters": "[':']"},
+ )
+ ]
+ )
+ response = await ls.register_capability_async(params)
+ if response is None:
+ ls.show_message("Successfully registered completions method")
+ else:
+ ls.show_message(
+ "Error happened during completions registration.", lsp.MessageType.Error
+ )
+
+
+@json_server.command(JsonLanguageServer.CMD_SHOW_CONFIGURATION_ASYNC)
+async def show_configuration_async(ls: JsonLanguageServer, *args):
+ """Gets exampleConfiguration from the client settings using coroutines."""
+ try:
+ config = await ls.get_configuration_async(
+ lsp.WorkspaceConfigurationParams(
+ items=[
+ lsp.ConfigurationItem(
+ scope_uri="", section=JsonLanguageServer.CONFIGURATION_SECTION
+ )
+ ]
+ )
+ )
+
+ example_config = config[0].get("exampleConfiguration")
+
+ ls.show_message(f"jsonServer.exampleConfiguration value: {example_config}")
+
+ except Exception as e:
+ ls.show_message_log(f"Error ocurred: {e}")
+
+
+@json_server.command(JsonLanguageServer.CMD_SHOW_CONFIGURATION_CALLBACK)
+def show_configuration_callback(ls: JsonLanguageServer, *args):
+ """Gets exampleConfiguration from the client settings using callback."""
+
+ def _config_callback(config):
+ try:
+ example_config = config[0].get("exampleConfiguration")
+
+ ls.show_message(f"jsonServer.exampleConfiguration value: {example_config}")
+
+ except Exception as e:
+ ls.show_message_log(f"Error ocurred: {e}")
+
+ ls.get_configuration(
+ lsp.WorkspaceConfigurationParams(
+ items=[
+ lsp.ConfigurationItem(
+ scope_uri="", section=JsonLanguageServer.CONFIGURATION_SECTION
+ )
+ ]
+ ),
+ _config_callback,
+ )
+
+
+@json_server.thread()
+@json_server.command(JsonLanguageServer.CMD_SHOW_CONFIGURATION_THREAD)
+def show_configuration_thread(ls: JsonLanguageServer, *args):
+ """Gets exampleConfiguration from the client settings using thread pool."""
+ try:
+ config = ls.get_configuration(
+ lsp.WorkspaceConfigurationParams(
+ items=[
+ lsp.ConfigurationItem(
+ scope_uri="", section=JsonLanguageServer.CONFIGURATION_SECTION
+ )
+ ]
+ )
+ ).result(2)
+
+ example_config = config[0].get("exampleConfiguration")
+
+ ls.show_message(f"jsonServer.exampleConfiguration value: {example_config}")
+
+ except Exception as e:
+ ls.show_message_log(f"Error ocurred: {e}")
+
+
+@json_server.command(JsonLanguageServer.CMD_UNREGISTER_COMPLETIONS)
+async def unregister_completions(ls: JsonLanguageServer, *args):
+ """Unregister completions method on the client."""
+ params = lsp.UnregistrationParams(
+ unregisterations=[
+ lsp.Unregistration(
+ id=str(uuid.uuid4()), method=lsp.TEXT_DOCUMENT_COMPLETION
+ )
+ ]
+ )
+ response = await ls.unregister_capability_async(params)
+ if response is None:
+ ls.show_message("Successfully unregistered completions method")
+ else:
+ ls.show_message(
+ "Error happened during completions unregistration.", lsp.MessageType.Error
+ )
+
+
+def add_arguments(parser):
+ parser.description = "simple json server example"
+
+ parser.add_argument("--tcp", action="store_true", help="Use TCP server")
+ parser.add_argument("--ws", action="store_true", help="Use WebSocket server")
+ parser.add_argument("--host", default="127.0.0.1", help="Bind to this address")
+ parser.add_argument("--port", type=int, default=2087, help="Bind to this port")
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ add_arguments(parser)
+ args = parser.parse_args()
+
+ if args.tcp:
+ json_server.start_tcp(args.host, args.port)
+ elif args.ws:
+ json_server.start_ws(args.host, args.port)
+ else:
+ json_server.start_io()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/servers/workspace/Untitled-1.ipynb b/examples/servers/workspace/Untitled-1.ipynb
new file mode 100644
index 0000000..d45e746
--- /dev/null
+++ b/examples/servers/workspace/Untitled-1.ipynb
@@ -0,0 +1,44 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "12\n",
+ "#"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "mykey": 3
+ },
+ "outputs": [],
+ "source": [
+ "#"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "name": "python",
+ "version": "3.11.4"
+ },
+ "orig_nbformat": 4
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/examples/servers/workspace/sums.txt b/examples/servers/workspace/sums.txt
new file mode 100644
index 0000000..fcbc410
--- /dev/null
+++ b/examples/servers/workspace/sums.txt
@@ -0,0 +1,7 @@
+1 + 1 =
+
+
+2 + 3 =
+
+
+6 + 6 =
diff --git a/examples/servers/workspace/test.json b/examples/servers/workspace/test.json
new file mode 100644
index 0000000..21da3b2
--- /dev/null
+++ b/examples/servers/workspace/test.json
@@ -0,0 +1,3 @@
+{
+ "key": "value"
+}