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]>
Цей коміт міститься в:
Arnaud Ritti authored and GitHub committed 2026-11-07 09:39:18 +08:00
1 джерело 809bc1cc2f
коміт 39f6162c12
72 files changed
+1936 -178

No files matched your search

+61 -17
Переглянути файл
@@ -137,23 +137,23 @@ 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 an SVG of the package), `location` (`left`, `right`, `bottom`), `web?` (see _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`), `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 |
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
@@ -543,6 +543,50 @@ Scripts get the same as `openidea.theme` (`{ dark, vars }`) and
`openidea.onThemeChange(fn)`, for drawing on a canvas, say. Variables are only
ever added.
#### Services views
`location = "services"` (API 1.3: `engines.openidea = "^1.3"`) puts a view's
tree in the Services tool window, next to Docker and the run configurations,
instead of a tool window of its own. Its code renders it (no `web`):
```toml
[[contributes.views]]
id = "kube"
title = "Kubernetes"
icon = "icons/kubernetes.svg"
location = "services"
[capabilities]
optional = ["process:kubectl"]
```
The items of the view's `tree` are Services nodes: `render`, `handle` and the
toolbar work as in a tool window, the toolbar's buttons acting on the one
selected node, and `viewItem.context` menus give the nodes a right-click menu.
The pane beside the tree comes from the `service-provider` export: register
`register_plugin!(MyPlugin, views, services)` and implement
`openidea_plugin::services::Services`, which returns the selected node's tabs
(at most 8; asked again after `host::refresh_view("kube")`):
```rust
impl Services for MyPlugin {
fn detail(&mut self, view: &str, key: &str, _ctx: &Invocation) -> Result<Vec<Tab>, Error> {
let pod = key.trim_start_matches("pod:");
let log = ProcessLog::new("kubectl", &["logs", "-f", "--tail=1000", pod])
.toggle("ts", "Timestamps", &["--timestamps"]);
Ok(vec![Tab::new("log", "Log", log), Tab::new("inspect", "Inspect", Pane::Text(self.describe(pod)?))])
}
}
```
The IDE switches tabs without calling the plugin. A tab is a `Pane::Message`,
read-only `Pane::Text` (at most 1 MiB) or a `ProcessLog`: the IDE asks for the
pane again and runs that program (it needs the plugin's `process:<program>`
grant) in the project root, following its output in the log Docker containers
use (ANSI stripped, the last 5000 lines and 16 KiB per line, Soft-wrap, Clear,
the exit code at the end). A toggle appends its `args` and starts the program
again. It stops when the node's pane closes or the Services window is hidden.
#### Web views
A view or custom editor can instead be a web page of the package, for UIs the
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
cbc68107db26fa7d6fa77c8983f469536defe541d9bcd037d490030669ff999d
eb76da4bc9db84dcbad34143bac18c9ecf974276f793009791a15aab2eee291d
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
0715a5670087df9e7f653f0efcd4087988a995c1ffa3eb165d119cf2737438c7
0a19948927a23de81b2a453b9992e021744093cbe41ea92daa9feb2b1bc9a2a9
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
b015bde71d2e116255e27190829c08bd6768e1077d0ee61321a8660d7008b229
564bfd00694f27f35f09eb0b81d341207a755528b5fe7c46c96717b3868d0f65
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
6c93f780430507806394a152041e20062b69bc2ae2642ec24eaffc93e189bac5
f3f447366fa38f9bf4911f35b6e899405b0e072ceee3ad5ea2fe90639d70fa53
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
0b0b37abc7412a1517c0c1bd7d678279acc1e1afbdb6cedcded1582779a068a1
85cad466adb94f2b58e62263617e885d1c47cf70db5e8b38942bd415f0d779af
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
629c5aed682a5be77a5e1a4e9a3f3483533e28679c07bfc88b21021a0dee28d8
659446eda0b50aa0fb08f6af68f7d355fcaf3342847e2e579999dcf52a7a7399
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
bcfd9e933a2c5cc575fc9d2f22b8b5a151fd1f366a31235b44dcc3ef8bd3422d
51ef9d43315ca9d7239999c222fbf7a2c01bc85c2b40f53f34a1a8e5d31baa6d
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
a69659e480b2f88b818d33b3b28ef8fbe29f7564800ac6d57e6c2e013c85a9d9
bdf9455c6e7c8c340c54d783f351220b38cffed36907e1b5a1f2e11801e6f5bb
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
55f4e790142e1de70853080ced47dc80415384f7a0ec5645b5376b89f31d8354
7e4d00e1ea63b2eca48b9297667e04c05a9f9832f1dee2262272801d516b9a16
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
a89ac3590b8f7474a1d503ced271d2c60e3c99bbd5c28ae9e4c04256b1a794a2
00cbd2dd7f0937554633ba5e533f19be85f70579c7c6fedb6fe0228f8f63bf4c
Бінарний файл не відображається.
+1 -1
Переглянути файл
@@ -1 +1 @@
adae51c270a7ad0dd05082f25deb0b0ff5012225b2dc7e7efda923dad3904869
eecf2cf561dd5e3e3edbf9f67ccb479573dd538a75d4426a1d086edf0a310043