dev: init summary page (#137)

* feat: add summary frontend

* dev: init summary page

* build: update cargo.lock

* feat: init diagnostics frontend

* build: update snapshot hash
This commit is contained in:
Myriad-Dreamin 2024-04-01 10:16:26 +08:00 committed by GitHub
parent 2e39afde78
commit 7e453872b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 773 additions and 17 deletions

View file

@ -39,6 +39,7 @@ if_chain = "1"
percent-encoding = "2"
unscanny = "0.1"
pathdiff = "0.2"
ttf-parser = "0.20.0"
[dev-dependencies]
once_cell.workspace = true

View file

@ -5,8 +5,9 @@ use std::{
};
use once_cell::sync::OnceCell;
use reflexo::{cow_mut::CowMut, ImmutPath};
use reflexo::{cow_mut::CowMut, debug_loc::DataSource, ImmutPath};
use typst::syntax::FileId as TypstFileId;
use typst::text::Font;
use typst::{
diag::{eco_format, FileError, FileResult, PackageError},
syntax::{package::PackageSpec, Source, VirtualPath},
@ -78,6 +79,11 @@ pub trait AnaylsisResources {
/// Get all the files in the workspace.
fn iter_dependencies(&self, f: &mut dyn FnMut(&ImmutPath, std::time::SystemTime));
/// Resolve extra font information.
fn font_info(&self, _font: Font) -> Option<Arc<DataSource>> {
None
}
}
/// The context for analyzers.

View file

@ -0,0 +1,184 @@
use std::sync::Arc;
use std::{collections::HashMap, path::PathBuf};
use reflexo::debug_loc::DataSource;
use serde::{Deserialize, Serialize};
use typst::text::Font;
use typst::{
layout::{Frame, FrameItem},
model::Document,
text::TextItem,
};
use crate::{AnalysisContext, StatefulRequest, VersionedDocument};
/// Span information for some content.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SpanInfo {
/// The sources that are used in the span information.
pub sources: Vec<DataSource>,
}
/// Annotated content for a font.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AnnotatedContent {
/// A string of the content for slicing.
pub content: String,
/// The kind of the span encoding.
pub span_kind: String,
/// Encoded spans.
pub spans: Vec<i32>,
}
/// Information about a font.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentFontInfo {
/// The display name of the font, which is computed by this crate and
/// unnecessary from any fields of the font file.
pub name: String,
/// The PostScript name of the font.
pub postscript_name: Option<String>,
/// The Family in font file.
pub family: Option<String>,
/// The Full Name in font file.
pub full_name: Option<String>,
/// The Fixed Family used by Typst.
pub fixed_family: Option<String>,
/// The source of the font.
pub source: Option<u32>,
/// The index of the font in the source.
pub index: Option<u32>,
/// The annotated content length of the font.
/// If it is None, the uses is not calculated.
/// Otherwise, it is the length of the uses.
pub uses_scale: Option<u32>,
/// The annotated content of the font.
/// If it is not None, the uses_scale must be provided.
pub uses: Option<AnnotatedContent>,
}
/// The response to a DocumentMetricsRequest.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentMetricsResponse {
/// File span information.
pub span_info: SpanInfo,
/// Font information.
pub font_info: Vec<DocumentFontInfo>,
}
/// A request to compute DocumentMetrics for a document.
///
/// This is not part of the LSP protocol.
#[derive(Debug, Clone)]
pub struct DocumentMetricsRequest {
/// The path of the document to compute DocumentMetricss.
pub path: PathBuf,
}
impl StatefulRequest for DocumentMetricsRequest {
type Response = DocumentMetricsResponse;
fn request(
self,
ctx: &mut AnalysisContext,
doc: Option<VersionedDocument>,
) -> Option<Self::Response> {
let doc = doc?;
let doc = doc.document;
let mut worker = DocumentMetricsWorker {
ctx,
span_info: Default::default(),
span_info2: Default::default(),
font_info: Default::default(),
};
worker.work(&doc)?;
let font_info = worker.compute()?;
let span_info = SpanInfo {
sources: worker.span_info2,
};
Some(DocumentMetricsResponse {
span_info,
font_info,
})
}
}
struct DocumentMetricsWorker<'a, 'w> {
ctx: &'a mut AnalysisContext<'w>,
span_info: HashMap<Arc<DataSource>, u32>,
span_info2: Vec<DataSource>,
font_info: HashMap<Font, u32>,
}
impl<'a, 'w> DocumentMetricsWorker<'a, 'w> {
fn work(&mut self, doc: &Document) -> Option<()> {
for page in &doc.pages {
self.work_frame(&page.frame)?;
}
Some(())
}
fn work_frame(&mut self, frame: &Frame) -> Option<()> {
for (_, elem) in frame.items() {
self.work_elem(elem)?;
}
Some(())
}
fn work_elem(&mut self, elem: &FrameItem) -> Option<()> {
match elem {
FrameItem::Text(text) => self.work_text(text),
FrameItem::Group(frame) => self.work_frame(&frame.frame),
FrameItem::Shape(..) | FrameItem::Image(..) | FrameItem::Meta(..) => Some(()),
}
}
fn work_text(&mut self, text: &TextItem) -> Option<()> {
let use_cnt = self.font_info.entry(text.font.clone()).or_default();
*use_cnt = use_cnt.checked_add(text.glyphs.len() as u32)?;
Some(())
}
fn internal_source(&mut self, source: Arc<DataSource>) -> u32 {
if let Some(&id) = self.span_info.get(source.as_ref()) {
return id;
}
let id = self.span_info2.len() as u32;
self.span_info2.push(source.as_ref().clone());
self.span_info.insert(source, id);
id
}
fn compute(&mut self) -> Option<Vec<DocumentFontInfo>> {
use ttf_parser::name_id::*;
let font_info = std::mem::take(&mut self.font_info)
.into_iter()
.map(|(font, uses)| {
let extra = self.ctx.resources.font_info(font.clone());
DocumentFontInfo {
name: format!("{} ({:?})", font.info().family, font.info().variant),
postscript_name: font.find_name(POST_SCRIPT_NAME),
full_name: font.find_name(FULL_NAME),
family: font.find_name(FAMILY),
fixed_family: Some(font.info().family.clone()),
source: extra.map(|e| self.internal_source(e)),
index: Some(font.index()),
uses_scale: Some(uses),
uses: None,
}
})
.collect();
Some(font_info)
}
}

