дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 03:05:40 +02:00
feat(plugins): commit workflow API for plugins
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
Цей коміт міститься в:
1 джерело
a36c7ed3d5
коміт
2dc078b93b
81 files changed
+2605
-148
No files matched your search
+77
-32
@@ -34,7 +34,7 @@ plugins/
|
||||
├─ regex-tester/ # html view talking to its code, settings, the editor's selection
|
||||
└─ license-header/ # file-opened events, quick fix, project scan in a task
|
||||
sdk/rust/openidea-plugin/ # the Rust SDK (host API, Plugin trait, mock host)
|
||||
crates/ide-plugin/tests/fixtures/ # test plugins: hello, jobs, language-toy, misbehave, semantic-tokens, hover
|
||||
crates/ide-plugin/tests/fixtures/ # test plugins: hello, jobs, language-toy, misbehave, semantic-tokens, hover, commit
|
||||
```
|
||||
|
||||
The plugin API is defined in [`wit/`](../wit/) (`openidea:[email protected]`, world
|
||||
@@ -137,23 +137,24 @@ work), but 0.1 code no longer runs: rebuild it against the 1.0 SDK.
|
||||
|
||||
### Contribution points
|
||||
|
||||
| Point | Declaration | Code needed |
|
||||
| --------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
||||
| `commands` | `id`, `title`, `category?`, `when?` | to run them |
|
||||
| `themes` | `path` (theme JSON), `editor-scheme?` | no |
|
||||
| `iconThemes` | `id`, `label`, `path` (icon theme JSON, see §5) | no |
|
||||
| `languages` | `path` (`languages/<id>.toml` format, see §6) | no |
|
||||
| `runDetectors` | `id`, `name`, `files`, `command`, `args?`, `test?` (§6.2) | no |
|
||||
| `providers` | `kinds`, `languages` (editor providers, see _Providers_) | yes |
|
||||
| `secrets` | `name`, `label`, `description?` (see _Network_) | to use them |
|
||||
| `menus` | `menu` (`editor.context`, `projectView.context`, `editorTab.context`, `viewItem.context`, `tools`), `view?`, `command`, `when?` | no |
|
||||
| `keymaps` | `command`, `key`, `mac?` (`Ctrl+Alt+C`; a chord: `Meta+K Meta+C`) | no |
|
||||
| `statusBar` | `id`, `text`, `tooltip?`, `command?`, `align?`, `priority?` | no |
|
||||
| `settings` | `schema` (JSON file), `title?` — one table, see _Settings_ | no |
|
||||
| `views` | `id`, `title`, `icon?` (an icon name or a package SVG), `location` (`left`, `right`, `bottom`, `services`), `web?` (_Web views_) | without `web` |
|
||||
| `customEditors` | `id`, `title`, `patterns` (file names), `priority` (`default`, `option`), `layout?`, `web?` | without `web` |
|
||||
| `fileIcons` | `id`, `patterns` (file names), `icon` (SVG), `iconDark?` — whatever icon theme is in use | no |
|
||||
| `fileTemplates` | `id`, `name`, `extension`, `path` (the new file's content; template variables work) — New menu | no |
|
||||
| Point | Declaration | Code needed |
|
||||
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
||||
| `commands` | `id`, `title`, `category?`, `when?` | to run them |
|
||||
| `themes` | `path` (theme JSON), `editor-scheme?` | no |
|
||||
| `iconThemes` | `id`, `label`, `path` (icon theme JSON, see §5) | no |
|
||||
| `languages` | `path` (`languages/<id>.toml` format, see §6) | no |
|
||||
| `runDetectors` | `id`, `name`, `files`, `command`, `args?`, `test?` (§6.2) | no |
|
||||
| `providers` | `kinds`, `languages` (editor providers, see _Providers_) | yes |
|
||||
| `secrets` | `name`, `label`, `description?` (see _Network_) | to use them |
|
||||
| `menus` | `menu` (`editor.context`, `projectView.context`, `editorTab.context`, `viewItem.context`, `tools`, `commit.message`), `view?`, `command`, `when?` | no |
|
||||
| `keymaps` | `command`, `key`, `mac?` (`Ctrl+Alt+C`; a chord: `Meta+K Meta+C`) | no |
|
||||
| `statusBar` | `id`, `text`, `tooltip?`, `command?`, `align?`, `priority?` | no |
|
||||
| `settings` | `schema` (JSON file), `title?` — one table, see _Settings_ | no |
|
||||
| `views` | `id`, `title`, `icon?` (an icon name or a package SVG), `location` (`left`, `right`, `bottom`, `services`), `web?` (_Web views_) | without `web` |
|
||||
| `customEditors` | `id`, `title`, `patterns` (file names), `priority` (`default`, `option`), `layout?`, `web?` | without `web` |
|
||||
| `fileIcons` | `id`, `patterns` (file names), `icon` (SVG), `iconDark?` — whatever icon theme is in use | no |
|
||||
| `fileTemplates` | `id`, `name`, `extension`, `path` (the new file's content; template variables work) — New menu | no |
|
||||
| `commitChecks` | `id`, `title` — a before-commit check in Commit Options (see _Commits_) | yes |
|
||||
|
||||
A `viewItem.context` menu needs `view`, the id of one of the plugin's own
|
||||
views. It adds the commands to the right-click menu of the items in that
|
||||
@@ -174,6 +175,10 @@ command = "kube.logs"
|
||||
when = "viewItemKind == pod"
|
||||
```
|
||||
|
||||
A `commit.message` menu puts the command, with the plugin's icon, in the
|
||||
toolbar above the commit message of the Commit tool window and the Commit
|
||||
dialog (see _Commits_). It is new in API 1.3 (`engines.openidea = "^1.3"`).
|
||||
|
||||
Declared contributions are live as soon as the plugin is enabled — commands
|
||||
show in Find Action, menus and the status bar, shortcuts work, themes and icon
|
||||
themes can be applied, settings pages are there — **before** any plugin code
|
||||
@@ -227,20 +232,20 @@ installing (or when a new version asks for more). Low-risk capabilities are
|
||||
listed; high-risk ones are called out as equivalent to running code on the
|
||||
machine. Users can revoke optional capabilities in Settings › Plugins.
|
||||
|
||||
| Capability | Risk | Unlocks today |
|
||||
| ------------------- | ------ | ----------------------------------------------------------------------------------- |
|
||||
| `project.read` | low | `Project::root`, `read_file`, `find_files`, `host::projects`, `publish_diagnostics` |
|
||||
| `editor.read` | low | `TextEditor::document`, `selections` |
|
||||
| `editor.write` | medium | `TextEditor::apply_edits` |
|
||||
| `vcs.read` | low | `Project::vcs_status` (branch, upstream, ahead/behind, changes), `vcs_remotes` |
|
||||
| `vcs.write` | medium | `Project::vcs_fetch` (trusted projects only) |
|
||||
| `storage` | low | `host::storage` (1 MiB per plugin) |
|
||||
| `events` | low | file opened/saved, editor focused, repository changed events |
|
||||
| `events.input` | medium | `action-performed` events (which actions you run, and how) |
|
||||
| `process:<program>` | high | `Project::run_process`, language servers, debug adapters, runs |
|
||||
| `net:<host>` | medium | `host::fetch` to that host (`*.example.com`: its subdomains) |
|
||||
| `secrets` | medium | `{{secret:<name>}}` in request headers, `host::secret_is_set` |
|
||||
| `clipboard` | medium | `host::copy_to_clipboard` and the clipboard API in the plugin's web views |
|
||||
| Capability | Risk | Unlocks today |
|
||||
| ------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `project.read` | low | `Project::root`, `read_file`, `find_files`, `host::projects`, `publish_diagnostics` |
|
||||
| `editor.read` | low | `TextEditor::document`, `selections` |
|
||||
| `editor.write` | medium | `TextEditor::apply_edits` |
|
||||
| `vcs.read` | low | `Project::vcs_status` (branch, upstream, ahead/behind, changes), `vcs_remotes`, `host::pending_commit`, before-commit checks |
|
||||
| `vcs.write` | medium | `Project::vcs_fetch` (trusted projects only), `host::set_commit_message` |
|
||||
| `storage` | low | `host::storage` (1 MiB per plugin) |
|
||||
| `events` | low | file opened/saved, editor focused, repository changed events |
|
||||
| `events.input` | medium | `action-performed` events (which actions you run, and how) |
|
||||
| `process:<program>` | high | `Project::run_process`, language servers, debug adapters, runs |
|
||||
| `net:<host>` | medium | `host::fetch` to that host (`*.example.com`: its subdomains) |
|
||||
| `secrets` | medium | `{{secret:<name>}}` in request headers, `host::secret_is_set` |
|
||||
| `clipboard` | medium | `host::copy_to_clipboard` and the clipboard API in the plugin's web views |
|
||||
|
||||
`process:` capabilities are also derived from what the contributions start: a
|
||||
language's `language-servers` and `debugger` commands and a run detector's
|
||||
@@ -349,6 +354,7 @@ then run `openidea-plugin check`.
|
||||
| `Project::root()`, `read_file(path)` | `workspace.project` | Absolute or project-relative; `..`/symlinks escaping the root are rejected; UTF-8 only, ≤ 16 MiB; unsaved editor contents win. |
|
||||
| `host::projects()` | `workspace.projects` | Every open project (for scheduled work without a window). |
|
||||
| `Project::vcs_status()`, `vcs_remotes()`, `vcs_fetch()` | `vcs` | `vcs_remotes` returns `(name, url)` pairs; fetch runs git, so only in trusted projects (call it inside a task). |
|
||||
| `host::pending_commit()`, `host::set_commit_message(revision, text)` | `commit` | The commit being prepared in the calling window and its message; see _Commits_. |
|
||||
| `host::subscribe(kind)` | `events.subscribe` | For the session, across idle reclaim (an event activates the plugin). Or declare `on-event:<kind>`. |
|
||||
| `host::begin_task(title, cancellable)` → `Task` | `tasks.begin` | Shown with the background tasks; `report`, `is_cancelled`; the call may run 60 s instead of 5 s while it is open; dropping it ends it. |
|
||||
| `host::storage::{get, set, delete, keys}` | `storage` | Survives restarts; the instance's memory does not. Deleted on uninstall. |
|
||||
@@ -395,6 +401,45 @@ a new API: an IDE refuses a kind it doesn't know with `invalid`, and a kind
|
||||
newer than the plugin's SDK arrives as `Event::Other { kind, data }` (JSON).
|
||||
The SDK's event enums are `#[non_exhaustive]`; match them with a `_` arm.
|
||||
|
||||
### Commits
|
||||
|
||||
A plugin can work on the commit the user is preparing, in the Commit tool
|
||||
window or the Commit dialog (API 1.3, `engines.openidea = "^1.3"`):
|
||||
|
||||
- **A button above the message.** `[[contributes.menus]]` with
|
||||
`menu = "commit.message"` shows one of the plugin's commands there, with the
|
||||
plugin's icon.
|
||||
- **The pending commit.** `host::pending_commit()` (`vcs.read`) returns what
|
||||
Commit would commit right now: per file its repository, path, status and a
|
||||
`git diff` of only the included changes (the checked files and changed
|
||||
blocks, against HEAD; 1 MiB in all, then `truncated`), plus the message,
|
||||
Amend and a `revision`. Unsaved editors of those files are saved first, as
|
||||
Commit does; nothing is staged or committed. `None` without a repository.
|
||||
- **The message.** `host::set_commit_message(revision, text)` (`vcs.write`)
|
||||
replaces it only while the draft is still at `revision`. If the user typed
|
||||
or changed the included changes meanwhile, it fails with `Error::Stale` and
|
||||
nothing is overwritten. The user reviews the text and commits.
|
||||
|
||||
Both work from a command or event of a window, in a trusted project; they wait
|
||||
for the window, which isn't charged to the time limit. A message generator
|
||||
calls a model with `host::fetch` and a `{{secret:…}}` key (see _Network_)
|
||||
inside a task:
|
||||
|
||||
```rust
|
||||
let draft = host::pending_commit()?.ok_or("no repository")?;
|
||||
let message = ask_the_model(&draft.changes)?;
|
||||
host::set_commit_message(draft.revision, &message)?;
|
||||
```
|
||||
|
||||
**Before-commit checks.** Each `[[contributes.commitChecks]]` (`id`, `title`)
|
||||
is listed in Commit Options, on by default. When the user commits, after the
|
||||
IDE's own checks, the IDE calls the optional `commit-checker` export
|
||||
(`register_plugin!(MyPlugin, commit_checks)`, trait `commit::CommitChecks`)
|
||||
with the check's id and the draft. Problems (`message`, `location?`) stop the
|
||||
commit and show beside the message with the check's title, until the user
|
||||
changes it or chooses Commit Anyway. A check that fails, times out or lacks
|
||||
`vcs.read` is reported as a problem too.
|
||||
|
||||
### Providers
|
||||
|
||||
Declare what the plugin provides in the editor; only plugins whose
|
||||
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
9384d8964c7c05d3df06521e0d724a932fdd17608cbcede13ec2acadfab3783c
|
||||
89515d507386505d244ed6ac19a85b7288f6d8ef30f19f4b770d8b0f84f33d63
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
9d7e857ef367eaa9748d1a2b05b7b45226c254aa578c29e1ed987c7353738acf
|
||||
a0b3353006032af9f9052c8413a00ff8069294d1c3e86ed520f9c4b8012094c1
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
3e11039d36306f51c728bf9d08030fe620de461fa5b0037700aea95c4d27714c
|
||||
7b055db21ef99eea933df6b311248ac18ec8a57d57cbe9af0bc5f694c8e8d48a
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
ef50957a407c9aff9999fdd197a5e72c90b7ba77aedbd7569408262ab7e158be
|
||||
d742d45a1bee8c5d0c4c2bab9341eb3d3fc7feae1c1229485330dd0b58374092
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
7160b309f39fe973e67bb20d361384955aed6afe98f00db329c3026ffe923759
|
||||
511a8419aab4bde97c09a7a7134ee523dade17b771697382ffb521ad1e5798bf
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
1f1afa285a64a7f7758f08a80306d6397c8aee4d2ac7ed43369e77aae61add04
|
||||
8b5e78147995375b841f342c7441158d20d33388be808f08c233eca27c3b4d7f
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
ddd9205da9fc947215ab90c505d8b443d0937b1dd11734e9ce94d277517c77a6
|
||||
6925c7bdc12a5bd74e864ab4e706c155b0a994d5bbf27cc437f0220aed4cfc68
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
c61a0ec6ffcbd71e590e5a16a6f08f2d2772481497edc77e11914cfb3d0bfcd1
|
||||
43c47ef0199e7c7550655bd571c75d4109cb6435ae0e99fa68123c8e4d823e48
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
e11cd0b664ae058274808bc2b2152bac2738207979758d13bc0c0cd96c38ef4f
|
||||
3a632f982c08c24cc2d67511f1fac3ff3b29229e552399b46e4d0aa2623200f7
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
59319cf4be14448b0126b07d074cc08c351cfd12a3de26f111c2445fe8bff664
|
||||
588b3af37102b1871b40184509b299b39ecd15212ccdb58467badb889ca7bc03
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
59844ae52aa765af404b43f7895db94d0063a3a6fce5bd54bf9af680ccac357b
|
||||
723d68d4de1ec8b6f246b1bd30e56f177df08f7bf3c17907b0ec9ef6cbdf1bf3
|
||||
Reference in new issue
Заблокувати користувача