дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 11:15:40 +02:00
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 <[email protected]>
Цей коміт міститься в:
1 джерело
c614355753
коміт
56796fb2c6
60 files changed
+953
-27
No files matched your search
@@ -677,7 +677,22 @@ pub async fn lsp_format(
|
||||
}
|
||||
|
||||
pub async fn lsp_inlay_hints(host: &Host, window: &str, doc: DocId, range: Range) -> IdeResult<Vec<InlayHint>> {
|
||||
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<Option<SemanticTokens>> {
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
use ide_core::Range;
|
||||
use ide_lsp::types::{InlayHint, InlayKind};
|
||||
use ide_plugin::PluginInlayHint;
|
||||
|
||||
pub(crate) fn merge(server: Vec<InlayHint>, plugin: Vec<PluginInlayHint>, range: Range) -> Vec<InlayHint> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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<PluginInlayHint> {
|
||||
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.
|
||||
|
||||
@@ -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 = "<?php\nclass Order\n{\n public function user(): ?User {}\n}\n$order->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);
|
||||
}
|
||||
@@ -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<wit_detail::Guest>,
|
||||
hover: Option<wit_hover::Guest>,
|
||||
commit_checks: Option<wit_cc::Guest>,
|
||||
inlay: Option<wit_inlay::Guest>,
|
||||
}
|
||||
|
||||
/// 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<Vec<PluginInlayHint>, 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 {
|
||||
|
||||
@@ -466,6 +466,16 @@ pub struct PluginHover {
|
||||
pub range: Option<ide_core::Range>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct PluginInlayHint {
|
||||
pub position: ide_core::Pos,
|
||||
pub label: String,
|
||||
pub kind: String,
|
||||
pub tooltip: Option<String>,
|
||||
pub padding_left: bool,
|
||||
pub padding_right: bool,
|
||||
}
|
||||
|
||||
// ---- declarative views (wit/views.wit) ---------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)]
|
||||
|
||||
@@ -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 },
|
||||
});
|
||||
}
|
||||
@@ -30,6 +30,7 @@ fn components() -> Vec<PathBuf> {
|
||||
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();
|
||||
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
09a64c18ef137348f70121c088419040ee606089d9f27b0dbe63256d5de49cd6
|
||||
9cdb9b328e2b72c2f8849548b1d5bc119044acb4136dac614319138db2e4890d
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
8eba458c25d7722ffc1d396812bd83114a67c1176b9dd6fa3bfbbf02c0ca0976
|
||||
32634dd8c1172c6980cd1c4f9345fb2ed7b2e5cbacfd061bcefaa7fa9aad162e
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
5f7112055543becca6fc9ad9a33850c355efe38b3ab019ecb4fb26346f3ac9b5
|
||||
b7b63faf536d558cafc375a17765823015863e47d897e2128d453fbc8975e19c
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
1f571177a22c2f07055b38385c0e5f44c063c520b9e1d7be3d528e4cfb3b8b43
|
||||
809180a1c4ba29988af7b1ead1c31c1a3018fe3a1c266930ba588f34de1d3319
|
||||
+333
@@ -0,0 +1,333 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d15f86d12da44cd8d4ec3c73984537eb7f4cadcb9776a575a0a8a05ca11005c"
|
||||
|
||||
[[package]]
|
||||
name = "macro-string"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e7b3a5bbb2f63aaa223a671ea1bd0e7bc16bd30ce387324cf63995deaddbbd2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||
|
||||
[[package]]
|
||||
name = "openidea-plugin"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openidea-test-inlay-hints"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"openidea-plugin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bfe0f4c752e450fc2faf62654f1c134747922825d5b04ca717b8874f41a40c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.154"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "3.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.262.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4495a6f85dd94c411c749303efcbc5defade0b0cbdbc0541495241ab917a9b5a"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.262.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c86ff6668190d9fb70372b306b4f9fe8930f0e2f57ce72e4922bf2f117900949"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.262.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "974d643b125b272607e2fff8012ea14a8328acd7621e4bafa7afb32500052b1b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.63.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b85a987ab61d365382d25c7d47e26edd48941b3bf6e3fbf76eff918bc71dab06"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.63.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "775e122e390603abfacd548027016cd0113a6e2bf2f8ce6bdd3658b013456ea8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.63.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84e26e13b9d972026778d2ffb7985736de116026fdc8d9464f966e12df2eb240"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.63.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16a008a0d6b4840677326beb4edbeb210a2f1b7d0183fb7cc33f466b90fd7fb6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"macro-string",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.262.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "078def49fdb49cab405c3006e05793b3fdaf7069126ba6b9c97d01ec8c98fe4c"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.262.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00f0dc051466ffe46787721fca3d088536f158ae400be044f1b1c386ba9397ad"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hashbrown",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-ident",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
|
||||
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "openidea-test-inlay-hints"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
|
||||
[workspace]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
openidea-plugin = { path = "../../../../../sdk/rust/openidea-plugin" }
|
||||
|
||||
[profile.release]
|
||||
opt-level = "s"
|
||||
lto = true
|
||||
strip = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
@@ -0,0 +1,7 @@
|
||||
# Inlay hints test fixture
|
||||
|
||||
After every `->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.
|
||||
Бінарний файл не відображається.
@@ -0,0 +1 @@
|
||||
6fd9e638afa22170ef2e7a6bd7351bd5e0200d6726cf5d5835be7c5c7cfa19dc
|
||||
@@ -0,0 +1,9 @@
|
||||
id = "test.inlay-hints"
|
||||
name = "Inlay hints fixture"
|
||||
version = "0.1.0"
|
||||
engines.openidea = "^1.3"
|
||||
wasm = "dist/plugin.wasm"
|
||||
|
||||
[[contributes.providers]]
|
||||
kinds = ["inlay-hints"]
|
||||
languages = ["php"]
|
||||
@@ -0,0 +1,47 @@
|
||||
use openidea_plugin::inlay_hints::{Document, InlayHint, InlayHints, InlayKind};
|
||||
use openidea_plugin::{Error, Invocation, Plugin, Range, register_plugin, text};
|
||||
|
||||
#[derive(Default)]
|
||||
struct Fixture;
|
||||
|
||||
impl Plugin for Fixture {
|
||||
fn run_command(&mut self, id: &str, _: &Invocation, _: Option<&str>) -> Result<Option<String>, Error> {
|
||||
Err(Error::NotFound(id.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl InlayHints for Fixture {
|
||||
fn inlay_hints(&mut self, doc: &Document, _range: Range) -> Vec<InlayHint> {
|
||||
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);
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
165f1dc7ebf47b3910fd8e23eb72cecfe47156c3498d2b06f5d6c39ecd299e2a
|
||||
81a1c6a7d78cf6ca6e463ffff6a4e243d668230a8d802028262ed5fc752f2f27
|
||||
@@ -1 +1 @@
|
||||
5be61cbf46d28a7d06c73fb2562e81f3d1a86e16c5c6389d402cbeaf703e331a
|
||||
c3b3e3c96d62106398299a6704e6a39fb18cfa734be877d1137f005d16553b4b
|
||||
Бінарний файл не відображається.
+1
-1
@@ -1 +1 @@
|
||||
81db471a9be4aefb97d21c3937bc0afd637114cc2703fa2cc815b0ae61608467
|
||||
fac2994442eb96b09dea926546d2bacde333615392bc59fd70df73999ae84f62
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
cbb5a54e287ef3ed11391985c286e5d4e24646f2fd84b8b969fe825bbd44c350
|
||||
2a97235992302d0dfda55a1774883c046fe8aed6353b0f047aafa8bfe0155fa3
|
||||
@@ -34,7 +34,7 @@ plugins/
|
||||
├─ regex-tester/ # html view talking to its code, settings, the editor's selection
|
||||
└─ license-header/ # file-opened events, quick fix, project scan in a task
|
||||
sdk/rust/openidea-plugin/ # the Rust SDK (host API, Plugin trait, mock host)
|
||||
crates/ide-plugin/tests/fixtures/ # test plugins: hello, jobs, language-toy, misbehave, semantic-tokens, hover, commit
|
||||
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:[email protected]`, world
|
||||
@@ -448,7 +448,7 @@ declaration matches the file's language are called (and loaded):
|
||||
|
||||
```toml
|
||||
[[contributes.providers]]
|
||||
kinds = ["definition", "line-markers", "completion"] # also "inline-completion", "code-actions", "semantic-tokens", "hover"
|
||||
kinds = ["definition", "line-markers", "completion"] # also "inline-completion", "code-actions", "semantic-tokens", "hover", "inlay-hints"
|
||||
languages = ["java", "xml"] # editor language ids; "*" for any
|
||||
```
|
||||
|
||||
@@ -523,6 +523,27 @@ edit-path budget, and shows the server's hover first, then each plugin's
|
||||
below a rule. `range`, when given, is the text the hover describes (used when
|
||||
the server names none). At most 64 KiB of Markdown per plugin.
|
||||
|
||||
#### Inlay hints
|
||||
|
||||
The `inlay-hints` kind (API 1.3) shows short labels inside the code of
|
||||
documents of the declared languages, such as a type after a call. Register
|
||||
with `register_plugin!(MyPlugin, inlay_hints)` and implement
|
||||
`openidea_plugin::inlay_hints::InlayHints`:
|
||||
|
||||
```rust
|
||||
impl InlayHints for MyPlugin {
|
||||
fn inlay_hints(&mut self, doc: &Document, range: Range) -> Vec<InlayHint> {
|
||||
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
|
||||
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
2e34ee9cb774344d4dedf1ec077d776a051abd08e435f6df3dca6c7d12fa94fb
|
||||
5de6d0c1cb0ee1a92d36b57ec18590f03829b2c29cc1c306be453931c528c692
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
8ee84d6a4a835ec7104877fa105e593b47e8091339b3419690b018f3e8877b94
|
||||
9a89ec93d158e460998cf472837d7e0e8aa76d5441cb1dd24c3184bd814a29c1
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
2af4ae2589bebf9703afa6b7b24bc073334344e6836b0ea29aab259338b38762
|
||||
652557045106ab5bd1126cb8e05965d030c4aa28c01ede31be3fe285aab98a67
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
a7d656941097113fdde3e64a07446d1780f045702a935a5e5153ac1f540d6b06
|
||||
7c8c97e02f754676ff512d10f454a7451468e2a98c2e992e790096215dd28658
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
3f6549848f81cc999a42ba0fa04e0efb43640e09a67ab28d799d4f79832f6178
|
||||
1b6e3f30cc49d9d67d95f7655cbe337c5f8465c6ef7b8709cfab4734c3c40fdf
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
31ff37106dc0829e226de01b5148f6ee67b1d776acd6def4729edf4c63946219
|
||||
3225880631e679780c3c7eb09683b598bb3803fcf7be7b6940369eada633c48f
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
eb4d09a79172cb46857c7bb3e50e7fbd6f4b6be379bb1c79a41ea23a5643f875
|
||||
a59623542b4df024f5a85bc2cd0e046189044a0c292c793384fdb5465e8eec48
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
4f2e5d5c877554b111bb454603d002e50bbafb3dca3cca5768c052fbc48f73ab
|
||||
f230ae32c2d5ab469f23c6851fc7830a6ecce7538c7d435be39b5bbebe38a64f
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
0cae1d9dd2075b1a107d894854f0d01ab81331b67793f2ad972835a8ab34d3a0
|
||||
63f52f8cf8a90affc751ac7e19352a89f0726fc7c8dbbb303172ebd42e23af8c
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
13e34d174abf5187bf7c191736974305afb0af257fc103eec96fe8fa8edb8c88
|
||||
178862630cf39bca56f5d54cd135dcac4a1ab74a3e10356163d00e5a718ae728
|
||||
Бінарний файл не відображається.
@@ -1 +1 @@
|
||||
7e010175c19a159a22c42a76ca07ebc663a27ce9143c21e742ca99435a538f83
|
||||
a6c188aed4fc57cd9955d6b47ef715c52dc3f8c7b69f7d18ea71b3932888710f
|
||||
@@ -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<String>,
|
||||
pub padding_left: bool,
|
||||
pub padding_right: bool,
|
||||
}
|
||||
|
||||
impl InlayHint {
|
||||
pub fn new(position: Position, label: impl Into<String>) -> 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<String>) -> 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<InlayHint>;
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
@@ -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/[email protected]": 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<T: Plugin + crate::inlay_hints::InlayHints + Default>(
|
||||
d: crate::inlay_bindings::exports::openidea::plugin::inlay_hints_provider::Document,
|
||||
range: b::types::Range,
|
||||
) -> Vec<crate::inlay_bindings::exports::openidea::plugin::inlay_hints_provider::InlayHint> {
|
||||
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::<T, _>(|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;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package openidea:[email protected];
|
||||
|
||||
/// 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<string>,
|
||||
/// 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<inlay-hint>;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package openidea:[email protected];
|
||||
|
||||
/// 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<string>,
|
||||
/// 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<inlay-hint>;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Заблокувати користувача