feat: associate request function

This commit is contained in:
Myriad-Dreamin 2024-03-07 16:31:16 +08:00
parent 0c7e67ed9a
commit 50ca444915
9 changed files with 215 additions and 206 deletions

View file

@ -7,20 +7,23 @@ pub struct CompletionRequest {
pub explicit: bool, pub explicit: bool,
} }
pub fn completion( impl CompletionRequest {
world: &TypstSystemWorld, pub fn request(
doc: Option<Arc<TypstDocument>>, self,
req: CompletionRequest, world: &TypstSystemWorld,
position_encoding: PositionEncoding, doc: Option<Arc<TypstDocument>>,
) -> Option<CompletionResponse> { position_encoding: PositionEncoding,
let source = get_suitable_source_in_workspace(world, &req.path).ok()?; ) -> Option<CompletionResponse> {
let typst_offset = lsp_to_typst::position_to_offset(req.position, position_encoding, &source); let source = get_suitable_source_in_workspace(world, &self.path).ok()?;
let typst_offset =
lsp_to_typst::position_to_offset(self.position, position_encoding, &source);
let (typst_start_offset, completions) = let (typst_start_offset, completions) =
typst_ide::autocomplete(world, doc.as_deref(), &source, typst_offset, req.explicit)?; typst_ide::autocomplete(world, doc.as_deref(), &source, typst_offset, self.explicit)?;
let lsp_start_position = let lsp_start_position =
typst_to_lsp::offset_to_position(typst_start_offset, position_encoding, &source); typst_to_lsp::offset_to_position(typst_start_offset, position_encoding, &source);
let replace_range = LspRawRange::new(lsp_start_position, req.position); let replace_range = LspRawRange::new(lsp_start_position, self.position);
Some(typst_to_lsp::completions(&completions, replace_range).into()) Some(typst_to_lsp::completions(&completions, replace_range).into())
}
} }

View file

@ -5,17 +5,19 @@ pub struct DocumentSymbolRequest {
pub path: PathBuf, pub path: PathBuf,
} }
pub fn document_symbol( impl DocumentSymbolRequest {
world: &TypstSystemWorld, pub fn request(
req: DocumentSymbolRequest, self,
position_encoding: PositionEncoding, world: &TypstSystemWorld,
) -> Option<DocumentSymbolResponse> { position_encoding: PositionEncoding,
let source = get_suitable_source_in_workspace(world, &req.path).ok()?; ) -> Option<DocumentSymbolResponse> {
let source = get_suitable_source_in_workspace(world, &self.path).ok()?;
let uri = Url::from_file_path(req.path).unwrap(); let uri = Url::from_file_path(self.path).unwrap();
let symbols = get_document_symbols(source, uri, position_encoding); let symbols = get_document_symbols(source, uri, position_encoding);
symbols.map(DocumentSymbolResponse::Flat) symbols.map(DocumentSymbolResponse::Flat)
}
} }
#[comemo::memoize] #[comemo::memoize]

View file

@ -6,22 +6,25 @@ pub struct HoverRequest {
pub position: LspPosition, pub position: LspPosition,
} }
pub fn hover( impl HoverRequest {
world: &TypstSystemWorld, pub fn request(
doc: Option<Arc<TypstDocument>>, self,
req: HoverRequest, world: &TypstSystemWorld,
position_encoding: PositionEncoding, doc: Option<Arc<TypstDocument>>,
) -> Option<Hover> { position_encoding: PositionEncoding,
let source = get_suitable_source_in_workspace(world, &req.path).ok()?; ) -> Option<Hover> {
let typst_offset = lsp_to_typst::position_to_offset(req.position, position_encoding, &source); let source = get_suitable_source_in_workspace(world, &self.path).ok()?;
let typst_offset =
lsp_to_typst::position_to_offset(self.position, position_encoding, &source);
let typst_tooltip = typst_ide::tooltip(world, doc.as_deref(), &source, typst_offset)?; let typst_tooltip = typst_ide::tooltip(world, doc.as_deref(), &source, typst_offset)?;
let ast_node = LinkedNode::new(source.root()).leaf_at(typst_offset)?; let ast_node = LinkedNode::new(source.root()).leaf_at(typst_offset)?;
let range = typst_to_lsp::range(ast_node.range(), &source, position_encoding); let range = typst_to_lsp::range(ast_node.range(), &source, position_encoding);
Some(Hover { Some(Hover {
contents: typst_to_lsp::tooltip(&typst_tooltip), contents: typst_to_lsp::tooltip(&typst_tooltip),
range: Some(range.raw_range), range: Some(range.raw_range),
}) })
}
} }

