58 Commits
Author SHA1 Message Date
Arnaud Ritti 1446d5592c feat(plugins): refresh diagnostics while typing (#2048)
Closes #1952

API 1.3 adds a diagnostics provider for unsaved documents: the host sends versioned text for the declared languages after a 300 ms debounce, cancels superseded calls, drops late or stale answers under the editor-provider budget, and clears results with the document, project or plugin. Ranges are UTF-16. Actor panics are contained, queued work is rejected after a host panic, and diagnostics arriving after plugin cleanup are discarded. Verified on macOS with the diagnostics fixture.

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-08 04:14:27 +08:00
Deng Liuanddengliu fbf57bed23 feat(plugins): generic streaming log editor tab for plugin web views (#2043)
A plugin web view can open a read-only editor tab that streams a process's output, with plugin-defined toggles, Find, wrap, zoom and Clear. The process is authorised in the backend against the exact granted `process:<program>` and its pinned executable, output is bounded, and the stream survives tab switches but ends when the tab closes, the plugin is disabled or the window closes. Adds a web-log test fixture and docs. Verified on macOS; Windows/Linux not run.

Co-authored-by: dengliu <[email protected]>
2026-11-08 04:06:04 +08:00
Deng Liuanddengliu af19880390 feat(plugins): PTY session bridge for plugin web views (#2038)
A plugin web view can open interactive PTY sessions (spawn, input, resize, kill, streamed output and exit). Spawning requires the exact granted `process:<program>` and its pinned executable, with no PATH re-resolution or fallback shell; the cwd is the trusted project, arguments, environment (append-only) and sizes are validated, and at most eight sessions run per plugin. A frame can only drive the sessions it opened; output is acknowledged chunk by chunk and input/output are bounded. Sessions end on permission change, reload, disable, uninstall, frame teardown or window close. Requires API ^1.3; adds a web-pty test fixture. Verified on macOS; Windows/Linux not run.

Co-authored-by: dengliu <[email protected]>
2026-11-08 03:29:36 +08:00
Arnaud Ritti 4a78310f76 feat(plugins): support detail panes for tree and list views (#2033)
Closes #1950

Plugins can declare `detail = true` on a tree or list view (any tool-window side, grouped or not) to show the selected item's detail-provider tabs beside the list. The pane follows selection and refresh, clears stale content when the item, project or remote host changes, shows an empty state when nothing is selected, and remembers its resizable divider. Services keeps its single split. Released WIT and IPC shapes are unchanged. Verified on macOS in light and dark themes.

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-08 03:18:44 +08:00
Arnaud Ritti 7cedf0b9be feat(plugins): expose indexed workspace symbol search (#2040)
Closes #1944

API 1.3 lets a plugin search the IDE's workspace symbol index. Results are limited to paths the plugin may read under its `project.read` grant (external content roots and symlink aliases excluded), the search honours the call's cancellation and deadline including lock waits, and ranges use UTF-16. Incremental index updates no longer publish symlink-ancestor aliases. Verified on macOS with the test fixture.

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-08 03:15:08 +08:00
Arnaud Ritti 0bee9b7aab feat(plugins): allow custom gutter marker icons (#2044)
Closes #1951

Gutter markers only recognised a short alias list and the runtime truncated icon references at 32 bytes, so ordinary icon names and package paths were dropped. Markers now accept any built-in icon name, and API 1.3 `contributes.markerIcons` declares package SVGs (with an optional dark variant) that are size-limited, sanitised and rendered through the icon registry. The shared SVG sanitiser now also rejects URL strings in any CSS function (e.g. image-set), not only url(). Verified on macOS in light and dark themes.

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-08 03:12:01 +08:00
WangTongchaoandchaoes 3efec1c848 feat(plugins): let a plugin keep a process running behind a handle (#2027)
Closes #1826

API 1.3 adds `run-command` (one-shot with stdin, cwd and environment) and a handle-based `start` for a long-running process with incremental stdin/stdout/stderr, both gated by the existing `process:<program>` grants. A plugin's environment entries are append-only: a key the child would already inherit (including the login-shell PATH) is refused with an error naming it, as are empty or malformed keys. Processes run in their own process group and are killed with their descendants when the handle is dropped, the plugin is disabled, unloaded or reclaimed, or the owning window closes; at most eight handles per plugin. Output is bounded and UTF-8 safe, stdin writes honour cancellation, and validation failures return typed errors instead of trapping the plugin. Verified on macOS; Windows/Linux not run.

Co-authored-by: chaoes <[email protected]>
2026-11-08 02:53:04 +08:00
Arnaud Ritti f6531542de feat(plugins): add fixed filenames and file template variables (#2032)
Closes #1954

Plugin file templates could only take a typed name plus a fixed extension, so dot-files such as .dockerignore and templates needing more than a name weren't expressible. Templates (API ^1.3) can now declare a fixed `fileName` and named `variables` that the New File dialog asks for, and plugin templates get the same Java/Kotlin/Go/PHP package inference as built-in ones. Unsafe filenames and existing files are refused. The dialog no longer submits on Enter while an IME is composing, and its variable form has proper padding. Adds a file-templates test fixture.

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-08 02:37:26 +08:00
Arnaud Ritti 9e6cb7b93e feat(plugins): allow prompts while resolving code actions (#2025)
Closes #1945

Resolving a lazy code action or file action dropped the invoking window, so a plugin couldn't ask the user anything (input, pick, confirm) while building its edit. Resolution now runs with that window, so prompts work during resolution; previews and Actions on Save never prompt, cancellation keeps the text unchanged, and stale edits are rejected. WIT and SDK docs describe the new behaviour; the released API surface is unchanged.

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-08 02:26:26 +08:00
Arnaud Ritti da3d996e10 feat(plugins): group views and apply visibility conditions (#2019)
Closes #1949. Plugin API 1.3: views from several plugins can share one tool window (shown as tabs) and a view can declare a visibility condition (e.g. only when the project contains a file, or for the active file) so it appears only when it applies. Additive within API 1.x, with a fixture plugin, regression tests and docs.

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-08 02:04:10 +08:00
WangTongchaoandchaoes 3f65cb0f57 feat(plugins): let a plugin write files, inside and outside the project (#2016)
Adds fs.write-file / fs.write-bytes behind a new fs.write:<path> capability (high risk; resolved to the real path, a missing file through its real directory, so a link cannot reach outside the grant; writing doesn't imply reading), and workspace.project.write-file(path, text) behind project.write (medium risk; stays inside the root and creates missing directories).

Writes go through the editor's save path: atomic, keeping the file's encoding and BOM (new files are UTF-8), capped at 16 MiB, and refused while an editor holds unsaved changes for the file. Both capabilities need engines.openidea ^1.3. Tested through the real component (misbehave fixture) and the SDK mock.

Closes #1823
Closes #1824

Co-authored-by: chaoes <[email protected]>
2026-11-07 23:57:13 +08:00
WangTongchaoandchaoes 377a8c8379 feat(plugins): let a plugin read files outside the project (#2006)
Adds the API 1.3 `fs` import behind the existing `fs.read:<path>` capability: `read-file` (decoded like the editor, unsaved contents win), `read-bytes`, `stat` and `read-dir` (sorted, at most 4096), all at most 16 MiB. Paths are absolute or `~/`; the requested path and the grant are both resolved to their real location before the prefix check, so `..`, symlinks and name-prefix siblings can't climb out, and a whole-file-system grant is refused. A refusal is `denied` naming the `fs.read:<path>` to declare. Reuses `workspace.file-kind` / `file-stat`; SDK `host::fs`. Closes #1822, closes #1825.

Co-authored-by: chaoes <[email protected]>
2026-11-07 23:45:21 +08:00
Arnaud Ritti dc69c36604 feat(plugins): read-only form fields and table row interactions (#2013)
API 1.3 through the released `other` extension cases, no WIT change: form fields `label` (plain value) and `readonly-text` (selectable, optionally multi-line, drawn as read-only), and table view-events `table-select` / `table-activate` (click, Space; double-click, Enter) with absolute row indices. Table rows also get the view's `viewItem.context` commands, keyed by row. SDK: `FieldValue::label`, `FieldValue::readonly_text`, `ViewEvent::table_select` / `table_activate`. Closes #1953.

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-07 23:36:59 +08:00
Arnaud Ritti 1891119f9d feat(plugins): filter file events by path and resolve a document's project (#2003)
Adds API 1.3 `events.subscribe-paths(kind, globs)` for `file-opened` and `file-saved`: project-relative globs checked before an event is queued or wakes an idle plugin, and again before a queued event is delivered. A new filter replaces the old one atomically; `subscribe` restores unfiltered delivery; filters survive idle reclamation. Also `workspace.project-of(path)`, the most specific open project containing a document (reading it still needs `project.read`). Closes #1958.

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-07 23:30:28 +08:00
Arnaud Ritti 668206ddfd feat(plugins): share project HTTP endpoints with the HTTP client (#1991)
Adds the API 1.3 `endpoints-provider` export and `[[contributes.endpoints]]` (file globs, ^1.3, `project.read`). A plugin's routes join the Spring ones in Search Everywhere, `.http` URL completion and the declaration's Open in HTTP Client gutter action. The UI asks `plugin_endpoints` on demand; the host runs the plugin on a blocking thread, validates the whole list (bounds, project-relative locations, UTF-16 ranges) and caches it per project until a file matching the globs changes, then emits `EndpointsChanged`. A failed or invalid run keeps the last list; disabling, crashing or closing the project withdraws it, and late results can't restore it. Closes #1960.

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-07 22:59:21 +08:00
Arnaud Ritti 112ed773fb feat(plugins): notifications with command buttons (#1974)
Adds API 1.3 `ui.notify(level, title, body, actions) -> result<_, error>`: up to three buttons, each running one of the plugin's declared commands with an optional argument in the balloon's window and dismissing the balloon. An invalid action or an undeclared command fails the call and shows nothing. The plugin `Notification` event carries the actions; SDK `host::notify_with_actions` and `NotificationAction`. Closes #1955.

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-07 22:35:36 +08:00
Arnaud Ritti f3837e0fe7 feat(plugins): let window commands choose folders and open projects (#1998)
Adds API 1.3 `ui.pick-directory` (native folder chooser, same window/budget rules as `pick`) and `workspace.open-project(path, new-window)`, which goes through the normal File › Open flow with its trust prompt, window reuse and unsaved-change handling. Paths are absolute on the UI machine and validated by the host whatever its OS. Closes #1959.

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-07 22:02:38 +08:00
Arnaud Ritti 25d930b780 feat(plugins): let a code action create, rename and delete files (#1778)
A new optional file-actions export (worlds plugin-file-actions / file-actions-provider, API 1.3) lists quick fixes whose operations (create with text, rename, delete) and text edits are applied as one workspace edit, so a failure changes nothing and one undo reverses it; paths stay inside the project root, and `open` shows a file once the action is applied. Like providers' code actions, an action can carry only `data` and get its operations from `resolve` when picked.

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-07 21:24:50 +08:00
Vadim LazukandInf1mum 309bb48f2c feat(plugins): clickable column headers with sort events for plugin tables (#1785)
A plugin opts a table into click-to-sort by sending it as view body other(kind "table") with its sort; a header click comes back as view-event other(kind "sort", {column, descending}), so no released WIT type changes. The SDK builds and decodes both (Body::sortable_table, ViewEvent::sort). Headers are keyboard-operable, carry aria-sort and show the reported order with an arrow.

Co-authored-by: Inf1mum <[email protected]>
2026-11-07 21:09:18 +08:00
arookieofc 2945d20208 feat(plugin): multi-line text input via ui.input-multiline (#1968)
* feat(plugin): multi-line text input via ui.input-multiline

Adds host::input_multiline: a multi-line text dialog pre-filled with a
value, dismissed to none, confirmed with Cmd/Ctrl+Enter. Same rules as
input (command or event of a window, no capability, wait not charged).

Prompt::Input gains a multiline flag the frontend renders as a
textarea; the jobs fixture, SDK (wasm + mock), WIT and the plugin API
table are updated, and all plugin wasms rebuilt.

Fixes #1946

* chore: regenerate bindings after merging detail-provider rename
2026-11-07 19:58:32 +08:00
lidwt721andlidwt721 e68b35f534 feat(plugins): group a plugin's context menu entries under a submenu (#1671)
Context menu entries of a plugin can name a submenu on [[contributes.menus]] (editor, Project view, editor tab and view item menus; API 1.3): entries with the same submenu are grouped under it, a submenu with one visible entry stays a plain entry, and the label goes through the plugin's translations. Opt-in rather than by command category, since the category is the Find Action/keymap prefix and many plugins share one across all commands.

Co-authored-by: lidwt721 <[email protected]>
2026-11-07 19:53:26 +08:00
Arnaud Ritti adc308eb99 feat(plugins): let a plugin ship translations and read the UI language (#1781)
A package's locales/<tag>.json (English source text -> translation) is applied to the plugin's own manifest titles and labels and to its view data; new ui.locale() (API 1.3) returns the UI language for text the plugin builds itself. Catalogs only ever apply to their own plugin; text without an entry stays English.

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-07 19:40:50 +08:00
Pedro Pessoa 56796fb2c6 feat(plugins): let plugins show inlay hints in any language (#1937)
A new optional inlay-hints-provider export (worlds plugin-inlay-hints / inlay-hints), declared as the inlay-hints provider kind for any language (API 1.3). The host asks the language server and the plugins together for the visible range and returns both in position order; with no server hints the plugins' hints still show. Kinds map to the Inlay Hints settings; labels, tooltips and counts are capped.

Co-authored-by: yzPeedro <[email protected]>
2026-11-07 19:25:10 +08:00
l0ng-ai 7a9c72ca24 refactor(plugins): name the services pane export detail-provider (#1972)
Renamed before API 1.3 ships, so tree and list views outside Services can reuse the pane (#1950); behaviour unchanged.
2026-11-07 19:06:42 +08:00
Arnaud Ritti 17a121e7f5 fix(plugins): allow 64 MiB of persistent storage per plugin (#1967) 2026-11-07 18:51:23 +08:00
arookieofc 303d5950bf fix(plugin): report missing files as not-found and add project exists/stat (#1964)
* fix(plugin): report missing files as not-found and add project exists/stat

read-file of a missing path now returns error::not-found in the host,
matching the SDK mock, instead of error::invalid. project gains
exists(path) for a cheap existence check (files and directories, hidden
and ignored included) and stat(path) for kind/size/modified, both
gated by project.read and refusing paths outside the root.

Fixes #1957

* chore: silence clippy in sandbox test and rebuild plugin components

- drop an unnecessary to_path_buf clippy flags on unix
- rebuild every checked-in plugin.wasm: the SDK and wit/ changed, so
  their source fingerprints moved

* test(plugin): cover missing-file not-found and fix CRLF-only Windows failure
2026-11-07 18:01:33 +08:00
l0ng-ai d235c432ec feat(plugins): commit workflow API for plugins (#1941)
Plugin API 1.3 adds what commit-message generators and before-commit checks need:

- `menu = "commit.message"`: a plugin command, with the plugin's icon, in the toolbar above the commit message of the Commit tool window and the Commit dialog.
- `commit.pending-commit()` (`vcs.read`): what Commit would commit right now, per file its repository, path, status and a `git diff` of only the included files and changed blocks against HEAD (1 MiB in all), plus the message, Amend and a revision. The diff is made by the same selection the commit uses, in a copy of the index, so the index and the working tree stay as they are; unsaved editors of the included files are saved first, as Commit does. Only in trusted projects.
- `commit.set-commit-message(revision, message)` (`vcs.write`): replaces the message only while the draft is still at that revision, so text the user typed meanwhile is never overwritten (`stale`).
- `[[contributes.commitChecks]]` and the optional `commit-checker` export (worlds `plugin-commit-checks` / `commit-checks`): checks listed in Commit Options (on by default, `vcs.commit.disabledPluginChecks` turns them off) and run after the IDE's own; their problems, optionally with a location, stop the commit until Commit Anyway.

The window answers the host through `CommitRequested` / `plugin_answer_commit`, also for remote windows. The Rust SDK gains `host::pending_commit`, `host::set_commit_message`, `openidea_plugin::commit` and their mock; a `commit` test fixture exercises them. All components are rebuilt for the WIT change.

Refs #1650
2026-11-07 16:17:10 +08:00
Pedro Pessoa 50567473f7 feat(plugins): let plugins add to the hover of any language (#1931)
Plugin API 1.3 gains an optional hover-provider export (worlds plugin-hover
and hover): hover(doc, at) returns Markdown and the range it describes. A
plugin declares the hover kind under [[contributes.providers]] for any
language; the host asks it with the language server, under the edit-path
budget, and shows the server's hover first and each plugin's below a rule.
Declaring the kind needs engines.openidea ^1.3. The Rust SDK gains
openidea_plugin::hover and register_plugin!(T, hover); every shipped
component is rebuilt for the WIT change.
2026-11-07 15:29:16 +08:00
Arnaud Ritti 39f6162c12 feat(plugins): services views in the Services tool window (#1760)
A plugin view declared with `location = "services"` (plugin API 1.3, `engines.openidea ^1.3`) shows its tree in the Services tool window, next to Docker and the run configurations. The nodes, the toolbar (acting on one selected node) and their events come from `view-provider`; `viewItem.context` menus give the nodes a right-click menu. The pane beside the tree comes from a new optional export, `service-provider.detail(view, key)` (worlds `plugin-services` / `services`), which returns typed tabs: a message, read-only text or a process log. The IDE switches tabs without calling the plugin.

For a log, the UI names only the node and tab (`plugin_service_log`); the host asks the plugin for the pane again and runs its program with the plugin's `process:` grant in the project root, with the arguments of the toggles that are on, streaming its output into the log pane Docker uses until it exits (the exit code is shown), the pane closes or the Services window is hidden. A cancelled request starts nothing, lines are cut at 16 KiB, and revoking the grant or cancelling kills the program and what it started. The log pane moves to `src/ui/LogPane` and drops output that arrives after its source was stopped. The Rust SDK gains `openidea_plugin::services` (`Services`, `Tab`, `Pane`, `ProcessLog`). All components are rebuilt for the WIT change.

Closes #1736

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-07 09:39:18 +08:00
Pedro Pessoa 8259a81335 feat(plugins): let plugins colour tokens in files of other languages (#1882)
Plugin API 1.3 adds an optional semantic-tokens-provider export (world plugin-semantic-tokens). A plugin declares the semantic-tokens kind under [[contributes.providers]] for any language, including ones it does not contribute, and returns single-line tokens with LSP token kinds and modifiers. The host asks it with the other editor providers (50 ms / 500 ms) when the editor requests semantic tokens and joins the result with the language server's: plugin tokens replace the server tokens they overlap, so the colour scheme styles them like any semantic token. Declaring the kind needs engines.openidea ^1.3.

API 1.2 shipped with 0.1.38, so wit/ and the SDK's copy, ENGINE_VERSION and both API_VERSION constants move to 1.3 and every shipped component is rebuilt. The SDK gains openidea_plugin::semantic_tokens, and register_plugin! accepts any combination of optional exports (providers, views, indexing, syntax, semantic_tokens) instead of a fixed list of pairs.
2026-11-07 04:30:30 +08:00
Arnaud Ritti abdbc39d0d feat(plugins): let a command ask the user with a picker, an input box or a confirmation (#1779)
Plugin API 1.2 adds `ui.pick`, `ui.pick-many`, `ui.input`, `ui.confirm` and `views.reveal`. The dialogs show in the window whose command or event asked and block that call until the user answers; the wait is added to the call's deadline, so it doesn't use up the time budget. They need no capability and return `invalid` without a window (events without a client, rendering, editor providers). Prompts are withdrawn when the call is cancelled or the plugin stops, and reach remote windows through the dispatcher (`plugin_answer_prompt`). `views.reveal` brings one of the plugin's declared views to the front. The Rust SDK and its mock gain `pick`, `pick_many`, `input`, `confirm` and `reveal_view`. All components are rebuilt for the WIT change.

Closes #1768

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-06 23:18:30 +08:00
Arnaud Ritti a3d5336642 feat(plugins): let plugin code set context keys for its when clauses (#1762)
Plugin API 1.2 adds `ui.set-context(key, value)` and `ui.remove-context(key)`: a plugin sets `plugin.<id>.<key>` for `when` clauses, typed as `context-value` (flag, number or text), in the window the call came from or every window outside a command or event; a window's value wins over the global one. Keys are 1-64 of `A-Za-z0-9_-`, at most 100 per plugin, kept across window reloads and idle reclaim, dropped when the plugin stops or the window closes; remote windows get them through the dispatcher. The Rust SDK takes `impl Into<ContextValue>`. All components are rebuilt for the WIT change.

Closes #1622

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-06 23:02:34 +08:00
Arnaud Ritti 708f1b051b feat(plugins): let a command read every file selected in the Project view (#1744)
Plugin API 1.2 adds `invocation.selected-paths`: the files and folders a Project view or editor tab menu ran the command on, as absolute paths (the command's argument stays the first of them, so existing plugins see no change). The frontend passes the menu's selection to `plugin_run_command` on the desktop and through the remote dispatcher; the Rust SDK and its mock gain `Invocation::selected_paths`. All components are rebuilt for the WIT change.

Closes #1621

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-06 22:54:44 +08:00
Arnaud Ritti 4f65f030ce feat(plugins): right-click menu on the items of plugin tree and list views (#1746)
A plugin can declare `[[contributes.menus]]` with `menu = "viewItem.context"` and `view = "<its view>"`. Right-clicking an item of that tree or list view lists the matching commands, each run with the item's key as argument; `when` sees `viewItem` (the key) and `viewItemKind` (the key up to its first `:`). Validation refuses the menu without `view`, with an undeclared or web view, `view` on another menu, and (new in API 1.2) a manifest whose engines.openidea still admits older IDEs.

Closes #1618

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-06 22:47:40 +08:00
Arnaud Ritti e65825e661 feat(plugins): let a plugin set its own icon (#1761)
A plugin can set `icon` (and `icon-dark`) in plugin.toml. The SVGs go through the existing sanitiser (16 KiB each), ship with the package, and show in Settings › Plugins (list rows and detail header) and the marketplace, whose index entries carry them; the IDE sanitises index icons again and drops unsafe ones.

IDEs before plugin API 1.2 refuse a manifest with `icon`, so Manifest::needs_api refuses one whose engines.openidea still admits them (check and install), keeping the index from offering such a package to older IDEs.

Closes #1732

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-06 22:45:42 +08:00
l0ng-ai 37f0897eb7 chore(release): prepare the release commit with one command (#1672)
pnpm release:prepare <version> sets the app version in Cargo.toml, package.json and tauri.conf.json, refreshes Cargo.lock's workspace entries, and copies wit/*.wit over the plugin API snapshot when wit/ declares a newer API. The release workflow refused a tag with a stale snapshot, but the copy itself was a manual step.
2026-11-06 10:30:56 +08:00
qiaojianlong ed08b96382 feat(plugins): add background document-indexer API (#1021)
Plugin API 1.2 gains an optional document-indexer export (plugin-indexing world). A plugin returns per document the keys it defines and refers to, aliases between key prefixes and gutter markers; the host indexes a trusted project in cancellable background batches, keeps the project-wide graph, and answers Go to Declaration / Ctrl-click and multi-target gutter markers from memory without calling the plugin again. Edits and file changes invalidate the affected entries; reload, disable or a failing runtime drops the plugin's graph.

The interface is limited to cross-file associations: icons chosen by file content and embedded-language regions are separate concerns for later interfaces. The plugin navigation choosers' titles are translated.

Co-authored-by: qjlqyf <[email protected]>
2026-11-06 10:07:53 +08:00
l0ng-ai a6987b3eaa feat(plugins): open plugin API 1.2 (#1654)
API 1.1.0 shipped with 0.1.37 and wit/ matched the released snapshot, so additions start with a bump: the package in wit/ and the SDK's copy, ENGINE_VERSION and both API_VERSION constants move to 1.2, and the official plugins and fixtures are rebuilt against it. No interface changes; 1.0 and 1.1 components keep linking. The release that ships 1.2 copies wit/ over the released snapshot.
2026-11-06 09:29:43 +08:00
Arnaud Ritti 7bd96a998b feat(plugins): when clauses that test whether the project contains a file (#1620)
Plugin when clauses can use workspaceContains:<path>, a path relative to the project root (any project of a workspace). Installation refuses empty, absolute and .. paths. The keys are re-checked on plugin changes, project switches, created or removed files and changes in ignored folders; plugins that don't use the key do no IPC. The key regex avoids lookbehind for WebKit.

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-06 05:27:50 +08:00
l0ng-ai 91d6c7de57 test(plugins): keep one snapshot of the released plugin API and require it at release (#1512)
The snapshot of a released API was a manual step, and v0.1.37 shipped API 1.1.0
without one, so nothing checked the APIs added in 1.1. Each version also added a
full copy of wit/.

- tests/api/released/ holds the latest released WIT (now 1.1.0) and replaces the
  per-version folders: each snapshot passed the check against the one before,
  so the latest covers every release.
- api_frozen.rs: under the released version wit/ must match the snapshot
  exactly; additions need a version bump. Before, a function added to a
  released version passed, and components built from it didn't link in that
  release's IDE.
- release.yml: create-release fails when the wit/ version differs from the
  snapshot's, naming the folder to copy wit/ into.
2026-11-05 17:54:21 +08:00
l0ng-ai 7d267ab50e docs(plugins): describe released plugin API versions as frozen
The README still said the API could change freely while the plugin system is a preview. API 1.0.0 shipped with 0.1.36 and is checked by api_frozen; 1.1.0 ships with 0.1.37.
2026-11-05 16:28:55 +08:00
Lee Dong Geon 456b329d73 feat(themes): preview themes from the gear menu and Settings, and import theme plugins from every JetBrains IDE (#1498)
The main toolbar gear's Theme submenu and Settings › Appearance › Theme preview the theme under the pointer after the same delay as Quick Switch Scheme, and closing without a choice brings the selected theme back. Choosing a theme while it was previewed left the preview flag on, so the start-up theme cache kept the previous theme; `setTheme` now ends the preview.

Import Theme from JetBrains IDE… lists one row per theme plugin and installs it without switching the theme, like any theme plugin install. It reads only the newest settings folder of each product, so folders left behind by upgrades don't list their plugins again.

Co-authored-by: ldg030201 <[email protected]>
2026-11-05 15:57:04 +08:00
l0ng-ai 04fe65328e fix(plugins): refuse components that don't link against the host in check and install (#1471)
check_component only compared import names and the API version, so a component
built from unreleased WIT (a function added under @1.0.0) or missing the plugin
world's exports passed `openidea-plugin check` and installing, then failed when
the IDE started it. It now links the component against the host's linker and
reports the missing or mismatched function with what to do.

Closes #1468
2026-11-05 14:07:16 +08:00
Deng Liuanddengliu 4557811ef8 feat(plugins): add vcs.remotes, ui.copy-to-clipboard and ui.open-url (#1366)
Plugins get three host calls in API 1.1: vcs.remotes (under vcs.read) lists the
repository's remotes, ui.copy-to-clipboard and ui.open-url act on the window that
invoked the plugin's command.

Remote URLs are returned without the user:password part they may carry, and the
two UI calls fail when there is no invoking window instead of reaching every window.

Co-authored-by: dengliu <[email protected]>
2026-11-05 13:49:59 +08:00
Pedro Pessoa 8d784ac164 feat(plugins): let plugin views open a project file at a line (#1362)
Plugin tool windows that list code locations (routes, failed tests,
queries) had no way to take the user to file:line. A host import
`views.open-location(path, line, column)` opens a file inside the project
in an editor of the window the call came from, caret at the given line and
column (0-based, UTF-16). It needs `project.read` and refuses paths outside
the project; the event goes only to the calling window, as `open-terminal`
does. The Rust SDK exposes it as `host::open_location` (with the mock), the
API table lists it, and the shipped components are rebuilt for the new WIT.

Closes #1334

Co-authored-by: yzPeedro <[email protected]>
2026-11-05 07:56:02 +08:00
Pedro Pessoa 7ffaa0b5da feat(plugins): let plugin tool windows use an SVG icon from the package (#1363)
A view's `icon` could only be a built-in icon name, so a plugin's tool
window button couldn't show the plugin's own mark. Like `fileIcons`, an
`icon` ending in `.svg` is now a path in the package: the host reads and
checks it at install time (a missing file or an SVG with `<script>` refuses
the install), serves it as the `viewIcon/<id>` asset, and the tool window
registers it as an external icon, falling back to the plugin icon if it
can't be loaded. Icon names work as before.

Closes #1335

Co-authored-by: yzPeedro <[email protected]>
2026-11-05 07:48:39 +08:00
l0ng-ai 4b5ef7069c fix(plugins): build new plugins outside the repository
- `openidea-plugin new` makes a Rust plugin depend on the SDK at the tool's
  release tag (git) unless the plugin is inside the source tree the tool was
  built from or `--sdk` is given; the path written before only resolved on
  the machine that built the tool.
- The command template compiles again: `run_command` returns
  `openidea_plugin::Error`. A test now runs `cargo test` on the generated
  plugin against the SDK.
- plugins/README.md: installing the tool with `cargo install --git`, the SDK
  dependency, where plugins can be installed (welcome screen and trusted
  projects), and where to look when a plugin misbehaves.
2026-11-04 20:55:31 +08:00
l0ng-ai dd370654cb feat(plugins): leave room in plugin API 1.0 for what will grow (#1245)
Closes #1237
2026-11-04 20:11:46 +08:00
l0ng-ai 953e844541 feat(plugins): community plugins, version compatibility and categories in the marketplace (#1226)
* feat(plugins): list community plugins from a registry in the marketplace

The marketplace index can now carry plugins of other publishers, published
from their authors' repositories through the l0ng-ai/openidea-plugins
registry.

- `openidea-plugin index` with a key replaces only that publisher's plugins
  (it used to drop every other entry); without a key it indexes each package
  against the publishers the index already lists, checking the key and the
  id prefix instead of only the signature itself.
- `openidea-plugin merge` takes another index's publishers, plugins and
  revocations in place of every publisher but `--keep`; the other index can't
  list or revoke the kept publishers' plugins.
- `openidea-plugin check` accepts a `.oiplugin` file.
- Releases merge the registry's index into plugins-index.json and attach the
  Linux x64 tool; plugins-index.yml refreshes the community plugins in the
  latest release hourly or when the registry signals a change.
- plugins/README.md and CONTRIBUTING.md describe official and community
  plugins.

* feat(plugins): offer the newest plugin version this IDE can load

Index entries record the plugin API each version needs (`engine`: the
manifest's `engines.openidea` narrowed to the API its component was built
for), and an index may list several versions of a plugin. Settings › Plugins
offers the newest version this IDE can load; with none, the plugin is shown
as incompatible with the API it needs, and downloading it is refused.

`openidea-plugin index` keeps, of each plugin, the newest version per API
requirement, so users of an older IDE keep the last version built for them;
`--download-base` makes downloads absolute, and the release workflow points
them at the release that published them. Scratch directories are now unique
per call.

* feat(plugins): file marketplace plugins under categories

`plugin.toml` takes `categories` (up to 3 of languages, themes, icon-themes,
linters, vcs, ai, frameworks, tools, other). A plugin that declares none is
filed under what its contributions imply (themes, icon themes, languages),
else under other; categories an IDE doesn't know are ignored, and `check`
warns about them. Index entries carry the categories, and Settings › Plugins
› Available filters by them and shows them in a plugin's details. The
official plugins declare theirs.

* feat(plugins): let a marketplace index point to where it moved

An index with `moved` sends the IDE to the index at that URL (relative or
absolute, up to three hops; an https index can only move to https). Downloads
resolve against the index actually read, so a marketplace can change hosts
while released IDEs keep the address they were built with.

* fix(plugins): full-width category filter and aligned incompatibility note

* fix(plugins): translate the AI category in Chinese

* feat(plugins): keep a plugin's signer across updates and announce updates

- Index format: fields are only added; an IDE reading a newer `format`
  still applies its revocations and `moved`, lists no plugins and asks to
  be updated, instead of failing to read the index.
- Key continuity: an installed plugin updates silently only to a package
  signed with the same key, or a key it endorsed (`KeyEntry.endorsement`,
  `index --previous-key`, `openidea-plugin endorse`). Another signer, or an
  unsigned package replacing a signed one, is shown on the install page and
  needs the user's confirmation (`Consent.signerChange`).
- Official key: release builds carry the official publisher's public key
  (`OPENIDEA_OFFICIAL_PLUGIN_KEYS`, derived from PLUGIN_SIGNING_KEY);
  `openidea.*` plugins must be signed with it or a key it endorsed,
  whatever the index lists.
- Updates: the daily marketplace check announces new versions once, with
  Update All (also in the gear menu and as an action). An update that asks
  for nothing new installs with the grants the installed version has.
- Index entries need `engine`; `categories` beyond the first three are
  ignored instead of rejecting the manifest.
2026-11-04 18:24:43 +08:00
Lee Dong Geon 6dec1252db feat(plugins): import IntelliJ theme plugins (#1184)
Settings › Plugins (gear menu) gets Import Theme from IntelliJ IDE… (theme plugins of the JetBrains IDEs on this machine, the one each IDE uses first) and Import IntelliJ Theme from File… (a Marketplace .jar / .zip or a .theme.json). The host reads the plugin with size limits; the UI converts it with the IntelliJ mapping (moved from scripts/theme to src/features/plugins/intellijTheme.ts, still used by pnpm theme:import-intellij) into one OpenIDEA theme plugin (engines.openidea ^1.0) with all of its themes, installed through the usual install page, and applies the chosen theme.

Theme pickers separate each plugin's themes, and Quick Switch Scheme › Theme previews the theme under the pointer or keyboard after 500 ms and restores the selected one when closed without a choice.

Co-authored-by: ldg030201 <[email protected]>
2026-11-04 15:28:00 +08:00