summaryrefslogtreecommitdiffstats
path: root/examples/servers/json_server.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--examples/servers/json_server.py387
1 files changed, 387 insertions, 0 deletions
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()