mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 06:11:16 +00:00
LSP: code action to preview a particular component
This commit is contained in:
parent
ea5e611f41
commit
bcce36385d
2 changed files with 112 additions and 35 deletions
|
@ -16,11 +16,12 @@ use std::path::Path;
|
|||
|
||||
use lsp_server::{Connection, Message, Request, RequestId, Response};
|
||||
use lsp_types::notification::{DidChangeTextDocument, DidOpenTextDocument, Notification};
|
||||
use lsp_types::request::GotoDefinition;
|
||||
use lsp_types::request::{CodeActionRequest, ExecuteCommand, GotoDefinition};
|
||||
use lsp_types::request::{Completion, HoverRequest};
|
||||
use lsp_types::{
|
||||
CompletionItem, CompletionItemKind, CompletionOptions, DidChangeTextDocumentParams,
|
||||
DidOpenTextDocumentParams, GotoDefinitionResponse, Hover, HoverProviderCapability,
|
||||
CodeActionOrCommand, CodeActionProviderCapability, Command, CompletionItem, CompletionItemKind,
|
||||
CompletionOptions, DidChangeTextDocumentParams, DidOpenTextDocumentParams,
|
||||
ExecuteCommandOptions, GotoDefinitionResponse, Hover, HoverProviderCapability,
|
||||
InitializeParams, LocationLink, OneOf, Position, PublishDiagnosticsParams, Range,
|
||||
ServerCapabilities, TextDocumentSyncCapability, Url, WorkDoneProgressOptions,
|
||||
};
|
||||
|
@ -33,6 +34,8 @@ use sixtyfps_compilerlib::{object_tree, CompilerConfiguration};
|
|||
|
||||
type Error = Box<dyn std::error::Error>;
|
||||
|
||||
const SHOW_PREVIEW_COMMAND: &str = "showPreview";
|
||||
|
||||
struct DocumentCache<'a> {
|
||||
documents: TypeLoader<'a>,
|
||||
newline_offsets: HashMap<Url, Vec<u32>>,
|
||||
|
@ -76,7 +79,11 @@ fn run_lsp_server() -> Result<(), Error> {
|
|||
text_document_sync: Some(TextDocumentSyncCapability::Kind(
|
||||
lsp_types::TextDocumentSyncKind::Full,
|
||||
)),
|
||||
|
||||
code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
|
||||
execute_command_provider: Some(ExecuteCommandOptions {
|
||||
commands: vec![SHOW_PREVIEW_COMMAND.into()],
|
||||
..Default::default()
|
||||
}),
|
||||
..ServerCapabilities::default()
|
||||
};
|
||||
let server_capabilities = serde_json::to_value(&capabilities).unwrap();
|
||||
|
@ -118,12 +125,20 @@ fn handle_request(
|
|||
) -> Result<(), Error> {
|
||||
let mut req = Some(req);
|
||||
if let Some((id, params)) = cast::<GotoDefinition>(&mut req) {
|
||||
let result = token_descr(document_cache, params.text_document_position_params)
|
||||
let result = token_descr(
|
||||
document_cache,
|
||||
params.text_document_position_params.text_document,
|
||||
params.text_document_position_params.position,
|
||||
)
|
||||
.and_then(|token| goto_definition(document_cache, token.parent()));
|
||||
let resp = Response::new_ok(id, result);
|
||||
connection.sender.send(Message::Response(resp))?;
|
||||
} else if let Some((id, params)) = cast::<Completion>(&mut req) {
|
||||
let result = token_descr(document_cache, params.text_document_position)
|
||||
let result = token_descr(
|
||||
document_cache,
|
||||
params.text_document_position.text_document,
|
||||
params.text_document_position.position,
|
||||
)
|
||||
.and_then(|token| completion_at(document_cache, token.parent()));
|
||||
let resp = Response::new_ok(id, result);
|
||||
connection.sender.send(Message::Response(resp))?;
|
||||
|
@ -139,6 +154,18 @@ fn handle_request(
|
|||
let resp = Response::new_ok(id, result);
|
||||
connection.sender.send(Message::Response(resp))?;*/
|
||||
connection.sender.send(Message::Response(Response::new_ok(id, None::<Hover>)))?;
|
||||
} else if let Some((id, params)) = cast::<CodeActionRequest>(&mut req) {
|
||||
let result = token_descr(document_cache, params.text_document, params.range.start)
|
||||
.and_then(|token| get_code_actions(document_cache, token.parent()));
|
||||
connection.sender.send(Message::Response(Response::new_ok(id, result)))?;
|
||||
} else if let Some((id, params)) = cast::<ExecuteCommand>(&mut req) {
|
||||
match params.command.as_str() {
|
||||
SHOW_PREVIEW_COMMAND => show_preview_command(¶ms.arguments, connection)?,
|
||||
_ => (),
|
||||
}
|
||||
connection
|
||||
.sender
|
||||
.send(Message::Response(Response::new_ok(id, None::<serde_json::Value>)))?;
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
@ -180,21 +207,31 @@ fn handle_notification(
|
|||
)?;
|
||||
}
|
||||
"sixtyfps/showPreview" => {
|
||||
show_preview_command(req.params.as_array().map_or(&[], |x| x.as_slice()), connection)?;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn show_preview_command(
|
||||
params: &[serde_json::Value],
|
||||
connection: &Connection,
|
||||
) -> Result<(), Error> {
|
||||
let e = || -> Error { "InvalidParameter".into() };
|
||||
let path = if let serde_json::Value::String(s) = req.params.get(0).ok_or_else(e)? {
|
||||
let path = if let serde_json::Value::String(s) = params.get(0).ok_or_else(e)? {
|
||||
std::path::PathBuf::from(s)
|
||||
} else {
|
||||
return Err(e());
|
||||
};
|
||||
let path_canon = path.canonicalize().unwrap_or_else(|_| path.to_owned());
|
||||
let component = params.get(1).and_then(|v| v.as_str()).map(|v| v.to_string());
|
||||
preview::load_preview(
|
||||
connection.sender.clone(),
|
||||
path_canon.into(),
|
||||
component,
|
||||
preview::PostLoadBehavior::ShowAfterLoad,
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -279,18 +316,14 @@ fn to_range(span: (usize, usize)) -> Range {
|
|||
}
|
||||
|
||||
fn token_descr(
|
||||
document_cache: &DocumentCache,
|
||||
lsp_position: lsp_types::TextDocumentPositionParams,
|
||||
document_cache: &mut DocumentCache,
|
||||
text_document: lsp_types::TextDocumentIdentifier,
|
||||
pos: Position,
|
||||
) -> Option<sixtyfps_compilerlib::parser::SyntaxToken> {
|
||||
let o = document_cache
|
||||
.newline_offsets
|
||||
.get(&lsp_position.text_document.uri)?
|
||||
.get(lsp_position.position.line as usize)?
|
||||
+ lsp_position.position.character as u32;
|
||||
let o = document_cache.newline_offsets.get(&text_document.uri)?.get(pos.line as usize)?
|
||||
+ pos.character as u32;
|
||||
|
||||
let doc = document_cache
|
||||
.documents
|
||||
.get_document(&lsp_position.text_document.uri.to_file_path().ok()?)?;
|
||||
let doc = document_cache.documents.get_document(&text_document.uri.to_file_path().ok()?)?;
|
||||
let node = doc.node.as_ref()?;
|
||||
if !node.text_range().contains(o.into()) {
|
||||
return None;
|
||||
|
@ -379,6 +412,34 @@ fn goto_node(
|
|||
}]))
|
||||
}
|
||||
|
||||
fn get_code_actions(
|
||||
_document_cache: &mut DocumentCache,
|
||||
node: SyntaxNode,
|
||||
) -> Option<Vec<CodeActionOrCommand>> {
|
||||
let component = syntax_nodes::Component::new(node.clone())
|
||||
.or_else(|| {
|
||||
syntax_nodes::DeclaredIdentifier::new(node.clone())
|
||||
.and_then(|n| n.parent())
|
||||
.and_then(|p| syntax_nodes::Component::new(p))
|
||||
})
|
||||
.or_else(|| {
|
||||
syntax_nodes::QualifiedName::new(node.clone())
|
||||
.and_then(|n| n.parent())
|
||||
.and_then(|p| syntax_nodes::Element::new(p))
|
||||
.and_then(|n| n.parent())
|
||||
.and_then(|p| syntax_nodes::Component::new(p))
|
||||
})?;
|
||||
|
||||
let component_name =
|
||||
sixtyfps_compilerlib::parser::identifier_text(&component.DeclaredIdentifier())?;
|
||||
|
||||
Some(vec![CodeActionOrCommand::Command(Command::new(
|
||||
"Show preview".into(),
|
||||
SHOW_PREVIEW_COMMAND.into(),
|
||||
Some(vec![component.source_file.path().to_string_lossy().into(), component_name.into()]),
|
||||
))])
|
||||
}
|
||||
|
||||
fn completion_at(document_cache: &DocumentCache, token: SyntaxNode) -> Option<Vec<CompletionItem>> {
|
||||
match token.kind() {
|
||||
SyntaxKind::Element => {
|
||||
|
|
|
@ -68,11 +68,12 @@ pub enum PostLoadBehavior {
|
|||
pub fn load_preview(
|
||||
sender: crossbeam_channel::Sender<Message>,
|
||||
path: std::path::PathBuf,
|
||||
component: Option<String>,
|
||||
post_load_behavior: PostLoadBehavior,
|
||||
) {
|
||||
run_in_ui_thread(Box::pin(
|
||||
async move { reload_preview(sender, &path, post_load_behavior).await },
|
||||
));
|
||||
run_in_ui_thread(Box::pin(async move {
|
||||
reload_preview(sender, &path, component, post_load_behavior).await
|
||||
}));
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -80,6 +81,7 @@ struct ContentCache {
|
|||
source_code: HashMap<PathBuf, String>,
|
||||
dependency: HashSet<PathBuf>,
|
||||
current_root: PathBuf,
|
||||
current_component: Option<String>,
|
||||
sender: Option<crossbeam_channel::Sender<Message>>,
|
||||
}
|
||||
|
||||
|
@ -91,10 +93,11 @@ pub fn set_contents(path: &Path, content: String) {
|
|||
cache.source_code.insert(path.to_owned(), content);
|
||||
if cache.dependency.contains(path) {
|
||||
let current_root = cache.current_root.clone();
|
||||
let current_component = cache.current_component.clone();
|
||||
let sender = cache.sender.take();
|
||||
drop(cache);
|
||||
if let Some(sender) = sender {
|
||||
load_preview(sender, current_root, PostLoadBehavior::DoNothing);
|
||||
load_preview(sender, current_root, current_component, PostLoadBehavior::DoNothing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -111,6 +114,7 @@ fn get_file_from_cache(path: PathBuf) -> Option<String> {
|
|||
async fn reload_preview(
|
||||
sender: crossbeam_channel::Sender<Message>,
|
||||
path: &std::path::Path,
|
||||
component: Option<String>,
|
||||
post_load_behavior: PostLoadBehavior,
|
||||
) {
|
||||
send_notification(&sender, "Loading Preview…", Health::Ok);
|
||||
|
@ -127,7 +131,19 @@ async fn reload_preview(
|
|||
Box::pin(async move { get_file_from_cache(path).map(Result::Ok) })
|
||||
});
|
||||
|
||||
let compiled = if let Some(from_cache) = get_file_from_cache(path.to_owned()) {
|
||||
let compiled = if let Some(mut from_cache) = get_file_from_cache(path.to_owned()) {
|
||||
if let Some(component) = component {
|
||||
from_cache = format!(
|
||||
r#"{}
|
||||
_Preview := Window {{
|
||||
{} {{
|
||||
width <=> root.width;
|
||||
height <=> root.height;
|
||||
}}
|
||||
}}"#,
|
||||
from_cache, component
|
||||
);
|
||||
}
|
||||
builder.build_from_source(from_cache, path.to_owned()).await
|
||||
} else {
|
||||
builder.build_from_path(path).await
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue