Skip to content

Commit

Permalink
lsp: Add end-to-end tests for directive completions
Browse files Browse the repository at this point in the history
  • Loading branch information
alcarney committed Jan 14, 2024
1 parent 3e81a09 commit 231d8a7
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 9 deletions.
2 changes: 1 addition & 1 deletion lib/esbonio/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def uri_for():

def fn(*args):
path = (TEST_DIR / pathlib.Path(*args)).resolve()
assert path.exists()
assert path.exists(), f"{path} does not exist"
return Uri.for_file(str(path))

return fn
222 changes: 222 additions & 0 deletions lib/esbonio/tests/e2e/test_e2e_directives.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
from __future__ import annotations

import pathlib
import typing

import pytest
from lsprotocol import types
from pytest_lsp import LanguageClient

if typing.TYPE_CHECKING:
from typing import Optional
from typing import Set


EXPECTED = {
"function",
"module",
"option",
"program",
"image",
"toctree",
"c:macro",
"c:function",
"py:function",
"py:module",
"std:program",
"std:option",
}

UNEXPECTED = {
"macro",
"restructuredtext-test-directive",
}


@pytest.mark.parametrize(
"text, expected, unexpected",
[
(".", None, None),
("..", EXPECTED, UNEXPECTED),
(".. ", EXPECTED, UNEXPECTED),
(".. d", EXPECTED, UNEXPECTED),
(".. code-b", EXPECTED, UNEXPECTED),
(".. codex-block:: ", None, None),
(".. c:", EXPECTED, UNEXPECTED),
(".. _some_label:", None, None),
(" .", None, None),
(" ..", EXPECTED, UNEXPECTED),
(" .. ", EXPECTED, UNEXPECTED),
(" .. d", EXPECTED, UNEXPECTED),
(" .. doctest:: ", None, None),
(" .. code-b", EXPECTED, UNEXPECTED),
(" .. codex-block:: ", None, None),
(" .. _some_label:", None, None),
(" .. c:", EXPECTED, UNEXPECTED),
],
)
@pytest.mark.asyncio(scope="session")
async def test_rst_directive_completions(
client: LanguageClient,
uri_for,
text: str,
expected: Optional[Set[str]],
unexpected: Optional[Set[str]],
):
"""Ensure that the language server can offer directive completions in rst
documents."""
test_uri = uri_for("workspaces", "demo", "rst", "directives.rst")

uri = str(test_uri)
fpath = pathlib.Path(test_uri)
contents = fpath.read_text()
linum = contents.splitlines().index(".. Add your note here...")

# Open the file
client.text_document_did_open(
types.DidOpenTextDocumentParams(
text_document=types.TextDocumentItem(
uri=uri,
language_id="restructuredtext",
version=1,
text=contents,
)
)
)

# Write some text
#
# This should replace the '.. Add your note here...' comment in
# 'demo/rst/directives.rst' with the provided text
client.text_document_did_change(
types.DidChangeTextDocumentParams(
text_document=types.VersionedTextDocumentIdentifier(uri=uri, version=2),
content_changes=[
types.TextDocumentContentChangeEvent_Type1(
text=text,
range=types.Range(
start=types.Position(line=linum, character=0),
end=types.Position(line=linum + 1, character=0),
),
)
],
)
)

# Make the completion request
results = await client.text_document_completion_async(
types.CompletionParams(
text_document=types.TextDocumentIdentifier(uri=uri),
position=types.Position(line=linum, character=len(text)),
)
)

# Close the document - without saving!
client.text_document_did_close(
types.DidCloseTextDocumentParams(
text_document=types.TextDocumentIdentifier(uri=uri)
)
)

if expected is None:
assert results is None
else:
items = {item.label for item in results.items}
unexpected = unexpected or set()

assert expected == items & expected
assert set() == items & unexpected


