Skip to content

Commit

Permalink
Update pygls
Browse files Browse the repository at this point in the history
  • Loading branch information
regen100 committed Jan 8, 2023
1 parent 691beef commit 35aa3cd
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 300 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
run: |
cmake --version
python -m pip install --upgrade setuptools pip wheel
python -m pip install tox tox-gh-actions tox-pdm
python -m pip install tox tox-gh-actions
- name: Test with tox
run: |
tox
Expand Down
24 changes: 14 additions & 10 deletions cmake_language_server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@
from pathlib import Path
from typing import Any, Callable, List, Optional, Tuple

from pygls.lsp.methods import (
COMPLETION,
FORMATTING,
HOVER,
from lsprotocol.types import (
ALL_TYPES_MAP,
INITIALIZE,
INITIALIZED,
TEXT_DOCUMENT_COMPLETION,
TEXT_DOCUMENT_DID_SAVE,
)
from pygls.lsp.types import (
TEXT_DOCUMENT_FORMATTING,
TEXT_DOCUMENT_HOVER,
CompletionItem,
CompletionItemKind,
CompletionList,
Expand All @@ -38,6 +37,10 @@
logger = logging.getLogger(__name__)


# fix pygls bug
ALL_TYPES_MAP["TextDocumentSaveOptions"] = TextDocumentSaveRegistrationOptions


class CMakeLanguageServer(LanguageServer):
_api: Optional[API]

Expand All @@ -60,7 +63,8 @@ def initialize(params: InitializeParams) -> None:
trigger_characters = ["{", "("]

@self.feature(
COMPLETION, CompletionOptions(trigger_characters=trigger_characters)
TEXT_DOCUMENT_COMPLETION,
CompletionOptions(trigger_characters=trigger_characters),
)
def completions(params: CompletionParams) -> CompletionList:
assert self._api is not None
Expand Down Expand Up @@ -151,7 +155,7 @@ def completions(params: CompletionParams) -> CompletionList:

if shutil.which("cmake-format") is not None:

@self.feature(FORMATTING)
@self.feature(TEXT_DOCUMENT_FORMATTING)
def formatting(
params: DocumentFormattingParams,
) -> Optional[List[TextEdit]]:
Expand All @@ -174,7 +178,7 @@ def formatting(
)
]

@self.feature(HOVER)
@self.feature(TEXT_DOCUMENT_HOVER)
def hover(params: TextDocumentPositionParams) -> Optional[Hover]:
assert self._api is not None
api = self._api
Expand Down Expand Up @@ -256,4 +260,4 @@ def main() -> None:

logging.basicConfig(level=logging.INFO)
logging.getLogger("pygls").setLevel(logging.WARNING)
CMakeLanguageServer().start_io() # type: ignore
CMakeLanguageServer("cmake-language-server", __version__).start_io() # type: ignore
249 changes: 63 additions & 186 deletions pdm.lock

Large diffs are not rendered by default.

7 changes: 3 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ authors = [
{name = "Regen"},
]
dependencies = [
"pygls>=0.12.2",
"pygls>=1.0.0",
]
requires-python = ">=3.7"
requires-python = ">=3.7,<3.12"
readme = "README.md"
license = {text = "MIT"}
keywords = ["cmake", "completion", "vim", "lsp"]
Expand Down Expand Up @@ -41,11 +41,10 @@ dev = [
"pytest-datadir>=1.4.1",
"pytest-cov>=4.0.0",
"cmakelang>=0.6.13",
"tox-pdm>=0.6.1",
]
lint = [
"mypy>=0.991",
"flake8>=3.9.2",
"flake8>=5.0.4",
"black>=22.12.0",
"isort>=5.11.4",
]
Expand Down
24 changes: 10 additions & 14 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import asyncio
import logging
import os
from pathlib import Path
Expand All @@ -7,7 +6,7 @@
from typing import Iterable, Tuple

import pytest
from pygls.lsp.methods import EXIT
from lsprotocol.types import EXIT, SHUTDOWN
from pygls.server import LanguageServer

from cmake_language_server.server import CMakeLanguageServer
Expand Down Expand Up @@ -38,24 +37,21 @@ def client_server() -> Iterable[Tuple[LanguageServer, CMakeLanguageServer]]:
s2c_r, s2c_w = os.pipe()

def start(ls: LanguageServer, fdr: int, fdw: int) -> None:
# TODO: better patch is needed
# disable `close()` to avoid error messages
close = ls.loop.close
ls.loop.close = lambda: None # type: ignore
ls.start_io(os.fdopen(fdr, "rb"), os.fdopen(fdw, "wb")) # type: ignore
ls.loop.close = close # type: ignore

server = CMakeLanguageServer(asyncio.new_event_loop())
ls.start_io( # type: ignore[no-untyped-call]
os.fdopen(fdr, "rb"), os.fdopen(fdw, "wb")
)

server = CMakeLanguageServer("server", "v1")
server_thread = Thread(target=start, args=(server, c2s_r, s2c_w))
server_thread.start()

client = LanguageServer(asyncio.new_event_loop())
client = LanguageServer("client", "v1")
client_thread = Thread(target=start, args=(client, s2c_r, c2s_w))
client_thread.start()

yield client, server

client.send_notification(EXIT)
server.send_notification(EXIT)
server_thread.join()
client.lsp.send_request(SHUTDOWN)
client.lsp.notify(EXIT)
client_thread.join()
server_thread.join()
126 changes: 44 additions & 82 deletions tests/test_server.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
from concurrent import futures
from pathlib import Path
from typing import Any, Dict, Optional, Tuple
from typing import Optional, Tuple

from pygls.lsp.methods import (
COMPLETION,
FORMATTING,
HOVER,
import pytest
from lsprotocol.types import (
INITIALIZE,
TEXT_DOCUMENT_COMPLETION,
TEXT_DOCUMENT_DID_OPEN,
)
from pygls.lsp.types import (
TEXT_DOCUMENT_FORMATTING,
TEXT_DOCUMENT_HOVER,
ClientCapabilities,
CompletionContext,
CompletionList,
CompletionParams,
CompletionTriggerKind,
DidOpenTextDocumentParams,
DocumentFormattingParams,
FormattingOptions,
HoverParams,
InitializeParams,
Position,
TextDocumentIdentifier,
TextDocumentItem,
TextDocumentPositionParams,
)
from pygls.server import LanguageServer

Expand Down Expand Up @@ -50,8 +50,7 @@ def _init(client: LanguageServer, root: Path) -> None:

def _open(client: LanguageServer, path: Path, text: Optional[str] = None) -> None:
if text is None:
with open(path) as fp:
text = fp.read()
text = path.read_text()

client.lsp.notify(
TEXT_DOCUMENT_DID_OPEN,
Expand All @@ -68,7 +67,7 @@ def _test_completion(
datadir: Path,
content: str,
context: Optional[CompletionContext],
) -> Dict[str, Any]:
) -> CompletionList:
client, server = client_server
_init(client, datadir)
path = datadir / "CMakeLists.txt"
Expand All @@ -78,11 +77,10 @@ def _test_completion(
position=Position(line=0, character=len(content)),
context=context,
)
if context is None:
# some clients do not send context
del params.context
ret = client.lsp.send_request(COMPLETION, params).result(timeout=CALL_TIMEOUT)
assert isinstance(ret, dict)
ret = client.lsp.send_request(TEXT_DOCUMENT_COMPLETION, params).result(
timeout=CALL_TIMEOUT
)
assert isinstance(ret, CompletionList)
return ret


Expand All @@ -96,79 +94,43 @@ def test_initialize(
assert server._api is not None


def test_completions_invoked(
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
) -> None:
response = _test_completion(
client_server,
datadir,
"projec",
CompletionContext(trigger_kind=CompletionTriggerKind.Invoked),
)
item = next(filter(lambda x: x["label"] == "project", response["items"]), None)
assert item is not None
assert isinstance(item["documentation"], str)
assert "<PROJECT-NAME>" in item["documentation"]


def test_completions_nocontext(
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
@pytest.mark.parametrize(
"context", [CompletionContext(trigger_kind=CompletionTriggerKind.Invoked), None]
)
def test_completions(
context: Optional[CompletionContext],
client_server: Tuple[LanguageServer, CMakeLanguageServer],
datadir: Path,
) -> None:
response = _test_completion(client_server, datadir, "projec", None)
item = next(filter(lambda x: x["label"] == "project", response["items"]), None)
response = _test_completion(client_server, datadir, "projec", context)
item = next(filter(lambda x: x.label == "project", response.items), None)
assert item is not None
assert isinstance(item["documentation"], str)
assert "<PROJECT-NAME>" in item["documentation"]


def test_completions_triggercharacter_variable(
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
) -> None:
response = _test_completion(
client_server,
datadir,
"${",
CompletionContext(
trigger_kind=CompletionTriggerKind.TriggerCharacter, trigger_character="{"
),
)
assert "PROJECT_VERSION" in [x["label"] for x in response["items"]]

response_nocontext = _test_completion(client_server, datadir, "${", None)
assert response == response_nocontext


def test_completions_triggercharacter_module(
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
) -> None:
response = _test_completion(
client_server,
datadir,
"include(",
CompletionContext(
trigger_kind=CompletionTriggerKind.TriggerCharacter, trigger_character="("
),
)
assert "GoogleTest" in [x["label"] for x in response["items"]]
assert isinstance(item.documentation, str)
assert "<PROJECT-NAME>" in item.documentation

response_nocontext = _test_completion(client_server, datadir, "include(", None)
assert response == response_nocontext


def test_completions_triggercharacter_package(
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
@pytest.mark.parametrize(
"text, item",
[("find_package(", "Boost"), ("include(", "GoogleTest"), ("${", "PROJECT_VERSION")],
)
def test_completions_triggercharacter(
text: str,
item: str,
client_server: Tuple[LanguageServer, CMakeLanguageServer],
datadir: Path,
) -> None:
response = _test_completion(
client_server,
datadir,
"find_package(",
text,
CompletionContext(
trigger_kind=CompletionTriggerKind.TriggerCharacter, trigger_character="("
trigger_kind=CompletionTriggerKind.TriggerCharacter,
trigger_character=text[-1],
),
)
assert "Boost" in [x["label"] for x in response["items"]]
assert item in [x.label for x in response.items]

response_nocontext = _test_completion(client_server, datadir, "find_package(", None)
response_nocontext = _test_completion(client_server, datadir, text, None)
assert response == response_nocontext


Expand All @@ -180,13 +142,13 @@ def test_formatting(
path = datadir / "CMakeLists.txt"
_open(client, path, "a ( b c ) ")
response = client.lsp.send_request(
FORMATTING,
TEXT_DOCUMENT_FORMATTING,
DocumentFormattingParams(
text_document=TextDocumentIdentifier(uri=path.as_uri()),
options=FormattingOptions(tab_size=2, insert_spaces=True),
),
).result(timeout=CALL_TIMEOUT)
assert response[0]["newText"] == "a(b c)\n"
assert response[0].new_text == "a(b c)\n"


def test_hover(
Expand All @@ -197,10 +159,10 @@ def test_hover(
path = datadir / "CMakeLists.txt"
_open(client, path, "project()")
response = client.lsp.send_request(
HOVER,
TextDocumentPositionParams(
TEXT_DOCUMENT_HOVER,
HoverParams(
text_document=TextDocumentIdentifier(uri=path.as_uri()),
position=Position(line=0, character=0),
),
).result(timeout=CALL_TIMEOUT)
assert "<PROJECT-NAME>" in response["contents"]["value"]
assert "<PROJECT-NAME>" in response.contents.value
10 changes: 7 additions & 3 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
env_list = py{37,38,39,310}
isolated_build = True
passenv = *
setenv =
PDM_IGNORE_SAVED_PYTHON="1"

[gh-actions]
python =
Expand All @@ -11,7 +13,9 @@ python =
3.10: py310

[testenv]
groups = dev, lint
allowlist_externals =
pdm
commands =
lint
test
pdm install --dev -G :all
pdm run lint
pdm run test

0 comments on commit 35aa3cd

Please sign in to comment.