Skip to content

feat: add completion and hover language-feature providers - #5

Merged
jourdain merged 3 commits into
masterfrom
language-feature-providers
Jun 16, 2026
Merged

feat: add completion and hover language-feature providers#5
jourdain merged 3 commits into
masterfrom
language-feature-providers

Conversation

@jlee-kitware

Copy link
Copy Markdown
Collaborator

Add 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

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.

@jlee-kitware
jlee-kitwareforce-pushed the language-feature-providers branch from 6fea1ee to 171adb0CompareJune 12, 2026 23:01
Add `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
@jlee-kitware
jlee-kitwareforce-pushed the language-feature-providers branch from 171adb0 to a6ece94CompareJune 12, 2026 23:45
@jlee-kitwarejlee-kitware self-assigned this Jun 13, 2026
Comment threadvue-components/src/components/Editor.js
Comment threadvue-components/src/components/Editor.js
Comment threadvue-components/src/components/Editor.js
@@ -0,0 +1,98 @@
"""Editor language features: completion + hover backed by a Python callback.

@jourdainjourdainJun 15, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()

Comment threadtrame_code/widgets/code.py Outdated
:param theme:
:param language:
:param textmate:
:param completion: name of a server ``@trigger`` that returns completion

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be function/method pointer directly. Let's compute their trigger internally.

Comment threadtrame_code/widgets/code.py Outdated

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Collaborator

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

Copy link
Copy Markdown
CollaboratorAuthor

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.

Comment threadexample/live-state/app.py Outdated

import re

from trame.app import get_server

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make use of TrameApp instead of get_server?

@jourdain

Copy link
Copy Markdown
Collaborator

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

Copy link
Copy Markdown
CollaboratorAuthor

this looks alot better, thanks for the guidance.

@jourdain
jourdain merged commit 522f513 into masterJun 16, 2026
6 checks passed
@jourdain
jourdain deleted the language-feature-providers branch June 16, 2026 15:17
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@jlee-kitware@jourdain