@pytest.mark.parametrize(
"text, expected, unexpected",
[
("`", None, None),
("``", None, None),
("```", EXPECTED, UNEXPECTED),
("```{", EXPECTED, UNEXPECTED),
("```{d", EXPECTED, UNEXPECTED),
("```{code-b", EXPECTED, UNEXPECTED),
("```{codex-block} ", None, None),
("```{c:", EXPECTED, UNEXPECTED),
(" `", None, None),
(" ``", None, None),
(" ```", EXPECTED, UNEXPECTED),
(" ```{", EXPECTED, UNEXPECTED),
(" ```{d", EXPECTED, UNEXPECTED),
(" ```{doctest}", None, None),
(" ```{code-b", EXPECTED, UNEXPECTED),
(" ```{codex-block}", None, None),
(" ```{c:", EXPECTED, UNEXPECTED),
],
)
@pytest.mark.asyncio(scope="session")
async def test_myst_directive_completions(
client: LanguageClient,
uri_for,
text: str,
expected: Optional[Set[str]],
unexpected: Optional[Set[str]],
):
"""Ensure that the language server can offer completions in MyST documents."""
test_uri = uri_for("workspaces", "demo", "myst", "directives.md")

uri = str(test_uri)
fpath = pathlib.Path(test_uri)
contents = fpath.read_text()
linum = contents.splitlines().index("% Add your note here...")

# Open the file
client.text_document_did_open(
types.DidOpenTextDocumentParams(
text_document=types.TextDocumentItem(
uri=uri,
language_id="markdown",
version=1,
text=contents,
)
)
)

# Write some text
#
# This should replace the '% Add your note here...' comment in
# 'demo/myst/directives.md' with the provided text
client.text_document_did_change(
types.DidChangeTextDocumentParams(
text_document=types.VersionedTextDocumentIdentifier(uri=uri, version=2),
content_changes=[
types.TextDocumentContentChangeEvent_Type1(
text=text,
range=types.Range(
start=types.Position(line=linum, character=0),
end=types.Position(line=linum + 1, character=0),
),
)
],
)
)

# Make the completion request
results = await client.text_document_completion_async(
types.CompletionParams(
text_document=types.TextDocumentIdentifier(uri=uri),
position=types.Position(line=linum, character=len(text)),
)
)

# Close the document - without saving!
client.text_document_did_close(
types.DidCloseTextDocumentParams(
text_document=types.TextDocumentIdentifier(uri=uri)
)
)

if expected is None:
assert results is None
else:
items = {item.label for item in results.items}
unexpected = unexpected or set()

assert expected == items & expected
assert set() == items & unexpected
6 changes: 6 additions & 0 deletions lib/esbonio/tests/sphinx-agent/handlers/test_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ async def test_files_table(client: SubprocessSphinxClient):

expected = {
(anuri(src, "index.rst"), "index", "index.html"),
(anuri(src, "rst", "directives.rst"), "rst/directives", "rst/directives.html"),
(anuri(src, "rst", "symbols.rst"), "rst/symbols", "rst/symbols.html"),
(
anuri(src, "myst", "directives.md"),
"myst/directives",
"myst/directives.html",
),
(anuri(src, "myst", "symbols.md"), "myst/symbols", "myst/symbols.html"),
(anuri(src, "demo_rst.rst"), "demo_rst", "demo_rst.html"),
(anuri(src, "demo_myst.md"), "demo_myst", "demo_myst.html"),
Expand Down
11 changes: 11 additions & 0 deletions lib/esbonio/tests/workspaces/demo/myst/directives.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Directives

The language server has extensive support for directives.

## Completion

The most obvious feature is the completion suggestions, try inserting a `{note}` directive on the next line

% Add your note here...

Notice how VSCode automatically presented you with a list of all the directives you can use in this Sphinx project?
8 changes: 0 additions & 8 deletions lib/esbonio/tests/workspaces/demo/rst/directives.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,3 @@ The most obvious feature is the completion suggestions, try inserting a ``.. not
.. Add your note here...
Notice how VSCode automatically presented you with a list of all the directives you can use in this Sphinx project?

Goto ...
--------

The language server also provides a number of "Goto" navigation commands.
On the ``.. note::`` directive you inserted above, try each of the following commands

- ``Implementation`` goto the source code that implements the selected directive

0 comments on commit 231d8a7

Please sign in to comment.