mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-03 17:58:17 +00:00
feat: add label view (#570)
* feat: add label view * fix: typo * fix: snapshot * chore: final tune
This commit is contained in:
parent
a7951b3832
commit
b8d933615d
10 changed files with 315 additions and 6 deletions
|
@ -36,6 +36,8 @@ mod document_highlight;
|
|||
pub use document_highlight::*;
|
||||
mod document_symbol;
|
||||
pub use document_symbol::*;
|
||||
mod workspace_label;
|
||||
pub use workspace_label::*;
|
||||
mod document_metrics;
|
||||
pub use document_metrics::*;
|
||||
mod folding_range;
|
||||
|
@ -259,6 +261,7 @@ mod polymorphic {
|
|||
OnEnter(OnEnterRequest),
|
||||
|
||||
DocumentMetrics(DocumentMetricsRequest),
|
||||
WorkspaceLabel(WorkspaceLabelRequest),
|
||||
ServerInfo(ServerInfoRequest),
|
||||
}
|
||||
|
||||
|
@ -282,6 +285,7 @@ mod polymorphic {
|
|||
Self::Rename(..) => Mergeable,
|
||||
Self::PrepareRename(..) => Mergeable,
|
||||
Self::DocumentSymbol(..) => ContextFreeUnique,
|
||||
Self::WorkspaceLabel(..) => Mergeable,
|
||||
Self::Symbol(..) => Mergeable,
|
||||
Self::SemanticTokensFull(..) => ContextFreeUnique,
|
||||
Self::SemanticTokensDelta(..) => ContextFreeUnique,
|
||||
|
@ -316,6 +320,7 @@ mod polymorphic {
|
|||
Self::PrepareRename(req) => &req.path,
|
||||
Self::DocumentSymbol(req) => &req.path,
|
||||
Self::Symbol(..) => return None,
|
||||
Self::WorkspaceLabel(..) => return None,
|
||||
Self::SemanticTokensFull(req) => &req.path,
|
||||
Self::SemanticTokensDelta(req) => &req.path,
|
||||
Self::Formatting(req) => &req.path,
|
||||
|
@ -350,6 +355,7 @@ mod polymorphic {
|
|||
Rename(Option<WorkspaceEdit>),
|
||||
DocumentSymbol(Option<DocumentSymbolResponse>),
|
||||
Symbol(Option<Vec<SymbolInformation>>),
|
||||
WorkspaceLabel(Option<Vec<SymbolInformation>>),
|
||||
SemanticTokensFull(Option<SemanticTokensResult>),
|
||||
SemanticTokensDelta(Option<SemanticTokensFullDeltaResult>),
|
||||
Formatting(Option<Vec<TextEdit>>),
|
||||
|
@ -383,6 +389,7 @@ mod polymorphic {
|
|||
Self::Rename(res) => serde_json::to_value(res),
|
||||
Self::DocumentSymbol(res) => serde_json::to_value(res),
|
||||
Self::Symbol(res) => serde_json::to_value(res),
|
||||
Self::WorkspaceLabel(res) => serde_json::to_value(res),
|
||||
Self::SemanticTokensFull(res) => serde_json::to_value(res),
|
||||
Self::SemanticTokensDelta(res) => serde_json::to_value(res),
|
||||
Self::Formatting(res) => serde_json::to_value(res),
|
||||
|
|
|
@ -561,6 +561,20 @@ impl LexicalHierarchyWorker {
|
|||
fn get_ident(&self, node: &LinkedNode) -> anyhow::Result<Option<LexicalInfo>> {
|
||||
let (name, kind) = match node.kind() {
|
||||
SyntaxKind::Label if self.g.affect_symbol() => {
|
||||
// filter out label in code context.
|
||||
let p = node.prev_sibling_kind();
|
||||
if p.is_some_and(|p| {
|
||||
matches!(
|
||||
p,
|
||||
SyntaxKind::LeftBracket
|
||||
| SyntaxKind::LeftBrace
|
||||
| SyntaxKind::LeftParen
|
||||
| SyntaxKind::Comma
|
||||
| SyntaxKind::Colon
|
||||
) || p.is_keyword()
|
||||
}) {
|
||||
return Ok(None);
|
||||
}
|
||||
let ast_node = node
|
||||
.cast::<ast::Label>()
|
||||
.ok_or_else(|| anyhow!("cast to ast node failed: {:?}", node))?;
|
||||
|
|
82
crates/tinymist-query/src/workspace_label.rs
Normal file
82
crates/tinymist-query/src/workspace_label.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use crate::{
|
||||
prelude::*,
|
||||
syntax::{
|
||||
get_lexical_hierarchy, LexicalHierarchy, LexicalKind, LexicalScopeKind, LexicalVarKind,
|
||||
},
|
||||
SemanticRequest,
|
||||
};
|
||||
|
||||
/// The `workspace/label` request resembles [`workspace/symbol`] request but is
|
||||
/// extended for typst cases.
|
||||
///
|
||||
/// [`workspace/symbol`]: https://microsoft.github.io/language-server-protocol/specification#workspace_symbol
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WorkspaceLabelRequest {}
|
||||
|
||||
impl SemanticRequest for WorkspaceLabelRequest {
|
||||
type Response = Vec<SymbolInformation>;
|
||||
|
||||
fn request(self, ctx: &mut AnalysisContext) -> Option<Self::Response> {
|
||||
// todo: let typst.ts expose source
|
||||
|
||||
let mut symbols = vec![];
|
||||
|
||||
for id in ctx.source_files().clone() {
|
||||
let now = reflexo::time::Instant::now();
|
||||
log::info!("workspace/label: {:?}", id);
|
||||
let Ok(source) = ctx.source_by_id(id) else {
|
||||
continue;
|
||||
};
|
||||
let Ok(path) = ctx.path_for_id(id) else {
|
||||
continue;
|
||||
};
|
||||
let uri = path_to_url(&path).unwrap();
|
||||
let res =
|
||||
get_lexical_hierarchy(source.clone(), LexicalScopeKind::Symbol).map(|symbols| {
|
||||
filter_document_labels(&symbols, &source, &uri, ctx.position_encoding())
|
||||
});
|
||||
|
||||
if let Some(mut res) = res {
|
||||
symbols.append(&mut res)
|
||||
}
|
||||
log::info!("workspace/label: {:?} took {:?}", id, now.elapsed());
|
||||
}
|
||||
|
||||
Some(symbols)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn filter_document_labels(
|
||||
symbols: &[LexicalHierarchy],
|
||||
source: &Source,
|
||||
uri: &Url,
|
||||
position_encoding: PositionEncoding,
|
||||
) -> Vec<SymbolInformation> {
|
||||
symbols
|
||||
.iter()
|
||||
.flat_map(|e| {
|
||||
[e].into_iter()
|
||||
.chain(e.children.as_deref().into_iter().flatten())
|
||||
})
|
||||
.flat_map(|e| {
|
||||
if !matches!(e.info.kind, LexicalKind::Var(LexicalVarKind::Label)) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let rng = typst_to_lsp::range(e.info.range.clone(), source, position_encoding);
|
||||
|
||||
Some(SymbolInformation {
|
||||
name: e.info.name.clone(),
|
||||
kind: e.info.kind.clone().try_into().unwrap(),
|
||||
tags: None,
|
||||
deprecated: None,
|
||||
location: LspLocation {
|
||||
uri: uri.clone(),
|
||||
range: rng,
|
||||
},
|
||||
container_name: None,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
|
@ -464,6 +464,15 @@ impl LanguageState {
|
|||
run_query!(req_id, self.DocumentMetrics(path))
|
||||
}
|
||||
|
||||
/// Get all syntatic labels in workspace.
|
||||
pub fn get_workspace_labels(
|
||||
&mut self,
|
||||
req_id: RequestId,
|
||||
_arguments: Vec<JsonValue>,
|
||||
) -> ScheduledResult {
|
||||
run_query!(req_id, self.WorkspaceLabel())
|
||||
}
|
||||
|
||||
/// Get the server info.
|
||||
pub fn get_server_info(
|
||||
&mut self,
|
||||
|
|
|
@ -256,6 +256,7 @@ impl LanguageState {
|
|||
.with_command_("tinymist.interactCodeContext", State::interact_code_context)
|
||||
.with_command("tinymist.getDocumentTrace", State::get_document_trace)
|
||||
.with_command_("tinymist.getDocumentMetrics", State::get_document_metrics)
|
||||
.with_command_("tinymist.getWorkspaceLabels", State::get_workspace_labels)
|
||||
.with_command_("tinymist.getServerInfo", State::get_server_info)
|
||||
// resources
|
||||
.with_resource("/symbols", State::resource_symbols)
|
||||
|
@ -1019,6 +1020,7 @@ impl LanguageState {
|
|||
Rename(req) => handle.run_stateful(snap, req, R::Rename),
|
||||
PrepareRename(req) => handle.run_stateful(snap, req, R::PrepareRename),
|
||||
Symbol(req) => handle.run_semantic(snap, req, R::Symbol),
|
||||
WorkspaceLabel(req) => handle.run_semantic(snap, req, R::WorkspaceLabel),
|
||||
DocumentMetrics(req) => handle.run_stateful(snap, req, R::DocumentMetrics),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue