feat: hover-to-open navigation for RPC and function references - #369
feat: hover-to-open navigation for RPC and function references#369Kateřina Beňová (KatBen-Make) wants to merge 6 commits into
Conversation
Add a hover popup over RPC references (rpc://Name) and custom IML function calls in app code. Clicking the popup link opens the referenced component's code file and reveals it in the sidebar. Works in both online mode (cloud temp files, opened via apps-sdk.load-source and revealed in the apps tree) and local-development mode (resolved via makecomapp.json + idMapping, opened from disk and revealed in the explorer). Built on the shared app-component-search helper (fetchAppComponentsSummary, buildComponentTreeItem). Token detection and online-path parsing live in a pure, unit-tested module (src/libs/component-reference.ts). Co-authored-by: Cursor <cursoragent@cursor.com>
Apply the dev-conventions distilled from prior PR reviews (#361/#362/#366): - Type the open-referenced-component command target instead of using an untyped parameter. - Fetch the live app tree node and component summary in parallel, wrapped in a progress notification, instead of a sequential synthetic-node path. - Make reveal failures non-fatal and log them instead of throwing after the file has already opened successfully. - Restrict imljson function-reference detection to {{ }} template regions and use half-open ranges so trailing characters are not matched. - Fix the online-cache to evict on rejection instead of replaying a failed fetch, and cache negative local makecomapp.json lookups per directory. - Update unit tests to cover the new detection and caching behavior. Co-authored-by: Cursor <cursoragent@cursor.com>
Generated |
There was a problem hiding this comment.
Pull request overview
Adds editor hover support to navigate from in-code component references (e.g. rpc://Name and custom IML function calls) to the referenced component’s source, integrating with both online (temp-file + tree reveal) and local-development (disk file + explorer reveal) workflows in the Make Apps VS Code extension.
Changes:
- Introduces
ComponentReferenceHoverProviderto detect references on hover, resolve them against the app’s known components, and offer an Open link. - Adds a pure helper library (
src/libs/component-reference.ts) with unit tests for reference detection and online temp-path parsing. - Registers a hidden command (
apps-sdk.open-referenced-component) and wires it up to open/reveal the referenced code in online and local modes.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/providers/ComponentReferenceHoverProvider.ts | New hover provider that resolves references (online via API summary; local via makecomapp.json) and renders an “Open” hover link. |
| src/libs/component-reference.ts | New pure helper for syntactic reference detection + online temp-path context parsing. |
| src/libs/component-reference.test.ts | Unit tests for detection scopes/ranges and online path parsing. |
| src/extension.ts | Registers the hover provider and the apps-sdk.open-referenced-component command implementation. |
| package.json | Contributes the new command and hides it from the command palette. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/providers/ComponentReferenceHoverProvider.ts:243
markdown.isTrusted = truetrusts any command URI that might end up in this MarkdownString. Since this hover only needs a single command, it’s safer to restrict trust to that command viaenabledCommands.
markdown.isTrusted = true;
src/providers/ComponentReferenceHoverProvider.ts:132
- Local reference resolution can repeatedly throw and be re-tried when the opened file is outside the current workspace (or when the cached root is outside the workspace). Since
getMakecomappRootDirenforces “file must be in workspace”, consider short-circuiting early and then reusing the already-found rootappRootFsPathwhen loading makecomapp.json to avoid an extra upward walk on every hover.
const appRootFsPath = this.findLocalAppRoot(documentUri.fsPath);
if (!appRootFsPath) {
return undefined;
}
- Tolerate an apps-list fetch failure in open-referenced-component: log and fall back to an empty list instead of failing the whole command, since the file can still be opened from target.appName/appVersion alone. - Restrict MarkdownString.isTrusted to the single command the hover link actually invokes, instead of trusting every command URI. - Grammar: "That allow-list" -> "This allow-list". Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/providers/ComponentReferenceHoverProvider.ts:126
- In local-development resolution,
findLocalAppRoot()already walked/cached themakecomapp.jsonroot, but the subsequentgetMakecomappRootDir(documentUri)andgetMakecomappJson(documentUri)will re-walk the filesystem (andgetMakecomappJsoncallsgetMakecomappRootDiragain). This largely defeats the negative-result caching and adds avoidable synchronous FS work on every hover. Use the cachedappRootFsPathas the anchor when resolving the root and readingmakecomapp.jsonso the follow-up checks are O(1).
let root: vscode.Uri;
let makecomappJson: Awaited<ReturnType<typeof getMakecomappJson>>;
try {
root = getMakecomappRootDir(documentUri);
makecomappJson = await getMakecomappJson(documentUri);
| let makecomappJson: Awaited<ReturnType<typeof getMakecomappJson>>; | ||
| try { | ||
| root = getMakecomappRootDir(documentUri); | ||
| makecomappJson = await getMakecomappJson(documentUri); |
There was a problem hiding this comment.
This call also writes - it migrates and saves. So a mouse hover mutates a file tracked by git. migrateMakecomappJsonFile will update manifest after hover over rpc://example
There was a problem hiding this comment.
Good catch - fixed in d5622a6. Added an opt-in { readOnly: true } option to getMakecomappJson() that skips the migrate-and-save write-back, and the hover resolver now passes it. All other (write-intent) call sites are unaffected since the option defaults to off.
|
|
||
| constructor(private readonly authorization: string, private readonly environment: Environment) {} | ||
|
|
||
| async provideHover( |
There was a problem hiding this comment.
provideHover drops the CancellationToken, so we keep doing network work after VS Code has cancelled the hover (i.e. after the user moved the mouse away).
Low impact because the per-app promise is cached, but it's free to bail on token.isCancellationRequested
There was a problem hiding this comment.
Fixed in d5622a6: provideHover now takes the CancellationToken and both resolveOnline/resolveLocal check token.isCancellationRequested after their awaits, bailing out early instead of continuing.
| * Walks from the file's directory upward looking for `makecomapp.json`, caching hits and misses | ||
| * per directory so unrelated workspace `.js` hovers do not repeat the walk. | ||
| */ | ||
| private findLocalAppRoot(fileFsPath: string): string | null { |
There was a problem hiding this comment.
could we reuse getMakecomappRootDir?
There was a problem hiding this comment.
Fixed in d5622a6: findLocalAppRoot now delegates to getMakecomappRootDir() instead of a hand-rolled fs.existsSync walk, so it also correctly respects the workspace boundary (which the old walk did not).
| const codeName = target.supertype === 'rpc' ? 'api' : 'code'; | ||
| const language = target.supertype === 'rpc' ? 'imljson' : 'js'; |
There was a problem hiding this comment.
rpc -> api/imljson and function -> code/js is already encoded in AppsProvider.js and in component-code-def.ts. Any future component type means remembering to update three places. Can we have a centralized place for this?
There was a problem hiding this comment.
Fixed in d5622a6, scoped to this feature: added REFERENCE_CODE_DEF in component-reference.ts so extension.ts and ComponentReferenceHoverProvider now share one rpc/function -> code-file mapping instead of each hardcoding it. I did not attempt to also unify this with AppsProvider.js / component-code-def.ts, since those cover every component type (module/webhook/connection/endpoint) and touching them would be a materially larger, unrelated refactor - happy to file that separately if useful.
| * path upward from a failed resolve) is not under a `makecomapp.json`. Avoids re-walking the | ||
| * filesystem on every hover over `foo(` in unrelated `.js` files. | ||
| */ | ||
| private readonly localAppRootCache = new Map<string, string | null>(); |
There was a problem hiding this comment.
This cache stores negative results and never invalidates. So if someone hovers in a folder before cloning an app there, that directory is remembered as "not a Make project" and after they clone, hover stays dead until they reload the window. They'd have no idea why.
Worth clearing the cache on a makecomapp.json create
There was a problem hiding this comment.
Fixed in d5622a6: added a **/makecomapp.json FileSystemWatcher in extension.ts that clears the local-app-root cache on create/delete, via a new clearLocalAppRootCache() method.
- Critical: getMakecomappJson() migrates-and-saves makecomapp.json when needed, so a plain hover could silently dirty a git-tracked file. Add an opt-in readOnly option and use it from the hover resolver; all other (write-intent) call sites are unaffected. - Respect the CancellationToken VS Code passes into provideHover, bailing out after each await instead of continuing network/FS work once the user has moved the mouse away. - Reuse getMakecomappRootDir() instead of re-implementing the upward makecomapp.json walk with raw fs.existsSync, which also makes the local resolution correctly respect the workspace boundary. - Centralize the rpc/function -> code-file mapping (REFERENCE_CODE_DEF in component-reference.ts) so extension.ts and ComponentReferenceHoverProvider share one source instead of each hardcoding it. - Clear the local-app-root cache when a makecomapp.json is created or deleted anywhere in the workspace (FileSystemWatcher in extension.ts), so hover does not stay dead in a directory after an app is cloned into it. Co-authored-by: Cursor <cursoragent@cursor.com>
…tegromat/vscode-apps-sdk into feat/app-component-navigation
Jira task
https://make.atlassian.net/browse/IEN-15822
Summary
pc://Name reference or a custom IML function call (e.g. {{getTimeActivityBody(parameters)}}) shows an Open link that opens the referenced component's code and reveals it in the sidebar / file explorer.
Test plan
equire()-import warnings remain in extension.ts)
pc://Name and a custom function call in both online and local-development mode shows the popup and Open correctly navigates and reveals the component