From 6e932f09a9b68a55731eb3e69fc2bc91a5ff603c Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Sat, 29 Jul 2023 16:47:04 -0500 Subject: [PATCH] chore: introduce `black` formatting https://black.readthedocs.io/en/stable/ --- docs/source/conf.py | 46 +-- .../fountain-vscode-extension/src/server.py | 12 +- .../json-vscode-extension/server/__main__.py | 22 +- .../json-vscode-extension/server/server.py | 206 +++++----- .../server/tests/unit/test_features.py | 74 ++-- examples/servers/code_actions.py | 18 +- examples/servers/inlay_hints.py | 2 +- poetry.lock | 93 ++++- pygls/__init__.py | 6 +- pygls/capabilities.py | 127 ++++--- pygls/client.py | 16 +- pygls/constants.py | 12 +- pygls/exceptions.py | 62 ++- pygls/feature_manager.py | 57 ++- pygls/lsp/__init__.py | 20 +- pygls/lsp/client.py | 354 +++++++++++++----- pygls/progress.py | 35 +- pygls/protocol.py | 311 ++++++++------- pygls/server.py | 143 ++++--- pygls/uris.py | 71 ++-- pygls/workspace.py | 87 +++-- pyproject.toml | 8 +- scripts/check_client_is_uptodate.py | 14 +- scripts/generate_client.py | 8 +- scripts/generate_contributors_md.py | 6 +- tests/_init_server_stall_fix_hack.py | 9 +- tests/conftest.py | 2 +- tests/ls_setup.py | 48 +-- .../test_delta_missing_legend.py | 10 +- .../test_full_missing_legend.py | 2 +- tests/lsp/semantic_tokens/test_range.py | 15 +- .../test_semantic_tokens_full.py | 15 +- tests/lsp/test_call_hierarchy.py | 49 +-- tests/lsp/test_code_action.py | 1 - tests/lsp/test_code_lens.py | 8 +- tests/lsp/test_declaration.py | 7 +- tests/lsp/test_definition.py | 6 +- tests/lsp/test_document_highlight.py | 4 +- tests/lsp/test_hover.py | 10 +- tests/lsp/test_implementation.py | 6 +- tests/lsp/test_linked_editing_range.py | 7 +- tests/lsp/test_on_type_formatting.py | 19 +- tests/lsp/test_prepare_rename.py | 9 +- tests/lsp/test_progress.py | 15 +- tests/lsp/test_range_formatting.py | 4 +- tests/lsp/test_rename.py | 16 +- tests/lsp/test_signature_help.py | 7 +- tests/lsp/test_type_definition.py | 6 +- tests/pyodide_testrunner/run.py | 37 +- tests/servers/invalid_json.py | 5 +- tests/servers/large_response.py | 5 +- tests/test_client.py | 7 +- tests/test_document.py | 113 +++--- tests/test_feature_manager.py | 73 ++-- tests/test_language_server.py | 10 +- tests/test_protocol.py | 87 ++--- tests/test_server_connection.py | 17 +- tests/test_types.py | 32 +- tests/test_workspace.py | 7 +- 59 files changed, 1380 insertions(+), 1098 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 32a7221f..5ddf83c4 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -19,17 +19,17 @@ # -- Project information ----------------------------------------------------- -project = 'pygls' -copyright = 'Open Law Library' -author = 'Open Law Library' +project = "pygls" +copyright = "Open Law Library" +author = "Open Law Library" # The short X.Y version -version = '' +version = "" # The full version, including alpha/beta/rc tags -release = '' +release = "" -title = 'pygls Documentation' -description = 'a pythonic generic language server' +title = "pygls Documentation" +description = "a pythonic generic language server" # -- General configuration --------------------------------------------------- @@ -41,27 +41,26 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [ -] +extensions = [] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = 'en' +language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -77,7 +76,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -88,7 +87,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] +# html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. @@ -104,7 +103,7 @@ # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = 'pyglsdoc' +htmlhelp_basename = "pyglsdoc" # -- Options for LaTeX output ------------------------------------------------ @@ -113,15 +112,12 @@ # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # # 'preamble': '', - # Latex figure (float) alignment # # 'figure_align': 'htbp', @@ -131,8 +127,7 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'pygls.tex', title, - author, 'manual'), + (master_doc, "pygls.tex", title, author, "manual"), ] @@ -140,9 +135,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'pygls', description, [author], 1) -] +man_pages = [(master_doc, "pygls", description, [author], 1)] # -- Options for Texinfo output ---------------------------------------------- @@ -151,8 +144,7 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'pygls', title, author, - 'pygls', description, 'Miscellaneous'), + (master_doc, "pygls", title, author, "pygls", description, "Miscellaneous"), ] @@ -171,4 +163,4 @@ # epub_uid = '' # A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] +epub_exclude_files = ["search.html"] diff --git a/examples/fountain-vscode-extension/src/server.py b/examples/fountain-vscode-extension/src/server.py index 94e07183..2c1a1abf 100644 --- a/examples/fountain-vscode-extension/src/server.py +++ b/examples/fountain-vscode-extension/src/server.py @@ -1,13 +1,19 @@ import re from pygls.server import LanguageServer from lsprotocol.types import TEXT_DOCUMENT_COMPLETION -from lsprotocol.types import (CompletionItem, CompletionParams, CompletionList, CompletionOptions) +from lsprotocol.types import ( + CompletionItem, + CompletionParams, + CompletionList, + CompletionOptions, +) server = LanguageServer("foutain-language-server", "v0.1") CHARACTER = re.compile(r"^[A-Z][A-Z ]+$", re.MULTILINE) -@server.feature(TEXT_DOCUMENT_COMPLETION, CompletionOptions(trigger_characters=['.'])) + +@server.feature(TEXT_DOCUMENT_COMPLETION, CompletionOptions(trigger_characters=["."])) def on_completion(ls: LanguageServer, params: CompletionParams) -> CompletionList: """Completion suggestions for character names.""" @@ -18,5 +24,5 @@ def on_completion(ls: LanguageServer, params: CompletionParams) -> CompletionLis return CompletionList( is_incomplete=False, - items=[CompletionItem(label=character) for character in characters] + items=[CompletionItem(label=character) for character in characters], ) diff --git a/examples/json-vscode-extension/server/__main__.py b/examples/json-vscode-extension/server/__main__.py index 69a1e2ab..cb1bf8ef 100644 --- a/examples/json-vscode-extension/server/__main__.py +++ b/examples/json-vscode-extension/server/__main__.py @@ -25,22 +25,10 @@ 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" - ) + 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(): @@ -56,5 +44,5 @@ def main(): json_server.start_io() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/json-vscode-extension/server/server.py b/examples/json-vscode-extension/server/server.py index 8dd24548..737f4c72 100644 --- a/examples/json-vscode-extension/server/server.py +++ b/examples/json-vscode-extension/server/server.py @@ -22,21 +22,38 @@ from json import JSONDecodeError from typing import Optional -from lsprotocol.types import (TEXT_DOCUMENT_COMPLETION, TEXT_DOCUMENT_DID_CHANGE, - TEXT_DOCUMENT_DID_CLOSE, TEXT_DOCUMENT_DID_OPEN, - TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL) -from lsprotocol.types import (CompletionItem, CompletionList, CompletionOptions, - CompletionParams, ConfigurationItem, - Diagnostic, - DidChangeTextDocumentParams, - DidCloseTextDocumentParams, - DidOpenTextDocumentParams, MessageType, Position, - Range, Registration, RegistrationParams, - SemanticTokens, SemanticTokensLegend, SemanticTokensParams, - Unregistration, UnregistrationParams, - WorkDoneProgressBegin, WorkDoneProgressEnd, - WorkDoneProgressReport, - WorkspaceConfigurationParams) +from lsprotocol.types import ( + TEXT_DOCUMENT_COMPLETION, + TEXT_DOCUMENT_DID_CHANGE, + TEXT_DOCUMENT_DID_CLOSE, + TEXT_DOCUMENT_DID_OPEN, + TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, +) +from lsprotocol.types import ( + CompletionItem, + CompletionList, + CompletionOptions, + CompletionParams, + ConfigurationItem, + Diagnostic, + DidChangeTextDocumentParams, + DidCloseTextDocumentParams, + DidOpenTextDocumentParams, + MessageType, + Position, + Range, + Registration, + RegistrationParams, + SemanticTokens, + SemanticTokensLegend, + SemanticTokensParams, + Unregistration, + UnregistrationParams, + WorkDoneProgressBegin, + WorkDoneProgressEnd, + WorkDoneProgressReport, + WorkspaceConfigurationParams, +) from pygls.server import LanguageServer COUNT_DOWN_START_IN_SECONDS = 10 @@ -44,26 +61,26 @@ 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' + 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 = 'jsonServer' + CONFIGURATION_SECTION = "jsonServer" def __init__(self, *args): super().__init__(*args) -json_server = JsonLanguageServer('pygls-json-example', 'v0.1') +json_server = JsonLanguageServer("pygls-json-example", "v0.1") def _validate(ls, params): - ls.show_message_log('Validating json...') + ls.show_message_log("Validating json...") text_doc = ls.workspace.get_document(params.text_document.uri) @@ -87,10 +104,10 @@ def _validate_json(source): d = Diagnostic( range=Range( start=Position(line=line - 1, character=col - 1), - end=Position(line=line - 1, character=col) + end=Position(line=line - 1, character=col), ), message=msg, - source=type(json_server).__name__ + source=type(json_server).__name__, ) diagnostics.append(d) @@ -98,18 +115,20 @@ def _validate_json(source): return diagnostics -@json_server.feature(TEXT_DOCUMENT_COMPLETION, CompletionOptions(trigger_characters=[','])) +@json_server.feature( + TEXT_DOCUMENT_COMPLETION, CompletionOptions(trigger_characters=[","]) +) def completions(params: Optional[CompletionParams] = None) -> CompletionList: """Returns completion items.""" return CompletionList( is_incomplete=False, items=[ CompletionItem(label='"'), - CompletionItem(label='['), - CompletionItem(label=']'), - CompletionItem(label='{'), - CompletionItem(label='}'), - ] + CompletionItem(label="["), + CompletionItem(label="]"), + CompletionItem(label="{"), + CompletionItem(label="}"), + ], ) @@ -120,7 +139,7 @@ def count_down_10_seconds_blocking(ls, *args): completion items. """ for i in range(COUNT_DOWN_START_IN_SECONDS): - ls.show_message(f'Counting down... {COUNT_DOWN_START_IN_SECONDS - i}') + ls.show_message(f"Counting down... {COUNT_DOWN_START_IN_SECONDS - i}") time.sleep(COUNT_DOWN_SLEEP_IN_SECONDS) @@ -131,7 +150,7 @@ async def count_down_10_seconds_non_blocking(ls, *args): completion items. """ for i in range(COUNT_DOWN_START_IN_SECONDS): - ls.show_message(f'Counting down... {COUNT_DOWN_START_IN_SECONDS - i}') + ls.show_message(f"Counting down... {COUNT_DOWN_START_IN_SECONDS - i}") await asyncio.sleep(COUNT_DOWN_SLEEP_IN_SECONDS) @@ -144,22 +163,19 @@ def did_change(ls, params: DidChangeTextDocumentParams): @json_server.feature(TEXT_DOCUMENT_DID_CLOSE) def did_close(server: JsonLanguageServer, params: DidCloseTextDocumentParams): """Text document did close notification.""" - server.show_message('Text Document Did Close') + server.show_message("Text Document Did Close") @json_server.feature(TEXT_DOCUMENT_DID_OPEN) async def did_open(ls, params: DidOpenTextDocumentParams): """Text document did open notification.""" - ls.show_message('Text Document Did Open') + ls.show_message("Text Document Did Open") _validate(ls, params) @json_server.feature( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, - SemanticTokensLegend( - token_types = ["operator"], - token_modifiers = [] - ) + SemanticTokensLegend(token_types=["operator"], token_modifiers=[]), ) def semantic_tokens(ls: JsonLanguageServer, params: SemanticTokensParams): """See https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens @@ -180,13 +196,7 @@ def semantic_tokens(ls: JsonLanguageServer, params: SemanticTokensParams): for match in TOKENS.finditer(line): start, end = match.span() - data += [ - (lineno - last_line), - (start - last_start), - (end - start), - 0, - 0 - ] + data += [(lineno - last_line), (start - last_start), (end - start), 0, 0] last_line = lineno last_start = start @@ -194,7 +204,6 @@ def semantic_tokens(ls: JsonLanguageServer, params: SemanticTokensParams): return SemanticTokens(data=data) - @json_server.command(JsonLanguageServer.CMD_PROGRESS) async def progress(ls: JsonLanguageServer, *args): """Create and start the progress on the client.""" @@ -202,7 +211,9 @@ async def progress(ls: JsonLanguageServer, *args): # Create await ls.progress.create_async(token) # Begin - ls.progress.begin(token, WorkDoneProgressBegin(title='Indexing', percentage=0, cancellable=True)) + ls.progress.begin( + token, WorkDoneProgressBegin(title="Indexing", percentage=0, cancellable=True) + ) # Report for i in range(1, 10): # Check for cancellation from client @@ -211,28 +222,32 @@ async def progress(ls: JsonLanguageServer, *args): return ls.progress.report( token, - WorkDoneProgressReport(message=f'{i * 10}%', percentage= i * 10), + WorkDoneProgressReport(message=f"{i * 10}%", percentage=i * 10), ) await asyncio.sleep(2) # End - ls.progress.end(token, WorkDoneProgressEnd(message='Finished')) + ls.progress.end(token, WorkDoneProgressEnd(message="Finished")) @json_server.command(JsonLanguageServer.CMD_REGISTER_COMPLETIONS) async def register_completions(ls: JsonLanguageServer, *args): """Register completions method on the client.""" - params = RegistrationParams(registrations=[ - Registration( - id=str(uuid.uuid4()), - method=TEXT_DOCUMENT_COMPLETION, - register_options={"triggerCharacters": "[':']"}) - ]) + params = RegistrationParams( + registrations=[ + Registration( + id=str(uuid.uuid4()), + method=TEXT_DOCUMENT_COMPLETION, + register_options={"triggerCharacters": "[':']"}, + ) + ] + ) response = await ls.register_capability_async(params) if response is None: - ls.show_message('Successfully registered completions method') + ls.show_message("Successfully registered completions method") else: - ls.show_message('Error happened during completions registration.', - MessageType.Error) + ls.show_message( + "Error happened during completions registration.", MessageType.Error + ) @json_server.command(JsonLanguageServer.CMD_SHOW_CONFIGURATION_ASYNC) @@ -240,41 +255,45 @@ async def show_configuration_async(ls: JsonLanguageServer, *args): """Gets exampleConfiguration from the client settings using coroutines.""" try: config = await ls.get_configuration_async( - WorkspaceConfigurationParams(items=[ - ConfigurationItem( - scope_uri='', - section=JsonLanguageServer.CONFIGURATION_SECTION) - ])) + WorkspaceConfigurationParams( + items=[ + ConfigurationItem( + scope_uri="", section=JsonLanguageServer.CONFIGURATION_SECTION + ) + ] + ) + ) - example_config = config[0].get('exampleConfiguration') + example_config = config[0].get("exampleConfiguration") - ls.show_message(f'jsonServer.exampleConfiguration value: {example_config}') + ls.show_message(f"jsonServer.exampleConfiguration value: {example_config}") except Exception as e: - ls.show_message_log(f'Error ocurred: {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') + example_config = config[0].get("exampleConfiguration") - ls.show_message(f'jsonServer.exampleConfiguration value: {example_config}') + ls.show_message(f"jsonServer.exampleConfiguration value: {example_config}") except Exception as e: - ls.show_message_log(f'Error ocurred: {e}') + ls.show_message_log(f"Error ocurred: {e}") ls.get_configuration( WorkspaceConfigurationParams( items=[ ConfigurationItem( - scope_uri='', - section=JsonLanguageServer.CONFIGURATION_SECTION) + scope_uri="", section=JsonLanguageServer.CONFIGURATION_SECTION + ) ] ), - _config_callback + _config_callback, ) @@ -283,29 +302,36 @@ def _config_callback(config): def show_configuration_thread(ls: JsonLanguageServer, *args): """Gets exampleConfiguration from the client settings using thread pool.""" try: - config = ls.get_configuration(WorkspaceConfigurationParams(items=[ - ConfigurationItem( - scope_uri='', - section=JsonLanguageServer.CONFIGURATION_SECTION) - ])).result(2) + config = ls.get_configuration( + WorkspaceConfigurationParams( + items=[ + ConfigurationItem( + scope_uri="", section=JsonLanguageServer.CONFIGURATION_SECTION + ) + ] + ) + ).result(2) - example_config = config[0].get('exampleConfiguration') + example_config = config[0].get("exampleConfiguration") - ls.show_message(f'jsonServer.exampleConfiguration value: {example_config}') + ls.show_message(f"jsonServer.exampleConfiguration value: {example_config}") except Exception as e: - ls.show_message_log(f'Error ocurred: {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 = UnregistrationParams(unregisterations=[ - Unregistration(id=str(uuid.uuid4()), method=TEXT_DOCUMENT_COMPLETION) - ]) + params = UnregistrationParams( + unregisterations=[ + Unregistration(id=str(uuid.uuid4()), method=TEXT_DOCUMENT_COMPLETION) + ] + ) response = await ls.unregister_capability_async(params) if response is None: - ls.show_message('Successfully unregistered completions method') + ls.show_message("Successfully unregistered completions method") else: - ls.show_message('Error happened during completions unregistration.', - MessageType.Error) + ls.show_message( + "Error happened during completions unregistration.", MessageType.Error + ) diff --git a/examples/json-vscode-extension/server/tests/unit/test_features.py b/examples/json-vscode-extension/server/tests/unit/test_features.py index 1b762ace..8cd462fd 100644 --- a/examples/json-vscode-extension/server/tests/unit/test_features.py +++ b/examples/json-vscode-extension/server/tests/unit/test_features.py @@ -22,10 +22,13 @@ import pytest from unittest.mock import Mock -from lsprotocol.types import (DidCloseTextDocumentParams, - DidOpenTextDocumentParams, TextDocumentIdentifier, - WorkspaceConfigurationResponse, - TextDocumentItem) +from lsprotocol.types import ( + DidCloseTextDocumentParams, + DidOpenTextDocumentParams, + TextDocumentIdentifier, + WorkspaceConfigurationResponse, + TextDocumentItem, +) from pygls.server import StdOutTransportAdapter from pygls.workspace import Document, Workspace @@ -39,22 +42,21 @@ show_configuration_thread, ) -fake_document_uri = 'file://fake_doc.txt' -fake_document_content = 'text' +fake_document_uri = "file://fake_doc.txt" +fake_document_content = "text" fake_document = Document(fake_document_uri, fake_document_content) -server = JsonLanguageServer('test-json-server', 'v1') +server = JsonLanguageServer("test-json-server", "v1") server.publish_diagnostics = Mock() server.show_message = Mock() server.show_message_log = Mock() -server.lsp.workspace = Workspace('', None) +server.lsp.workspace = Workspace("", None) server.lsp._send_only_body = True server.workspace.get_document = Mock(return_value=fake_document) def _reset_mocks(stdin=None, stdout=None): - stdin = stdin or io.StringIO() stdout = stdout or io.StringIO() @@ -69,17 +71,18 @@ def test_completions(): labels = [i.label for i in completion_list.items] assert '"' in labels - assert '[' in labels - assert ']' in labels - assert '{' in labels - assert '}' in labels + assert "[" in labels + assert "]" in labels + assert "{" in labels + assert "}" in labels def test_did_close(): _reset_mocks() params = DidCloseTextDocumentParams( - text_document=TextDocumentIdentifier(uri=fake_document_uri)) + text_document=TextDocumentIdentifier(uri=fake_document_uri) + ) did_close(server, params) @@ -100,10 +103,13 @@ async def test_did_open(): expected_msg = err.msg params = DidOpenTextDocumentParams( - text_document=TextDocumentItem(uri=fake_document_uri, - language_id='json', - version=1, - text=fake_document_content)) + text_document=TextDocumentItem( + uri=fake_document_uri, + language_id="json", + version=1, + text=fake_document_content, + ) + ) await did_open(server, params) @@ -126,20 +132,17 @@ def test_show_configuration_callback(): show_configuration_callback(server) # Grab the request id - id_ = json.loads(stdout.getvalue())['id'] + id_ = json.loads(stdout.getvalue())["id"] # Simulate the client response server.lsp._procedure_handler( WorkspaceConfigurationResponse( - id=id_, - result=[ - {'exampleConfiguration': 'some_value'} - ] + id=id_, result=[{"exampleConfiguration": "some_value"}] ) ) server.show_message.assert_called_with( - 'jsonServer.exampleConfiguration value: some_value' + "jsonServer.exampleConfiguration value: some_value" ) @@ -152,25 +155,19 @@ async def send_response(): await asyncio.sleep(0.1) # Grab the request id - id_ = json.loads(stdout.getvalue())['id'] + id_ = json.loads(stdout.getvalue())["id"] # Simulate the client response server.lsp._procedure_handler( WorkspaceConfigurationResponse( - id=id_, - result=[ - {"exampleConfiguration": "some_value"} - ] + id=id_, result=[{"exampleConfiguration": "some_value"}] ) ) - await asyncio.gather( - show_configuration_async(server), - send_response() - ) + await asyncio.gather(show_configuration_async(server), send_response()) server.show_message.assert_called_with( - 'jsonServer.exampleConfiguration value: some_value' + "jsonServer.exampleConfiguration value: some_value" ) @@ -186,20 +183,17 @@ def test_show_configuration_thread(): time.sleep(1) # Grab the request id - id_ = json.loads(stdout.getvalue())['id'] + id_ = json.loads(stdout.getvalue())["id"] # Simulate the client response server.lsp._procedure_handler( WorkspaceConfigurationResponse( - id=id_, - result=[ - {'exampleConfiguration': 'some_value'} - ] + id=id_, result=[{"exampleConfiguration": "some_value"}] ) ) thread.join() server.show_message.assert_called_with( - 'jsonServer.exampleConfiguration value: some_value' + "jsonServer.exampleConfiguration value: some_value" ) diff --git a/examples/servers/code_actions.py b/examples/servers/code_actions.py index 24be8fab..0613ce13 100644 --- a/examples/servers/code_actions.py +++ b/examples/servers/code_actions.py @@ -35,9 +35,7 @@ @server.feature( TEXT_DOCUMENT_CODE_ACTION, - CodeActionOptions( - code_action_kinds=[CodeActionKind.QuickFix] - ) + CodeActionOptions(code_action_kinds=[CodeActionKind.QuickFix]), ) def code_actions(params: CodeActionParams): items = [] @@ -47,31 +45,25 @@ def code_actions(params: CodeActionParams): start_line = params.range.start.line end_line = params.range.end.line - lines = document.lines[start_line:end_line+1] + 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) + 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}!" - ) + 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]} - ) + edit=WorkspaceEdit(changes={document_uri: [text_edit]}), ) items.append(action) diff --git a/examples/servers/inlay_hints.py b/examples/servers/inlay_hints.py index 84e344eb..a5e3a6c2 100644 --- a/examples/servers/inlay_hints.py +++ b/examples/servers/inlay_hints.py @@ -57,7 +57,7 @@ def inlay_hints(params: InlayHintParams): if number is None: continue - binary_num = bin(number).split('b')[1] + binary_num = bin(number).split("b")[1] items.append( InlayHint( label=f":{binary_num}", diff --git a/poetry.lock b/poetry.lock index 70e8d7c0..8cba70bb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -56,6 +56,53 @@ files = [ [package.dependencies] pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} +[[package]] +name = "black" +version = "22.8.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.6.2" +files = [ + {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"}, + {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"}, + {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"}, + {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"}, + {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"}, + {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"}, + {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"}, + {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"}, + {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"}, + {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"}, + {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"}, + {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"}, + {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"}, + {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"}, + {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"}, + {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"}, + {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"}, + {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"}, + {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"}, + {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"}, + {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"}, + {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"}, + {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + [[package]] name = "cattrs" version = "23.1.2" @@ -252,6 +299,21 @@ files = [ {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, ] +[[package]] +name = "click" +version = "8.1.6" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"}, + {file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + [[package]] name = "colorama" version = "0.4.6" @@ -540,6 +602,35 @@ files = [ {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, ] +[[package]] +name = "pathspec" +version = "0.11.2" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, +] + +[[package]] +name = "platformdirs" +version = "3.10.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, + {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.8\""} + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] + [[package]] name = "pluggy" version = "1.2.0" @@ -1194,4 +1285,4 @@ ws = ["websockets"] [metadata] lock-version = "2.0" python-versions = ">=3.7.16,<4" -content-hash = "e43e7a7c60d3a017d22a1b9d3a0ff650a703ad6c8aa143bc7b43a80c031dc823" +content-hash = "0963df26c51f1bebf513af40b40cf9030252bb20b90aa8724beb7e128da138ed" diff --git a/pygls/__init__.py b/pygls/__init__.py index 0c1799e3..147cd9e1 100644 --- a/pygls/__init__.py +++ b/pygls/__init__.py @@ -19,7 +19,7 @@ import os import sys -IS_WIN = os.name == 'nt' -IS_PYODIDE = 'pyodide' in sys.modules +IS_WIN = os.name == "nt" +IS_PYODIDE = "pyodide" in sys.modules -pygls = 'pygls' +pygls = "pygls" diff --git a/pygls/capabilities.py b/pygls/capabilities.py index 73ca2021..5a2e7860 100644 --- a/pygls/capabilities.py +++ b/pygls/capabilities.py @@ -18,34 +18,65 @@ from typing import Any from lsprotocol.types import ( - INLAY_HINT_RESOLVE, TEXT_DOCUMENT_CODE_ACTION, TEXT_DOCUMENT_CODE_LENS, - TEXT_DOCUMENT_COMPLETION, TEXT_DOCUMENT_DECLARATION, - TEXT_DOCUMENT_DEFINITION, TEXT_DOCUMENT_DOCUMENT_COLOR, - TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT, TEXT_DOCUMENT_DOCUMENT_LINK, - TEXT_DOCUMENT_DOCUMENT_SYMBOL, TEXT_DOCUMENT_FOLDING_RANGE, - TEXT_DOCUMENT_FORMATTING, TEXT_DOCUMENT_HOVER, - TEXT_DOCUMENT_IMPLEMENTATION, TEXT_DOCUMENT_INLAY_HINT, TEXT_DOCUMENT_ON_TYPE_FORMATTING, - TEXT_DOCUMENT_RANGE_FORMATTING, TEXT_DOCUMENT_REFERENCES, - TEXT_DOCUMENT_RENAME, TEXT_DOCUMENT_SELECTION_RANGE, - TEXT_DOCUMENT_SIGNATURE_HELP, TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY, - TEXT_DOCUMENT_DID_CLOSE, TEXT_DOCUMENT_DID_OPEN, - TEXT_DOCUMENT_DID_SAVE, TEXT_DOCUMENT_LINKED_EDITING_RANGE, - TEXT_DOCUMENT_MONIKER, TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, - TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, - TEXT_DOCUMENT_WILL_SAVE, TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL, - TEXT_DOCUMENT_TYPE_DEFINITION, WORKSPACE_DID_CREATE_FILES, - WORKSPACE_DID_DELETE_FILES, WORKSPACE_DID_RENAME_FILES, - WORKSPACE_SYMBOL, WORKSPACE_WILL_CREATE_FILES, - WORKSPACE_WILL_DELETE_FILES, WORKSPACE_WILL_RENAME_FILES, InlayHintOptions + INLAY_HINT_RESOLVE, + TEXT_DOCUMENT_CODE_ACTION, + TEXT_DOCUMENT_CODE_LENS, + TEXT_DOCUMENT_COMPLETION, + TEXT_DOCUMENT_DECLARATION, + TEXT_DOCUMENT_DEFINITION, + TEXT_DOCUMENT_DOCUMENT_COLOR, + TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT, + TEXT_DOCUMENT_DOCUMENT_LINK, + TEXT_DOCUMENT_DOCUMENT_SYMBOL, + TEXT_DOCUMENT_FOLDING_RANGE, + TEXT_DOCUMENT_FORMATTING, + TEXT_DOCUMENT_HOVER, + TEXT_DOCUMENT_IMPLEMENTATION, + TEXT_DOCUMENT_INLAY_HINT, + TEXT_DOCUMENT_ON_TYPE_FORMATTING, + TEXT_DOCUMENT_RANGE_FORMATTING, + TEXT_DOCUMENT_REFERENCES, + TEXT_DOCUMENT_RENAME, + TEXT_DOCUMENT_SELECTION_RANGE, + TEXT_DOCUMENT_SIGNATURE_HELP, + TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY, + TEXT_DOCUMENT_DID_CLOSE, + TEXT_DOCUMENT_DID_OPEN, + TEXT_DOCUMENT_DID_SAVE, + TEXT_DOCUMENT_LINKED_EDITING_RANGE, + TEXT_DOCUMENT_MONIKER, + TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, + TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, + TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, + TEXT_DOCUMENT_WILL_SAVE, + TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL, + TEXT_DOCUMENT_TYPE_DEFINITION, + WORKSPACE_DID_CREATE_FILES, + WORKSPACE_DID_DELETE_FILES, + WORKSPACE_DID_RENAME_FILES, + WORKSPACE_SYMBOL, + WORKSPACE_WILL_CREATE_FILES, + WORKSPACE_WILL_DELETE_FILES, + WORKSPACE_WILL_RENAME_FILES, + InlayHintOptions, ) from lsprotocol.types import ( - ClientCapabilities, CodeLensOptions, CompletionOptions, - DocumentLinkOptions, ExecuteCommandOptions, ImplementationOptions, - SemanticTokensOptions, SemanticTokensRegistrationOptions, - SemanticTokensOptionsFullType1, ServerCapabilities, - ServerCapabilitiesWorkspaceType, SignatureHelpOptions, - TextDocumentSyncOptions, TypeDefinitionOptions, - FileOperationOptions, WorkspaceFoldersServerCapabilities + ClientCapabilities, + CodeLensOptions, + CompletionOptions, + DocumentLinkOptions, + ExecuteCommandOptions, + ImplementationOptions, + SemanticTokensOptions, + SemanticTokensRegistrationOptions, + SemanticTokensOptionsFullType1, + ServerCapabilities, + ServerCapabilitiesWorkspaceType, + SignatureHelpOptions, + TextDocumentSyncOptions, + TypeDefinitionOptions, + FileOperationOptions, + WorkspaceFoldersServerCapabilities, ) @@ -69,13 +100,9 @@ class ServerCapabilitiesBuilder: """Create `ServerCapabilities` instance depending on builtin and user registered features. """ + def __init__( - self, - client_capabilities, - features, - feature_options, - commands, - sync_kind + self, client_capabilities, features, feature_options, commands, sync_kind ): self.client_capabilities = client_capabilities self.features = features @@ -97,15 +124,14 @@ def _with_text_doc_sync(self): ) will_save = ( get_capability( - self.client_capabilities, - 'text_document.synchronization.will_save' + self.client_capabilities, "text_document.synchronization.will_save" ) and TEXT_DOCUMENT_WILL_SAVE in self.features ) will_save_wait_until = ( get_capability( self.client_capabilities, - 'text_document.synchronization.will_save_wait_until' + "text_document.synchronization.will_save_wait_until", ) and TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL in self.features ) @@ -125,7 +151,9 @@ def _with_text_doc_sync(self): return self def _with_completion(self): - value = self._provider_options(TEXT_DOCUMENT_COMPLETION, default=CompletionOptions()) + value = self._provider_options( + TEXT_DOCUMENT_COMPLETION, default=CompletionOptions() + ) if value is not None: self.server_cap.completion_provider = value return self @@ -165,7 +193,9 @@ def _with_type_definition(self): return self def _with_inlay_hints(self): - value = self._provider_options(TEXT_DOCUMENT_INLAY_HINT, default=InlayHintOptions()) + value = self._provider_options( + TEXT_DOCUMENT_INLAY_HINT, default=InlayHintOptions() + ) if value is not None: value.resolve_provider = INLAY_HINT_RESOLVE in self.features self.server_cap.inlay_hint_provider = value @@ -204,13 +234,17 @@ def _with_code_action(self): return self def _with_code_lens(self): - value = self._provider_options(TEXT_DOCUMENT_CODE_LENS, default=CodeLensOptions()) + value = self._provider_options( + TEXT_DOCUMENT_CODE_LENS, default=CodeLensOptions() + ) if value is not None: self.server_cap.code_lens_provider = value return self def _with_document_link(self): - value = self._provider_options(TEXT_DOCUMENT_DOCUMENT_LINK, default=DocumentLinkOptions()) + value = self._provider_options( + TEXT_DOCUMENT_DOCUMENT_LINK, default=DocumentLinkOptions() + ) if value is not None: self.server_cap.document_link_provider = value return self @@ -252,8 +286,9 @@ def _with_folding_range(self): return self def _with_execute_command(self): - self.server_cap.execute_command_provider = \ - ExecuteCommandOptions(commands=self.commands) + self.server_cap.execute_command_provider = ExecuteCommandOptions( + commands=self.commands + ) return self def _with_selection_range(self): @@ -269,11 +304,10 @@ def _with_call_hierarchy(self): return self def _with_semantic_tokens(self): - providers = [ TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, - TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE + TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, ] for provider in providers: @@ -296,7 +330,7 @@ def _with_semantic_tokens(self): options = SemanticTokensOptions( legend=value, full=full_support or None, - range=TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE in self.features or None + range=TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE in self.features or None, ) if options.full or options.range: @@ -336,7 +370,7 @@ def _with_workspace_capabilities(self): for method_name, capability_name in operations: client_supports_method = get_capability( - self.client_capabilities, f'workspace.file_operations.{capability_name}' + self.client_capabilities, f"workspace.file_operations.{capability_name}" ) if client_supports_method: @@ -357,8 +391,7 @@ def _build(self): def build(self): return ( - self - ._with_text_doc_sync() + self._with_text_doc_sync() ._with_completion() ._with_hover() ._with_signature_help() diff --git a/pygls/client.py b/pygls/client.py index cff22f25..a1cfb104 100644 --- a/pygls/client.py +++ b/pygls/client.py @@ -35,8 +35,7 @@ async def aio_readline(stop_event, reader, message_handler): - - CONTENT_LENGTH_PATTERN = re.compile(rb'^Content-Length: (\d+)\r\n$') + CONTENT_LENGTH_PATTERN = re.compile(rb"^Content-Length: (\d+)\r\n$") # Initialize message buffer message = [] @@ -54,11 +53,10 @@ async def aio_readline(stop_event, reader, message_handler): match = CONTENT_LENGTH_PATTERN.fullmatch(header) if match: content_length = int(match.group(1)) - logger.debug('Content length: %s', content_length) + logger.debug("Content length: %s", content_length) # Check if all headers have been read (as indicated by an empty line \r\n) if content_length and not header.strip(): - # Read body body = await reader.readexactly(content_length) if not body: @@ -66,7 +64,7 @@ async def aio_readline(stop_event, reader, message_handler): message.append(body) # Pass message to protocol - message_handler(b''.join(message)) + message_handler(b"".join(message)) # Reset the buffer message = [] @@ -93,7 +91,9 @@ def stopped(self) -> bool: return self._stop_event.is_set() def feature( - self, feature_name: str, options: Optional[Any] = None, + self, + feature_name: str, + options: Optional[Any] = None, ): """Decorator used to register LSP features. @@ -107,9 +107,7 @@ def completions(ls, params: LogMessageParams): async def start_io(self, cmd: str, *args, **kwargs): """Start the given server and communicate with it over stdio.""" - logger.debug( - "Starting server process: %s", ' '.join([cmd, *args]) - ) + logger.debug("Starting server process: %s", " ".join([cmd, *args])) server = await asyncio.create_subprocess_exec( cmd, *args, diff --git a/pygls/constants.py b/pygls/constants.py index 7350f026..ec2fa09a 100644 --- a/pygls/constants.py +++ b/pygls/constants.py @@ -16,11 +16,11 @@ ############################################################################ # Dynamically assigned attributes -ATTR_EXECUTE_IN_THREAD = 'execute_in_thread' -ATTR_COMMAND_TYPE = 'command' -ATTR_FEATURE_TYPE = 'feature' -ATTR_REGISTERED_NAME = 'reg_name' -ATTR_REGISTERED_TYPE = 'reg_type' +ATTR_EXECUTE_IN_THREAD = "execute_in_thread" +ATTR_COMMAND_TYPE = "command" +ATTR_FEATURE_TYPE = "feature" +ATTR_REGISTERED_NAME = "reg_name" +ATTR_REGISTERED_TYPE = "reg_type" # Parameters -PARAM_LS = 'ls' +PARAM_LS = "ls" diff --git a/pygls/exceptions.py b/pygls/exceptions.py index 21ad5bd1..533640e6 100644 --- a/pygls/exceptions.py +++ b/pygls/exceptions.py @@ -23,10 +23,10 @@ class JsonRpcException(Exception): """A class used as a base class for json rpc exceptions.""" def __init__(self, message=None, code=None, data=None): - message = message or getattr(self.__class__, 'MESSAGE') + message = message or getattr(self.__class__, "MESSAGE") super().__init__(message) self.message = message - self.code = code or getattr(self.__class__, 'CODE') + self.code = code or getattr(self.__class__, "CODE") self.data = data def __eq__(self, other): @@ -43,108 +43,110 @@ def __hash__(self): def from_error(error): for exc_class in _EXCEPTIONS: if exc_class.supports_code(error.code): - return exc_class(code=error.code, message=error.message, data=error.data) + return exc_class( + code=error.code, message=error.message, data=error.data + ) return JsonRpcException(code=error.code, message=error.message, data=error.data) @classmethod def supports_code(cls, code): # Defaults to UnknownErrorCode - return getattr(cls, 'CODE', -32001) == code + return getattr(cls, "CODE", -32001) == code def to_dict(self): exception_dict = { - 'code': self.code, - 'message': self.message, + "code": self.code, + "message": self.message, } if self.data is not None: - exception_dict['data'] = str(self.data) + exception_dict["data"] = str(self.data) return exception_dict class JsonRpcInternalError(JsonRpcException): CODE = -32602 - MESSAGE = 'Internal Error' + MESSAGE = "Internal Error" @classmethod def of(cls, exc_info): exc_type, exc_value, exc_tb = exc_info return cls( - message=''.join(traceback.format_exception_only( - exc_type, exc_value)).strip(), - data={'traceback': traceback.format_tb(exc_tb)} + message="".join( + traceback.format_exception_only(exc_type, exc_value) + ).strip(), + data={"traceback": traceback.format_tb(exc_tb)}, ) class JsonRpcInvalidParams(JsonRpcException): CODE = -32602 - MESSAGE = 'Invalid Params' + MESSAGE = "Invalid Params" class JsonRpcInvalidRequest(JsonRpcException): CODE = -32600 - MESSAGE = 'Invalid Request' + MESSAGE = "Invalid Request" class JsonRpcMethodNotFound(JsonRpcException): CODE = -32601 - MESSAGE = 'Method Not Found' + MESSAGE = "Method Not Found" @classmethod def of(cls, method): - return cls(message=cls.MESSAGE + ': ' + method) + return cls(message=cls.MESSAGE + ": " + method) class JsonRpcParseError(JsonRpcException): CODE = -32700 - MESSAGE = 'Parse Error' + MESSAGE = "Parse Error" class JsonRpcRequestCancelled(JsonRpcException): CODE = -32800 - MESSAGE = 'Request Cancelled' + MESSAGE = "Request Cancelled" class JsonRpcContentModified(JsonRpcException): CODE = -32801 - MESSAGE = 'Content Modified' + MESSAGE = "Content Modified" class JsonRpcServerNotInitialized(JsonRpcException): CODE = -32002 - MESSAGE = 'ServerNotInitialized' + MESSAGE = "ServerNotInitialized" class JsonRpcUnknownErrorCode(JsonRpcException): CODE = -32001 - MESSAGE = 'UnknownErrorCode' + MESSAGE = "UnknownErrorCode" class JsonRpcReservedErrorRangeStart(JsonRpcException): CODE = -32099 - MESSAGE = 'jsonrpcReservedErrorRangeStart' + MESSAGE = "jsonrpcReservedErrorRangeStart" class JsonRpcReservedErrorRangeEnd(JsonRpcException): CODE = -32000 - MESSAGE = 'jsonrpcReservedErrorRangeEnd' + MESSAGE = "jsonrpcReservedErrorRangeEnd" class LspReservedErrorRangeStart(JsonRpcException): CODE = -32899 - MESSAGE = 'lspReservedErrorRangeStart' + MESSAGE = "lspReservedErrorRangeStart" class LspReservedErrorRangeEnd(JsonRpcException): CODE = -32800 - MESSAGE = 'lspReservedErrorRangeEnd' + MESSAGE = "lspReservedErrorRangeEnd" class JsonRpcServerError(JsonRpcException): - def __init__(self, message, code, data=None): if not _is_server_error_code(code): - raise ValueError('Error code should be in range -32099 - -32000') + raise ValueError("Error code should be in range -32099 - -32000") super().__init__(message=message, code=code, data=data) @classmethod @@ -172,7 +174,6 @@ class PyglsError(Exception): class CommandAlreadyRegisteredError(PyglsError): - def __init__(self, command_name): self.command_name = command_name @@ -181,7 +182,6 @@ def __repr__(self): class FeatureAlreadyRegisteredError(PyglsError): - def __init__(self, feature_name): self.feature_name = feature_name @@ -198,7 +198,6 @@ class FeatureNotificationError(PyglsError): class MethodTypeNotRegisteredError(PyglsError): - def __init__(self, name): self.name = name @@ -211,10 +210,9 @@ class ThreadDecoratorError(PyglsError): class ValidationError(PyglsError): - def __init__(self, errors=None): self.errors = errors or [] def __repr__(self): - opt_errs = '\n-'.join([e for e in self.errors]) - return f'Missing options: {opt_errs}' + opt_errs = "\n-".join([e for e in self.errors]) + return f"Missing options: {opt_errs}" diff --git a/pygls/feature_manager.py b/pygls/feature_manager.py index 9e6dcc67..d1859485 100644 --- a/pygls/feature_manager.py +++ b/pygls/feature_manager.py @@ -21,10 +21,20 @@ import logging from typing import Any, Callable, Dict, Optional, get_type_hints -from pygls.constants import (ATTR_COMMAND_TYPE, ATTR_EXECUTE_IN_THREAD, ATTR_FEATURE_TYPE, - ATTR_REGISTERED_NAME, ATTR_REGISTERED_TYPE, PARAM_LS) -from pygls.exceptions import (CommandAlreadyRegisteredError, FeatureAlreadyRegisteredError, - ThreadDecoratorError, ValidationError) +from pygls.constants import ( + ATTR_COMMAND_TYPE, + ATTR_EXECUTE_IN_THREAD, + ATTR_FEATURE_TYPE, + ATTR_REGISTERED_NAME, + ATTR_REGISTERED_TYPE, + PARAM_LS, +) +from pygls.exceptions import ( + CommandAlreadyRegisteredError, + FeatureAlreadyRegisteredError, + ThreadDecoratorError, + ValidationError, +) from pygls.lsp import get_method_options_type, is_instance logger = logging.getLogger(__name__) @@ -40,7 +50,9 @@ def assign_thread_attr(f): def get_help_attrs(f): - return getattr(f, ATTR_REGISTERED_NAME, None), getattr(f, ATTR_REGISTERED_TYPE, None) + return getattr(f, ATTR_REGISTERED_NAME, None), getattr( + f, ATTR_REGISTERED_TYPE, None + ) def has_ls_param_or_annotation(f, annotation): @@ -64,8 +76,10 @@ def wrap_with_server(f, server): return f if asyncio.iscoroutinefunction(f): + async def wrapped(*args, **kwargs): return await f(server, *args, **kwargs) + else: wrapped = functools.partial(f, server) if is_thread_function(f): @@ -99,7 +113,7 @@ def __init__(self, server=None): def add_builtin_feature(self, feature_name: str, func: Callable) -> None: """Registers builtin (predefined) feature.""" self._builtin_features[feature_name] = func - logger.info('Registered builtin feature %s', feature_name) + logger.info("Registered builtin feature %s", feature_name) @property def builtin_features(self) -> Dict: @@ -112,11 +126,12 @@ def command(self, command_name: str) -> Callable: Example: @ls.command('myCustomCommand') """ + def decorator(f): # Validate - if command_name is None or command_name.strip() == '': - logger.error('Missing command name.') - raise ValidationError('Command name is required.') + if command_name is None or command_name.strip() == "": + logger.error("Missing command name.") + raise ValidationError("Command name is required.") # Check if not already registered if command_name in self._commands: @@ -134,6 +149,7 @@ def decorator(f): logger.info('Command "%s" is successfully registered.', command_name) return f + return decorator @property @@ -142,18 +158,21 @@ def commands(self) -> Dict: return self._commands def feature( - self, feature_name: str, options: Optional[Any] = None, + self, + feature_name: str, + options: Optional[Any] = None, ) -> Callable: """Decorator used to register LSP features. Example: @ls.feature('textDocument/completion', CompletionItems(trigger_characters=['.'])) """ + def decorator(f): # Validate - if feature_name is None or feature_name.strip() == '': - logger.error('Missing feature name.') - raise ValidationError('Feature name is required.') + if feature_name is None or feature_name.strip() == "": + logger.error("Missing feature name.") + raise ValidationError("Feature name is required.") # Add feature if not exists if feature_name in self._features: @@ -172,14 +191,17 @@ def decorator(f): options_type = get_method_options_type(feature_name) if options_type and not is_instance(options, options_type): raise TypeError( - (f'Options of method "{feature_name}"' - f' should be instance of type {options_type}') + ( + f'Options of method "{feature_name}"' + f" should be instance of type {options_type}" + ) ) self._feature_options[feature_name] = options logger.info('Registered "%s" with options "%s"', feature_name, options) return f + return decorator @property @@ -194,10 +216,12 @@ def features(self) -> Dict: def thread(self) -> Callable: """Decorator that mark function to execute it in a thread.""" + def decorator(f): if asyncio.iscoroutinefunction(f): raise ThreadDecoratorError( - f"Thread decorator cannot be used with async functions \"{f.__name__}\"") + f'Thread decorator cannot be used with async functions "{f.__name__}"' + ) # Allow any decorator order try: @@ -213,4 +237,5 @@ def decorator(f): assign_thread_attr(f) return f + return decorator diff --git a/pygls/lsp/__init__.py b/pygls/lsp/__init__.py index 00acf04d..c867e1fd 100644 --- a/pygls/lsp/__init__.py +++ b/pygls/lsp/__init__.py @@ -33,7 +33,7 @@ SaveOptions, SemanticTokensLegend, SemanticTokensRegistrationOptions, - ShowDocumentResult + ShowDocumentResult, ) from typeguard import check_type @@ -44,9 +44,15 @@ METHOD_TO_OPTIONS = { TEXT_DOCUMENT_DID_SAVE: SaveOptions, - TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL: Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], - TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA: Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], - TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE: Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], + TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL: Union[ + SemanticTokensLegend, SemanticTokensRegistrationOptions + ], + TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA: Union[ + SemanticTokensLegend, SemanticTokensRegistrationOptions + ], + TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE: Union[ + SemanticTokensLegend, SemanticTokensRegistrationOptions + ], WORKSPACE_DID_CREATE_FILES: FileOperationRegistrationOptions, WORKSPACE_DID_DELETE_FILES: FileOperationRegistrationOptions, WORKSPACE_DID_RENAME_FILES: FileOperationRegistrationOptions, @@ -96,11 +102,13 @@ def get_method_options_type( if options_type is not None: return options_type - registration_type = get_method_registration_options_type(method_name, lsp_methods_map) + registration_type = get_method_registration_options_type( + method_name, lsp_methods_map + ) if registration_type is None: return None - type_name = registration_type.__name__.replace('Registration', '') + type_name = registration_type.__name__.replace("Registration", "") options_type = ALL_TYPES_MAP.get(type_name, None) if options_type is None: diff --git a/pygls/lsp/client.py b/pygls/lsp/client.py index b76783cd..eb351827 100644 --- a/pygls/lsp/client.py +++ b/pygls/lsp/client.py @@ -111,7 +111,6 @@ class LanguageClient(Client): - def __init__( self, name: str, @@ -127,7 +126,9 @@ def __init__( def call_hierarchy_incoming_calls( self, params: CallHierarchyIncomingCallsParams, - callback: Optional[Callable[[Optional[List[CallHierarchyIncomingCall]]], None]] = None, + callback: Optional[ + Callable[[Optional[List[CallHierarchyIncomingCall]]], None] + ] = None, ) -> Future: """Make a ``callHierarchy/incomingCalls`` request. @@ -138,7 +139,9 @@ def call_hierarchy_incoming_calls( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("callHierarchy/incomingCalls", params, callback) + return self.protocol.send_request( + "callHierarchy/incomingCalls", params, callback + ) async def call_hierarchy_incoming_calls_async( self, @@ -153,12 +156,16 @@ async def call_hierarchy_incoming_calls_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("callHierarchy/incomingCalls", params) + return await self.protocol.send_request_async( + "callHierarchy/incomingCalls", params + ) def call_hierarchy_outgoing_calls( self, params: CallHierarchyOutgoingCallsParams, - callback: Optional[Callable[[Optional[List[CallHierarchyOutgoingCall]]], None]] = None, + callback: Optional[ + Callable[[Optional[List[CallHierarchyOutgoingCall]]], None] + ] = None, ) -> Future: """Make a ``callHierarchy/outgoingCalls`` request. @@ -169,7 +176,9 @@ def call_hierarchy_outgoing_calls( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("callHierarchy/outgoingCalls", params, callback) + return self.protocol.send_request( + "callHierarchy/outgoingCalls", params, callback + ) async def call_hierarchy_outgoing_calls_async( self, @@ -184,7 +193,9 @@ async def call_hierarchy_outgoing_calls_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("callHierarchy/outgoingCalls", params) + return await self.protocol.send_request_async( + "callHierarchy/outgoingCalls", params + ) def code_action_resolve( self, @@ -414,7 +425,9 @@ async def shutdown_async( def text_document_code_action( self, params: CodeActionParams, - callback: Optional[Callable[[Optional[List[Union[Command, CodeAction]]]], None]] = None, + callback: Optional[ + Callable[[Optional[List[Union[Command, CodeAction]]]], None] + ] = None, ) -> Future: """Make a ``textDocument/codeAction`` request. @@ -481,7 +494,9 @@ def text_document_color_presentation( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/colorPresentation", params, callback) + return self.protocol.send_request( + "textDocument/colorPresentation", params, callback + ) async def text_document_color_presentation_async( self, @@ -498,12 +513,16 @@ async def text_document_color_presentation_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/colorPresentation", params) + return await self.protocol.send_request_async( + "textDocument/colorPresentation", params + ) def text_document_completion( self, params: CompletionParams, - callback: Optional[Callable[[Union[List[CompletionItem], CompletionList, None]], None]] = None, + callback: Optional[ + Callable[[Union[List[CompletionItem], CompletionList, None]], None] + ] = None, ) -> Future: """Make a ``textDocument/completion`` request. @@ -548,7 +567,9 @@ async def text_document_completion_async( def text_document_declaration( self, params: DeclarationParams, - callback: Optional[Callable[[Union[Location, List[Location], List[LocationLink], None]], None]] = None, + callback: Optional[ + Callable[[Union[Location, List[Location], List[LocationLink], None]], None] + ] = None, ) -> Future: """Make a ``textDocument/declaration`` request. @@ -580,12 +601,16 @@ async def text_document_declaration_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/declaration", params) + return await self.protocol.send_request_async( + "textDocument/declaration", params + ) def text_document_definition( self, params: DefinitionParams, - callback: Optional[Callable[[Union[Location, List[Location], List[LocationLink], None]], None]] = None, + callback: Optional[ + Callable[[Union[Location, List[Location], List[LocationLink], None]], None] + ] = None, ) -> Future: """Make a ``textDocument/definition`` request. @@ -622,7 +647,17 @@ async def text_document_definition_async( def text_document_diagnostic( self, params: DocumentDiagnosticParams, - callback: Optional[Callable[[Union[RelatedFullDocumentDiagnosticReport, RelatedUnchangedDocumentDiagnosticReport]], None]] = None, + callback: Optional[ + Callable[ + [ + Union[ + RelatedFullDocumentDiagnosticReport, + RelatedUnchangedDocumentDiagnosticReport, + ] + ], + None, + ] + ] = None, ) -> Future: """Make a ``textDocument/diagnostic`` request. @@ -638,7 +673,9 @@ def text_document_diagnostic( async def text_document_diagnostic_async( self, params: DocumentDiagnosticParams, - ) -> Union[RelatedFullDocumentDiagnosticReport, RelatedUnchangedDocumentDiagnosticReport]: + ) -> Union[ + RelatedFullDocumentDiagnosticReport, RelatedUnchangedDocumentDiagnosticReport + ]: """Make a ``textDocument/diagnostic`` request. The document diagnostic request definition. @@ -666,7 +703,9 @@ def text_document_document_color( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/documentColor", params, callback) + return self.protocol.send_request( + "textDocument/documentColor", params, callback + ) async def text_document_document_color_async( self, @@ -683,7 +722,9 @@ async def text_document_document_color_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/documentColor", params) + return await self.protocol.send_request_async( + "textDocument/documentColor", params + ) def text_document_document_highlight( self, @@ -702,7 +743,9 @@ def text_document_document_highlight( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/documentHighlight", params, callback) + return self.protocol.send_request( + "textDocument/documentHighlight", params, callback + ) async def text_document_document_highlight_async( self, @@ -720,7 +763,9 @@ async def text_document_document_highlight_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/documentHighlight", params) + return await self.protocol.send_request_async( + "textDocument/documentHighlight", params + ) def text_document_document_link( self, @@ -747,12 +792,16 @@ async def text_document_document_link_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/documentLink", params) + return await self.protocol.send_request_async( + "textDocument/documentLink", params + ) def text_document_document_symbol( self, params: DocumentSymbolParams, - callback: Optional[Callable[[Union[List[SymbolInformation], List[DocumentSymbol], None]], None]] = None, + callback: Optional[ + Callable[[Union[List[SymbolInformation], List[DocumentSymbol], None]], None] + ] = None, ) -> Future: """Make a ``textDocument/documentSymbol`` request. @@ -765,7 +814,9 @@ def text_document_document_symbol( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/documentSymbol", params, callback) + return self.protocol.send_request( + "textDocument/documentSymbol", params, callback + ) async def text_document_document_symbol_async( self, @@ -782,7 +833,9 @@ async def text_document_document_symbol_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/documentSymbol", params) + return await self.protocol.send_request_async( + "textDocument/documentSymbol", params + ) def text_document_folding_range( self, @@ -815,7 +868,9 @@ async def text_document_folding_range_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/foldingRange", params) + return await self.protocol.send_request_async( + "textDocument/foldingRange", params + ) def text_document_formatting( self, @@ -880,7 +935,9 @@ async def text_document_hover_async( def text_document_implementation( self, params: ImplementationParams, - callback: Optional[Callable[[Union[Location, List[Location], List[LocationLink], None]], None]] = None, + callback: Optional[ + Callable[[Union[Location, List[Location], List[LocationLink], None]], None] + ] = None, ) -> Future: """Make a ``textDocument/implementation`` request. @@ -894,7 +951,9 @@ def text_document_implementation( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/implementation", params, callback) + return self.protocol.send_request( + "textDocument/implementation", params, callback + ) async def text_document_implementation_async( self, @@ -912,7 +971,9 @@ async def text_document_implementation_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/implementation", params) + return await self.protocol.send_request_async( + "textDocument/implementation", params + ) def text_document_inlay_hint( self, @@ -952,7 +1013,22 @@ async def text_document_inlay_hint_async( def text_document_inline_value( self, params: InlineValueParams, - callback: Optional[Callable[[Optional[List[Union[InlineValueText, InlineValueVariableLookup, InlineValueEvaluatableExpression]]]], None]] = None, + callback: Optional[ + Callable[ + [ + Optional[ + List[ + Union[ + InlineValueText, + InlineValueVariableLookup, + InlineValueEvaluatableExpression, + ] + ] + ] + ], + None, + ] + ] = None, ) -> Future: """Make a ``textDocument/inlineValue`` request. @@ -970,7 +1046,15 @@ def text_document_inline_value( async def text_document_inline_value_async( self, params: InlineValueParams, - ) -> Optional[List[Union[InlineValueText, InlineValueVariableLookup, InlineValueEvaluatableExpression]]]: + ) -> Optional[ + List[ + Union[ + InlineValueText, + InlineValueVariableLookup, + InlineValueEvaluatableExpression, + ] + ] + ]: """Make a ``textDocument/inlineValue`` request. A request to provide inline values in a document. The request's parameter is of @@ -982,7 +1066,9 @@ async def text_document_inline_value_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/inlineValue", params) + return await self.protocol.send_request_async( + "textDocument/inlineValue", params + ) def text_document_linked_editing_range( self, @@ -998,7 +1084,9 @@ def text_document_linked_editing_range( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/linkedEditingRange", params, callback) + return self.protocol.send_request( + "textDocument/linkedEditingRange", params, callback + ) async def text_document_linked_editing_range_async( self, @@ -1013,7 +1101,9 @@ async def text_document_linked_editing_range_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/linkedEditingRange", params) + return await self.protocol.send_request_async( + "textDocument/linkedEditingRange", params + ) def text_document_moniker( self, @@ -1060,7 +1150,9 @@ def text_document_on_type_formatting( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/onTypeFormatting", params, callback) + return self.protocol.send_request( + "textDocument/onTypeFormatting", params, callback + ) async def text_document_on_type_formatting_async( self, @@ -1073,7 +1165,9 @@ async def text_document_on_type_formatting_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/onTypeFormatting", params) + return await self.protocol.send_request_async( + "textDocument/onTypeFormatting", params + ) def text_document_prepare_call_hierarchy( self, @@ -1090,7 +1184,9 @@ def text_document_prepare_call_hierarchy( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/prepareCallHierarchy", params, callback) + return self.protocol.send_request( + "textDocument/prepareCallHierarchy", params, callback + ) async def text_document_prepare_call_hierarchy_async( self, @@ -1106,12 +1202,26 @@ async def text_document_prepare_call_hierarchy_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/prepareCallHierarchy", params) + return await self.protocol.send_request_async( + "textDocument/prepareCallHierarchy", params + ) def text_document_prepare_rename( self, params: PrepareRenameParams, - callback: Optional[Callable[[Union[Range, PrepareRenameResult_Type1, PrepareRenameResult_Type2, None]], None]] = None, + callback: Optional[ + Callable[ + [ + Union[ + Range, + PrepareRenameResult_Type1, + PrepareRenameResult_Type2, + None, + ] + ], + None, + ] + ] = None, ) -> Future: """Make a ``textDocument/prepareRename`` request. @@ -1122,7 +1232,9 @@ def text_document_prepare_rename( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/prepareRename", params, callback) + return self.protocol.send_request( + "textDocument/prepareRename", params, callback + ) async def text_document_prepare_rename_async( self, @@ -1137,7 +1249,9 @@ async def text_document_prepare_rename_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/prepareRename", params) + return await self.protocol.send_request_async( + "textDocument/prepareRename", params + ) def text_document_prepare_type_hierarchy( self, @@ -1154,7 +1268,9 @@ def text_document_prepare_type_hierarchy( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/prepareTypeHierarchy", params, callback) + return self.protocol.send_request( + "textDocument/prepareTypeHierarchy", params, callback + ) async def text_document_prepare_type_hierarchy_async( self, @@ -1170,7 +1286,9 @@ async def text_document_prepare_type_hierarchy_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/prepareTypeHierarchy", params) + return await self.protocol.send_request_async( + "textDocument/prepareTypeHierarchy", params + ) def text_document_range_formatting( self, @@ -1184,7 +1302,9 @@ def text_document_range_formatting( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/rangeFormatting", params, callback) + return self.protocol.send_request( + "textDocument/rangeFormatting", params, callback + ) async def text_document_range_formatting_async( self, @@ -1197,7 +1317,9 @@ async def text_document_range_formatting_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/rangeFormatting", params) + return await self.protocol.send_request_async( + "textDocument/rangeFormatting", params + ) def text_document_references( self, @@ -1276,7 +1398,9 @@ def text_document_selection_range( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/selectionRange", params, callback) + return self.protocol.send_request( + "textDocument/selectionRange", params, callback + ) async def text_document_selection_range_async( self, @@ -1292,7 +1416,9 @@ async def text_document_selection_range_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/selectionRange", params) + return await self.protocol.send_request_async( + "textDocument/selectionRange", params + ) def text_document_semantic_tokens_full( self, @@ -1306,7 +1432,9 @@ def text_document_semantic_tokens_full( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/semanticTokens/full", params, callback) + return self.protocol.send_request( + "textDocument/semanticTokens/full", params, callback + ) async def text_document_semantic_tokens_full_async( self, @@ -1319,12 +1447,16 @@ async def text_document_semantic_tokens_full_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/semanticTokens/full", params) + return await self.protocol.send_request_async( + "textDocument/semanticTokens/full", params + ) def text_document_semantic_tokens_full_delta( self, params: SemanticTokensDeltaParams, - callback: Optional[Callable[[Union[SemanticTokens, SemanticTokensDelta, None]], None]] = None, + callback: Optional[ + Callable[[Union[SemanticTokens, SemanticTokensDelta, None]], None] + ] = None, ) -> Future: """Make a ``textDocument/semanticTokens/full/delta`` request. @@ -1333,7 +1465,9 @@ def text_document_semantic_tokens_full_delta( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/semanticTokens/full/delta", params, callback) + return self.protocol.send_request( + "textDocument/semanticTokens/full/delta", params, callback + ) async def text_document_semantic_tokens_full_delta_async( self, @@ -1346,7 +1480,9 @@ async def text_document_semantic_tokens_full_delta_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/semanticTokens/full/delta", params) + return await self.protocol.send_request_async( + "textDocument/semanticTokens/full/delta", params + ) def text_document_semantic_tokens_range( self, @@ -1360,7 +1496,9 @@ def text_document_semantic_tokens_range( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/semanticTokens/range", params, callback) + return self.protocol.send_request( + "textDocument/semanticTokens/range", params, callback + ) async def text_document_semantic_tokens_range_async( self, @@ -1373,39 +1511,41 @@ async def text_document_semantic_tokens_range_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/semanticTokens/range", params) + return await self.protocol.send_request_async( + "textDocument/semanticTokens/range", params + ) def text_document_signature_help( self, params: SignatureHelpParams, callback: Optional[Callable[[Optional[SignatureHelp]], None]] = None, ) -> Future: - """Make a ``textDocument/signatureHelp`` request. - - - """ + """Make a ``textDocument/signatureHelp`` request.""" if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/signatureHelp", params, callback) + return self.protocol.send_request( + "textDocument/signatureHelp", params, callback + ) async def text_document_signature_help_async( self, params: SignatureHelpParams, ) -> Optional[SignatureHelp]: - """Make a ``textDocument/signatureHelp`` request. - - - """ + """Make a ``textDocument/signatureHelp`` request.""" if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/signatureHelp", params) + return await self.protocol.send_request_async( + "textDocument/signatureHelp", params + ) def text_document_type_definition( self, params: TypeDefinitionParams, - callback: Optional[Callable[[Union[Location, List[Location], List[LocationLink], None]], None]] = None, + callback: Optional[ + Callable[[Union[Location, List[Location], List[LocationLink], None]], None] + ] = None, ) -> Future: """Make a ``textDocument/typeDefinition`` request. @@ -1419,7 +1559,9 @@ def text_document_type_definition( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/typeDefinition", params, callback) + return self.protocol.send_request( + "textDocument/typeDefinition", params, callback + ) async def text_document_type_definition_async( self, @@ -1437,7 +1579,9 @@ async def text_document_type_definition_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/typeDefinition", params) + return await self.protocol.send_request_async( + "textDocument/typeDefinition", params + ) def text_document_will_save_wait_until( self, @@ -1457,7 +1601,9 @@ def text_document_will_save_wait_until( if self.stopped: raise RuntimeError("Client has been stopped.") - return self.protocol.send_request("textDocument/willSaveWaitUntil", params, callback) + return self.protocol.send_request( + "textDocument/willSaveWaitUntil", params, callback + ) async def text_document_will_save_wait_until_async( self, @@ -1476,7 +1622,9 @@ async def text_document_will_save_wait_until_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("textDocument/willSaveWaitUntil", params) + return await self.protocol.send_request_async( + "textDocument/willSaveWaitUntil", params + ) def type_hierarchy_subtypes( self, @@ -1538,7 +1686,9 @@ async def type_hierarchy_supertypes_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("typeHierarchy/supertypes", params) + return await self.protocol.send_request_async( + "typeHierarchy/supertypes", params + ) def workspace_diagnostic( self, @@ -1602,12 +1752,18 @@ async def workspace_execute_command_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("workspace/executeCommand", params) + return await self.protocol.send_request_async( + "workspace/executeCommand", params + ) def workspace_symbol( self, params: WorkspaceSymbolParams, - callback: Optional[Callable[[Union[List[SymbolInformation], List[WorkspaceSymbol], None]], None]] = None, + callback: Optional[ + Callable[ + [Union[List[SymbolInformation], List[WorkspaceSymbol], None]], None + ] + ] = None, ) -> Future: """Make a ``workspace/symbol`` request. @@ -1713,7 +1869,9 @@ async def workspace_will_create_files_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("workspace/willCreateFiles", params) + return await self.protocol.send_request_async( + "workspace/willCreateFiles", params + ) def workspace_will_delete_files( self, @@ -1746,7 +1904,9 @@ async def workspace_will_delete_files_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("workspace/willDeleteFiles", params) + return await self.protocol.send_request_async( + "workspace/willDeleteFiles", params + ) def workspace_will_rename_files( self, @@ -1779,13 +1939,12 @@ async def workspace_will_rename_files_async( if self.stopped: raise RuntimeError("Client has been stopped.") - return await self.protocol.send_request_async("workspace/willRenameFiles", params) + return await self.protocol.send_request_async( + "workspace/willRenameFiles", params + ) def cancel_request(self, params: CancelParams) -> None: - """Send a ``$/cancelRequest`` notification. - - - """ + """Send a ``$/cancelRequest`` notification.""" if self.stopped: raise RuntimeError("Client has been stopped.") @@ -1814,17 +1973,18 @@ def initialized(self, params: InitializedParams) -> None: self.protocol.notify("initialized", params) - def notebook_document_did_change(self, params: DidChangeNotebookDocumentParams) -> None: - """Send a ``notebookDocument/didChange`` notification. - - - """ + def notebook_document_did_change( + self, params: DidChangeNotebookDocumentParams + ) -> None: + """Send a ``notebookDocument/didChange`` notification.""" if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("notebookDocument/didChange", params) - def notebook_document_did_close(self, params: DidCloseNotebookDocumentParams) -> None: + def notebook_document_did_close( + self, params: DidCloseNotebookDocumentParams + ) -> None: """Send a ``notebookDocument/didClose`` notification. A notification sent when a notebook closes. @@ -1861,20 +2021,14 @@ def notebook_document_did_save(self, params: DidSaveNotebookDocumentParams) -> N self.protocol.notify("notebookDocument/didSave", params) def progress(self, params: ProgressParams) -> None: - """Send a ``$/progress`` notification. - - - """ + """Send a ``$/progress`` notification.""" if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("$/progress", params) def set_trace(self, params: SetTraceParams) -> None: - """Send a ``$/setTrace`` notification. - - - """ + """Send a ``$/setTrace`` notification.""" if self.stopped: raise RuntimeError("Client has been stopped.") @@ -1948,7 +2102,9 @@ def text_document_will_save(self, params: WillSaveTextDocumentParams) -> None: self.protocol.notify("textDocument/willSave", params) - def window_work_done_progress_cancel(self, params: WorkDoneProgressCancelParams) -> None: + def window_work_done_progress_cancel( + self, params: WorkDoneProgressCancelParams + ) -> None: """Send a ``window/workDoneProgress/cancel`` notification. The `window/workDoneProgress/cancel` notification is sent from the client to the @@ -1959,7 +2115,9 @@ def window_work_done_progress_cancel(self, params: WorkDoneProgressCancelParams) self.protocol.notify("window/workDoneProgress/cancel", params) - def workspace_did_change_configuration(self, params: DidChangeConfigurationParams) -> None: + def workspace_did_change_configuration( + self, params: DidChangeConfigurationParams + ) -> None: """Send a ``workspace/didChangeConfiguration`` notification. The configuration change notification is sent from the client to the server when @@ -1973,7 +2131,9 @@ def workspace_did_change_configuration(self, params: DidChangeConfigurationParam self.protocol.notify("workspace/didChangeConfiguration", params) - def workspace_did_change_watched_files(self, params: DidChangeWatchedFilesParams) -> None: + def workspace_did_change_watched_files( + self, params: DidChangeWatchedFilesParams + ) -> None: """Send a ``workspace/didChangeWatchedFiles`` notification. The watched files notification is sent from the client to the server when the @@ -1984,7 +2144,9 @@ def workspace_did_change_watched_files(self, params: DidChangeWatchedFilesParams self.protocol.notify("workspace/didChangeWatchedFiles", params) - def workspace_did_change_workspace_folders(self, params: DidChangeWorkspaceFoldersParams) -> None: + def workspace_did_change_workspace_folders( + self, params: DidChangeWorkspaceFoldersParams + ) -> None: """Send a ``workspace/didChangeWorkspaceFolders`` notification. The `workspace/didChangeWorkspaceFolders` notification is sent from the client to diff --git a/pygls/progress.py b/pygls/progress.py index 77ea6dcf..a2f0e5cd 100644 --- a/pygls/progress.py +++ b/pygls/progress.py @@ -3,10 +3,14 @@ from typing import Dict from lsprotocol.types import ( - PROGRESS, WINDOW_WORK_DONE_PROGRESS_CREATE, - ProgressParams, ProgressToken, WorkDoneProgressBegin, - WorkDoneProgressEnd, WorkDoneProgressReport, - WorkDoneProgressCreateParams + PROGRESS, + WINDOW_WORK_DONE_PROGRESS_CREATE, + ProgressParams, + ProgressToken, + WorkDoneProgressBegin, + WorkDoneProgressEnd, + WorkDoneProgressReport, + WorkDoneProgressCreateParams, ) from pygls.protocol import LanguageServerProtocol @@ -34,8 +38,7 @@ def _register_token(self, token: ProgressToken) -> None: self.tokens[token] = Future() def create(self, token: ProgressToken, callback=None) -> Future: - """Create a server initiated work done progress. - """ + """Create a server initiated work done progress.""" self._check_token_registered(token) def on_created(*args, **kwargs): @@ -50,8 +53,7 @@ def on_created(*args, **kwargs): ) async def create_async(self, token: ProgressToken) -> asyncio.Future: - """Create a server initiated work done progress. - """ + """Create a server initiated work done progress.""" self._check_token_registered(token) result = await self._lsp.send_request_async( @@ -62,25 +64,16 @@ async def create_async(self, token: ProgressToken) -> asyncio.Future: return result def begin(self, token: ProgressToken, value: WorkDoneProgressBegin) -> None: - """Notify beginning of work. - """ + """Notify beginning of work.""" # Register cancellation future for the case of client initiated progress self.tokens.setdefault(token, Future()) - return self._lsp.notify( - PROGRESS, - ProgressParams( - token=token, - value=value - ) - ) + return self._lsp.notify(PROGRESS, ProgressParams(token=token, value=value)) def report(self, token: ProgressToken, value: WorkDoneProgressReport) -> None: - """Notify progress of work. - """ + """Notify progress of work.""" self._lsp.notify(PROGRESS, ProgressParams(token=token, value=value)) def end(self, token: ProgressToken, value: WorkDoneProgressEnd) -> None: - """Notify end of work. - """ + """Notify end of work.""" self._lsp.notify(PROGRESS, ProgressParams(token=token, value=value)) diff --git a/pygls/protocol.py b/pygls/protocol.py index 5bdf2048..7e9ac306 100644 --- a/pygls/protocol.py +++ b/pygls/protocol.py @@ -36,36 +36,67 @@ from pygls.capabilities import ServerCapabilitiesBuilder from pygls.constants import ATTR_FEATURE_TYPE from pygls.exceptions import ( - JsonRpcException, JsonRpcInternalError, JsonRpcInvalidParams, - JsonRpcMethodNotFound, JsonRpcRequestCancelled, - FeatureNotificationError, FeatureRequestError + JsonRpcException, + JsonRpcInternalError, + JsonRpcInvalidParams, + JsonRpcMethodNotFound, + JsonRpcRequestCancelled, + FeatureNotificationError, + FeatureRequestError, ) from pygls.feature_manager import FeatureManager, assign_help_attrs, is_thread_function from pygls.lsp import ConfigCallbackType, ShowDocumentCallbackType from lsprotocol.types import ( - CANCEL_REQUEST, CLIENT_REGISTER_CAPABILITY, - CLIENT_UNREGISTER_CAPABILITY, EXIT, INITIALIZE, INITIALIZED, - METHOD_TO_TYPES, LOG_TRACE, SET_TRACE, SHUTDOWN, - TEXT_DOCUMENT_DID_CHANGE, TEXT_DOCUMENT_DID_CLOSE, - TEXT_DOCUMENT_DID_OPEN, TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS, - WINDOW_LOG_MESSAGE, WINDOW_SHOW_DOCUMENT, WINDOW_SHOW_MESSAGE, + CANCEL_REQUEST, + CLIENT_REGISTER_CAPABILITY, + CLIENT_UNREGISTER_CAPABILITY, + EXIT, + INITIALIZE, + INITIALIZED, + METHOD_TO_TYPES, + LOG_TRACE, + SET_TRACE, + SHUTDOWN, + TEXT_DOCUMENT_DID_CHANGE, + TEXT_DOCUMENT_DID_CLOSE, + TEXT_DOCUMENT_DID_OPEN, + TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS, + WINDOW_LOG_MESSAGE, + WINDOW_SHOW_DOCUMENT, + WINDOW_SHOW_MESSAGE, WINDOW_WORK_DONE_PROGRESS_CANCEL, - WORKSPACE_APPLY_EDIT, WORKSPACE_CONFIGURATION, - WORKSPACE_DID_CHANGE_WORKSPACE_FOLDERS, WORKSPACE_EXECUTE_COMMAND, - WORKSPACE_SEMANTIC_TOKENS_REFRESH + WORKSPACE_APPLY_EDIT, + WORKSPACE_CONFIGURATION, + WORKSPACE_DID_CHANGE_WORKSPACE_FOLDERS, + WORKSPACE_EXECUTE_COMMAND, + WORKSPACE_SEMANTIC_TOKENS_REFRESH, ) from lsprotocol.types import ( ApplyWorkspaceEditParams, Diagnostic, - DidChangeTextDocumentParams, DidChangeWorkspaceFoldersParams, - DidCloseTextDocumentParams, DidOpenTextDocumentParams, - ExecuteCommandParams, InitializeParams, InitializeResult, - LogMessageParams, LogTraceParams, MessageType, PublishDiagnosticsParams, - RegistrationParams, ResponseErrorMessage, SetTraceParams, - ShowDocumentParams, ShowMessageParams, - TraceValues, UnregistrationParams, WorkspaceApplyEditResponse, - WorkspaceEdit, InitializeResultServerInfoType, - WorkspaceConfigurationParams, WorkDoneProgressCancelParams + DidChangeTextDocumentParams, + DidChangeWorkspaceFoldersParams, + DidCloseTextDocumentParams, + DidOpenTextDocumentParams, + ExecuteCommandParams, + InitializeParams, + InitializeResult, + LogMessageParams, + LogTraceParams, + MessageType, + PublishDiagnosticsParams, + RegistrationParams, + ResponseErrorMessage, + SetTraceParams, + ShowDocumentParams, + ShowMessageParams, + TraceValues, + UnregistrationParams, + WorkspaceApplyEditResponse, + WorkspaceEdit, + InitializeResultServerInfoType, + WorkspaceConfigurationParams, + WorkDoneProgressCancelParams, ) from pygls.uris import from_fs_path from pygls.workspace import Workspace @@ -79,6 +110,7 @@ def call_user_feature(base_func, method_name): """Wraps generic LSP features and calls user registered feature immediately after it. """ + @functools.wraps(base_func) def decorator(self, *args, **kwargs): ret_val = base_func(self, *args, **kwargs) @@ -89,8 +121,9 @@ def decorator(self, *args, **kwargs): except KeyError: pass except Exception: - logger.exception('Failed to handle user defined notification "%s": %s', - method_name, args) + logger.exception( + 'Failed to handle user defined notification "%s": %s', method_name, args + ) return ret_val @@ -140,23 +173,23 @@ def _dict_to_object(d: Any): if not isinstance(d, dict): return d - type_name = d.pop('type_name', 'Object') + type_name = d.pop("type_name", "Object") return json.loads( json.dumps(d), - object_hook=lambda p: namedtuple(type_name, p.keys(), rename=True)(*p.values()) + object_hook=lambda p: namedtuple(type_name, p.keys(), rename=True)(*p.values()), ) def _params_field_structure_hook(obj, cls): - if 'params' in obj: - obj['params'] = _dict_to_object(obj['params']) + if "params" in obj: + obj["params"] = _dict_to_object(obj["params"]) return cls(**obj) def _result_field_structure_hook(obj, cls): - if 'result' in obj: - obj['result'] = _dict_to_object(obj['result']) + if "result" in obj: + obj["result"] = _dict_to_object(obj["result"]) return cls(**obj) @@ -173,9 +206,7 @@ def default_converter(): JsonRPCResponseMessage, _result_field_structure_hook ) - converter.register_structure_hook( - JsonRPCNotification, _params_field_structure_hook - ) + converter.register_structure_hook(JsonRPCNotification, _params_field_structure_hook) return converter @@ -188,18 +219,19 @@ class JsonRPCProtocol(asyncio.Protocol): This class provides bidirectional communication which is needed for LSP. """ - CHARSET = 'utf-8' - CONTENT_TYPE = 'application/vscode-jsonrpc' + + CHARSET = "utf-8" + CONTENT_TYPE = "application/vscode-jsonrpc" MESSAGE_PATTERN = re.compile( - rb'^(?:[^\r\n]+\r\n)*' - + rb'Content-Length: (?P\d+)\r\n' - + rb'(?:[^\r\n]+\r\n)*\r\n' - + rb'(?P{.*)', + rb"^(?:[^\r\n]+\r\n)*" + + rb"Content-Length: (?P\d+)\r\n" + + rb"(?:[^\r\n]+\r\n)*\r\n" + + rb"(?P{.*)", re.DOTALL, ) - VERSION = '2.0' + VERSION = "2.0" def __init__(self, server, converter): self._server = server @@ -227,14 +259,13 @@ def _execute_notification(self, handler, *params): future.add_done_callback(self._execute_notification_callback) else: if is_thread_function(handler): - self._server.thread_pool.apply_async(handler, (*params, )) + self._server.thread_pool.apply_async(handler, (*params,)) else: handler(*params) def _execute_notification_callback(self, future): """Success callback used for coroutine notification message.""" if future.exception(): - try: raise future.exception() except Exception: @@ -256,11 +287,14 @@ def _execute_request(self, msg_id, handler, params): # Can't be canceled if is_thread_function(handler): self._server.thread_pool.apply_async( - handler, (params, ), + handler, + (params,), callback=partial( - self._send_response, msg_id, + self._send_response, + msg_id, ), - error_callback=partial(self._execute_request_err_callback, msg_id)) + error_callback=partial(self._execute_request_err_callback, msg_id), + ) else: self._send_response(msg_id, handler(params)) @@ -272,7 +306,9 @@ def _execute_request_callback(self, msg_id, future): else: self._send_response( msg_id, - error=JsonRpcRequestCancelled(f'Request with id "{msg_id}" is canceled') + error=JsonRpcRequestCancelled( + f'Request with id "{msg_id}" is canceled' + ), ) self._request_futures.pop(msg_id, None) except Exception: @@ -325,7 +361,7 @@ def _handle_notification(self, method_name, params): 'Failed to handle notification "%s": %s', method_name, params, - exc_info=True + exc_info=True, ) self._server._report_server_error(error, FeatureNotificationError) @@ -342,21 +378,21 @@ def _handle_request(self, msg_id, method_name, params): except JsonRpcException as error: logger.exception( - 'Failed to handle request %s %s %s', + "Failed to handle request %s %s %s", msg_id, method_name, params, - exc_info=True + exc_info=True, ) self._send_response(msg_id, None, error.to_dict()) self._server._report_server_error(error, FeatureRequestError) except Exception as error: logger.exception( - 'Failed to handle request %s %s %s', + "Failed to handle request %s %s %s", msg_id, method_name, params, - exc_info=True + exc_info=True, ) err = JsonRpcInternalError.of(sys.exc_info()).to_dict() self._send_response(msg_id, None, err) @@ -380,7 +416,7 @@ def _handle_response(self, msg_id, result=None, error=None): def _serialize_message(self, data): """Function used to serialize data sent to the client.""" - if hasattr(data, '__attrs_attrs__'): + if hasattr(data, "__attrs_attrs__"): return self._converter.unstructure(data) if isinstance(data, enum.Enum): @@ -391,26 +427,26 @@ def _serialize_message(self, data): def _deserialize_message(self, data): """Function used to deserialize data recevied from the client.""" - if 'jsonrpc' not in data: + if "jsonrpc" not in data: return data try: - if 'id' in data: - if 'error' in data: + if "id" in data: + if "error" in data: return self._converter.structure(data, ResponseErrorMessage) - elif 'method' in data: + elif "method" in data: request_type = ( - self.get_message_type(data['method']) or JsonRPCRequestMessage + self.get_message_type(data["method"]) or JsonRPCRequestMessage ) return self._converter.structure(data, request_type) else: response_type = ( - self._result_types.pop(data['id']) or JsonRPCResponseMessage + self._result_types.pop(data["id"]) or JsonRPCResponseMessage ) return self._converter.structure(data, response_type) else: - method = data.get('method', '') + method = data.get("method", "") notification_type = self.get_message_type(method) or JsonRPCNotification return self._converter.structure(data, notification_type) @@ -429,23 +465,23 @@ def _procedure_handler(self, message): logger.warning('Unknown message "%s"', message) return - if self._shutdown and getattr(message, 'method', '') != EXIT: - logger.warning('Server shutting down. No more requests!') + if self._shutdown and getattr(message, "method", "") != EXIT: + logger.warning("Server shutting down. No more requests!") return - if hasattr(message, 'method'): - if hasattr(message, 'id'): - logger.debug('Request message received.') + if hasattr(message, "method"): + if hasattr(message, "id"): + logger.debug("Request message received.") self._handle_request(message.id, message.method, message.params) else: - logger.debug('Notification message received.') + logger.debug("Notification message received.") self._handle_notification(message.method, message.params) else: - if hasattr(message, 'error'): - logger.debug('Error message received.') + if hasattr(message, "error"): + logger.debug("Error message received.") self._handle_response(message.id, None, message.error) else: - logger.debug('Response message received.') + logger.debug("Response message received.") self._handle_response(message.id, message.result) def _send_data(self, data): @@ -459,18 +495,18 @@ def _send_data(self, data): try: body = json.dumps(data, default=self._serialize_message) - logger.info('Sending data: %s', body) + logger.info("Sending data: %s", body) body = body.encode(self.CHARSET) if not self._send_only_body: header = ( - f'Content-Length: {len(body)}\r\n' - f'Content-Type: {self.CONTENT_TYPE}; charset={self.CHARSET}\r\n\r\n' + f"Content-Length: {len(body)}\r\n" + f"Content-Type: {self.CONTENT_TYPE}; charset={self.CHARSET}\r\n\r\n" ).encode(self.CHARSET) self.transport.write(header + body) else: - self.transport.write(body.decode('utf-8')) + self.transport.write(body.decode("utf-8")) except Exception as error: logger.exception("Error sending data", exc_info=True) self._server._report_server_error(error, JsonRpcInternalError) @@ -499,7 +535,7 @@ def connection_lost(self, exc): """Method from base class, called when connection is lost, in which case we want to shutdown the server's process as well. """ - logger.error('Connection to the client is lost! Shutting down the server.') + logger.error("Connection to the client is lost! Shutting down the server.") sys.exit(1) def connection_made(self, transport: asyncio.BaseTransport): @@ -515,18 +551,18 @@ def data_received(self, data: bytes): def _data_received(self, data: bytes): """Method from base class, called when server receives the data""" - logger.debug('Received %r', data) + logger.debug("Received %r", data) while len(data): # Append the incoming chunk to the message buffer self._message_buf.append(data) # Look for the body of the message - message = b''.join(self._message_buf) + message = b"".join(self._message_buf) found = JsonRPCProtocol.MESSAGE_PATTERN.fullmatch(message) - body = found.group('body') if found else b'' - length = int(found.group('length')) if found else 1 + body = found.group("body") if found else b"" + length = int(found.group("length")) if found else 1 if len(body) < length: # Message is incomplete; bail until more data arrives @@ -540,8 +576,10 @@ def _data_received(self, data: bytes): # Parse the body self._procedure_handler( - json.loads(body.decode(self.CHARSET), - object_hook=self._deserialize_message)) + json.loads( + body.decode(self.CHARSET), object_hook=self._deserialize_message + ) + ) def get_message_type(self, method: str) -> Optional[Type]: """Return the type definition of the message associated with the given method.""" @@ -558,9 +596,7 @@ def notify(self, method: str, params=None): notification_type = self.get_message_type(method) or JsonRPCNotification notification = notification_type( - method=method, - params=params, - jsonrpc=JsonRPCProtocol.VERSION + method=method, params=params, jsonrpc=JsonRPCProtocol.VERSION ) self._send_data(notification) @@ -592,10 +628,12 @@ def send_request(self, method, params=None, callback=None, msg_id=None): future = Future() # If callback function is given, call it when result is received if callback: + def wrapper(future: Future): result = future.result() - logger.info('Client response for %s received: %s', params, result) + logger.info("Client response for %s received: %s", params, result) callback(result) + future.add_done_callback(wrapper) self._request_futures[msg_id] = future @@ -630,6 +668,7 @@ def lsp_method(method_name: str) -> Callable[[F], F]: def decorator(f: F) -> F: f.method_name = method_name # type: ignore[attr-defined] return f + return decorator @@ -639,9 +678,10 @@ class LSPMeta(type): Built-in features cannot be overridden but user defined features with the same LSP name will be called after them. """ + def __new__(mcs, cls_name, cls_bases, cls): for attr_name, attr_val in cls.items(): - if callable(attr_val) and hasattr(attr_val, 'method_name'): + if callable(attr_val) and hasattr(attr_val, "method_name"): method_name = attr_val.method_name wrapped = call_user_feature(attr_val, method_name) assign_help_attrs(wrapped, method_name, ATTR_FEATURE_TYPE) @@ -668,6 +708,7 @@ def __init__(self, server, converter): self.trace = None from pygls.progress import Progress + self.progress = Progress(self) self.server_info = InitializeResultServerInfoType( @@ -681,7 +722,7 @@ def _register_builtin_features(self): """Registers generic LSP features from this class.""" for name in dir(self): attr = getattr(self, name) - if callable(attr) and hasattr(attr, 'method_name'): + if callable(attr) and hasattr(attr, "method_name"): self.fm.add_builtin_feature(attr.method_name, attr) @lru_cache() @@ -697,8 +738,9 @@ def apply_edit( self, edit: WorkspaceEdit, label: Optional[str] = None ) -> WorkspaceApplyEditResponse: """Sends apply edit request to the client.""" - return self.send_request(WORKSPACE_APPLY_EDIT, - ApplyWorkspaceEditParams(edit=edit, label=label)) + return self.send_request( + WORKSPACE_APPLY_EDIT, ApplyWorkspaceEditParams(edit=edit, label=label) + ) @lsp_method(EXIT) def lsp_exit(self, *args) -> None: @@ -714,7 +756,7 @@ def lsp_initialize(self, params: InitializeParams) -> InitializeResult: It will compute and return server capabilities based on registered features. """ - logger.info('Language server initialized %s', params) + logger.info("Language server initialized %s", params) self._server.process_id = params.process_id @@ -728,8 +770,8 @@ def lsp_initialize(self, params: InitializeParams) -> InitializeResult: self._server.sync_kind, ).build() logger.debug( - 'Server capabilities: %s', - json.dumps(self.server_capabilities, default=self._serialize_message) + "Server capabilities: %s", + json.dumps(self.server_capabilities, default=self._serialize_message), ) root_path = params.root_path @@ -761,7 +803,9 @@ def lsp_shutdown(self, *args) -> None: return None @lsp_method(TEXT_DOCUMENT_DID_CHANGE) - def lsp_text_document__did_change(self, params: DidChangeTextDocumentParams) -> None: + def lsp_text_document__did_change( + self, params: DidChangeTextDocumentParams + ) -> None: """Updates document's content. (Incremental(from server capabilities); not configurable for now) """ @@ -785,9 +829,10 @@ def lsp_set_trace(self, params: SetTraceParams) -> None: @lsp_method(WORKSPACE_DID_CHANGE_WORKSPACE_FOLDERS) def lsp_workspace__did_change_workspace_folders( - self, params: DidChangeWorkspaceFoldersParams) -> None: + self, params: DidChangeWorkspaceFoldersParams + ) -> None: """Adds/Removes folders from the workspace.""" - logger.info('Workspace folders changed: %s', params) + logger.info("Workspace folders changed: %s", params) added_folders = params.event.added or [] removed_folders = params.event.removed or [] @@ -799,22 +844,31 @@ def lsp_workspace__did_change_workspace_folders( self.workspace.remove_folder(f_remove.uri) @lsp_method(WORKSPACE_EXECUTE_COMMAND) - def lsp_workspace__execute_command(self, params: ExecuteCommandParams, msg_id: str) -> None: + def lsp_workspace__execute_command( + self, params: ExecuteCommandParams, msg_id: str + ) -> None: """Executes commands with passed arguments and returns a value.""" cmd_handler = self.fm.commands[params.command] self._execute_request(msg_id, cmd_handler, params.arguments) @lsp_method(WINDOW_WORK_DONE_PROGRESS_CANCEL) - def lsp_work_done_progress_cancel(self, params: WorkDoneProgressCancelParams) -> None: + def lsp_work_done_progress_cancel( + self, params: WorkDoneProgressCancelParams + ) -> None: """Received a progress cancellation from client.""" future = self.progress.tokens.get(params.token) if future is None: - logger.warning('Ignoring work done progress cancel for unknown token %s', params.token) + logger.warning( + "Ignoring work done progress cancel for unknown token %s", params.token + ) else: future.cancel() - def get_configuration(self, params: WorkspaceConfigurationParams, - callback: Optional[ConfigCallbackType] = None) -> Future: + def get_configuration( + self, + params: WorkspaceConfigurationParams, + callback: Optional[ConfigCallbackType] = None, + ) -> Future: """Sends configuration request to the client. Args: @@ -827,7 +881,9 @@ def get_configuration(self, params: WorkspaceConfigurationParams, """ return self.send_request(WORKSPACE_CONFIGURATION, params, callback) - def get_configuration_async(self, params: WorkspaceConfigurationParams) -> asyncio.Future: + def get_configuration_async( + self, params: WorkspaceConfigurationParams + ) -> asyncio.Future: """Calls `get_configuration` method but designed to use with coroutines Args: @@ -853,7 +909,7 @@ def _publish_diagnostics_deprecator( params_or_uri: Union[str, PublishDiagnosticsParams], diagnostics: Optional[List[Diagnostic]], version: Optional[int], - **kwargs + **kwargs, ) -> PublishDiagnosticsParams: if isinstance(params_or_uri, str): message = "DEPRECATION: " @@ -863,10 +919,7 @@ def _publish_diagnostics_deprecator( logging.warning(message) params = self._construct_publish_diagnostic_type( - params_or_uri, - diagnostics, - version, - **kwargs + params_or_uri, diagnostics, version, **kwargs ) else: params = params_or_uri @@ -877,18 +930,14 @@ def _construct_publish_diagnostic_type( uri: str, diagnostics: Optional[List[Diagnostic]], version: Optional[int], - **kwargs + **kwargs, ) -> PublishDiagnosticsParams: if diagnostics is None: diagnostics = [] args = { - **{ - "uri": uri, - "diagnostics": diagnostics, - "version": version - }, - **kwargs + **{"uri": uri, "diagnostics": diagnostics, "version": version}, + **kwargs, } params = PublishDiagnosticsParams(**args) # type:ignore @@ -899,7 +948,7 @@ def publish_diagnostics( params_or_uri: Union[str, PublishDiagnosticsParams], diagnostics: Optional[List[Diagnostic]] = None, version: Optional[int] = None, - **kwargs + **kwargs, ): """ Sends diagnostic notification to the client. @@ -907,15 +956,13 @@ def publish_diagnostics( `uri`, `diagnostics` and `version` fields will be deprecated """ params = self._publish_diagnostics_deprecator( - params_or_uri, - diagnostics, - version, - **kwargs + params_or_uri, diagnostics, version, **kwargs ) self.notify(TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS, params) - def register_capability(self, params: RegistrationParams, - callback: Optional[Callable[[], None]] = None) -> Future: + def register_capability( + self, params: RegistrationParams, callback: Optional[Callable[[], None]] = None + ) -> Future: """Register a new capability on the client. Args: @@ -940,7 +987,9 @@ def register_capability_async(self, params: RegistrationParams) -> asyncio.Futur """ return asyncio.wrap_future(self.register_capability(params, None)) - def semantic_tokens_refresh(self, callback: Optional[Callable[[], None]] = None) -> Future: + def semantic_tokens_refresh( + self, callback: Optional[Callable[[], None]] = None + ) -> Future: """Requesting a refresh of all semantic tokens. Args: @@ -962,8 +1011,11 @@ def semantic_tokens_refresh_async(self) -> asyncio.Future: """ return asyncio.wrap_future(self.semantic_tokens_refresh(None)) - def show_document(self, params: ShowDocumentParams, - callback: Optional[ShowDocumentCallbackType] = None) -> Future: + def show_document( + self, + params: ShowDocumentParams, + callback: Optional[ShowDocumentCallbackType] = None, + ) -> Future: """Display a particular document in the user interface. Args: @@ -991,14 +1043,21 @@ def show_document_async(self, params: ShowDocumentParams) -> asyncio.Future: def show_message(self, message, msg_type=MessageType.Info): """Sends message to the client to display message.""" - self.notify(WINDOW_SHOW_MESSAGE, ShowMessageParams(type=msg_type, message=message)) + self.notify( + WINDOW_SHOW_MESSAGE, ShowMessageParams(type=msg_type, message=message) + ) def show_message_log(self, message, msg_type=MessageType.Log): """Sends message to the client's output channel.""" - self.notify(WINDOW_LOG_MESSAGE, LogMessageParams(type=msg_type, message=message)) + self.notify( + WINDOW_LOG_MESSAGE, LogMessageParams(type=msg_type, message=message) + ) - def unregister_capability(self, params: UnregistrationParams, - callback: Optional[Callable[[], None]] = None) -> Future: + def unregister_capability( + self, + params: UnregistrationParams, + callback: Optional[Callable[[], None]] = None, + ) -> Future: """Unregister a new capability on the client. Args: @@ -1011,7 +1070,9 @@ def unregister_capability(self, params: UnregistrationParams, """ return self.send_request(CLIENT_UNREGISTER_CAPABILITY, params, callback) - def unregister_capability_async(self, params: UnregistrationParams) -> asyncio.Future: + def unregister_capability_async( + self, params: UnregistrationParams + ) -> asyncio.Future: """Unregister a new capability on the client. Args: diff --git a/pygls/server.py b/pygls/server.py index c7402b33..605a021e 100644 --- a/pygls/server.py +++ b/pygls/server.py @@ -28,11 +28,16 @@ from pygls.exceptions import PyglsError, JsonRpcException, FeatureRequestError from lsprotocol.types import ( ClientCapabilities, - Diagnostic, MessageType, RegistrationParams, - ServerCapabilities, ShowDocumentParams, - TextDocumentSyncKind, UnregistrationParams, - WorkspaceApplyEditResponse, WorkspaceEdit, - WorkspaceConfigurationParams + Diagnostic, + MessageType, + RegistrationParams, + ServerCapabilities, + ShowDocumentParams, + TextDocumentSyncKind, + UnregistrationParams, + WorkspaceApplyEditResponse, + WorkspaceEdit, + WorkspaceConfigurationParams, ) from pygls.progress import Progress from pygls.protocol import LanguageServerProtocol, default_converter @@ -44,13 +49,13 @@ logger = logging.getLogger(__name__) -F = TypeVar('F', bound=Callable) +F = TypeVar("F", bound=Callable) async def aio_readline(loop, executor, stop_event, rfile, proxy): """Reads data from stdin in separate thread (asynchronously).""" - CONTENT_LENGTH_PATTERN = re.compile(rb'^Content-Length: (\d+)\r\n$') + CONTENT_LENGTH_PATTERN = re.compile(rb"^Content-Length: (\d+)\r\n$") # Initialize message buffer message = [] @@ -68,11 +73,10 @@ async def aio_readline(loop, executor, stop_event, rfile, proxy): match = CONTENT_LENGTH_PATTERN.fullmatch(header) if match: content_length = int(match.group(1)) - logger.debug('Content length: %s', content_length) + logger.debug("Content length: %s", content_length) # Check if all headers have been read (as indicated by an empty line \r\n) if content_length and not header.strip(): - # Read body body = await loop.run_in_executor(executor, rfile.read, content_length) if not body: @@ -80,7 +84,7 @@ async def aio_readline(loop, executor, stop_event, rfile, proxy): message.append(body) # Pass message to language server protocol - proxy(b''.join(message)) + proxy(b"".join(message)) # Reset the buffer message = [] @@ -172,10 +176,16 @@ class Server: - lazy instantiated """ - def __init__(self, protocol_cls, converter_factory, loop=None, max_workers=2, - sync_kind=TextDocumentSyncKind.Incremental): + def __init__( + self, + protocol_cls, + converter_factory, + loop=None, + max_workers=2, + sync_kind=TextDocumentSyncKind.Incremental, + ): if not issubclass(protocol_cls, asyncio.Protocol): - raise TypeError('Protocol class should be subclass of asyncio.Protocol') + raise TypeError("Protocol class should be subclass of asyncio.Protocol") self._max_workers = max_workers self._server = None @@ -196,7 +206,7 @@ def __init__(self, protocol_cls, converter_factory, loop=None, max_workers=2, def shutdown(self): """Shutdown server.""" - logger.info('Shutting down the server') + logger.info("Shutting down the server") self._stop_event.set() @@ -212,35 +222,38 @@ def shutdown(self): self.loop.run_until_complete(self._server.wait_closed()) if self._owns_loop and not self.loop.is_closed: - logger.info('Closing the event loop.') + logger.info("Closing the event loop.") self.loop.close() def start_io(self, stdin: Optional[TextIO] = None, stdout: Optional[TextIO] = None): """Starts IO server.""" - logger.info('Starting IO server') + logger.info("Starting IO server") self._stop_event = Event() - transport = StdOutTransportAdapter(stdin or sys.stdin.buffer, - stdout or sys.stdout.buffer) + transport = StdOutTransportAdapter( + stdin or sys.stdin.buffer, stdout or sys.stdout.buffer + ) self.lsp.connection_made(transport) try: self.loop.run_until_complete( - aio_readline(self.loop, - self.thread_pool_executor, - self._stop_event, - stdin or sys.stdin.buffer, - self.lsp.data_received)) + aio_readline( + self.loop, + self.thread_pool_executor, + self._stop_event, + stdin or sys.stdin.buffer, + self.lsp.data_received, + ) + ) except BrokenPipeError: - logger.error('Connection to the client is lost! Shutting down the server.') + logger.error("Connection to the client is lost! Shutting down the server.") except (KeyboardInterrupt, SystemExit): pass finally: self.shutdown() def start_pyodide(self): - - logger.info('Starting Pyodide server') + logger.info("Starting Pyodide server") # Note: We don't actually start anything running as the main event # loop will be handled by the web platform. @@ -250,7 +263,7 @@ def start_pyodide(self): def start_tcp(self, host: str, port: int) -> None: """Starts TCP server.""" - logger.info('Starting TCP server on %s:%s', host, port) + logger.info("Starting TCP server on %s:%s", host, port) self._stop_event = Event() self._server = self.loop.run_until_complete( @@ -268,10 +281,10 @@ def start_ws(self, host: str, port: int) -> None: try: from websockets.server import serve except ImportError: - logger.error('Run `pip install pygls[ws]` to install `websockets`.') + logger.error("Run `pip install pygls[ws]` to install `websockets`.") sys.exit(1) - logger.info('Starting WebSocket server on {}:{}'.format(host, port)) + logger.info("Starting WebSocket server on {}:{}".format(host, port)) self._stop_event = Event() self.lsp._send_only_body = True # Don't send headers within the payload @@ -310,8 +323,9 @@ def thread_pool(self) -> ThreadPool: def thread_pool_executor(self) -> ThreadPoolExecutor: """Returns thread pool instance (lazy initialization).""" if not self._thread_pool_executor: - self._thread_pool_executor = \ - ThreadPoolExecutor(max_workers=self._max_workers) + self._thread_pool_executor = ThreadPoolExecutor( + max_workers=self._max_workers + ) return self._thread_pool_executor @@ -329,9 +343,12 @@ class LanguageServer(Server): max_workers(int, optional): Number of workers for `ThreadPool` and `ThreadPoolExecutor` """ + lsp: LanguageServerProtocol - default_error_message = "Unexpected error in LSP server, see server's logs for details" + default_error_message = ( + "Unexpected error in LSP server, see server's logs for details" + ) """ The default error message sent to the user's editor when this server encounters an uncaught exception. @@ -344,11 +361,12 @@ def __init__( loop=None, protocol_cls=LanguageServerProtocol, converter_factory=default_converter, - max_workers: int = 2 + max_workers: int = 2, ): - if not issubclass(protocol_cls, LanguageServerProtocol): - raise TypeError('Protocol class should be subclass of LanguageServerProtocol') + raise TypeError( + "Protocol class should be subclass of LanguageServerProtocol" + ) self.name = name self.version = version @@ -376,7 +394,9 @@ def client_capabilities(self) -> ClientCapabilities: return self.lsp.client_capabilities def feature( - self, feature_name: str, options: Optional[Any] = None, + self, + feature_name: str, + options: Optional[Any] = None, ) -> Callable[[F], F]: """Decorator used to register LSP features. @@ -387,12 +407,17 @@ def completions(ls, params: CompletionParams): """ return self.lsp.fm.feature(feature_name, options) - def get_configuration(self, params: WorkspaceConfigurationParams, - callback: Optional[ConfigCallbackType] = None) -> Future: + def get_configuration( + self, + params: WorkspaceConfigurationParams, + callback: Optional[ConfigCallbackType] = None, + ) -> Future: """Gets the configuration settings from the client.""" return self.lsp.get_configuration(params, callback) - def get_configuration_async(self, params: WorkspaceConfigurationParams) -> asyncio.Future: + def get_configuration_async( + self, params: WorkspaceConfigurationParams + ) -> asyncio.Future: """Gets the configuration settings from the client. Should be called with `await`""" return self.lsp.get_configuration_async(params) @@ -416,15 +441,13 @@ def publish_diagnostics( Sends diagnostic notification to the client. """ params = self.lsp._construct_publish_diagnostic_type( - uri, - diagnostics, - version, - **kwargs + uri, diagnostics, version, **kwargs ) self.lsp.publish_diagnostics(params, **kwargs) - def register_capability(self, params: RegistrationParams, - callback: Optional[Callable[[], None]] = None) -> Future: + def register_capability( + self, params: RegistrationParams, callback: Optional[Callable[[], None]] = None + ) -> Future: """Register a new capability on the client.""" return self.lsp.register_capability(params, callback) @@ -432,7 +455,9 @@ def register_capability_async(self, params: RegistrationParams) -> asyncio.Futur """Register a new capability on the client. Should be called with `await`""" return self.lsp.register_capability_async(params) - def semantic_tokens_refresh(self, callback: Optional[Callable[[], None]] = None) -> Future: + def semantic_tokens_refresh( + self, callback: Optional[Callable[[], None]] = None + ) -> Future: """Request a refresh of all semantic tokens.""" return self.lsp.semantic_tokens_refresh(callback) @@ -449,8 +474,11 @@ def server_capabilities(self) -> ServerCapabilities: """Return server capabilities.""" return self.lsp.server_capabilities - def show_document(self, params: ShowDocumentParams, - callback: Optional[ShowDocumentCallbackType] = None) -> Future: + def show_document( + self, + params: ShowDocumentParams, + callback: Optional[ShowDocumentCallbackType] = None, + ) -> Future: """Display a particular document in the user interface.""" return self.lsp.show_document(params, callback) @@ -466,14 +494,18 @@ def show_message_log(self, message, msg_type=MessageType.Log) -> None: """Sends message to the client's output channel.""" self.lsp.show_message_log(message, msg_type) - def _report_server_error(self, error: Exception, source: Union[PyglsError, JsonRpcException]): + def _report_server_error( + self, error: Exception, source: Union[PyglsError, JsonRpcException] + ): # Prevent recursive error reporting try: self.report_server_error(error, source) except Exception: logger.warning("Failed to report error to client") - def report_server_error(self, error: Exception, source: Union[PyglsError, JsonRpcException]): + def report_server_error( + self, error: Exception, source: Union[PyglsError, JsonRpcException] + ): """ Sends error to the client for displaying. @@ -501,12 +533,17 @@ def thread(self) -> Callable[[F], F]: """Decorator that mark function to execute it in a thread.""" return self.lsp.thread() - def unregister_capability(self, params: UnregistrationParams, - callback: Optional[Callable[[], None]] = None) -> Future: + def unregister_capability( + self, + params: UnregistrationParams, + callback: Optional[Callable[[], None]] = None, + ) -> Future: """Unregister a new capability on the client.""" return self.lsp.unregister_capability(params, callback) - def unregister_capability_async(self, params: UnregistrationParams) -> asyncio.Future: + def unregister_capability_async( + self, params: UnregistrationParams + ) -> asyncio.Future: """Unregister a new capability on the client. Should be called with `await`""" return self.lsp.unregister_capability_async(params) diff --git a/pygls/uris.py b/pygls/uris.py index 91126872..2b9997db 100644 --- a/pygls/uris.py +++ b/pygls/uris.py @@ -25,22 +25,22 @@ from pygls import IS_WIN -RE_DRIVE_LETTER_PATH = re.compile(r'^\/[a-zA-Z]:') +RE_DRIVE_LETTER_PATH = re.compile(r"^\/[a-zA-Z]:") def _normalize_win_path(path): - netloc = '' + netloc = "" # normalize to fwd-slashes on windows, # on other systems bwd-slashes are valid # filename character, eg /f\oo/ba\r.txt if IS_WIN: - path = path.replace('\\', '/') + path = path.replace("\\", "/") # check for authority as used in UNC shares # or use the path as given - if path[:2] == '//': - idx = path.index('/', 2) + if path[:2] == "//": + idx = path.index("/", 2) if idx == -1: netloc = path[2:] else: @@ -49,8 +49,8 @@ def _normalize_win_path(path): # Ensure that path starts with a slash # or that it is at least a slash - if not path.startswith('/'): - path = '/' + path + if not path.startswith("/"): + path = "/" + path # Normalize drive paths to lower case if RE_DRIVE_LETTER_PATH.match(path): @@ -62,8 +62,8 @@ def _normalize_win_path(path): def from_fs_path(path): """Returns a URI for the given filesystem path.""" try: - scheme = 'file' - params, query, fragment = '', '', '' + scheme = "file" + params, query, fragment = "", "", "" path, netloc = _normalize_win_path(path) return urlunparse((scheme, netloc, path, params, query, fragment)) except (AttributeError, TypeError): @@ -82,9 +82,9 @@ def to_fs_path(uri): # scheme://netloc/path;parameters?query#fragment scheme, netloc, path, _params, _query, _fragment = urlparse(uri) - if netloc and path and scheme == 'file': + if netloc and path and scheme == "file": # unc path: file://shares/c$/far/boo - value = f'//{netloc}{path}' + value = f"//{netloc}{path}" elif RE_DRIVE_LETTER_PATH.match(path): # windows drive letter: file:///C:/far/boo @@ -95,7 +95,7 @@ def to_fs_path(uri): value = path if IS_WIN: - value = value.replace('/', '\\') + value = value.replace("/", "\\") return value except TypeError: @@ -109,23 +109,28 @@ def uri_scheme(uri): return None -def uri_with(uri, scheme=None, netloc=None, path=None, params=None, query=None, fragment=None): +def uri_with( + uri, scheme=None, netloc=None, path=None, params=None, query=None, fragment=None +): """Return a URI with the given part(s) replaced. Parts are decoded / encoded. """ - old_scheme, old_netloc, old_path, old_params, old_query, old_fragment = \ - urlparse(uri) + old_scheme, old_netloc, old_path, old_params, old_query, old_fragment = urlparse( + uri + ) path, _netloc = _normalize_win_path(path) - return urlunparse(( - scheme or old_scheme, - netloc or old_netloc, - path or old_path, - params or old_params, - query or old_query, - fragment or old_fragment - )) + return urlunparse( + ( + scheme or old_scheme, + netloc or old_netloc, + path or old_path, + params or old_params, + query or old_query, + fragment or old_fragment, + ) + ) def urlparse(uri): @@ -137,7 +142,7 @@ def urlparse(uri): parse.unquote(path), parse.unquote(params), parse.unquote(query), - parse.unquote(fragment) + parse.unquote(fragment), ) @@ -151,11 +156,13 @@ def urlunparse(parts): else: quoted_path = parse.quote(path) - return parse.urlunparse(( - parse.quote(scheme), - parse.quote(netloc), - quoted_path, - parse.quote(params), - parse.quote(query), - parse.quote(fragment) - )) + return parse.urlunparse( + ( + parse.quote(scheme), + parse.quote(netloc), + quoted_path, + parse.quote(params), + parse.quote(query), + parse.quote(fragment), + ) + ) diff --git a/pygls/workspace.py b/pygls/workspace.py index dad73438..4fcdc4f4 100644 --- a/pygls/workspace.py +++ b/pygls/workspace.py @@ -23,16 +23,20 @@ from typing import List, Optional, Pattern from lsprotocol.types import ( - Position, Range, TextDocumentContentChangeEvent, + Position, + Range, + TextDocumentContentChangeEvent, TextDocumentContentChangeEvent_Type1, - TextDocumentItem, TextDocumentSyncKind, - VersionedTextDocumentIdentifier, WorkspaceFolder + TextDocumentItem, + TextDocumentSyncKind, + VersionedTextDocumentIdentifier, + WorkspaceFolder, ) from pygls.uris import to_fs_path, uri_scheme # TODO: this is not the best e.g. we capture numbers -RE_END_WORD = re.compile('^[A-Za-z_0-9]*') -RE_START_WORD = re.compile('[A-Za-z_0-9]*$') +RE_END_WORD = re.compile("^[A-Za-z_0-9]*") +RE_START_WORD = re.compile("[A-Za-z_0-9]*$") log = logging.getLogger(__name__) @@ -90,7 +94,7 @@ def position_from_utf16(lines: List[str], position: Position) -> Position: return Position(len(lines) - 1, utf16_num_units(lines[-1])) _line = lines[position.line] - _line = _line.replace('\r\n', '\n') # TODO: it's a bit of a hack + _line = _line.replace("\r\n", "\n") # TODO: it's a bit of a hack _utf16_len = utf16_num_units(_line) _utf32_len = len(_line) @@ -153,7 +157,7 @@ def position_to_utf16(lines: List[str], position: Position) -> Position: return Position( line=position.line, character=position.character - + utf16_unit_offset(lines[position.line][:position.character]) + + utf16_unit_offset(lines[position.line][: position.character]), ) except IndexError: return Position(line=len(lines), character=0) @@ -173,7 +177,7 @@ def range_from_utf16(lines: List[str], range: Range) -> Range: """ range_new = Range( start=position_from_utf16(lines, range.start), - end=position_from_utf16(lines, range.end) + end=position_from_utf16(lines, range.end), ) return range_new @@ -192,12 +196,11 @@ def range_to_utf16(lines: List[str], range: Range) -> Range: """ return Range( start=position_to_utf16(lines, range.start), - end=position_to_utf16(lines, range.end) + end=position_to_utf16(lines, range.end), ) class Document(object): - def __init__( self, uri: str, @@ -205,7 +208,7 @@ def __init__( version: Optional[int] = None, language_id: Optional[str] = None, local: bool = True, - sync_kind: TextDocumentSyncKind = TextDocumentSyncKind.Incremental + sync_kind: TextDocumentSyncKind = TextDocumentSyncKind.Incremental, ): self.uri = uri self.version = version @@ -223,7 +226,9 @@ def __init__( def __str__(self): return str(self.uri) - def _apply_incremental_change(self, change: TextDocumentContentChangeEvent_Type1) -> None: + def _apply_incremental_change( + self, change: TextDocumentContentChangeEvent_Type1 + ) -> None: """Apply an ``Incremental`` text change to the document""" lines = self.lines text = change.text @@ -320,38 +325,38 @@ def offset_at_position(self, position: Position) -> int: @property def source(self) -> str: if self._source is None: - with io.open(self.path, 'r', encoding='utf-8') as f: + with io.open(self.path, "r", encoding="utf-8") as f: return f.read() return self._source def word_at_position( - self, - position: Position, - re_start_word: Pattern = RE_START_WORD, - re_end_word: Pattern = RE_END_WORD + self, + position: Position, + re_start_word: Pattern = RE_START_WORD, + re_end_word: Pattern = RE_END_WORD, ) -> str: """Return the word at position. - Arguments: - position (Position): - The line and character offset. - re_start_word (Pattern): - The regular expression for extracting the word backward from - position. Specifically, the first match from a re.findall - call on the line up to the character value of position. The - default pattern is '[A-Za-z_0-9]*$'. - re_end_word (Pattern): - The regular expression for extracting the word forward from - position. Specifically, the last match from a re.findall - call on the line from the character value of position. The - default pattern is '^[A-Za-z_0-9]*'. - - Returns: - The word (obtained by concatenating the two matches) at position. + Arguments: + position (Position): + The line and character offset. + re_start_word (Pattern): + The regular expression for extracting the word backward from + position. Specifically, the first match from a re.findall + call on the line up to the character value of position. The + default pattern is '[A-Za-z_0-9]*$'. + re_end_word (Pattern): + The regular expression for extracting the word forward from + position. Specifically, the last match from a re.findall + call on the line from the character value of position. The + default pattern is '^[A-Za-z_0-9]*'. + + Returns: + The word (obtained by concatenating the two matches) at position. """ lines = self.lines if position.line >= len(lines): - return '' + return "" pos = position_from_utf16(lines, position) row, col = pos.line, pos.character @@ -369,7 +374,6 @@ def word_at_position( class Workspace(object): - def __init__(self, root_uri, sync_kind=None, workspace_folders=None): self._root_uri = root_uri self._root_uri_scheme = uri_scheme(self._root_uri) @@ -394,7 +398,7 @@ def _create_document( source=source, version=version, language_id=language_id, - sync_kind=self._sync_kind + sync_kind=self._sync_kind, ) def add_folder(self, folder: WorkspaceFolder): @@ -419,8 +423,7 @@ def get_document(self, doc_uri: str) -> Document: def is_local(self): return ( - self._root_uri_scheme == '' - or self._root_uri_scheme == 'file' + self._root_uri_scheme == "" or self._root_uri_scheme == "file" ) and os.path.exists(self._root_path) def put_document(self, text_document: TextDocumentItem): @@ -451,9 +454,11 @@ def root_path(self): def root_uri(self): return self._root_uri - def update_document(self, - text_doc: VersionedTextDocumentIdentifier, - change: TextDocumentContentChangeEvent): + def update_document( + self, + text_doc: VersionedTextDocumentIdentifier, + change: TextDocumentContentChangeEvent, + ): doc_uri = text_doc.uri self._docs[doc_uri].apply_change(change) self._docs[doc_uri].version = text_doc.version diff --git a/pyproject.toml b/pyproject.toml index 0b5687cc..f04f739b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,9 +24,11 @@ ws = ["websockets"] [tool.poetry.group.dev.dependencies] # Replaces (amongst many other things) flake8 and bandit ruff = "^0.0.275" -# TODO `poethepoet==0.20` needs python 3.8 +# TODO `poethepoet>=0.20` needs python 3.8 poethepoet = "^0.19.0" mypy = "^1.4.1" +# TODO `black>=23` needs python 3.8 +black = "22.8" [tool.poetry.group.test.dependencies] pytest = "7.1.2" @@ -55,10 +57,12 @@ lint = [ "ruff", "mypy", "check_generated_client", - "check_commit_style" + "check_commit_style", + "black_check" ] generate_client = "python scripts/generate_client.py --output pygls/lsp/client.py" generate_contributors_md = "python scripts/generate_contributors_md.py" +black_check = "black --check ." [tool.ruff] line-length = 120 diff --git a/scripts/check_client_is_uptodate.py b/scripts/check_client_is_uptodate.py index 4e4ead38..1c5b1be2 100644 --- a/scripts/check_client_is_uptodate.py +++ b/scripts/check_client_is_uptodate.py @@ -3,17 +3,11 @@ AUTOGENERATED_CLIENT_FILE = "pygls/lsp/client.py" -subprocess.run([ - "poe", - "generate_client" -]) +subprocess.run(["poe", "generate_client"]) -result = subprocess.run([ - "git", - "diff", - "--exit-code", - AUTOGENERATED_CLIENT_FILE -], stdout = subprocess.DEVNULL) +result = subprocess.run( + ["git", "diff", "--exit-code", AUTOGENERATED_CLIENT_FILE], stdout=subprocess.DEVNULL +) if result.returncode == 0: print("✅ Pygls client is up to date") diff --git a/scripts/generate_client.py b/scripts/generate_client.py index 15f64897..777c07d6 100644 --- a/scripts/generate_client.py +++ b/scripts/generate_client.py @@ -61,7 +61,7 @@ def write_notification( textwrap.indent(inspect.getdoc(request) or "", " "), ' """', " if self.stopped:", - ' raise RuntimeError(\"Client has been stopped.\")', + ' raise RuntimeError("Client has been stopped.")', "", f' self.protocol.notify("{method}", params)', "", @@ -70,7 +70,6 @@ def write_notification( def get_response_type(response: Type, imports: Set[Tuple[str, str]]) -> str: - # Find the response type. result_field = [f for f in response.__attrs_attrs__ if f.name == "result"][0] result = re.sub(r"", r"\1", str(result_field.type)) @@ -120,7 +119,7 @@ def write_method( textwrap.indent(inspect.getdoc(request) or "", " "), ' """', " if self.stopped:", - ' raise RuntimeError(\"Client has been stopped.\")', + ' raise RuntimeError("Client has been stopped.")', "", f' return self.protocol.send_request("{method}", params, callback)', "", @@ -133,7 +132,7 @@ def write_method( textwrap.indent(inspect.getdoc(request) or "", " "), ' """', " if self.stopped:", - ' raise RuntimeError(\"Client has been stopped.\")', + ' raise RuntimeError("Client has been stopped.")', "", f' return await self.protocol.send_request_async("{method}", params)', "", @@ -153,7 +152,6 @@ def generate_client() -> str: } for method_name, types in METHOD_TO_TYPES.items(): - # Skip any requests that come from the server. if message_direction(method_name) == "serverToClient": continue diff --git a/scripts/generate_contributors_md.py b/scripts/generate_contributors_md.py index e2459b22..655181a1 100644 --- a/scripts/generate_contributors_md.py +++ b/scripts/generate_contributors_md.py @@ -25,7 +25,9 @@ import requests -PYGLS_CONTRIBUTORS_JSON_URL = "https://api.github.com/repos/openlawlibrary/pygls/contributors" +PYGLS_CONTRIBUTORS_JSON_URL = ( + "https://api.github.com/repos/openlawlibrary/pygls/contributors" +) CONTRIBUTORS_FILE = "CONTRIBUTORS.md" response = requests.get(PYGLS_CONTRIBUTORS_JSON_URL) @@ -37,7 +39,7 @@ name = contributor["login"] contributions = contributor["contributions"] url = contributor["html_url"] - contents += f'* [{name}]({url}) ({contributions})\n' + contents += f"* [{name}]({url}) ({contributions})\n" file = open(CONTRIBUTORS_FILE, "w") n = file.write(contents) diff --git a/tests/_init_server_stall_fix_hack.py b/tests/_init_server_stall_fix_hack.py index c7fa4513..04895b0e 100644 --- a/tests/_init_server_stall_fix_hack.py +++ b/tests/_init_server_stall_fix_hack.py @@ -11,8 +11,7 @@ def retry_stalled_init_fix_hack(): - - if 'DISABLE_TIMEOUT' in os.environ: + if "DISABLE_TIMEOUT" in os.environ: return lambda f: f def decorator(func): @@ -23,10 +22,12 @@ def newfn(*args, **kwargs): return func(*args, **kwargs) except concurrent.futures._base.TimeoutError: print( - '\n\nRetrying timeouted test server init ' - '%d of %d\n' % (attempt, RETRIES) + "\n\nRetrying timeouted test server init " + "%d of %d\n" % (attempt, RETRIES) ) attempt += 1 return func(*args, **kwargs) + return newfn + return decorator diff --git a/tests/conftest.py b/tests/conftest.py index 3f0ce78a..d5ddf2fd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -48,7 +48,7 @@ @pytest.fixture(autouse=False) def client_server(request): - if hasattr(request, 'param'): + if hasattr(request, "param"): ConfiguredClientServer = request.param client_server = ConfiguredClientServer() else: diff --git a/tests/ls_setup.py b/tests/ls_setup.py index e556eeb7..e52c06cd 100644 --- a/tests/ls_setup.py +++ b/tests/ls_setup.py @@ -36,8 +36,8 @@ CALL_TIMEOUT = 3 -def setup_ls_features(server): +def setup_ls_features(server): # Commands @server.command(CMD_ASYNC) async def cmd_test3(ls, *args): # pylint: disable=unused-variable @@ -64,9 +64,7 @@ def close(self): def write(self, data): object_hook = self.dest.lsp._deserialize_message - self.dest.lsp._procedure_handler( - json.loads(data, object_hook=object_hook) - ) + self.dest.lsp._procedure_handler(json.loads(data, object_hook=object_hook)) class PyodideClientServer: @@ -74,9 +72,8 @@ class PyodideClientServer: environment.""" def __init__(self, LS=LanguageServer): - - self.server = LS('pygls-server', 'v1') - self.client = LS('pygls-client', 'v1') + self.server = LS("pygls-server", "v1") + self.client = LS("pygls-client", "v1") self.server.lsp.connection_made(PyodideTestTransportAdapter(self.client)) self.server.lsp._send_only_body = True @@ -92,20 +89,14 @@ def stop(self): @classmethod def decorate(cls): - return pytest.mark.parametrize( - 'client_server', - [cls], - indirect=True - ) + return pytest.mark.parametrize("client_server", [cls], indirect=True) def initialize(self): response = self.client.lsp.send_request( INITIALIZE, InitializeParams( - process_id=12345, - root_uri="file://", - capabilities=ClientCapabilities() - ) + process_id=12345, root_uri="file://", capabilities=ClientCapabilities() + ), ).result(timeout=CALL_TIMEOUT) assert response.capabilities is not None @@ -123,18 +114,18 @@ def __init__(self, LS=LanguageServer): scr, scw = os.pipe() # Setup Server - self.server = LS('server', 'v1') + self.server = LS("server", "v1") self.server_thread = threading.Thread( - name='Server Thread', + name="Server Thread", target=self.server.start_io, args=(os.fdopen(csr, "rb"), os.fdopen(scw, "wb")), ) self.server_thread.daemon = True # Setup client - self.client = LS('client', 'v1', asyncio.new_event_loop()) + self.client = LS("client", "v1", asyncio.new_event_loop()) self.client_thread = threading.Thread( - name='Client Thread', + name="Client Thread", target=self.client.start_io, args=(os.fdopen(scr, "rb"), os.fdopen(csw, "wb")), ) @@ -142,11 +133,7 @@ def __init__(self, LS=LanguageServer): @classmethod def decorate(cls): - return pytest.mark.parametrize( - 'client_server', - [cls], - indirect=True - ) + return pytest.mark.parametrize("client_server", [cls], indirect=True) def start(self): self.server_thread.start() @@ -155,9 +142,7 @@ def start(self): self.initialize() def stop(self): - shutdown_response = self.client.lsp.send_request( - SHUTDOWN - ).result() + shutdown_response = self.client.lsp.send_request(SHUTDOWN).result() assert shutdown_response is None self.client.lsp.notify(EXIT) self.server_thread.join() @@ -170,14 +155,11 @@ def stop(self): @retry_stalled_init_fix_hack() def initialize(self): - - timeout = None if 'DISABLE_TIMEOUT' in os.environ else 1 + timeout = None if "DISABLE_TIMEOUT" in os.environ else 1 response = self.client.lsp.send_request( INITIALIZE, InitializeParams( - process_id=12345, - root_uri="file://", - capabilities=ClientCapabilities() + process_id=12345, root_uri="file://", capabilities=ClientCapabilities() ), ).result(timeout=timeout) assert response.capabilities is not None diff --git a/tests/lsp/semantic_tokens/test_delta_missing_legend.py b/tests/lsp/semantic_tokens/test_delta_missing_legend.py index b7a5c010..a3069da5 100644 --- a/tests/lsp/semantic_tokens/test_delta_missing_legend.py +++ b/tests/lsp/semantic_tokens/test_delta_missing_legend.py @@ -38,8 +38,7 @@ def __init__(self): @self.server.feature( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, SemanticTokensLegend( - token_types=["keyword", "operator"], - token_modifiers=["readonly"] + token_types=["keyword", "operator"], token_modifiers=["readonly"] ), ) def f( @@ -60,9 +59,7 @@ def test_capabilities(client_server): "keyword", "operator", ] - assert provider.legend.token_modifiers == [ - "readonly" - ] + assert provider.legend.token_modifiers == ["readonly"] @ConfiguredLS.decorate() @@ -71,8 +68,7 @@ def test_semantic_tokens_full_delta_return_tokens(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, SemanticTokensDeltaParams( - text_document=TextDocumentIdentifier( - uri="file://return.tokens"), + text_document=TextDocumentIdentifier(uri="file://return.tokens"), previous_result_id="id", ), ).result() diff --git a/tests/lsp/semantic_tokens/test_full_missing_legend.py b/tests/lsp/semantic_tokens/test_full_missing_legend.py index 76c00e4b..e18dbde3 100644 --- a/tests/lsp/semantic_tokens/test_full_missing_legend.py +++ b/tests/lsp/semantic_tokens/test_full_missing_legend.py @@ -22,7 +22,7 @@ from lsprotocol.types import ( SemanticTokens, SemanticTokensPartialResult, - SemanticTokensParams + SemanticTokensParams, ) from ...conftest import ClientServer diff --git a/tests/lsp/semantic_tokens/test_range.py b/tests/lsp/semantic_tokens/test_range.py index 8099e92e..a65504b6 100644 --- a/tests/lsp/semantic_tokens/test_range.py +++ b/tests/lsp/semantic_tokens/test_range.py @@ -32,10 +32,7 @@ from ...conftest import ClientServer SemanticTokenReturnType = Optional[ - Union[ - SemanticTokensPartialResult, - Optional[SemanticTokens] - ] + Union[SemanticTokensPartialResult, Optional[SemanticTokens]] ] @@ -46,8 +43,7 @@ def __init__(self): @self.server.feature( TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, SemanticTokensLegend( - token_types=["keyword", "operator"], - token_modifiers=["readonly"] + token_types=["keyword", "operator"], token_modifiers=["readonly"] ), ) def f( @@ -68,9 +64,7 @@ def test_capabilities(client_server): "keyword", "operator", ] - assert provider.legend.token_modifiers == [ - "readonly" - ] + assert provider.legend.token_modifiers == ["readonly"] @ConfiguredLS.decorate() @@ -79,8 +73,7 @@ def test_semantic_tokens_range_return_tokens(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, SemanticTokensRangeParams( - text_document=TextDocumentIdentifier( - uri="file://return.tokens"), + text_document=TextDocumentIdentifier(uri="file://return.tokens"), range=Range( start=Position(line=0, character=0), end=Position(line=10, character=80), diff --git a/tests/lsp/semantic_tokens/test_semantic_tokens_full.py b/tests/lsp/semantic_tokens/test_semantic_tokens_full.py index 41c3c85c..dba9fa68 100644 --- a/tests/lsp/semantic_tokens/test_semantic_tokens_full.py +++ b/tests/lsp/semantic_tokens/test_semantic_tokens_full.py @@ -30,10 +30,7 @@ from ...conftest import ClientServer SemanticTokenReturnType = Optional[ - Union[ - SemanticTokensPartialResult, - Optional[SemanticTokens] - ] + Union[SemanticTokensPartialResult, Optional[SemanticTokens]] ] @@ -44,8 +41,7 @@ def __init__(self): @self.server.feature( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, SemanticTokensLegend( - token_types=["keyword", "operator"], - token_modifiers=["readonly"] + token_types=["keyword", "operator"], token_modifiers=["readonly"] ), ) def f( @@ -66,9 +62,7 @@ def test_capabilities(client_server): "keyword", "operator", ] - assert provider.legend.token_modifiers == [ - "readonly" - ] + assert provider.legend.token_modifiers == ["readonly"] @ConfiguredLS.decorate() @@ -77,8 +71,7 @@ def test_semantic_tokens_full_return_tokens(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, SemanticTokensParams( - text_document=TextDocumentIdentifier( - uri="file://return.tokens") + text_document=TextDocumentIdentifier(uri="file://return.tokens") ), ).result() diff --git a/tests/lsp/test_call_hierarchy.py b/tests/lsp/test_call_hierarchy.py index c86d0d59..410a9827 100644 --- a/tests/lsp/test_call_hierarchy.py +++ b/tests/lsp/test_call_hierarchy.py @@ -19,13 +19,21 @@ from lsprotocol.types import ( CALL_HIERARCHY_INCOMING_CALLS, CALL_HIERARCHY_OUTGOING_CALLS, - TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY + TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY, ) from lsprotocol.types import ( - CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, - CallHierarchyItem, CallHierarchyOptions, CallHierarchyOutgoingCall, - CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, - Position, Range, SymbolKind, SymbolTag, TextDocumentIdentifier + CallHierarchyIncomingCall, + CallHierarchyIncomingCallsParams, + CallHierarchyItem, + CallHierarchyOptions, + CallHierarchyOutgoingCall, + CallHierarchyOutgoingCallsParams, + CallHierarchyPrepareParams, + Position, + Range, + SymbolKind, + SymbolTag, + TextDocumentIdentifier, ) from ..conftest import ClientServer @@ -49,9 +57,9 @@ def check_call_hierarchy_item_response(item): - assert item.name == 'test_name' + assert item.name == "test_name" assert item.kind == SymbolKind.File - assert item.uri == 'test_uri' + assert item.uri == "test_uri" assert item.range.start.line == 0 assert item.range.start.character == 0 assert item.range.end.line == 1 @@ -62,8 +70,8 @@ def check_call_hierarchy_item_response(item): assert item.selection_range.end.character == 2 assert len(item.tags) == 1 assert item.tags[0] == SymbolTag.Deprecated - assert item.detail == 'test_detail' - assert item.data == 'test_data' + assert item.detail == "test_detail" + assert item.data == "test_data" class ConfiguredLS(ClientServer): @@ -74,17 +82,15 @@ def __init__(self): TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY, CallHierarchyOptions(), ) - def f1( - params: CallHierarchyPrepareParams - ) -> Optional[List[CallHierarchyItem]]: - if params.text_document.uri == 'file://return.list': + def f1(params: CallHierarchyPrepareParams) -> Optional[List[CallHierarchyItem]]: + if params.text_document.uri == "file://return.list": return [CALL_HIERARCHY_ITEM] else: return None @self.server.feature(CALL_HIERARCHY_INCOMING_CALLS) def f2( - params: CallHierarchyIncomingCallsParams + params: CallHierarchyIncomingCallsParams, ) -> Optional[List[CallHierarchyIncomingCall]]: return [ CallHierarchyIncomingCall( @@ -100,7 +106,7 @@ def f2( @self.server.feature(CALL_HIERARCHY_OUTGOING_CALLS) def f3( - params: CallHierarchyOutgoingCallsParams + params: CallHierarchyOutgoingCallsParams, ) -> Optional[List[CallHierarchyOutgoingCall]]: return [ CallHierarchyOutgoingCall( @@ -128,9 +134,9 @@ def test_call_hierarchy_prepare_return_list(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY, CallHierarchyPrepareParams( - text_document=TextDocumentIdentifier(uri='file://return.list'), + text_document=TextDocumentIdentifier(uri="file://return.list"), position=Position(line=0, character=0), - ) + ), ).result() check_call_hierarchy_item_response(response[0]) @@ -142,9 +148,9 @@ def test_call_hierarchy_prepare_return_none(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY, CallHierarchyPrepareParams( - text_document=TextDocumentIdentifier(uri='file://return.none'), + text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), - ) + ), ).result() assert response is None @@ -155,7 +161,7 @@ def test_call_hierarchy_incoming_calls_return_list(client_server): client, _ = client_server response = client.lsp.send_request( CALL_HIERARCHY_INCOMING_CALLS, - CallHierarchyIncomingCallsParams(item=CALL_HIERARCHY_ITEM) + CallHierarchyIncomingCallsParams(item=CALL_HIERARCHY_ITEM), ).result() item = response[0] @@ -173,7 +179,7 @@ def test_call_hierarchy_outgoing_calls_return_list(client_server): client, _ = client_server response = client.lsp.send_request( CALL_HIERARCHY_OUTGOING_CALLS, - CallHierarchyOutgoingCallsParams(item=CALL_HIERARCHY_ITEM) + CallHierarchyOutgoingCallsParams(item=CALL_HIERARCHY_ITEM), ).result() item = response[0] @@ -184,4 +190,3 @@ def test_call_hierarchy_outgoing_calls_return_list(client_server): assert item.from_ranges[0].start.character == 3 assert item.from_ranges[0].end.line == 4 assert item.from_ranges[0].end.character == 4 - diff --git a/tests/lsp/test_code_action.py b/tests/lsp/test_code_action.py index a9057dbb..3a12d179 100644 --- a/tests/lsp/test_code_action.py +++ b/tests/lsp/test_code_action.py @@ -94,4 +94,3 @@ async def test_code_actions(client: LanguageClient, workspace_dir): assert fix.range == expected_range assert fix.new_text == "1 + 1 = 2!" - diff --git a/tests/lsp/test_code_lens.py b/tests/lsp/test_code_lens.py index 2b728bf4..70241109 100644 --- a/tests/lsp/test_code_lens.py +++ b/tests/lsp/test_code_lens.py @@ -71,9 +71,7 @@ def test_code_lens_return_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_CODE_LENS, - CodeLensParams( - text_document=TextDocumentIdentifier(uri="file://return.list") - ), + CodeLensParams(text_document=TextDocumentIdentifier(uri="file://return.list")), ).result() assert response[0].data == "some data" @@ -90,9 +88,7 @@ def test_code_lens_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_CODE_LENS, - CodeLensParams( - text_document=TextDocumentIdentifier(uri="file://return.none") - ), + CodeLensParams(text_document=TextDocumentIdentifier(uri="file://return.none")), ).result() assert response is None diff --git a/tests/lsp/test_declaration.py b/tests/lsp/test_declaration.py index 4a8c3de9..221982d6 100644 --- a/tests/lsp/test_declaration.py +++ b/tests/lsp/test_declaration.py @@ -84,9 +84,7 @@ def test_declaration_return_location(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_DECLARATION, DeclarationParams( - text_document=TextDocumentIdentifier( - uri="file://return.location" - ), + text_document=TextDocumentIdentifier(uri="file://return.location"), position=Position(line=0, character=0), ), ).result() @@ -105,8 +103,7 @@ def test_declaration_return_location_list(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_DECLARATION, DeclarationParams( - text_document=TextDocumentIdentifier( - uri="file://return.location_list"), + text_document=TextDocumentIdentifier(uri="file://return.location_list"), position=Position(line=0, character=0), ), ).result() diff --git a/tests/lsp/test_definition.py b/tests/lsp/test_definition.py index d101efb5..3ed2f964 100644 --- a/tests/lsp/test_definition.py +++ b/tests/lsp/test_definition.py @@ -87,8 +87,7 @@ def test_definition_return_location(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_DEFINITION, DefinitionParams( - text_document=TextDocumentIdentifier( - uri="file://return.location"), + text_document=TextDocumentIdentifier(uri="file://return.location"), position=Position(line=0, character=0), ), ).result() @@ -107,8 +106,7 @@ def test_definition_return_location_list(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_DEFINITION, DefinitionParams( - text_document=TextDocumentIdentifier( - uri="file://return.location_list"), + text_document=TextDocumentIdentifier(uri="file://return.location_list"), position=Position(line=0, character=0), ), ).result() diff --git a/tests/lsp/test_document_highlight.py b/tests/lsp/test_document_highlight.py index dcfc0162..e4afc5fe 100644 --- a/tests/lsp/test_document_highlight.py +++ b/tests/lsp/test_document_highlight.py @@ -39,9 +39,7 @@ def __init__(self): TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT, DocumentHighlightOptions(), ) - def f( - params: DocumentHighlightParams - ) -> Optional[List[DocumentHighlight]]: + def f(params: DocumentHighlightParams) -> Optional[List[DocumentHighlight]]: if params.text_document.uri == "file://return.list": return [ DocumentHighlight( diff --git a/tests/lsp/test_hover.py b/tests/lsp/test_hover.py index 7e320a8b..9007c780 100644 --- a/tests/lsp/test_hover.py +++ b/tests/lsp/test_hover.py @@ -67,8 +67,7 @@ def f(params: HoverParams) -> Optional[Hover]: ), "file://return.markup_content": Hover( range=range, - contents=MarkupContent( - kind=MarkupKind.Markdown, value="value"), + contents=MarkupContent(kind=MarkupKind.Markdown, value="value"), ), }.get(params.text_document.uri, None) @@ -87,8 +86,7 @@ def test_hover_return_marked_string(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_HOVER, HoverParams( - text_document=TextDocumentIdentifier( - uri="file://return.marked_string"), + text_document=TextDocumentIdentifier(uri="file://return.marked_string"), position=Position(line=0, character=0), ), ).result() @@ -135,9 +133,7 @@ def test_hover_return_markup_content(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_HOVER, HoverParams( - text_document=TextDocumentIdentifier( - uri="file://return.markup_content" - ), + text_document=TextDocumentIdentifier(uri="file://return.markup_content"), position=Position(line=0, character=0), ), ).result() diff --git a/tests/lsp/test_implementation.py b/tests/lsp/test_implementation.py index 9a8e690c..4fea3a91 100644 --- a/tests/lsp/test_implementation.py +++ b/tests/lsp/test_implementation.py @@ -87,8 +87,7 @@ def test_type_definition_return_location(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_IMPLEMENTATION, ImplementationParams( - text_document=TextDocumentIdentifier( - uri="file://return.location"), + text_document=TextDocumentIdentifier(uri="file://return.location"), position=Position(line=0, character=0), ), ).result() @@ -107,8 +106,7 @@ def test_type_definition_return_location_list(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_IMPLEMENTATION, ImplementationParams( - text_document=TextDocumentIdentifier( - uri="file://return.location_list"), + text_document=TextDocumentIdentifier(uri="file://return.location_list"), position=Position(line=0, character=0), ), ).result() diff --git a/tests/lsp/test_linked_editing_range.py b/tests/lsp/test_linked_editing_range.py index 8b00644c..2650b7eb 100644 --- a/tests/lsp/test_linked_editing_range.py +++ b/tests/lsp/test_linked_editing_range.py @@ -38,9 +38,7 @@ def __init__(self): TEXT_DOCUMENT_LINKED_EDITING_RANGE, LinkedEditingRangeOptions(), ) - def f( - params: LinkedEditingRangeParams - ) -> Optional[LinkedEditingRanges]: + def f(params: LinkedEditingRangeParams) -> Optional[LinkedEditingRanges]: if params.text_document.uri == "file://return.ranges": return LinkedEditingRanges( ranges=[ @@ -73,8 +71,7 @@ def test_linked_editing_ranges_return_ranges(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_LINKED_EDITING_RANGE, LinkedEditingRangeParams( - text_document=TextDocumentIdentifier( - uri="file://return.ranges"), + text_document=TextDocumentIdentifier(uri="file://return.ranges"), position=Position(line=0, character=0), ), ).result() diff --git a/tests/lsp/test_on_type_formatting.py b/tests/lsp/test_on_type_formatting.py index b0f041e8..2e7adc9b 100644 --- a/tests/lsp/test_on_type_formatting.py +++ b/tests/lsp/test_on_type_formatting.py @@ -42,9 +42,7 @@ def __init__(self): more_trigger_character=[",", "."], ), ) - def f( - params: DocumentOnTypeFormattingParams - ) -> Optional[List[TextEdit]]: + def f(params: DocumentOnTypeFormattingParams) -> Optional[List[TextEdit]]: if params.text_document.uri == "file://return.list": return [ TextEdit( @@ -66,17 +64,12 @@ def test_capabilities(client_server): assert capabilities.document_on_type_formatting_provider assert ( - capabilities - .document_on_type_formatting_provider - .first_trigger_character - == ":" - ) - assert ( - capabilities - .document_on_type_formatting_provider - .more_trigger_character - == [",", "."] + capabilities.document_on_type_formatting_provider.first_trigger_character == ":" ) + assert capabilities.document_on_type_formatting_provider.more_trigger_character == [ + ",", + ".", + ] @ConfiguredLS.decorate() diff --git a/tests/lsp/test_prepare_rename.py b/tests/lsp/test_prepare_rename.py index 11dae9c4..39d07129 100644 --- a/tests/lsp/test_prepare_rename.py +++ b/tests/lsp/test_prepare_rename.py @@ -36,7 +36,7 @@ def __init__(self): @self.server.feature(TEXT_DOCUMENT_PREPARE_RENAME) def f( - params: PrepareRenameParams + params: PrepareRenameParams, ) -> Optional[Union[Range, PrepareRenameResult]]: return { # type: ignore "file://return.range": Range( @@ -64,8 +64,7 @@ def test_prepare_rename_return_range(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_PREPARE_RENAME, PrepareRenameParams( - text_document=TextDocumentIdentifier( - uri="file://return.range"), + text_document=TextDocumentIdentifier(uri="file://return.range"), position=Position(line=0, character=0), ), ).result() @@ -84,9 +83,7 @@ def test_prepare_rename_return_prepare_rename(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_PREPARE_RENAME, PrepareRenameParams( - text_document=TextDocumentIdentifier( - uri="file://return.prepare_rename" - ), + text_document=TextDocumentIdentifier(uri="file://return.prepare_rename"), position=Position(line=0, character=0), ), ).result() diff --git a/tests/lsp/test_progress.py b/tests/lsp/test_progress.py index 6e42c225..49657729 100644 --- a/tests/lsp/test_progress.py +++ b/tests/lsp/test_progress.py @@ -49,8 +49,7 @@ def __init__(self): @self.server.feature( TEXT_DOCUMENT_CODE_LENS, - CodeLensOptions(resolve_provider=False, - work_done_progress=True), + CodeLensOptions(resolve_provider=False, work_done_progress=True), ) async def f1(params: CodeLensParams) -> Optional[List[CodeLens]]: if "client_initiated_token" in params.text_document.uri: @@ -68,19 +67,23 @@ async def f1(params: CodeLensParams) -> Optional[List[CodeLens]]: assert token self.server.lsp.progress.begin( token, - WorkDoneProgressBegin(kind='begin', title="starting", percentage=0), + WorkDoneProgressBegin(kind="begin", title="starting", percentage=0), ) await asyncio.sleep(0.1) if self.server.lsp.progress.tokens[token].cancelled(): self.server.lsp.progress.end( - token, WorkDoneProgressEnd(kind='end', message="cancelled") + token, WorkDoneProgressEnd(kind="end", message="cancelled") ) else: self.server.lsp.progress.report( token, - WorkDoneProgressReport(kind='report', message="doing", percentage=50), + WorkDoneProgressReport( + kind="report", message="doing", percentage=50 + ), + ) + self.server.lsp.progress.end( + token, WorkDoneProgressEnd(kind="end", message="done") ) - self.server.lsp.progress.end(token, WorkDoneProgressEnd(kind='end', message="done")) return None @self.client.feature(PROGRESS) diff --git a/tests/lsp/test_range_formatting.py b/tests/lsp/test_range_formatting.py index 7e8c480b..e7a118cc 100644 --- a/tests/lsp/test_range_formatting.py +++ b/tests/lsp/test_range_formatting.py @@ -39,9 +39,7 @@ def __init__(self): TEXT_DOCUMENT_RANGE_FORMATTING, DocumentRangeFormattingOptions(), ) - def f( - params: DocumentRangeFormattingParams - ) -> Optional[List[TextEdit]]: + def f(params: DocumentRangeFormattingParams) -> Optional[List[TextEdit]]: if params.text_document.uri == "file://return.list": return [ TextEdit( diff --git a/tests/lsp/test_rename.py b/tests/lsp/test_rename.py index a4e5952a..48cface7 100644 --- a/tests/lsp/test_rename.py +++ b/tests/lsp/test_rename.py @@ -99,7 +99,8 @@ ignore_if_not_exists=True, ), ), - ], } + ], +} class ConfiguredLS(ClientServer): @@ -132,9 +133,7 @@ def test_rename_return_workspace_edit(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_RENAME, RenameParams( - text_document=TextDocumentIdentifier( - uri="file://return.workspace_edit" - ), + text_document=TextDocumentIdentifier(uri="file://return.workspace_edit"), position=Position(line=0, character=0), new_name="new name", ), @@ -160,14 +159,9 @@ def test_rename_return_workspace_edit(client_server): assert changes[0].text_document.version == 3 assert changes[0].edits[0].new_text == "text3" assert changes[0].edits[0].range.start.line == 2 - assert ( - changes[0].edits[0].range.start.character - == 2 - ) + assert changes[0].edits[0].range.start.character == 2 assert changes[0].edits[0].range.end.line == 3 - assert ( - changes[0].edits[0].range.end.character == 3 - ) + assert changes[0].edits[0].range.end.character == 3 assert changes[1].kind == ResourceOperationKind.Create.value assert changes[1].uri == "create file" diff --git a/tests/lsp/test_signature_help.py b/tests/lsp/test_signature_help.py index 3c3292c4..e3181203 100644 --- a/tests/lsp/test_signature_help.py +++ b/tests/lsp/test_signature_help.py @@ -86,9 +86,7 @@ def test_signature_help_return_signature_help(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_SIGNATURE_HELP, SignatureHelpParams( - text_document=TextDocumentIdentifier( - uri="file://return.signature_help" - ), + text_document=TextDocumentIdentifier(uri="file://return.signature_help"), position=Position(line=0, character=0), context=SignatureHelpContext( trigger_kind=SignatureHelpTriggerKind.TriggerCharacter, @@ -123,8 +121,7 @@ def test_signature_help_return_signature_help(client_server): assert response["signatures"][0]["documentation"] == "documentation" assert response["signatures"][0]["parameters"][0]["label"] == [0, 0] assert ( - response["signatures"][0]["parameters"][0]["documentation"] - == "documentation" + response["signatures"][0]["parameters"][0]["documentation"] == "documentation" ) diff --git a/tests/lsp/test_type_definition.py b/tests/lsp/test_type_definition.py index da437b63..b6d3eff3 100644 --- a/tests/lsp/test_type_definition.py +++ b/tests/lsp/test_type_definition.py @@ -86,8 +86,7 @@ def test_type_definition_return_location(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_TYPE_DEFINITION, TypeDefinitionParams( - text_document=TextDocumentIdentifier( - uri="file://return.location"), + text_document=TextDocumentIdentifier(uri="file://return.location"), position=Position(line=0, character=0), ), ).result() @@ -106,8 +105,7 @@ def test_type_definition_return_location_list(client_server): response = client.lsp.send_request( TEXT_DOCUMENT_TYPE_DEFINITION, TypeDefinitionParams( - text_document=TextDocumentIdentifier( - uri="file://return.location_list"), + text_document=TextDocumentIdentifier(uri="file://return.location_list"), position=Position(line=0, character=0), ), ).result() diff --git a/tests/pyodide_testrunner/run.py b/tests/pyodide_testrunner/run.py index cf9101c6..f70b1d07 100644 --- a/tests/pyodide_testrunner/run.py +++ b/tests/pyodide_testrunner/run.py @@ -34,7 +34,6 @@ def build_wheel() -> str: """ with tempfile.TemporaryDirectory() as tmpdir: - # Copy all required files. dest = pathlib.Path(tmpdir) @@ -45,31 +44,27 @@ def build_wheel() -> str: for src, target in directories: shutil.copytree(REPO / src, dest / target) - files = [ - "pyproject.toml", - "poetry.lock", - "README.md", - "ThirdPartyNotices.txt" - ] + files = ["pyproject.toml", "poetry.lock", "README.md", "ThirdPartyNotices.txt"] for src in files: shutil.copy(REPO / src, dest) # Convert the lock file to requirements.txt. # Ensures reproducible behavour for testing. - subprocess.run([ - "poetry", - "export", - "-f", "requirements.txt", - "--output", "requirements.txt" - ], cwd=dest) - subprocess.run([ - "poetry", - "run", - "pip", - "install", - "-r", "requirements.txt" - ], cwd=dest) + subprocess.run( + [ + "poetry", + "export", + "-f", + "requirements.txt", + "--output", + "requirements.txt", + ], + cwd=dest, + ) + subprocess.run( + ["poetry", "run", "pip", "install", "-r", "requirements.txt"], cwd=dest + ) # Build the wheel subprocess.run(["poetry", "build", "--format", "wheel"], cwd=dest) whl = list((dest / "dist").glob("*.whl"))[0] @@ -89,7 +84,6 @@ def spawn_http_server(q: Queue, directory: str): def main(): - exit_code = 1 whl = build_wheel() @@ -104,7 +98,6 @@ def main(): print("Running tests...") try: - driver_cls, options_cls = BROWSERS[os.environ.get("BROWSER", "chrome")] options = options_cls() diff --git a/tests/servers/invalid_json.py b/tests/servers/invalid_json.py index e3c946a9..5b40d7a6 100644 --- a/tests/servers/invalid_json.py +++ b/tests/servers/invalid_json.py @@ -6,8 +6,9 @@ from pygls.server import aio_readline + def handler(data): - content = 'Content-Length: 5\r\n\r\n{"ll}'.encode('utf8') + content = 'Content-Length: 5\r\n\r\n{"ll}'.encode("utf8") sys.stdout.buffer.write(content) sys.stdout.flush() @@ -18,7 +19,7 @@ async def main(): ThreadPoolExecutor(), threading.Event(), sys.stdin.buffer, - handler + handler, ) diff --git a/tests/servers/large_response.py b/tests/servers/large_response.py index 9055292a..fd85b627 100644 --- a/tests/servers/large_response.py +++ b/tests/servers/large_response.py @@ -6,6 +6,7 @@ from pygls.server import aio_readline + def handler(data): payload = dict( jsonrpc="2.0", @@ -15,7 +16,7 @@ def handler(data): ), ) content = str(payload).replace("'", '"') - message = f'Content-Length: {len(content)}\r\n\r\n{content}'.encode('utf8') + message = f"Content-Length: {len(content)}\r\n\r\n{content}".encode("utf8") sys.stdout.buffer.write(message) sys.stdout.flush() @@ -27,7 +28,7 @@ async def main(): ThreadPoolExecutor(), threading.Event(), sys.stdin.buffer, - handler + handler, ) diff --git a/tests/test_client.py b/tests/test_client.py index e03e1807..a21b630a 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -50,7 +50,6 @@ async def server_exit(self, server: asyncio.subprocess.Process): assert client.server_exit_called, message - @pytest.mark.asyncio @pytest.mark.skipif(IS_PYODIDE, reason="Subprocesses are not available on pyodide.") async def test_client_detect_invalid_json(): @@ -70,18 +69,18 @@ def report_server_error( self._server.kill() self._stop_event.set() - assert 'Unterminated string' in str(error) + assert "Unterminated string" in str(error) client = TestClient() await client.start_io(sys.executable, str(SERVERS / "invalid_json.py")) - future = client.protocol.send_request_async('method/name', {}) + future = client.protocol.send_request_async("method/name", {}) client.future = future try: await future except asyncio.CancelledError: - pass # Ignore the exception generated by cancelling the future + pass # Ignore the exception generated by cancelling the future finally: await client.stop() diff --git a/tests/test_document.py b/tests/test_document.py index 8b21ac26..402c43fe 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -38,8 +38,7 @@ def test_document_empty_edit(): doc = Document("file:///uri", "") change = TextDocumentContentChangeEvent_Type1( range=Range( - start=Position(line=0, character=0), - end=Position(line=0, character=0) + start=Position(line=0, character=0), end=Position(line=0, character=0) ), range_length=0, text="f", @@ -54,8 +53,7 @@ def test_document_end_of_file_edit(): change = TextDocumentContentChangeEvent_Type1( range=Range( - start=Position(line=2, character=0), - end=Position(line=2, character=0) + start=Position(line=2, character=0), end=Position(line=2, character=0) ), range_length=0, text="o", @@ -71,12 +69,10 @@ def test_document_end_of_file_edit(): def test_document_full_edit(): old = ["def hello(a, b):\n", " print a\n", " print b\n"] - doc = Document("file:///uri", "".join(old), - sync_kind=TextDocumentSyncKind.Full) + doc = Document("file:///uri", "".join(old), sync_kind=TextDocumentSyncKind.Full) change = TextDocumentContentChangeEvent_Type1( range=Range( - start=Position(line=1, character=4), - end=Position(line=2, character=11) + start=Position(line=1, character=4), end=Position(line=2, character=11) ), range_length=0, text="print a, b", @@ -85,8 +81,7 @@ def test_document_full_edit(): assert doc.lines == ["print a, b"] - doc = Document("file:///uri", "".join(old), - sync_kind=TextDocumentSyncKind.Full) + doc = Document("file:///uri", "".join(old), sync_kind=TextDocumentSyncKind.Full) change = TextDocumentContentChangeEvent_Type1(range=None, text="print a, b") doc.apply_change(change) @@ -97,8 +92,7 @@ def test_document_line_edit(): doc = Document("file:///uri", "itshelloworld") change = TextDocumentContentChangeEvent_Type1( range=Range( - start=Position(line=0, character=3), - end=Position(line=0, character=8) + start=Position(line=0, character=3), end=Position(line=0, character=8) ), range_length=0, text="goodbye", @@ -119,8 +113,7 @@ def test_document_multiline_edit(): ) change = TextDocumentContentChangeEvent_Type1( range=Range( - start=Position(line=1, character=4), - end=Position(line=2, character=11) + start=Position(line=1, character=4), end=Position(line=2, character=11) ), range_length=0, text="print a, b", @@ -134,8 +127,7 @@ def test_document_multiline_edit(): ) change = TextDocumentContentChangeEvent_Type1( range=Range( - start=Position(line=1, character=4), - end=Position(line=2, character=11) + start=Position(line=1, character=4), end=Position(line=2, character=11) ), text="print a, b", ) @@ -146,12 +138,10 @@ def test_document_multiline_edit(): def test_document_no_edit(): old = ["def hello(a, b):\n", " print a\n", " print b\n"] - doc = Document("file:///uri", "".join(old), - sync_kind=TextDocumentSyncKind.None_) + doc = Document("file:///uri", "".join(old), sync_kind=TextDocumentSyncKind.None_) change = TextDocumentContentChangeEvent_Type1( range=Range( - start=Position(line=1, character=4), - end=Position(line=2, character=11) + start=Position(line=1, character=4), end=Position(line=2, character=11) ), range_length=0, text="print a, b", @@ -173,14 +163,10 @@ def test_document_source_unicode(): def test_position_from_utf16(): - assert position_from_utf16( - ['x="😋"'], Position(line=0, character=3) - ) == Position( + assert position_from_utf16(['x="😋"'], Position(line=0, character=3)) == Position( line=0, character=3 ) - assert position_from_utf16( - ['x="😋"'], Position(line=0, character=5) - ) == Position( + assert position_from_utf16(['x="😋"'], Position(line=0, character=5)) == Position( line=0, character=4 ) @@ -190,15 +176,11 @@ def test_position_from_utf16(): def test_position_to_utf16(): - assert position_to_utf16( - ['x="😋"'], Position(line=0, character=3) - ) == Position( + assert position_to_utf16(['x="😋"'], Position(line=0, character=3)) == Position( line=0, character=3 ) - assert position_to_utf16( - ['x="😋"'], Position(line=0, character=4) - ) == Position( + assert position_to_utf16(['x="😋"'], Position(line=0, character=4)) == Position( line=0, character=5 ) @@ -210,12 +192,8 @@ def test_position_to_utf16(): def test_range_from_utf16(): assert range_from_utf16( ['x="😋"'], - Range(start=Position(line=0, character=3), - end=Position(line=0, character=5)), - ) == Range( - start=Position(line=0, character=3), - end=Position(line=0, character=4) - ) + Range(start=Position(line=0, character=3), end=Position(line=0, character=5)), + ) == Range(start=Position(line=0, character=3), end=Position(line=0, character=4)) range = Range( start=Position(line=0, character=3), end=Position(line=0, character=5) @@ -230,12 +208,8 @@ def test_range_from_utf16(): def test_range_to_utf16(): assert range_to_utf16( ['x="😋"'], - Range(start=Position(line=0, character=3), - end=Position(line=0, character=4)), - ) == Range( - start=Position(line=0, character=3), - end=Position(line=0, character=5) - ) + Range(start=Position(line=0, character=3), end=Position(line=0, character=4)), + ) == Range(start=Position(line=0, character=3), end=Position(line=0, character=5)) range = Range( start=Position(line=0, character=3), end=Position(line=0, character=4) @@ -258,22 +232,47 @@ def test_offset_at_position(doc): assert doc.offset_at_position(Position(line=4, character=0)) == 40 assert doc.offset_at_position(Position(line=5, character=0)) == 40 + def test_utf16_to_utf32_position_cast(doc): - lines = ['', '😋😋', ''] - assert position_from_utf16(lines, Position(line=0, character=0)) == Position(line=0, character=0) - assert position_from_utf16(lines, Position(line=0, character=1)) == Position(line=0, character=0) - assert position_from_utf16(lines, Position(line=1, character=0)) == Position(line=1, character=0) - assert position_from_utf16(lines, Position(line=1, character=2)) == Position(line=1, character=1) - assert position_from_utf16(lines, Position(line=1, character=3)) == Position(line=1, character=2) - assert position_from_utf16(lines, Position(line=1, character=4)) == Position(line=1, character=2) - assert position_from_utf16(lines, Position(line=1, character=100)) == Position(line=1, character=2) - assert position_from_utf16(lines, Position(line=3, character=0)) == Position(line=2, character=0) - assert position_from_utf16(lines, Position(line=4, character=10)) == Position(line=2, character=0) + lines = ["", "😋😋", ""] + assert position_from_utf16(lines, Position(line=0, character=0)) == Position( + line=0, character=0 + ) + assert position_from_utf16(lines, Position(line=0, character=1)) == Position( + line=0, character=0 + ) + assert position_from_utf16(lines, Position(line=1, character=0)) == Position( + line=1, character=0 + ) + assert position_from_utf16(lines, Position(line=1, character=2)) == Position( + line=1, character=1 + ) + assert position_from_utf16(lines, Position(line=1, character=3)) == Position( + line=1, character=2 + ) + assert position_from_utf16(lines, Position(line=1, character=4)) == Position( + line=1, character=2 + ) + assert position_from_utf16(lines, Position(line=1, character=100)) == Position( + line=1, character=2 + ) + assert position_from_utf16(lines, Position(line=3, character=0)) == Position( + line=2, character=0 + ) + assert position_from_utf16(lines, Position(line=4, character=10)) == Position( + line=2, character=0 + ) + def test_position_for_line_endings(doc): - lines = ['x\r\n', 'y\n'] - assert position_from_utf16(lines, Position(line=0, character=10)) == Position(line=0, character=1) - assert position_from_utf16(lines, Position(line=1, character=10)) == Position(line=1, character=1) + lines = ["x\r\n", "y\n"] + assert position_from_utf16(lines, Position(line=0, character=10)) == Position( + line=0, character=1 + ) + assert position_from_utf16(lines, Position(line=1, character=10)) == Position( + line=1, character=1 + ) + def test_word_at_position(doc): """ diff --git a/tests/test_feature_manager.py b/tests/test_feature_manager.py index c5665386..24856c7f 100644 --- a/tests/test_feature_manager.py +++ b/tests/test_feature_manager.py @@ -33,6 +33,7 @@ from typeguard import TypeCheckError from typeguard._utils import qualified_name + class Temp: pass @@ -53,7 +54,6 @@ def f3(temp: "Temp", a, b, c): def test_register_command_validation_error(feature_manager): - with pytest.raises(ValidationError): @feature_manager.command(" \n\t") @@ -106,7 +106,7 @@ class Options: with pytest.raises( TypeCheckError, match=( - f'{qualified_name(Options)} is not an instance of ' + f"{qualified_name(Options)} is not an instance of " "lsprotocol.types.CompletionOptions" ), # noqa ): @@ -135,7 +135,6 @@ def code_lens(): def test_register_same_command_twice_error(feature_manager): - with pytest.raises(CommandAlreadyRegisteredError): @feature_manager.command("cmd1") @@ -148,7 +147,6 @@ def cmd2(): # pylint: disable=unused-variable def test_register_same_feature_twice_error(feature_manager): - with pytest.raises(FeatureAlreadyRegisteredError): @feature_manager.feature(lsp.TEXT_DOCUMENT_CODE_ACTION) @@ -195,7 +193,6 @@ def f(ls): assert wrapped.execute_in_thread is True - def server_capabilities(**kwargs): """Helper to reduce the amount of boilerplate required to specify the expected server capabilities by filling in some fields - unless they are explicitly @@ -203,18 +200,19 @@ def server_capabilities(**kwargs): if "text_document_sync" not in kwargs: kwargs["text_document_sync"] = lsp.TextDocumentSyncOptions( - open_close=False, save=False, + open_close=False, + save=False, ) if "execute_command_provider" not in kwargs: - kwargs["execute_command_provider" ] = lsp.ExecuteCommandOptions(commands=[]) + kwargs["execute_command_provider"] = lsp.ExecuteCommandOptions(commands=[]) if "workspace" not in kwargs: kwargs["workspace"] = lsp.ServerCapabilitiesWorkspaceType( workspace_folders=lsp.WorkspaceFoldersServerCapabilities( supported=True, change_notifications=True ), - file_operations=lsp.FileOperationOptions() + file_operations=lsp.FileOperationOptions(), ) return lsp.ServerCapabilities(**kwargs) @@ -229,10 +227,9 @@ def server_capabilities(**kwargs): lsp.ClientCapabilities(), server_capabilities( text_document_sync=lsp.TextDocumentSyncOptions( - open_close=False, - save=lsp.SaveOptions(include_text=True) + open_close=False, save=lsp.SaveOptions(include_text=True) ) - ) + ), ), ( lsp.TEXT_DOCUMENT_DID_SAVE, @@ -242,7 +239,7 @@ def server_capabilities(**kwargs): text_document_sync=lsp.TextDocumentSyncOptions( open_close=False, save=True ) - ) + ), ), ( lsp.TEXT_DOCUMENT_WILL_SAVE, @@ -252,7 +249,7 @@ def server_capabilities(**kwargs): text_document_sync=lsp.TextDocumentSyncOptions( open_close=False, save=False ) - ) + ), ), ( lsp.TEXT_DOCUMENT_WILL_SAVE, @@ -268,7 +265,7 @@ def server_capabilities(**kwargs): text_document_sync=lsp.TextDocumentSyncOptions( open_close=False, save=False, will_save=True ) - ) + ), ), ( lsp.TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL, @@ -284,7 +281,7 @@ def server_capabilities(**kwargs): text_document_sync=lsp.TextDocumentSyncOptions( open_close=False, save=False, will_save_wait_until=True ) - ) + ), ), ( lsp.TEXT_DOCUMENT_DID_OPEN, @@ -294,7 +291,7 @@ def server_capabilities(**kwargs): text_document_sync=lsp.TextDocumentSyncOptions( open_close=True, save=False ) - ) + ), ), ( lsp.TEXT_DOCUMENT_DID_CLOSE, @@ -312,7 +309,7 @@ def server_capabilities(**kwargs): lsp.ClientCapabilities(), server_capabilities( inlay_hint_provider=lsp.InlayHintOptions(resolve_provider=False), - ) + ), ), ( lsp.WORKSPACE_WILL_CREATE_FILES, @@ -324,7 +321,7 @@ def server_capabilities(**kwargs): ] ), lsp.ClientCapabilities(), - server_capabilities() + server_capabilities(), ), ( lsp.WORKSPACE_WILL_CREATE_FILES, @@ -357,7 +354,7 @@ def server_capabilities(**kwargs): ) ), ) - ) + ), ), ( lsp.WORKSPACE_DID_CREATE_FILES, @@ -369,7 +366,7 @@ def server_capabilities(**kwargs): ] ), lsp.ClientCapabilities(), - server_capabilities() + server_capabilities(), ), ( lsp.WORKSPACE_DID_CREATE_FILES, @@ -382,9 +379,7 @@ def server_capabilities(**kwargs): ), lsp.ClientCapabilities( workspace=lsp.WorkspaceClientCapabilities( - file_operations=lsp.FileOperationClientCapabilities( - did_create=True - ) + file_operations=lsp.FileOperationClientCapabilities(did_create=True) ) ), server_capabilities( @@ -402,7 +397,7 @@ def server_capabilities(**kwargs): ) ), ) - ) + ), ), ( lsp.WORKSPACE_WILL_DELETE_FILES, @@ -414,7 +409,7 @@ def server_capabilities(**kwargs): ] ), lsp.ClientCapabilities(), - server_capabilities() + server_capabilities(), ), ( lsp.WORKSPACE_WILL_DELETE_FILES, @@ -447,7 +442,7 @@ def server_capabilities(**kwargs): ) ), ) - ) + ), ), ( lsp.WORKSPACE_DID_DELETE_FILES, @@ -459,7 +454,7 @@ def server_capabilities(**kwargs): ] ), lsp.ClientCapabilities(), - server_capabilities() + server_capabilities(), ), ( lsp.WORKSPACE_DID_DELETE_FILES, @@ -472,9 +467,7 @@ def server_capabilities(**kwargs): ), lsp.ClientCapabilities( workspace=lsp.WorkspaceClientCapabilities( - file_operations=lsp.FileOperationClientCapabilities( - did_delete=True - ) + file_operations=lsp.FileOperationClientCapabilities(did_delete=True) ) ), server_capabilities( @@ -492,7 +485,7 @@ def server_capabilities(**kwargs): ) ), ) - ) + ), ), ( lsp.WORKSPACE_WILL_RENAME_FILES, @@ -504,7 +497,7 @@ def server_capabilities(**kwargs): ] ), lsp.ClientCapabilities(), - server_capabilities() + server_capabilities(), ), ( lsp.WORKSPACE_WILL_RENAME_FILES, @@ -537,7 +530,7 @@ def server_capabilities(**kwargs): ) ), ) - ) + ), ), ( lsp.WORKSPACE_DID_RENAME_FILES, @@ -549,7 +542,7 @@ def server_capabilities(**kwargs): ] ), lsp.ClientCapabilities(), - server_capabilities() + server_capabilities(), ), ( lsp.WORKSPACE_DID_RENAME_FILES, @@ -562,9 +555,7 @@ def server_capabilities(**kwargs): ), lsp.ClientCapabilities( workspace=lsp.WorkspaceClientCapabilities( - file_operations=lsp.FileOperationClientCapabilities( - did_rename=True - ) + file_operations=lsp.FileOperationClientCapabilities(did_rename=True) ) ), server_capabilities( @@ -582,9 +573,9 @@ def server_capabilities(**kwargs): ) ), ) - ) - ) - ] + ), + ), + ], ) def test_register_feature( feature_manager: FeatureManager, @@ -614,6 +605,7 @@ def test_register_feature( expected The expected server capabilties we are expecting to see. """ + @feature_manager.feature(method, options) def _(): pass @@ -632,7 +624,6 @@ def _(): def test_register_inlay_hint_resolve( feature_manager: FeatureManager, ): - @feature_manager.feature(lsp.TEXT_DOCUMENT_INLAY_HINT) def _(): pass diff --git a/tests/test_language_server.py b/tests/test_language_server.py index 16a5854e..10acce62 100644 --- a/tests/test_language_server.py +++ b/tests/test_language_server.py @@ -91,7 +91,7 @@ def test_bf_text_document_did_open(client_server): assert document.language_id == "python" -@pytest.mark.skipif(IS_PYODIDE, reason='threads are not available in pyodide.') +@pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.") def test_command_async(client_server): client, server = client_server @@ -103,7 +103,7 @@ def test_command_async(client_server): assert thread_id == server.thread_id -@pytest.mark.skipif(IS_PYODIDE, reason='threads are not available in pyodide.') +@pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.") def test_command_sync(client_server): client, server = client_server @@ -115,7 +115,7 @@ def test_command_sync(client_server): assert thread_id == server.thread_id -@pytest.mark.skipif(IS_PYODIDE, reason='threads are not available in pyodide.') +@pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.") def test_command_thread(client_server): client, server = client_server @@ -131,7 +131,7 @@ def test_allow_custom_protocol_derived_from_lsp(): class CustomProtocol(LanguageServerProtocol): pass - server = LanguageServer('pygls-test', 'v1', protocol_cls=CustomProtocol) + server = LanguageServer("pygls-test", "v1", protocol_cls=CustomProtocol) assert isinstance(server.lsp, CustomProtocol) @@ -141,4 +141,4 @@ class CustomProtocol: pass with pytest.raises(TypeError): - LanguageServer('pygls-test', 'v1', protocol_cls=CustomProtocol) + LanguageServer("pygls-test", "v1", protocol_cls=CustomProtocol) diff --git a/tests/test_protocol.py b/tests/test_protocol.py index b47dde36..63b689fa 100644 --- a/tests/test_protocol.py +++ b/tests/test_protocol.py @@ -46,7 +46,7 @@ JsonRPCProtocol, JsonRPCRequestMessage, JsonRPCResponseMessage, - JsonRPCNotification + JsonRPCNotification, ) EXAMPLE_NOTIFICATION = "example/notification" @@ -57,7 +57,7 @@ class IntResult: id: str result: int - jsonrpc: str = attrs.field(default='2.0') + jsonrpc: str = attrs.field(default="2.0") @attrs.define @@ -72,7 +72,7 @@ class InnerType: @attrs.define class ExampleNotification: - jsonrpc: str = attrs.field(default='2.0') + jsonrpc: str = attrs.field(default="2.0") method: str = EXAMPLE_NOTIFICATION params: ExampleParams = attrs.field(default=None) @@ -80,7 +80,7 @@ class ExampleNotification: @attrs.define class ExampleRequest: id: str - jsonrpc: str = attrs.field(default='2.0') + jsonrpc: str = attrs.field(default="2.0") method: str = EXAMPLE_REQUEST params: ExampleParams = attrs.field(default=None) @@ -92,7 +92,6 @@ class ExampleRequest: class ExampleProtocol(JsonRPCProtocol): - def get_message_type(self, method: str): return EXAMPLE_LSP_METHODS_MAP.get(method, (None,))[0] @@ -118,7 +117,9 @@ def test_deserialize_notification_message_valid_params(protocol): result = json.loads(params, object_hook=protocol._deserialize_message) - assert isinstance(result, ExampleNotification), f"Expected FeatureRequest instance, got {result}" + assert isinstance( + result, ExampleNotification + ), f"Expected FeatureRequest instance, got {result}" assert result.jsonrpc == "2.0" assert result.method == EXAMPLE_NOTIFICATION @@ -179,6 +180,7 @@ def test_deserialize_response_message_custom_converter(): "result": "1" } """ + # Just for fun, let's create a converter that reverses all the keys in a dict. # @attrs.define @@ -237,9 +239,7 @@ def custom_converter(): EXAMPLE_NOTIFICATION, ExampleParams( field_a="field one", - field_b=ExampleParams.InnerType( - inner_field="field two" - ), + field_b=ExampleParams.InnerType(inner_field="field two"), ), { "jsonrpc": "2.0", @@ -255,12 +255,7 @@ def custom_converter(): ( # Custom notification with dict params. EXAMPLE_NOTIFICATION, - { - "fieldA": "field one", - "fieldB": { - "innerField": "field two" - } - }, + {"fieldA": "field one", "fieldB": {"innerField": "field two"}}, { "jsonrpc": "2.0", "method": EXAMPLE_NOTIFICATION, @@ -271,7 +266,7 @@ def custom_converter(): }, }, }, - ) + ), ], ) def test_serialize_notification_message(method, params, expected): @@ -415,42 +410,33 @@ def test_deserialize_request_message_without_registered_type(protocol): ], }, ), - ( # Unknown type with object params. + ( # Unknown type with object params. JsonRPCResponseMessage, ExampleParams( field_a="field one", - field_b=ExampleParams.InnerType(inner_field="field two") + field_b=ExampleParams.InnerType(inner_field="field two"), ), { "jsonrpc": "2.0", "id": "1", "result": { "fieldA": "field one", - "fieldB": { - "innerField": "field two" - } + "fieldB": {"innerField": "field two"}, }, - } + }, ), - ( # Unknown type with dict params. + ( # Unknown type with dict params. JsonRPCResponseMessage, - { - "fieldA": "field one", - "fieldB": { - "innerField": "field two" - } - }, + {"fieldA": "field one", "fieldB": {"innerField": "field two"}}, { "jsonrpc": "2.0", "id": "1", "result": { "fieldA": "field one", - "fieldB": { - "innerField": "field two" - } + "fieldB": {"innerField": "field two"}, }, - } - ) + }, + ), ], ) def test_serialize_response_message(msg_type, result, expected): @@ -480,23 +466,23 @@ def test_serialize_response_message(msg_type, result, expected): TEXT_DOCUMENT_COMPLETION, CompletionParams( text_document=TextDocumentIdentifier(uri="file:///file.txt"), - position=Position(line=1, character=0) + position=Position(line=1, character=0), ), { "jsonrpc": "2.0", "id": "1", "method": TEXT_DOCUMENT_COMPLETION, "params": { - "textDocument": { "uri": "file:///file.txt" }, - "position": { "line": 1, "character": 0 } + "textDocument": {"uri": "file:///file.txt"}, + "position": {"line": 1, "character": 0}, }, }, ), - ( # Unknown type with object params. + ( # Unknown type with object params. EXAMPLE_REQUEST, ExampleParams( field_a="field one", - field_b=ExampleParams.InnerType(inner_field="field two") + field_b=ExampleParams.InnerType(inner_field="field two"), ), { "jsonrpc": "2.0", @@ -504,32 +490,23 @@ def test_serialize_response_message(msg_type, result, expected): "method": EXAMPLE_REQUEST, "params": { "fieldA": "field one", - "fieldB": { - "innerField": "field two" - } + "fieldB": {"innerField": "field two"}, }, - } + }, ), - ( # Unknown type with dict params. + ( # Unknown type with dict params. EXAMPLE_REQUEST, - { - "fieldA": "field one", - "fieldB": { - "innerField": "field two" - } - }, + {"fieldA": "field one", "fieldB": {"innerField": "field two"}}, { "jsonrpc": "2.0", "id": "1", "method": EXAMPLE_REQUEST, "params": { "fieldA": "field one", - "fieldB": { - "innerField": "field two" - } + "fieldB": {"innerField": "field two"}, }, - } - ) + }, + ), ], ) def test_serialize_request_message(method, params, expected): diff --git a/tests/test_server_connection.py b/tests/test_server_connection.py index dfce294d..1fda258b 100644 --- a/tests/test_server_connection.py +++ b/tests/test_server_connection.py @@ -18,11 +18,11 @@ @pytest.mark.asyncio -@pytest.mark.skipif(IS_PYODIDE, reason='threads are not available in pyodide.') +@pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.") async def test_tcp_connection_lost(): loop = asyncio.new_event_loop() - server = LanguageServer('pygls-test', 'v1', loop=loop) + server = LanguageServer("pygls-test", "v1", loop=loop) server.lsp.connection_made = Mock() server.lsp.connection_lost = Mock() @@ -57,21 +57,17 @@ async def test_tcp_connection_lost(): @pytest.mark.asyncio -@pytest.mark.skipif(IS_PYODIDE, reason='threads are not available in pyodide.') +@pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.") async def test_io_connection_lost(): # Client to Server pipe. csr, csw = os.pipe() # Server to client pipe. scr, scw = os.pipe() - server = LanguageServer('pygls-test', 'v1', loop=asyncio.new_event_loop()) + server = LanguageServer("pygls-test", "v1", loop=asyncio.new_event_loop()) server.lsp.connection_made = Mock() server_thread = Thread( - target=server.start_io, - args=( - os.fdopen(csr, "rb"), - os.fdopen(scw, "wb") - ) + target=server.start_io, args=(os.fdopen(csr, "rb"), os.fdopen(scw, "wb")) ) server_thread.daemon = True server_thread.start() @@ -94,7 +90,7 @@ async def test_ws_server(): """Smoke test to ensure we can send/receive messages over websockets""" loop = asyncio.new_event_loop() - server = LanguageServer('pygls-test', 'v1', loop=loop) + server = LanguageServer("pygls-test", "v1", loop=loop) # Run the server over Websockets in a separate thread server_thread = Thread( @@ -114,7 +110,6 @@ async def test_ws_server(): port = server._server.sockets[0].getsockname()[1] # Simulate client's connection async with websockets.connect(f"ws://127.0.0.1:{port}") as connection: - # Send an 'initialize' request msg = dict( jsonrpc="2.0", id=1, method="initialize", params=dict(capabilities=dict()) diff --git a/tests/test_types.py b/tests/test_types.py index b11c93ae..09584936 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -31,22 +31,16 @@ def test_position(): def test_range(): assert Range( start=Position(line=1, character=2), end=Position(line=3, character=4) - ) == Range( - start=Position(line=1, character=2), end=Position(line=3, character=4) - ) + ) == Range(start=Position(line=1, character=2), end=Position(line=3, character=4)) assert Range( start=Position(line=0, character=2), end=Position(line=3, character=4) - ) != Range( - start=Position(line=1, character=2), end=Position(line=3, character=4) - ) + ) != Range(start=Position(line=1, character=2), end=Position(line=3, character=4)) assert ( - Range(start=Position(line=0, character=2), - end=Position(line=3, character=4)) + Range(start=Position(line=0, character=2), end=Position(line=3, character=4)) != "something else" ) assert "1:2-3:4" == repr( - Range(start=Position(line=1, character=2), - end=Position(line=3, character=4)) + Range(start=Position(line=1, character=2), end=Position(line=3, character=4)) ) @@ -54,35 +48,30 @@ def test_location(): assert Location( uri="file:///document.txt", range=Range( - start=Position(line=1, character=2), - end=Position(line=3, character=4) + start=Position(line=1, character=2), end=Position(line=3, character=4) ), ) == Location( uri="file:///document.txt", range=Range( - start=Position(line=1, character=2), - end=Position(line=3, character=4) + start=Position(line=1, character=2), end=Position(line=3, character=4) ), ) assert Location( uri="file:///document.txt", range=Range( - start=Position(line=1, character=2), - end=Position(line=3, character=4) + start=Position(line=1, character=2), end=Position(line=3, character=4) ), ) != Location( uri="file:///another.txt", range=Range( - start=Position(line=1, character=2), - end=Position(line=3, character=4) + start=Position(line=1, character=2), end=Position(line=3, character=4) ), ) assert ( Location( uri="file:///document.txt", range=Range( - start=Position(line=1, character=2), - end=Position(line=3, character=4) + start=Position(line=1, character=2), end=Position(line=3, character=4) ), ) != "something else" @@ -91,8 +80,7 @@ def test_location(): Location( uri="file:///document.txt", range=Range( - start=Position(line=1, character=2), - end=Position(line=3, character=4) + start=Position(line=1, character=2), end=Position(line=3, character=4) ), ) ) diff --git a/tests/test_workspace.py b/tests/test_workspace.py index 4d7051f3..5f9f09a4 100644 --- a/tests/test_workspace.py +++ b/tests/test_workspace.py @@ -24,12 +24,7 @@ DOC_URI = uris.from_fs_path(__file__) DOC_TEXT = """test""" -DOC = TextDocumentItem( - uri=DOC_URI, - language_id="plaintext", - version=0, - text=DOC_TEXT -) +DOC = TextDocumentItem(uri=DOC_URI, language_id="plaintext", version=0, text=DOC_TEXT) def test_add_folder(workspace):