feat: add label view (#570)

* feat: add label view

* fix: typo

* fix: snapshot

* chore: final tune
This commit is contained in:
Myriad-Dreamin 2024-08-27 19:31:57 +08:00 committed by GitHub
parent a7951b3832
commit b8d933615d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 315 additions and 6 deletions

View file

@ -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),

View file

@ -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))?;

View 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()
}

View file

@ -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,

View file

@ -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!(),
}