Skip to content

Commit 4004f03

Browse files
committed
Add testing helpers in pylsp
1 parent 24d78a6 commit 4004f03

File tree

1 file changed

+115
-7
lines changed

1 file changed

+115
-7
lines changed

testsuite/drivers/pylsp.py

Lines changed: 115 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,16 @@
1111
import urllib
1212
import urllib.parse
1313
from pathlib import Path
14-
from typing import Any, Awaitable, Callable, Sequence, Type
14+
from typing import (
15+
Any,
16+
Awaitable,
17+
Callable,
18+
Literal,
19+
Sequence,
20+
Type,
21+
TypedDict,
22+
)
23+
import uuid
1524
import warnings
1625

1726
import attrs
@@ -36,6 +45,7 @@
3645
CallHierarchyItem,
3746
CallHierarchyPrepareParams,
3847
ClientCapabilities,
48+
DidChangeConfigurationParams,
3949
DidOpenTextDocumentParams,
4050
ExecuteCommandParams,
4151
InitializeParams,
@@ -201,8 +211,45 @@ def connection_made(self, transport: asyncio.Transport): # type: ignore
201211
return super().connection_made(transport)
202212

203213

214+
class OnTypeFormattingSetting(TypedDict):
215+
indentOnly: bool
216+
217+
218+
class ALSSettings(
219+
TypedDict,
220+
# This indicates that the dictionary keys can be omitted, they are not required to
221+
# appear
222+
total=False,
223+
):
224+
"""This class helps create a dictionary of ALS settings. It has to be manually
225+
updated when new settings are added.
226+
227+
So if you see a missing setting that you need, consider adding it.
228+
"""
229+
230+
defaultCharset: str | None
231+
displayMethodAncestryOnNavigation: bool | None
232+
documentationStyle: Literal["gnat", "leading"] | None
233+
enableDiagnostics: bool | None
234+
enableIndexing: bool | None
235+
foldComments: bool | None
236+
followSymlinks: bool | None
237+
insertWithClauses: bool | None
238+
logThreshold: int | None
239+
namedNotationThreshold: int | None
240+
onTypeFormatting: OnTypeFormattingSetting | None
241+
projectDiagnostics: bool | None
242+
projectFile: str | None
243+
relocateBuildTree: str | None
244+
renameInComments: bool | None
245+
rootDir: str | None
246+
scenarioVariables: dict[str, str] | None
247+
useCompletionSnippets: bool | None
248+
useGnatformat: bool | None
249+
250+
204251
class ALSLanguageClient(LanguageClient):
205-
"""This class provides methods to communicate with a language server."""
252+
"""This class provides methods to communicate with the Ada Language Server."""
206253

207254
def __init__(
208255
self,
@@ -215,6 +262,63 @@ def __init__(
215262

216263
super().__init__(*args, configuration=configuration, **kwargs)
217264

265+
async def getObjectDir(self) -> str | None:
266+
"""Send the "als-object-dir" command to obtain the object dir of the currently
267+
loaded project.
268+
"""
269+
return await self.workspace_execute_command_async(
270+
ExecuteCommandParams("als-object-dir")
271+
)
272+
273+
async def getObjDirBasename(self) -> str | None:
274+
"""Send the "als-object-dir" command to obtain the object dir of the currently
275+
loaded project, and return its basename.
276+
"""
277+
obj_dir = await self.getObjectDir()
278+
if obj_dir is not None:
279+
return os.path.basename(obj_dir)
280+
else:
281+
return None
282+
283+
def didChangeConfig(self, settings: ALSSettings) -> None:
284+
"""Send a workspace/didChangeConfiguration notification with as set of ALS
285+
settings.
286+
"""
287+
self.workspace_did_change_configuration(
288+
DidChangeConfigurationParams(settings={"ada": settings})
289+
)
290+
291+
def didOpenVirtual(
292+
self, uri: str | None = None, language_id="ada", version=0, text: str = ""
293+
) -> str:
294+
"""Send a didOpen notification for a file that doesn't exist on disk.
295+
296+
If the `uri` parameter is omitted, a random one is generated
297+
automatically with a `.ads` extension.
298+
299+
:param uri: the URI of the file. If None, that will be automatically
300+
generated and returned as a result of the call.
301+
:param language_id: the language_id parameter of the LSP notification.
302+
:param version: the version parameter of the LSP notification. Defaults
303+
to 0.
304+
:param text: the text parameter of the LSP notification.
305+
306+
:return: the URI of the document
307+
"""
308+
if uri is None:
309+
path = str(uuid.uuid4())
310+
if language_id == "ada":
311+
path += ".ads"
312+
else:
313+
path += "." + language_id
314+
uri = URI(path)
315+
316+
self.text_document_did_open(
317+
DidOpenTextDocumentParams(TextDocumentItem(uri, language_id, version, text))
318+
)
319+
320+
return uri
321+
218322

219323
def als_client_factory() -> ALSLanguageClient:
220324
"""This function is an ugly copy-paste of pytest_lsp.make_test_lsp_client. It is
@@ -647,13 +751,16 @@ def to_str(item: tuple[str, int]):
647751

648752
async def awaitIndexingEnd(lsp: LanguageClient):
649753
"""Wait until the ALS finishes indexing."""
754+
LOG.info("Awaiting indexing start and end")
755+
650756
indexing_progress = None
651757
while indexing_progress is None:
652758
await asyncio.sleep(0.2)
653-
LOG.info(
654-
"Awaiting indexing progress - lsp.progress_reports = %s",
655-
lsp.progress_reports,
656-
)
759+
if args.verbose >= 2:
760+
LOG.debug(
761+
"Awaiting indexing progress - lsp.progress_reports = %s",
762+
lsp.progress_reports,
763+
)
657764
indexing_progress = next(
658765
(prog for prog in lsp.progress_reports if "indexing" in str(prog)),
659766
None,
@@ -664,7 +771,8 @@ async def awaitIndexingEnd(lsp: LanguageClient):
664771
last_progress = lsp.progress_reports[indexing_progress][-1]
665772
while not isinstance(last_progress, WorkDoneProgressEnd):
666773
await asyncio.sleep(0.2)
667-
LOG.info("Waiting for indexing end - last_progress = %s", last_progress)
774+
if args.verbose >= 2:
775+
LOG.debug("Waiting for indexing end - last_progress = %s", last_progress)
668776
last_progress = lsp.progress_reports[indexing_progress][-1]
669777

670778
LOG.info("Received indexing end message")

0 commit comments

Comments
 (0)