diff --git a/crates/ide-extension/src/manifest.rs b/crates/ide-extension/src/manifest.rs index bfec7cd72..951a355bd 100644 --- a/crates/ide-extension/src/manifest.rs +++ b/crates/ide-extension/src/manifest.rs @@ -34,7 +34,7 @@ use std::path::{Component, Path, PathBuf}; pub const MANIFEST_FILE: &str = "plugin.toml"; /// The plugin API (engine) version this host implements. -pub const ENGINE_VERSION: &str = "1.7.0"; +pub const ENGINE_VERSION: &str = "1.8.0"; /// The 0.1 API, still accepted for contribution-only packages and 0.1 components. pub const LEGACY_API: &str = "0.1"; /// Marketplace categories, in the order they are shown. @@ -166,6 +166,7 @@ pub const EVENT_KINDS: &[&str] = &[ "editor-focused", "action-performed", "vcs-changed", + "problems-changed", ]; impl std::fmt::Display for ActivationEvent { @@ -372,6 +373,11 @@ impl Manifest { { self.needs_api(&format!("`{event}`"), 6)?; } + // Added after 1.7.0 shipped: a 1.7 host rejects the manifest, so the gate is the minor + // that carries it. + if matches!(event, ActivationEvent::Event(kind) if kind == "problems-changed") { + self.needs_api(&format!("`{event}`"), 8)?; + } if matches!(event, ActivationEvent::Event(kind) if matches!(kind.as_str(), "file-closed" | "settings-changed" | "input-mode-changed" | "run-exited" | "terminal-exited" | "content-tab-action")) { self.needs_api(&format!("`{event}`"), 4)?; diff --git a/crates/ide-host/src/diagnostics_store.rs b/crates/ide-host/src/diagnostics_store.rs new file mode 100644 index 000000000..a3938d3f1 --- /dev/null +++ b/crates/ide-host/src/diagnostics_store.rs @@ -0,0 +1,78 @@ +//! Host-owned diagnostics snapshot: every `Diagnostics` event lands here, whoever +//! emitted it and whatever window it was addressed to. Plugins read this instead of +//! the UI-reported Problems reports, so what a plugin sees no longer depends on +//! window lifetime, the invoking window's project, or the report loop's liveness. + +use ide_core::{Diagnostic, EventSink, IdeEvent, SharedSink}; +use parking_lot::RwLock; +use std::collections::BTreeMap; +use std::path::PathBuf; +use std::sync::Arc; + +#[derive(Default)] +pub struct DiagnosticsStore(RwLock>>>); + +impl DiagnosticsStore { + /// Full replacement for one (file, producer), like the event itself. + pub fn replace(&self, path: &PathBuf, source: &str, items: Vec) { + let mut map = self.0.write(); + let sources = map.entry(path.clone()).or_default(); + if items.is_empty() { + sources.remove(source); + if sources.is_empty() { + map.remove(path); + } + } else { + sources.insert(source.to_owned(), items); + } + } + + /// Every file's diagnostics as one entry per producer. + pub fn problems(&self) -> Vec<(PathBuf, String, Vec)> { + let map = self.0.read(); + map.iter() + .flat_map(|(path, sources)| { + sources.iter().map(move |(source, items)| (path.clone(), source.clone(), items.clone())) + }) + .collect() + } + + pub fn file(&self, path: &PathBuf) -> BTreeMap> { + self.0.read().get(path).cloned().unwrap_or_default() + } + + /// Drops the files under `root` (their project closed; the producers will re-report + /// on open). Returns the number of files removed. + pub fn forget_root(&self, root: &PathBuf) -> usize { + let mut map = self.0.write(); + let before = map.len(); + map.retain(|path, _| !path.starts_with(root)); + before - map.len() + } +} + +/// Records `Diagnostics` events, then delivers them as usual. +pub struct RecordingSink { + pub inner: SharedSink, + pub store: Arc, +} + +impl EventSink for RecordingSink { + fn emit(&self, event: IdeEvent) { + if let IdeEvent::Diagnostics { path, source, items } = &event { + self.store.replace(&path.0.clone(), source, items.clone()); + } + self.inner.emit(event); + } +} + +/// A sink whose diagnostics are recorded; the store outlives every project. +pub type RecordSinkFactory = Box SharedSink + Send + Sync>; + +/// Wraps a sink factory so every sink it creates records diagnostics. +pub fn recording_sink_factory(store: Arc, inner: crate::SinkFactory) -> crate::SinkFactory { + Box::new(move |window| { + let inner = inner(window); + Arc::new(RecordingSink { inner, store: store.clone() }) as SharedSink + }) +} diff --git a/crates/ide-host/src/lib.rs b/crates/ide-host/src/lib.rs index 17e6c66aa..8ea2f63bc 100644 --- a/crates/ide-host/src/lib.rs +++ b/crates/ide-host/src/lib.rs @@ -5,6 +5,7 @@ pub mod agent_secrets; pub mod data_dirs; +pub mod diagnostics_store; pub mod environment; mod formatter; mod java_runtime; @@ -58,6 +59,9 @@ pub struct HostConfig { pub struct Host { pub(crate) mcp_executable: RwLock>, plugin_host: RwLock>, + /// Every `Diagnostics` event the host ever emitted, per file and producer: + /// the snapshot plugins read (`problems`), independent of windows and reports. + pub diagnostics: Arc, pub mcp_reports: mcp::Reports, pub mcp_requests: mcp::Requests, /// Commands agents run in the Run tool window (`run_command`). @@ -135,12 +139,16 @@ impl Host { runtime, project_environment, } = config; + // Every sink the host hands out records diagnostics into the shared store first. + let diagnostics: Arc = Arc::default(); + let sink_for: SinkFactory = diagnostics_store::recording_sink_factory(diagnostics.clone(), sink_for); let global_settings = SettingsStore::load(&config_dir, None); let ssh = Arc::new(ide_ssh::SshManager::new(data_dir.join("ssh-passwords.json"))); ssh.set_configs(global_settings.effective().ssh.configs); let host = Self { plugin_host: RwLock::default(), mcp_executable: RwLock::default(), + diagnostics, mcp_reports: Default::default(), mcp_requests: Default::default(), mcp_agent_runs: Default::default(), @@ -288,6 +296,9 @@ impl Host { } project.files_events_active.store(false, std::sync::atomic::Ordering::Release); project.plugin_diagnostics.close(); + // The producers will re-report when a project reopens; until then the snapshot + // must not answer for files whose project is gone. + self.diagnostics.forget_root(&project.root); if let Some(plugins) = plugins::editor_plugin_host() { plugins.invalidate_client_navigation(&project.root, Some(window), None); plugins.set_active_file(window, None); diff --git a/crates/ide-host/src/ops/spell.rs b/crates/ide-host/src/ops/spell.rs index dd741da92..9ac868bf3 100644 --- a/crates/ide-host/src/ops/spell.rs +++ b/crates/ide-host/src/ops/spell.rs @@ -47,14 +47,17 @@ pub async fn spell_check( let syntax = host.syntax(); let cancel = host.begin_request(request); let result = blocking(move || { + let path = project.documents.snapshot(doc)?.path.0.clone(); let snap = project.documents.text_snapshot(doc)?; if snap.large { - return Ok(Vec::new()); + return Ok((path, Vec::new())); } let regions = if PROSE.contains(&snap.language.as_str()) { ide_spell::prose_regions(&snap.text) } else { - let Some(found) = syntax.spelling_regions(&snap.language, &snap.text) else { return Ok(Vec::new()) }; + let Some(found) = syntax.spelling_regions(&snap.language, &snap.text) else { + return Ok((path, Vec::new())); + }; found .into_iter() .map(|r| Region { @@ -72,11 +75,32 @@ pub async fn spell_check( if cancel.is_cancelled() { return Err(IdeError::cancelled()); } - speller(&project).check(&snap.text, ®ions, &|| cancel.is_cancelled()).ok_or_else(IdeError::cancelled) + let typos = + speller(&project).check(&snap.text, ®ions, &|| cancel.is_cancelled()).ok_or_else(IdeError::cancelled)?; + Ok((path, typos)) }) .await; host.end_request(request); - result + // Record the typos in the host's diagnostics snapshot so line-end annotations + // see them without waiting for a UI report; the editor still draws from its + // own call above, unchanged. + if let Ok((path, typos)) = &result { + let items: Vec = typos + .iter() + .map(|t| ide_core::Diagnostic { + range: t.range, + severity: ide_core::Severity::Info, + message: format!("Typo: In word '{}'", t.word), + source: Some("Spelling".into()), + code: None, + unnecessary: false, + deprecated: false, + server: None, + }) + .collect(); + host.diagnostics.replace(path, "Spelling", items); + } + result.map(|(_, typos)| typos) } /// Replacements for a misspelt `word`, best first (Typo › Change to…). diff --git a/crates/ide-host/src/plugins/api.rs b/crates/ide-host/src/plugins/api.rs index e4c83af78..48be3908c 100644 --- a/crates/ide-host/src/plugins/api.rs +++ b/crates/ide-host/src/plugins/api.rs @@ -3176,26 +3176,17 @@ impl PluginWorkspace for HostWorkspace { Ok(out) } fn problems(&self, client: &str, path: Option<&Path>) -> Result, HostError> { + // The host records every `Diagnostics` event it emits (language servers, + // builds, spelling, plugin providers), so this sees a file's problems + // whatever window opened it — not just the ones its UI reported. let host = self.0.upgrade().ok_or(HostError::Cancelled)?; - let root = host.project(client).map_err(failed)?.root.clone(); - let windows = host.windows_for_root(&root); + host.project(client).map_err(failed)?; let mut out = Vec::new(); - for file in host.mcp_reports.problems(&windows) { - if path.is_some_and(|p| p != file.path.0) { + for (file, source, diagnostics) in host.diagnostics.problems() { + if path.is_some_and(|p| p != file) { continue; } - if ide_plugin::resolve_new_in_root(&root, &to_ipc(&file.path.0)).is_err() { - continue; - } - let mut sources: BTreeMap> = BTreeMap::new(); - for d in file.items { - sources.entry(d.server.clone().or_else(|| d.source.clone()).unwrap_or_default()).or_default().push(d); - } - out.extend(sources.into_iter().map(|(source, diagnostics)| ide_plugin::FileProblems { - path: to_ipc(&file.path.0), - source, - diagnostics, - })); + out.push(ide_plugin::FileProblems { path: to_ipc(&file), source, diagnostics }); } Ok(out) } @@ -3321,6 +3312,8 @@ impl PluginWorkspace for HostWorkspace { path: &Path, items: Vec, ) -> Result<(), HostError> { + // Any open project may publish problems for any file: annotations follow the + // file, not the window (an editor can show a file outside every project root). let projects = HostWorkspace::projects(self); let project = projects .iter() @@ -3332,7 +3325,8 @@ impl PluginWorkspace for HostWorkspace { .iter() .find(|p| crate::project::workspace_member_roots(&p.root).iter().any(|m| path.starts_with(m))) }) - .ok_or_else(|| HostError::Invalid(format!("{} is not in an open project", path.display())))?; + .or_else(|| projects.first()) + .ok_or_else(|| HostError::NotFound("no project is open".into()))?; project.sink.emit(ide_core::IdeEvent::Diagnostics { path: path.to_path_buf().into(), source: format!("plugin:{source}"), @@ -4135,16 +4129,23 @@ impl PluginApi { return Err(HostError::Invalid("reversed decoration range".into())); } let replacement = if let Some(path) = path { - let workspace = self.shared.workspace().ok_or_else(|| HostError::NotFound("no project open".into()))?; - let (root, real) = workspace - .projects() - .into_iter() - .filter_map(|root| ide_plugin::resolve_new_in_root(&root, &to_ipc(path)).ok().map(|real| (root, real))) - .max_by_key(|(root, _)| root.components().count()) - .ok_or_else(|| HostError::Invalid("path outside open projects".into()))?; + // The decoration follows the file, not the window's project: editors render + // whatever they have open, including files outside every project root. The + // set's root stays the path's own (deepest open) project for tree filtering. + let root = self + .shared + .workspace() + .map(|w| { + w.projects() + .into_iter() + .filter(|root| path.starts_with(root)) + .max_by_key(|root| root.components().count()) + }) + .unwrap_or_default() + .unwrap_or_else(|| path.parent().map(Path::to_path_buf).unwrap_or_else(|| path.to_path_buf())); Some(super::DecorationSet { root: root.into(), - path: real.into(), + path: ide_core::IpcPath(path.to_path_buf()), set_id: set.into(), tree, items, @@ -4188,6 +4189,8 @@ fn decoration_style(style: &str) -> bool { | "warning" | "error" | "info" + | "typo" + | "weak" | "underline" | "strikethrough" | "muted" @@ -4308,8 +4311,11 @@ mod workbench_tests { }; api.decorations(Some(&path), "set", vec![item("highlight"), item("unknown")]).unwrap(); assert_eq!(api.shared.decoration_sets()["test"][0].items.len(), 1); - assert!(api.decorations(Some(&root.join("../outside.txt")), "set", vec![]).is_err()); + // Decorations follow the file: a path outside the open project is accepted too. + api.decorations(Some(&root.join("../outside.txt")), "set", vec![]).unwrap(); + assert_eq!(api.shared.decoration_sets()["test"].len(), 2); api.decorations(None, "set", vec![]).unwrap(); + assert!(api.shared.decoration_sets()["test"].is_empty()); for i in 0..50 { api.decorations(Some(&path), &format!("s{i}"), vec![]).unwrap(); } @@ -4350,6 +4356,29 @@ mod workbench_tests { api.shared.clear_plugin("test", true); assert!(api.shared.decoration_sets().is_empty()); } + #[test] + fn decoration_styles_keep_the_severity_palette_the_editor_renders() { + // Inspection Lens colours typos and weak warnings with their own styles; the + // host must keep them, not silently drop them the way an unknown style is dropped. + for style in [ + "highlight", + "search", + "added", + "modified", + "removed", + "warning", + "error", + "info", + "typo", + "weak", + "underline", + "strikethrough", + "muted", + ] { + assert!(decoration_style(style), "{style} should be a known style"); + } + assert!(!decoration_style("unknown")); + } fn git(root: &Path, args: &[&str]) { let out = StdCommand::new("git").current_dir(root).args(args).output().unwrap(); assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr)); diff --git a/crates/ide-host/src/plugins/event_paths.rs b/crates/ide-host/src/plugins/event_paths.rs index aa1cccad2..ad0228495 100644 --- a/crates/ide-host/src/plugins/event_paths.rs +++ b/crates/ide-host/src/plugins/event_paths.rs @@ -129,6 +129,7 @@ impl PluginHost { } let path = match event { WorkbenchEvent::FileOpened(path) | WorkbenchEvent::FileSaved(path) => Path::new(path), + WorkbenchEvent::EditorFocused(Some(path)) => Path::new(path), WorkbenchEvent::DocumentChanged(change) => Path::new(&change.path), _ => return Some(event.clone()), }; @@ -136,16 +137,25 @@ impl PluginHost { return None; } let workspace = workspace.as_ref()?; + let real = ide_plugin::resolve_real(path); + // The file's own project, not the invoking window's: annotations follow + // the file, so a plugin hears about a file another window has open. let root = match client { - Some(client) => workspace.project_of(client), - None => { - let real = ide_plugin::resolve_real(path); - workspace - .projects() - .into_iter() - .filter(|r| real.starts_with(ide_plugin::resolve_real(r))) - .max_by_key(|r| r.components().count()) - } + Some(client) => workspace + .project_of(client) + .filter(|r| real.starts_with(ide_plugin::resolve_real(r))) + .or_else(|| { + workspace + .projects() + .into_iter() + .filter(|r| real.starts_with(ide_plugin::resolve_real(r))) + .max_by_key(|r| r.components().count()) + }), + None => workspace + .projects() + .into_iter() + .filter(|r| real.starts_with(ide_plugin::resolve_real(r))) + .max_by_key(|r| r.components().count()), }?; if plugin.dev_root.as_deref().is_some_and(|r| r != root) { return None; diff --git a/crates/ide-host/src/plugins/ui_points.rs b/crates/ide-host/src/plugins/ui_points.rs index b8ca5f94f..06ed4e3d8 100644 --- a/crates/ide-host/src/plugins/ui_points.rs +++ b/crates/ide-host/src/plugins/ui_points.rs @@ -352,6 +352,15 @@ where deserializer.deserialize_map(Properties) } +/// A sample shown in a setting's own style (the Colour Scheme colours), e.g. the +/// message an Inspection Lens severity produces. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct SettingExample { + pub text: String, + /// `error`, `warning`, `weak`, `typo`, `info` or `muted`: which sample style to draw. + pub style: String, +} + /// One setting of a plugin. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] @@ -371,6 +380,8 @@ pub struct SettingSchema { #[serde(default, skip_serializing_if = "Option::is_none")] pub group: Option, #[serde(default, skip_serializing_if = "Option::is_none")] + pub example: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] pub items: Option>, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub properties: Vec, @@ -455,6 +466,20 @@ fn setting_at(key: &str, raw: &serde_json::value::RawValue, depth: u8) -> Result Some(_) => Err(format!("setting {key:?}: `{name}` is a string up to {max} bytes")), } }; + let example = match o.get("example") { + None => None, + Some(v) => { + let e: SettingExample = serde_json::from_value(v.clone()) + .map_err(|e| format!("setting {key:?}: `example` is {{\"text\", \"style\"}}: {e}"))?; + if e.text.is_empty() || e.text.len() > 300 { + return Err(format!("setting {key:?}: `example.text` is 1–300 bytes")); + } + if e.style.len() > 32 || !e.style.chars().all(|c| c.is_ascii_lowercase()) { + return Err(format!("setting {key:?}: `example.style` is a lowercase style name")); + } + Some(e) + } + }; let s = SettingSchema { key: key.to_owned(), kind: kind.to_owned(), @@ -464,6 +489,7 @@ fn setting_at(key: &str, raw: &serde_json::value::RawValue, depth: u8) -> Result options, multiline: o.get("multiline").and_then(Value::as_bool).unwrap_or(false), group: text("group", MAX_TITLE)?, + example, items, properties, }; diff --git a/crates/ide-host/tests/plugin_diagnostics.rs b/crates/ide-host/tests/plugin_diagnostics.rs index e12e40b04..35200241f 100644 --- a/crates/ide-host/tests/plugin_diagnostics.rs +++ b/crates/ide-host/tests/plugin_diagnostics.rs @@ -430,3 +430,61 @@ async fn typing_is_debounced_versioned_and_scoped_to_document_project_and_window host.shutdown().await; set_editor_providers(None); } + +/// A host with a project open: `problems` and `decorations` need a workspace. +fn harness(root: &Path) -> Arc { + let host = Arc::new(Host::new(HostConfig { + config_dir: root.join("config"), + data_dir: root.join("data"), + local_data_dir: root.join("local"), + languages: Arc::new(ide_language::LanguageRegistry::load(None).unwrap()), + syntax: Arc::default(), + sink_for: Box::new(|_| Arc::new(ide_core::NullSink)), + owner: |w| w, + runtime: tokio::runtime::Handle::current(), + project_environment: false, + })); + host.open_project("main", root).unwrap(); + host +} + +fn plugins_on(host: &Arc, root: &Path) -> PluginHost { + let plugins = PluginHost::new(root.join("plugins"), Arc::new(|_: Option<&str>, _| {}), generous()).unwrap(); + plugins.set_workspace(Some(Arc::new(HostWorkspace(Arc::downgrade(host))))); + plugins.install_from_dir(&fixture(), &Consent::all()).unwrap(); + plugins +} + +#[tokio::test(flavor = "multi_thread")] +async fn a_plugin_reads_back_the_diagnostics_the_host_recorded() { + let tmp = tempfile::tempdir().unwrap(); + let host = harness(tmp.path()); + let plugins = plugins_on(&host, tmp.path()); + let path = tmp.path().join("a.txt"); + std::fs::write(&path, "text").unwrap(); + // `publish` goes through the host's recording sink into the snapshot a plugin reads. + let invocation = || Invocation { client: Some("main".into()), ..Invocation::default() }; + plugins.run_command(ID, "publish", Some(path.to_str().unwrap()), invocation()).unwrap(); + let problems = plugins.run_command(ID, "problems", None, invocation()).unwrap().unwrap(); + assert!(problems.contains("a.txt"), "problems returned {problems:?}"); + assert!(problems.ends_with(":1"), "one diagnostic from its producer, got {problems:?}"); +} + +#[tokio::test(flavor = "multi_thread")] +async fn a_line_end_annotation_takes_the_typo_and_weak_styles() { + let tmp = tempfile::tempdir().unwrap(); + let host = harness(tmp.path()); + let plugins = plugins_on(&host, tmp.path()); + let path = tmp.path().join("a.txt"); + std::fs::write(&path, "text").unwrap(); + // `decorate` publishes both styles; the host drops an unknown style silently, so a + // rejection here means one of the two is not a known decoration style. + plugins + .run_command( + ID, + "decorate", + Some(path.to_str().unwrap()), + Invocation { client: Some("main".into()), ..Invocation::default() }, + ) + .unwrap(); +} diff --git a/crates/ide-host/tests/plugin_workbench.rs b/crates/ide-host/tests/plugin_workbench.rs index 117c4552a..a74b8c061 100644 --- a/crates/ide-host/tests/plugin_workbench.rs +++ b/crates/ide-host/tests/plugin_workbench.rs @@ -139,7 +139,7 @@ async fn text_search_uses_overlay_excludes_utf16_and_limits() { } #[tokio::test(flavor = "multi_thread")] -async fn problems_share_project_reports_and_keep_sources_separate() { +async fn problems_come_from_the_host_snapshot_whatever_window_published_them() { let f = Harness::new(); let path = f.root.join("a.txt"); std::fs::write(&path, "text").unwrap(); @@ -153,21 +153,30 @@ async fn problems_share_project_reports_and_keep_sources_separate() { deprecated: false, server: None, }; - f.host.mcp_reports.report_problems( - "second", - true, - vec![ide_host::mcp::FileProblems { + // Producers publish per (file, producer) through the event sink, like the + // language servers and the spelling check do. + let publish = |host: &Host, source: &str, items: Vec| { + host.project("main").unwrap().sink.emit(ide_core::IdeEvent::Diagnostics { path: path.clone().into(), - items: vec![diagnostic("lsp"), diagnostic("plugin")], - }], - ); + source: source.into(), + items, + }); + }; + publish(&f.host, "rust-analyzer", vec![diagnostic("lsp")]); + publish(&f.host, "build", vec![diagnostic("plugin")]); + // A file outside the window's project is visible too: annotations follow the file. + let outside = f.host.diagnostics.problems().len(); let workspace = f.workspace(); let files = workspace.problems("main", Some(&path)).unwrap(); assert_eq!(files.len(), 2); assert!(files.iter().all(|f| f.diagnostics.len() == 1)); assert!(workspace.problems("main", Some(&f.root.join("other.txt"))).unwrap().is_empty()); - f.host.mcp_reports.forget_window("second"); - assert!(workspace.problems("main", None).unwrap().is_empty()); + assert_eq!(outside, 2); + // A full replacement per producer clears only that producer. + publish(&f.host, "rust-analyzer", vec![]); + let files = workspace.problems("main", None).unwrap(); + assert_eq!(files.len(), 1); + assert_eq!(files[0].source, "build"); } #[tokio::test(flavor = "multi_thread")] diff --git a/crates/ide-plugin/src/runtime.rs b/crates/ide-plugin/src/runtime.rs index 9a7c77ae7..46f5adcd7 100644 --- a/crates/ide-plugin/src/runtime.rs +++ b/crates/ide-plugin/src/runtime.rs @@ -55,7 +55,7 @@ use views_bindings::exports::openidea::plugin::view_provider as wit_view; use views_bindings::openidea::plugin::view_types as wit_vt; /// The API version of the world above, `major.minor` of [`host_api`]. -pub const API_VERSION: &str = "1.7"; +pub const API_VERSION: &str = "1.8"; const EPOCH_TICK: Duration = Duration::from_millis(10); pub(crate) const MAX_TEXT: usize = 1 << 20; pub(crate) const MAX_TITLE: usize = 256; @@ -1058,6 +1058,9 @@ fn event_to_wit(e: WorkbenchEvent) -> wit_events::Event { }), WorkbenchEvent::EditorFocused(p) => wit_events::Event::EditorFocused(p), WorkbenchEvent::VcsChanged(p) => wit_events::Event::VcsChanged(p), + WorkbenchEvent::ProblemsChanged => { + wit_events::Event::Other(wit_types::Extension { kind: "problems-changed".into(), data: "{}".into() }) + } WorkbenchEvent::Scheduled => wit_events::Event::Scheduled, WorkbenchEvent::ActionPerformed(a) => wit_events::Event::ActionPerformed(wit_events::ActionPerformed { action: a.action, diff --git a/crates/ide-plugin/src/types.rs b/crates/ide-plugin/src/types.rs index 07d9508c4..a86b9bf77 100644 --- a/crates/ide-plugin/src/types.rs +++ b/crates/ide-plugin/src/types.rs @@ -119,11 +119,13 @@ pub enum EventKind { EditorFocused, ActionPerformed, VcsChanged, + /// Problems of the window's files changed (a language server or a report). + ProblemsChanged, Scheduled, } impl EventKind { - pub const ALL: [Self; 16] = [ + pub const ALL: [Self; 17] = [ Self::DocumentChanged, Self::WebsocketReady, Self::FileClosed, @@ -139,6 +141,7 @@ impl EventKind { Self::EditorFocused, Self::ActionPerformed, Self::VcsChanged, + Self::ProblemsChanged, Self::Scheduled, ]; @@ -159,6 +162,7 @@ impl EventKind { Self::EditorFocused => "editor-focused", Self::ActionPerformed => "action-performed", Self::VcsChanged => "vcs-changed", + Self::ProblemsChanged => "problems-changed", Self::Scheduled => "scheduled", } } @@ -294,6 +298,8 @@ pub enum WorkbenchEvent { EditorFocused(Option), ActionPerformed(ActionPerformed), VcsChanged(String), + /// Problems of the window's files changed; plugins re-read `problems`. + ProblemsChanged, Scheduled, } @@ -315,6 +321,7 @@ impl WorkbenchEvent { Self::EditorFocused(_) => EventKind::EditorFocused, Self::ActionPerformed(_) => EventKind::ActionPerformed, Self::VcsChanged(_) => EventKind::VcsChanged, + Self::ProblemsChanged => EventKind::ProblemsChanged, Self::Scheduled => EventKind::Scheduled, } } diff --git a/crates/ide-plugin/tests/api_compat.rs b/crates/ide-plugin/tests/api_compat.rs index 882379639..45a1d18c1 100644 --- a/crates/ide-plugin/tests/api_compat.rs +++ b/crates/ide-plugin/tests/api_compat.rs @@ -91,15 +91,15 @@ fn the_sdk_carries_the_hosts_wit() { #[test] fn the_version_check_runs_compatible_components_and_explains_the_rest() { - let host = v("1.7.0"); - for ok in ["1.0.0", "1.3.0", "1.4.7", "1.5.0", "1.6.0", "1.7.0", "1.0.0-rc.1"] { + let host = v("1.8.0"); + for ok in ["1.0.0", "1.3.0", "1.4.7", "1.5.0", "1.6.0", "1.7.0", "1.8.0", "1.0.0-rc.1"] { assert_eq!(check_api(&v(ok), &host), Ok(()), "{ok}"); } - let newer = check_api(&v("1.8.0"), &host).unwrap_err(); - assert!(newer.contains("1.8") && newer.contains("update OpenIDEA"), "{newer}"); + let newer = check_api(&v("1.9.0"), &host).unwrap_err(); + assert!(newer.contains("1.9") && newer.contains("update OpenIDEA"), "{newer}"); assert!(check_api(&v("2.0.0"), &host).unwrap_err().contains("update OpenIDEA")); let older = check_api(&v("0.2.0"), &host).unwrap_err(); - assert!(older.contains("rebuild it with the 1.7 SDK"), "{older}"); + assert!(older.contains("rebuild it with the 1.8 SDK"), "{older}"); // 0.x: every minor is its own API. assert_eq!(check_api(&v("0.2.0"), &v("0.2.5")), Ok(())); assert!(check_api(&v("0.1.0"), &v("0.2.0")).is_err()); diff --git a/crates/ide-plugin/tests/fixtures/api14/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/api14/dist/plugin.wasm index e993a26c6..37c7662d8 100644 Binary files a/crates/ide-plugin/tests/fixtures/api14/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/api14/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/api14/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/api14/dist/plugin.wasm.sources index 05ae033f6..0b68a6c45 100644 --- a/crates/ide-plugin/tests/fixtures/api14/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/api14/dist/plugin.wasm.sources @@ -1 +1 @@ -d6c855f1f91aeb02eec5d6c8a07be4eb64bae0704619f44cb67f3b832daf01ee +6c50332c868a890b91a7f92ef7f92e76a4818b7435291e99c77c0b9f0931b81c diff --git a/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm index d41d9002e..9e8c7dfb6 100644 Binary files a/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm.sources index f2d8f7a7c..be6cb4c6c 100644 --- a/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm.sources @@ -1 +1 @@ -51d00702c42c7167083caea70cf2df022e512bcaf89af8addf526421ff45a15f +fdcb208365db90bcd06d0d0df3d436b69d1229c40f7c63dd6f1ab768e0d236b8 diff --git a/crates/ide-plugin/tests/fixtures/data-tools/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/data-tools/dist/plugin.wasm index 3005b4795..ba145c311 100644 Binary files a/crates/ide-plugin/tests/fixtures/data-tools/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/data-tools/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/data-tools/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/data-tools/dist/plugin.wasm.sources index 4f5678f35..fd05232f4 100644 --- a/crates/ide-plugin/tests/fixtures/data-tools/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/data-tools/dist/plugin.wasm.sources @@ -1 +1 @@ -40d2fd76ba96b0c93ea0a034c6749709ddc00fc119046e653a50b180a9c4718d +3108abe8d381b224ec4b44ecf4c43c045671bd5e221ee21582e25497f8c14dff diff --git a/crates/ide-plugin/tests/fixtures/detail-views/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/detail-views/dist/plugin.wasm index c7d169bf8..9a7048a74 100644 Binary files a/crates/ide-plugin/tests/fixtures/detail-views/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/detail-views/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/detail-views/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/detail-views/dist/plugin.wasm.sources index c6be58b09..505f25652 100644 --- a/crates/ide-plugin/tests/fixtures/detail-views/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/detail-views/dist/plugin.wasm.sources @@ -1 +1 @@ -08822287f3649412340f9dbbe2f0ea5a5cd2f543a3c242c5d0a29c422c4c5367 +876a84b7da5a4b27fce47cf71a4dc28eb1ea6c9d54283c1b73a2ae2411653bf5 diff --git a/crates/ide-plugin/tests/fixtures/diagnostics/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/diagnostics/dist/plugin.wasm index 881c7ac96..86d302416 100644 Binary files a/crates/ide-plugin/tests/fixtures/diagnostics/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/diagnostics/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/diagnostics/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/diagnostics/dist/plugin.wasm.sources index 13937efd6..be6cf7984 100644 --- a/crates/ide-plugin/tests/fixtures/diagnostics/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/diagnostics/dist/plugin.wasm.sources @@ -1 +1 @@ -43eba23cc14cf3c74bfc934099cd58df20f07ee5df96cc10100b90016c544a7a +234dc693e01e484b91fa7f2d0ac0e7757fb69c9fba81f06bf8ef1cd3624228d8 diff --git a/crates/ide-plugin/tests/fixtures/diagnostics/src/lib.rs b/crates/ide-plugin/tests/fixtures/diagnostics/src/lib.rs index d7e57196f..b936262d0 100644 --- a/crates/ide-plugin/tests/fixtures/diagnostics/src/lib.rs +++ b/crates/ide-plugin/tests/fixtures/diagnostics/src/lib.rs @@ -21,6 +21,31 @@ impl Plugin for Fixture { &[Diagnostic::new(Range::default(), Severity::Info, "Manual problem")], ) .map(|()| None), + // The host-owned snapshot, one entry per producer: what `problems` reads. + "problems" => { + let files: Vec = host::problems(None)? + .into_iter() + .map(|f| format!("{}:{}:{}", f.path, f.source, f.diagnostics.len())) + .collect(); + Ok(Some(files.join("|"))) + } + // A line-end annotation in the severity styles the editor draws: the host must + // keep `typo` and `weak` instead of dropping them as unknown. + "decorate" => { + let path = arg.unwrap_or_default(); + let item = |style: &str, after: &str| openidea_plugin::Decoration { + range: Range::default(), + style: style.into(), + hover: None, + after: Some(after.into()), + }; + host::set_decorations( + path, + "fixture", + &[item("typo", "typo message"), item("weak", "weak message")], + ) + .map(|()| None) + } _ => Err(Error::NotFound(id.into())), } } diff --git a/crates/ide-plugin/tests/fixtures/endpoints/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/endpoints/dist/plugin.wasm index 7b742da06..a19a9146c 100644 Binary files a/crates/ide-plugin/tests/fixtures/endpoints/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/endpoints/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/endpoints/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/endpoints/dist/plugin.wasm.sources index aa0b6f21e..10fcc2993 100644 --- a/crates/ide-plugin/tests/fixtures/endpoints/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/endpoints/dist/plugin.wasm.sources @@ -1 +1 @@ -520d9a95328dc772bbb913a2e5e22af8243c95ca45744cbb65b25747653d2a34 +927c329fd21d12fd5f423b937e1be756be45a4c6dceee08679e762d5d9f511f2 diff --git a/crates/ide-plugin/tests/fixtures/execution/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/execution/dist/plugin.wasm index a8582fce2..8ceb7f712 100644 Binary files a/crates/ide-plugin/tests/fixtures/execution/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/execution/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/execution/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/execution/dist/plugin.wasm.sources index 4e40f208f..d0d8fbcb3 100644 --- a/crates/ide-plugin/tests/fixtures/execution/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/execution/dist/plugin.wasm.sources @@ -1 +1 @@ -acd3d0ee3f886c6a271ce3139200ce5bdb46e40f9c68e495c5ff9b4db98a88ac +e7c77fde915e0765f91929dba2aa8eff606f2302e078b7305d272a4de5b9d80b diff --git a/crates/ide-plugin/tests/fixtures/ext-owner/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/ext-owner/dist/plugin.wasm index 904940c73..16f45abba 100644 Binary files a/crates/ide-plugin/tests/fixtures/ext-owner/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/ext-owner/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/ext-owner/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/ext-owner/dist/plugin.wasm.sources index bb15ce5d4..95b6776b0 100644 --- a/crates/ide-plugin/tests/fixtures/ext-owner/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/ext-owner/dist/plugin.wasm.sources @@ -1 +1 @@ -0cf3bc2197f0bd5339fe3c2199bd973e68b3637b8c5ae07902e62659160275e3 +3feb9df8b408dedbc3586864db00b92c8cb244c8ad5b8f10c95edb8eb3d30df1 diff --git a/crates/ide-plugin/tests/fixtures/file-templates/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/file-templates/dist/plugin.wasm index 7b1148a93..7f1f12f8d 100644 Binary files a/crates/ide-plugin/tests/fixtures/file-templates/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/file-templates/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/file-templates/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/file-templates/dist/plugin.wasm.sources index 5cf40b770..142962890 100644 --- a/crates/ide-plugin/tests/fixtures/file-templates/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/file-templates/dist/plugin.wasm.sources @@ -1 +1 @@ -17f837c8d6f58800728e00060afe68b2a093085d816d809c9e67065fdd165352 +3bfce29f90b9d2f20082b9bdd0957c6c58c265a346305ec948f514537a4579c8 diff --git a/crates/ide-plugin/tests/fixtures/files-events/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/files-events/dist/plugin.wasm index 645d48f2b..931e0665f 100644 Binary files a/crates/ide-plugin/tests/fixtures/files-events/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/files-events/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/files-events/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/files-events/dist/plugin.wasm.sources index 71aa984ce..737abd1e2 100644 --- a/crates/ide-plugin/tests/fixtures/files-events/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/files-events/dist/plugin.wasm.sources @@ -1 +1 @@ -2cca80220fab6764a8e1f51db8ed2f55b603000c8d0dbf5913a441876b7857ba +ca72c5cfa2f76a93bc07495d50e58181a9c6c2fc75854cf548bb0278814ddb87 diff --git a/crates/ide-plugin/tests/fixtures/grouped-views/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/grouped-views/dist/plugin.wasm index 18dbd86d9..1248830e9 100644 Binary files a/crates/ide-plugin/tests/fixtures/grouped-views/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/grouped-views/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/grouped-views/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/grouped-views/dist/plugin.wasm.sources index 945599603..4a9935fb6 100644 --- a/crates/ide-plugin/tests/fixtures/grouped-views/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/grouped-views/dist/plugin.wasm.sources @@ -1 +1 @@ -54211437002ad83a9d3d642f2dca5f37cadd89d7d77ddc7de545db87b99fb8b7 +8a4a7df8fd6cf397750ac24cedf06174ed50e4214ef44029e29f7d0557d426ba diff --git a/crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm index ec95eb325..5f8e2f150 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 2390b49e5..16d543970 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 @@ -4326d9cdefe71f4bb9aab043ccef883abbd13145b1579352b97c66889f514c68 +8d12313675e0869e087ac4c79bdfb1a9738867e28af2437eff27cb07254f572b diff --git a/crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm index 175b249cb..a317db085 100644 Binary files a/crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm.sources index c564021f6..9d1841c7f 100644 --- a/crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm.sources @@ -1 +1 @@ -2469023f4f29b9abdaa27c9d96774229c4dd002366421dc508926c3fed1fba1b +1b2ad52d4cbb4abb662be5772f050844836f8e3f0c525edd0de19ff45e95a148 diff --git a/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm index 637f33136..eab7caf4b 100644 Binary files a/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm.sources index ad44252c4..1d2478b72 100644 --- a/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm.sources @@ -1 +1 @@ -233461ab52a12e27fa600e108e83cab1b930b3e61812e0c048c99c1e18c6d646 +13babf73d8d3d029aba26641b01a4add3abb3dab84394d8bd676edb064dc0c19 diff --git a/crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm index 35bf222e2..90ba12497 100644 Binary files a/crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm.sources index 88456bafd..9cf12de79 100644 --- a/crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm.sources @@ -1 +1 @@ -bfc654523da608bbc008ea6a15a0cfd0b129ffab70088bf6b610786b63b5352e +a7790bf28783d57ed47f635a58981b80ddaecd7074cf88c539465b57132a9f8f diff --git a/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm index 1224753b5..7667e515e 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 eee3f9a14..214219b97 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 @@ -c8536be072d27aa89f5e92b5b96e38ed65b7f06c35c05ecbd4a151218203c5f7 +af7070f6ca3a14cf480260aa21b7a6e95038996d224d40e53a487c96a8f3acf2 diff --git a/crates/ide-plugin/tests/fixtures/language-features/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/language-features/dist/plugin.wasm index e0419de00..eb850efaa 100644 Binary files a/crates/ide-plugin/tests/fixtures/language-features/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/language-features/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/language-features/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/language-features/dist/plugin.wasm.sources index 689cccfb2..eb5e9f489 100644 --- a/crates/ide-plugin/tests/fixtures/language-features/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/language-features/dist/plugin.wasm.sources @@ -1 +1 @@ -7c0c1f5035e12c794d48cb75a8fc259d11dd99ba3188030fe1d9956720ee6ad5 +16930ecfe74d096e6dcaa5fbf35664128b4d3844f47993a947aabe9d2a9650de diff --git a/crates/ide-plugin/tests/fixtures/misbehave/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/misbehave/dist/plugin.wasm index 0ecfa0230..04bacd9eb 100644 Binary files a/crates/ide-plugin/tests/fixtures/misbehave/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/misbehave/dist/plugin.wasm differ 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 66f567d07..72ec0c21b 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 @@ -3cf5fd6ab0ce909b806230e20a7fa46264f85937018cfa68865ec0eeb92d4b72 +25eaf7ac2837ade4b3204698d3af41854324ece6ec196daba84bea6bb41f6cde diff --git a/crates/ide-plugin/tests/fixtures/misbehave/src/lib.rs b/crates/ide-plugin/tests/fixtures/misbehave/src/lib.rs index 0b7b24b88..343072d67 100644 --- a/crates/ide-plugin/tests/fixtures/misbehave/src/lib.rs +++ b/crates/ide-plugin/tests/fixtures/misbehave/src/lib.rs @@ -5,9 +5,9 @@ wit_bindgen::generate!({ inline: " package fixture:misbehave; world misbehave { - include openidea:plugin/plugin-syntax@1.7.0; - export openidea:plugin/providers@1.7.0; - export openidea:plugin/file-actions@1.7.0; + include openidea:plugin/plugin-syntax@1.8.0; + export openidea:plugin/providers@1.8.0; + export openidea:plugin/file-actions@1.8.0; } ", path: "../../../../../wit", diff --git a/crates/ide-plugin/tests/fixtures/path-events/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/path-events/dist/plugin.wasm index 1dfacd5ed..315a44862 100644 Binary files a/crates/ide-plugin/tests/fixtures/path-events/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/path-events/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/path-events/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/path-events/dist/plugin.wasm.sources index 9f3b87571..d38403405 100644 --- a/crates/ide-plugin/tests/fixtures/path-events/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/path-events/dist/plugin.wasm.sources @@ -1 +1 @@ -d349977fd3cbfba92d1d953ac09f65dae5b83b7070723b59553d791c638acf81 +f7cd477be706ee5da2656ffb5435cb2b52443aec1270b3b1d804b2bdc6ed8bc8 diff --git a/crates/ide-plugin/tests/fixtures/private-dir/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/private-dir/dist/plugin.wasm index 0f82eb418..d36273fa1 100644 Binary files a/crates/ide-plugin/tests/fixtures/private-dir/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/private-dir/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/private-dir/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/private-dir/dist/plugin.wasm.sources index b27389fbf..4c2cf629c 100644 --- a/crates/ide-plugin/tests/fixtures/private-dir/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/private-dir/dist/plugin.wasm.sources @@ -1 +1 @@ -e796039ae2f8e534ffc8c2ed2e47e910aaf593c464525287699cf1daf3e195ef +c97989134a8e8a0a12013909f4726288b6d9cdaa44d02a58ff505a6cbb1fb9a1 diff --git a/crates/ide-plugin/tests/fixtures/semantic-tokens/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/semantic-tokens/dist/plugin.wasm index 33115a161..844c35054 100644 Binary files a/crates/ide-plugin/tests/fixtures/semantic-tokens/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/semantic-tokens/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/semantic-tokens/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/semantic-tokens/dist/plugin.wasm.sources index 0c7841c68..e6c9364cb 100644 --- a/crates/ide-plugin/tests/fixtures/semantic-tokens/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/semantic-tokens/dist/plugin.wasm.sources @@ -1 +1 @@ -5e084fa409bb0641409110d91f203a6f99b451b95cd8067bd1ee68140bf8a892 +67660f5ebbfdd3967fabcc0b5c41f215d4a38d9092b91eb0166f12bccfdffffa diff --git a/crates/ide-plugin/tests/fixtures/settings-pages/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/settings-pages/dist/plugin.wasm index 7c2e20384..818babf16 100644 Binary files a/crates/ide-plugin/tests/fixtures/settings-pages/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/settings-pages/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/settings-pages/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/settings-pages/dist/plugin.wasm.sources index 63d155f81..c95a7c09e 100644 --- a/crates/ide-plugin/tests/fixtures/settings-pages/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/settings-pages/dist/plugin.wasm.sources @@ -1 +1 @@ -521557c1a062a9b82afa14547bc54b57255cdbed3b2ab4b1b86424dfb8accbdc +09c810ce7ebd820ea21525dc9faea99b13e7665b308cb131510e02b86e763c9f diff --git a/crates/ide-plugin/tests/fixtures/storage/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/storage/dist/plugin.wasm index 0cad5add5..1c61d3fb6 100644 Binary files a/crates/ide-plugin/tests/fixtures/storage/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/storage/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/storage/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/storage/dist/plugin.wasm.sources index 3d9eb8f55..1bcc2c4f3 100644 --- a/crates/ide-plugin/tests/fixtures/storage/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/storage/dist/plugin.wasm.sources @@ -1 +1 @@ -5280990269e944aef81ec91b356fadf4aa2fc7e7bb10e4b6842e1dc1c669704a +9b3a210440560e05326947c7d549ce691315839daea2931c6a78720f7b0d9e7c diff --git a/crates/ide-plugin/tests/fixtures/symbols/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/symbols/dist/plugin.wasm index a9521c8ec..76d5baa07 100644 Binary files a/crates/ide-plugin/tests/fixtures/symbols/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/symbols/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/symbols/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/symbols/dist/plugin.wasm.sources index 59136e9d2..9151aa080 100644 --- a/crates/ide-plugin/tests/fixtures/symbols/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/symbols/dist/plugin.wasm.sources @@ -1 +1 @@ -ff1391d3c9338b6270fef8af7d7c4a5bc595c391997c220a3a827b4b8a068de8 +57d8b00b5853ca789fe704f21e4394d1524dd73dfd27ea4a0b34f4b0e4f1a5f8 diff --git a/crates/ide-plugin/tests/fixtures/tables/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/tables/dist/plugin.wasm index b277b1049..6b6b09496 100644 Binary files a/crates/ide-plugin/tests/fixtures/tables/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/tables/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/tables/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/tables/dist/plugin.wasm.sources index 9fc9666d6..fa2cbc4e6 100644 --- a/crates/ide-plugin/tests/fixtures/tables/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/tables/dist/plugin.wasm.sources @@ -1 +1 @@ -b78ac0b6fa14401a6379f3e6d07d1398a63d70e882930b3747ec843c0fdb5de7 +53e355922819b8034c93070323c7eb2b1505d3bf561dd5de6abb930fe514748c diff --git a/crates/ide-plugin/tests/fixtures/web-content/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/web-content/dist/plugin.wasm index 6b0f9c261..43ff53fab 100644 Binary files a/crates/ide-plugin/tests/fixtures/web-content/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/web-content/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/web-content/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/web-content/dist/plugin.wasm.sources index 223f5b773..8a4adfeca 100644 --- a/crates/ide-plugin/tests/fixtures/web-content/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/web-content/dist/plugin.wasm.sources @@ -1 +1 @@ -55331477c1d5322b0dc35d13fc7f8223bf697f5f21328d340f75519dd1098727 +7af929a1adf5d89929d66e78068253ccde9ad5cedee329c2d4db6b7a5e29790e diff --git a/crates/ide-plugin/tests/fixtures/web-log/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/web-log/dist/plugin.wasm index 0b65ce5e3..1eecae82a 100644 Binary files a/crates/ide-plugin/tests/fixtures/web-log/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/web-log/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/web-log/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/web-log/dist/plugin.wasm.sources index a70411522..3ba4b4273 100644 --- a/crates/ide-plugin/tests/fixtures/web-log/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/web-log/dist/plugin.wasm.sources @@ -1 +1 @@ -0ae6b7edd0d7cd5025ead4a4afacf0fbe3ebc9548d847f94be0e63585a19747e +511fab979382c5d64054ba4f5fdfc98d50e252dfd5896265ee589336bf8e7d70 diff --git a/crates/ide-plugin/tests/fixtures/web-pty/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/web-pty/dist/plugin.wasm index ac6d97164..401c6845f 100644 Binary files a/crates/ide-plugin/tests/fixtures/web-pty/dist/plugin.wasm and b/crates/ide-plugin/tests/fixtures/web-pty/dist/plugin.wasm differ diff --git a/crates/ide-plugin/tests/fixtures/web-pty/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/web-pty/dist/plugin.wasm.sources index 7ab067bb0..cc31a6d50 100644 --- a/crates/ide-plugin/tests/fixtures/web-pty/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/web-pty/dist/plugin.wasm.sources @@ -1 +1 @@ -5d909d6ab2e73fabba2d213a944546ef7e425552852cf7f74e00eb94c9d167ac +444497128ec5e1e01229a93d2340b49ccc5bc27e67defb2c4fe19683042442a3 diff --git a/docs/en/plugins/versioning.mdx b/docs/en/plugins/versioning.mdx index d504db25f..a477b48ae 100644 --- a/docs/en/plugins/versioning.mdx +++ b/docs/en/plugins/versioning.mdx @@ -3,7 +3,7 @@ title: "API versions and compatibility" description: "engines.openidea, the plugin API versions from 1.0 to 1.6, and the rules that keep released plugins loading in newer IDEs." --- -The plugin API has one version number: the version of the WIT package in [`wit/`](https://github.com/l0ng-ai/openidea/tree/main/wit) (`openidea:plugin@1.7.0` on `main`). The host's engine version and the SDK's `API_VERSION` always agree with it, and the SDK carries an identical copy of the WIT files. +The plugin API has one version number: the version of the WIT package in [`wit/`](https://github.com/l0ng-ai/openidea/tree/main/wit) (`openidea:plugin@1.8.0` on `main`). The host's engine version and the SDK's `API_VERSION` always agree with it, and the SDK carries an identical copy of the WIT files. The plugin system is a preview. The compatibility rules below hold for released 1.x versions; what is still unreleased @@ -21,7 +21,8 @@ The plugin API has one version number: the version of the WIT package in [`wit/` | 1.4.0 | OpenIDEA 0.1.40 | `net.http:`, `ipc:`, `ide.commands` and `database.sources` capabilities; editor and project snapshots, decorations, text search, Problems, Git log/blame/diff; output channels; OAuth loopback; runs, terminal sessions, run configurations, test result parsing and run targets; formatting and other language features; embedded languages; snippets; more menu locations; database sources; MCP tools | | 1.5.0 | OpenIDEA 0.1.41 | `Project::notify` (a notification in one project's window), the `net:ask` capability, run target validation, per-command run target working-directory arguments | | 1.6.0 | OpenIDEA 0.1.43 | Bounded, project-bound background WebSockets for event-stream plugins | -| 1.7.0 | In development on `main` | `views.open-diff`: two texts in the diff viewer, such as a review's file before and after | +| 1.7.0 | OpenIDEA 0.1.44 | `views.open-diff`: two texts in the diff viewer, such as a review's file before and after | +| 1.8.0 | In development on `main` | The `problems-changed` workbench event: the host keeps every producer's diagnostics per file, so an annotation follows the file rather than the invoking window | The full list of what each version added is in [`plugins/README.md`](https://github.com/l0ng-ai/openidea/blob/main/plugins/README.md). diff --git a/docs/zh/plugins/versioning.mdx b/docs/zh/plugins/versioning.mdx index d61e63754..1be2ca697 100644 --- a/docs/zh/plugins/versioning.mdx +++ b/docs/zh/plugins/versioning.mdx @@ -3,7 +3,7 @@ title: "API 版本与兼容" description: "engines.openidea、插件 API 从 1.0 到 1.6 的各个版本,以及让已发布插件在新 IDE 中继续加载的规则。" --- -插件 API 只有一个版本号:[`wit/`](https://github.com/l0ng-ai/openidea/tree/main/wit) 中 WIT 包的版本(`main` 上是 `openidea:plugin@1.7.0`)。宿主的引擎版本和 SDK 的 `API_VERSION` 始终与它一致,SDK 里也带着一份完全相同的 WIT 文件。 +插件 API 只有一个版本号:[`wit/`](https://github.com/l0ng-ai/openidea/tree/main/wit) 中 WIT 包的版本(`main` 上是 `openidea:plugin@1.8.0`)。宿主的引擎版本和 SDK 的 `API_VERSION` 始终与它一致,SDK 里也带着一份完全相同的 WIT 文件。 插件系统目前是预览版。下面的兼容规则适用于已发布的 1.x 版本;`main` 上尚未发布的内容在发布前仍可能变化。 @@ -18,7 +18,8 @@ description: "engines.openidea、插件 API 从 1.0 到 1.6 的各个版本, | 1.4.0 | OpenIDEA 0.1.40 | `net.http:`、`ipc:`、`ide.commands` 和 `database.sources` 能力;编辑器与项目快照、装饰、文本搜索、问题视图、Git log/blame/diff;输出通道;OAuth 回环;运行、终端会话、运行配置、测试结果解析和运行目标;格式化等语言功能;嵌入语言;代码片段;更多菜单位置;数据库数据源;MCP 工具 | | 1.5.0 | OpenIDEA 0.1.41 | `Project::notify`(只在某个项目的窗口中显示通知)、`net:ask` 能力、运行目标启动前校验、运行目标按命令设置工作目录参数 | | 1.6.0 | OpenIDEA 0.1.43 | 面向事件流插件、受容量限制且绑定项目的后台 WebSocket | -| 1.7.0 | `main` 上开发中 | `views.open-diff`:在差异查看器中对比两段文本,例如评审中某个文件的修改前后 | +| 1.7.0 | OpenIDEA 0.1.44 | `views.open-diff`:在差异查看器中对比两段文本,例如评审中某个文件的修改前后 | +| 1.8.0 | `main` 上开发中 | `problems-changed` 工作台事件:宿主按文件保存每个生产者的诊断,注解跟着文件走,而不是跟着调用它的窗口 | 每个版本新增内容的完整列表见 [`plugins/README.md`](https://github.com/l0ng-ai/openidea/blob/main/plugins/README.md)。 diff --git a/plugins/README.md b/plugins/README.md index ede506b80..c4e93054d 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -36,7 +36,7 @@ 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, commit, inlay-hints ``` -The plugin API is defined in [`wit/`](../wit/) (`openidea:plugin@1.7.0`, world +The plugin API is defined in [`wit/`](../wit/) (`openidea:plugin@1.8.0`, world `plugin`). ## 1. Quick start @@ -418,9 +418,22 @@ are always available. Calling a gated function without its capability returns ## 3. Writing code (Rust SDK) +### API 1.8 + +API 1.8.0 is in development on `main`. Use `engines.openidea = "^1.8"` and the +matching SDK. Released 1.7 plugins keep working. New in 1.8: + +- `problems-changed` (activation `on-event:problems-changed`, event + `Event::ProblemsChanged`): the host records every `Diagnostics` event it + emits — language servers, builds, spelling and plugin providers — per file + and producer, and `diagnostics.problems` reads that snapshot, so a plugin's + annotations follow the file instead of the window that published them. + `set_decorations` accepts files outside the open project for the same reason, + and the `typo` / `weak` styles are no longer dropped. + ### API 1.7 -API 1.7.0 is in development on `main`. Use `engines.openidea = "^1.7"` and the +API 1.7.0 shipped with OpenIDEA 0.1.44. Use `engines.openidea = "^1.7"` and the matching SDK. Released 1.6 plugins keep working. New in 1.7: - `host::open_diff(&ContentDiff { .. }, file_name)` (`views.open-diff`) opens diff --git a/plugins/official/ai-completion/dist/plugin.wasm b/plugins/official/ai-completion/dist/plugin.wasm index 13b60cf30..e4d8002c2 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 d859fb962..84f010092 100644 --- a/plugins/official/ai-completion/dist/plugin.wasm.sources +++ b/plugins/official/ai-completion/dist/plugin.wasm.sources @@ -1 +1 @@ -b064bd91f44d088c778a0b7e207f9682a56156724588ff500c0c864d22085205 +446156fc345df7c8252592f73129f3996139e5980c6ed9a296a9cbecc334eb17 diff --git a/plugins/official/blame-annotations/dist/plugin.wasm b/plugins/official/blame-annotations/dist/plugin.wasm index f4dab72e4..5df9ada9c 100644 Binary files a/plugins/official/blame-annotations/dist/plugin.wasm and b/plugins/official/blame-annotations/dist/plugin.wasm differ diff --git a/plugins/official/blame-annotations/dist/plugin.wasm.sources b/plugins/official/blame-annotations/dist/plugin.wasm.sources index 53f25c981..6ad0c94e8 100644 --- a/plugins/official/blame-annotations/dist/plugin.wasm.sources +++ b/plugins/official/blame-annotations/dist/plugin.wasm.sources @@ -1 +1 @@ -4145b677141c592dbd9130000eae707615ec36069b78ea3e25fe598dc642bf14 +78465a96e1e34ef3d4cf658c3b80f8388e43604dfe5abbf105f1149d040c5994 diff --git a/plugins/official/checkstyle/dist/plugin.wasm b/plugins/official/checkstyle/dist/plugin.wasm index 81fc6fed0..cbf6d94cb 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 fc90681cc..d6fddb00e 100644 --- a/plugins/official/checkstyle/dist/plugin.wasm.sources +++ b/plugins/official/checkstyle/dist/plugin.wasm.sources @@ -1 +1 @@ -52f7ccad8966c991505e7a377c59dbf24d0660df66eca97e7a2a8c5d58e3ce68 +b072d07c4aa4a972a71fffba355ea215da7860b8f5527c6074055f52c52a29be diff --git a/plugins/official/container-runner/dist/plugin.wasm b/plugins/official/container-runner/dist/plugin.wasm index e29ff97e7..a31e94ae5 100644 Binary files a/plugins/official/container-runner/dist/plugin.wasm and b/plugins/official/container-runner/dist/plugin.wasm differ diff --git a/plugins/official/container-runner/dist/plugin.wasm.sources b/plugins/official/container-runner/dist/plugin.wasm.sources index e82625b5b..3f3963165 100644 --- a/plugins/official/container-runner/dist/plugin.wasm.sources +++ b/plugins/official/container-runner/dist/plugin.wasm.sources @@ -1 +1 @@ -a438e04a1e179469eec1ee16522b031fea9bbb669d34c77973ee6fd85885a088 +173a8b66a3d5969bc51b2c97fcb435e2313555608aecbe1752d4586e3508d74f diff --git a/plugins/official/csv-editor/dist/plugin.wasm b/plugins/official/csv-editor/dist/plugin.wasm index 3dd8023c4..9b64b7507 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 e10dbdbad..fdc4912a6 100644 --- a/plugins/official/csv-editor/dist/plugin.wasm.sources +++ b/plugins/official/csv-editor/dist/plugin.wasm.sources @@ -1 +1 @@ -7ff4e304120c9c7bd316b95ce7526fda74adf00867642fcc66311effa1567d76 +a78162e7c4c6ebd3a22c06737fd35948e11b0dd8949571617bc099ba6b4423f0 diff --git a/plugins/official/git-toolbox/dist/plugin.wasm b/plugins/official/git-toolbox/dist/plugin.wasm index 2276ccf62..7a70c8b42 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 1d1296f9e..0a4170f02 100644 --- a/plugins/official/git-toolbox/dist/plugin.wasm.sources +++ b/plugins/official/git-toolbox/dist/plugin.wasm.sources @@ -1 +1 @@ -d6991b9065d8b623efdf9c03fb92b4875e95e666b3dadb3925ff5ad327f5fdcb +6ddc2f9166452e4473cdaa9974943cb85ad4575457d2b3ca3d35abea65cb8b30 diff --git a/plugins/official/graphviz/dist/plugin.wasm b/plugins/official/graphviz/dist/plugin.wasm index 2aed05622..1f59b8098 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 99792d3b2..d7f5d1ac7 100644 --- a/plugins/official/graphviz/dist/plugin.wasm.sources +++ b/plugins/official/graphviz/dist/plugin.wasm.sources @@ -1 +1 @@ -78b71cba21352bfc91cd631523e4b69a6a2df2d5f6bfbf8164e63698a4625594 +e97394224db64bcefb5ac43eba55eaba38f9d33122241313fae311ef8f0957db diff --git a/plugins/official/key-promoter/dist/plugin.wasm b/plugins/official/key-promoter/dist/plugin.wasm index 977157e40..b8fd2cd64 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 0d778b789..c8f9c2d0e 100644 --- a/plugins/official/key-promoter/dist/plugin.wasm.sources +++ b/plugins/official/key-promoter/dist/plugin.wasm.sources @@ -1 +1 @@ -6d5ea800a0bcbf26e4770a985b1bc8ef09ca71e7e5c94d59f510dbd108c79ece +ca7266484d34f16de46b97e6317135984fdc4141099e50bdd52294617c2af9c0 diff --git a/plugins/official/license-header/dist/plugin.wasm b/plugins/official/license-header/dist/plugin.wasm index 6cc28ec30..01038fa70 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 dc18155bc..34af720c2 100644 --- a/plugins/official/license-header/dist/plugin.wasm.sources +++ b/plugins/official/license-header/dist/plugin.wasm.sources @@ -1 +1 @@ -21061c0875e8213efe809e95b0a4d4b0dd73962398fd793d176a443163e73e0e +6d84abcc58c1894203506fa89855a6eade61f14add0ed72fdd7af9e3cd1298f6 diff --git a/plugins/official/mybatis/dist/plugin.wasm b/plugins/official/mybatis/dist/plugin.wasm index 96eff2829..e87d8b2e7 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 bd0af77ad..3e02aa38c 100644 --- a/plugins/official/mybatis/dist/plugin.wasm.sources +++ b/plugins/official/mybatis/dist/plugin.wasm.sources @@ -1 +1 @@ -6a95d38a5f41dbed72dca23584f715db9a12192d19222460cee8669b4a352770 +a303522c605ccc9804e67f85326778a380951ce14022a70bc37ba3ae63b76f79 diff --git a/plugins/official/regex-tester/dist/plugin.wasm b/plugins/official/regex-tester/dist/plugin.wasm index 1731f3e9a..0a5bc9555 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 642f5f639..fd2ea7121 100644 --- a/plugins/official/regex-tester/dist/plugin.wasm.sources +++ b/plugins/official/regex-tester/dist/plugin.wasm.sources @@ -1 +1 @@ -bfab2d2fa19bdaac6890a117aa055c4caf1393065f87b6c104ff46114d9189c8 +b7b4e6be26ff08cc0e7fa791cb956acd9afa09905720d2c0c93acdae5638a35e diff --git a/plugins/official/solidity/dist/plugin.wasm b/plugins/official/solidity/dist/plugin.wasm index 49736b68b..11170d760 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 496262be3..b18867464 100644 --- a/plugins/official/solidity/dist/plugin.wasm.sources +++ b/plugins/official/solidity/dist/plugin.wasm.sources @@ -1 +1 @@ -1e860aa8277c6cc5ffd887e1bc5c3107feb8c7f856d2c4df7294497879308ce7 +4f585c3a1a3ae01a89811e05d27fc39121dd8e74210c1281d04ff31556d393b5 diff --git a/plugins/official/string-tools/dist/plugin.wasm b/plugins/official/string-tools/dist/plugin.wasm index dcc5ab9d5..921774ac1 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 b38fa86c8..2abe7ac08 100644 --- a/plugins/official/string-tools/dist/plugin.wasm.sources +++ b/plugins/official/string-tools/dist/plugin.wasm.sources @@ -1 +1 @@ -cbe64480e49895004af403c165735d58cfe657910b45cea2d86d7eadcd93b93b +81637f9a265e6bcb9839b1019b0d58b43f80535fb1fca044716904aef0c12724 diff --git a/plugins/official/test-runner/dist/plugin.wasm b/plugins/official/test-runner/dist/plugin.wasm index 36477796a..d617111d9 100644 Binary files a/plugins/official/test-runner/dist/plugin.wasm and b/plugins/official/test-runner/dist/plugin.wasm differ diff --git a/plugins/official/test-runner/dist/plugin.wasm.sources b/plugins/official/test-runner/dist/plugin.wasm.sources index 39f48b106..51fec0c03 100644 --- a/plugins/official/test-runner/dist/plugin.wasm.sources +++ b/plugins/official/test-runner/dist/plugin.wasm.sources @@ -1 +1 @@ -8186e9cfb58fedf1add5288eb30010b8a376dd3dbd2aee39da5ddf067f5b0aae +3f38eb70364973b8107edd58f2704872f8d7bafd0b00650ced1a1cf590ce741b diff --git a/plugins/official/todo-finder/dist/plugin.wasm b/plugins/official/todo-finder/dist/plugin.wasm index 5bcdae00e..8fcbe9aad 100644 Binary files a/plugins/official/todo-finder/dist/plugin.wasm and b/plugins/official/todo-finder/dist/plugin.wasm differ diff --git a/plugins/official/todo-finder/dist/plugin.wasm.sources b/plugins/official/todo-finder/dist/plugin.wasm.sources index c4bbac29d..bb132e77a 100644 --- a/plugins/official/todo-finder/dist/plugin.wasm.sources +++ b/plugins/official/todo-finder/dist/plugin.wasm.sources @@ -1 +1 @@ -75d6e447d1d54ad254845b423c38d3c395f07f2e31ee2dd376edc1ed7079ae06 +d241068b9152dcd72bc4e0895a29cbe919731be66319ee228188cda62e05df61 diff --git a/sdk/rust/openidea-plugin/src/lib.rs b/sdk/rust/openidea-plugin/src/lib.rs index a7a5eae56..9058f3387 100644 --- a/sdk/rust/openidea-plugin/src/lib.rs +++ b/sdk/rust/openidea-plugin/src/lib.rs @@ -98,7 +98,7 @@ pub mod providers_bindings { export_macro_name: "__export_editor_providers_world", default_bindings_module: "openidea_plugin::providers_bindings", with: { - "openidea:plugin/types@1.7.0": crate::bindings::openidea::plugin::types, + "openidea:plugin/types@1.8.0": crate::bindings::openidea::plugin::types, }, }); } @@ -115,10 +115,10 @@ pub mod views_bindings { export_macro_name: "__export_views_world", default_bindings_module: "openidea_plugin::views_bindings", with: { - "openidea:plugin/types@1.7.0": crate::bindings::openidea::plugin::types, - "openidea:plugin/workspace@1.7.0": crate::bindings::openidea::plugin::workspace, - "openidea:plugin/editor@1.7.0": crate::bindings::openidea::plugin::editor, - "openidea:plugin/invocation@1.7.0": crate::bindings::openidea::plugin::invocation, + "openidea:plugin/types@1.8.0": crate::bindings::openidea::plugin::types, + "openidea:plugin/workspace@1.8.0": crate::bindings::openidea::plugin::workspace, + "openidea:plugin/editor@1.8.0": crate::bindings::openidea::plugin::editor, + "openidea:plugin/invocation@1.8.0": crate::bindings::openidea::plugin::invocation, }, }); } @@ -130,7 +130,7 @@ pub mod indexing_bindings { world: "indexing", path: "wit", pub_export_macro: true, export_macro_name: "__export_indexing_world", default_bindings_module: "openidea_plugin::indexing_bindings", - with: { "openidea:plugin/types@1.7.0": crate::bindings::openidea::plugin::types }, + with: { "openidea:plugin/types@1.8.0": crate::bindings::openidea::plugin::types }, }); } @@ -142,10 +142,10 @@ pub mod details_bindings { export_macro_name: "__export_details_world", default_bindings_module: "openidea_plugin::details_bindings", with: { - "openidea:plugin/types@1.7.0": crate::bindings::openidea::plugin::types, - "openidea:plugin/workspace@1.7.0": crate::bindings::openidea::plugin::workspace, - "openidea:plugin/editor@1.7.0": crate::bindings::openidea::plugin::editor, - "openidea:plugin/invocation@1.7.0": crate::bindings::openidea::plugin::invocation, + "openidea:plugin/types@1.8.0": crate::bindings::openidea::plugin::types, + "openidea:plugin/workspace@1.8.0": crate::bindings::openidea::plugin::workspace, + "openidea:plugin/editor@1.8.0": crate::bindings::openidea::plugin::editor, + "openidea:plugin/invocation@1.8.0": crate::bindings::openidea::plugin::invocation, }, }); } @@ -157,7 +157,7 @@ pub mod semantic_bindings { world: "semantic-tokens", path: "wit", pub_export_macro: true, export_macro_name: "__export_semantic_tokens_world", default_bindings_module: "openidea_plugin::semantic_bindings", - with: { "openidea:plugin/types@1.7.0": crate::bindings::openidea::plugin::types }, + with: { "openidea:plugin/types@1.8.0": crate::bindings::openidea::plugin::types }, }); } @@ -169,8 +169,8 @@ pub mod diagnostics_bindings { export_macro_name: "__export_diagnostics_world", default_bindings_module: "openidea_plugin::diagnostics_bindings", with: { - "openidea:plugin/types@1.7.0": crate::bindings::openidea::plugin::types, - "openidea:plugin/diagnostics@1.7.0": crate::bindings::openidea::plugin::diagnostics, + "openidea:plugin/types@1.8.0": crate::bindings::openidea::plugin::types, + "openidea:plugin/diagnostics@1.8.0": crate::bindings::openidea::plugin::diagnostics, }, }); } @@ -182,7 +182,7 @@ pub mod hover_bindings { world: "hover", path: "wit", pub_export_macro: true, export_macro_name: "__export_hover_world", default_bindings_module: "openidea_plugin::hover_bindings", - with: { "openidea:plugin/types@1.7.0": crate::bindings::openidea::plugin::types }, + with: { "openidea:plugin/types@1.8.0": crate::bindings::openidea::plugin::types }, }); } @@ -194,11 +194,11 @@ pub mod commit_checks_bindings { export_macro_name: "__export_commit_checks_world", default_bindings_module: "openidea_plugin::commit_checks_bindings", with: { - "openidea:plugin/types@1.7.0": crate::bindings::openidea::plugin::types, - "openidea:plugin/workspace@1.7.0": crate::bindings::openidea::plugin::workspace, - "openidea:plugin/editor@1.7.0": crate::bindings::openidea::plugin::editor, - "openidea:plugin/invocation@1.7.0": crate::bindings::openidea::plugin::invocation, - "openidea:plugin/commit@1.7.0": crate::bindings::openidea::plugin::commit, + "openidea:plugin/types@1.8.0": crate::bindings::openidea::plugin::types, + "openidea:plugin/workspace@1.8.0": crate::bindings::openidea::plugin::workspace, + "openidea:plugin/editor@1.8.0": crate::bindings::openidea::plugin::editor, + "openidea:plugin/invocation@1.8.0": crate::bindings::openidea::plugin::invocation, + "openidea:plugin/commit@1.8.0": crate::bindings::openidea::plugin::commit, }, }); } @@ -210,7 +210,7 @@ pub mod inlay_bindings { world: "inlay-hints", path: "wit", pub_export_macro: true, export_macro_name: "__export_inlay_hints_world", default_bindings_module: "openidea_plugin::inlay_bindings", - with: { "openidea:plugin/types@1.7.0": crate::bindings::openidea::plugin::types }, + with: { "openidea:plugin/types@1.8.0": crate::bindings::openidea::plugin::types }, }); } @@ -221,7 +221,7 @@ pub mod file_actions_bindings { world: "file-actions-provider", path: "wit", pub_export_macro: true, export_macro_name: "__export_file_actions_world", default_bindings_module: "openidea_plugin::file_actions_bindings", - with: { "openidea:plugin/types@1.7.0": crate::bindings::openidea::plugin::types }, + with: { "openidea:plugin/types@1.8.0": crate::bindings::openidea::plugin::types }, }); } @@ -233,10 +233,10 @@ pub mod endpoints_bindings { export_macro_name: "__export_endpoints_world", default_bindings_module: "openidea_plugin::endpoints_bindings", with: { - "openidea:plugin/types@1.7.0": crate::bindings::openidea::plugin::types, - "openidea:plugin/workspace@1.7.0": crate::bindings::openidea::plugin::workspace, - "openidea:plugin/editor@1.7.0": crate::bindings::openidea::plugin::editor, - "openidea:plugin/invocation@1.7.0": crate::bindings::openidea::plugin::invocation, + "openidea:plugin/types@1.8.0": crate::bindings::openidea::plugin::types, + "openidea:plugin/workspace@1.8.0": crate::bindings::openidea::plugin::workspace, + "openidea:plugin/editor@1.8.0": crate::bindings::openidea::plugin::editor, + "openidea:plugin/invocation@1.8.0": crate::bindings::openidea::plugin::invocation, }, }); } @@ -1322,7 +1322,7 @@ pub mod run_configurations_bindings { pub mod test_results_bindings { wit_bindgen::generate!({ world: "test-results-provider", path: "wit", pub_export_macro: true, export_macro_name: "__export_tests_world", default_bindings_module: "openidea_plugin::test_results_bindings", - with: { "openidea:plugin/types@1.7.0": crate::bindings::openidea::plugin::types }, + with: { "openidea:plugin/types@1.8.0": crate::bindings::openidea::plugin::types }, }); } #[doc(hidden)] @@ -1360,7 +1360,7 @@ pub mod language_features; pub mod language_features_bindings { wit_bindgen::generate!({world: "editor-language-features", path: "wit", pub_export_macro: true, export_macro_name: "__export_language_features_world", default_bindings_module: "openidea_plugin::language_features_bindings", - with: { "openidea:plugin/types@1.7.0": crate::bindings::openidea::plugin::types }, + with: { "openidea:plugin/types@1.8.0": crate::bindings::openidea::plugin::types }, }); } @@ -1392,7 +1392,7 @@ fn gutter_actions(doc: $crate::language_features_bindings::exports::openidea::pl pub mod injections_bindings { wit_bindgen::generate!({world: "language-injections", path: "wit", pub_export_macro: true, export_macro_name: "__export_injections_world", default_bindings_module: "openidea_plugin::injections_bindings", - with: { "openidea:plugin/types@1.7.0": crate::bindings::openidea::plugin::types }, + with: { "openidea:plugin/types@1.8.0": crate::bindings::openidea::plugin::types }, }); } @@ -1422,8 +1422,8 @@ pub mod mcp_bindings { wit_bindgen::generate!({ world: "mcp-tools", path: "wit", pub_export_macro: true, export_macro_name: "__export_mcp_world", default_bindings_module: "openidea_plugin::mcp_bindings", with: { - "openidea:plugin/types@1.7.0": crate::bindings::openidea::plugin::types, - "openidea:plugin/invocation@1.7.0": crate::bindings::openidea::plugin::invocation, + "openidea:plugin/types@1.8.0": crate::bindings::openidea::plugin::types, + "openidea:plugin/invocation@1.8.0": crate::bindings::openidea::plugin::invocation, }, }); } diff --git a/sdk/rust/openidea-plugin/src/types.rs b/sdk/rust/openidea-plugin/src/types.rs index fc4be7d81..802e34258 100644 --- a/sdk/rust/openidea-plugin/src/types.rs +++ b/sdk/rust/openidea-plugin/src/types.rs @@ -245,6 +245,8 @@ pub enum EventKind { /// Needs `events.input`. ActionPerformed, VcsChanged, + /// Problems of the window's files changed (a language server or a report): JSON object (`Event::Other`). + ProblemsChanged, Scheduled, } @@ -267,6 +269,7 @@ impl EventKind { Self::EditorFocused => "editor-focused", Self::ActionPerformed => "action-performed", Self::VcsChanged => "vcs-changed", + Self::ProblemsChanged => "problems-changed", Self::Scheduled => "scheduled", } } diff --git a/sdk/rust/openidea-plugin/wit/commit.wit b/sdk/rust/openidea-plugin/wit/commit.wit index b3e5b9fb5..46aee192e 100644 --- a/sdk/rust/openidea-plugin/wit/commit.wit +++ b/sdk/rust/openidea-plugin/wit/commit.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// The commit being prepared in the window the current command or event came /// from (the Commit tool window, or the Commit dialog while it is open): the diff --git a/sdk/rust/openidea-plugin/wit/database.wit b/sdk/rust/openidea-plugin/wit/database.wit index 59e8221bc..14484c068 100644 --- a/sdk/rust/openidea-plugin/wit/database.wit +++ b/sdk/rust/openidea-plugin/wit/database.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Plugin-owned project data sources. Requires database.sources and project trust. /// Runs on the Database subsystem's host, including on remote project servers. diff --git a/sdk/rust/openidea-plugin/wit/decorations.wit b/sdk/rust/openidea-plugin/wit/decorations.wit index fb9daf387..7ed27bd67 100644 --- a/sdk/rust/openidea-plugin/wit/decorations.wit +++ b/sdk/rust/openidea-plugin/wit/decorations.wit @@ -1,9 +1,10 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; -/// Project-scoped decorations (API 1.4). Requires project.read; paths must be inside -/// an open project. Sets belong to the plugin and disappear when it stops. +/// Editor and project-tree decorations (API 1.4). Requires project.read. An editor set +/// follows its file: the file's own project, or the nearest one when it is outside them all. +/// Tree sets are rooted in an open project. Sets belong to the plugin and disappear when it stops. /// Styles: highlight, search, added, modified, removed, warning, error, info, -/// underline, strikethrough, muted. Unknown styles are ignored. Theme tokens only. +/// typo, weak, underline, strikethrough, muted. Unknown styles are ignored. Theme tokens only. interface decorations { use types.{error, range}; /// An editor annotation; after is plain text shown after the range. diff --git a/sdk/rust/openidea-plugin/wit/details.wit b/sdk/rust/openidea-plugin/wit/details.wit index 2096fbaef..7bb6da47b 100644 --- a/sdk/rust/openidea-plugin/wit/details.wit +++ b/sdk/rust/openidea-plugin/wit/details.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// What the pane beside the Services tree shows for a plugin's node. interface detail-types { diff --git a/sdk/rust/openidea-plugin/wit/diagnostics.wit b/sdk/rust/openidea-plugin/wit/diagnostics.wit index db8a52315..c795062a6 100644 --- a/sdk/rust/openidea-plugin/wit/diagnostics.wit +++ b/sdk/rust/openidea-plugin/wit/diagnostics.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Optional export (world `plugin-diagnostics`): problems in the current, /// possibly unsaved document, for the languages declared with the diff --git a/sdk/rust/openidea-plugin/wit/endpoints.wit b/sdk/rust/openidea-plugin/wit/endpoints.wit index e738e8340..492f3d420 100644 --- a/sdk/rust/openidea-plugin/wit/endpoints.wit +++ b/sdk/rust/openidea-plugin/wit/endpoints.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// HTTP endpoints found in a project (`[[contributes.endpoints]]`). interface endpoints-provider { diff --git a/sdk/rust/openidea-plugin/wit/events.wit b/sdk/rust/openidea-plugin/wit/events.wit index 12f2da2cf..6e3a5148d 100644 --- a/sdk/rust/openidea-plugin/wit/events.wit +++ b/sdk/rust/openidea-plugin/wit/events.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Workbench events, delivered in batches to `event-handler.handle`. interface events { diff --git a/sdk/rust/openidea-plugin/wit/exports.wit b/sdk/rust/openidea-plugin/wit/exports.wit index 26cc428b1..f062d30fc 100644 --- a/sdk/rust/openidea-plugin/wit/exports.wit +++ b/sdk/rust/openidea-plugin/wit/exports.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; interface lifecycle { use types.{context, error}; diff --git a/sdk/rust/openidea-plugin/wit/extensions.wit b/sdk/rust/openidea-plugin/wit/extensions.wit index 6675ea48b..04826a3fc 100644 --- a/sdk/rust/openidea-plugin/wit/extensions.wit +++ b/sdk/rust/openidea-plugin/wit/extensions.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// What other plugins contributed to the extension points this plugin declares /// (`[[extension-points]]` in `plugin.toml`; the contributors list this plugin diff --git a/sdk/rust/openidea-plugin/wit/file-actions.wit b/sdk/rust/openidea-plugin/wit/file-actions.wit index 0d955df27..3c1c89635 100644 --- a/sdk/rust/openidea-plugin/wit/file-actions.wit +++ b/sdk/rust/openidea-plugin/wit/file-actions.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Optional export (world `plugin-file-actions`): quick fixes and /// refactorings that create, rename or delete files, next to the text edits diff --git a/sdk/rust/openidea-plugin/wit/fs.wit b/sdk/rust/openidea-plugin/wit/fs.wit index 839e4e965..1dc9420d0 100644 --- a/sdk/rust/openidea-plugin/wit/fs.wit +++ b/sdk/rust/openidea-plugin/wit/fs.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Files outside the open projects: the configuration of the program a /// plugin integrates with, a tool installed elsewhere, a licence file. diff --git a/sdk/rust/openidea-plugin/wit/host.wit b/sdk/rust/openidea-plugin/wit/host.wit index 1df9bda16..a86d34b48 100644 --- a/sdk/rust/openidea-plugin/wit/host.wit +++ b/sdk/rust/openidea-plugin/wit/host.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Writes to the IDE log, tagged with the plugin id. interface log { diff --git a/sdk/rust/openidea-plugin/wit/hover.wit b/sdk/rust/openidea-plugin/wit/hover.wit index 49612d00f..c066f9a0d 100644 --- a/sdk/rust/openidea-plugin/wit/hover.wit +++ b/sdk/rust/openidea-plugin/wit/hover.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Optional export (world `plugin-hover`): Markdown added to the hover of /// documents of the languages declared with the `hover` provider kind, diff --git a/sdk/rust/openidea-plugin/wit/indexing.wit b/sdk/rust/openidea-plugin/wit/indexing.wit index 4f0cfe224..57b6061c2 100644 --- a/sdk/rust/openidea-plugin/wit/indexing.wit +++ b/sdk/rust/openidea-plugin/wit/indexing.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Cross-file associations a plugin publishes for the files it declares under /// `[[contributes.documentIndexers]]`. The host calls `index` in cancellable diff --git a/sdk/rust/openidea-plugin/wit/injections.wit b/sdk/rust/openidea-plugin/wit/injections.wit index 2a5be91f8..6667afc69 100644 --- a/sdk/rust/openidea-plugin/wit/injections.wit +++ b/sdk/rust/openidea-plugin/wit/injections.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Embedded languages. The host routes plugin providers and configured language /// servers inside these ranges, preserving host-document UTF-16 coordinates. diff --git a/sdk/rust/openidea-plugin/wit/inlay.wit b/sdk/rust/openidea-plugin/wit/inlay.wit index 4bcfa613a..208416f3f 100644 --- a/sdk/rust/openidea-plugin/wit/inlay.wit +++ b/sdk/rust/openidea-plugin/wit/inlay.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Optional export (world `plugin-inlay-hints`): inline hints in documents of /// the languages declared with the `inlay-hints` provider kind, including diff --git a/sdk/rust/openidea-plugin/wit/io.wit b/sdk/rust/openidea-plugin/wit/io.wit index 7d1ed4428..212385b48 100644 --- a/sdk/rust/openidea-plugin/wit/io.wit +++ b/sdk/rust/openidea-plugin/wit/io.wit @@ -1,11 +1,14 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Problems in files, shown in the editor and the Problems view. interface diagnostics { use types.{error, range, location}; - /// Problems reported by a source for a file in this window's project. + /// Problems one producer reported for a file, wherever the file is: the host records every + /// diagnostics event it emits, so this follows the file, not the invoking window. record file-problems { path: string, source: string, diagnostics: list } - /// Current Problems view snapshot, optionally filtered to a project file. + /// The diagnostics the host recorded, one entry per producer, optionally + /// filtered to a file: language servers, builds, spelling and plugin + /// providers alike, whatever window or project opened the file. /// Requires project.read and an invoking window. Empty for remote projects. problems: func(path: option) -> result, error>; diff --git a/sdk/rust/openidea-plugin/wit/language-features.wit b/sdk/rust/openidea-plugin/wit/language-features.wit index c6403b0a5..0a406cba1 100644 --- a/sdk/rust/openidea-plugin/wit/language-features.wit +++ b/sdk/rust/openidea-plugin/wit/language-features.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Additional language features, called only for declared API 1.4 provider kinds. /// Editor calls have a 50 ms soft / 500 ms hard budget; formatting has 5 seconds. diff --git a/sdk/rust/openidea-plugin/wit/mcp.wit b/sdk/rust/openidea-plugin/wit/mcp.wit index 903a15f27..15e6c9139 100644 --- a/sdk/rust/openidea-plugin/wit/mcp.wit +++ b/sdk/rust/openidea-plugin/wit/mcp.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Discovery of the IDE's built-in MCP server; never exposes its URL or token. interface mcp { diff --git a/sdk/rust/openidea-plugin/wit/plugin.wit b/sdk/rust/openidea-plugin/wit/plugin.wit index 2e7da4359..39c55203c 100644 --- a/sdk/rust/openidea-plugin/wit/plugin.wit +++ b/sdk/rust/openidea-plugin/wit/plugin.wit @@ -8,7 +8,7 @@ /// `fs` and `workspace`, and everything else goes through the imports below, /// gated by the capabilities granted in `plugin.toml` (a missing one returns /// `error::denied`). -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; world plugin { import log; diff --git a/sdk/rust/openidea-plugin/wit/providers.wit b/sdk/rust/openidea-plugin/wit/providers.wit index 7bb02b8db..8b098e4a6 100644 --- a/sdk/rust/openidea-plugin/wit/providers.wit +++ b/sdk/rust/openidea-plugin/wit/providers.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Editor features a plugin provides for the languages it declares under /// `[[contributes.providers]]` (the host calls only declared kinds). They run diff --git a/sdk/rust/openidea-plugin/wit/run-targets.wit b/sdk/rust/openidea-plugin/wit/run-targets.wit index e29456015..5e780df04 100644 --- a/sdk/rust/openidea-plugin/wit/run-targets.wit +++ b/sdk/rust/openidea-plugin/wit/run-targets.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Project-scoped targets owned by the registering plugin (API 1.4). interface run-targets { diff --git a/sdk/rust/openidea-plugin/wit/run.wit b/sdk/rust/openidea-plugin/wit/run.wit index e4875733f..abb398bea 100644 --- a/sdk/rust/openidea-plugin/wit/run.wit +++ b/sdk/rust/openidea-plugin/wit/run.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Launches this plugin's configurations in the invoking window's Run tool window. interface run { diff --git a/sdk/rust/openidea-plugin/wit/semantic.wit b/sdk/rust/openidea-plugin/wit/semantic.wit index 021b94dbf..c3c26fad4 100644 --- a/sdk/rust/openidea-plugin/wit/semantic.wit +++ b/sdk/rust/openidea-plugin/wit/semantic.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Optional export (world `plugin-semantic-tokens`): semantic colouring of /// documents of the languages declared with the `semantic-tokens` provider diff --git a/sdk/rust/openidea-plugin/wit/types.wit b/sdk/rust/openidea-plugin/wit/types.wit index aa255a617..d888fdab8 100644 --- a/sdk/rust/openidea-plugin/wit/types.wit +++ b/sdk/rust/openidea-plugin/wit/types.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Shared types. Positions are 0-based lines and UTF-16 code unit columns, /// as in the editor and LSP. diff --git a/sdk/rust/openidea-plugin/wit/views.wit b/sdk/rust/openidea-plugin/wit/views.wit index 4fbcaaff6..268e76ac9 100644 --- a/sdk/rust/openidea-plugin/wit/views.wit +++ b/sdk/rust/openidea-plugin/wit/views.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Declarative views: tool windows declared /// under `[[contributes.views]]` and custom editors under diff --git a/sdk/rust/openidea-plugin/wit/workspace.wit b/sdk/rust/openidea-plugin/wit/workspace.wit index 5030af173..9b798f2d1 100644 --- a/sdk/rust/openidea-plugin/wit/workspace.wit +++ b/sdk/rust/openidea-plugin/wit/workspace.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; interface workspace { use types.{error, location}; diff --git a/src/bindings.ts b/src/bindings.ts index e90519552..9523967ba 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -4813,7 +4813,9 @@ export type ErrorKind = "notFound" | "alreadyExists" | "permissionDenied" | "inv "unavailable" | "io" | "protocol" | "internal"; /** Kinds of workbench events a plugin can subscribe to (`on-event:`). */ -export type EventKind = "document-changed" | "websocket-ready" | "file-closed" | "settings-changed" | "input-mode-changed" | "run-exited" | "terminal-exited" | "content-tab-action" | "extensions-changed" | "file-opened" | "file-saved" | "files-changed" | "editor-focused" | "action-performed" | "vcs-changed" | "scheduled"; +export type EventKind = "document-changed" | "websocket-ready" | "file-closed" | "settings-changed" | "input-mode-changed" | "run-exited" | "terminal-exited" | "content-tab-action" | "extensions-changed" | "file-opened" | "file-saved" | "files-changed" | "editor-focused" | "action-performed" | "vcs-changed" | +/** Problems of the window's files changed (a language server or a report). */ +"problems-changed" | "scheduled"; /** An exception breakpoint filter offered by an adapter (`exceptionBreakpointFilters`). */ export type ExceptionFilter = { @@ -10558,7 +10560,9 @@ export type WindowTab = { /** A workbench event delivered to plugins. */ export type WorkbenchEvent = { kind: "documentChanged"; value: DocumentChanged } | { kind: "websocketReady"; value: WebsocketReady } | { kind: "runExited"; value: RunExited } | { kind: "terminalExited"; value: TerminalExited } | { kind: "contentTabAction"; value: ContentTabAction } | { kind: "fileClosed"; value: string } | { kind: "settingsChanged"; value: string[] } | /** Ids of extension points the receiving plugin declares whose contributions changed. */ -{ kind: "extensionsChanged"; value: string[] } | { kind: "inputModeChanged"; value: InputMode } | { kind: "fileOpened"; value: string } | { kind: "fileSaved"; value: string } | { kind: "filesChanged"; value: FilesChanged } | { kind: "editorFocused"; value: string | null } | { kind: "actionPerformed"; value: ActionPerformed } | { kind: "vcsChanged"; value: string } | { kind: "scheduled" }; +{ kind: "extensionsChanged"; value: string[] } | { kind: "inputModeChanged"; value: InputMode } | { kind: "fileOpened"; value: string } | { kind: "fileSaved"; value: string } | { kind: "filesChanged"; value: FilesChanged } | { kind: "editorFocused"; value: string | null } | { kind: "actionPerformed"; value: ActionPerformed } | { kind: "vcsChanged"; value: string } | +/** Problems of the window's files changed; plugins re-read `problems`. */ +{ kind: "problemsChanged" } | { kind: "scheduled" }; export type WorkingTreeChange = { path: string, diff --git a/src/editor/core/cm/codeLens.ts b/src/editor/core/cm/codeLens.ts index a5a6594f1..bc91d4659 100644 --- a/src/editor/core/cm/codeLens.ts +++ b/src/editor/core/cm/codeLens.ts @@ -232,7 +232,10 @@ const lensTheme = EditorView.theme({ // positioned inner box, the way IDEA paints line-end inlays. Nothing here takes part in the // line's flow: the hint can neither wrap to the next line (a soft-wrapped line has no room for // it) nor stretch the line's height, and it overflows past the editor edge instead. + // The anchor's own offset is a custom property: the blame and plugin messages step past the + // hint by measuring this inner box plus that offset (inlineBlame.ts, decorations.ts). ".cm-openidea-lensInline": { + "--openidea-hint-offset": "12px", display: "inline-block", width: "0", position: "relative", @@ -243,7 +246,7 @@ const lensTheme = EditorView.theme({ }, ".cm-openidea-lensInlineInner": { position: "absolute", - left: "12px", + left: "var(--openidea-hint-offset)", bottom: "-2px", display: "flex", alignItems: "center", diff --git a/src/features/plugins/PluginSettings.module.css b/src/features/plugins/PluginSettings.module.css index 8e8643773..d2e450381 100644 --- a/src/features/plugins/PluginSettings.module.css +++ b/src/features/plugins/PluginSettings.module.css @@ -84,3 +84,37 @@ .scalars { max-width: 560px; } + +/* A severity's sample, drawn the way the annotation appears in the editor: the line-end + message carries the severity colour alone — the wave belongs to the code token, the + message itself is never underlined. The label column is fixed so the samples line up, + one row per setting. */ +.exampleRow { + display: grid; + grid-template-columns: 180px minmax(0, 1fr); + align-items: baseline; + column-gap: var(--space-3); +} +.example { + font-family: var(--openidea-editor-font, var(--font-mono)); + font-size: var(--fs-code); + white-space: pre-wrap; +} +.example[data-style="error"] { + color: var(--wave-error); +} +.example[data-style="warning"] { + color: var(--wave-warning); +} +.example[data-style="weak"] { + color: var(--fg-weak-warning); +} +.example[data-style="typo"] { + color: var(--wave-typo, var(--success)); +} +.example[data-style="info"] { + color: var(--info); +} +.example[data-style="muted"] { + color: var(--fg-muted); +} diff --git a/src/features/plugins/__tests__/decorations.test.ts b/src/features/plugins/__tests__/decorations.test.ts index 46bf4602d..f5c524f43 100644 --- a/src/features/plugins/__tests__/decorations.test.ts +++ b/src/features/plugins/__tests__/decorations.test.ts @@ -44,7 +44,11 @@ afterEach(() => { it("renders plain annotations, maps them through edits, and withdraws them", () => { setPluginDecorations("test", [set()]); expect(editor.view.dom.textContent).toContain(""); + // The range mark carries the hover on the code; the line-end message carries it too. expect(editor.view.dom.querySelector('[title="hover"]')?.textContent).toBe("te"); + const message = editor.view.dom.querySelector(".cm-plugin-after"); + expect(message?.firstElementChild?.textContent).toBe(""); + expect(message?.firstElementChild?.getAttribute("title")).toBe("hover"); expect(editor.view.dom.querySelector("plain")).toBeNull(); editor.view.dispatch({ changes: { from: 0, insert: "x" } }); expect(editor.view.dom.querySelector('[title="hover"]')?.textContent).toBe("te"); @@ -52,15 +56,19 @@ it("renders plain annotations, maps them through edits, and withdraws them", () expect(editor.view.dom.textContent).not.toContain(""); }); -it("filters project roots and unknown styles and replaces tree badges", () => { +it("filters tree badges by project root but not editor annotations, and drops unknown styles", () => { + // Editor annotations follow the file: a set from another root still renders where the file is open. setPluginDecorations("test", [set("/other")]); - expect(editor.view.dom.textContent).not.toContain(""); + expect(editor.view.dom.textContent).toContain(""); const unknown = set(); unknown.items[0]!.style = "unknown"; setPluginDecorations("test", [unknown]); expect(editor.view.dom.textContent).not.toContain(""); - const tree = { ...set(), tree: true, items: [], file: { style: "modified", badge: "M", tooltip: "changed" } }; + // Tree badges are per root. + const tree = { ...set("/other"), tree: true, items: [], file: { style: "modified", badge: "M", tooltip: "changed" } }; setPluginDecorations("test", [tree]); + expect(getFileDecoration("/p/a.txt")?.badge).toBeUndefined(); + setPluginDecorations("test", [{ ...tree, root: "/p" }]); expect(getFileDecoration("/p/a.txt")?.badge).toBe("M"); setPluginDecorations("test", []); expect(getFileDecoration("/p/a.txt")?.badge).toBeUndefined(); diff --git a/src/features/plugins/decorations.module.css b/src/features/plugins/decorations.module.css index 572ea515f..b6859d4c6 100644 --- a/src/features/plugins/decorations.module.css +++ b/src/features/plugins/decorations.module.css @@ -23,6 +23,12 @@ .info { text-decoration: underline dotted var(--info); } +.typo { + text-decoration: underline wavy var(--wave-typo, var(--success)); +} +.weak { + color: var(--fg-weak-warning); +} .underline { text-decoration: underline; } @@ -32,8 +38,55 @@ .muted { color: var(--fg-muted); } +/* A zero-width anchor at the line end with the message in an absolutely positioned inner box, + the way the code-vision hint is drawn: the annotation takes no part in the line's flow, so it + neither stretches the line nor moves the caret at the line end. The message's own offset is a + custom property: the blame steps past the message by measuring this inner box plus that + offset (inlineBlame.ts). */ .after { + --openidea-message-offset: var(--space-2); + display: inline-block; + width: 0; + position: relative; + line-height: 1; + user-select: none; +} +.afterInner { + position: absolute; + left: var(--openidea-message-offset); + bottom: -2px; white-space: pre; - margin-inline-start: calc(var(--space-2) / 2); color: var(--fg-muted); + /* No width cap and no overflow: a descender (g, y, p) would be clipped by a + line-height-1 box, and a long message simply runs past the editor's edge, + where the scroller clips it — the way IDEA paints line-end inlays. */ + pointer-events: auto; +} + +/* Inline text follows the scheme's Error / Warning colours, the way Inspection Lens + colours an annotation. The message span carries the severity class, and the line-end + text itself is never underlined: the wave belongs to the code token. */ +.after .error { + color: var(--wave-error); + text-decoration: none; +} +.after .warning { + color: var(--wave-warning); + text-decoration: none; +} +.after .info { + color: var(--info, var(--fg-weak-warning)); + text-decoration: none; +} +.after .typo { + color: var(--wave-typo, var(--success)); + text-decoration: none; +} +.after .weak { + color: var(--fg-weak-warning); + text-decoration: none; +} +.after .underline, +.after .strikethrough { + text-decoration: none; } diff --git a/src/features/plugins/decorations.ts b/src/features/plugins/decorations.ts index 657820856..576463fde 100644 --- a/src/features/plugins/decorations.ts +++ b/src/features/plugins/decorations.ts @@ -19,19 +19,24 @@ const colors: Record = { warning: "var(--warning)", error: "var(--danger)", info: "var(--info)", + typo: "var(--wave-typo)", + weak: "var(--fg-weak-warning)", underline: "var(--fg-editor)", strikethrough: "var(--fg-editor)", muted: "var(--fg-muted)", }; -function currentSets(sets: DecorationSet[]) { +function currentSets(sets: DecorationSet[], tree: boolean) { + // Editor annotations follow the file, so a set applies wherever its file is open — + // including a file outside this window's project. Tree decorations are per root. + if (!tree) return sets; const root = useProject.getState().project?.root; return sets.filter((s) => s.root === root); } function updateTree(plugin: string, sets: DecorationSet[]) { const items = new Map(); - for (const s of currentSets(sets)) + for (const s of currentSets(sets, true)) if (s.tree && s.file && (Object.hasOwn(colors, s.file.style) || s.file.note)) { items.set(s.path, { color: Object.hasOwn(colors, s.file.style) ? colors[s.file.style] : undefined, @@ -60,7 +65,16 @@ function offset(doc: Text, pos: Range["start"]): number | null { return pos.col <= line.length ? line.from + pos.col : null; } +/** An element's own custom property in px (its anchor offset), or 0. */ +function offsetOf(el: HTMLElement, property: string): number { + return Number.parseFloat(getComputedStyle(el).getPropertyValue(property)) || 0; +} + class AfterText extends WidgetType { + /** Last offset applied, so the observer does not rewrite the style on every keystroke. */ + private appliedWidth = -1; + private observer: MutationObserver | undefined; + constructor( readonly text: string, readonly style: string, @@ -71,21 +85,70 @@ class AfterText extends WidgetType { override eq(other: AfterText) { return this.text === other.text && this.style === other.style && this.hover === other.hover; } + /** + * Line-end annotations stack left to right: the code-vision git hint, every plugin message on + * the line, then the inline blame. Each message steps past the hint and its predecessors on the + * inner box's offset, not the anchor's margin: a margin would shift the in-flow siblings (the + * blame) too, and the blame would count them a second time. Neighbours can appear (or go away) + * after this widget mounted, hence the observer below. + */ + private stepPastLineEnd(el: HTMLElement) { + const line = el.closest(".cm-line"); + const inner = el.firstElementChild as HTMLElement | null; + if (!line || !inner) return; + const own = offsetOf(el, "--openidea-message-offset"); + const hint = line.querySelector(".cm-openidea-lensInlineInner"); + // The hint's inner box sits `--openidea-hint-offset` past its zero-width anchor. + let width = hint ? Math.round(hint.getBoundingClientRect().width) + offsetOf(hint, "--openidea-hint-offset") : 0; + // Every message before this one adds its own width and offset. + for (const other of line.querySelectorAll(".cm-plugin-after")) { + if (other === el || other.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING) continue; + const box = other.firstElementChild; + if (box) width += Math.round(box.getBoundingClientRect().width) + offsetOf(other, "--openidea-message-offset"); + } + if (width === this.appliedWidth) return; + this.appliedWidth = width; + // The stylesheet parks the message `own` past the anchor; shift it past the chain too. + if (width) inner.style.left = `${width + own}px`; + else inner.style.removeProperty("left"); + } override toDOM() { + // A zero-width anchor with the message in an absolutely positioned inner box, the way the + // code-vision hint is drawn: the annotation takes no part in the line's flow, so it neither + // stretches the line nor moves the caret at the line end. const span = document.createElement("span"); - span.className = `${styles.after} ${this.style}`; - span.textContent = this.text; - if (this.hover) span.title = this.hover; + span.className = styles.after ?? ""; + // A stable hook for the inline blame, which steps past this message (inlineBlame.ts). + span.classList.add("cm-plugin-after"); + const text = document.createElement("span"); + text.className = `${styles.afterInner ?? ""} ${this.style}`; + text.textContent = this.text; + if (this.hover) text.title = this.hover; + span.appendChild(text); + // The element is only measurable once CodeMirror has inserted it: defer to the next task, not a + // nested frame, so a widget that goes away first simply skips. + setTimeout(() => { + if (!span.isConnected) return; + this.stepPastLineEnd(span); + const line = span.closest(".cm-line"); + if (!line) return; + this.observer = new MutationObserver(() => this.stepPastLineEnd(span)); + this.observer.observe(line, { childList: true, subtree: true, characterData: true }); + }, 0); return span; } + override destroy() { + this.observer?.disconnect(); + this.observer = undefined; + } } export function decorationExtension(ctx: EditorDocContext) { const build = (doc: Text): CmDecorations => { const ranges = []; for (const sets of snapshots.values()) - for (const set of currentSets(sets)) - if (!set.tree && set.path === ctx.path) { + for (const set of currentSets(sets, false)) + if (!set.tree && String(set.path) === String(ctx.path)) { for (const item of set.items) { const className = Object.hasOwn(colors, item.style) ? styles[item.style] : undefined; if (!className) continue; @@ -99,10 +162,15 @@ export function decorationExtension(ctx: EditorDocContext) { to, ), ); - if (item.after) + if (item.after) { + // The message belongs at the line end even when the diagnostic's range ends + // mid-line (a typo in the middle of a long line). `side: 1` keeps it after the + // caret there; the inline blame steps past it, so: git hint | lens | blame. + const end = doc.lineAt(to); ranges.push( - Decoration.widget({ widget: new AfterText(item.after, className, item.hover), side: 1 }).range(to), + Decoration.widget({ widget: new AfterText(item.after, className, item.hover), side: 1 }).range(end.to), ); + } } } return Decoration.set(ranges, true); diff --git a/src/features/plugins/runtimeUi.tsx b/src/features/plugins/runtimeUi.tsx index 09473327e..26cfc0723 100644 --- a/src/features/plugins/runtimeUi.tsx +++ b/src/features/plugins/runtimeUi.tsx @@ -363,11 +363,17 @@ function trackWorkbenchEvents(): () => void { const offVcs = onIdeEvent("vcsChanged", (e) => { if (wanted.has("vcs-changed")) post({ kind: "vcsChanged", value: e.root }); }); + // Diagnostics arrive from language servers and builds without a file event; plugins + // that draw them (Inspection Lens) re-read the snapshot when this fires. + const offProblems = onIdeEvent("diagnostics", () => { + if (wanted.has("problems-changed")) post({ kind: "problemsChanged" }); + }); return () => { offEditor(); offActions(); offVcs(); offSettings(); + offProblems(); if (postTimer) clearTimeout(postTimer); postTimer = null; queue = []; diff --git a/src/features/plugins/settings.tsx b/src/features/plugins/settings.tsx index 7b63ffc0b..ea78b0ba6 100644 --- a/src/features/plugins/settings.tsx +++ b/src/features/plugins/settings.tsx @@ -19,6 +19,8 @@ export interface SettingSchema { options?: string[] | null; multiline?: boolean; group?: string | null; + /** A sample drawn in the setting's own style, e.g. the message a severity produces. */ + example?: { text: string; style: string } | null; items?: SettingSchema | null; properties?: SettingSchema[]; } @@ -217,20 +219,36 @@ function SettingControl({ pluginId, s }: { pluginId: string; s: SettingSchema }) const all = form.useValue("plugins") as PluginSettings | undefined; const stored = all?.[pluginId]?.[s.key]; const value = accepts(s, stored) ? stored : s.default; + const control = ( + { + const valid = accepts(s, v); + setInvalid(!valid); + // Keep the chosen value even when it equals the default: false and "" + // are real plugin settings, not "remove the override". + if (valid) form.setMapEntry("plugins", [pluginId, s.key], v); + }} + /> + ); + const example = s.example ? ( + + {s.example.text} + + ) : null; return (
{s.type !== "boolean" && {s.title}} - { - const valid = accepts(s, v); - setInvalid(!valid); - // Keep the chosen value even when it equals the default: false and "" - // are real plugin settings, not "remove the override". - if (valid) form.setMapEntry("plugins", [pluginId, s.key], v); - }} - /> + {/* A sample sits in a second column, one row per setting, the way PyCharm previews a severity. */} + {example ? ( +
+ {control} + {example} +
+ ) : ( + control + )} {s.description &&
{s.description}
} {invalid && (

diff --git a/src/features/vcs/__tests__/inlineBlame.test.ts b/src/features/vcs/__tests__/inlineBlame.test.ts index 7867589de..64ec2eeac 100644 --- a/src/features/vcs/__tests__/inlineBlame.test.ts +++ b/src/features/vcs/__tests__/inlineBlame.test.ts @@ -320,6 +320,17 @@ describe("current line decoration", () => { return el; } } + // A plugin's line-end message is a zero-width anchor with its text in an inner box. + class FakeMessage extends WidgetType { + toDOM() { + const anchor = document.createElement("span"); + anchor.className = "cm-plugin-after"; + const inner = document.createElement("span"); + inner.getBoundingClientRect = () => ({ width: 70 }) as DOMRect; + anchor.append(inner); + return anchor; + } + } const lens = new Compartment(); view = new EditorView({ parent: document.body, @@ -333,14 +344,20 @@ describe("current line decoration", () => { // the hint shows up only after the blame mounted — the blame must still step past it view.dispatch({ effects: lens.reconfigure( - EditorView.decorations.of(Decoration.set([Decoration.widget({ widget: new FakeHint(), side: 1 }).range(3)])), + EditorView.decorations.of( + Decoration.set([ + Decoration.widget({ widget: new FakeHint(), side: 1 }).range(3), + Decoration.widget({ widget: new FakeMessage(), side: 1 }).range(3), + ]), + ), ), }); await vi.advanceTimersByTimeAsync(10); expect(view.dom.querySelector(".cm-openidea-lensInlineInner")).not.toBeNull(); - expect(hint()!.style.paddingLeft).toBe("calc(3ch + 120px)"); + // hint 120px, then the message's 70px: the label steps past the whole chain + expect(hint()!.style.paddingLeft).toBe("calc(3ch + 190px)"); - // and it gives the space back when the hint goes away + // and it gives the space back when the hint and the message go away view.dispatch({ effects: lens.reconfigure([]) }); await vi.advanceTimersByTimeAsync(10); expect(hint()!.style.paddingLeft).toBe(""); diff --git a/src/features/vcs/inlineBlame.ts b/src/features/vcs/inlineBlame.ts index ba184d726..521c047c5 100644 --- a/src/features/vcs/inlineBlame.ts +++ b/src/features/vcs/inlineBlame.ts @@ -84,6 +84,12 @@ class BlameCache { } } +/** An element's own custom property in px (its anchor offset), or 0. Inherited custom + properties resolve through the cascade, so reading the inner box finds the anchor's. */ +function offsetOf(el: HTMLElement, property: string): number { + return Number.parseFloat(getComputedStyle(el).getPropertyValue(property)) || 0; +} + class BlameWidget extends WidgetType { /** Last offset applied, so the observer does not rewrite the style on every keystroke. */ private appliedWidth = -1; @@ -106,16 +112,25 @@ class BlameWidget extends WidgetType { } /** - * A code-vision hint drawn at the line end is out of flow (see codeLens.ts), so it does not - * make room for itself: step the label past its measured width, as IDEA lays its line-end - * inlays out. The hint can also appear (or go away) after this widget mounted, hence the + * Line-end annotations stack left to right: the code-vision hint, every plugin's line-end + * message, then this label. The hint is out of flow (see codeLens.ts) and each message steps + * past its predecessors itself, so this one steps past the whole chain, as IDEA lays its + * line-end inlays out. They can also appear (or go away) after this widget mounted, hence the * observer below. */ private stepPastLineEnd(el: HTMLElement) { const line = el.closest(".cm-line"); if (!line) return; + let width = 0; const hint = line.querySelector(".cm-openidea-lensInlineInner"); - const width = hint ? Math.round(hint.getBoundingClientRect().width) : 0; + // The hint's inner box sits its anchor's `--openidea-hint-offset` past that anchor. + if (hint) width += Math.round(hint.getBoundingClientRect().width) + offsetOf(hint, "--openidea-hint-offset"); + // Plugin messages are zero-width anchors; their inner boxes carry the visible width and + // inherit the anchor's `--openidea-message-offset`. + for (const anchor of line.querySelectorAll(".cm-plugin-after")) { + const box = anchor.firstElementChild as HTMLElement | null; + if (box) width += Math.round(box.getBoundingClientRect().width) + offsetOf(box, "--openidea-message-offset"); + } if (width === this.appliedWidth) return; this.appliedWidth = width; // Padding, not margin: the gap is virtual (it only moves the label), so the caret at the diff --git a/wit/commit.wit b/wit/commit.wit index b3e5b9fb5..46aee192e 100644 --- a/wit/commit.wit +++ b/wit/commit.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// The commit being prepared in the window the current command or event came /// from (the Commit tool window, or the Commit dialog while it is open): the diff --git a/wit/database.wit b/wit/database.wit index 59e8221bc..14484c068 100644 --- a/wit/database.wit +++ b/wit/database.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Plugin-owned project data sources. Requires database.sources and project trust. /// Runs on the Database subsystem's host, including on remote project servers. diff --git a/wit/decorations.wit b/wit/decorations.wit index fb9daf387..7ed27bd67 100644 --- a/wit/decorations.wit +++ b/wit/decorations.wit @@ -1,9 +1,10 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; -/// Project-scoped decorations (API 1.4). Requires project.read; paths must be inside -/// an open project. Sets belong to the plugin and disappear when it stops. +/// Editor and project-tree decorations (API 1.4). Requires project.read. An editor set +/// follows its file: the file's own project, or the nearest one when it is outside them all. +/// Tree sets are rooted in an open project. Sets belong to the plugin and disappear when it stops. /// Styles: highlight, search, added, modified, removed, warning, error, info, -/// underline, strikethrough, muted. Unknown styles are ignored. Theme tokens only. +/// typo, weak, underline, strikethrough, muted. Unknown styles are ignored. Theme tokens only. interface decorations { use types.{error, range}; /// An editor annotation; after is plain text shown after the range. diff --git a/wit/details.wit b/wit/details.wit index 2096fbaef..7bb6da47b 100644 --- a/wit/details.wit +++ b/wit/details.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// What the pane beside the Services tree shows for a plugin's node. interface detail-types { diff --git a/wit/diagnostics.wit b/wit/diagnostics.wit index db8a52315..c795062a6 100644 --- a/wit/diagnostics.wit +++ b/wit/diagnostics.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Optional export (world `plugin-diagnostics`): problems in the current, /// possibly unsaved document, for the languages declared with the diff --git a/wit/endpoints.wit b/wit/endpoints.wit index e738e8340..492f3d420 100644 --- a/wit/endpoints.wit +++ b/wit/endpoints.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// HTTP endpoints found in a project (`[[contributes.endpoints]]`). interface endpoints-provider { diff --git a/wit/events.wit b/wit/events.wit index 12f2da2cf..6e3a5148d 100644 --- a/wit/events.wit +++ b/wit/events.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Workbench events, delivered in batches to `event-handler.handle`. interface events { diff --git a/wit/exports.wit b/wit/exports.wit index 26cc428b1..f062d30fc 100644 --- a/wit/exports.wit +++ b/wit/exports.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; interface lifecycle { use types.{context, error}; diff --git a/wit/extensions.wit b/wit/extensions.wit index 6675ea48b..04826a3fc 100644 --- a/wit/extensions.wit +++ b/wit/extensions.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// What other plugins contributed to the extension points this plugin declares /// (`[[extension-points]]` in `plugin.toml`; the contributors list this plugin diff --git a/wit/file-actions.wit b/wit/file-actions.wit index 0d955df27..3c1c89635 100644 --- a/wit/file-actions.wit +++ b/wit/file-actions.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Optional export (world `plugin-file-actions`): quick fixes and /// refactorings that create, rename or delete files, next to the text edits diff --git a/wit/fs.wit b/wit/fs.wit index 839e4e965..1dc9420d0 100644 --- a/wit/fs.wit +++ b/wit/fs.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Files outside the open projects: the configuration of the program a /// plugin integrates with, a tool installed elsewhere, a licence file. diff --git a/wit/host.wit b/wit/host.wit index 1df9bda16..a86d34b48 100644 --- a/wit/host.wit +++ b/wit/host.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Writes to the IDE log, tagged with the plugin id. interface log { diff --git a/wit/hover.wit b/wit/hover.wit index 49612d00f..c066f9a0d 100644 --- a/wit/hover.wit +++ b/wit/hover.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Optional export (world `plugin-hover`): Markdown added to the hover of /// documents of the languages declared with the `hover` provider kind, diff --git a/wit/indexing.wit b/wit/indexing.wit index 4f0cfe224..57b6061c2 100644 --- a/wit/indexing.wit +++ b/wit/indexing.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Cross-file associations a plugin publishes for the files it declares under /// `[[contributes.documentIndexers]]`. The host calls `index` in cancellable diff --git a/wit/injections.wit b/wit/injections.wit index 2a5be91f8..6667afc69 100644 --- a/wit/injections.wit +++ b/wit/injections.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Embedded languages. The host routes plugin providers and configured language /// servers inside these ranges, preserving host-document UTF-16 coordinates. diff --git a/wit/inlay.wit b/wit/inlay.wit index 4bcfa613a..208416f3f 100644 --- a/wit/inlay.wit +++ b/wit/inlay.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Optional export (world `plugin-inlay-hints`): inline hints in documents of /// the languages declared with the `inlay-hints` provider kind, including diff --git a/wit/io.wit b/wit/io.wit index 7d1ed4428..212385b48 100644 --- a/wit/io.wit +++ b/wit/io.wit @@ -1,11 +1,14 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Problems in files, shown in the editor and the Problems view. interface diagnostics { use types.{error, range, location}; - /// Problems reported by a source for a file in this window's project. + /// Problems one producer reported for a file, wherever the file is: the host records every + /// diagnostics event it emits, so this follows the file, not the invoking window. record file-problems { path: string, source: string, diagnostics: list } - /// Current Problems view snapshot, optionally filtered to a project file. + /// The diagnostics the host recorded, one entry per producer, optionally + /// filtered to a file: language servers, builds, spelling and plugin + /// providers alike, whatever window or project opened the file. /// Requires project.read and an invoking window. Empty for remote projects. problems: func(path: option) -> result, error>; diff --git a/wit/language-features.wit b/wit/language-features.wit index c6403b0a5..0a406cba1 100644 --- a/wit/language-features.wit +++ b/wit/language-features.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Additional language features, called only for declared API 1.4 provider kinds. /// Editor calls have a 50 ms soft / 500 ms hard budget; formatting has 5 seconds. diff --git a/wit/mcp.wit b/wit/mcp.wit index 903a15f27..15e6c9139 100644 --- a/wit/mcp.wit +++ b/wit/mcp.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Discovery of the IDE's built-in MCP server; never exposes its URL or token. interface mcp { diff --git a/wit/plugin.wit b/wit/plugin.wit index 2e7da4359..39c55203c 100644 --- a/wit/plugin.wit +++ b/wit/plugin.wit @@ -8,7 +8,7 @@ /// `fs` and `workspace`, and everything else goes through the imports below, /// gated by the capabilities granted in `plugin.toml` (a missing one returns /// `error::denied`). -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; world plugin { import log; diff --git a/wit/providers.wit b/wit/providers.wit index 7bb02b8db..8b098e4a6 100644 --- a/wit/providers.wit +++ b/wit/providers.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Editor features a plugin provides for the languages it declares under /// `[[contributes.providers]]` (the host calls only declared kinds). They run diff --git a/wit/run-targets.wit b/wit/run-targets.wit index e29456015..5e780df04 100644 --- a/wit/run-targets.wit +++ b/wit/run-targets.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Project-scoped targets owned by the registering plugin (API 1.4). interface run-targets { diff --git a/wit/run.wit b/wit/run.wit index e4875733f..abb398bea 100644 --- a/wit/run.wit +++ b/wit/run.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Launches this plugin's configurations in the invoking window's Run tool window. interface run { diff --git a/wit/semantic.wit b/wit/semantic.wit index 021b94dbf..c3c26fad4 100644 --- a/wit/semantic.wit +++ b/wit/semantic.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Optional export (world `plugin-semantic-tokens`): semantic colouring of /// documents of the languages declared with the `semantic-tokens` provider diff --git a/wit/types.wit b/wit/types.wit index aa255a617..d888fdab8 100644 --- a/wit/types.wit +++ b/wit/types.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Shared types. Positions are 0-based lines and UTF-16 code unit columns, /// as in the editor and LSP. diff --git a/wit/views.wit b/wit/views.wit index 4fbcaaff6..268e76ac9 100644 --- a/wit/views.wit +++ b/wit/views.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; /// Declarative views: tool windows declared /// under `[[contributes.views]]` and custom editors under diff --git a/wit/workspace.wit b/wit/workspace.wit index 5030af173..9b798f2d1 100644 --- a/wit/workspace.wit +++ b/wit/workspace.wit @@ -1,4 +1,4 @@ -package openidea:plugin@1.7.0; +package openidea:plugin@1.8.0; interface workspace { use types.{error, location};