Skip to content

Commit

Permalink
LSP: hover (tooltip) support
Browse files Browse the repository at this point in the history
  • Loading branch information
ogoffart committed Aug 27, 2024
1 parent 794bf97 commit 5911d11
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 16 deletions.
32 changes: 16 additions & 16 deletions tools/lsp/language.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
pub mod completion;
mod formatting;
mod goto;
mod hover;
mod semantic_tokens;
#[cfg(test)]
pub mod test;
Expand All @@ -29,10 +30,10 @@ use lsp_types::request::{
use lsp_types::{
ClientCapabilities, CodeActionOrCommand, CodeActionProviderCapability, CodeLens,
CodeLensOptions, Color, ColorInformation, ColorPresentation, Command, CompletionOptions,
DocumentSymbol, DocumentSymbolResponse, Hover, InitializeParams, InitializeResult, OneOf,
Position, PrepareRenameResponse, PublishDiagnosticsParams, RenameOptions,
SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions, ServerCapabilities,
ServerInfo, TextDocumentSyncCapability, TextEdit, Url, WorkDoneProgressOptions,
DocumentSymbol, DocumentSymbolResponse, InitializeParams, InitializeResult, OneOf, Position,
PrepareRenameResponse, PublishDiagnosticsParams, RenameOptions, SemanticTokensFullOptions,
SemanticTokensLegend, SemanticTokensOptions, ServerCapabilities, ServerInfo,
TextDocumentSyncCapability, TextEdit, Url, WorkDoneProgressOptions,
};
use std::cell::RefCell;
use std::collections::HashMap;
Expand Down Expand Up @@ -171,6 +172,7 @@ impl RequestHandler {
pub fn server_initialize_result(client_cap: &ClientCapabilities) -> InitializeResult {
InitializeResult {
capabilities: ServerCapabilities {
hover_provider: Some(true.into()),
completion_provider: Some(CompletionOptions {
resolve_provider: None,
trigger_characters: Some(vec![".".to_owned()]),
Expand Down Expand Up @@ -263,18 +265,16 @@ pub fn register_request_handlers(rh: &mut RequestHandler) {
});
Ok(result)
});
rh.register::<HoverRequest, _>(|_params, _ctx| async move {
/*let result =
token_descr(document_cache, params.text_document_position_params).map(|x| Hover {
contents: lsp_types::HoverContents::Scalar(MarkedString::from_language_code(
"text".into(),
format!("{:?}", x.token),
)),
range: None,
});
let resp = Response::new_ok(id, result);
connection.sender.send(Message::Response(resp))?;*/
Ok(None::<Hover>)
rh.register::<HoverRequest, _>(|params, ctx| async move {
let document_cache = &mut ctx.document_cache.borrow_mut();
let result = token_descr(
document_cache,
&params.text_document_position_params.text_document.uri,
&params.text_document_position_params.position,
)
.and_then(|(token, _)| hover::get_tooltip(document_cache, token));

Ok(result)
});
rh.register::<CodeActionRequest, _>(|params, ctx| async move {
let document_cache = &mut ctx.document_cache.borrow_mut();
Expand Down
88 changes: 88 additions & 0 deletions tools/lsp/language/hover.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

use super::token_info::TokenInfo;
use crate::common::DocumentCache;
use i_slint_compiler::langtype::{ElementType, PropertyLookupResult, Type};
use i_slint_compiler::parser::SyntaxToken;
use itertools::Itertools as _;
use lsp_types::{Hover, HoverContents, MarkupContent};

pub fn get_tooltip(document_cache: &mut DocumentCache, token: SyntaxToken) -> Option<Hover> {
let token_info = crate::language::token_info::token_info(document_cache, token)?;
let contents = match token_info {
TokenInfo::Type(ty) => from_plain_text(ty.to_string()),
TokenInfo::ElementType(e) => match e {
ElementType::Component(c) => {
if c.is_global() {
from_slint_code(&format!("global {}", c.id))
} else {
from_slint_code(&format!("component {}", c.id))
}
}
ElementType::Builtin(b) => from_plain_text(format!("{} (builtin)", b.name)),
_ => return None,
},
TokenInfo::ElementRc(e) => {
let e = e.borrow();
let component = &e.enclosing_component.upgrade().unwrap();
if component.is_global() {
from_slint_code(&format!("global {}", component.id))
} else if e.id.is_empty() {
from_slint_code(&format!("{} {{ /*...*/ }}", e.base_type))
} else {
from_slint_code(&format!("{} := {} {{ /*...*/ }}", e.id, e.base_type))
}
}
TokenInfo::NamedReference(nr) => {
let prop_info = nr.element().borrow().lookup_property(nr.name());
from_prop_result(prop_info)?
}
TokenInfo::EnumerationValue(v) => from_slint_code(&format!("{}.{}", v.enumeration.name, v)),
TokenInfo::FileName(_) => return None,
// TODO
TokenInfo::LocalProperty(_) => return None,
TokenInfo::LocalCallback(_) => return None,
TokenInfo::IncompleteNamedReference(el, name) => {
let prop_info = el.lookup_property(&name);
from_prop_result(prop_info)?
}
};

Some(Hover { contents: HoverContents::Markup(contents), range: None })
}

fn from_prop_result(prop_info: PropertyLookupResult) -> Option<MarkupContent> {
let pure = if prop_info.declared_pure.is_some_and(|x| x) { "pure " } else { "" };
if let Type::Callback { return_type, args } = &prop_info.property_type {
let ret = return_type.as_ref().map(|x| format!(" -> {}", x)).unwrap_or_default();
let args = args.iter().map(|x| x.to_string()).join(", ");
Some(from_slint_code(&format!("{pure}callback {}({args}){ret}", prop_info.resolved_name)))
} else if let Type::Function { return_type, args } = &prop_info.property_type {
let ret = if matches!(**return_type, Type::Void) {
String::new()
} else {
format!(" -> {return_type}")
};
let args = args.iter().map(|x| x.to_string()).join(", ");
Some(from_slint_code(&format!("{pure}function {}({args}){ret}", prop_info.resolved_name)))
} else if prop_info.property_type.is_property_type() {
Some(from_slint_code(&format!(
"property <{}> {}",
prop_info.property_type, prop_info.resolved_name
)))
} else {
None
}
}

fn from_plain_text(value: String) -> MarkupContent {
MarkupContent { kind: lsp_types::MarkupKind::PlainText, value }
}

fn from_slint_code(value: &str) -> MarkupContent {
MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
value: format!("```slint\n{value}\n```"),
}
}

0 comments on commit 5911d11

Please sign in to comment.