-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Ensure that autocompletion works fine for simple things
- Loading branch information
Théophane Hufschmitt
committed
Jun 21, 2024
1 parent
a32698b
commit bcb0811
Showing
7 changed files
with
276 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__pycache__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import testlib | ||
import asyncio | ||
import pytest | ||
import pytest_asyncio | ||
from lsprotocol import types as lsp | ||
|
||
@pytest_asyncio.fixture | ||
async def client(): | ||
# Setup | ||
client = testlib.LanguageClient("organist-test-suite", "v1") | ||
await client.start_io("nls") | ||
response = await client.initialize_async( | ||
lsp.InitializeParams( | ||
capabilities=lsp.ClientCapabilities(), | ||
root_uri="." | ||
) | ||
) | ||
assert response is not None | ||
client.initialized(lsp.InitializedParams()) | ||
return client | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{ organist = import "../../../lib/organist.ncl" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import pytest | ||
import pytest_asyncio | ||
from lsprotocol import types as lsp | ||
from testlib import LanguageClient, open_file | ||
|
||
async def complete(client: LanguageClient, file_uri: str, pos: lsp.Position): | ||
""" | ||
Trigger an autocompletion in the given file at the given position | ||
""" | ||
results = await client.text_document_completion_async( | ||
params=lsp.CompletionParams( | ||
text_document=lsp.TextDocumentIdentifier(file_uri), | ||
position=pos, | ||
) | ||
) | ||
assert results is not None | ||
|
||
if isinstance(results, lsp.CompletionList): | ||
items = results.items | ||
else: | ||
items = results | ||
return items | ||
|
||
@pytest.mark.asyncio | ||
async def test_completion_at_toplevel(client): | ||
""" | ||
Test that getting an autocompletion at toplevel shows the available fields | ||
""" | ||
|
||
test_file = 'template/project.ncl' | ||
with open('../../templates/default/project.ncl') as template_file: | ||
test_file_content = template_file.read() | ||
|
||
test_uri = open_file(client, test_file, test_file_content) | ||
|
||
completion_items = await complete( | ||
client, | ||
test_uri, | ||
lsp.Position(line=12, character=0) # Empty line in the `config` record | ||
) | ||
|
||
labels = [item.label for item in completion_items] | ||
assert "files" in labels | ||
files_item = [item for item in completion_items if item.label == "files"][0] | ||
assert files_item.documentation.value != "" | ||
|
||
@pytest.mark.asyncio | ||
async def test_completion_sub_field(client: LanguageClient): | ||
""" | ||
Test that completing on an option shows the available sub-options | ||
""" | ||
test_file = 'template/projectxx.ncl' | ||
test_file_content = """ | ||
let inputs = import "./nickel.lock.ncl" in | ||
let organist = inputs.organist in | ||
organist.OrganistExpression | ||
& { | ||
Schema, | ||
config | Schema = { | ||
files.foo.c | ||
}, | ||
} | ||
| organist.modules.T | ||
""" | ||
test_uri = open_file(client, test_file, test_file_content) | ||
completion_items = await complete( | ||
client, | ||
test_uri, | ||
lsp.Position(line=8, character=17) # The `c` in `files.foo.c` | ||
) | ||
|
||
labels = [item.label for item in completion_items] | ||
assert "content" in labels | ||
content_item = [item for item in completion_items if item.label == "content"][0] | ||
assert content_item.documentation.value != "" | ||
|
||
@pytest.mark.asyncio | ||
async def test_completion_with_custom_module(client: LanguageClient): | ||
""" | ||
Test that completing takes into account extra modules | ||
""" | ||
test_file = 'template/projectxx.ncl' | ||
test_file_content = """ | ||
let inputs = import "./nickel.lock.ncl" in | ||
let organist = inputs.organist in | ||
organist.OrganistExpression & organist.tools.direnv | ||
& { | ||
Schema, | ||
config | Schema = { | ||
}, | ||
} | ||
| organist.modules.T | ||
""" | ||
test_uri = open_file(client, test_file, test_file_content) | ||
completion_items = await complete( | ||
client, | ||
test_uri, | ||
lsp.Position(line=8, character=0) # Empty line in the `config` record | ||
) | ||
|
||
labels = [item.label for item in completion_items] | ||
assert "direnv" in labels | ||
|
||
## No documentation for direnv yet | ||
# content_item = [item for item in completion_items if item.label == "direnv"][0] | ||
# assert content_item.documentation.value != "" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import pytest | ||
import pytest_asyncio | ||
from lsprotocol import types as lsp | ||
from testlib import LanguageClient, open_file | ||
from dataclasses import dataclass | ||
from typing import Callable, List | ||
|
||
async def hover(client: LanguageClient, file_uri: str, pos: lsp.Position): | ||
""" | ||
Trigger a hover in the given file at the given position | ||
""" | ||
results = await client.text_document_hover_async( | ||
params=lsp.HoverParams( | ||
text_document=lsp.TextDocumentIdentifier(file_uri), | ||
position=pos, | ||
) | ||
) | ||
return results | ||
|
||
@dataclass | ||
class HoverTest: | ||
file: str | ||
position: lsp.Position | ||
checks: Callable[[lsp.Hover], List[bool]] | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_hover_on_option(client: LanguageClient): | ||
""" | ||
Test that hovering over an option shows the right thing™ | ||
""" | ||
test_file = 'template/projectxx.ncl' | ||
test_file_content = """ | ||
let inputs = import "./nickel.lock.ncl" in | ||
let organist = inputs.organist in | ||
organist.OrganistExpression & organist.tools.direnv | ||
& { | ||
Schema, | ||
config | Schema = { | ||
files."foo.ncl".content = "1", | ||
shells = organist.shells.Bash, | ||
}, | ||
} | ||
| organist.modules.T | ||
""" | ||
test_uri = open_file(client, test_file, test_file_content) | ||
|
||
tests = [ | ||
HoverTest( | ||
file=test_uri, | ||
position=lsp.Position(line=8, character=11), # `files` | ||
checks= lambda hover_info: [ | ||
lsp.MarkedString_Type1(language='nickel', value='Files') in hover_info.contents, | ||
# Test that the contents contain a plain string (the documentation), and that it's non empty | ||
next(content for content in hover_info.contents if type(content) is str) != "", | ||
] | ||
), | ||
HoverTest( | ||
file=test_uri, | ||
position=lsp.Position(line=8, character=28), # `content` | ||
checks= lambda hover_info: [ | ||
lsp.MarkedString_Type1(language='nickel', value='nix.derivation.NullOr nix.derivation.NixString') in hover_info.contents, | ||
# Test that the contents contain a plain string (the documentation), and that it's non empty | ||
next(content for content in hover_info.contents if type(content) is str) != "", | ||
] | ||
), | ||
HoverTest( | ||
file=test_uri, | ||
position=lsp.Position(line=10, character=11), # `shells( =)` | ||
checks= lambda hover_info: [ | ||
lsp.MarkedString_Type1(language='nickel', value='OrganistShells') in hover_info.contents, | ||
# Test that the contents contain a plain string (the documentation), and that it's non empty | ||
next(content for content in hover_info.contents if type(content) is str) != "", | ||
] | ||
), | ||
] | ||
|
||
for test in tests: | ||
hover_info = await hover( | ||
client, | ||
test.file, | ||
test.position, | ||
) | ||
print(hover_info.contents) | ||
for check in test.checks(hover_info): | ||
assert check | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from pygls.lsp.client import BaseLanguageClient | ||
from typing import Optional | ||
import os | ||
from lsprotocol import types as lsp | ||
|
||
class LanguageClient(BaseLanguageClient): | ||
pass | ||
|
||
def open_file(client: LanguageClient, file_path: str, file_content: Optional[str] = None): | ||
""" | ||
Open the given file in the LSP. | ||
If `file_content` is non `None`, then it will be used as the content sent to the LSP. | ||
Otherwise, the actual file content will be read from disk. | ||
""" | ||
file_uri = f"file://{os.path.abspath(file_path)}" | ||
actual_file_content = file_content | ||
if file_content is None: | ||
with open(file_path) as content: | ||
actual_file_content = content.read() | ||
|
||
client.text_document_did_open( | ||
lsp.DidOpenTextDocumentParams( | ||
text_document=lsp.TextDocumentItem( | ||
uri=file_uri, | ||
language_id="nickel", | ||
version=1, | ||
text=actual_file_content | ||
) | ||
) | ||
) | ||
return file_uri | ||
|