Uh oh!
There was an error while loading. Please reload this page.
feat: add completion and hover language-feature providers - #5
Conversation
6fea1ee to
171adb0CompareAdd `completion` and `hover` props to the Editor widget. Each names a trame @trigger that receives (code, line, column) and returns results; the component registers Monaco completion/hover providers that call the trigger over the existing websocket and map the normalized results onto Monaco. The consumer writes only Python: no client-side JavaScript and no access to the Monaco instance are required. - completion items: {label, kind, detail, documentation, insertText} - hover: a markdown string, a list of strings, or {contents: [...]} - positions: line is 1-based, column is 0-based - requests honor Monaco's CancellationToken; providers are disposed on unmount and re-registered when the language changes - adds a jedi-backed Python example under example/language-features
171adb0 to
a6ece94CompareUh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| @@ -0,0 +1,98 @@ | |||
| """Editor language features: completion + hover backed by a Python callback. | |||
There was a problem hiding this comment.
This file could looks like
"""Editor language features: completion + hover backed by a Python callback.The ``completion`` and ``hover`` props on ``code.Editor`` each name a trame``@trigger`` that receives ``(code, line, column)`` and returns results. Hereboth are backed by jedi, giving live Python completion and docstring-on-hoverentirely in-process, with no client-side JavaScript.The contract:* completion trigger returns a list of items, each a dict with keys ``label`` (required), ``kind``, ``detail``, ``documentation``, ``insertText``.* hover trigger returns a markdown string, a list of markdown strings, or ``{"contents": [...]}`` (or ``None`` for no hover).* positions are passed as ``line`` (1-based) and ``column`` (0-based), matching jedi's API directly.Run with:: pip install trame trame-vuetify trame-code jedi python app.py"""importjedifromtrame.appimportTrameAppfromtrame.ui.vuetify3importSinglePageLayoutfromtrame.widgetsimportcodefromtrame.widgetsimportvuetify3asv3INITIAL_CODE='''import mathdef circle_area(radius): """Return the area of a circle with the given radius.""" return math.pi * radius**2# Type "math." below, or hover a name, to see completion and docstrings.math.'''classPyCodeEditor(TrameApp):
def__init__(self, server=None):
super().__init__(server)
self._build_ui()
def_build_ui(self):
self.state.trame__title="PyEditor"withSinglePageLayout(server) asself.ui:
self.ui.title.set_text("Editor language features (jedi)")
withself.ui.content:
withv3.VContainer(fluid=True, classes="fill-height pa-0"):
code.Editor(
value=INITIAL_CODE,
language="python",
theme="vs",
style="width: 100%; height: 100%;",
completion=self.on_completion,
hover=self.on_hover,
)
defon_completion(self, code_text, line, column):
"""Completion items for (code, line, column). line 1-based, column 0-based."""try:
completions=jedi.Script(code=code_text).complete(line, column)
exceptException:
return []
return [
{
"label": c.name,
"kind": c.type,
"detail": (c.descriptionor"")[:80],
}
forcincompletions[:200]
]
defon_hover(self, code_text, line, column):
"""Hover markdown (signature + docstring) for the symbol at the cursor."""try:
definitions=jedi.Script(code=code_text).help(line, column)
exceptException:
returnNoneifnotdefinitions:
returnNonedefinition=definitions[0]
contents= []
signatures= [s.to_string() forsindefinition.get_signatures()]
ifsignatures:
contents.append("```python\n"+"\n".join(signatures) +"\n```")
doc=definition.docstring(raw=True) or""ifdoc:
contents.append(doc)
return {"contents": contents} ifcontentselseNoneif__name__=="__main__":
app=PyCodeEditor()
app.server.start()| :param theme: | ||
| :param language: | ||
| :param textmate: | ||
| :param completion: name of a server ``@trigger`` that returns completion |
There was a problem hiding this comment.
should be function/method pointer directly. Let's compute their trigger internally.
There was a problem hiding this comment.
def__init__(self, completion=None, hover=None, **kwargs):
super().__init__(
"vs-editor",
**kwargs,
)
self._attr_names+= [
"options",
"value",
("model_value", "modelValue"),
"theme",
"language",
"textmate",
("completion_trigger_characters", "completionTriggerCharacters"),
]
self._event_names+= [
"input",
]
ifcompletionisnotNone:
self._attributes["completion_trigger"] =f'completion="{self.ctrl.trigger_name(completion)}"'ifhoverisnotNone:
self._attributes["hover_trigger"] =f'hover="{self.ctrl.trigger_name(hover)}"'jourdain
commented
Jun 15, 2026
Looks great, but lets make it easier on the user. |
Address review: pass a function/method to completion/hover instead of a trigger-name string. The widget registers it via ctrl.trigger_name and hands the client the generated name, keeping the trigger internal while still returning results to Monaco's providers. Convert both examples to the callable form and update the test to the callable contract.
jlee-kitware
commented
Jun 15, 2026
Done in the follow-up commit, switched completion/hover to take the callable directly and register the trigger internally via ctrl.trigger_name, as you laid out. Both examples now pass the function. |
| import re | ||
| from trame.app import get_server |
There was a problem hiding this comment.
Can you make use of TrameApp instead of get_server?
jourdain
commented
Jun 15, 2026
Happy to merge as-is, but ideally I would like the examples to lineup with the latest best practice. |
Match the language-features example: wrap the live-state demo in a TrameApp subclass with the completion callback as a method, instead of a bare get_server plus module-level layout.
jlee-kitware
commented
Jun 16, 2026
this looks alot better, thanks for the guidance. |
Add
completionandhoverprops to the Editor widget. Each names a trame @trigger that receives (code, line, column) and returns results; the component registers Monaco completion/hover providers that call the trigger over the existing websocket and map the normalized results onto Monaco. The consumer writes only Python: no client-side JavaScript and no access to the Monaco instance are required.this is not an LSP as what is proposed in the language-servers branch. as such we can register other complete handlers like a live-state contextual complete as demonstrated in the example. This capability is complementary to the LSP capability.