View file

@ -6,22 +6,25 @@ pub struct SelectionRangeRequest {
pub positions: Vec<LspPosition>, pub positions: Vec<LspPosition>,
} }
pub fn selection_range( impl SelectionRangeRequest {
world: &TypstSystemWorld, pub fn request(
req: SelectionRangeRequest, self,
position_encoding: PositionEncoding, world: &TypstSystemWorld,
) -> Option<Vec<SelectionRange>> { position_encoding: PositionEncoding,
let source = get_suitable_source_in_workspace(world, &req.path).ok()?; ) -> Option<Vec<SelectionRange>> {
let source = get_suitable_source_in_workspace(world, &self.path).ok()?;
let mut ranges = Vec::new(); let mut ranges = Vec::new();
for position in req.positions { for position in self.positions {
let typst_offset = lsp_to_typst::position_to_offset(position, position_encoding, &source); let typst_offset =
let tree = LinkedNode::new(source.root()); lsp_to_typst::position_to_offset(position, position_encoding, &source);
let leaf = tree.leaf_at(typst_offset)?; let tree = LinkedNode::new(source.root());
ranges.push(range_for_node(&source, position_encoding, &leaf)); let leaf = tree.leaf_at(typst_offset)?;
ranges.push(range_for_node(&source, position_encoding, &leaf));
}
Some(ranges)
} }
Some(ranges)
} }
fn range_for_node( fn range_for_node(

View file

@ -6,32 +6,34 @@ pub struct SemanticTokensDeltaRequest {
pub previous_result_id: String, pub previous_result_id: String,
} }
pub fn semantic_tokens_delta( impl SemanticTokensDeltaRequest {
cache: &SemanticTokenCache, pub fn request(
source: Source, self,
req: SemanticTokensDeltaRequest, cache: &SemanticTokenCache,
position_encoding: PositionEncoding, source: Source,
) -> Option<SemanticTokensFullDeltaResult> { position_encoding: PositionEncoding,
let (tokens, result_id) = cache.try_semantic_tokens_delta_from_result_id( ) -> Option<SemanticTokensFullDeltaResult> {
&source, let (tokens, result_id) = cache.try_semantic_tokens_delta_from_result_id(
&req.previous_result_id, &source,
position_encoding, &self.previous_result_id,
); position_encoding,
);
match tokens { match tokens {
Ok(edits) => Some( Ok(edits) => Some(
SemanticTokensDelta { SemanticTokensDelta {
result_id: Some(result_id), result_id: Some(result_id),
edits, edits,
} }
.into(), .into(),
), ),
Err(tokens) => Some( Err(tokens) => Some(
SemanticTokens { SemanticTokens {
result_id: Some(result_id), result_id: Some(result_id),
data: tokens, data: tokens,
} }
.into(), .into(),
), ),
}
} }
} }

View file

@ -5,19 +5,21 @@ pub struct SemanticTokensFullRequest {
pub path: PathBuf, pub path: PathBuf,
} }
pub fn semantic_tokens_full( impl SemanticTokensFullRequest {
cache: &SemanticTokenCache, pub fn request(
source: Source, self,
_req: SemanticTokensFullRequest, cache: &SemanticTokenCache,
position_encoding: PositionEncoding, source: Source,
) -> Option<SemanticTokensResult> { position_encoding: PositionEncoding,
let (tokens, result_id) = cache.get_semantic_tokens_full(&source, position_encoding); ) -> Option<SemanticTokensResult> {
let (tokens, result_id) = cache.get_semantic_tokens_full(&source, position_encoding);
Some( Some(
SemanticTokens { SemanticTokens {
result_id: Some(result_id), result_id: Some(result_id),
data: tokens, data: tokens,
} }
.into(), .into(),
) )
}
} }

View file

