From 56796fb2c69371a1c495d26fd85c7e13b6a837ed Mon Sep 17 00:00:00 2001 From: Pedro Pessoa <54516747+yzPeedro@users.noreply.github.com> Date: Sat, 7 Nov 2026 08:25:10 -0300 Subject: [PATCH] feat(plugins): let plugins show inlay hints in any language (#1937) A new optional inlay-hints-provider export (worlds plugin-inlay-hints / inlay-hints), declared as the inlay-hints provider kind for any language (API 1.3). The host asks the language server and the plugins together for the visible range and returns both in position order; with no server hints the plugins' hints still show. Kinds map to the Inlay Hints settings; labels, tooltips and counts are capped. Co-authored-by: yzPeedro <54516747+yzPeedro@users.noreply.github.com> --- crates/ide-host/src/ops/lsp.rs | 17 +- crates/ide-host/src/plugins/inlay_hints.rs | 94 +++++ crates/ide-host/src/plugins/mod.rs | 2 + crates/ide-host/src/plugins/providers.rs | 16 +- crates/ide-host/tests/plugin_inlay_hints.rs | 59 ++++ crates/ide-plugin/src/runtime.rs | 60 +++- crates/ide-plugin/src/types.rs | 10 + crates/ide-plugin/src/wit_host.rs | 7 + crates/ide-plugin/tests/api_compat.rs | 1 + .../tests/fixtures/commit/dist/plugin.wasm | Bin 100250 -> 100250 bytes .../fixtures/commit/dist/plugin.wasm.sources | 2 +- .../tests/fixtures/hello/dist/plugin.wasm | Bin 143243 -> 143243 bytes .../fixtures/hello/dist/plugin.wasm.sources | 2 +- .../tests/fixtures/hover/dist/plugin.wasm | Bin 89596 -> 89596 bytes .../fixtures/hover/dist/plugin.wasm.sources | 2 +- .../tests/fixtures/indexer/dist/plugin.wasm | Bin 83023 -> 83023 bytes .../fixtures/indexer/dist/plugin.wasm.sources | 2 +- .../tests/fixtures/inlay-hints/Cargo.lock | 333 ++++++++++++++++++ .../tests/fixtures/inlay-hints/Cargo.toml | 20 ++ .../tests/fixtures/inlay-hints/README.md | 7 + .../fixtures/inlay-hints/dist/plugin.wasm | Bin 0 -> 91543 bytes .../inlay-hints/dist/plugin.wasm.sources | 1 + .../tests/fixtures/inlay-hints/plugin.toml | 9 + .../tests/fixtures/inlay-hints/src/lib.rs | 47 +++ .../tests/fixtures/jobs/dist/plugin.wasm | Bin 144343 -> 144343 bytes .../fixtures/jobs/dist/plugin.wasm.sources | 2 +- .../misbehave/dist/plugin.wasm.sources | 2 +- .../fixtures/semantic-tokens/dist/plugin.wasm | Bin 91819 -> 91827 bytes .../semantic-tokens/dist/plugin.wasm.sources | 2 +- .../tests/fixtures/storage/dist/plugin.wasm | Bin 73958 -> 73958 bytes .../fixtures/storage/dist/plugin.wasm.sources | 2 +- plugins/README.md | 25 +- .../official/ai-completion/dist/plugin.wasm | Bin 173095 -> 173103 bytes .../ai-completion/dist/plugin.wasm.sources | 2 +- plugins/official/checkstyle/dist/plugin.wasm | Bin 169550 -> 169550 bytes .../checkstyle/dist/plugin.wasm.sources | 2 +- plugins/official/csv-editor/dist/plugin.wasm | Bin 133413 -> 133413 bytes .../csv-editor/dist/plugin.wasm.sources | 2 +- plugins/official/git-toolbox/dist/plugin.wasm | Bin 110087 -> 110087 bytes .../git-toolbox/dist/plugin.wasm.sources | 2 +- plugins/official/graphviz/dist/plugin.wasm | Bin 129121 -> 129121 bytes .../graphviz/dist/plugin.wasm.sources | 2 +- .../official/key-promoter/dist/plugin.wasm | Bin 100203 -> 100203 bytes .../key-promoter/dist/plugin.wasm.sources | 2 +- .../official/license-header/dist/plugin.wasm | Bin 152622 -> 152622 bytes .../license-header/dist/plugin.wasm.sources | 2 +- plugins/official/mybatis/dist/plugin.wasm | Bin 168362 -> 168362 bytes .../official/mybatis/dist/plugin.wasm.sources | 2 +- .../official/regex-tester/dist/plugin.wasm | Bin 931746 -> 931746 bytes .../regex-tester/dist/plugin.wasm.sources | 2 +- plugins/official/solidity/dist/plugin.wasm | Bin 108037 -> 108045 bytes .../solidity/dist/plugin.wasm.sources | 2 +- .../official/string-tools/dist/plugin.wasm | Bin 165803 -> 165803 bytes .../string-tools/dist/plugin.wasm.sources | 2 +- sdk/rust/openidea-plugin/src/inlay_hints.rs | 84 +++++ sdk/rust/openidea-plugin/src/lib.rs | 50 +++ sdk/rust/openidea-plugin/wit/inlay.wit | 39 ++ sdk/rust/openidea-plugin/wit/plugin.wit | 11 + wit/inlay.wit | 39 ++ wit/plugin.wit | 11 + 60 files changed, 953 insertions(+), 27 deletions(-) create mode 100644 crates/ide-host/src/plugins/inlay_hints.rs create mode 100644 crates/ide-host/tests/plugin_inlay_hints.rs create mode 100644 crates/ide-plugin/tests/fixtures/inlay-hints/Cargo.lock create mode 100644 crates/ide-plugin/tests/fixtures/inlay-hints/Cargo.toml create mode 100644 crates/ide-plugin/tests/fixtures/inlay-hints/README.md create mode 100644 crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm create mode 100644 crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm.sources create mode 100644 crates/ide-plugin/tests/fixtures/inlay-hints/plugin.toml create mode 100644 crates/ide-plugin/tests/fixtures/inlay-hints/src/lib.rs create mode 100644 sdk/rust/openidea-plugin/src/inlay_hints.rs create mode 100644 sdk/rust/openidea-plugin/wit/inlay.wit create mode 100644 wit/inlay.wit diff --git a/crates/ide-host/src/ops/lsp.rs b/crates/ide-host/src/ops/lsp.rs index 535b1acd4..c4a14fa8c 100644 --- a/crates/ide-host/src/ops/lsp.rs +++ b/crates/ide-host/src/ops/lsp.rs @@ -677,7 +677,22 @@ pub async fn lsp_format( } pub async fn lsp_inlay_hints(host: &Host, window: &str, doc: DocId, range: Range) -> IdeResult> { - host.project(window)?.lsp.inlay_hints(doc, range).await + let lsp = host.project(window)?.lsp.clone(); + let Some((plugins, d)) = plugin_providers(host, window, doc, ProviderKind::InlayHints) else { + return lsp.inlay_hints(doc, range).await; + }; + let (result, hints) = + tokio::join!(lsp.inlay_hints(doc, range), plugin_call(move || plugins.provide_inlay_hints(&d, range))); + let hints = hints.unwrap_or_default(); + if hints.is_empty() { + return result; + } + let server = match result { + Ok(server) => server, + Err(e) if matches!(e.kind, ErrorKind::NotFound | ErrorKind::Unavailable) => Vec::new(), + Err(e) => return Err(e), + }; + Ok(crate::plugins::merge_inlay_hints(server, hints, range)) } pub async fn lsp_semantic_tokens(host: &Host, window: &str, doc: DocId) -> IdeResult> { diff --git a/crates/ide-host/src/plugins/inlay_hints.rs b/crates/ide-host/src/plugins/inlay_hints.rs new file mode 100644 index 000000000..b7636c92a --- /dev/null +++ b/crates/ide-host/src/plugins/inlay_hints.rs @@ -0,0 +1,94 @@ +use ide_core::Range; +use ide_lsp::types::{InlayHint, InlayKind}; +use ide_plugin::PluginInlayHint; + +pub(crate) fn merge(server: Vec, plugin: Vec, range: Range) -> Vec { + let inside = |h: &PluginInlayHint| { + let at = (h.position.line, h.position.col); + (range.start.line, range.start.col) <= at && at <= (range.end.line, range.end.col) + }; + let mut out = server; + out.extend(plugin.into_iter().filter(inside).map(|h| InlayHint { + position: h.position, + label: h.label, + kind: match h.kind.as_str() { + "type" => InlayKind::Type, + "parameter" => InlayKind::Parameter, + _ => InlayKind::Other, + }, + padding_left: h.padding_left, + padding_right: h.padding_right, + tooltip: h.tooltip, + parts: Vec::new(), + })); + out.sort_by_key(|h| (h.position.line, h.position.col)); + out +} + +#[cfg(test)] +mod tests { + use super::*; + use ide_core::Pos; + + fn plugin(line: u32, col: u32, label: &str, kind: &str) -> PluginInlayHint { + PluginInlayHint { + position: Pos::new(line, col), + label: label.into(), + kind: kind.into(), + tooltip: Some("from the plugin".into()), + padding_left: true, + padding_right: false, + } + } + + fn server(line: u32, col: u32, label: &str) -> InlayHint { + InlayHint { + position: Pos::new(line, col), + label: label.into(), + kind: InlayKind::Parameter, + padding_left: false, + padding_right: true, + tooltip: None, + parts: Vec::new(), + } + } + + fn summary(hints: &[InlayHint]) -> Vec<(u32, u32, String)> { + hints.iter().map(|h| (h.position.line, h.position.col, h.label.clone())).collect() + } + + #[test] + fn plugin_hints_join_the_servers_in_position_order() { + let range = Range::new(Pos::new(0, 0), Pos::new(10, 0)); + let merged = merge( + vec![server(2, 4, "name:"), server(5, 1, "id:")], + vec![plugin(3, 9, ": string", "type"), plugin(2, 1, "GET /orders", "other")], + range, + ); + assert_eq!( + summary(&merged), + [ + (2, 1, "GET /orders".to_owned()), + (2, 4, "name:".to_owned()), + (3, 9, ": string".to_owned()), + (5, 1, "id:".to_owned()) + ] + ); + let typed = merged.iter().find(|h| h.label == ": string").unwrap(); + assert!(matches!(typed.kind, InlayKind::Type)); + assert!(typed.padding_left && !typed.padding_right); + assert_eq!(typed.tooltip.as_deref(), Some("from the plugin")); + } + + #[test] + fn unknown_kinds_are_other_and_hints_outside_the_range_are_dropped() { + let range = Range::new(Pos::new(2, 5), Pos::new(4, 3)); + let merged = merge( + Vec::new(), + vec![plugin(2, 4, "before", "type"), plugin(3, 0, "in", "route"), plugin(4, 4, "after", "type")], + range, + ); + assert_eq!(summary(&merged), [(3, 0, "in".to_owned())]); + assert!(matches!(merged[0].kind, InlayKind::Other)); + } +} diff --git a/crates/ide-host/src/plugins/mod.rs b/crates/ide-host/src/plugins/mod.rs index be40ee1c5..8d66bf1e4 100644 --- a/crates/ide-host/src/plugins/mod.rs +++ b/crates/ide-host/src/plugins/mod.rs @@ -23,6 +23,7 @@ mod icon_patterns; mod icon_settings; mod icons; pub mod index_ops; +mod inlay_hints; mod install; pub mod intellij; mod market; @@ -44,6 +45,7 @@ pub(crate) use hover::merge as merge_hover; pub use icons::{ MAX_PLUGIN_ICON, map_asset as icon_theme_map_asset, plugin_icon, sanitize_svg, svg_prefix as icon_theme_svg_prefix, }; +pub(crate) use inlay_hints::merge as merge_inlay_hints; pub use market::{ INDEX_FILE as MARKET_INDEX_FILE, MarketPlugin, OFFICIAL_INDEX as OFFICIAL_MARKET_INDEX, OFFICIAL_PUBLISHER, read_url as read_market_url, resolve as resolve_market_href, diff --git a/crates/ide-host/src/plugins/providers.rs b/crates/ide-host/src/plugins/providers.rs index b262b709a..2703ca127 100644 --- a/crates/ide-host/src/plugins/providers.rs +++ b/crates/ide-host/src/plugins/providers.rs @@ -39,6 +39,7 @@ pub enum ProviderKind { LineMarkers, SemanticTokens, Hover, + InlayHints, } #[derive(Clone, Debug, Deserialize)] @@ -120,6 +121,9 @@ impl ExtensionPoint for ProvidersPoint { if d.kinds.contains(&ProviderKind::Hover) { pkg.manifest.needs_api("contributes.providers: hover", 3)?; } + if d.kinds.contains(&ProviderKind::InlayHints) { + pkg.manifest.needs_api("contributes.providers: inlay-hints", 3)?; + } for l in &d.languages { if l != "*" && (l.is_empty() @@ -169,7 +173,7 @@ use super::{LoadedPlugin, PluginHost, PluginStatus}; use ide_core::CancellationToken; use ide_plugin::{ ActorError, CallError, CodeAction, CompletionItem, InlineSuggestion, LineMarker, Location, Pending, PluginHover, - ProviderDocument, Runtime, SemanticToken, + PluginInlayHint, ProviderDocument, Runtime, SemanticToken, }; use std::sync::Weak; use std::time::{Duration, Instant}; @@ -357,6 +361,16 @@ impl PluginHost { .collect() } + pub fn provide_inlay_hints(&self, doc: &ProviderDocument, range: ide_core::Range) -> Vec { + let d = Arc::new(doc.clone()); + self.edit_calls(ProviderKind::InlayHints, &doc.language, move |rt, timeout| { + rt.provide_inlay_hints(&d, range, timeout) + }) + .into_iter() + .flat_map(|(_, hints)| hints) + .collect() + } + /// Ghost text from the first plugin that has some. Not bound by the /// edit-path soft deadline (it may need the network) but by `cancel`: /// the editor cancels a request as soon as the user types on. diff --git a/crates/ide-host/tests/plugin_inlay_hints.rs b/crates/ide-host/tests/plugin_inlay_hints.rs new file mode 100644 index 000000000..c7d7c239f --- /dev/null +++ b/crates/ide-host/tests/plugin_inlay_hints.rs @@ -0,0 +1,59 @@ +use ide_core::{Pos, Range}; +use ide_host::plugins::{Consent, PluginHost}; +use ide_plugin::{Limits, ProviderDocument}; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use std::time::Duration; + +fn fixture_dir() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")).join("../ide-plugin/tests/fixtures/inlay-hints") +} + +fn host(dir: &Path) -> PluginHost { + let limits = Limits { edit_soft: Duration::from_secs(5), edit_hard: Duration::from_secs(10), ..Limits::default() }; + PluginHost::new(dir.join("plugins"), Arc::new(|_: Option<&str>, _| {}), limits).unwrap() +} + +fn doc(dir: &Path, language: &str, text: &str) -> ProviderDocument { + ProviderDocument { path: dir.join("Order.php"), language: language.into(), version: 1, text: text.into() } +} + +const ORDER: &str = "user();\n$order->total();\n$order->user;\n"; + +fn all() -> Range { + Range::new(Pos::new(0, 0), Pos::new(100, 0)) +} + +#[test] +fn calls_of_declared_methods_get_a_type_hint() { + let tmp = tempfile::tempdir().unwrap(); + let host = host(tmp.path()); + host.install_from_dir(&fixture_dir(), &Consent::all()).unwrap(); + let hints = host.provide_inlay_hints(&doc(tmp.path(), "php", ORDER), all()); + let found: Vec<_> = + hints.iter().map(|h| (h.position.line, h.position.col, h.label.as_str(), h.kind.as_str())).collect(); + assert_eq!(found, [(5, 14, ": ?User", "type")]); + assert_eq!(hints[0].tooltip.as_deref(), Some("user() returns ?User")); + assert!(!hints[0].padding_left && !hints[0].padding_right); +} + +#[test] +fn only_declared_languages_are_asked() { + let tmp = tempfile::tempdir().unwrap(); + let host = host(tmp.path()); + host.install_from_dir(&fixture_dir(), &Consent::all()).unwrap(); + assert!(host.provide_inlay_hints(&doc(tmp.path(), "javascript", ORDER), all()).is_empty()); +} + +#[test] +fn the_kind_needs_plugin_api_1_3() { + let tmp = tempfile::tempdir().unwrap(); + let host = host(tmp.path()); + let pkg = tmp.path().join("old"); + std::fs::create_dir_all(pkg.join("dist")).unwrap(); + std::fs::copy(fixture_dir().join("dist/plugin.wasm"), pkg.join("dist/plugin.wasm")).unwrap(); + let manifest = std::fs::read_to_string(fixture_dir().join("plugin.toml")).unwrap(); + std::fs::write(pkg.join("plugin.toml"), manifest.replace("\"^1.3\"", "\"^1.0\"")).unwrap(); + let err = host.install_from_dir(&pkg, &Consent::all()).unwrap_err(); + assert!(err.message.contains("new in plugin API 1.3"), "{}", err.message); +} diff --git a/crates/ide-plugin/src/runtime.rs b/crates/ide-plugin/src/runtime.rs index c9c747402..4f46788b2 100644 --- a/crates/ide-plugin/src/runtime.rs +++ b/crates/ide-plugin/src/runtime.rs @@ -5,7 +5,7 @@ use crate::types::{ CodeAction, CommitDraft, CommitProblem, CompletionItem, InlineSuggestion, LineMarker, Location, PluginHover, - ProviderDocument, SemanticToken, + PluginInlayHint, ProviderDocument, SemanticToken, }; use crate::types::{ EventKind, HostError, HttpRequest, HttpResponse, Invocation, Limits, NotificationLevel, ProcessOutput, Prompt, @@ -16,8 +16,8 @@ use crate::types::{ ViewField, ViewListItem, ViewTable, ViewTreeItem, Viewport, }; use crate::wit_host::{ - bindings, commit_checks_bindings, details_bindings, hover_bindings, indexing_bindings, providers_bindings, - semantic_bindings, syntax_bindings, views_bindings, + bindings, commit_checks_bindings, details_bindings, hover_bindings, indexing_bindings, inlay_bindings, + providers_bindings, semantic_bindings, syntax_bindings, views_bindings, }; use ide_core::CancellationToken; use ide_core::ipc_path::to_ipc; @@ -39,6 +39,7 @@ use details_bindings::exports::openidea::plugin::detail_provider as wit_detail; use details_bindings::openidea::plugin::detail_types as wit_dt; use hover_bindings::exports::openidea::plugin::hover_provider as wit_hover; use indexing_bindings::exports::openidea::plugin::document_indexer as wit_index; +use inlay_bindings::exports::openidea::plugin::inlay_hints_provider as wit_inlay; use providers_bindings::exports::openidea::plugin::providers as wit_prov; use semantic_bindings::exports::openidea::plugin::semantic_tokens_provider as wit_sem; use syntax_bindings::exports::openidea::plugin::syntax_provider as wit_syntax; @@ -545,6 +546,7 @@ struct Exports { details: Option, hover: Option, commit_checks: Option, + inlay: Option, } /// Most results one syntax call may return (more are dropped). @@ -628,6 +630,7 @@ impl Runtime { let detail_indices = wit_detail::GuestIndices::new(&pre).ok(); let hover_indices = wit_hover::GuestIndices::new(&pre).ok(); let commit_check_indices = wit_cc::GuestIndices::new(&pre).ok(); + let inlay_indices = wit_inlay::GuestIndices::new(&pre).ok(); let instance = pre.instantiate(&mut store).map_err(api_hint)?; let plugin = plugin_indices.load(&mut store, &instance).map_err(api_hint)?; let syntax = match syntax_indices { @@ -662,9 +665,24 @@ impl Runtime { Some(indices) => Some(indices.load(&mut store, &instance).map_err(api_hint)?), None => None, }; + let inlay = match inlay_indices { + Some(indices) => Some(indices.load(&mut store, &instance).map_err(api_hint)?), + None => None, + }; Ok(Self { store, - exports: Exports { plugin, syntax, providers, views, indexer, semantic, details, hover, commit_checks }, + exports: Exports { + plugin, + syntax, + providers, + views, + indexer, + semantic, + details, + hover, + commit_checks, + inlay, + }, limits: setup.limits, version: setup.version, }) @@ -945,6 +963,40 @@ impl Runtime { .map(|h| PluginHover { markdown: clip(h.markdown, MAX_HOVER_BYTES), range: h.range.map(range_from_wit) })) } + pub fn provide_inlay_hints( + &mut self, + doc: &ProviderDocument, + range: ide_core::Range, + timeout: Duration, + ) -> Result, CallError> { + if self.exports.inlay.is_none() { + return Err(CallError::missing_export("inlay-hints-provider")); + } + let d = wit_inlay::Document { + path: to_ipc(&doc.path), + language: doc.language.clone(), + version: doc.version, + text: doc.text.clone(), + }; + let r = wit_types::Range { start: pos_to_wit(range.start), end: pos_to_wit(range.end) }; + let hints = self.call(Invocation::default(), timeout, |e, store| { + e.inlay.as_ref().expect("checked").call_inlay_hints(store, &d, r) + })?; + Ok(hints + .into_iter() + .filter(|h| !h.label.trim().is_empty()) + .take(MAX_PROVIDER_ITEMS) + .map(|h| PluginInlayHint { + position: ide_core::Pos::new(h.position.line, h.position.character), + label: clip(h.label, 256), + kind: clip(h.kind, 32), + tooltip: h.tooltip.map(|t| clip(t, 1024)), + padding_left: h.padding_left, + padding_right: h.padding_right, + }) + .collect()) + } + pub fn activate(&mut self) -> Result<(), CallError> { let data = self.store.data(); let ctx = wit_types::Context { diff --git a/crates/ide-plugin/src/types.rs b/crates/ide-plugin/src/types.rs index 6239d3822..1156e0779 100644 --- a/crates/ide-plugin/src/types.rs +++ b/crates/ide-plugin/src/types.rs @@ -466,6 +466,16 @@ pub struct PluginHover { pub range: Option, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PluginInlayHint { + pub position: ide_core::Pos, + pub label: String, + pub kind: String, + pub tooltip: Option, + pub padding_left: bool, + pub padding_right: bool, +} + // ---- declarative views (wit/views.wit) --------------------------------------- #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)] diff --git a/crates/ide-plugin/src/wit_host.rs b/crates/ide-plugin/src/wit_host.rs index 88bd4da8f..088157272 100644 --- a/crates/ide-plugin/src/wit_host.rs +++ b/crates/ide-plugin/src/wit_host.rs @@ -968,3 +968,10 @@ pub(crate) mod hover_bindings { with: { "openidea:plugin/types": super::bindings::openidea::plugin::types }, }); } + +pub(crate) mod inlay_bindings { + wasmtime::component::bindgen!({ + world: "inlay-hints", path: "../../wit", + with: { "openidea:plugin/types": super::bindings::openidea::plugin::types }, + }); +} diff --git a/crates/ide-plugin/tests/api_compat.rs b/crates/ide-plugin/tests/api_compat.rs index 40ec97c09..4ed1461aa 100644 --- a/crates/ide-plugin/tests/api_compat.rs +++ b/crates/ide-plugin/tests/api_compat.rs @@ -30,6 +30,7 @@ fn components() -> Vec { repo().join("crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm"), repo().join("crates/ide-plugin/tests/fixtures/semantic-tokens/dist/plugin.wasm"), repo().join("crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm"), + repo().join("crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm"), ]; let official = repo().join("plugins/official"); let mut dirs: Vec<_> = std::fs::read_dir(&official).unwrap().map(|e| e.unwrap().path()).collect(); diff --git a/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm index 4b6b98271a3f37953b1f74fa268ba52373b0d0ce..8f307470c3cb96fa68b35e977aae69634f3f8ca0 100644 GIT binary patch delta 152 zcmbQ$&o--{ZNr8f#ygug=5%v1u9<9G7Qi@X@`c%wo0pgUU}rosc|n~ESip9(QvG}( z#xI)>O+3NL*a8vXd|`GoBV)_vkh%L98P{wUp6|uTcw}?jf?1mt&6pS%lo=Qp&aw+I ogaBz3AYT!QuQpvWgmL}!gb+rS=_f)MJ6M783_05)Lm3^709!#cI{*Lx delta 151 zcmbQ$&o--{ZNr8f#v7YA=5%v1u9$3F7Qi@T@`c%wo0pgUU}roqc|n~ESip9(QvG}( z#t)khO+3NL*Z>jVd|`GoBV)tnkh%L98CPr;p6|uTcwlqff?1mt#hDlwlo=Qp&aw+I mgaBz)AYT!Q&pur;gmL}!Ga-x}oIqg)pfp3q_Q+61MCWX||u^I|7$My7_7n=d#QFfvZq?C9#v%-FNJ z*Q1-0@y=w)pa8}*lMhBoZf*|x!Or+)a!r&A77Kj*7k?rP} z b`RNAwj4ab9=ri761728b9?k?rP} z Y`RP;i8Eo_!7#aU;KHurZ$T(%QRCivdq8SqdgE9jH tgCeT{LkN&o0rC}*_-fNviZJ$1zaYZMGTlOyv4a&T&tS9tq$p#X9so{rGTs0H delta 152 zcmX@#!Fs-fbwf@#;oY2!KPLZ72w;3Md10pH=H$d5?2Hp8$7H%NzSz7l^NtYX zfz5R_CpeidtehO1=?qq7yZL0B8zbX|%>o_!7#V+TKHurZ$T(rMRCivdqBs)+gE9jH rgCeT{LkN&&1@aY<`0UeHiZJ$1w-IIR-~name()` call in a PHP document that declares +`function name(): Type`, shows `: Type` as a type hint. + +Used by `cargo test -p ide-host --test plugin_inlay_hints` to exercise the +optional `inlay-hints-provider` export. It is not a marketplace plugin. diff --git a/crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm new file mode 100644 index 0000000000000000000000000000000000000000..6be609a4e1f4ebb4143446679f31bfe86398f1e0 GIT binary patch literal 91543 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 -> Result, Error> { + Err(Error::NotFound(id.into())) + } +} + +impl InlayHints for Fixture { + fn inlay_hints(&mut self, doc: &Document, _range: Range) -> Vec { + let text = doc.text.as_str(); + let mut out = Vec::new(); + let mut rest = 0; + while let Some(i) = text[rest..].find("->") { + let start = rest + i + 2; + let end = text[start..] + .find(|c: char| !(c.is_ascii_alphanumeric() || c == '_')) + .map_or(text.len(), |n| start + n); + rest = end; + if end == start || !text[end..].starts_with("()") { + continue; + } + let name = &text[start..end]; + let declared = format!("function {name}(): "); + let Some(at) = text.find(&declared) else { continue }; + let ty: String = text[at + declared.len()..] + .chars() + .take_while(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '\\' | '?')) + .collect(); + if ty.is_empty() { + continue; + } + out.push( + InlayHint::new(text::position(text, end + 2), format!(": {ty}")) + .kind(InlayKind::Type) + .tooltip(format!("{name}() returns {ty}")), + ); + } + out + } +} + +register_plugin!(Fixture, inlay_hints); diff --git a/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm index db4a7ca32f6e35bbee2cd2d8e8d10a74f6cafe50..fa2d0592ea14068f435767352b4dd8b148ff20d6 100644 GIT binary patch delta 151 zcmccqoa6d)jtx)mGA`Nt>~1$F~1$FU^M-M?I{@}HGpYap 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 5b84fff77..371992907 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 @@ -165f1dc7ebf47b3910fd8e23eb72cecfe47156c3498d2b06f5d6c39ecd299e2a +81a1c6a7d78cf6ca6e463ffff6a4e243d668230a8d802028262ed5fc752f2f27 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 26a06522f..30108e4f2 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 @@ -5be61cbf46d28a7d06c73fb2562e81f3d1a86e16c5c6389d402cbeaf703e331a +c3b3e3c96d62106398299a6704e6a39fb18cfa734be877d1137f005d16553b4b 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 6d54047b70e3542376684b1292d1a1074a5a9749..e85457d1d9690c0ed180647a481114a71ca9ddc3 100644 GIT binary patch delta 2466 zcmZuy2~bm46#e%HLL{KEf&vx!L{zk*BTC$W;nyl7j)K;LqKI1yMVu7rgz?w3|qi`&a|X|Kf-pp`aFT?x=m$4?tB^KNf5YPqBUj&tP(ZqRDAoG+*P zfvo~3wv&xB=E6O4XGRd*qaQ?8@t`BdnZa-w(U%OEb(7b&(0^y?c<3PIpT)y7qKHVf!#!&k_sgxEyb(9-dcI# zMy;0Pl!9Dv5}d5f74<}yvI@I3C^Zo=Cv^c<@+9@?|H~v#R&DQA8bX^^cOp%{khAOh z!A^R6-4Gu35=ftgJ)NB%XW9Fb>*+aoUYzkM_Q}F7_B(`*TR#Q+O*1o-u_$8J&cwEd zZioPE!PbqNVK42uX@?BQs~{^1PSB@W?tll!>|o^OMhOgGOS`fxs)C5ZM4YM zGJoDd%?Y?D!tD(+k`Y@cVjGeySi030_ZPP&cI#`>wm{Ub+2#jEnzwDA99l_G?mWcY z+!*|B9_4P5yI-}$^5;F&V2^Q?ZrGd8<6QdWx%V^u%9`iTb84B*K@5B91MHMch>|8u3Cw zIAYHO-hHiZSpCkET|sKLnTiMSgENyEg|iWh3ojrh6(u3I6&>)f-BG9=1=&*KC}}>Fj{Atxg@{K=hXq>m5#$2T z%lbParL^8r%SAIuEgOoF^2Tz`CjBU2@uW zauMnmpL~EAQODw>)#dp}5jYL6=!JuU|MPEPS{L%hoMH(loMPkDerf`uPyJ6|E`C%G z*xARY*LR;xdc#mOEp5=))$h>L(Xa#6+B5SJ@19{Kl^Zc5ddsL~WXwIs(wlg`KRI-+ z0(X&(?CZ>FWYUJlAMHNhsXy7I8EdUZ&55iYtOXZ0OIiGTv;HnMDNZ$#H7MoCx`qvUiG>)~Hb367wrvCXlJl;bVKo$p+?yfO~> z_WaSZMTXMUYs;a8-oF;!7hch!kJ_Da>G`a|5nq?)=M{)+UsQpK4tkj&!#~CEUwhz# za|Th*&`aOS{HYF0&yI`WAQe7{BjO2Fp^hhZa7H_sdK5=%nHG=Hew(}mv`l7xdwu&I|J|3RO^QQJ zipnAt=;(e`urjs!Mm?uay3IH7dL|AT)BapbvhmA&1rsO zt-y(`WXsI?&`Mfo`a=tSe^xmU22vj$2Ly}@0>~=9uULl(a=Uz5n8nQ zC}KIJlk5n8Af zda^NkJqO9Xe5=Z#yA_Uu%H5nHT3bSbU@|Sf352-j?1NcrOR!y7x8z#tzSi=d?1rfmSY>$@JVoXt6ps z2HBE3134rw+r?r_T_G@&(|M6N-7h}?IXmAUnU)`pY{?&h?8pyBjw^^l?kX6AyigE| z?6J?QkIfCM-+78FNQZ5q;y(P~EM#WkT%^A60y3^B4tb+!pND;qN~;iL)9ks57LvI? z9!;L_AA%J0!;sVUer}GsrZ`N^BuC!|KUrI8P5Njw9Z}pDIi=VS`AxBa+*>>wZ5|a* z#s26LH{{fkG@NNFNydKIfhEX82Zs6C@)48)e6>u?huD7hQXHx)JHuOuQ|ScUESC0{ z1Q478c#Yb&N2?SqB(c;NBjuO&Lo-uppcLvDakG}izjN2$mWJFu$F!Mr{=Qvt9u@CL zD$e$_M)V@9FKoizlM7x**9Im@NCT7PWCJ_HXALn5{I`v2iejQ1Z65A?{hIZak>lI* zXY+O$&L&=6h0oxntb@rTzQ_~4vE z8W}x}*O@=fLFw6X5#&-qdGxQ>U+~asiUhdAS|2%dsQ6dccWYhY8nl`IafR<>z1wa1 zfWT`Cm=CquWXkFV6@wsscTfO~@b1fV+(3@w%A7RZTx=CM_USxCX3%p0 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 266382c9e..03d4d1ef9 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 @@ -81db471a9be4aefb97d21c3937bc0afd637114cc2703fa2cc815b0ae61608467 +fac2994442eb96b09dea926546d2bacde333615392bc59fd70df73999ae84f62 diff --git a/crates/ide-plugin/tests/fixtures/storage/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/storage/dist/plugin.wasm index 21b4a80e6f652c9b06a6bc60c5485d757b324eb3..36c158c773443f07f54fc215bbfeccd91b4070cd 100644 GIT binary patch delta 145 zcmaEMkmcDymJJd%j9WHK+H`X=woG2?8o>Bvvag@yW`4IH?2L0JbNIP1e%b8ncSnfv z%x0~`6P!$UR!-*ha|Wxj-CUaO#>jYQ^PZf2jEpUttMj}V8Ru+1mjAj{(Ts_KL79Pp l!HHFXAp}UP0QrhYe6`7!zRsVV`>lr+D8b;end$ozO#nv0Fq{AY delta 149 zcmaEMkmcDymJJd%j2kvf+H`X=HcVdX8o>Brvag@yW`4IH?2I!ebNIP1e%S2mcSnfv z#AdC;6P!#pR!-*ha|Wxj-CUaO#>jYM^PZf2jEoJNtMj}V8E0%hmjAj{QJjf^L79Pp o!HHFXAp}UX0{MzaeD=wgzRsUq`mKW#D98YmWN_Hb^! Vec { + vec![InlayHint::new(range.start, ": string").kind(InlayKind::Type).tooltip("returns string")] + } +} +``` + +`range` is the visible part of the document; hints outside it are dropped. A +hint's kind (`type`, `parameter` or `other`) decides which Inlay Hints setting +shows it. The host asks the language server and the plugins together, under +the edit-path budget, and shows both; the editor asks again after edits and +scrolling. Labels are cut at 256 bytes and tooltips at 1 KiB. + ### Network and secrets `host::fetch` makes an HTTPS request (plain HTTP only to `localhost`) to a host diff --git a/plugins/official/ai-completion/dist/plugin.wasm b/plugins/official/ai-completion/dist/plugin.wasm index 022900fe1bbbc3714d39a08ce6531481f85e1856..191a729ce8b38683caee544ea7fe91f90d23156c 100644 GIT binary patch delta 2664 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 O@(c&IdwMgmdIJF4T{Kz% diff --git a/plugins/official/checkstyle/dist/plugin.wasm.sources b/plugins/official/checkstyle/dist/plugin.wasm.sources index 1c67258b2..d18c3eb6f 100644 --- a/plugins/official/checkstyle/dist/plugin.wasm.sources +++ b/plugins/official/checkstyle/dist/plugin.wasm.sources @@ -1 +1 @@ -8ee84d6a4a835ec7104877fa105e593b47e8091339b3419690b018f3e8877b94 +9a89ec93d158e460998cf472837d7e0e8aa76d5441cb1dd24c3184bd814a29c1 diff --git a/plugins/official/csv-editor/dist/plugin.wasm b/plugins/official/csv-editor/dist/plugin.wasm index 9543406a8bf8e77836993259f3488651a0077018..4f93512a0bba269f150caa43e59cba0f7a8ef8c6 100644 GIT binary patch delta 173 zcmZ2FiDT&`jt#$WF|OGB=T;#j-ZTZH@Cj} z!Ol2ma@{8vh=}c`&+~;C&um}7%6Nj4>CXAd^`D#>zibDpZDwS=v)xFLaUUaN%l3~# zj9!e4bGF-vFjj0=G-G05P-b9Yu;&zD2m#V6K)xapUv2u;(~S402b^JKnZD%=;|f-w NJcG;j)U%8StN{?TJ#qj5 delta 172 zcmZ2FiDT&`jt#$WF)rEs=T;#j-ZTZH@Cj} z!Ol2ia@{8vh=}c`&+~;CPi$Yn%6Nj4>BjlV^`D#>KWqo8ZDwS=vE4|JaUUaN!}gCt zj9!e4Gq&4^Fjj0=6lY>!P-b9Yu;&zD2m#WpK)xappMCn((~S40?>NJ_f)gmr0F-8M L*q(Zp@qje|n|nJg diff --git a/plugins/official/csv-editor/dist/plugin.wasm.sources b/plugins/official/csv-editor/dist/plugin.wasm.sources index a99cce3f5..da4a2a37d 100644 --- a/plugins/official/csv-editor/dist/plugin.wasm.sources +++ b/plugins/official/csv-editor/dist/plugin.wasm.sources @@ -1 +1 @@ -2af4ae2589bebf9703afa6b7b24bc073334344e6836b0ea29aab259338b38762 +652557045106ab5bd1126cb8e05965d030c4aa28c01ede31be3fe285aab98a67 diff --git a/plugins/official/git-toolbox/dist/plugin.wasm b/plugins/official/git-toolbox/dist/plugin.wasm index 806daedea2753f143b3b5a96ce0a649817474da5..b238f5dd66adb45ea7bc28c3447365412bd815ef 100644 GIT binary patch delta 154 zcmZp_!`6O>ZG(IjW5;HNDp^Lxp3QdE%QzYDOn%xDz<6eIZ?EKL|JEPuj9(^u^tv#f z+1%TkE6TWJ^Y29`I2pG<#DI!yH@mHMV`SX2xp&<@M#ejveKvS8GJe@SePj0lMKdM_ s24w~YhD;6th7cgF0^}ZG(IjW6NfRDp^Lxj?H$}%QzWtOn%xDz<6SEZ?EKL|JEPuj2|X@^tv#f z*xcKjE6TWF^Y29`I2kuU#DI!yH@mHMV`SX0xp&<@M#dYPeKvS8GJe=RePj0lMR6tu v24w~YhD;6th7cgl3gjyy@!6+47crinE>_IAfD7@>Mq{= zj8nG%;%7X;$+!d}2vlXe-BpUwjgfK5_8w`*eT66)^B@SKY^GV+8=cqd7?c delta 157 zcmaF(f&JkJ_6;vyG5*;6@>Mq{= zj1#v1;%7X;$+!R_2vlXe-BpUwjgfJ|_8w`*eTYd vG6Ms{c6I@V5FpJ8BY#{viZ@f|7#V^m>3w885kHA mu?sMS0BIEBY#{u=&xd|7#V+nHU(985kHA pu?sMS0BKerUlEDVKAkC&ar^YWk&GRjKw$=;G=s)=uP8=ET>#H;Fi8La diff --git a/plugins/official/key-promoter/dist/plugin.wasm.sources b/plugins/official/key-promoter/dist/plugin.wasm.sources index a71ed00b0..0686d1aa4 100644 --- a/plugins/official/key-promoter/dist/plugin.wasm.sources +++ b/plugins/official/key-promoter/dist/plugin.wasm.sources @@ -1 +1 @@ -31ff37106dc0829e226de01b5148f6ee67b1d776acd6def4729edf4c63946219 +3225880631e679780c3c7eb09683b598bb3803fcf7be7b6940369eada633c48f diff --git a/plugins/official/license-header/dist/plugin.wasm b/plugins/official/license-header/dist/plugin.wasm index 6249ee735e5090f78ead19ccd4c1be0bcd885141..d4729e4a7eb41c958f0555339f1990cf11b39d96 100644 GIT binary patch delta 163 zcmZ3tf^*#p&W0_F0*s7HwhJ;cF5_hUGJOXZV*ulm=?TJ&lG~NI8Go=d_Dq)+W^`eE zvK=UvE6TWM`#BB96P!$EZcLXEW^{(AwcRdh%;?6*cxHQo3FAIS#xL9D%^1BH8GE*8 znKP!JQZ!>?U{GdYV3^1uzz_nYRe*d&B);18PmE0ar>|vV>R|;+F>KlH#LV<20068q BEN=h+ delta 167 zcmZ3tf^*#p&W0_F0*s6cwhJ;cF5_hUFntFXV*ulW=?TJ&lG~NI8Go=dc1)KRW^`eE zupKCtE6TWI`#BB96P!#ZZcLXEW^{(AwcRdh%;?6*cw&2k3FAIS#t+-&%^1BH89TOT znKP!JQWR%mU{GdYV3^1uzz_nYS%G{-BtHA}PmE0ar*CCq>fi(lGXSL-Hf(odW_l9< E05A3{>Hq)$ diff --git a/plugins/official/license-header/dist/plugin.wasm.sources b/plugins/official/license-header/dist/plugin.wasm.sources index 737b70078..e9fd184a4 100644 --- a/plugins/official/license-header/dist/plugin.wasm.sources +++ b/plugins/official/license-header/dist/plugin.wasm.sources @@ -1 +1 @@ -eb4d09a79172cb46857c7bb3e50e7fbd6f4b6be379bb1c79a41ea23a5643f875 +a59623542b4df024f5a85bc2cd0e046189044a0c292c793384fdb5465e8eec48 diff --git a/plugins/official/mybatis/dist/plugin.wasm b/plugins/official/mybatis/dist/plugin.wasm index 92a8b77a309df63ca8ae8e7af40346ce2084d0b6..736d37b3364d7e0a3735df8230ae7a78509e6058 100644 GIT binary patch delta 202 zcmZ3rm}}Kyu7)j)KXn-wZ2zUp=*+~pVY;0GqdDV&?PUgxrJRfxw!g7tEM#PSuszP2 zQJRJE&h{#M#$}w0Yo;rDG6pct*U{GdYV6f&AU5?^h5tT)qors)fOnOLUt_%YpJ1u9_3+5Xm#X|EFiw?aKM delta 201 zcmZ3rm}}Kyu7)j)KXn=BZ2zUp=*+~pX1bjLqdDWA?PUgxrJRgsw!g7tEM#Q7vpvq5 zQJRJE#`Y?E#$}w0E2b-YG6pct*k0txD9FKhV7fvPqYIFGGLW%XjPb+v@C?QioQw@n z;psqO+wF^r7~L2d8@9hJX57cfxMKT?QbsRE#sk|wmoa)?Q50ulU{GdYV6f&AUvi-s1)7N!>F7M2#)7Pc+yT1AX6wrdx$J2Nr)0o7GG3Yfpq)K{@yK?=4t7Bf#y8W?uVr^(JTe`~m7K1-j$LK@(FyDh z!c23PPX8FlF0oyH3Hy3Z#x=;wwgZ)AF*2^%K4U%mK1RkX+ru}qdoeP;X&Q!3tEwAhJF82d}9;0570ZX#fBK delta 243 zcmZ4V-eS>vi-s1)7N!>F7M2#)7Pc+yT1AXcwrdx$J2NqUnVw(5ZqC@UeR&CcHz!lW zvgrX_9MaqU>)0o7GG3Vepq)K{@xXS&4t7Bf#uwAiuVr^(JTM)|m7K1-j$LK@(FyDh z!b~%kPX8FlF0oyH3Hy3Z#udoQwgZ)AF*2^$K4U%mK1Rk1+ru}qdoeP;XkWC69f&!A zm=lP(fS7yxqD?$a#){%h3=GN)3=H#?1sFnrG%Jv=h{R`acYVgQ-Sru7y5RH;-+6a% S0_7NhY8V8z=lb>;p^c?AwX>Knfiyg@ei_<+u`Cr%?o7ra6X{^ndvEiAhW2yMj zld+;^g@~KIQm9Tt=1h2%R9^8A9x9Dc+V^+geNU|O&iQO!P2@ab~=K)@rqk7o$p{l4E!iY1}lI)m|e(aRIEh zURV})h=mF=U~>?d5xvNo&DD@DeDKRr7Mh77aRF>6k%?+F*_^l#lF7rwQHTSRRzns^ zOX>$%!igj&K=s+=FeoN5sUE_Jts}rW>l&>=brSP44B2|pjcnXj47tK2GK&QhN!{@y z93_2ILLigOPw~d}`jix`z+E>5;>ki?kW2gx8i0qf3@y8f9M<_l6S=AzJ}9|P44t|j zLvu2op%yIP-JV7JcXqx+`=P1tL$MH(S}nt-LiU6LWD3c@0rZZqr9mcH8RFxGeQE9h z?W8dM9JC9ud-wKWLo^vtaE_#91VIC-%UI&w{3g?C;4NpISQhYR;j7FHKq_7Kk94dM zE9Zc35T@;q2RJ8G{-NzD&*JQ8%yTK5Kpt6i;Jd&wv07D;$flBW;AEW4a<#m{qOuUJ zt=NN6hz|~a3OmWPLy!L7JDHY~(BtVi!RK&0Qbz_Hb9czC7kR&= z%bf_W!t7&CEaa2k$Gyn@)uY$GQh(G|mJY-Ld0v_-hc>dk{A0xC z@(5@XLeFoI_pK1K4Po8YV4IW*p`l_AJIriTvCa$|EKy!|n{qUSWz;gOgW8TSBL}Vw z#5OKoi9`%EOh?>kNOH2oP}_6VgsY6e;~y$Dh({_%A>OJCM+~eQfEZCV9xJ>9IO*r^>SI)hY+SxB$O2h&V)uFgSZYtA5+*F+-*TuXDe`Z!>A*<@?2 zyqVm;wggQ+s`Wu!S34B(K&`*4t*?nT(=yUr>ju`={OT5XSYzhP)OKiRBgITI>-;fL zbsdM;UKfU5lj|qpI=S8z@%Q>oc;a{cCtPP={|wR5IMmY;jgxEGJeiUWw*0Kkk#HmR zTx=YJm~QMZalk1wtV(GyQpcn(G;?c6w=!T zwyhk@8KM6I-t6QjYkRJy_K7c=+CSCU)UVMq^Jx;QjZZ&83~Hw&DV||PLf|tkB_sbi zEpJz6e`5C{8&`*4&+89m9xmuqn~qstRlc1lXfH)bRUv`A0kf{$=LWmpe*2jB+$r0sJKR9I%c{)FT$VO4+ z4>@4c$Bu+Z8MNul8gPd)-P?gE45H{~Xb?XZXQeNe6K5xNSEdgOghebH+o;!%fs5iI zJ_zcReRhj39B{0vkh&?Ar}ZmC;jj-R>wIEhn3slS7;gsu)+kiW9Fzlb@6~PZqhB5a l8TPt9DE=vXS= zbY-k4CPMRNw{%%`&M3QQ{`E(?skn$jr4fof?{~j@#X9$#-}AoD^SsZW-}ij)cej`n z8%&Da1&**!Sm-!Qu{N)vh*7O&c%vjuk~|>EW=YDaQ@gYQy4i8fdh#{8ak4|Qq&}Q( zH9L#G(!m_bm?B9RF|0-4UDvPhGB*_In3$mqd>g6K_FZ?1wJ!uvn}&O#$mCeDKd5}l|)m(7XuA)eez9EmtE zX%%FU^rXI!A)H8Z1~8E`$Ucru*@ zGfCV217wpvso}7T%uV&d^}5tltiVG*5p-m}KEzdbjRxRh3`5JVCr9-DP){!FhYgB1 zi=orjVQ5aqGt`6SyZf`~|K^To7;i}0dtek|)2d|HRQT=)fL%iJuK>MtH8jY08$-NK z*qiPF&_W6`N})xF+q0(!AEM1%1Vto0GXyRZbLL{7##fov0B_ZCVp+gjgfFu)0jYG| zKisKQtegY>vJkmH9-v65_)XVSo`u;lnCC(^fgG~n;J3j>v0AlJWK+dCaxzY4eYCvE zYO)fo;uM^cQ`$_#_0TeWT5*RKA>KXo38aw7ocsUpokSi^=utaX@IBIj)R6(lJRA?y zio9RY=Z1lsFyojr3%R7%ac{CNFA(yHkT+M{AH{X?@fnb1B_Bs(EQyG8CItm*qRuZQ zUm0Q{OSo!SjQx{I1yNuUb{9+pd{~Vq;viR8QkW#eP1mVuxS8^2A8PrcC9;#^PA#+h+B%^K`awdUgC}G!6l1&JhitZ2(1+*10i2{Qj(^CW|C6& z5n@Z(ENB)Y&aPMVDHXH%u^t+*PfDp!e||SR)M7WWE({wgQEt3RIr3u}jm&mHrq z!Ak?NjdPcx5rfMoBW@^9a<<0M$T{l5Rm?*5_Z3>iqZK0&Z&XAf23HP1oK^WQVru0a z#Ma8;h=Z!a5tmeXx!X!{&^U#;fK-V^$f&{>(?Skko{7j-pGGXJjzJu8CEdez#u2m2 zCfI7_Eu`tnVs!bi#ussI%@D+cH9>CnzQXLTM$%H_j<3A6wE*)xPg~4tjZZ)rws2+Tsc0A|$3dD=o zM#|JYe~);Mso$uF{yR?8-IS$X3v#Oxc@i0g0C0Ds(EjX2`gP{hw~)gp#AQlGCI z%MrtF(?M8yI~4K6?L}_320X&Mh1T8~Za27fH>|$16wkHXIg41>M58t}84(XO)66x^ zH1q7cBfugg-u0rBlylDq@%%k>-FE*kT%NxFuA{^dklZ#uR8_Zmk?Ac7_KYu;($+$U z+rO2wkF1+U(Y>U3t)HPoaqBI_>^53bb=xlkq!f&nb#%o^z|X;RnAEL9@_-h6`T?Dv zaqVLe*R+>`B{Sk*z$ZK5;hLVSsd?y+uJT7(yZI%0ranqSbKRql5kopCNy^7qkr4b? zN6E;4Ld)CP)t@*#&BoP{r}PEA`;=Z082?7C&6oJGn#iA$%en9+-zd$tBwQ=WSvE`ClaNq>%%Z0`^F439@av>Z%^ z0y)e9Fzelv5a85aM{g^ zf@~C(L2ww%hPdGnErVu#vlcwSsDC3Eg-H~BP4(i(;*9jga^@VQ?u>?M!LWd3JL?UG z(Qr;&#D{=c)jLHz!U4ys3+XYH%3xR#0Y`ixUhf+VL%m;T8ODdfzcorVGZUp8_uk$1 mO2e{P$dv2*Er)n&W1I~+%V99U+lKP*U-75o diff --git a/plugins/official/solidity/dist/plugin.wasm.sources b/plugins/official/solidity/dist/plugin.wasm.sources index b75a15c60..3d6de62b0 100644 --- a/plugins/official/solidity/dist/plugin.wasm.sources +++ b/plugins/official/solidity/dist/plugin.wasm.sources @@ -1 +1 @@ -13e34d174abf5187bf7c191736974305afb0af257fc103eec96fe8fa8edb8c88 +178862630cf39bca56f5d54cd135dcac4a1ab74a3e10356163d00e5a718ae728 diff --git a/plugins/official/string-tools/dist/plugin.wasm b/plugins/official/string-tools/dist/plugin.wasm index 98c358652c91d3e6b235446db846d9cdcff5491a..e853e15dc65fbd83cad858339593d2665f3bf911 100644 GIT binary patch delta 207 zcmZ48&$YUrt6>YHg9Ky8c1H7oPI2f=?*JUjv-|E>To6l0K`>2j{pDw delta 211 zcmZ48&$YUrt6>YHg9KyCc1H7oPI8h=>{iIoB=4$5U_l8IFkVYfN?zb diff --git a/plugins/official/string-tools/dist/plugin.wasm.sources b/plugins/official/string-tools/dist/plugin.wasm.sources index 5967abb8d..0b4279f8f 100644 --- a/plugins/official/string-tools/dist/plugin.wasm.sources +++ b/plugins/official/string-tools/dist/plugin.wasm.sources @@ -1 +1 @@ -7e010175c19a159a22c42a76ca07ebc663a27ce9143c21e742ca99435a538f83 +a6c188aed4fc57cd9955d6b47ef715c52dc3f8c7b69f7d18ea71b3932888710f diff --git a/sdk/rust/openidea-plugin/src/inlay_hints.rs b/sdk/rust/openidea-plugin/src/inlay_hints.rs new file mode 100644 index 000000000..c9febbf44 --- /dev/null +++ b/sdk/rust/openidea-plugin/src/inlay_hints.rs @@ -0,0 +1,84 @@ +use crate::types::{Position, Range}; + +pub use crate::providers::Document; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum InlayKind { + Type, + Parameter, + Other, +} + +impl InlayKind { + pub fn as_str(self) -> &'static str { + match self { + Self::Type => "type", + Self::Parameter => "parameter", + Self::Other => "other", + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct InlayHint { + pub position: Position, + pub label: String, + pub kind: InlayKind, + pub tooltip: Option, + pub padding_left: bool, + pub padding_right: bool, +} + +impl InlayHint { + pub fn new(position: Position, label: impl Into) -> Self { + Self { + position, + label: label.into(), + kind: InlayKind::Other, + tooltip: None, + padding_left: false, + padding_right: false, + } + } + + pub fn kind(mut self, kind: InlayKind) -> Self { + self.kind = kind; + self + } + + pub fn tooltip(mut self, tooltip: impl Into) -> Self { + self.tooltip = Some(tooltip.into()); + self + } + + pub fn padding_left(mut self) -> Self { + self.padding_left = true; + self + } + + pub fn padding_right(mut self) -> Self { + self.padding_right = true; + self + } +} + +pub trait InlayHints { + fn inlay_hints(&mut self, doc: &Document, range: Range) -> Vec; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn a_hint_is_built_step_by_step() { + let at = Position { line: 3, character: 14 }; + let hint = InlayHint::new(at, "GET /orders").kind(InlayKind::Type).tooltip("orders.index").padding_left(); + assert_eq!(hint.position, at); + assert_eq!(hint.kind.as_str(), "type"); + assert_eq!(hint.tooltip.as_deref(), Some("orders.index")); + assert!(hint.padding_left && !hint.padding_right); + assert_eq!(InlayHint::new(at, "x").kind, InlayKind::Other); + } +} diff --git a/sdk/rust/openidea-plugin/src/lib.rs b/sdk/rust/openidea-plugin/src/lib.rs index 15647e5ff..3af96e16c 100644 --- a/sdk/rust/openidea-plugin/src/lib.rs +++ b/sdk/rust/openidea-plugin/src/lib.rs @@ -44,6 +44,7 @@ pub mod details; pub mod host; pub mod hover; pub mod indexing; +pub mod inlay_hints; #[cfg(not(target_arch = "wasm32"))] pub mod mock; pub mod providers; @@ -185,6 +186,17 @@ pub mod commit_checks_bindings { }); } +#[cfg(target_arch = "wasm32")] +#[doc(hidden)] +pub mod inlay_bindings { + wit_bindgen::generate!({ + 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.3.0": crate::bindings::openidea::plugin::types }, + }); +} + /// The plugin API version this SDK targets. pub const API_VERSION: &str = "1.3"; @@ -251,6 +263,9 @@ macro_rules! __optional_exports { (commit_checks, $plugin:ty, $component:ident) => { $crate::__commit_checks_exports!($plugin, $component); }; + (inlay_hints, $plugin:ty, $component:ident) => { + $crate::__inlay_hints_exports!($plugin, $component); + }; (syntax, $plugin:ty, $component:ident) => { impl $crate::syntax_bindings::exports::openidea::plugin::syntax_provider::Guest for $component { fn highlights( @@ -457,6 +472,22 @@ macro_rules! __hover_exports { }; } +#[doc(hidden)] +#[macro_export] +macro_rules! __inlay_hints_exports { + ($plugin:ty, $component:ident) => { + impl $crate::inlay_bindings::exports::openidea::plugin::inlay_hints_provider::Guest for $component { + fn inlay_hints( + doc: $crate::inlay_bindings::exports::openidea::plugin::inlay_hints_provider::Document, + range: $crate::bindings::openidea::plugin::types::Range, + ) -> ::std::vec::Vec<$crate::inlay_bindings::exports::openidea::plugin::inlay_hints_provider::InlayHint> { + $crate::__rt::inlay_hints::<$plugin>(doc, range) + } + } + $crate::inlay_bindings::__export_inlay_hints_world!($component with_types_in $crate::inlay_bindings); + }; +} + #[doc(hidden)] #[macro_export] macro_rules! __plugin_exports { @@ -722,6 +753,25 @@ pub mod __rt { .map(|h| w::HoverContent { markdown: h.markdown, range: h.range.map(crate::host::__range_to) }) } + pub fn inlay_hints( + d: crate::inlay_bindings::exports::openidea::plugin::inlay_hints_provider::Document, + range: b::types::Range, + ) -> Vec { + use crate::inlay_bindings::exports::openidea::plugin::inlay_hints_provider as w; + let doc = crate::providers::Document { path: d.path, language: d.language, version: d.version, text: d.text }; + with::(|p| p.inlay_hints(&doc, crate::host::__range_from(range))) + .into_iter() + .map(|h| w::InlayHint { + position: b::types::Position { line: h.position.line, character: h.position.character }, + label: h.label, + kind: h.kind.as_str().into(), + tooltip: h.tooltip, + padding_left: h.padding_left, + padding_right: h.padding_right, + }) + .collect() + } + // ---- views ---- use crate::views as v; diff --git a/sdk/rust/openidea-plugin/wit/inlay.wit b/sdk/rust/openidea-plugin/wit/inlay.wit new file mode 100644 index 000000000..ab00b0c9b --- /dev/null +++ b/sdk/rust/openidea-plugin/wit/inlay.wit @@ -0,0 +1,39 @@ +package openidea:plugin@1.3.0; + +/// Optional export (world `plugin-inlay-hints`): inline hints in documents of +/// the languages declared with the `inlay-hints` provider kind, including +/// languages the plugin doesn't contribute. They are shown next to the +/// language server's hints. +interface inlay-hints-provider { + use types.{position, range}; + + /// The document the hints are for (the editor's text while it is open). + record document { + path: string, + language: string, + version: u64, + text: string, + } + + /// A hint shown at `position`, before the character there. + record inlay-hint { + /// UTF-16 position (0-based line and column). + position: position, + /// The text shown, e.g. `GET /orders` or `: string`. + label: string, + /// `type` or `parameter` follow the Inlay Hints settings of that kind; + /// anything else counts as `other`. + kind: string, + /// Shown when the pointer rests on the hint. + tooltip: option, + /// A space before the label. + padding-left: bool, + /// A space after the label. + padding-right: bool, + } + + /// The hints within `range` (the visible part of the document), under the + /// edit-path budget. Hints outside `range` are dropped; at most 10,000 per + /// call. + inlay-hints: func(doc: document, range: range) -> list; +} diff --git a/sdk/rust/openidea-plugin/wit/plugin.wit b/sdk/rust/openidea-plugin/wit/plugin.wit index b01f7a96f..19c6dc9e1 100644 --- a/sdk/rust/openidea-plugin/wit/plugin.wit +++ b/sdk/rust/openidea-plugin/wit/plugin.wit @@ -67,6 +67,12 @@ world plugin-hover { export hover-provider; } +/// A plugin with inlay hints (`inlay-hints` under `[[contributes.providers]]`). +world plugin-inlay-hints { + include plugin; + export inlay-hints-provider; +} + /// A plugin whose views show in the Services tool window /// (`location = "services"`): their nodes' detail pane. world plugin-details { @@ -109,6 +115,11 @@ world hover { export hover-provider; } +/// Only the `inlay-hints-provider` export, generated next to `plugin` like `syntax`. +world inlay-hints { + export inlay-hints-provider; +} + /// Only the `detail-provider` export, generated next to `plugin` like `syntax`. world details { export detail-provider; diff --git a/wit/inlay.wit b/wit/inlay.wit new file mode 100644 index 000000000..ab00b0c9b --- /dev/null +++ b/wit/inlay.wit @@ -0,0 +1,39 @@ +package openidea:plugin@1.3.0; + +/// Optional export (world `plugin-inlay-hints`): inline hints in documents of +/// the languages declared with the `inlay-hints` provider kind, including +/// languages the plugin doesn't contribute. They are shown next to the +/// language server's hints. +interface inlay-hints-provider { + use types.{position, range}; + + /// The document the hints are for (the editor's text while it is open). + record document { + path: string, + language: string, + version: u64, + text: string, + } + + /// A hint shown at `position`, before the character there. + record inlay-hint { + /// UTF-16 position (0-based line and column). + position: position, + /// The text shown, e.g. `GET /orders` or `: string`. + label: string, + /// `type` or `parameter` follow the Inlay Hints settings of that kind; + /// anything else counts as `other`. + kind: string, + /// Shown when the pointer rests on the hint. + tooltip: option, + /// A space before the label. + padding-left: bool, + /// A space after the label. + padding-right: bool, + } + + /// The hints within `range` (the visible part of the document), under the + /// edit-path budget. Hints outside `range` are dropped; at most 10,000 per + /// call. + inlay-hints: func(doc: document, range: range) -> list; +} diff --git a/wit/plugin.wit b/wit/plugin.wit index b01f7a96f..19c6dc9e1 100644 --- a/wit/plugin.wit +++ b/wit/plugin.wit @@ -67,6 +67,12 @@ world plugin-hover { export hover-provider; } +/// A plugin with inlay hints (`inlay-hints` under `[[contributes.providers]]`). +world plugin-inlay-hints { + include plugin; + export inlay-hints-provider; +} + /// A plugin whose views show in the Services tool window /// (`location = "services"`): their nodes' detail pane. world plugin-details { @@ -109,6 +115,11 @@ world hover { export hover-provider; } +/// Only the `inlay-hints-provider` export, generated next to `plugin` like `syntax`. +world inlay-hints { + export inlay-hints-provider; +} + /// Only the `detail-provider` export, generated next to `plugin` like `syntax`. world details { export detail-provider;