summaryrefslogtreecommitdiffstats
path: root/tests/lsp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/lsp')
-rw-r--r--tests/lsp/__init__.py0
-rw-r--r--tests/lsp/semantic_tokens/__init__.py0
-rw-r--r--tests/lsp/semantic_tokens/test_delta_missing_legend.py92
-rw-r--r--tests/lsp/semantic_tokens/test_delta_missing_legend_none.py48
-rw-r--r--tests/lsp/semantic_tokens/test_full_missing_legend.py46
-rw-r--r--tests/lsp/semantic_tokens/test_range.py103
-rw-r--r--tests/lsp/semantic_tokens/test_range_missing_legends.py47
-rw-r--r--tests/lsp/semantic_tokens/test_semantic_tokens_full.py93
-rw-r--r--tests/lsp/test_call_hierarchy.py192
-rw-r--r--tests/lsp/test_code_action.py60
-rw-r--r--tests/lsp/test_code_lens.py94
-rw-r--r--tests/lsp/test_color_presentation.py103
-rw-r--r--tests/lsp/test_completion.py48
-rw-r--r--tests/lsp/test_declaration.py161
-rw-r--r--tests/lsp/test_definition.py164
-rw-r--r--tests/lsp/test_diagnostics.py68
-rw-r--r--tests/lsp/test_document_color.py81
-rw-r--r--tests/lsp/test_document_highlight.py108
-rw-r--r--tests/lsp/test_document_link.py98
-rw-r--r--tests/lsp/test_document_symbol.py168
-rw-r--r--tests/lsp/test_errors.py135
-rw-r--r--tests/lsp/test_folding_range.py92
-rw-r--r--tests/lsp/test_formatting.py108
-rw-r--r--tests/lsp/test_hover.py149
-rw-r--r--tests/lsp/test_implementation.py164
-rw-r--r--tests/lsp/test_inlay_hints.py56
-rw-r--r--tests/lsp/test_inline_value.py60
-rw-r--r--tests/lsp/test_linked_editing_range.py103
-rw-r--r--tests/lsp/test_moniker.py94
-rw-r--r--tests/lsp/test_on_type_formatting.py122
-rw-r--r--tests/lsp/test_prepare_rename.py111
-rw-r--r--tests/lsp/test_progress.py225
-rw-r--r--tests/lsp/test_range_formatting.py116
-rw-r--r--tests/lsp/test_references.py103
-rw-r--r--tests/lsp/test_rename.py195
-rw-r--r--tests/lsp/test_selection_range.py110
-rw-r--r--tests/lsp/test_signature_help.py161
-rw-r--r--tests/lsp/test_type_definition.py163
-rw-r--r--tests/lsp/test_type_hierarchy.py127
39 files changed, 4168 insertions, 0 deletions
diff --git a/tests/lsp/__init__.py b/tests/lsp/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/lsp/__init__.py
diff --git a/tests/lsp/semantic_tokens/__init__.py b/tests/lsp/semantic_tokens/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/lsp/semantic_tokens/__init__.py
diff --git a/tests/lsp/semantic_tokens/test_delta_missing_legend.py b/tests/lsp/semantic_tokens/test_delta_missing_legend.py
new file mode 100644
index 0000000..a3069da
--- /dev/null
+++ b/tests/lsp/semantic_tokens/test_delta_missing_legend.py
@@ -0,0 +1,92 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+from typing import Optional, Union
+
+from lsprotocol.types import (
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA,
+)
+from lsprotocol.types import (
+ SemanticTokens,
+ SemanticTokensDeltaParams,
+ SemanticTokensLegend,
+ SemanticTokensPartialResult,
+ SemanticTokensOptionsFullType1,
+ TextDocumentIdentifier,
+)
+
+from ...conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA,
+ SemanticTokensLegend(
+ token_types=["keyword", "operator"], token_modifiers=["readonly"]
+ ),
+ )
+ def f(
+ params: SemanticTokensDeltaParams,
+ ) -> Union[SemanticTokensPartialResult, Optional[SemanticTokens]]:
+ if params.text_document.uri == "file://return.tokens":
+ return SemanticTokens(data=[0, 0, 3, 0, 0])
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ provider = capabilities.semantic_tokens_provider
+ assert provider.full == SemanticTokensOptionsFullType1(delta=True)
+ assert provider.legend.token_types == [
+ "keyword",
+ "operator",
+ ]
+ assert provider.legend.token_modifiers == ["readonly"]
+
+
+@ConfiguredLS.decorate()
+def test_semantic_tokens_full_delta_return_tokens(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA,
+ SemanticTokensDeltaParams(
+ text_document=TextDocumentIdentifier(uri="file://return.tokens"),
+ previous_result_id="id",
+ ),
+ ).result()
+
+ assert response
+
+ assert response.data == [0, 0, 3, 0, 0]
+
+
+@ConfiguredLS.decorate()
+def test_semantic_tokens_full_delta_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA,
+ SemanticTokensDeltaParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ previous_result_id="id",
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/semantic_tokens/test_delta_missing_legend_none.py b/tests/lsp/semantic_tokens/test_delta_missing_legend_none.py
new file mode 100644
index 0000000..6f4fa17
--- /dev/null
+++ b/tests/lsp/semantic_tokens/test_delta_missing_legend_none.py
@@ -0,0 +1,48 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+from typing import Optional, Union
+
+from lsprotocol.types import (
+ SemanticTokens,
+ SemanticTokensDeltaParams,
+ SemanticTokensPartialResult,
+)
+from lsprotocol.types import (
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA,
+)
+
+from ...conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA)
+ def f(
+ params: SemanticTokensDeltaParams,
+ ) -> Union[SemanticTokensPartialResult, Optional[SemanticTokens]]:
+ return SemanticTokens(data=[0, 0, 3, 0, 0])
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.semantic_tokens_provider is None
+ assert capabilities.semantic_tokens_provider is None
diff --git a/tests/lsp/semantic_tokens/test_full_missing_legend.py b/tests/lsp/semantic_tokens/test_full_missing_legend.py
new file mode 100644
index 0000000..e18dbde
--- /dev/null
+++ b/tests/lsp/semantic_tokens/test_full_missing_legend.py
@@ -0,0 +1,46 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+from typing import Optional, Union
+
+from lsprotocol.types import (
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
+)
+from lsprotocol.types import (
+ SemanticTokens,
+ SemanticTokensPartialResult,
+ SemanticTokensParams,
+)
+
+from ...conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL)
+ def f(
+ params: SemanticTokensParams,
+ ) -> Union[SemanticTokensPartialResult, Optional[SemanticTokens]]:
+ return SemanticTokens(data=[0, 0, 3, 0, 0])
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+ assert capabilities.semantic_tokens_provider is None
diff --git a/tests/lsp/semantic_tokens/test_range.py b/tests/lsp/semantic_tokens/test_range.py
new file mode 100644
index 0000000..a65504b
--- /dev/null
+++ b/tests/lsp/semantic_tokens/test_range.py
@@ -0,0 +1,103 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+from typing import Optional, Union
+
+from lsprotocol.types import (
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE,
+)
+from lsprotocol.types import (
+ Position,
+ Range,
+ SemanticTokens,
+ SemanticTokensLegend,
+ SemanticTokensPartialResult,
+ SemanticTokensRangeParams,
+ TextDocumentIdentifier,
+)
+
+from ...conftest import ClientServer
+
+SemanticTokenReturnType = Optional[
+ Union[SemanticTokensPartialResult, Optional[SemanticTokens]]
+]
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE,
+ SemanticTokensLegend(
+ token_types=["keyword", "operator"], token_modifiers=["readonly"]
+ ),
+ )
+ def f(
+ params: SemanticTokensRangeParams,
+ ) -> SemanticTokenReturnType:
+ if params.text_document.uri == "file://return.tokens":
+ return SemanticTokens(data=[0, 0, 3, 0, 0])
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ provider = capabilities.semantic_tokens_provider
+ assert provider.range
+ assert provider.legend.token_types == [
+ "keyword",
+ "operator",
+ ]
+ assert provider.legend.token_modifiers == ["readonly"]
+
+
+@ConfiguredLS.decorate()
+def test_semantic_tokens_range_return_tokens(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE,
+ SemanticTokensRangeParams(
+ text_document=TextDocumentIdentifier(uri="file://return.tokens"),
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=10, character=80),
+ ),
+ ),
+ ).result()
+
+ assert response
+
+ assert response.data == [0, 0, 3, 0, 0]
+
+
+@ConfiguredLS.decorate()
+def test_semantic_tokens_range_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE,
+ SemanticTokensRangeParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=10, character=80),
+ ),
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/semantic_tokens/test_range_missing_legends.py b/tests/lsp/semantic_tokens/test_range_missing_legends.py
new file mode 100644
index 0000000..69780ef
--- /dev/null
+++ b/tests/lsp/semantic_tokens/test_range_missing_legends.py
@@ -0,0 +1,47 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+from typing import Optional, Union
+
+from lsprotocol.types import (
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE,
+)
+from lsprotocol.types import (
+ SemanticTokens,
+ SemanticTokensParams,
+ SemanticTokensPartialResult,
+)
+
+from ...conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE)
+ def f(
+ params: SemanticTokensParams,
+ ) -> Union[SemanticTokensPartialResult, Optional[SemanticTokens]]:
+ return SemanticTokens(data=[0, 0, 3, 0, 0])
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.semantic_tokens_provider is None
diff --git a/tests/lsp/semantic_tokens/test_semantic_tokens_full.py b/tests/lsp/semantic_tokens/test_semantic_tokens_full.py
new file mode 100644
index 0000000..dba9fa6
--- /dev/null
+++ b/tests/lsp/semantic_tokens/test_semantic_tokens_full.py
@@ -0,0 +1,93 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+from typing import Optional, Union
+
+from lsprotocol.types import (
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
+)
+from lsprotocol.types import (
+ SemanticTokens,
+ SemanticTokensLegend,
+ SemanticTokensParams,
+ SemanticTokensPartialResult,
+ TextDocumentIdentifier,
+)
+
+from ...conftest import ClientServer
+
+SemanticTokenReturnType = Optional[
+ Union[SemanticTokensPartialResult, Optional[SemanticTokens]]
+]
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
+ SemanticTokensLegend(
+ token_types=["keyword", "operator"], token_modifiers=["readonly"]
+ ),
+ )
+ def f(
+ params: SemanticTokensParams,
+ ) -> SemanticTokenReturnType:
+ if params.text_document.uri == "file://return.tokens":
+ return SemanticTokens(data=[0, 0, 3, 0, 0])
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ provider = capabilities.semantic_tokens_provider
+ assert provider.full
+ assert provider.legend.token_types == [
+ "keyword",
+ "operator",
+ ]
+ assert provider.legend.token_modifiers == ["readonly"]
+
+
+@ConfiguredLS.decorate()
+def test_semantic_tokens_full_return_tokens(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
+ SemanticTokensParams(
+ text_document=TextDocumentIdentifier(uri="file://return.tokens")
+ ),
+ ).result()
+
+ assert response
+
+ assert response.data == [0, 0, 3, 0, 0]
+
+
+@ConfiguredLS.decorate()
+def test_semantic_tokens_full_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
+ SemanticTokensParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none")
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_call_hierarchy.py b/tests/lsp/test_call_hierarchy.py
new file mode 100644
index 0000000..410a982
--- /dev/null
+++ b/tests/lsp/test_call_hierarchy.py
@@ -0,0 +1,192 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+from typing import List, Optional
+
+from lsprotocol.types import (
+ CALL_HIERARCHY_INCOMING_CALLS,
+ CALL_HIERARCHY_OUTGOING_CALLS,
+ TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY,
+)
+from lsprotocol.types import (
+ CallHierarchyIncomingCall,
+ CallHierarchyIncomingCallsParams,
+ CallHierarchyItem,
+ CallHierarchyOptions,
+ CallHierarchyOutgoingCall,
+ CallHierarchyOutgoingCallsParams,
+ CallHierarchyPrepareParams,
+ Position,
+ Range,
+ SymbolKind,
+ SymbolTag,
+ TextDocumentIdentifier,
+)
+
+from ..conftest import ClientServer
+
+CALL_HIERARCHY_ITEM = CallHierarchyItem(
+ name="test_name",
+ kind=SymbolKind.File,
+ uri="test_uri",
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ selection_range=Range(
+ start=Position(line=1, character=1),
+ end=Position(line=2, character=2),
+ ),
+ tags=[SymbolTag.Deprecated],
+ detail="test_detail",
+ data="test_data",
+)
+
+
+def check_call_hierarchy_item_response(item):
+ assert item.name == "test_name"
+ assert item.kind == SymbolKind.File
+ assert item.uri == "test_uri"
+ assert item.range.start.line == 0
+ assert item.range.start.character == 0
+ assert item.range.end.line == 1
+ assert item.range.end.character == 1
+ assert item.selection_range.start.line == 1
+ assert item.selection_range.start.character == 1
+ assert item.selection_range.end.line == 2
+ 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"
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY,
+ CallHierarchyOptions(),
+ )
+ 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,
+ ) -> Optional[List[CallHierarchyIncomingCall]]:
+ return [
+ CallHierarchyIncomingCall(
+ from_=params.item,
+ from_ranges=[
+ Range(
+ start=Position(line=2, character=2),
+ end=Position(line=3, character=3),
+ ),
+ ],
+ ),
+ ]
+
+ @self.server.feature(CALL_HIERARCHY_OUTGOING_CALLS)
+ def f3(
+ params: CallHierarchyOutgoingCallsParams,
+ ) -> Optional[List[CallHierarchyOutgoingCall]]:
+ return [
+ CallHierarchyOutgoingCall(
+ to=params.item,
+ from_ranges=[
+ Range(
+ start=Position(line=3, character=3),
+ end=Position(line=4, character=4),
+ ),
+ ],
+ ),
+ ]
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+ assert capabilities.call_hierarchy_provider
+
+
+@ConfiguredLS.decorate()
+def test_call_hierarchy_prepare_return_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY,
+ CallHierarchyPrepareParams(
+ text_document=TextDocumentIdentifier(uri="file://return.list"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ check_call_hierarchy_item_response(response[0])
+
+
+@ConfiguredLS.decorate()
+def test_call_hierarchy_prepare_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY,
+ CallHierarchyPrepareParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response is None
+
+
+@ConfiguredLS.decorate()
+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),
+ ).result()
+
+ item = response[0]
+
+ check_call_hierarchy_item_response(item.from_)
+
+ assert item.from_ranges[0].start.line == 2
+ assert item.from_ranges[0].start.character == 2
+ assert item.from_ranges[0].end.line == 3
+ assert item.from_ranges[0].end.character == 3
+
+
+@ConfiguredLS.decorate()
+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),
+ ).result()
+
+ item = response[0]
+
+ check_call_hierarchy_item_response(item.to)
+
+ assert item.from_ranges[0].start.line == 3
+ 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
new file mode 100644
index 0000000..c10dcd5
--- /dev/null
+++ b/tests/lsp/test_code_action.py
@@ -0,0 +1,60 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+from typing import Tuple
+
+from lsprotocol import types
+
+from ..client import LanguageClient
+
+
+async def test_code_actions(
+ code_action_client: Tuple[LanguageClient, types.InitializeResult], uri_for
+):
+ """Ensure that the example code action server is working as expected."""
+ client, initialize_result = code_action_client
+
+ code_action_options = initialize_result.capabilities.code_action_provider
+ assert code_action_options.code_action_kinds == [types.CodeActionKind.QuickFix]
+
+ test_uri = uri_for("sums.txt")
+ assert test_uri is not None
+
+ response = await client.text_document_code_action_async(
+ types.CodeActionParams(
+ text_document=types.TextDocumentIdentifier(uri=test_uri),
+ range=types.Range(
+ start=types.Position(line=0, character=0),
+ end=types.Position(line=1, character=0),
+ ),
+ context=types.CodeActionContext(diagnostics=[]),
+ )
+ )
+
+ assert len(response) == 1
+ code_action = response[0]
+
+ assert code_action.title == "Evaluate '1 + 1 ='"
+ assert code_action.kind == types.CodeActionKind.QuickFix
+
+ fix = code_action.edit.changes[test_uri][0]
+ expected_range = types.Range(
+ start=types.Position(line=0, character=0),
+ end=types.Position(line=0, character=7),
+ )
+
+ 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
new file mode 100644
index 0000000..7024110
--- /dev/null
+++ b/tests/lsp/test_code_lens.py
@@ -0,0 +1,94 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+from typing import List, Optional
+
+from lsprotocol.types import TEXT_DOCUMENT_CODE_LENS
+from lsprotocol.types import (
+ CodeLens,
+ CodeLensOptions,
+ CodeLensParams,
+ Command,
+ Position,
+ Range,
+ TextDocumentIdentifier,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_CODE_LENS,
+ CodeLensOptions(resolve_provider=False),
+ )
+ def f(params: CodeLensParams) -> Optional[List[CodeLens]]:
+ if params.text_document.uri == "file://return.list":
+ return [
+ CodeLens(
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ command=Command(
+ title="cmd1",
+ command="cmd1",
+ ),
+ data="some data",
+ ),
+ ]
+ else:
+ return None
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.code_lens_provider
+ assert not capabilities.code_lens_provider.resolve_provider
+
+
+@ConfiguredLS.decorate()
+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")),
+ ).result()
+
+ assert response[0].data == "some data"
+ assert response[0].range.start.line == 0
+ assert response[0].range.start.character == 0
+ assert response[0].range.end.line == 1
+ assert response[0].range.end.character == 1
+ assert response[0].command.title == "cmd1"
+ assert response[0].command.command == "cmd1"
+
+
+@ConfiguredLS.decorate()
+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")),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_color_presentation.py b/tests/lsp/test_color_presentation.py
new file mode 100644
index 0000000..6748e66
--- /dev/null
+++ b/tests/lsp/test_color_presentation.py
@@ -0,0 +1,103 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import List
+
+from lsprotocol.types import TEXT_DOCUMENT_COLOR_PRESENTATION
+from lsprotocol.types import (
+ Color,
+ ColorPresentation,
+ ColorPresentationParams,
+ Position,
+ Range,
+ TextDocumentIdentifier,
+ TextEdit,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(TEXT_DOCUMENT_COLOR_PRESENTATION)
+ def f(params: ColorPresentationParams) -> List[ColorPresentation]:
+ return [
+ ColorPresentation(
+ label="label1",
+ text_edit=TextEdit(
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ new_text="te",
+ ),
+ additional_text_edits=[
+ TextEdit(
+ range=Range(
+ start=Position(line=1, character=1),
+ end=Position(line=2, character=2),
+ ),
+ new_text="ate1",
+ ),
+ TextEdit(
+ range=Range(
+ start=Position(line=2, character=2),
+ end=Position(line=3, character=3),
+ ),
+ new_text="ate2",
+ ),
+ ],
+ )
+ ]
+
+
+@ConfiguredLS.decorate()
+def test_color_presentation(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_COLOR_PRESENTATION,
+ ColorPresentationParams(
+ text_document=TextDocumentIdentifier(uri="file://return.list"),
+ color=Color(red=0.6, green=0.2, blue=0.3, alpha=0.5),
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=3, character=3),
+ ),
+ ),
+ ).result()
+
+ assert response[0].label == "label1"
+ assert response[0].text_edit.new_text == "te"
+
+ assert response[0].text_edit.range.start.line == 0
+ assert response[0].text_edit.range.start.character == 0
+ assert response[0].text_edit.range.end.line == 1
+ assert response[0].text_edit.range.end.character == 1
+
+ range = response[0].additional_text_edits[0].range
+ assert range.start.line == 1
+ assert range.start.character == 1
+ assert range.end.line == 2
+ assert range.end.character == 2
+
+ range = response[0].additional_text_edits[1].range
+ assert range.start.line == 2
+ assert range.start.character == 2
+ assert range.end.line == 3
+ assert range.end.character == 3
diff --git a/tests/lsp/test_completion.py b/tests/lsp/test_completion.py
new file mode 100644
index 0000000..dcb8124
--- /dev/null
+++ b/tests/lsp/test_completion.py
@@ -0,0 +1,48 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+from typing import Tuple
+
+from lsprotocol import types
+
+
+from ..client import LanguageClient
+
+
+async def test_completion(
+ json_server_client: Tuple[LanguageClient, types.InitializeResult],
+ uri_for,
+):
+ """Ensure that the completion methods are working as expected."""
+ client, initialize_result = json_server_client
+
+ completion_provider = initialize_result.capabilities.completion_provider
+ assert completion_provider
+ assert completion_provider.trigger_characters == [","]
+ assert completion_provider.all_commit_characters == [":"]
+
+ test_uri = uri_for("example.json")
+ assert test_uri is not None
+
+ response = await client.text_document_completion_async(
+ types.CompletionParams(
+ text_document=types.TextDocumentIdentifier(uri=test_uri),
+ position=types.Position(line=0, character=0),
+ )
+ )
+
+ labels = {i.label for i in response.items}
+ assert labels == set(['"', "[", "]", "{", "}"])
diff --git a/tests/lsp/test_declaration.py b/tests/lsp/test_declaration.py
new file mode 100644
index 0000000..221982d
--- /dev/null
+++ b/tests/lsp/test_declaration.py
@@ -0,0 +1,161 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import List, Optional, Union
+
+from lsprotocol.types import TEXT_DOCUMENT_DECLARATION
+from lsprotocol.types import (
+ DeclarationOptions,
+ DeclarationParams,
+ Location,
+ LocationLink,
+ Position,
+ Range,
+ TextDocumentIdentifier,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(TEXT_DOCUMENT_DECLARATION, DeclarationOptions())
+ def f(
+ params: DeclarationParams,
+ ) -> Optional[Union[Location, List[Location], List[LocationLink]]]:
+ location = Location(
+ uri="uri",
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ )
+
+ location_link = LocationLink(
+ target_uri="uri",
+ target_range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ target_selection_range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=2, character=2),
+ ),
+ origin_selection_range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=3, character=3),
+ ),
+ )
+
+ return { # type: ignore
+ "file://return.location": location,
+ "file://return.location_list": [location],
+ "file://return.location_link_list": [location_link],
+ }.get(params.text_document.uri, None)
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.declaration_provider
+
+
+@ConfiguredLS.decorate()
+def test_declaration_return_location(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_DECLARATION,
+ DeclarationParams(
+ text_document=TextDocumentIdentifier(uri="file://return.location"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response.uri == "uri"
+
+ assert response.range.start.line == 0
+ assert response.range.start.character == 0
+ assert response.range.end.line == 1
+ assert response.range.end.character == 1
+
+
+@ConfiguredLS.decorate()
+def test_declaration_return_location_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_DECLARATION,
+ DeclarationParams(
+ text_document=TextDocumentIdentifier(uri="file://return.location_list"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response[0].uri == "uri"
+
+ assert response[0].range.start.line == 0
+ assert response[0].range.start.character == 0
+ assert response[0].range.end.line == 1
+ assert response[0].range.end.character == 1
+
+
+@ConfiguredLS.decorate()
+def test_declaration_return_location_link_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_DECLARATION,
+ DeclarationParams(
+ text_document=TextDocumentIdentifier(
+ uri="file://return.location_link_list"
+ ),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response[0].target_uri == "uri"
+
+ assert response[0].target_range.start.line == 0
+ assert response[0].target_range.start.character == 0
+ assert response[0].target_range.end.line == 1
+ assert response[0].target_range.end.character == 1
+
+ assert response[0].target_selection_range.start.line == 0
+ assert response[0].target_selection_range.start.character == 0
+ assert response[0].target_selection_range.end.line == 2
+ assert response[0].target_selection_range.end.character == 2
+
+ assert response[0].origin_selection_range.start.line == 0
+ assert response[0].origin_selection_range.start.character == 0
+ assert response[0].origin_selection_range.end.line == 3
+ assert response[0].origin_selection_range.end.character == 3
+
+
+@ConfiguredLS.decorate()
+def test_declaration_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_DECLARATION,
+ DeclarationParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_definition.py b/tests/lsp/test_definition.py
new file mode 100644
index 0000000..3ed2f96
--- /dev/null
+++ b/tests/lsp/test_definition.py
@@ -0,0 +1,164 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import List, Optional, Union
+
+from lsprotocol.types import TEXT_DOCUMENT_DEFINITION
+from lsprotocol.types import (
+ DefinitionOptions,
+ DefinitionParams,
+ Location,
+ LocationLink,
+ Position,
+ Range,
+ TextDocumentIdentifier,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_DEFINITION,
+ DefinitionOptions(),
+ )
+ def f(
+ params: DefinitionParams,
+ ) -> Optional[Union[Location, List[Location], List[LocationLink]]]:
+ location = Location(
+ uri="uri",
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ )
+
+ location_link = LocationLink(
+ target_uri="uri",
+ target_range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ target_selection_range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=2, character=2),
+ ),
+ origin_selection_range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=3, character=3),
+ ),
+ )
+
+ return { # type: ignore
+ "file://return.location": location,
+ "file://return.location_list": [location],
+ "file://return.location_link_list": [location_link],
+ }.get(params.text_document.uri, None)
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.definition_provider is not None
+
+
+@ConfiguredLS.decorate()
+def test_definition_return_location(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_DEFINITION,
+ DefinitionParams(
+ text_document=TextDocumentIdentifier(uri="file://return.location"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response.uri == "uri"
+
+ assert response.range.start.line == 0
+ assert response.range.start.character == 0
+ assert response.range.end.line == 1
+ assert response.range.end.character == 1
+
+
+@ConfiguredLS.decorate()
+def test_definition_return_location_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_DEFINITION,
+ DefinitionParams(
+ text_document=TextDocumentIdentifier(uri="file://return.location_list"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response[0].uri == "uri"
+
+ assert response[0].range.start.line == 0
+ assert response[0].range.start.character == 0
+ assert response[0].range.end.line == 1
+ assert response[0].range.end.character == 1
+
+
+@ConfiguredLS.decorate()
+def test_definition_return_location_link_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_DEFINITION,
+ DefinitionParams(
+ text_document=TextDocumentIdentifier(
+ uri="file://return.location_link_list"
+ ),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response[0].target_uri == "uri"
+
+ assert response[0].target_range.start.line == 0
+ assert response[0].target_range.start.character == 0
+ assert response[0].target_range.end.line == 1
+ assert response[0].target_range.end.character == 1
+
+ assert response[0].target_selection_range.start.line == 0
+ assert response[0].target_selection_range.start.character == 0
+ assert response[0].target_selection_range.end.line == 2
+ assert response[0].target_selection_range.end.character == 2
+
+ assert response[0].origin_selection_range.start.line == 0
+ assert response[0].origin_selection_range.start.character == 0
+ assert response[0].origin_selection_range.end.line == 3
+ assert response[0].origin_selection_range.end.character == 3
+
+
+@ConfiguredLS.decorate()
+def test_definition_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_DEFINITION,
+ DefinitionParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_diagnostics.py b/tests/lsp/test_diagnostics.py
new file mode 100644
index 0000000..c420942
--- /dev/null
+++ b/tests/lsp/test_diagnostics.py
@@ -0,0 +1,68 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+import json
+from typing import Tuple
+
+from lsprotocol import types
+
+
+from ..client import LanguageClient
+
+
+async def test_diagnostics(
+ json_server_client: Tuple[LanguageClient, types.InitializeResult],
+ uri_for,
+):
+ """Ensure that diagnostics are working as expected."""
+ client, _ = json_server_client
+
+ test_uri = uri_for("example.json")
+ assert test_uri is not None
+
+ # Get the expected error message
+ document_content = "text"
+ try:
+ json.loads(document_content)
+ except json.JSONDecodeError as err:
+ expected_message = err.msg
+
+ client.text_document_did_open(
+ types.DidOpenTextDocumentParams(
+ text_document=types.TextDocumentItem(
+ uri=test_uri, language_id="json", version=1, text=document_content
+ )
+ )
+ )
+
+ await client.wait_for_notification(types.TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS)
+
+ diagnostics = client.diagnostics[test_uri]
+ assert diagnostics[0].message == expected_message
+
+ result = await client.text_document_diagnostic_async(
+ types.DocumentDiagnosticParams(
+ text_document=types.TextDocumentIdentifier(test_uri)
+ )
+ )
+ diagnostics = result.items
+ assert diagnostics[0].message == expected_message
+
+ workspace_result = await client.workspace_diagnostic_async(
+ types.WorkspaceDiagnosticParams(previous_result_ids=[])
+ )
+ diagnostics = workspace_result.items[0].items
+ assert diagnostics[0].message == expected_message
diff --git a/tests/lsp/test_document_color.py b/tests/lsp/test_document_color.py
new file mode 100644
index 0000000..460a60b
--- /dev/null
+++ b/tests/lsp/test_document_color.py
@@ -0,0 +1,81 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import List
+
+from lsprotocol.types import TEXT_DOCUMENT_DOCUMENT_COLOR
+from lsprotocol.types import (
+ Color,
+ ColorInformation,
+ DocumentColorOptions,
+ DocumentColorParams,
+ Position,
+ Range,
+ TextDocumentIdentifier,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_DOCUMENT_COLOR,
+ DocumentColorOptions(),
+ )
+ def f(params: DocumentColorParams) -> List[ColorInformation]:
+ return [
+ ColorInformation(
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ color=Color(red=0.5, green=0.5, blue=0.5, alpha=0.5),
+ )
+ ]
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.color_provider
+
+
+@ConfiguredLS.decorate()
+def test_document_color(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_DOCUMENT_COLOR,
+ DocumentColorParams(
+ text_document=TextDocumentIdentifier(uri="file://return.list")
+ ),
+ ).result()
+
+ assert response
+ assert response[0].color.red == 0.5
+ assert response[0].color.green == 0.5
+ assert response[0].color.blue == 0.5
+ assert response[0].color.alpha == 0.5
+
+ assert response[0].range.start.line == 0
+ assert response[0].range.start.character == 0
+ assert response[0].range.end.line == 1
+ assert response[0].range.end.character == 1
diff --git a/tests/lsp/test_document_highlight.py b/tests/lsp/test_document_highlight.py
new file mode 100644
index 0000000..e4afc5f
--- /dev/null
+++ b/tests/lsp/test_document_highlight.py
@@ -0,0 +1,108 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import List, Optional
+
+from lsprotocol.types import TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT
+from lsprotocol.types import (
+ DocumentHighlight,
+ DocumentHighlightKind,
+ DocumentHighlightOptions,
+ DocumentHighlightParams,
+ Position,
+ Range,
+ TextDocumentIdentifier,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT,
+ DocumentHighlightOptions(),
+ )
+ def f(params: DocumentHighlightParams) -> Optional[List[DocumentHighlight]]:
+ if params.text_document.uri == "file://return.list":
+ return [
+ DocumentHighlight(
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ ),
+ DocumentHighlight(
+ range=Range(
+ start=Position(line=1, character=1),
+ end=Position(line=2, character=2),
+ ),
+ kind=DocumentHighlightKind.Write,
+ ),
+ ]
+ else:
+ return None
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.document_highlight_provider
+
+
+@ConfiguredLS.decorate()
+def test_document_highlight_return_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT,
+ DocumentHighlightParams(
+ text_document=TextDocumentIdentifier(uri="file://return.list"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response
+
+ assert response[0].range.start.line == 0
+ assert response[0].range.start.character == 0
+ assert response[0].range.end.line == 1
+ assert response[0].range.end.character == 1
+ assert response[0].kind is None
+
+ assert response[1].range.start.line == 1
+ assert response[1].range.start.character == 1
+ assert response[1].range.end.line == 2
+ assert response[1].range.end.character == 2
+ assert response[1].kind == DocumentHighlightKind.Write
+
+
+@ConfiguredLS.decorate()
+def test_document_highlight_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT,
+ DocumentHighlightParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_document_link.py b/tests/lsp/test_document_link.py
new file mode 100644
index 0000000..0602773
--- /dev/null
+++ b/tests/lsp/test_document_link.py
@@ -0,0 +1,98 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import List, Optional
+
+from lsprotocol.types import TEXT_DOCUMENT_DOCUMENT_LINK
+from lsprotocol.types import (
+ DocumentLink,
+ DocumentLinkOptions,
+ DocumentLinkParams,
+ Position,
+ Range,
+ TextDocumentIdentifier,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_DOCUMENT_LINK,
+ DocumentLinkOptions(resolve_provider=True),
+ )
+ def f(params: DocumentLinkParams) -> Optional[List[DocumentLink]]:
+ if params.text_document.uri == "file://return.list":
+ return [
+ DocumentLink(
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ target="target",
+ tooltip="tooltip",
+ data="data",
+ ),
+ ]
+ else:
+ return None
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.document_link_provider
+ assert capabilities.document_link_provider.resolve_provider
+
+
+@ConfiguredLS.decorate()
+def test_document_link_return_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_DOCUMENT_LINK,
+ DocumentLinkParams(
+ text_document=TextDocumentIdentifier(uri="file://return.list"),
+ ),
+ ).result()
+
+ assert response
+
+ assert response[0].range.start.line == 0
+ assert response[0].range.start.character == 0
+ assert response[0].range.end.line == 1
+ assert response[0].range.end.character == 1
+ assert response[0].target == "target"
+ assert response[0].tooltip == "tooltip"
+ assert response[0].data == "data"
+
+
+@ConfiguredLS.decorate()
+def test_document_link_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_DOCUMENT_LINK,
+ DocumentLinkParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_document_symbol.py b/tests/lsp/test_document_symbol.py
new file mode 100644
index 0000000..251c8fb
--- /dev/null
+++ b/tests/lsp/test_document_symbol.py
@@ -0,0 +1,168 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import List, Union
+
+from lsprotocol.types import TEXT_DOCUMENT_DOCUMENT_SYMBOL
+from lsprotocol.types import (
+ DocumentSymbol,
+ DocumentSymbolOptions,
+ DocumentSymbolParams,
+ Location,
+ Position,
+ Range,
+ SymbolInformation,
+ SymbolKind,
+ TextDocumentIdentifier,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_DOCUMENT_SYMBOL,
+ DocumentSymbolOptions(),
+ )
+ def f(
+ params: DocumentSymbolParams,
+ ) -> Union[List[SymbolInformation], List[DocumentSymbol]]:
+ symbol_info = SymbolInformation(
+ name="symbol",
+ kind=SymbolKind.Namespace,
+ location=Location(
+ uri="uri",
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ ),
+ container_name="container",
+ deprecated=False,
+ )
+
+ document_symbol_inner = DocumentSymbol(
+ name="inner_symbol",
+ kind=SymbolKind.Number,
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ selection_range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ )
+
+ document_symbol = DocumentSymbol(
+ name="symbol",
+ kind=SymbolKind.Object,
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=10, character=10),
+ ),
+ selection_range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=10, character=10),
+ ),
+ detail="detail",
+ children=[document_symbol_inner],
+ deprecated=True,
+ )
+
+ return { # type: ignore
+ "file://return.symbol_information_list": [symbol_info],
+ "file://return.document_symbol_list": [document_symbol],
+ }.get(params.text_document.uri, None)
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.document_symbol_provider
+
+
+@ConfiguredLS.decorate()
+def test_document_symbol_return_symbol_information_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_DOCUMENT_SYMBOL,
+ DocumentSymbolParams(
+ text_document=TextDocumentIdentifier(
+ uri="file://return.symbol_information_list"
+ ),
+ ),
+ ).result()
+
+ assert response
+
+ assert response[0].name == "symbol"
+ assert response[0].kind == SymbolKind.Namespace
+ assert response[0].location.uri == "uri"
+ assert response[0].location.range.start.line == 0
+ assert response[0].location.range.start.character == 0
+ assert response[0].location.range.end.line == 1
+ assert response[0].location.range.end.character == 1
+ assert response[0].container_name == "container"
+ assert not response[0].deprecated
+
+
+@ConfiguredLS.decorate()
+def test_document_symbol_return_document_symbol_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_DOCUMENT_SYMBOL,
+ DocumentSymbolParams(
+ text_document=TextDocumentIdentifier(
+ uri="file://return.document_symbol_list"
+ ),
+ ),
+ ).result()
+
+ assert response
+
+ assert response[0].name == "symbol"
+ assert response[0].kind == SymbolKind.Object
+ assert response[0].range.start.line == 0
+ assert response[0].range.start.character == 0
+ assert response[0].range.end.line == 10
+ assert response[0].range.end.character == 10
+ assert response[0].selection_range.start.line == 0
+ assert response[0].selection_range.start.character == 0
+ assert response[0].selection_range.end.line == 10
+ assert response[0].selection_range.end.character == 10
+ assert response[0].detail == "detail"
+ assert response[0].deprecated
+
+ assert response[0].children[0].name == "inner_symbol"
+ assert response[0].children[0].kind == SymbolKind.Number
+ assert response[0].children[0].range.start.line == 0
+ assert response[0].children[0].range.start.character == 0
+ assert response[0].children[0].range.end.line == 1
+ assert response[0].children[0].range.end.character == 1
+ range = response[0].children[0].selection_range
+ assert range.start.line == 0
+ assert range.start.character == 0
+ assert range.end.line == 1
+ assert range.end.character == 1
+
+ assert response[0].children[0].children is None
diff --git a/tests/lsp/test_errors.py b/tests/lsp/test_errors.py
new file mode 100644
index 0000000..1fbc05b
--- /dev/null
+++ b/tests/lsp/test_errors.py
@@ -0,0 +1,135 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import Any, List, Union
+import time
+
+import pytest
+
+from pygls.exceptions import JsonRpcInternalError, PyglsError, JsonRpcException
+from lsprotocol.types import WINDOW_SHOW_MESSAGE, MessageType
+from pygls.server import LanguageServer
+
+from ..conftest import ClientServer
+
+ERROR_TRIGGER = "test/triggerError"
+ERROR_MESSAGE = "Testing errors"
+
+
+class CustomLanguageServerSafe(LanguageServer):
+ def report_server_error(
+ self, error: Exception, source: Union[PyglsError, JsonRpcException]
+ ):
+ pass
+
+
+class CustomLanguageServerPotentialRecursion(LanguageServer):
+ def report_server_error(
+ self, error: Exception, source: Union[PyglsError, JsonRpcException]
+ ):
+ raise Exception()
+
+
+class CustomLanguageServerSendAll(LanguageServer):
+ def report_server_error(
+ self, error: Exception, source: Union[PyglsError, JsonRpcException]
+ ):
+ self.show_message(self.default_error_message, msg_type=MessageType.Error)
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self, LS=LanguageServer):
+ super().__init__(LS)
+ self.init()
+
+ def init(self):
+ self.client.messages: List[str] = []
+
+ @self.server.feature(ERROR_TRIGGER)
+ def f1(params: Any):
+ raise Exception(ERROR_MESSAGE)
+
+ @self.client.feature(WINDOW_SHOW_MESSAGE)
+ def f2(params: Any):
+ self.client.messages.append(params.message)
+
+
+class CustomConfiguredLSSafe(ConfiguredLS):
+ def __init__(self):
+ super().__init__(CustomLanguageServerSafe)
+
+
+class CustomConfiguredLSPotentialRecusrion(ConfiguredLS):
+ def __init__(self):
+ super().__init__(CustomLanguageServerPotentialRecursion)
+
+
+class CustomConfiguredLSSendAll(ConfiguredLS):
+ def __init__(self):
+ super().__init__(CustomLanguageServerSendAll)
+
+
+@ConfiguredLS.decorate()
+def test_request_error_reporting_default(client_server):
+ client, _ = client_server
+ assert len(client.messages) == 0
+
+ with pytest.raises(JsonRpcInternalError, match=ERROR_MESSAGE):
+ client.lsp.send_request(ERROR_TRIGGER).result()
+
+ time.sleep(0.1)
+ assert len(client.messages) == 0
+
+
+@CustomConfiguredLSSendAll.decorate()
+def test_request_error_reporting_override(client_server):
+ client, _ = client_server
+ assert len(client.messages) == 0
+
+ with pytest.raises(JsonRpcInternalError, match=ERROR_MESSAGE):
+ client.lsp.send_request(ERROR_TRIGGER).result()
+
+ time.sleep(0.1)
+ assert len(client.messages) == 1
+
+
+@ConfiguredLS.decorate()
+def test_notification_error_reporting(client_server):
+ client, _ = client_server
+ client.lsp.notify(ERROR_TRIGGER)
+ time.sleep(0.1)
+
+ assert len(client.messages) == 1
+ assert client.messages[0] == LanguageServer.default_error_message
+
+
+@CustomConfiguredLSSafe.decorate()
+def test_overriding_error_reporting(client_server):
+ client, _ = client_server
+ client.lsp.notify(ERROR_TRIGGER)
+ time.sleep(0.1)
+
+ assert len(client.messages) == 0
+
+
+@CustomConfiguredLSPotentialRecusrion.decorate()
+def test_overriding_error_reporting_with_potential_recursion(client_server):
+ client, _ = client_server
+ client.lsp.notify(ERROR_TRIGGER)
+ time.sleep(0.1)
+
+ assert len(client.messages) == 0
diff --git a/tests/lsp/test_folding_range.py b/tests/lsp/test_folding_range.py
new file mode 100644
index 0000000..8f9c749
--- /dev/null
+++ b/tests/lsp/test_folding_range.py
@@ -0,0 +1,92 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import List, Optional
+
+from lsprotocol.types import TEXT_DOCUMENT_FOLDING_RANGE
+from lsprotocol.types import (
+ FoldingRange,
+ FoldingRangeKind,
+ FoldingRangeOptions,
+ FoldingRangeParams,
+ TextDocumentIdentifier,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_FOLDING_RANGE,
+ FoldingRangeOptions(),
+ )
+ def f(params: FoldingRangeParams) -> Optional[List[FoldingRange]]:
+ if params.text_document.uri == "file://return.list":
+ return [
+ FoldingRange(
+ start_line=0,
+ end_line=0,
+ start_character=1,
+ end_character=1,
+ kind=FoldingRangeKind.Comment,
+ ),
+ ]
+ else:
+ return None
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.folding_range_provider
+
+
+@ConfiguredLS.decorate()
+def test_folding_range_return_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_FOLDING_RANGE,
+ FoldingRangeParams(
+ text_document=TextDocumentIdentifier(uri="file://return.list"),
+ ),
+ ).result()
+
+ assert response
+
+ assert response[0].start_line == 0
+ assert response[0].end_line == 0
+ assert response[0].start_character == 1
+ assert response[0].end_character == 1
+ assert response[0].kind == FoldingRangeKind.Comment
+
+
+@ConfiguredLS.decorate()
+def test_folding_range_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_FOLDING_RANGE,
+ FoldingRangeParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_formatting.py b/tests/lsp/test_formatting.py
new file mode 100644
index 0000000..0c3fbd6
--- /dev/null
+++ b/tests/lsp/test_formatting.py
@@ -0,0 +1,108 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import List, Optional
+
+from lsprotocol.types import TEXT_DOCUMENT_FORMATTING
+from lsprotocol.types import (
+ DocumentFormattingOptions,
+ DocumentFormattingParams,
+ FormattingOptions,
+ Position,
+ Range,
+ TextDocumentIdentifier,
+ TextEdit,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_FORMATTING,
+ DocumentFormattingOptions(),
+ )
+ def f(params: DocumentFormattingParams) -> Optional[List[TextEdit]]:
+ if params.text_document.uri == "file://return.list":
+ return [
+ TextEdit(
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ new_text="text",
+ )
+ ]
+ else:
+ return None
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.document_formatting_provider
+
+
+@ConfiguredLS.decorate()
+def test_document_formatting_return_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_FORMATTING,
+ DocumentFormattingParams(
+ text_document=TextDocumentIdentifier(uri="file://return.list"),
+ options=FormattingOptions(
+ tab_size=2,
+ insert_spaces=True,
+ trim_trailing_whitespace=True,
+ insert_final_newline=True,
+ trim_final_newlines=True,
+ ),
+ ),
+ ).result()
+
+ assert response
+
+ assert response[0].new_text == "text"
+ assert response[0].range.start.line == 0
+ assert response[0].range.start.character == 0
+ assert response[0].range.end.line == 1
+ assert response[0].range.end.character == 1
+
+
+@ConfiguredLS.decorate()
+def test_document_formatting_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_FORMATTING,
+ DocumentFormattingParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ options=FormattingOptions(
+ tab_size=2,
+ insert_spaces=True,
+ trim_trailing_whitespace=True,
+ insert_final_newline=True,
+ trim_final_newlines=True,
+ ),
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_hover.py b/tests/lsp/test_hover.py
new file mode 100644
index 0000000..9007c78
--- /dev/null
+++ b/tests/lsp/test_hover.py
@@ -0,0 +1,149 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import Optional
+
+from lsprotocol.types import TEXT_DOCUMENT_HOVER
+from lsprotocol.types import (
+ Hover,
+ HoverOptions,
+ HoverParams,
+ MarkedString_Type1,
+ MarkupContent,
+ MarkupKind,
+ Position,
+ Range,
+ TextDocumentIdentifier,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_HOVER,
+ HoverOptions(),
+ )
+ def f(params: HoverParams) -> Optional[Hover]:
+ range = Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ )
+
+ return {
+ "file://return.marked_string": Hover(
+ range=range,
+ contents=MarkedString_Type1(
+ language="language",
+ value="value",
+ ),
+ ),
+ "file://return.marked_string_list": Hover(
+ range=range,
+ contents=[
+ MarkedString_Type1(
+ language="language",
+ value="value",
+ ),
+ "str type",
+ ],
+ ),
+ "file://return.markup_content": Hover(
+ range=range,
+ contents=MarkupContent(kind=MarkupKind.Markdown, value="value"),
+ ),
+ }.get(params.text_document.uri, None)
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.hover_provider
+
+
+@ConfiguredLS.decorate()
+def test_hover_return_marked_string(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_HOVER,
+ HoverParams(
+ text_document=TextDocumentIdentifier(uri="file://return.marked_string"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response
+
+ assert response.contents.language == "language"
+ assert response.contents.value == "value"
+
+ assert response.range.start.line == 0
+ assert response.range.start.character == 0
+ assert response.range.end.line == 1
+ assert response.range.end.character == 1
+
+
+@ConfiguredLS.decorate()
+def test_hover_return_marked_string_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_HOVER,
+ HoverParams(
+ text_document=TextDocumentIdentifier(
+ uri="file://return.marked_string_list"
+ ),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response
+
+ assert response.contents[0].language == "language"
+ assert response.contents[0].value == "value"
+ assert response.contents[1] == "str type"
+
+ assert response.range.start.line == 0
+ assert response.range.start.character == 0
+ assert response.range.end.line == 1
+ assert response.range.end.character == 1
+
+
+@ConfiguredLS.decorate()
+def test_hover_return_markup_content(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_HOVER,
+ HoverParams(
+ text_document=TextDocumentIdentifier(uri="file://return.markup_content"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response
+
+ assert response.contents.kind == MarkupKind.Markdown
+ assert response.contents.value == "value"
+
+ assert response.range.start.line == 0
+ assert response.range.start.character == 0
+ assert response.range.end.line == 1
+ assert response.range.end.character == 1
diff --git a/tests/lsp/test_implementation.py b/tests/lsp/test_implementation.py
new file mode 100644
index 0000000..4fea3a9
--- /dev/null
+++ b/tests/lsp/test_implementation.py
@@ -0,0 +1,164 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import List, Optional, Union
+
+from lsprotocol.types import TEXT_DOCUMENT_IMPLEMENTATION
+from lsprotocol.types import (
+ ImplementationOptions,
+ ImplementationParams,
+ Location,
+ LocationLink,
+ Position,
+ Range,
+ TextDocumentIdentifier,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_IMPLEMENTATION,
+ ImplementationOptions(),
+ )
+ def f(
+ params: ImplementationParams,
+ ) -> Optional[Union[Location, List[Location], List[LocationLink]]]:
+ location = Location(
+ uri="uri",
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ )
+
+ location_link = LocationLink(
+ target_uri="uri",
+ target_range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ target_selection_range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=2, character=2),
+ ),
+ origin_selection_range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=3, character=3),
+ ),
+ )
+
+ return { # type: ignore
+ "file://return.location": location,
+ "file://return.location_list": [location],
+ "file://return.location_link_list": [location_link],
+ }.get(params.text_document.uri, None)
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.implementation_provider
+
+
+@ConfiguredLS.decorate()
+def test_type_definition_return_location(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_IMPLEMENTATION,
+ ImplementationParams(
+ text_document=TextDocumentIdentifier(uri="file://return.location"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response.uri == "uri"
+
+ assert response.range.start.line == 0
+ assert response.range.start.character == 0
+ assert response.range.end.line == 1
+ assert response.range.end.character == 1
+
+
+@ConfiguredLS.decorate()
+def test_type_definition_return_location_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_IMPLEMENTATION,
+ ImplementationParams(
+ text_document=TextDocumentIdentifier(uri="file://return.location_list"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response[0].uri == "uri"
+
+ assert response[0].range.start.line == 0
+ assert response[0].range.start.character == 0
+ assert response[0].range.end.line == 1
+ assert response[0].range.end.character == 1
+
+
+@ConfiguredLS.decorate()
+def test_type_definition_return_location_link_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_IMPLEMENTATION,
+ ImplementationParams(
+ text_document=TextDocumentIdentifier(
+ uri="file://return.location_link_list"
+ ),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response[0].target_uri == "uri"
+
+ assert response[0].target_range.start.line == 0
+ assert response[0].target_range.start.character == 0
+ assert response[0].target_range.end.line == 1
+ assert response[0].target_range.end.character == 1
+
+ assert response[0].target_selection_range.start.line == 0
+ assert response[0].target_selection_range.start.character == 0
+ assert response[0].target_selection_range.end.line == 2
+ assert response[0].target_selection_range.end.character == 2
+
+ assert response[0].origin_selection_range.start.line == 0
+ assert response[0].origin_selection_range.start.character == 0
+ assert response[0].origin_selection_range.end.line == 3
+ assert response[0].origin_selection_range.end.character == 3
+
+
+@ConfiguredLS.decorate()
+def test_type_definition_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_IMPLEMENTATION,
+ ImplementationParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_inlay_hints.py b/tests/lsp/test_inlay_hints.py
new file mode 100644
index 0000000..1146e1b
--- /dev/null
+++ b/tests/lsp/test_inlay_hints.py
@@ -0,0 +1,56 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+from typing import Tuple
+
+from lsprotocol import types
+
+from ..client import LanguageClient
+
+
+async def test_code_actions(
+ inlay_hints_client: Tuple[LanguageClient, types.InitializeResult], uri_for
+):
+ """Ensure that the example code action server is working as expected."""
+ client, initialize_result = inlay_hints_client
+
+ inlay_hint_provider = initialize_result.capabilities.inlay_hint_provider
+ assert inlay_hint_provider.resolve_provider is True
+
+ test_uri = uri_for("sums.txt")
+ assert test_uri is not None
+
+ response = await client.text_document_inlay_hint_async(
+ types.InlayHintParams(
+ text_document=types.TextDocumentIdentifier(uri=test_uri),
+ range=types.Range(
+ start=types.Position(line=3, character=0),
+ end=types.Position(line=4, character=0),
+ ),
+ )
+ )
+
+ assert len(response) == 2
+ two, three = response[0], response[1]
+
+ assert two.label == ":10"
+ assert two.tooltip is None
+
+ assert three.label == ":11"
+ assert three.tooltip is None
+
+ resolved = await client.inlay_hint_resolve_async(three)
+ assert resolved.tooltip == "Binary representation of the number: 3"
diff --git a/tests/lsp/test_inline_value.py b/tests/lsp/test_inline_value.py
new file mode 100644
index 0000000..68d682d
--- /dev/null
+++ b/tests/lsp/test_inline_value.py
@@ -0,0 +1,60 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+from typing import Tuple
+
+from lsprotocol import types
+
+
+from ..client import LanguageClient
+
+
+async def test_inline_value(
+ json_server_client: Tuple[LanguageClient, types.InitializeResult],
+ uri_for,
+):
+ """Ensure that inline values are working as expected."""
+ client, _ = json_server_client
+
+ test_uri = uri_for("example.json")
+ assert test_uri is not None
+
+ document_content = '{\n"foo": "bar"\n}'
+ client.text_document_did_open(
+ types.DidOpenTextDocumentParams(
+ text_document=types.TextDocumentItem(
+ uri=test_uri, language_id="json", version=1, text=document_content
+ )
+ )
+ )
+
+ result = await client.text_document_inline_value_async(
+ types.InlineValueParams(
+ text_document=types.TextDocumentIdentifier(test_uri),
+ range=types.Range(
+ start=types.Position(line=1, character=0),
+ end=types.Position(line=1, character=6),
+ ),
+ context=types.InlineValueContext(
+ frame_id=1,
+ stopped_location=types.Range(
+ start=types.Position(line=1, character=0),
+ end=types.Position(line=1, character=6),
+ ),
+ ),
+ )
+ )
+ assert result[0].text == "Inline value"
diff --git a/tests/lsp/test_linked_editing_range.py b/tests/lsp/test_linked_editing_range.py
new file mode 100644
index 0000000..2650b7e
--- /dev/null
+++ b/tests/lsp/test_linked_editing_range.py
@@ -0,0 +1,103 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import Optional
+
+from lsprotocol.types import TEXT_DOCUMENT_LINKED_EDITING_RANGE
+from lsprotocol.types import (
+ LinkedEditingRangeOptions,
+ LinkedEditingRangeParams,
+ LinkedEditingRanges,
+ Position,
+ Range,
+ TextDocumentIdentifier,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_LINKED_EDITING_RANGE,
+ LinkedEditingRangeOptions(),
+ )
+ def f(params: LinkedEditingRangeParams) -> Optional[LinkedEditingRanges]:
+ if params.text_document.uri == "file://return.ranges":
+ return LinkedEditingRanges(
+ ranges=[
+ Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ Range(
+ start=Position(line=1, character=1),
+ end=Position(line=2, character=2),
+ ),
+ ],
+ word_pattern="pattern",
+ )
+ else:
+ return None
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.linked_editing_range_provider
+
+
+@ConfiguredLS.decorate()
+def test_linked_editing_ranges_return_ranges(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_LINKED_EDITING_RANGE,
+ LinkedEditingRangeParams(
+ text_document=TextDocumentIdentifier(uri="file://return.ranges"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response
+
+ assert response.ranges[0].start.line == 0
+ assert response.ranges[0].start.character == 0
+ assert response.ranges[0].end.line == 1
+ assert response.ranges[0].end.character == 1
+ assert response.ranges[1].start.line == 1
+ assert response.ranges[1].start.character == 1
+ assert response.ranges[1].end.line == 2
+ assert response.ranges[1].end.character == 2
+ assert response.word_pattern == "pattern"
+
+
+@ConfiguredLS.decorate()
+def test_linked_editing_ranges_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_LINKED_EDITING_RANGE,
+ LinkedEditingRangeParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_moniker.py b/tests/lsp/test_moniker.py
new file mode 100644
index 0000000..09b962d
--- /dev/null
+++ b/tests/lsp/test_moniker.py
@@ -0,0 +1,94 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import List, Optional
+
+from lsprotocol.types import TEXT_DOCUMENT_MONIKER
+from lsprotocol.types import (
+ Moniker,
+ MonikerKind,
+ MonikerOptions,
+ MonikerParams,
+ Position,
+ TextDocumentIdentifier,
+ UniquenessLevel,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_MONIKER,
+ MonikerOptions(),
+ )
+ def f(params: MonikerParams) -> Optional[List[Moniker]]:
+ if params.text_document.uri == "file://return.list":
+ return [
+ Moniker(
+ scheme="test_scheme",
+ identifier="test_identifier",
+ unique=UniquenessLevel.Global,
+ kind=MonikerKind.Local,
+ ),
+ ]
+ else:
+ return None
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.moniker_provider
+
+
+@ConfiguredLS.decorate()
+def test_moniker_return_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_MONIKER,
+ MonikerParams(
+ text_document=TextDocumentIdentifier(uri="file://return.list"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response
+
+ assert response[0].scheme == "test_scheme"
+ assert response[0].identifier == "test_identifier"
+ assert response[0].unique == UniquenessLevel.Global
+ assert response[0].kind == MonikerKind.Local
+
+
+@ConfiguredLS.decorate()
+def test_references_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_MONIKER,
+ MonikerParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_on_type_formatting.py b/tests/lsp/test_on_type_formatting.py
new file mode 100644
index 0000000..2e7adc9
--- /dev/null
+++ b/tests/lsp/test_on_type_formatting.py
@@ -0,0 +1,122 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import List, Optional
+
+from lsprotocol.types import TEXT_DOCUMENT_ON_TYPE_FORMATTING
+from lsprotocol.types import (
+ DocumentOnTypeFormattingOptions,
+ DocumentOnTypeFormattingParams,
+ FormattingOptions,
+ Position,
+ Range,
+ TextDocumentIdentifier,
+ TextEdit,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_ON_TYPE_FORMATTING,
+ DocumentOnTypeFormattingOptions(
+ first_trigger_character=":",
+ more_trigger_character=[",", "."],
+ ),
+ )
+ def f(params: DocumentOnTypeFormattingParams) -> Optional[List[TextEdit]]:
+ if params.text_document.uri == "file://return.list":
+ return [
+ TextEdit(
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ new_text="text",
+ )
+ ]
+ else:
+ return None
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ 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 == [
+ ",",
+ ".",
+ ]
+
+
+@ConfiguredLS.decorate()
+def test_on_type_formatting_return_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_ON_TYPE_FORMATTING,
+ DocumentOnTypeFormattingParams(
+ text_document=TextDocumentIdentifier(uri="file://return.list"),
+ position=Position(line=0, character=0),
+ ch=":",
+ options=FormattingOptions(
+ tab_size=2,
+ insert_spaces=True,
+ trim_trailing_whitespace=True,
+ insert_final_newline=True,
+ trim_final_newlines=True,
+ ),
+ ),
+ ).result()
+
+ assert response
+
+ assert response[0].new_text == "text"
+ assert response[0].range.start.line == 0
+ assert response[0].range.start.character == 0
+ assert response[0].range.end.line == 1
+ assert response[0].range.end.character == 1
+
+
+@ConfiguredLS.decorate()
+def test_on_type_formatting_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_ON_TYPE_FORMATTING,
+ DocumentOnTypeFormattingParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ position=Position(line=0, character=0),
+ ch=":",
+ options=FormattingOptions(
+ tab_size=2,
+ insert_spaces=True,
+ trim_trailing_whitespace=True,
+ insert_final_newline=True,
+ trim_final_newlines=True,
+ ),
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_prepare_rename.py b/tests/lsp/test_prepare_rename.py
new file mode 100644
index 0000000..39d0712
--- /dev/null
+++ b/tests/lsp/test_prepare_rename.py
@@ -0,0 +1,111 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import Optional, Union
+
+from lsprotocol.types import TEXT_DOCUMENT_PREPARE_RENAME
+from lsprotocol.types import (
+ Position,
+ PrepareRenameResult,
+ PrepareRenameResult_Type1,
+ PrepareRenameParams,
+ Range,
+ TextDocumentIdentifier,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(TEXT_DOCUMENT_PREPARE_RENAME)
+ def f(
+ params: PrepareRenameParams,
+ ) -> Optional[Union[Range, PrepareRenameResult]]:
+ return { # type: ignore
+ "file://return.range": Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ "file://return.prepare_rename": PrepareRenameResult_Type1(
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ placeholder="placeholder",
+ ),
+ }.get(params.text_document.uri, None)
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ pass
+
+
+@ConfiguredLS.decorate()
+def test_prepare_rename_return_range(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_PREPARE_RENAME,
+ PrepareRenameParams(
+ text_document=TextDocumentIdentifier(uri="file://return.range"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response
+
+ assert response.start.line == 0
+ assert response.start.character == 0
+ assert response.end.line == 1
+ assert response.end.character == 1
+
+
+@ConfiguredLS.decorate()
+def test_prepare_rename_return_prepare_rename(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_PREPARE_RENAME,
+ PrepareRenameParams(
+ text_document=TextDocumentIdentifier(uri="file://return.prepare_rename"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response
+
+ assert response.range.start.line == 0
+ assert response.range.start.character == 0
+ assert response.range.end.line == 1
+ assert response.range.end.character == 1
+ assert response.placeholder == "placeholder"
+
+
+@ConfiguredLS.decorate()
+def test_prepare_rename_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_PREPARE_RENAME,
+ PrepareRenameParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_progress.py b/tests/lsp/test_progress.py
new file mode 100644
index 0000000..4965772
--- /dev/null
+++ b/tests/lsp/test_progress.py
@@ -0,0 +1,225 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+import asyncio
+from typing import List, Optional
+
+import pytest
+from lsprotocol.types import (
+ TEXT_DOCUMENT_CODE_LENS,
+ WINDOW_WORK_DONE_PROGRESS_CANCEL,
+ WINDOW_WORK_DONE_PROGRESS_CREATE,
+ PROGRESS,
+)
+from lsprotocol.types import (
+ CodeLens,
+ CodeLensParams,
+ CodeLensOptions,
+ ProgressParams,
+ TextDocumentIdentifier,
+ WorkDoneProgressBegin,
+ WorkDoneProgressEnd,
+ WorkDoneProgressReport,
+ WorkDoneProgressCancelParams,
+ WorkDoneProgressCreateParams,
+)
+from ..conftest import ClientServer
+from pygls import IS_PYODIDE
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+ self.client.notifications: List[ProgressParams] = []
+ self.client.method_calls: List[WorkDoneProgressCreateParams] = []
+
+ @self.server.feature(
+ TEXT_DOCUMENT_CODE_LENS,
+ 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:
+ token = params.work_done_token
+ else:
+ assert "server_initiated_token" in params.text_document.uri
+ token = params.text_document.uri[len("file://") :]
+ if "async" in params.text_document.uri:
+ await self.server.progress.create_async(token)
+ else:
+ f = self.server.progress.create(token)
+ await asyncio.sleep(0.1)
+ f.result()
+
+ assert token
+ self.server.lsp.progress.begin(
+ token,
+ 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")
+ )
+ else:
+ self.server.lsp.progress.report(
+ token,
+ WorkDoneProgressReport(
+ kind="report", message="doing", percentage=50
+ ),
+ )
+ self.server.lsp.progress.end(
+ token, WorkDoneProgressEnd(kind="end", message="done")
+ )
+ return None
+
+ @self.client.feature(PROGRESS)
+ def f2(params):
+ self.client.notifications.append(params)
+ if params.value["kind"] == "begin" and "cancel" in params.token:
+ # client cancels the progress token
+ self.client.lsp.notify(
+ WINDOW_WORK_DONE_PROGRESS_CANCEL,
+ WorkDoneProgressCancelParams(token=params.token),
+ )
+
+ @self.client.feature(WINDOW_WORK_DONE_PROGRESS_CREATE)
+ def f3(params: WorkDoneProgressCreateParams):
+ self.client.method_calls.append(params)
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ provider = capabilities.code_lens_provider
+ assert provider
+ assert provider.work_done_progress
+
+
+@pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.")
+@ConfiguredLS.decorate()
+async def test_progress_notifications(client_server):
+ client, _ = client_server
+ client.lsp.send_request(
+ TEXT_DOCUMENT_CODE_LENS,
+ CodeLensParams(
+ text_document=TextDocumentIdentifier(uri="file://client_initiated_token"),
+ work_done_token="token",
+ ),
+ ).result()
+
+ assert [notif.value for notif in client.notifications] == [
+ {
+ "kind": "begin",
+ "title": "starting",
+ "percentage": 0,
+ },
+ {
+ "kind": "report",
+ "message": "doing",
+ "percentage": 50,
+ },
+ {"kind": "end", "message": "done"},
+ ]
+ assert {notif.token for notif in client.notifications} == {"token"}
+
+
+@pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.")
+@pytest.mark.parametrize("registration", ("sync", "async"))
+@ConfiguredLS.decorate()
+async def test_server_initiated_progress_notifications(client_server, registration):
+ client, _ = client_server
+ client.lsp.send_request(
+ TEXT_DOCUMENT_CODE_LENS,
+ CodeLensParams(
+ text_document=TextDocumentIdentifier(
+ uri=f"file://server_initiated_token_{registration}"
+ ),
+ work_done_token="token",
+ ),
+ ).result()
+
+ assert [notif.value for notif in client.notifications] == [
+ {
+ "kind": "begin",
+ "title": "starting",
+ "percentage": 0,
+ },
+ {
+ "kind": "report",
+ "message": "doing",
+ "percentage": 50,
+ },
+ {"kind": "end", "message": "done"},
+ ]
+ assert {notif.token for notif in client.notifications} == {
+ f"server_initiated_token_{registration}"
+ }
+ assert [mc.token for mc in client.method_calls] == [
+ f"server_initiated_token_{registration}"
+ ]
+
+
+@pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.")
+@ConfiguredLS.decorate()
+def test_progress_cancel_notifications(client_server):
+ client, _ = client_server
+ client.lsp.send_request(
+ TEXT_DOCUMENT_CODE_LENS,
+ CodeLensParams(
+ text_document=TextDocumentIdentifier(uri="file://client_initiated_token"),
+ work_done_token="token_with_cancellation",
+ ),
+ ).result()
+ assert [notif.value for notif in client.notifications] == [
+ {
+ "kind": "begin",
+ "title": "starting",
+ "percentage": 0,
+ },
+ {"kind": "end", "message": "cancelled"},
+ ]
+ assert {notif.token for notif in client.notifications} == {
+ "token_with_cancellation"
+ }
+
+
+@pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.")
+@pytest.mark.parametrize("registration", ("sync", "async"))
+@ConfiguredLS.decorate()
+def test_server_initiated_progress_progress_cancel_notifications(
+ client_server, registration
+):
+ client, _ = client_server
+ client.lsp.send_request(
+ TEXT_DOCUMENT_CODE_LENS,
+ CodeLensParams(
+ text_document=TextDocumentIdentifier(
+ uri=f"file://server_initiated_token_{registration}_with_cancellation"
+ ),
+ ),
+ ).result()
+
+ assert [notif.value for notif in client.notifications] == [
+ {
+ "kind": "begin",
+ "title": "starting",
+ "percentage": 0,
+ },
+ {"kind": "end", "message": "cancelled"},
+ ]
diff --git a/tests/lsp/test_range_formatting.py b/tests/lsp/test_range_formatting.py
new file mode 100644
index 0000000..e7a118c
--- /dev/null
+++ b/tests/lsp/test_range_formatting.py
@@ -0,0 +1,116 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import List, Optional
+
+from lsprotocol.types import TEXT_DOCUMENT_RANGE_FORMATTING
+from lsprotocol.types import (
+ DocumentRangeFormattingOptions,
+ DocumentRangeFormattingParams,
+ FormattingOptions,
+ Position,
+ Range,
+ TextDocumentIdentifier,
+ TextEdit,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_RANGE_FORMATTING,
+ DocumentRangeFormattingOptions(),
+ )
+ def f(params: DocumentRangeFormattingParams) -> Optional[List[TextEdit]]:
+ if params.text_document.uri == "file://return.list":
+ return [
+ TextEdit(
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ new_text="text",
+ )
+ ]
+ else:
+ return None
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.document_range_formatting_provider
+
+
+@ConfiguredLS.decorate()
+def test_range_formatting_return_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_RANGE_FORMATTING,
+ DocumentRangeFormattingParams(
+ text_document=TextDocumentIdentifier(uri="file://return.list"),
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ options=FormattingOptions(
+ tab_size=2,
+ insert_spaces=True,
+ trim_trailing_whitespace=True,
+ insert_final_newline=True,
+ trim_final_newlines=True,
+ ),
+ ),
+ ).result()
+
+ assert response
+
+ assert response[0].new_text == "text"
+ assert response[0].range.start.line == 0
+ assert response[0].range.start.character == 0
+ assert response[0].range.end.line == 1
+ assert response[0].range.end.character == 1
+
+
+@ConfiguredLS.decorate()
+def test_range_formatting_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_RANGE_FORMATTING,
+ DocumentRangeFormattingParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ options=FormattingOptions(
+ tab_size=2,
+ insert_spaces=True,
+ trim_trailing_whitespace=True,
+ insert_final_newline=True,
+ trim_final_newlines=True,
+ ),
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_references.py b/tests/lsp/test_references.py
new file mode 100644
index 0000000..5867e35
--- /dev/null
+++ b/tests/lsp/test_references.py
@@ -0,0 +1,103 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import List, Optional
+
+from lsprotocol.types import TEXT_DOCUMENT_REFERENCES
+from lsprotocol.types import (
+ Location,
+ Position,
+ Range,
+ ReferenceContext,
+ ReferenceOptions,
+ ReferenceParams,
+ TextDocumentIdentifier,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_REFERENCES,
+ ReferenceOptions(),
+ )
+ def f(params: ReferenceParams) -> Optional[List[Location]]:
+ if params.text_document.uri == "file://return.list":
+ return [
+ Location(
+ uri="uri",
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ ),
+ ]
+ else:
+ return None
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.references_provider
+
+
+@ConfiguredLS.decorate()
+def test_references_return_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_REFERENCES,
+ ReferenceParams(
+ text_document=TextDocumentIdentifier(uri="file://return.list"),
+ position=Position(line=0, character=0),
+ context=ReferenceContext(
+ include_declaration=True,
+ ),
+ ),
+ ).result()
+
+ assert response
+
+ assert response[0].uri == "uri"
+
+ assert response[0].range.start.line == 0
+ assert response[0].range.start.character == 0
+ assert response[0].range.end.line == 1
+ assert response[0].range.end.character == 1
+
+
+@ConfiguredLS.decorate()
+def test_references_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_REFERENCES,
+ ReferenceParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ position=Position(line=0, character=0),
+ context=ReferenceContext(
+ include_declaration=True,
+ ),
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_rename.py b/tests/lsp/test_rename.py
new file mode 100644
index 0000000..48cface
--- /dev/null
+++ b/tests/lsp/test_rename.py
@@ -0,0 +1,195 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import Optional
+
+from lsprotocol.types import TEXT_DOCUMENT_RENAME
+from lsprotocol.types import (
+ CreateFile,
+ CreateFileOptions,
+ DeleteFile,
+ DeleteFileOptions,
+ OptionalVersionedTextDocumentIdentifier,
+ Position,
+ Range,
+ RenameFile,
+ RenameFileOptions,
+ RenameOptions,
+ RenameParams,
+ ResourceOperationKind,
+ TextDocumentEdit,
+ TextDocumentIdentifier,
+ TextEdit,
+ WorkspaceEdit,
+)
+
+from ..conftest import ClientServer
+
+workspace_edit = {
+ "changes": {
+ "uri1": [
+ TextEdit(
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ new_text="text1",
+ ),
+ TextEdit(
+ range=Range(
+ start=Position(line=1, character=1),
+ end=Position(line=2, character=2),
+ ),
+ new_text="text2",
+ ),
+ ],
+ },
+ "document_changes": [
+ TextDocumentEdit(
+ text_document=OptionalVersionedTextDocumentIdentifier(
+ uri="uri",
+ version=3,
+ ),
+ edits=[
+ TextEdit(
+ range=Range(
+ start=Position(line=2, character=2),
+ end=Position(line=3, character=3),
+ ),
+ new_text="text3",
+ ),
+ ],
+ ),
+ CreateFile(
+ kind=ResourceOperationKind.Create.value,
+ uri="create file",
+ options=CreateFileOptions(
+ overwrite=True,
+ ignore_if_exists=True,
+ ),
+ ),
+ RenameFile(
+ kind=ResourceOperationKind.Rename.value,
+ old_uri="rename old uri",
+ new_uri="rename new uri",
+ options=RenameFileOptions(
+ overwrite=True,
+ ignore_if_exists=True,
+ ),
+ ),
+ DeleteFile(
+ kind=ResourceOperationKind.Delete.value,
+ uri="delete file",
+ options=DeleteFileOptions(
+ recursive=True,
+ ignore_if_not_exists=True,
+ ),
+ ),
+ ],
+}
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_RENAME,
+ RenameOptions(prepare_provider=True),
+ )
+ def f(params: RenameParams) -> Optional[WorkspaceEdit]:
+ if params.text_document.uri == "file://return.workspace_edit":
+ return WorkspaceEdit(**workspace_edit)
+ else:
+ return None
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.rename_provider
+ assert capabilities.rename_provider.prepare_provider
+
+
+@ConfiguredLS.decorate()
+def test_rename_return_workspace_edit(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_RENAME,
+ RenameParams(
+ text_document=TextDocumentIdentifier(uri="file://return.workspace_edit"),
+ position=Position(line=0, character=0),
+ new_name="new name",
+ ),
+ ).result()
+
+ assert response
+
+ changes = response.changes["uri1"]
+ assert changes[0].new_text == "text1"
+ assert changes[0].range.start.line == 0
+ assert changes[0].range.start.character == 0
+ assert changes[0].range.end.line == 1
+ assert changes[0].range.end.character == 1
+
+ assert changes[1].new_text == "text2"
+ assert changes[1].range.start.line == 1
+ assert changes[1].range.start.character == 1
+ assert changes[1].range.end.line == 2
+ assert changes[1].range.end.character == 2
+
+ changes = response.document_changes
+ assert changes[0].text_document.uri == "uri"
+ 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.end.line == 3
+ assert changes[0].edits[0].range.end.character == 3
+
+ assert changes[1].kind == ResourceOperationKind.Create.value
+ assert changes[1].uri == "create file"
+ assert changes[1].options.ignore_if_exists
+ assert changes[1].options.overwrite
+
+ assert changes[2].kind == ResourceOperationKind.Rename.value
+ assert changes[2].new_uri == "rename new uri"
+ assert changes[2].old_uri == "rename old uri"
+ assert changes[2].options.ignore_if_exists
+ assert changes[2].options.overwrite
+
+ assert changes[3].kind == ResourceOperationKind.Delete.value
+ assert changes[3].uri == "delete file"
+ assert changes[3].options.ignore_if_not_exists
+ assert changes[3].options.recursive
+
+
+@ConfiguredLS.decorate()
+def test_rename_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_RENAME,
+ RenameParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ position=Position(line=0, character=0),
+ new_name="new name",
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_selection_range.py b/tests/lsp/test_selection_range.py
new file mode 100644
index 0000000..5f669a2
--- /dev/null
+++ b/tests/lsp/test_selection_range.py
@@ -0,0 +1,110 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+from typing import List, Optional
+
+from lsprotocol.types import TEXT_DOCUMENT_SELECTION_RANGE
+from lsprotocol.types import (
+ Position,
+ Range,
+ SelectionRange,
+ SelectionRangeOptions,
+ SelectionRangeParams,
+ TextDocumentIdentifier,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_SELECTION_RANGE,
+ SelectionRangeOptions(),
+ )
+ def f(params: SelectionRangeParams) -> Optional[List[SelectionRange]]:
+ if params.text_document.uri == "file://return.list":
+ root = SelectionRange(
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=10, character=10),
+ ),
+ )
+
+ inner_range = SelectionRange(
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ parent=root,
+ )
+
+ return [root, inner_range]
+ else:
+ return None
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.selection_range_provider
+
+
+@ConfiguredLS.decorate()
+def test_selection_range_return_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_SELECTION_RANGE,
+ SelectionRangeParams(
+ # query="query",
+ text_document=TextDocumentIdentifier(uri="file://return.list"),
+ positions=[Position(line=0, character=0)],
+ ),
+ ).result()
+
+ assert response
+
+ root = response[0]
+ assert root.range.start.line == 0
+ assert root.range.start.character == 0
+ assert root.range.end.line == 10
+ assert root.range.end.character == 10
+ assert root.parent is None
+
+ assert response[1].range.start.line == 0
+ assert response[1].range.start.character == 0
+ assert response[1].range.end.line == 1
+ assert response[1].range.end.character == 1
+ assert response[1].parent == root
+
+
+@ConfiguredLS.decorate()
+def test_selection_range_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_SELECTION_RANGE,
+ SelectionRangeParams(
+ # query="query",
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ positions=[Position(line=0, character=0)],
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_signature_help.py b/tests/lsp/test_signature_help.py
new file mode 100644
index 0000000..e318120
--- /dev/null
+++ b/tests/lsp/test_signature_help.py
@@ -0,0 +1,161 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+
+from typing import Optional
+
+import pytest
+
+from lsprotocol.types import TEXT_DOCUMENT_SIGNATURE_HELP
+from lsprotocol.types import (
+ ParameterInformation,
+ Position,
+ SignatureHelp,
+ SignatureHelpContext,
+ SignatureHelpOptions,
+ SignatureHelpParams,
+ SignatureHelpTriggerKind,
+ SignatureInformation,
+ TextDocumentIdentifier,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_SIGNATURE_HELP,
+ SignatureHelpOptions(
+ trigger_characters=["a", "b"],
+ retrigger_characters=["c", "d"],
+ ),
+ )
+ def f(params: SignatureHelpParams) -> Optional[SignatureHelp]:
+ if params.text_document.uri == "file://return.signature_help":
+ return SignatureHelp(
+ signatures=[
+ SignatureInformation(
+ label="label",
+ documentation="documentation",
+ parameters=[
+ ParameterInformation(
+ label=(0, 0),
+ documentation="documentation",
+ ),
+ ],
+ ),
+ ],
+ active_signature=0,
+ active_parameter=0,
+ )
+ else:
+ return None
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ provider = capabilities.signature_help_provider
+ assert provider
+ assert provider.trigger_characters == ["a", "b"]
+ assert provider.retrigger_characters == ["c", "d"]
+
+
+@ConfiguredLS.decorate()
+@pytest.mark.skip
+def test_signature_help_return_signature_help(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_SIGNATURE_HELP,
+ SignatureHelpParams(
+ text_document=TextDocumentIdentifier(uri="file://return.signature_help"),
+ position=Position(line=0, character=0),
+ context=SignatureHelpContext(
+ trigger_kind=SignatureHelpTriggerKind.TriggerCharacter,
+ is_retrigger=True,
+ trigger_character="a",
+ active_signature_help=SignatureHelp(
+ signatures=[
+ SignatureInformation(
+ label="label",
+ documentation="documentation",
+ parameters=[
+ ParameterInformation(
+ label=(0, 0),
+ documentation="documentation",
+ ),
+ ],
+ ),
+ ],
+ active_signature=0,
+ active_parameter=0,
+ ),
+ ),
+ ),
+ ).result()
+
+ assert response
+
+ assert response["activeParameter"] == 0
+ assert response["activeSignature"] == 0
+
+ assert response["signatures"][0]["label"] == "label"
+ assert response["signatures"][0]["documentation"] == "documentation"
+ assert response["signatures"][0]["parameters"][0]["label"] == [0, 0]
+ assert (
+ response["signatures"][0]["parameters"][0]["documentation"] == "documentation"
+ )
+
+
+@ConfiguredLS.decorate()
+@pytest.mark.skip
+def test_signature_help_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_SIGNATURE_HELP,
+ SignatureHelpParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ position=Position(line=0, character=0),
+ context=SignatureHelpContext(
+ trigger_kind=SignatureHelpTriggerKind.TriggerCharacter,
+ is_retrigger=True,
+ trigger_character="a",
+ active_signature_help=SignatureHelp(
+ signatures=[
+ SignatureInformation(
+ label="label",
+ documentation="documentation",
+ parameters=[
+ ParameterInformation(
+ label=(0, 0),
+ documentation="documentation",
+ ),
+ ],
+ ),
+ ],
+ active_signature=0,
+ active_parameter=0,
+ ),
+ ),
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_type_definition.py b/tests/lsp/test_type_definition.py
new file mode 100644
index 0000000..b6d3eff
--- /dev/null
+++ b/tests/lsp/test_type_definition.py
@@ -0,0 +1,163 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+from typing import List, Optional, Union
+
+from lsprotocol.types import TEXT_DOCUMENT_TYPE_DEFINITION
+from lsprotocol.types import (
+ Location,
+ LocationLink,
+ Position,
+ Range,
+ TextDocumentIdentifier,
+ TypeDefinitionOptions,
+ TypeDefinitionParams,
+)
+
+from ..conftest import ClientServer
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(
+ TEXT_DOCUMENT_TYPE_DEFINITION,
+ TypeDefinitionOptions(),
+ )
+ def f(
+ params: TypeDefinitionParams,
+ ) -> Optional[Union[Location, List[Location], List[LocationLink]]]:
+ location = Location(
+ uri="uri",
+ range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ )
+
+ location_link = LocationLink(
+ target_uri="uri",
+ target_range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=1, character=1),
+ ),
+ target_selection_range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=2, character=2),
+ ),
+ origin_selection_range=Range(
+ start=Position(line=0, character=0),
+ end=Position(line=3, character=3),
+ ),
+ )
+
+ return { # type: ignore
+ "file://return.location": location,
+ "file://return.location_list": [location],
+ "file://return.location_link_list": [location_link],
+ }.get(params.text_document.uri, None)
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+
+ assert capabilities.type_definition_provider
+
+
+@ConfiguredLS.decorate()
+def test_type_definition_return_location(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_TYPE_DEFINITION,
+ TypeDefinitionParams(
+ text_document=TextDocumentIdentifier(uri="file://return.location"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response.uri == "uri"
+
+ assert response.range.start.line == 0
+ assert response.range.start.character == 0
+ assert response.range.end.line == 1
+ assert response.range.end.character == 1
+
+
+@ConfiguredLS.decorate()
+def test_type_definition_return_location_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_TYPE_DEFINITION,
+ TypeDefinitionParams(
+ text_document=TextDocumentIdentifier(uri="file://return.location_list"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response[0].uri == "uri"
+
+ assert response[0].range.start.line == 0
+ assert response[0].range.start.character == 0
+ assert response[0].range.end.line == 1
+ assert response[0].range.end.character == 1
+
+
+@ConfiguredLS.decorate()
+def test_type_definition_return_location_link_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_TYPE_DEFINITION,
+ TypeDefinitionParams(
+ text_document=TextDocumentIdentifier(
+ uri="file://return.location_link_list"
+ ),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response[0].target_uri == "uri"
+
+ assert response[0].target_range.start.line == 0
+ assert response[0].target_range.start.character == 0
+ assert response[0].target_range.end.line == 1
+ assert response[0].target_range.end.character == 1
+
+ assert response[0].target_selection_range.start.line == 0
+ assert response[0].target_selection_range.start.character == 0
+ assert response[0].target_selection_range.end.line == 2
+ assert response[0].target_selection_range.end.character == 2
+
+ assert response[0].origin_selection_range.start.line == 0
+ assert response[0].origin_selection_range.start.character == 0
+ assert response[0].origin_selection_range.end.line == 3
+ assert response[0].origin_selection_range.end.character == 3
+
+
+@ConfiguredLS.decorate()
+def test_type_definition_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ TEXT_DOCUMENT_TYPE_DEFINITION,
+ TypeDefinitionParams(
+ text_document=TextDocumentIdentifier(uri="file://return.none"),
+ position=Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response is None
diff --git a/tests/lsp/test_type_hierarchy.py b/tests/lsp/test_type_hierarchy.py
new file mode 100644
index 0000000..e186c7f
--- /dev/null
+++ b/tests/lsp/test_type_hierarchy.py
@@ -0,0 +1,127 @@
+############################################################################
+# Copyright(c) Open Law Library. All rights reserved. #
+# See ThirdPartyNotices.txt in the project root for additional notices. #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License") #
+# you may not use this file except in compliance with the License. #
+# You may obtain a copy of the License at #
+# #
+# http: // www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+############################################################################
+from typing import List, Optional
+
+from lsprotocol import types as lsp
+
+from ..conftest import ClientServer
+
+
+TYPE_HIERARCHY_ITEM = lsp.TypeHierarchyItem(
+ name="test_name",
+ kind=lsp.SymbolKind.Class,
+ uri="test_uri",
+ range=lsp.Range(
+ start=lsp.Position(line=0, character=0),
+ end=lsp.Position(line=0, character=6),
+ ),
+ selection_range=lsp.Range(
+ start=lsp.Position(line=0, character=0),
+ end=lsp.Position(line=0, character=6),
+ ),
+)
+
+
+def check_type_hierarchy_item_response(item):
+ assert item.name == TYPE_HIERARCHY_ITEM.name
+ assert item.kind == TYPE_HIERARCHY_ITEM.kind
+ assert item.uri == TYPE_HIERARCHY_ITEM.uri
+ assert item.range == TYPE_HIERARCHY_ITEM.range
+ assert item.selection_range == TYPE_HIERARCHY_ITEM.selection_range
+
+
+class ConfiguredLS(ClientServer):
+ def __init__(self):
+ super().__init__()
+
+ @self.server.feature(lsp.TEXT_DOCUMENT_PREPARE_TYPE_HIERARCHY)
+ def f1(
+ params: lsp.TypeHierarchyPrepareParams,
+ ) -> Optional[List[lsp.TypeHierarchyItem]]:
+ if params.text_document.uri == "file://return.list":
+ return [TYPE_HIERARCHY_ITEM]
+ else:
+ return None
+
+ @self.server.feature(lsp.TYPE_HIERARCHY_SUPERTYPES)
+ def f2(
+ params: lsp.TypeHierarchySupertypesParams,
+ ) -> Optional[List[lsp.TypeHierarchyItem]]:
+ return [TYPE_HIERARCHY_ITEM]
+
+ @self.server.feature(lsp.TYPE_HIERARCHY_SUBTYPES)
+ def f3(
+ params: lsp.TypeHierarchySubtypesParams,
+ ) -> Optional[List[lsp.TypeHierarchyItem]]:
+ return [TYPE_HIERARCHY_ITEM]
+
+
+@ConfiguredLS.decorate()
+def test_capabilities(client_server):
+ _, server = client_server
+ capabilities = server.server_capabilities
+ assert capabilities.type_hierarchy_provider
+
+
+@ConfiguredLS.decorate()
+def test_type_hierarchy_prepare_return_list(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ lsp.TEXT_DOCUMENT_PREPARE_TYPE_HIERARCHY,
+ lsp.TypeHierarchyPrepareParams(
+ text_document=lsp.TextDocumentIdentifier(uri="file://return.list"),
+ position=lsp.Position(line=0, character=0),
+ ),
+ ).result()
+
+ check_type_hierarchy_item_response(response[0])
+
+
+@ConfiguredLS.decorate()
+def test_type_hierarchy_prepare_return_none(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ lsp.TEXT_DOCUMENT_PREPARE_TYPE_HIERARCHY,
+ lsp.TypeHierarchyPrepareParams(
+ text_document=lsp.TextDocumentIdentifier(uri="file://return.none"),
+ position=lsp.Position(line=0, character=0),
+ ),
+ ).result()
+
+ assert response is None
+
+
+@ConfiguredLS.decorate()
+def test_type_hierarchy_supertypes(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ lsp.TYPE_HIERARCHY_SUPERTYPES,
+ lsp.TypeHierarchySupertypesParams(item=TYPE_HIERARCHY_ITEM),
+ ).result()
+
+ check_type_hierarchy_item_response(response[0])
+
+
+@ConfiguredLS.decorate()
+def test_type_hierarchy_subtypes(client_server):
+ client, _ = client_server
+ response = client.lsp.send_request(
+ lsp.TYPE_HIERARCHY_SUBTYPES,
+ lsp.TypeHierarchySubtypesParams(item=TYPE_HIERARCHY_ITEM),
+ ).result()
+
+ check_type_hierarchy_item_response(response[0])