mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-08 17:02:17 +00:00
feat(plugins): let plugins add to the hover of any language (#1931)
Plugin API 1.3 gains an optional hover-provider export (worlds plugin-hover and hover): hover(doc, at) returns Markdown and the range it describes. A plugin declares the hover kind under [[contributes.providers]] for any language; the host asks it with the language server, under the edit-path budget, and shows the server's hover first and each plugin's below a rule. Declaring the kind needs engines.openidea ^1.3. The Rust SDK gains openidea_plugin::hover and register_plugin!(T, hover); every shipped component is rebuilt for the WIT change.
This commit is contained in:
1 parent
de4c325a28
commit
50567473f7
55 files changed
+804
-25
No files matched your search
@@ -195,9 +195,24 @@ pub async fn lsp_hover(
|
||||
) -> IdeResult<Option<Hover>> {
|
||||
let lsp = host.project(window)?.lsp.clone();
|
||||
let cancel = host.begin_request(request);
|
||||
let result = lsp.hover(doc, pos, cancel).await;
|
||||
let Some((plugins, d)) = plugin_providers(host, window, doc, ProviderKind::Hover) else {
|
||||
let result = lsp.hover(doc, pos, cancel).await;
|
||||
host.end_request(request);
|
||||
return result;
|
||||
};
|
||||
let (result, hovers) =
|
||||
tokio::join!(lsp.hover(doc, pos, cancel), plugin_call(move || plugins.provide_hover(&d, pos)));
|
||||
host.end_request(request);
|
||||
result
|
||||
let hovers = hovers.unwrap_or_default();
|
||||
if hovers.is_empty() {
|
||||
return result;
|
||||
}
|
||||
let server = match result {
|
||||
Ok(server) => server,
|
||||
Err(e) if matches!(e.kind, ErrorKind::NotFound | ErrorKind::Unavailable) => None,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
Ok(crate::plugins::merge_hover(server, hovers))
|
||||
}
|
||||
|
||||
pub async fn lsp_signature_help(host: &Host, window: &str, doc: DocId, pos: Pos) -> IdeResult<Option<SignatureHelp>> {
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
use ide_lsp::types::{Hover, Markup};
|
||||
use ide_plugin::PluginHover;
|
||||
|
||||
pub(crate) fn merge(server: Option<Hover>, plugin: Vec<PluginHover>) -> Option<Hover> {
|
||||
if plugin.is_empty() {
|
||||
return server;
|
||||
}
|
||||
let mut parts = Vec::with_capacity(plugin.len() + 1);
|
||||
let mut range = None;
|
||||
if let Some(h) = server {
|
||||
range = h.range;
|
||||
if !h.contents.value.trim().is_empty() {
|
||||
parts.push(if h.contents.kind == "markdown" { h.contents.value } else { escape(&h.contents.value) });
|
||||
}
|
||||
}
|
||||
for h in plugin {
|
||||
range = range.or(h.range);
|
||||
parts.push(h.markdown);
|
||||
}
|
||||
Some(Hover { contents: Markup { kind: "markdown".into(), value: parts.join("\n\n---\n\n") }, range })
|
||||
}
|
||||
|
||||
fn escape(plain: &str) -> String {
|
||||
let mut out = String::with_capacity(plain.len() + plain.len() / 4);
|
||||
for c in plain.chars() {
|
||||
match c {
|
||||
'\n' => out.push_str(" \n"),
|
||||
'\\' | '`' | '*' | '_' | '{' | '}' | '[' | ']' | '<' | '>' | '(' | ')' | '#' | '+' | '-' | '.' | '!'
|
||||
| '|' => {
|
||||
out.push('\\');
|
||||
out.push(c);
|
||||
}
|
||||
_ => out.push(c),
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ide_core::{Pos, Range};
|
||||
|
||||
fn range(line: u32, from: u32, to: u32) -> Range {
|
||||
Range::new(Pos::new(line, from), Pos::new(line, to))
|
||||
}
|
||||
|
||||
fn plugin(markdown: &str, at: Option<Range>) -> PluginHover {
|
||||
PluginHover { markdown: markdown.into(), range: at }
|
||||
}
|
||||
|
||||
fn server(kind: &str, value: &str, at: Option<Range>) -> Hover {
|
||||
Hover { contents: Markup { kind: kind.into(), value: value.into() }, range: at }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_content_follows_the_server_hover() {
|
||||
let merged = merge(
|
||||
Some(server("markdown", "```php\nfunction route()\n```", Some(range(3, 4, 9)))),
|
||||
vec![plugin("**orders.index** · `GET /orders`", Some(range(3, 10, 22)))],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(merged.contents.kind, "markdown");
|
||||
assert_eq!(merged.contents.value, "```php\nfunction route()\n```\n\n---\n\n**orders.index** · `GET /orders`");
|
||||
assert_eq!(merged.range, Some(range(3, 4, 9)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn without_a_server_hover_the_plugins_answer_alone() {
|
||||
let merged = merge(None, vec![plugin("one", None), plugin("two", Some(range(1, 0, 3)))]).unwrap();
|
||||
assert_eq!(merged.contents.value, "one\n\n---\n\ntwo");
|
||||
assert_eq!(merged.range, Some(range(1, 0, 3)));
|
||||
assert!(merge(None, Vec::new()).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plain_text_from_the_server_keeps_its_characters() {
|
||||
let merged = merge(Some(server("plaintext", "a_b *c*\nd", None)), vec![plugin("x", None)]).unwrap();
|
||||
assert_eq!(merged.contents.value, "a\\_b \\*c\\* \nd\n\n---\n\nx");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_empty_server_hover_is_left_out() {
|
||||
let merged = merge(Some(server("markdown", " ", None)), vec![plugin("x", None)]).unwrap();
|
||||
assert_eq!(merged.contents.value, "x");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_plugin_content_keeps_the_server_hover() {
|
||||
let kept = merge(Some(server("plaintext", "a_b", Some(range(0, 0, 1)))), Vec::new()).unwrap();
|
||||
assert_eq!((kept.contents.kind.as_str(), kept.contents.value.as_str()), ("plaintext", "a_b"));
|
||||
assert_eq!(kept.range, Some(range(0, 0, 1)));
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
//! projects) and folders linked with `openidea-plugin link` (`links.json`).
|
||||
|
||||
mod api;
|
||||
mod hover;
|
||||
mod icon_extensions;
|
||||
mod icon_patterns;
|
||||
mod icon_settings;
|
||||
@@ -38,6 +39,7 @@ pub mod views;
|
||||
pub mod web;
|
||||
|
||||
pub use api::{FileSecretStore, HostWorkspace, PluginWorkspace, SecretStore};
|
||||
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,
|
||||
};
|
||||
|
||||
@@ -38,6 +38,7 @@ pub enum ProviderKind {
|
||||
Definition,
|
||||
LineMarkers,
|
||||
SemanticTokens,
|
||||
Hover,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
@@ -116,6 +117,9 @@ impl ExtensionPoint for ProvidersPoint {
|
||||
if d.kinds.contains(&ProviderKind::SemanticTokens) {
|
||||
pkg.manifest.needs_api("contributes.providers: semantic-tokens", 3)?;
|
||||
}
|
||||
if d.kinds.contains(&ProviderKind::Hover) {
|
||||
pkg.manifest.needs_api("contributes.providers: hover", 3)?;
|
||||
}
|
||||
for l in &d.languages {
|
||||
if l != "*"
|
||||
&& (l.is_empty()
|
||||
@@ -164,7 +168,7 @@ pub(super) fn secret(_pkg: &Package, decl: toml::Value) -> Result<UiDecl, String
|
||||
use super::{LoadedPlugin, PluginHost, PluginStatus};
|
||||
use ide_core::CancellationToken;
|
||||
use ide_plugin::{
|
||||
ActorError, CallError, CodeAction, CompletionItem, InlineSuggestion, LineMarker, Location, Pending,
|
||||
ActorError, CallError, CodeAction, CompletionItem, InlineSuggestion, LineMarker, Location, Pending, PluginHover,
|
||||
ProviderDocument, Runtime, SemanticToken,
|
||||
};
|
||||
use std::sync::Weak;
|
||||
@@ -345,6 +349,14 @@ impl PluginHost {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn provide_hover(&self, doc: &ProviderDocument, at: ide_core::Pos) -> Vec<PluginHover> {
|
||||
let d = Arc::new(doc.clone());
|
||||
self.edit_calls(ProviderKind::Hover, &doc.language, move |rt, timeout| rt.provide_hover(&d, at, timeout))
|
||||
.into_iter()
|
||||
.filter_map(|(_, hover)| hover)
|
||||
.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,55 @@
|
||||
use ide_core::Pos;
|
||||
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/hover")
|
||||
}
|
||||
|
||||
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() {}\n}\n$order->user;\n$order->total;\n";
|
||||
|
||||
#[test]
|
||||
fn a_declared_name_gets_the_plugins_hover() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let host = host(tmp.path());
|
||||
host.install_from_dir(&fixture_dir(), &Consent::all()).unwrap();
|
||||
let hovers = host.provide_hover(&doc(tmp.path(), "php", ORDER), Pos::new(5, 10));
|
||||
assert_eq!(hovers.len(), 1);
|
||||
assert_eq!(hovers[0].markdown, "**user** · declared on line 4");
|
||||
let range = hovers[0].range.unwrap();
|
||||
assert_eq!((range.start.line, range.start.col, range.end.line, range.end.col), (5, 8, 5, 12));
|
||||
assert!(host.provide_hover(&doc(tmp.path(), "php", ORDER), Pos::new(6, 10)).is_empty());
|
||||
}
|
||||
|
||||
#[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_hover(&doc(tmp.path(), "javascript", ORDER), Pos::new(5, 10)).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);
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
//! themselves are in `wit_host`.
|
||||
|
||||
use crate::types::{
|
||||
CodeAction, CompletionItem, InlineSuggestion, LineMarker, Location, ProviderDocument, SemanticToken,
|
||||
CodeAction, CompletionItem, InlineSuggestion, LineMarker, Location, PluginHover, ProviderDocument, SemanticToken,
|
||||
};
|
||||
use crate::types::{
|
||||
EventKind, HostError, HttpRequest, HttpResponse, Invocation, Limits, NotificationLevel, ProcessOutput, Prompt,
|
||||
@@ -15,8 +15,8 @@ use crate::types::{
|
||||
ViewField, ViewListItem, ViewTable, ViewTreeItem, Viewport,
|
||||
};
|
||||
use crate::wit_host::{
|
||||
bindings, indexing_bindings, providers_bindings, semantic_bindings, services_bindings, syntax_bindings,
|
||||
views_bindings,
|
||||
bindings, hover_bindings, indexing_bindings, providers_bindings, semantic_bindings, services_bindings,
|
||||
syntax_bindings, views_bindings,
|
||||
};
|
||||
use ide_core::CancellationToken;
|
||||
use ide_core::ipc_path::to_ipc;
|
||||
@@ -33,6 +33,7 @@ use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};
|
||||
|
||||
use bindings::openidea::plugin::events as wit_events;
|
||||
use bindings::openidea::plugin::types as wit_types;
|
||||
use hover_bindings::exports::openidea::plugin::hover_provider as wit_hover;
|
||||
use indexing_bindings::exports::openidea::plugin::document_indexer as wit_index;
|
||||
use providers_bindings::exports::openidea::plugin::providers as wit_prov;
|
||||
use semantic_bindings::exports::openidea::plugin::semantic_tokens_provider as wit_sem;
|
||||
@@ -524,6 +525,7 @@ struct Exports {
|
||||
views: Option<wit_view::Guest>,
|
||||
semantic: Option<wit_sem::Guest>,
|
||||
services: Option<wit_svc::Guest>,
|
||||
hover: Option<wit_hover::Guest>,
|
||||
}
|
||||
|
||||
/// Most results one syntax call may return (more are dropped).
|
||||
@@ -605,6 +607,7 @@ impl Runtime {
|
||||
let view_indices = wit_view::GuestIndices::new(&pre).ok();
|
||||
let semantic_indices = wit_sem::GuestIndices::new(&pre).ok();
|
||||
let service_indices = wit_svc::GuestIndices::new(&pre).ok();
|
||||
let hover_indices = wit_hover::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 {
|
||||
@@ -631,9 +634,13 @@ impl Runtime {
|
||||
Some(indices) => Some(indices.load(&mut store, &instance).map_err(api_hint)?),
|
||||
None => None,
|
||||
};
|
||||
let hover = match hover_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, services },
|
||||
exports: Exports { plugin, syntax, providers, views, indexer, semantic, services, hover },
|
||||
limits: setup.limits,
|
||||
version: setup.version,
|
||||
})
|
||||
@@ -891,6 +898,29 @@ impl Runtime {
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn provide_hover(
|
||||
&mut self,
|
||||
doc: &ProviderDocument,
|
||||
at: ide_core::Pos,
|
||||
timeout: Duration,
|
||||
) -> Result<Option<PluginHover>, CallError> {
|
||||
if self.exports.hover.is_none() {
|
||||
return Err(CallError::missing_export("hover-provider"));
|
||||
}
|
||||
let d = wit_hover::Document {
|
||||
path: to_ipc(&doc.path),
|
||||
language: doc.language.clone(),
|
||||
version: doc.version,
|
||||
text: doc.text.clone(),
|
||||
};
|
||||
let hover = self.call(Invocation::default(), timeout, |e, store| {
|
||||
e.hover.as_ref().expect("checked").call_hover(store, &d, pos_to_wit(at))
|
||||
})?;
|
||||
Ok(hover
|
||||
.filter(|h| !h.markdown.trim().is_empty())
|
||||
.map(|h| PluginHover { markdown: clip(h.markdown, MAX_HOVER_BYTES), range: h.range.map(range_from_wit) }))
|
||||
}
|
||||
|
||||
pub fn activate(&mut self) -> Result<(), CallError> {
|
||||
let data = self.store.data();
|
||||
let ctx = wit_types::Context {
|
||||
@@ -1068,6 +1098,7 @@ impl Runtime {
|
||||
const MAX_PROVIDER_ITEMS: usize = 10_000;
|
||||
const MAX_SEMANTIC_TOKENS: usize = 1 << 16;
|
||||
const MAX_TOKEN_MODIFIERS: usize = 8;
|
||||
const MAX_HOVER_BYTES: usize = 64 * 1024;
|
||||
|
||||
fn pos_to_wit(p: ide_core::Pos) -> wit_types::Position {
|
||||
wit_types::Position { line: p.line, character: p.col }
|
||||
|
||||
@@ -426,6 +426,12 @@ pub struct SemanticToken {
|
||||
pub modifiers: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct PluginHover {
|
||||
pub markdown: String,
|
||||
pub range: Option<ide_core::Range>,
|
||||
}
|
||||
|
||||
// ---- declarative views (wit/views.wit) ---------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)]
|
||||
|
||||
@@ -834,3 +834,10 @@ pub(crate) mod semantic_bindings {
|
||||
with: { "openidea:plugin/types": super::bindings::openidea::plugin::types },
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) mod hover_bindings {
|
||||
wasmtime::component::bindgen!({
|
||||
world: "hover", path: "../../wit",
|
||||
with: { "openidea:plugin/types": super::bindings::openidea::plugin::types },
|
||||
});
|
||||
}
|
||||
@@ -29,6 +29,7 @@ fn components() -> Vec<PathBuf> {
|
||||
repo().join("crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm"),
|
||||
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"),
|
||||
];
|
||||
let official = repo().join("plugins/official");
|
||||
let mut dirs: Vec<_> = std::fs::read_dir(&official).unwrap().map(|e| e.unwrap().path()).collect();
|
||||
|
||||
Binary file not shown.
@@ -1 +1 @@
|
||||
670aec4d79e92537efb7048698bbb8f236cd6006524c74459891b03c9b613450
|
||||
885badd4727639ad3205b2551d2b8406490fc05b72c684c5d17f09aadd9541bd
|
||||
+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-hover"
|
||||
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-hover"
|
||||
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 @@
|
||||
# Hover test fixture
|
||||
|
||||
Over a name the PHP document declares as `function <name>(`, shows
|
||||
`**<name>** · declared on line <n>` for the name's range; nothing elsewhere.
|
||||
|
||||
Used by `cargo test -p ide-host --test plugin_hover` to exercise the optional
|
||||
`hover-provider` export. It is not a marketplace plugin.
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
ff1b39ac7850fbe4495e274dd78cca4c906a20705e8b9a975f58cf564e8ff137
|
||||
@@ -0,0 +1,9 @@
|
||||
id = "test.hover"
|
||||
name = "Hover fixture"
|
||||
version = "0.1.0"
|
||||
engines.openidea = "^1.3"
|
||||
wasm = "dist/plugin.wasm"
|
||||
|
||||
[[contributes.providers]]
|
||||
kinds = ["hover"]
|
||||
languages = ["php"]
|
||||
@@ -0,0 +1,31 @@
|
||||
use openidea_plugin::hover::{Document, Hover, HoverProvider};
|
||||
use openidea_plugin::{Error, Invocation, Plugin, Position, 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 HoverProvider for Fixture {
|
||||
fn hover(&mut self, doc: &Document, at: Position) -> Option<Hover> {
|
||||
let text = doc.text.as_str();
|
||||
let offset = text::offset(text, at)?;
|
||||
let word = |c: char| c.is_ascii_alphanumeric() || c == '_';
|
||||
let start = text[..offset].rfind(|c: char| !word(c)).map_or(0, |i| i + 1);
|
||||
let end = text[offset..].find(|c: char| !word(c)).map_or(text.len(), |n| offset + n);
|
||||
let name = &text[start..end];
|
||||
if name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let declared = text.find(&format!("function {name}("))?;
|
||||
let line = text::position(text, declared).line + 1;
|
||||
let range = Range { start: text::position(text, start), end: text::position(text, end) };
|
||||
Some(Hover::new(format!("**{name}** · declared on line {line}")).range(range))
|
||||
}
|
||||
}
|
||||
|
||||
register_plugin!(Fixture, hover);
|
||||
Binary file not shown.
@@ -1 +1 @@
|
||||
c913c2c94fecb43d00f73f3171cc2fda54caf7a28f170424b0addb1d40ed4cae
|
||||
c9bb4bc3603d9e7c770cf13ad413f5b3a787e78dbeed8f41a3b8537c0740275d
|
||||
Binary file not shown.
@@ -1 +1 @@
|
||||
911388ae62843d7b1e4c40d91307fbe507535c60680a9626cc07d724b75a9dfb
|
||||
a9cb7302b86e12e05cbbf87fb402609e2f88d3a463f6c6e96b254e643b76183d
|
||||
Binary file not shown.
@@ -1 +1 @@
|
||||
a3818b6ec40df8f141290537a7c024aadb02d7be1eb063ca9170799ca3271898
|
||||
0be22488fe8f20a59f48fc61089b3db791a5d723d71e4d654bfd008038c01003
|
||||
Binary file not shown.
@@ -1 +1 @@
|
||||
9e4c12fdf905227128d97e92639cf336124929c37384e5f5796e27ebfc5a7b37
|
||||
92ca920847c51036ac6dc7acd299ff9afaef9b4037e0112a0159bf50e822c7f3
|
||||
+22
-2
@@ -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
|
||||
crates/ide-plugin/tests/fixtures/ # test plugins: hello, jobs, language-toy, misbehave, semantic-tokens, hover
|
||||
```
|
||||
|
||||
The plugin API is defined in [`wit/`](../wit/) (`openidea:[email protected]`, world
|
||||
@@ -402,7 +402,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"
|
||||
kinds = ["definition", "line-markers", "completion"] # also "inline-completion", "code-actions", "semantic-tokens", "hover"
|
||||
languages = ["java", "xml"] # editor language ids; "*" for any
|
||||
```
|
||||
|
||||
@@ -457,6 +457,26 @@ the language server's semantic tokens and win where they overlap one, so the
|
||||
editor's colour scheme styles them like the server's. They are asked again
|
||||
after each edit, under the edit-path budget; at most 65,536 per document.
|
||||
|
||||
#### Hover
|
||||
|
||||
The `hover` kind (API 1.3) adds Markdown to the hover of documents of the
|
||||
declared languages, here too including languages the plugin doesn't
|
||||
contribute. Register with `register_plugin!(MyPlugin, hover)` and implement
|
||||
`openidea_plugin::hover::HoverProvider`:
|
||||
|
||||
```rust
|
||||
impl HoverProvider for MyPlugin {
|
||||
fn hover(&mut self, doc: &Document, at: Position) -> Option<Hover> {
|
||||
Some(Hover::new("**orders.index** · `GET /orders`"))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The host asks the language server and the plugins together, under the
|
||||
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.
|
||||
|
||||
### Network and secrets
|
||||
|
||||
`host::fetch` makes an HTTPS request (plain HTTP only to `localhost`) to a host
|
||||
|
||||
BIN
Binary file not shown.
+1
-1
@@ -1 +1 @@
|
||||
eb76da4bc9db84dcbad34143bac18c9ecf974276f793009791a15aab2eee291d
|
||||
9384d8964c7c05d3df06521e0d724a932fdd17608cbcede13ec2acadfab3783c
|
||||
BIN
Binary file not shown.
+1
-1
@@ -1 +1 @@
|
||||
0a19948927a23de81b2a453b9992e021744093cbe41ea92daa9feb2b1bc9a2a9
|
||||
9d7e857ef367eaa9748d1a2b05b7b45226c254aa578c29e1ed987c7353738acf
|
||||
BIN
Binary file not shown.
+1
-1
@@ -1 +1 @@
|
||||
564bfd00694f27f35f09eb0b81d341207a755528b5fe7c46c96717b3868d0f65
|
||||
3e11039d36306f51c728bf9d08030fe620de461fa5b0037700aea95c4d27714c
|
||||
BIN
Binary file not shown.
+1
-1
@@ -1 +1 @@
|
||||
f3f447366fa38f9bf4911f35b6e899405b0e072ceee3ad5ea2fe90639d70fa53
|
||||
ef50957a407c9aff9999fdd197a5e72c90b7ba77aedbd7569408262ab7e158be
|
||||
BIN
Binary file not shown.
+1
-1
@@ -1 +1 @@
|
||||
85cad466adb94f2b58e62263617e885d1c47cf70db5e8b38942bd415f0d779af
|
||||
7160b309f39fe973e67bb20d361384955aed6afe98f00db329c3026ffe923759
|
||||
BIN
Binary file not shown.
+1
-1
@@ -1 +1 @@
|
||||
659446eda0b50aa0fb08f6af68f7d355fcaf3342847e2e579999dcf52a7a7399
|
||||
1f1afa285a64a7f7758f08a80306d6397c8aee4d2ac7ed43369e77aae61add04
|
||||
Binary file not shown.
@@ -1 +1 @@
|
||||
51ef9d43315ca9d7239999c222fbf7a2c01bc85c2b40f53f34a1a8e5d31baa6d
|
||||
ddd9205da9fc947215ab90c505d8b443d0937b1dd11734e9ce94d277517c77a6
|
||||
BIN
Binary file not shown.
+1
-1
@@ -1 +1 @@
|
||||
bdf9455c6e7c8c340c54d783f351220b38cffed36907e1b5a1f2e11801e6f5bb
|
||||
c61a0ec6ffcbd71e590e5a16a6f08f2d2772481497edc77e11914cfb3d0bfcd1
|
||||
BIN
Binary file not shown.
+1
-1
@@ -1 +1 @@
|
||||
7e4d00e1ea63b2eca48b9297667e04c05a9f9832f1dee2262272801d516b9a16
|
||||
e11cd0b664ae058274808bc2b2152bac2738207979758d13bc0c0cd96c38ef4f
|
||||
BIN
Binary file not shown.
+1
-1
@@ -1 +1 @@
|
||||
00cbd2dd7f0937554633ba5e533f19be85f70579c7c6fedb6fe0228f8f63bf4c
|
||||
59319cf4be14448b0126b07d074cc08c351cfd12a3de26f111c2445fe8bff664
|
||||
BIN
Binary file not shown.
+1
-1
@@ -1 +1 @@
|
||||
eecf2cf561dd5e3e3edbf9f67ccb479573dd538a75d4426a1d086edf0a310043
|
||||
59844ae52aa765af404b43f7895db94d0063a3a6fce5bd54bf9af680ccac357b
|
||||
@@ -0,0 +1,38 @@
|
||||
use crate::types::{Position, Range};
|
||||
|
||||
pub use crate::providers::Document;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Hover {
|
||||
pub markdown: String,
|
||||
pub range: Option<Range>,
|
||||
}
|
||||
|
||||
impl Hover {
|
||||
pub fn new(markdown: impl Into<String>) -> Self {
|
||||
Self { markdown: markdown.into(), range: None }
|
||||
}
|
||||
|
||||
pub fn range(mut self, range: Range) -> Self {
|
||||
self.range = Some(range);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HoverProvider {
|
||||
fn hover(&mut self, doc: &Document, at: Position) -> Option<Hover>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn a_hover_can_name_the_range_it_describes() {
|
||||
let range = Range { start: Position { line: 2, character: 4 }, end: Position { line: 2, character: 9 } };
|
||||
let hover = Hover::new("**route** `orders.index`").range(range);
|
||||
assert_eq!(hover.markdown, "**route** `orders.index`");
|
||||
assert_eq!(hover.range, Some(range));
|
||||
assert_eq!(Hover::new("x").range, None);
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@
|
||||
//! wasm32-wasip2`); see `plugins/README.md`.
|
||||
|
||||
pub mod host;
|
||||
pub mod hover;
|
||||
pub mod indexing;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod mock;
|
||||
@@ -155,6 +156,17 @@ pub mod semantic_bindings {
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[doc(hidden)]
|
||||
pub mod hover_bindings {
|
||||
wit_bindgen::generate!({
|
||||
world: "hover", path: "wit", pub_export_macro: true,
|
||||
export_macro_name: "__export_hover_world",
|
||||
default_bindings_module: "openidea_plugin::hover_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";
|
||||
|
||||
@@ -215,6 +227,9 @@ macro_rules! __optional_exports {
|
||||
(services, $plugin:ty, $component:ident) => {
|
||||
$crate::__services_exports!($plugin, $component);
|
||||
};
|
||||
(hover, $plugin:ty, $component:ident) => {
|
||||
$crate::__hover_exports!($plugin, $component);
|
||||
};
|
||||
(syntax, $plugin:ty, $component:ident) => {
|
||||
impl $crate::syntax_bindings::exports::openidea::plugin::syntax_provider::Guest for $component {
|
||||
fn highlights(
|
||||
@@ -384,6 +399,22 @@ macro_rules! __semantic_tokens_exports {
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __hover_exports {
|
||||
($plugin:ty, $component:ident) => {
|
||||
impl $crate::hover_bindings::exports::openidea::plugin::hover_provider::Guest for $component {
|
||||
fn hover(
|
||||
doc: $crate::hover_bindings::exports::openidea::plugin::hover_provider::Document,
|
||||
at: $crate::bindings::openidea::plugin::types::Position,
|
||||
) -> ::core::option::Option<$crate::hover_bindings::exports::openidea::plugin::hover_provider::HoverContent> {
|
||||
$crate::__rt::hover::<$plugin>(doc, at)
|
||||
}
|
||||
}
|
||||
$crate::hover_bindings::__export_hover_world!($component with_types_in $crate::hover_bindings);
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __plugin_exports {
|
||||
@@ -639,6 +670,16 @@ pub mod __rt {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn hover<T: Plugin + crate::hover::HoverProvider + Default>(
|
||||
d: crate::hover_bindings::exports::openidea::plugin::hover_provider::Document,
|
||||
pos: b::types::Position,
|
||||
) -> Option<crate::hover_bindings::exports::openidea::plugin::hover_provider::HoverContent> {
|
||||
use crate::hover_bindings::exports::openidea::plugin::hover_provider as w;
|
||||
let doc = crate::providers::Document { path: d.path, language: d.language, version: d.version, text: d.text };
|
||||
with::<T, _>(|p| p.hover(&doc, at(pos)))
|
||||
.map(|h| w::HoverContent { markdown: h.markdown, range: h.range.map(crate::host::__range_to) })
|
||||
}
|
||||
|
||||
// ---- views ----
|
||||
|
||||
use crate::views as v;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package openidea:[email protected];
|
||||
|
||||
interface hover-provider {
|
||||
use types.{position, range};
|
||||
|
||||
record document {
|
||||
path: string,
|
||||
language: string,
|
||||
version: u64,
|
||||
text: string,
|
||||
}
|
||||
|
||||
record hover-content {
|
||||
markdown: string,
|
||||
range: option<range>,
|
||||
}
|
||||
|
||||
hover: func(doc: document, at: position) -> option<hover-content>;
|
||||
}
|
||||
@@ -61,6 +61,11 @@ world plugin-semantic-tokens {
|
||||
export semantic-tokens-provider;
|
||||
}
|
||||
|
||||
world plugin-hover {
|
||||
include plugin;
|
||||
export hover-provider;
|
||||
}
|
||||
|
||||
/// A plugin whose views show in the Services tool window
|
||||
/// (`location = "services"`): their nodes' detail pane.
|
||||
world plugin-services {
|
||||
@@ -93,6 +98,10 @@ world semantic-tokens {
|
||||
export semantic-tokens-provider;
|
||||
}
|
||||
|
||||
world hover {
|
||||
export hover-provider;
|
||||
}
|
||||
|
||||
/// Only the `service-provider` export, generated next to `plugin` like `syntax`.
|
||||
world services {
|
||||
export service-provider;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package openidea:[email protected];
|
||||
|
||||
interface hover-provider {
|
||||
use types.{position, range};
|
||||
|
||||
record document {
|
||||
path: string,
|
||||
language: string,
|
||||
version: u64,
|
||||
text: string,
|
||||
}
|
||||
|
||||
record hover-content {
|
||||
markdown: string,
|
||||
range: option<range>,
|
||||
}
|
||||
|
||||
hover: func(doc: document, at: position) -> option<hover-content>;
|
||||
}
|
||||
@@ -61,6 +61,11 @@ world plugin-semantic-tokens {
|
||||
export semantic-tokens-provider;
|
||||
}
|
||||
|
||||
world plugin-hover {
|
||||
include plugin;
|
||||
export hover-provider;
|
||||
}
|
||||
|
||||
/// A plugin whose views show in the Services tool window
|
||||
/// (`location = "services"`): their nodes' detail pane.
|
||||
world plugin-services {
|
||||
@@ -93,6 +98,10 @@ world semantic-tokens {
|
||||
export semantic-tokens-provider;
|
||||
}
|
||||
|
||||
world hover {
|
||||
export hover-provider;
|
||||
}
|
||||
|
||||
/// Only the `service-provider` export, generated next to `plugin` like `syntax`.
|
||||
world services {
|
||||
export service-provider;
|
||||
|
||||
Reference in new issue
Block a user