Skip to content

Commit aa10eb8

Browse files
committed
lsp: Implement completion argument suggestions for MyST
1 parent 9154fb7 commit aa10eb8

File tree

4 files changed

+84
-11
lines changed

4 files changed

+84
-11
lines changed

lib/esbonio/esbonio/server/features/directives/completion.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,12 @@ def render_rst_directive_with_insert_text(
172172

173173

174174
@directive_argument_renderer(language="rst", insert_behavior="replace")
175-
def render_directive_agument_with_text_edit(
175+
def render_rst_argument_with_text_edit(
176176
context: server.CompletionContext, item: types.CompletionItem
177177
) -> types.CompletionItem | None:
178178
"""Render a ``CompletionItem`` using ``textEdit``.
179179
180-
This implements the ``replace`` insert behavior for role targets.
180+
This implements the ``replace`` insert behavior for rst directive arguments.
181181
182182
Parameters
183183
----------
@@ -296,6 +296,30 @@ def render_myst_directive_with_text_edit(
296296
return item
297297

298298

299+
@directive_argument_renderer(language="markdown", insert_behavior="replace")
300+
def render_myst_argument_with_text_edit(
301+
context: server.CompletionContext, item: types.CompletionItem
302+
) -> types.CompletionItem | None:
303+
"""Render a ``CompletionItem`` using ``textEdit``.
304+
305+
This implements the ``replace`` insert behavior for MyST directive arguments.
306+
307+
Parameters
308+
----------
309+
context
310+
The context in which the completion is being generated.
311+
312+
item
313+
The ``CompletionItem`` representing the directive argument.
314+
315+
Returns
316+
-------
317+
Optional[types.CompletionItem]
318+
The rendered completion item, or ``None`` if the item should be skipped
319+
"""
320+
return item
321+
322+
299323
@directive_renderer(language="markdown", insert_behavior="insert")
300324
def render_myst_directive_with_insert_text(
301325
context: server.CompletionContext,

lib/esbonio/esbonio/server/features/myst/directives.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def __init__(self, directives: DirectiveFeature, *args, **kwargs):
2121
completion_trigger = server.CompletionTrigger(
2222
patterns=[MYST_DIRECTIVE],
2323
languages={"markdown"},
24-
characters={".", "`", "/"},
24+
characters={".", "`", "/", "{"},
2525
)
2626

2727
def initialized(self, params: types.InitializedParams):
@@ -49,24 +49,44 @@ async def completion(
4949
if "directive" not in groups:
5050
return await self.complete_options(context)
5151

52-
# Don't offer completions for targets
53-
if (groups["name"] or "").startswith("_"):
54-
return None
55-
5652
# Are we completing the directive's argument?
5753
directive_end = context.match.span()[0] + len(groups["directive"])
5854
complete_directive = groups["directive"].endswith("}")
5955

56+
directive_name = groups["name"]
6057
if complete_directive and directive_end < context.position.character:
61-
return await self.complete_arguments(context)
58+
return await self.complete_arguments(context, directive_name)
59+
60+
# Provide argument suggestions for `code-block` if the user is creating a regular code block.
61+
if "{" not in groups["directive"]:
62+
return await self.complete_arguments(context, "code-block")
6263

6364
return await self.complete_directives(context)
6465

6566
async def complete_options(self, context: server.CompletionContext):
6667
return None
6768

68-
async def complete_arguments(self, context: server.CompletionContext):
69-
return None
69+
async def complete_arguments(
70+
self,
71+
context: server.CompletionContext,
72+
directive_name: str,
73+
) -> list[types.CompletionItem] | None:
74+
"""Return completion suggestions for the current directive's argument."""
75+
76+
render_func = completion.get_directive_argument_renderer(
77+
context.language, self._insert_behavior
78+
)
79+
if render_func is None:
80+
return None
81+
82+
items = []
83+
suggestions = await self.directives.suggest_arguments(context, directive_name)
84+
85+
for argument in suggestions:
86+
if (item := render_func(context, argument)) is not None:
87+
items.append(item)
88+
89+
return items if len(items) > 0 else None
7090

7191
async def complete_directives(
7292
self, context: server.CompletionContext

lib/esbonio/esbonio/sphinx_agent/types/directives.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
(\s*) # directives can be indented
2222
(?P<directive>
2323
```(`*)? # directives start with at least 3 ` chars
24-
(?!\w) # -- regular code blocks are not directives
2524
[{]? # followed by an opening brace
2625
(?P<name>[^}]+)? # directives have a name
2726
[}]? # directives are closed with a closing brace

lib/esbonio/tests/workspaces/demo/myst/directives.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,33 @@ The most obvious feature is the completion suggestions, try inserting a `{note}`
99
% Add your note here...
1010

1111
Notice how VSCode automatically presented you with a list of all the directives you can use in this Sphinx project?
12+
13+
### Arguments
14+
15+
While directive names Just Work{sup}`TM`, Esbonio is only able to offer suggestions for specific argument types.
16+
By default Esbonio can provide suggestions for
17+
18+
**Filepaths**
19+
20+
Completing filepath arguments is supported for the following directives
21+
22+
- [`figure`](https://docutils.sourceforge.io/docs/ref/rst/directives.html#figure)
23+
- [`image`](https://docutils.sourceforge.io/docs/ref/rst/directives.html#image)
24+
- [`include`](https://docutils.sourceforge.io/docs/ref/rst/directives.html#image)
25+
- {external+sphinx:rst:dir}`literalinclude`
26+
27+
% Try using the `literalinclude` directive to insert the contents of this project's conf.py here...
28+
29+
30+
**Pygments Lexers**
31+
32+
Sphinx uses the [pygments](https://pygments.org/) library for its syntax highlighting, Esbonio will offer the names of available lexers as suggestions for the following directives.
33+
34+
- {external+sphinx:rst:dir}`code`
35+
- {external+sphinx:rst:dir}`code-block`
36+
- {external+sphinx:rst:dir}`highlight`
37+
- {external+sphinx:rst:dir}`sourcecode`
38+
39+
Of course MyST allows you to use standard Markdown code blocks as well, Esbonio will also offer suggestions in this context.
40+
41+
% Try inserting a code block on the next line...

0 commit comments

Comments
 (0)