@ -6,73 +6,76 @@ pub struct SignatureHelpRequest {
pub position: LspPosition, pub position: LspPosition,
} }
pub fn signature_help( impl SignatureHelpRequest {
world: &TypstSystemWorld, pub fn request(
SignatureHelpRequest { path, position }: SignatureHelpRequest, self,
position_encoding: PositionEncoding, world: &TypstSystemWorld,
) -> Option<SignatureHelp> { position_encoding: PositionEncoding,
let source = get_suitable_source_in_workspace(world, &path).ok()?; ) -> Option<SignatureHelp> {
let typst_offset = lsp_to_typst::position_to_offset(position, position_encoding, &source); let source = get_suitable_source_in_workspace(world, &self.path).ok()?;
let typst_offset =
lsp_to_typst::position_to_offset(self.position, position_encoding, &source);
let ast_node = LinkedNode::new(source.root()).leaf_at(typst_offset)?; let ast_node = LinkedNode::new(source.root()).leaf_at(typst_offset)?;
let (callee, callee_node, args) = surrounding_function_syntax(&ast_node)?; let (callee, callee_node, args) = surrounding_function_syntax(&ast_node)?;
let mut ancestor = &ast_node; let mut ancestor = &ast_node;
while !ancestor.is::<ast::Expr>() { while !ancestor.is::<ast::Expr>() {
ancestor = ancestor.parent()?; ancestor = ancestor.parent()?;
}
if !callee.hash() && !matches!(callee, ast::Expr::MathIdent(_)) {
return None;
}
let values = analyze_expr(world, &callee_node);
let function = values.into_iter().find_map(|v| match v {
Value::Func(f) => Some(f),
_ => None,
})?;
trace!("got function {function:?}");
let param_index = param_index_at_leaf(&ast_node, &function, args);
let label = format!(
"{}({}){}",
function.name().unwrap_or("<anonymous closure>"),
match function.params() {
Some(params) => params
.iter()
.map(typst_to_lsp::param_info_to_label)
.join(", "),
None => "".to_owned(),
},
match function.returns() {
Some(returns) => format!("-> {}", typst_to_lsp::cast_info_to_label(returns)),
None => "".to_owned(),
} }
);
let params = function
.params()
.unwrap_or_default()
.iter()
.map(typst_to_lsp::param_info)
.collect();
trace!("got signature info {label} {params:?}");
let documentation = function.docs().map(markdown_docs); if !callee.hash() && !matches!(callee, ast::Expr::MathIdent(_)) {
return None;
}
let active_parameter = param_index.map(|i| i as u32); let values = analyze_expr(world, &callee_node);
Some(SignatureHelp { let function = values.into_iter().find_map(|v| match v {
signatures: vec![SignatureInformation { Value::Func(f) => Some(f),
label, _ => None,
documentation, })?;
parameters: Some(params), trace!("got function {function:?}");
active_parameter,
}], let param_index = param_index_at_leaf(&ast_node, &function, args);
active_signature: Some(0),
active_parameter: None, let label = format!(
}) "{}({}){}",
function.name().unwrap_or("<anonymous closure>"),
match function.params() {
Some(params) => params
.iter()
.map(typst_to_lsp::param_info_to_label)
.join(", "),
None => "".to_owned(),
},
match function.returns() {
Some(returns) => format!("-> {}", typst_to_lsp::cast_info_to_label(returns)),
None => "".to_owned(),
}
);
let params = function
.params()
.unwrap_or_default()
.iter()
.map(typst_to_lsp::param_info)
.collect();
trace!("got signature info {label} {params:?}");
let documentation = function.docs().map(markdown_docs);
let active_parameter = param_index.map(|i| i as u32);
Some(SignatureHelp {
signatures: vec![SignatureInformation {
label,
documentation,
parameters: Some(params),
active_parameter,
}],
active_signature: Some(0),
active_parameter: None,
})
}
} }
fn surrounding_function_syntax<'b>( fn surrounding_function_syntax<'b>(

View file

@ -8,32 +8,34 @@ pub struct SymbolRequest {
pub pattern: Option<String>, pub pattern: Option<String>,
} }
pub fn symbol( impl SymbolRequest {
world: &TypstSystemWorld, pub fn request(
SymbolRequest { pattern }: SymbolRequest, self,
position_encoding: PositionEncoding, world: &TypstSystemWorld,
) -> Option<Vec<SymbolInformation>> { position_encoding: PositionEncoding,
// todo: expose source ) -> Option<Vec<SymbolInformation>> {
// todo: expose source
let mut symbols = vec![]; let mut symbols = vec![];
world.iter_dependencies(&mut |path, _| { world.iter_dependencies(&mut |path, _| {
let Ok(source) = get_suitable_source_in_workspace(world, path) else { let Ok(source) = get_suitable_source_in_workspace(world, path) else {
return; return;
}; };
let uri = Url::from_file_path(path).unwrap(); let uri = Url::from_file_path(path).unwrap();
let res = get_document_symbols(source, uri, position_encoding).and_then(|symbols| { let res = get_document_symbols(source, uri, position_encoding).and_then(|symbols| {
pattern self.pattern
.as_ref() .as_ref()
.map(|pattern| filter_document_symbols(symbols, pattern)) .map(|pattern| filter_document_symbols(symbols, pattern))
});
if let Some(mut res) = res {
symbols.append(&mut res)
}
}); });
if let Some(mut res) = res { Some(symbols)
symbols.append(&mut res) }
}
});
Some(symbols)
} }
fn filter_document_symbols( fn filter_document_symbols(

View file

@ -7,7 +7,9 @@ use std::{
use anyhow::anyhow; use anyhow::anyhow;
use futures::future::join_all; use futures::future::join_all;
use log::{error, trace, warn}; use log::{error, trace, warn};
use tinymist_query::{LspDiagnostic, LspRange, PositionEncoding, SemanticTokenCache}; use tinymist_query::{
DiagnosticsMap, LspDiagnostic, LspRange, PositionEncoding, SemanticTokenCache,
};
use tokio::sync::{broadcast, mpsc, watch, Mutex, RwLock}; use tokio::sync::{broadcast, mpsc, watch, Mutex, RwLock};
use tower_lsp::lsp_types::{ use tower_lsp::lsp_types::{
CompletionResponse, DocumentSymbolResponse, Hover, SelectionRange, CompletionResponse, DocumentSymbolResponse, Hover, SelectionRange,
@ -42,9 +44,6 @@ type CompileService<H> = CompileActor<Reporter<CompileExporter<CompileDriver>, H
type CompileClient<H> = TsCompileClient<CompileService<H>>; type CompileClient<H> = TsCompileClient<CompileService<H>>;
type DiagnosticsSender = mpsc::UnboundedSender<(String, DiagnosticsMap)>; type DiagnosticsSender = mpsc::UnboundedSender<(String, DiagnosticsMap)>;
type DiagnosticsMap = HashMap<Url, Vec<LspDiagnostic>>;
// type Client = TypstClient<CompileHandler>;
pub struct CompileCluster { pub struct CompileCluster {
position_encoding: PositionEncoding, position_encoding: PositionEncoding,
@ -65,7 +64,7 @@ pub fn create_cluster(
let primary = create_server( let primary = create_server(
"primary".to_owned(), "primary".to_owned(),
cfg, cfg,
create_compiler(roots.clone(), opts.clone()), CompileDriver::new(roots.clone(), opts.clone()),
diag_tx, diag_tx,
); );
@ -83,11 +82,6 @@ pub fn create_cluster(
} }
} }
fn create_compiler(roots: Vec<PathBuf>, opts: CompileOpts) -> CompileDriver {
let world = TypstSystemWorld::new(opts).expect("incorrect options");
CompileDriver::new(world, roots)
}
fn create_server( fn create_server(
diag_group: String, diag_group: String,
cfg: &ConstConfig, cfg: &ConstConfig,
@ -323,31 +317,31 @@ pub enum CompilerQueryResponse {
} }
macro_rules! query_state { macro_rules! query_state {
($self:ident, $method:ident, $query:expr, $req:expr) => {{ ($self:ident, $method:ident, $req:expr) => {{
let doc = $self.handler.result.lock().unwrap().clone().ok(); let doc = $self.handler.result.lock().unwrap().clone().ok();
let enc = $self.position_encoding; let enc = $self.position_encoding;
let res = $self.steal_world(move |w| $query(w, doc, $req, enc)).await; let res = $self.steal_world(move |w| $req.request(w, doc, enc)).await;
res.map(CompilerQueryResponse::$method) res.map(CompilerQueryResponse::$method)
}}; }};
} }
macro_rules! query_world { macro_rules! query_world {
($self:ident, $method:ident, $query:expr, $req:expr) => {{ ($self:ident, $method:ident, $req:expr) => {{
let enc = $self.position_encoding; let enc = $self.position_encoding;
let res = $self.steal_world(move |w| $query(w, $req, enc)).await; let res = $self.steal_world(move |w| $req.request(w, enc)).await;
res.map(CompilerQueryResponse::$method) res.map(CompilerQueryResponse::$method)
}}; }};
} }
macro_rules! query_tokens_cache { macro_rules! query_tokens_cache {
($self:ident, $method:ident, $query:expr, $req:expr) => {{ ($self:ident, $method:ident, $req:expr) => {{
let path: ImmutPath = $req.path.clone().into(); let path: ImmutPath = $req.path.clone().into();
let vfs = $self.memory_changes.read().await; let vfs = $self.memory_changes.read().await;
let snapshot = vfs.get(&path).ok_or_else(|| anyhow!("file missing"))?; let snapshot = vfs.get(&path).ok_or_else(|| anyhow!("file missing"))?;
let source = snapshot.content.clone(); let source = snapshot.content.clone();
let enc = $self.position_encoding; let enc = $self.position_encoding;
let res = $query(&$self.tokens_cache, source, $req, enc); let res = $req.request(&$self.tokens_cache, source, enc);
Ok(CompilerQueryResponse::$method(res)) Ok(CompilerQueryResponse::$method(res))
}}; }};
} }
@ -357,16 +351,11 @@ impl CompileCluster {
&self, &self,
query: CompilerQueryRequest, query: CompilerQueryRequest,
) -> anyhow::Result<CompilerQueryResponse> { ) -> anyhow::Result<CompilerQueryResponse> {
use tinymist_query::*;
use CompilerQueryRequest::*; use CompilerQueryRequest::*;
match query { match query {
SemanticTokensFull(req) => { SemanticTokensFull(req) => query_tokens_cache!(self, SemanticTokensFull, req),
query_tokens_cache!(self, SemanticTokensFull, semantic_tokens_full, req) SemanticTokensDelta(req) => query_tokens_cache!(self, SemanticTokensDelta, req),
}
SemanticTokensDelta(req) => {
query_tokens_cache!(self, SemanticTokensDelta, semantic_tokens_delta, req)
}
_ => self.primary.query(query).await, _ => self.primary.query(query).await,
} }
} }
@ -415,7 +404,8 @@ impl CompileMiddleware for CompileDriver {
} }
impl CompileDriver { impl CompileDriver {
fn new(world: TypstSystemWorld, roots: Vec<PathBuf>) -> Self { fn new(roots: Vec<PathBuf>, opts: CompileOpts) -> Self {
let world = TypstSystemWorld::new(opts).expect("incorrect options");
let driver = CompileDriverInner::new(world); let driver = CompileDriverInner::new(world);
Self { Self {
@ -679,7 +669,6 @@ impl<H: CompilationHandle> CompileNode<H> {
&self, &self,
query: CompilerQueryRequest, query: CompilerQueryRequest,
) -> anyhow::Result<CompilerQueryResponse> { ) -> anyhow::Result<CompilerQueryResponse> {
use tinymist_query::*;
use CompilerQueryRequest::*; use CompilerQueryRequest::*;
match query { match query {
@ -687,12 +676,12 @@ impl<H: CompilationHandle> CompileNode<H> {
self.on_save_export(path).await?; self.on_save_export(path).await?;
Ok(CompilerQueryResponse::OnSaveExport(())) Ok(CompilerQueryResponse::OnSaveExport(()))
} }
Hover(req) => query_state!(self, Hover, hover, req), Hover(req) => query_state!(self, Hover, req),
Completion(req) => query_state!(self, Completion, completion, req), Completion(req) => query_state!(self, Completion, req),
SignatureHelp(req) => query_world!(self, SignatureHelp, signature_help, req), SignatureHelp(req) => query_world!(self, SignatureHelp, req),
DocumentSymbol(req) => query_world!(self, DocumentSymbol, document_symbol, req), DocumentSymbol(req) => query_world!(self, DocumentSymbol, req),
Symbol(req) => query_world!(self, Symbol, symbol, req), Symbol(req) => query_world!(self, Symbol, req),
SelectionRange(req) => query_world!(self, SelectionRange, selection_range, req), SelectionRange(req) => query_world!(self, SelectionRange, req),
CompilerQueryRequest::SemanticTokensDelta(..) CompilerQueryRequest::SemanticTokensDelta(..)
| CompilerQueryRequest::SemanticTokensFull(..) => unreachable!(), | CompilerQueryRequest::SemanticTokensFull(..) => unreachable!(),
} }