diff --git a/crates/ide-host/src/plugins/api.rs b/crates/ide-host/src/plugins/api.rs index d1f8f1a1..1b1c997e 100644 --- a/crates/ide-host/src/plugins/api.rs +++ b/crates/ide-host/src/plugins/api.rs @@ -667,6 +667,17 @@ impl HostApi for PluginApi { Ok(()) } + fn open_location(&self, client: Option<&str>, path: &Path, line: u32, column: u32) -> Result<(), HostError> { + let event = PluginEvent::OpenLocation { + plugin_id: self.id().to_owned(), + path: path.to_path_buf().into(), + line, + column, + }; + self.shared.sink.emit(client, event); + Ok(()) + } + fn setting(&self, root: Option<&Path>, key: &str) -> Option { let schema: Vec = self .shared diff --git a/crates/ide-host/src/plugins/remote_host.rs b/crates/ide-host/src/plugins/remote_host.rs index 947c0b9b..9c869023 100644 --- a/crates/ide-host/src/plugins/remote_host.rs +++ b/crates/ide-host/src/plugins/remote_host.rs @@ -114,6 +114,7 @@ pub fn forwarded(event: &PluginEvent) -> bool { | PluginEvent::ViewChanged { .. } | PluginEvent::ViewMessage { .. } | PluginEvent::OpenTerminal { .. } + | PluginEvent::OpenLocation { .. } ) } diff --git a/crates/ide-host/src/plugins/types.rs b/crates/ide-host/src/plugins/types.rs index fc13800f..bd5e197c 100644 --- a/crates/ide-host/src/plugins/types.rs +++ b/crates/ide-host/src/plugins/types.rs @@ -192,6 +192,8 @@ pub enum PluginEvent { args: Vec, cwd: ide_core::IpcPath, }, + #[serde(rename_all = "camelCase")] + OpenLocation { plugin_id: String, path: ide_core::IpcPath, line: u32, column: u32 }, /// Plugin languages, or the servers they may start, changed: re-fetch /// `languages` (the host re-detects open documents: [`crate::Host::plugin_languages_changed`]). LanguagesChanged, diff --git a/crates/ide-host/tests/plugins.rs b/crates/ide-host/tests/plugins.rs index e4f925bf..299ece22 100644 --- a/crates/ide-host/tests/plugins.rs +++ b/crates/ide-host/tests/plugins.rs @@ -2045,6 +2045,38 @@ fn pinned_programs_fill_views_and_open_terminals() { assert_eq!(args, ["follow", "build", "--lines=5"]); } +#[test] +fn views_open_project_files_at_a_line() { + let f = fixture(Limits::default()); + let info = f.install(&sample("jobs")); + assert_eq!(info.status, PluginStatus::Inactive, "{:?}", info.error); + std::fs::write(f.project.join("jobs.log"), "build\ndeploy\nfailed\n").unwrap(); + let press = |button: &str| { + f.take_events(); + let result = f.host.view_event("jobs", "jobs", ide_plugin::ViewEvent::Button { id: button.into() }, f.inv()); + let opened = f.take_events().into_iter().find_map(|(client, e)| match e { + PluginEvent::OpenLocation { path, line, column, .. } => Some((client, path.0, line, column)), + _ => None, + }); + (result, opened) + }; + + let (result, opened) = press("log"); + assert!(result.is_ok(), "{result:?}"); + let (client, path, line, column) = opened.expect("a location"); + assert_eq!((client.as_deref(), line, column), (Some("main"), 2, 4)); + assert_eq!(path.canonicalize().unwrap(), f.project.join("jobs.log").canonicalize().unwrap()); + + let (result, opened) = press("outside"); + assert!(result.is_err(), "a path outside the project is refused"); + assert!(opened.is_none()); + + f.host.set_capability("jobs", "project.read", false).unwrap(); + let (result, opened) = press("log"); + assert!(result.unwrap_err().message.contains("project.read"), "needs project.read"); + assert!(opened.is_none()); +} + #[test] fn views_can_use_an_svg_icon_from_the_package() { let f = fixture(Limits::default()); diff --git a/crates/ide-plugin/src/runtime.rs b/crates/ide-plugin/src/runtime.rs index bd5a9975..55c38e33 100644 --- a/crates/ide-plugin/src/runtime.rs +++ b/crates/ide-plugin/src/runtime.rs @@ -105,6 +105,7 @@ pub trait HostApi: Send + Sync { args: Vec, cwd: Option<&Path>, ) -> Result<(), HostError>; + fn open_location(&self, client: Option<&str>, path: &Path, line: u32, column: u32) -> Result<(), HostError>; /// A setting of the plugin as JSON (project over global over the schema default). fn setting(&self, root: Option<&Path>, key: &str) -> Option; } diff --git a/crates/ide-plugin/src/wit_host.rs b/crates/ide-plugin/src/wit_host.rs index 0a2bbe36..bcd53cc8 100644 --- a/crates/ide-plugin/src/wit_host.rs +++ b/crates/ide-plugin/src/wit_host.rs @@ -640,6 +640,21 @@ impl views::Host for StoreData { let title = clip(title, crate::runtime::MAX_TITLE); Ok(self.host.open_terminal(client.as_deref(), title, &program, args, cwd.as_deref()).map_err(to_wit)) } + + fn open_location(&mut self, path: String, line: u32, column: u32) -> Fallible<()> { + if let Err(e) = self.require(Capability::ProjectRead) { + return Ok(Err(e)); + } + let Some(root) = self.invocation.workspace_root.clone() else { + return Ok(Err(wit::Error::Invalid("no project is open in this window".into()))); + }; + let real = match sandbox::resolve_in_root(&root, &path) { + Ok(real) => real, + Err(e) => return Ok(Err(wit::Error::Invalid(e))), + }; + let client = self.invocation.client.clone(); + Ok(self.host.open_location(client.as_deref(), &real, line, column).map_err(to_wit)) + } } impl settings::Host for StoreData { diff --git a/crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm index 70020250..368de83b 100644 Binary files a/crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm.sources index 07e607c7..f811c07c 100644 --- a/crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm.sources @@ -1 +1 @@ -a50d1eb41ab8dd0c9c8d6bae8266906cd6eb5de8e6f6ca6f681990389472bb5b +1f7402e2b1b4602e4073eb61837c3dc8840f9a4f7275d800dbd6fcf6ce5e40c3 diff --git a/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm index d15e5082..0899f53e 100644 Binary files a/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm.sources index 695b65da..3a0de87f 100644 --- a/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm.sources @@ -1 +1 @@ -2fb6aa879562733bf5b7f11dabe8fc6a415c9ef695868fe7845417a4245d2dc8 +4f6cd5b5e0c12e7dcbccf66a6ad97276bf968bc3ad26f0fd08e1f7765cbfee14 diff --git a/crates/ide-plugin/tests/fixtures/jobs/plugin.toml b/crates/ide-plugin/tests/fixtures/jobs/plugin.toml index 8a3d2c89..8fc68206 100644 --- a/crates/ide-plugin/tests/fixtures/jobs/plugin.toml +++ b/crates/ide-plugin/tests/fixtures/jobs/plugin.toml @@ -12,6 +12,7 @@ activation = ["on-view:jobs", "on-command:*"] [capabilities] required = ["process:jobs"] +optional = ["project.read"] [[contributes.views]] id = "jobs" diff --git a/crates/ide-plugin/tests/fixtures/jobs/src/lib.rs b/crates/ide-plugin/tests/fixtures/jobs/src/lib.rs index b6686393..fe28d779 100644 --- a/crates/ide-plugin/tests/fixtures/jobs/src/lib.rs +++ b/crates/ide-plugin/tests/fixtures/jobs/src/lib.rs @@ -64,6 +64,8 @@ impl Views for Jobs { return self.follow(); } ViewEvent::Button(b) if b == "follow" => return self.follow(), + ViewEvent::Button(b) if b == "log" => return host::open_location("jobs.log", 2, 4), + ViewEvent::Button(b) if b == "outside" => return host::open_location("../outside.log", 0, 0), _ => return Ok(()), } host::refresh_view(VIEW); diff --git a/crates/ide-plugin/tests/fixtures/misbehave/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/misbehave/dist/plugin.wasm.sources index 59546337..f95fc1ae 100644 --- a/crates/ide-plugin/tests/fixtures/misbehave/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/misbehave/dist/plugin.wasm.sources @@ -1 +1 @@ -25b0f419a68a3b1651d3c2774d834b05e40ede52bc5ed8c0eba6d27a204efe82 +3d4dd1b6365c7fbafdd3c1f99f4405e5ee40fc5e56c97da960adfe7af2e7edb8 diff --git a/plugins/README.md b/plugins/README.md index d4c77a24..8a74a1d1 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -313,6 +313,7 @@ then run `openidea-plugin check`. | `host::refresh_view(view)` | `views.refresh` | The view (or custom editors) render again; call it when the content changed. | | `host::post_view_message(view, text)` | `views.post-message` | To the script of the view's `html` body. | | `host::open_terminal(title, program, args)` | `views.open-terminal` | A terminal tab in the invoking window running the approved `process:` in the project root: output that keeps coming, interactive sessions. | +| `host::open_location(path, line, column)` | `views.open-location` | Opens a file inside the project in an editor of the invoking window, the caret at `line`/`column` (0-based, UTF-16): a view's links to code. Needs `project.read`. | | `host::setting(key)`, `setting_bool`, `setting_string`, `setting_number` | `settings.get` | The user's value (project over global) or the schema's default. | `run-command` receives an optional string argument: `executeAction("plugin..", "arg")` diff --git a/plugins/official/ai-completion/dist/plugin.wasm b/plugins/official/ai-completion/dist/plugin.wasm index b163d582..4fc5e771 100644 Binary files a/plugins/official/ai-completion/dist/plugin.wasm and b/plugins/official/ai-completion/dist/plugin.wasm differ diff --git a/plugins/official/ai-completion/dist/plugin.wasm.sources b/plugins/official/ai-completion/dist/plugin.wasm.sources index e5251dd3..d07cfcec 100644 --- a/plugins/official/ai-completion/dist/plugin.wasm.sources +++ b/plugins/official/ai-completion/dist/plugin.wasm.sources @@ -1 +1 @@ -28e4bf10a41bdda011111b0e150ebe96205c57f410bad98c19eea3f1286349d6 +9ad412cb7b853d40467c67305a1126bb87910d179ecfa1c88e13d4a900367c50 diff --git a/plugins/official/checkstyle/dist/plugin.wasm b/plugins/official/checkstyle/dist/plugin.wasm index e4ffd3c2..e719df8d 100644 Binary files a/plugins/official/checkstyle/dist/plugin.wasm and b/plugins/official/checkstyle/dist/plugin.wasm differ diff --git a/plugins/official/checkstyle/dist/plugin.wasm.sources b/plugins/official/checkstyle/dist/plugin.wasm.sources index 66dca89d..f7e63f8f 100644 --- a/plugins/official/checkstyle/dist/plugin.wasm.sources +++ b/plugins/official/checkstyle/dist/plugin.wasm.sources @@ -1 +1 @@ -7e015486cc916f5edbac542863373fd7204f5bcd534f6c408d5f9d2b72913862 +49a3c6bf2511ba137457397203244bdfca3d6e10f62dbc7fde9473dab07f6457 diff --git a/plugins/official/csv-editor/dist/plugin.wasm b/plugins/official/csv-editor/dist/plugin.wasm index 5e29e582..752aba90 100644 Binary files a/plugins/official/csv-editor/dist/plugin.wasm and b/plugins/official/csv-editor/dist/plugin.wasm differ diff --git a/plugins/official/csv-editor/dist/plugin.wasm.sources b/plugins/official/csv-editor/dist/plugin.wasm.sources index 3a0d19f0..4f6159a0 100644 --- a/plugins/official/csv-editor/dist/plugin.wasm.sources +++ b/plugins/official/csv-editor/dist/plugin.wasm.sources @@ -1 +1 @@ -a6cf59e74595e13908e2cbf1752c58665c6963de258413c9d7332d2a7dd9cbd0 +c199dc52e00d3943882d532e7e6095f7b2f8d7d6026cf1ccaa685dd6300f2319 diff --git a/plugins/official/git-toolbox/dist/plugin.wasm b/plugins/official/git-toolbox/dist/plugin.wasm index 936c901a..5e26ce4e 100644 Binary files a/plugins/official/git-toolbox/dist/plugin.wasm and b/plugins/official/git-toolbox/dist/plugin.wasm differ diff --git a/plugins/official/git-toolbox/dist/plugin.wasm.sources b/plugins/official/git-toolbox/dist/plugin.wasm.sources index 13c47727..0131a6b6 100644 --- a/plugins/official/git-toolbox/dist/plugin.wasm.sources +++ b/plugins/official/git-toolbox/dist/plugin.wasm.sources @@ -1 +1 @@ -b8dde206a74b222b93e61056d4cbe403617ae240d142e01e91420a2aacdb2da4 +6047be5eeaa1330d7aa1f182e88ae2f352f244829ad9a01fdc3a2a081bdca95d diff --git a/plugins/official/graphviz/dist/plugin.wasm b/plugins/official/graphviz/dist/plugin.wasm index 172e8708..912edbd6 100644 Binary files a/plugins/official/graphviz/dist/plugin.wasm and b/plugins/official/graphviz/dist/plugin.wasm differ diff --git a/plugins/official/graphviz/dist/plugin.wasm.sources b/plugins/official/graphviz/dist/plugin.wasm.sources index 8bdfeeb8..1657b2d5 100644 --- a/plugins/official/graphviz/dist/plugin.wasm.sources +++ b/plugins/official/graphviz/dist/plugin.wasm.sources @@ -1 +1 @@ -a681c2abd4a65d6aeb62efd514e9b170f4fca52b0c14d2d380a3b04be0a3fbc6 +7a3ee410d228d1e39155aa042237d222d23d7f5a45e261086cffc45c6ee1ad32 diff --git a/plugins/official/key-promoter/dist/plugin.wasm b/plugins/official/key-promoter/dist/plugin.wasm index 4a092d36..99400b9b 100644 Binary files a/plugins/official/key-promoter/dist/plugin.wasm and b/plugins/official/key-promoter/dist/plugin.wasm differ diff --git a/plugins/official/key-promoter/dist/plugin.wasm.sources b/plugins/official/key-promoter/dist/plugin.wasm.sources index 23ac06dc..84b4dfaa 100644 --- a/plugins/official/key-promoter/dist/plugin.wasm.sources +++ b/plugins/official/key-promoter/dist/plugin.wasm.sources @@ -1 +1 @@ -3c83bc9559df54457e19e650cdd21bab9d609e22daaa1fc4f596910b70834a34 +7658a95eb9fc27468cae4b1d20e3b5ab299fde478e07f69ab0fdb4cc10caf11d diff --git a/plugins/official/license-header/dist/plugin.wasm b/plugins/official/license-header/dist/plugin.wasm index 6672d2bd..fdcbdd18 100644 Binary files a/plugins/official/license-header/dist/plugin.wasm and b/plugins/official/license-header/dist/plugin.wasm differ diff --git a/plugins/official/license-header/dist/plugin.wasm.sources b/plugins/official/license-header/dist/plugin.wasm.sources index a70c551b..e5ee06b9 100644 --- a/plugins/official/license-header/dist/plugin.wasm.sources +++ b/plugins/official/license-header/dist/plugin.wasm.sources @@ -1 +1 @@ -63f1bbdf6eca5a4642cd3453ae384ff3f876de0373c12008b757a63852164b71 +227a30ef0028f167a20825bdd775b15392161e641f6c68030e01d0c79e263bb5 diff --git a/plugins/official/mybatis/dist/plugin.wasm b/plugins/official/mybatis/dist/plugin.wasm index 1fe1055f..b31649e9 100644 Binary files a/plugins/official/mybatis/dist/plugin.wasm and b/plugins/official/mybatis/dist/plugin.wasm differ diff --git a/plugins/official/mybatis/dist/plugin.wasm.sources b/plugins/official/mybatis/dist/plugin.wasm.sources index 532aada7..34acc267 100644 --- a/plugins/official/mybatis/dist/plugin.wasm.sources +++ b/plugins/official/mybatis/dist/plugin.wasm.sources @@ -1 +1 @@ -0db5cb24c358651d0e728ab3efb52286290c1a38ec379435225c935d4165ad6a +046f769822102de0d4ecec99ad41027cb792941065226716f38a6e64c50ff364 diff --git a/plugins/official/regex-tester/dist/plugin.wasm b/plugins/official/regex-tester/dist/plugin.wasm index d2740781..a8e10a84 100644 Binary files a/plugins/official/regex-tester/dist/plugin.wasm and b/plugins/official/regex-tester/dist/plugin.wasm differ diff --git a/plugins/official/regex-tester/dist/plugin.wasm.sources b/plugins/official/regex-tester/dist/plugin.wasm.sources index 845c9957..df4b262a 100644 --- a/plugins/official/regex-tester/dist/plugin.wasm.sources +++ b/plugins/official/regex-tester/dist/plugin.wasm.sources @@ -1 +1 @@ -257478b050ea360c55e904f2a89c6069293139ff0d97ba860de3f29dac927787 +0b9c3572390a3caab7d47f9b975439f8dde3feff6b9044579d99b8334ac2a692 diff --git a/plugins/official/solidity/dist/plugin.wasm b/plugins/official/solidity/dist/plugin.wasm index e1a8d32d..cef826bb 100644 Binary files a/plugins/official/solidity/dist/plugin.wasm and b/plugins/official/solidity/dist/plugin.wasm differ diff --git a/plugins/official/solidity/dist/plugin.wasm.sources b/plugins/official/solidity/dist/plugin.wasm.sources index 681447c6..09d61924 100644 --- a/plugins/official/solidity/dist/plugin.wasm.sources +++ b/plugins/official/solidity/dist/plugin.wasm.sources @@ -1 +1 @@ -21e7510d6a3712bf0f21d53a5c8d652e3c2787d89a1fc25998cde96efe124207 +0af62bcd4583d126a616cafbf2ddc80f039a3ead2b5dcb7144222b0ebc673432 diff --git a/plugins/official/string-tools/dist/plugin.wasm b/plugins/official/string-tools/dist/plugin.wasm index b38b7db6..3ae03a50 100644 Binary files a/plugins/official/string-tools/dist/plugin.wasm and b/plugins/official/string-tools/dist/plugin.wasm differ diff --git a/plugins/official/string-tools/dist/plugin.wasm.sources b/plugins/official/string-tools/dist/plugin.wasm.sources index 56d78c0a..c511466f 100644 --- a/plugins/official/string-tools/dist/plugin.wasm.sources +++ b/plugins/official/string-tools/dist/plugin.wasm.sources @@ -1 +1 @@ -04844bfbfbcb1770f4f4742658944c592922b037b2387e9340de58ef0d9baacc +24ef150beefb05622212228e1c44cb487fffe6526776762d713f3ebdf4c02ac0 diff --git a/sdk/rust/openidea-plugin/src/host.rs b/sdk/rust/openidea-plugin/src/host.rs index 760c6d30..88cef0e3 100644 --- a/sdk/rust/openidea-plugin/src/host.rs +++ b/sdk/rust/openidea-plugin/src/host.rs @@ -245,6 +245,10 @@ mod imp { wit::views::open_terminal(title, program, args).map_err(error) } + pub fn open_location(path: &str, line: u32, column: u32) -> Result<(), Error> { + wit::views::open_location(path, line, column).map_err(error) + } + pub fn setting(key: &str) -> Option { wit::settings::get(key) } @@ -387,6 +391,10 @@ pub fn open_terminal(title: &str, program: &str, args: &[String]) -> Result<(), imp::open_terminal(title, program, args) } +pub fn open_location(path: &str, line: u32, column: u32) -> Result<(), Error> { + imp::open_location(path, line, column) +} + /// One of the plugin's settings (`[contributes.settings]`) as JSON: the /// user's value (project over global) or the schema's default. pub fn setting(key: &str) -> Option { diff --git a/sdk/rust/openidea-plugin/src/mock.rs b/sdk/rust/openidea-plugin/src/mock.rs index 66f38eb7..334da48f 100644 --- a/sdk/rust/openidea-plugin/src/mock.rs +++ b/sdk/rust/openidea-plugin/src/mock.rs @@ -74,6 +74,7 @@ struct State { refreshed: Vec, view_messages: Vec<(String, String)>, terminals: Vec, + locations: Vec<(String, u32, u32)>, settings: BTreeMap, } @@ -220,6 +221,10 @@ impl MockHost { self.state.borrow().view_messages.clone() } + pub fn opened_locations(&self) -> Vec<(String, u32, u32)> { + self.state.borrow().locations.clone() + } + /// Terminals the plugin opened. pub fn terminals(&self) -> Vec { self.state.borrow().terminals.clone() @@ -595,6 +600,14 @@ pub mod current { }) } + pub fn open_location(path: &str, line: u32, column: u32) -> Result<(), Error> { + with(|s| { + require(s, "project.read")?; + s.locations.push((path.into(), line, column)); + Ok(()) + }) + } + pub fn setting(key: &str) -> Option { with(|s| s.settings.get(key).cloned()) } diff --git a/sdk/rust/openidea-plugin/wit/views.wit b/sdk/rust/openidea-plugin/wit/views.wit index dcc806dc..c4435ece 100644 --- a/sdk/rust/openidea-plugin/wit/views.wit +++ b/sdk/rust/openidea-plugin/wit/views.wit @@ -133,6 +133,10 @@ interface views { /// `program` (pinned by a `process:` grant) with `args` in the /// project root: long-running output (logs) and interactive sessions. open-terminal: func(title: string, program: string, args: list) -> result<_, error>; + /// Opens `path` (project-relative or absolute, inside the project) in an + /// editor of the window the call came from, the caret at `line` and + /// `column` (0-based; UTF-16 column): a view's links to code. + open-location: func(path: string, line: u32, column: u32) -> result<_, error>; } /// The plugin's own settings (`[contributes.settings]`), as the user set them diff --git a/src/bindings.ts b/src/bindings.ts index cb271201..ef2d2dab 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -5480,7 +5480,7 @@ export type PluginEvent = /** For the script of a view's `html` body. */ { type: "viewMessage"; pluginId: string; view: string; message: string } | /** Open a terminal running `program` (already approved and verified). */ -{ type: "openTerminal"; pluginId: string; title: string; program: IpcPath; args: string[]; cwd: IpcPath } | +{ type: "openTerminal"; pluginId: string; title: string; program: IpcPath; args: string[]; cwd: IpcPath } | { type: "openLocation"; pluginId: string; path: IpcPath; line: number; column: number } | /** * Plugin languages, or the servers they may start, changed: re-fetch * `languages` (the host re-detects open documents: [`crate::Host::plugin_languages_changed`]). diff --git a/src/features/plugins/runtimeUi.tsx b/src/features/plugins/runtimeUi.tsx index 85c54bf5..c0b7efd0 100644 --- a/src/features/plugins/runtimeUi.tsx +++ b/src/features/plugins/runtimeUi.tsx @@ -12,7 +12,7 @@ import { onIdeEvent } from "@/platform/ipc"; import { useProject } from "@/platform/project"; import { shortcutLabel } from "@/platform/keymap"; import { BarButton } from "@/ui/BarButton"; -import { registerStatusItem } from "@/workbench/contributions"; +import { maybeEditorService, registerStatusItem } from "@/workbench/contributions"; import { removeTask, upsertTask, useTasks } from "@/workbench/tasks"; import { pluginsBackend } from "./backend"; import { bumpView, deliverViewMessage } from "./views/viewStore"; @@ -259,6 +259,9 @@ export function handleRuntimeEvent(e: PluginEvent): boolean { // The program was approved (`process:`) and verified by the host. void executeAction("terminal.runCommand", { program: e.program, args: e.args, name: e.title, cwd: e.cwd }); return true; + case "openLocation": + void maybeEditorService()?.openFile(e.path, { selection: { line: e.line, col: e.column }, focus: true }); + return true; default: return false; } diff --git a/wit/views.wit b/wit/views.wit index dcc806dc..c4435ece 100644 --- a/wit/views.wit +++ b/wit/views.wit @@ -133,6 +133,10 @@ interface views { /// `program` (pinned by a `process:` grant) with `args` in the /// project root: long-running output (logs) and interactive sessions. open-terminal: func(title: string, program: string, args: list) -> result<_, error>; + /// Opens `path` (project-relative or absolute, inside the project) in an + /// editor of the window the call came from, the caret at `line` and + /// `column` (0-based; UTF-16 column): a view's links to code. + open-location: func(path: string, line: u32, column: u32) -> result<_, error>; } /// The plugin's own settings (`[contributes.settings]`), as the user set them