View file

@ -26,6 +26,8 @@ pub(crate) mod completion;
pub use completion::*;
pub(crate) mod document_symbol;
pub use document_symbol::*;
pub(crate) mod document_metrics;
pub use document_metrics::*;
pub(crate) mod folding_range;
pub use folding_range::*;
pub(crate) mod goto_declaration;
@ -186,6 +188,8 @@ mod polymorphic {
Formatting(FormattingRequest),
FoldingRange(FoldingRangeRequest),
SelectionRange(SelectionRangeRequest),
DocumentMetrics(DocumentMetricsRequest),
}
impl CompilerQueryRequest {
@ -211,6 +215,8 @@ mod polymorphic {
CompilerQueryRequest::Formatting(..) => ContextFreeUnique,
CompilerQueryRequest::FoldingRange(..) => ContextFreeUnique,
CompilerQueryRequest::SelectionRange(..) => ContextFreeUnique,
CompilerQueryRequest::DocumentMetrics(..) => PinnedFirst,
}
}
@ -235,6 +241,8 @@ mod polymorphic {
CompilerQueryRequest::Formatting(req) => &req.path,
CompilerQueryRequest::FoldingRange(req) => &req.path,
CompilerQueryRequest::SelectionRange(req) => &req.path,
CompilerQueryRequest::DocumentMetrics(req) => &req.path,
})
}
}
@ -260,6 +268,8 @@ mod polymorphic {
Formatting(Option<Vec<TextEdit>>),
FoldingRange(Option<Vec<FoldingRange>>),
SelectionRange(Option<Vec<SelectionRange>>),
DocumentMetrics(Option<DocumentMetricsResponse>),
}
}