mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-24 13:13:43 +00:00
refactor: refactor hover, analysis/global, and docs crates (#755)
* dev: refactor hover.rs * refactor refactor AnalysisContext * refactor: refactor docs crate
This commit is contained in:
parent
1c1bc19caf
commit
8f3566366e
44 changed files with 694 additions and 790 deletions
|
@ -202,7 +202,7 @@ mod module_tests {
|
||||||
ids
|
ids
|
||||||
}
|
}
|
||||||
|
|
||||||
let dependencies = construct_module_dependencies(&mut ctx.local);
|
let dependencies = construct_module_dependencies(ctx);
|
||||||
|
|
||||||
let mut dependencies = dependencies
|
let mut dependencies = dependencies
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -39,7 +39,7 @@ pub struct CallInfo {
|
||||||
// todo: cache call
|
// todo: cache call
|
||||||
/// Analyzes a function call.
|
/// Analyzes a function call.
|
||||||
pub fn analyze_call(
|
pub fn analyze_call(
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
source: Source,
|
source: Source,
|
||||||
node: LinkedNode,
|
node: LinkedNode,
|
||||||
) -> Option<Arc<CallInfo>> {
|
) -> Option<Arc<CallInfo>> {
|
||||||
|
@ -64,7 +64,7 @@ pub fn analyze_call(
|
||||||
/// Analyzes a function call without caching the result.
|
/// Analyzes a function call without caching the result.
|
||||||
// todo: testing
|
// todo: testing
|
||||||
pub fn analyze_call_no_cache(
|
pub fn analyze_call_no_cache(
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
source: Source,
|
source: Source,
|
||||||
callee_node: LinkedNode,
|
callee_node: LinkedNode,
|
||||||
args: ast::Args<'_>,
|
args: ast::Args<'_>,
|
||||||
|
|
|
@ -7,7 +7,7 @@ use typst::visualize::Color;
|
||||||
use super::prelude::*;
|
use super::prelude::*;
|
||||||
|
|
||||||
/// Get color expressions from a source.
|
/// Get color expressions from a source.
|
||||||
pub fn get_color_exprs(ctx: &mut AnalysisContext, src: &Source) -> Option<Vec<ColorInformation>> {
|
pub fn get_color_exprs(ctx: &mut LocalContext, src: &Source) -> Option<Vec<ColorInformation>> {
|
||||||
let mut worker = ColorExprWorker {
|
let mut worker = ColorExprWorker {
|
||||||
ctx,
|
ctx,
|
||||||
source: src.clone(),
|
source: src.clone(),
|
||||||
|
@ -18,13 +18,13 @@ pub fn get_color_exprs(ctx: &mut AnalysisContext, src: &Source) -> Option<Vec<Co
|
||||||
Some(worker.colors)
|
Some(worker.colors)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ColorExprWorker<'a, 'w> {
|
struct ColorExprWorker<'a> {
|
||||||
ctx: &'a mut AnalysisContext<'w>,
|
ctx: &'a mut LocalContext,
|
||||||
source: Source,
|
source: Source,
|
||||||
colors: Vec<ColorInformation>,
|
colors: Vec<ColorInformation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'w> ColorExprWorker<'a, 'w> {
|
impl<'a> ColorExprWorker<'a> {
|
||||||
fn collect_colors(&mut self, node: LinkedNode) -> Option<()> {
|
fn collect_colors(&mut self, node: LinkedNode) -> Option<()> {
|
||||||
match node.kind() {
|
match node.kind() {
|
||||||
SyntaxKind::FuncCall => {
|
SyntaxKind::FuncCall => {
|
||||||
|
|
|
@ -24,8 +24,9 @@ use typst::syntax::{package::PackageSpec, Span, VirtualPath};
|
||||||
|
|
||||||
use crate::analysis::prelude::*;
|
use crate::analysis::prelude::*;
|
||||||
use crate::analysis::{
|
use crate::analysis::{
|
||||||
analyze_bib, analyze_import_, analyze_signature, post_type_check, BibInfo, PathPreference,
|
analyze_bib, analyze_expr_, analyze_import_, analyze_signature, definition, post_type_check,
|
||||||
Signature, SignatureTarget, Ty, TypeScheme,
|
AllocStats, AnalysisStats, BibInfo, Definition, PathPreference, QueryStatGuard, Signature,
|
||||||
|
SignatureTarget, Ty, TypeScheme,
|
||||||
};
|
};
|
||||||
use crate::docs::{DefDocs, TidyModuleDocs};
|
use crate::docs::{DefDocs, TidyModuleDocs};
|
||||||
use crate::syntax::{
|
use crate::syntax::{
|
||||||
|
@ -39,46 +40,44 @@ use crate::{
|
||||||
SemanticTokenContext, TypstRange, VersionedDocument,
|
SemanticTokenContext, TypstRange, VersionedDocument,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{analyze_expr_, definition, AllocStats, AnalysisStats, Definition, QueryStatGuard};
|
|
||||||
|
|
||||||
/// The analysis data holds globally.
|
/// The analysis data holds globally.
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub struct Analysis {
|
pub struct Analysis {
|
||||||
/// The position encoding for the workspace.
|
/// The position encoding for the workspace.
|
||||||
pub position_encoding: PositionEncoding,
|
pub position_encoding: PositionEncoding,
|
||||||
/// The position encoding for the workspace.
|
/// The periscope provider.
|
||||||
pub enable_periscope: bool,
|
pub periscope: Option<Arc<dyn PeriscopeProvider + Send + Sync>>,
|
||||||
/// The global caches for analysis.
|
|
||||||
pub caches: AnalysisGlobalCaches,
|
|
||||||
/// The global caches for analysis.
|
|
||||||
pub workers: Arc<AnalysisGlobalWorkers>,
|
|
||||||
/// The global cache grid for analysis.
|
|
||||||
pub cache_grid: Arc<Mutex<AnalysisGlobalCacheGrid>>,
|
|
||||||
/// The semantic token context.
|
/// The semantic token context.
|
||||||
pub tokens_ctx: Arc<SemanticTokenContext>,
|
pub tokens_ctx: Arc<SemanticTokenContext>,
|
||||||
|
/// The global worker resources for analysis.
|
||||||
|
pub workers: Arc<AnalysisGlobalWorkers>,
|
||||||
|
/// The global caches for analysis.
|
||||||
|
pub caches: AnalysisGlobalCaches,
|
||||||
|
/// The global cache grid for analysis.
|
||||||
|
pub cache_grid: Arc<Mutex<AnalysisGlobalCacheGrid>>,
|
||||||
/// The statistics about the analyzers.
|
/// The statistics about the analyzers.
|
||||||
pub analysis_stats: Arc<AnalysisStats>,
|
pub stats: Arc<AnalysisStats>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Analysis {
|
impl Analysis {
|
||||||
/// Get a snapshot of the analysis data.
|
/// Get a snapshot of the analysis data.
|
||||||
pub fn snapshot<'a>(
|
pub fn snapshot(&self, world: LspWorld) -> LocalContextGuard {
|
||||||
&self,
|
let lifetime = self.caches.lifetime.fetch_add(1, Ordering::SeqCst);
|
||||||
world: LspWorld,
|
let slot = self.cache_grid.lock().find_revision(world.revision());
|
||||||
resources: &'a dyn AnalysisResources,
|
LocalContextGuard {
|
||||||
) -> AnalysisContext<'a> {
|
local: LocalContext {
|
||||||
AnalysisContext::new(world, resources, self.clone())
|
caches: AnalysisCaches::default(),
|
||||||
|
shared: Arc::new(SharedContext {
|
||||||
|
slot,
|
||||||
|
lifetime,
|
||||||
|
world,
|
||||||
|
analysis: self.clone(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear all cached resources.
|
/// Lock the revision in *main thread*.
|
||||||
pub fn clear_cache(&self) {
|
|
||||||
self.caches.signatures.clear();
|
|
||||||
self.caches.static_signatures.clear();
|
|
||||||
self.caches.terms.clear();
|
|
||||||
self.cache_grid.lock().clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lock the revision in main thread.
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn lock_revision(&self) -> RevisionLock {
|
pub fn lock_revision(&self) -> RevisionLock {
|
||||||
let mut grid = self.cache_grid.lock();
|
let mut grid = self.cache_grid.lock();
|
||||||
|
@ -90,9 +89,17 @@ impl Analysis {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clear all cached resources.
|
||||||
|
pub fn clear_cache(&self) {
|
||||||
|
self.caches.signatures.clear();
|
||||||
|
self.caches.static_signatures.clear();
|
||||||
|
self.caches.terms.clear();
|
||||||
|
self.cache_grid.lock().clear();
|
||||||
|
}
|
||||||
|
|
||||||
/// Report the statistics of the analysis.
|
/// Report the statistics of the analysis.
|
||||||
pub fn report_query_stats(&self) -> String {
|
pub fn report_query_stats(&self) -> String {
|
||||||
self.analysis_stats.report()
|
self.stats.report()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Report the statistics of the allocation.
|
/// Report the statistics of the allocation.
|
||||||
|
@ -101,12 +108,12 @@ impl Analysis {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The resources for analysis.
|
/// The periscope provider.
|
||||||
pub trait AnalysisResources {
|
pub trait PeriscopeProvider {
|
||||||
/// Resolve telescope image at the given position.
|
/// Resolve telescope image at the given position.
|
||||||
fn periscope_at(
|
fn periscope_at(
|
||||||
&self,
|
&self,
|
||||||
_ctx: &mut AnalysisContext,
|
_ctx: &mut LocalContext,
|
||||||
_doc: VersionedDocument,
|
_doc: VersionedDocument,
|
||||||
_pos: Position,
|
_pos: Position,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
|
@ -125,15 +132,13 @@ pub struct AnalysisGlobalWorkers {
|
||||||
tooltip: RateLimiter,
|
tooltip: RateLimiter,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The context for analyzers.
|
/// The local context guard that performs gc once dropped.
|
||||||
pub struct AnalysisContext<'a> {
|
pub struct LocalContextGuard {
|
||||||
/// The world surface for Typst compiler
|
/// Constructed local context
|
||||||
pub resources: &'a dyn AnalysisResources,
|
|
||||||
/// Constructed shared context
|
|
||||||
pub local: LocalContext,
|
pub local: LocalContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for AnalysisContext<'_> {
|
impl Deref for LocalContextGuard {
|
||||||
type Target = LocalContext;
|
type Target = LocalContext;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
|
@ -141,118 +146,20 @@ impl Deref for AnalysisContext<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DerefMut for AnalysisContext<'_> {
|
impl DerefMut for LocalContextGuard {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.local
|
&mut self.local
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: gc in new thread
|
// todo: gc in new thread
|
||||||
impl<'w> Drop for AnalysisContext<'w> {
|
impl Drop for LocalContextGuard {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.gc();
|
self.gc();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w> AnalysisContext<'w> {
|
impl LocalContextGuard {
|
||||||
/// Create a new analysis context.
|
|
||||||
pub fn new(world: LspWorld, resources: &'w dyn AnalysisResources, a: Analysis) -> Self {
|
|
||||||
let lifetime = a.caches.lifetime.fetch_add(1, Ordering::SeqCst);
|
|
||||||
let slot = a.cache_grid.lock().find_revision(world.revision());
|
|
||||||
Self {
|
|
||||||
resources,
|
|
||||||
local: LocalContext {
|
|
||||||
caches: AnalysisCaches::default(),
|
|
||||||
shared: Arc::new(SharedContext {
|
|
||||||
slot,
|
|
||||||
lifetime,
|
|
||||||
world,
|
|
||||||
analysis: a,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resolve extra font information.
|
|
||||||
pub fn font_info(&self, font: typst::text::Font) -> Option<Arc<DataSource>> {
|
|
||||||
self.world().font_resolver.describe_font(&font)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the world surface for Typst compiler.
|
|
||||||
pub fn world(&self) -> &LspWorld {
|
|
||||||
&self.shared.world
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the shared context.
|
|
||||||
pub fn shared(&self) -> &Arc<SharedContext> {
|
|
||||||
&self.local.shared
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the shared context.
|
|
||||||
pub fn shared_(&self) -> Arc<SharedContext> {
|
|
||||||
self.local.shared.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fork a new context for searching in the workspace.
|
|
||||||
pub fn fork_for_search<'s>(&'s mut self) -> SearchCtx<'s, 'w> {
|
|
||||||
SearchCtx {
|
|
||||||
ctx: self,
|
|
||||||
searched: Default::default(),
|
|
||||||
worklist: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn preload_package(&self, entry_point: TypstFileId) {
|
|
||||||
self.shared_().preload_package(entry_point);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn with_vm<T>(&self, f: impl FnOnce(&mut typst::eval::Vm) -> T) -> T {
|
|
||||||
crate::upstream::with_vm((self.world() as &dyn World).track(), f)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn const_eval(&self, rr: ast::Expr<'_>) -> Option<Value> {
|
|
||||||
SharedContext::const_eval(rr)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn mini_eval(&self, rr: ast::Expr<'_>) -> Option<Value> {
|
|
||||||
self.const_eval(rr)
|
|
||||||
.or_else(|| self.with_vm(|vm| rr.eval(vm).ok()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn type_of(&mut self, rr: &SyntaxNode) -> Option<Ty> {
|
|
||||||
self.type_of_span(rr.span())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn type_of_span(&mut self, s: Span) -> Option<Ty> {
|
|
||||||
let id = s.id()?;
|
|
||||||
let source = self.source_by_id(id).ok()?;
|
|
||||||
self.type_of_span_(&source, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn type_of_span_(&mut self, source: &Source, s: Span) -> Option<Ty> {
|
|
||||||
self.type_check(source).type_of_span(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn literal_type_of_node(&mut self, k: LinkedNode) -> Option<Ty> {
|
|
||||||
let id = k.span().id()?;
|
|
||||||
let source = self.source_by_id(id).ok()?;
|
|
||||||
let ty_chk = self.type_check(&source);
|
|
||||||
|
|
||||||
let ty = post_type_check(self.shared_(), &ty_chk, k.clone())
|
|
||||||
.or_else(|| ty_chk.type_of_span(k.span()))?;
|
|
||||||
Some(ty_chk.simplify(ty, false))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get module import at location.
|
|
||||||
pub fn module_ins_at(&mut self, def_fid: TypstFileId, cursor: usize) -> Option<Value> {
|
|
||||||
let def_src = self.source_by_id(def_fid).ok()?;
|
|
||||||
let def_root = LinkedNode::new(def_src.root());
|
|
||||||
let mod_exp = find_expr_in_import(def_root.leaf_at_compat(cursor)?)?;
|
|
||||||
let mod_import = mod_exp.parent()?.clone();
|
|
||||||
let mod_import_node = mod_import.cast::<ast::ModuleImport>()?;
|
|
||||||
self.analyze_import(mod_import_node.source().to_untyped()).1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gc(&self) {
|
fn gc(&self) {
|
||||||
let lifetime = self.lifetime;
|
let lifetime = self.lifetime;
|
||||||
loop {
|
loop {
|
||||||
|
@ -297,7 +204,7 @@ impl<'w> AnalysisContext<'w> {
|
||||||
pub struct LocalContext {
|
pub struct LocalContext {
|
||||||
/// Local caches for analysis.
|
/// Local caches for analysis.
|
||||||
pub caches: AnalysisCaches,
|
pub caches: AnalysisCaches,
|
||||||
/// Constructed shared context
|
/// The shared context
|
||||||
pub shared: Arc<SharedContext>,
|
pub shared: Arc<SharedContext>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,6 +281,81 @@ impl LocalContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the world surface for Typst compiler.
|
||||||
|
pub fn world(&self) -> &LspWorld {
|
||||||
|
&self.shared.world
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the shared context.
|
||||||
|
pub fn shared(&self) -> &Arc<SharedContext> {
|
||||||
|
&self.shared
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the shared context.
|
||||||
|
pub fn shared_(&self) -> Arc<SharedContext> {
|
||||||
|
self.shared.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fork a new context for searching in the workspace.
|
||||||
|
pub fn fork_for_search(&mut self) -> SearchCtx {
|
||||||
|
SearchCtx {
|
||||||
|
ctx: self,
|
||||||
|
searched: Default::default(),
|
||||||
|
worklist: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn preload_package(&self, entry_point: TypstFileId) {
|
||||||
|
self.shared_().preload_package(entry_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn with_vm<T>(&self, f: impl FnOnce(&mut typst::eval::Vm) -> T) -> T {
|
||||||
|
crate::upstream::with_vm((self.world() as &dyn World).track(), f)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn const_eval(&self, rr: ast::Expr<'_>) -> Option<Value> {
|
||||||
|
SharedContext::const_eval(rr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn mini_eval(&self, rr: ast::Expr<'_>) -> Option<Value> {
|
||||||
|
self.const_eval(rr)
|
||||||
|
.or_else(|| self.with_vm(|vm| rr.eval(vm).ok()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn type_of(&mut self, rr: &SyntaxNode) -> Option<Ty> {
|
||||||
|
self.type_of_span(rr.span())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn type_of_span(&mut self, s: Span) -> Option<Ty> {
|
||||||
|
let id = s.id()?;
|
||||||
|
let source = self.source_by_id(id).ok()?;
|
||||||
|
self.type_of_span_(&source, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn type_of_span_(&mut self, source: &Source, s: Span) -> Option<Ty> {
|
||||||
|
self.type_check(source).type_of_span(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn literal_type_of_node(&mut self, k: LinkedNode) -> Option<Ty> {
|
||||||
|
let id = k.span().id()?;
|
||||||
|
let source = self.source_by_id(id).ok()?;
|
||||||
|
let ty_chk = self.type_check(&source);
|
||||||
|
|
||||||
|
let ty = post_type_check(self.shared_(), &ty_chk, k.clone())
|
||||||
|
.or_else(|| ty_chk.type_of_span(k.span()))?;
|
||||||
|
Some(ty_chk.simplify(ty, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get module import at location.
|
||||||
|
pub fn module_ins_at(&mut self, def_fid: TypstFileId, cursor: usize) -> Option<Value> {
|
||||||
|
let def_src = self.source_by_id(def_fid).ok()?;
|
||||||
|
let def_root = LinkedNode::new(def_src.root());
|
||||||
|
let mod_exp = find_expr_in_import(def_root.leaf_at_compat(cursor)?)?;
|
||||||
|
let mod_import = mod_exp.parent()?.clone();
|
||||||
|
let mod_import_node = mod_import.cast::<ast::ModuleImport>()?;
|
||||||
|
self.analyze_import(mod_import_node.source().to_untyped()).1
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the expression information of a source file.
|
/// Get the expression information of a source file.
|
||||||
pub(crate) fn expr_stage_by_id(&mut self, fid: TypstFileId) -> Option<Arc<ExprInfo>> {
|
pub(crate) fn expr_stage_by_id(&mut self, fid: TypstFileId) -> Option<Arc<ExprInfo>> {
|
||||||
Some(self.expr_stage(&self.source_by_id(fid).ok()?))
|
Some(self.expr_stage(&self.source_by_id(fid).ok()?))
|
||||||
|
@ -399,11 +381,11 @@ impl LocalContext {
|
||||||
match def.decl.kind() {
|
match def.decl.kind() {
|
||||||
DefKind::Function => {
|
DefKind::Function => {
|
||||||
let sig = self.sig_of_def(def.clone())?;
|
let sig = self.sig_of_def(def.clone())?;
|
||||||
let docs = crate::docs::signature_docs(&sig, None)?;
|
let docs = crate::docs::sig_docs(&sig, None)?;
|
||||||
Some(DefDocs::Function(Box::new(docs)))
|
Some(DefDocs::Function(Box::new(docs)))
|
||||||
}
|
}
|
||||||
DefKind::Struct | DefKind::Constant | DefKind::Variable => {
|
DefKind::Struct | DefKind::Constant | DefKind::Variable => {
|
||||||
let docs = crate::docs::variable_docs(self, def.decl.span())?;
|
let docs = crate::docs::var_docs(self, def.decl.span())?;
|
||||||
Some(DefDocs::Variable(docs))
|
Some(DefDocs::Variable(docs))
|
||||||
}
|
}
|
||||||
DefKind::Module => {
|
DefKind::Module => {
|
||||||
|
@ -421,11 +403,11 @@ impl LocalContext {
|
||||||
pub struct SharedContext {
|
pub struct SharedContext {
|
||||||
/// The caches lifetime tick for analysis.
|
/// The caches lifetime tick for analysis.
|
||||||
pub lifetime: u64,
|
pub lifetime: u64,
|
||||||
/// Get the world surface for Typst compiler.
|
/// The world surface for Typst compiler.
|
||||||
pub world: LspWorld,
|
pub world: LspWorld,
|
||||||
/// The analysis data
|
/// The analysis data
|
||||||
pub analysis: Analysis,
|
pub analysis: Analysis,
|
||||||
/// The revision slot
|
/// The using revision slot
|
||||||
slot: Arc<RevisionSlot>,
|
slot: Arc<RevisionSlot>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,16 +417,6 @@ impl SharedContext {
|
||||||
self.slot.revision
|
self.slot.revision
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_stat(&self, id: TypstFileId, query: &'static str) -> QueryStatGuard {
|
|
||||||
let stats = &self.analysis.analysis_stats.query_stats;
|
|
||||||
let entry = stats.entry(id).or_default();
|
|
||||||
let entry = entry.entry(query).or_default();
|
|
||||||
QueryStatGuard {
|
|
||||||
bucket: entry.clone(),
|
|
||||||
since: std::time::SystemTime::now(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the position encoding during session.
|
/// Get the position encoding during session.
|
||||||
pub(crate) fn position_encoding(&self) -> PositionEncoding {
|
pub(crate) fn position_encoding(&self) -> PositionEncoding {
|
||||||
self.analysis.position_encoding
|
self.analysis.position_encoding
|
||||||
|
@ -481,7 +453,7 @@ impl SharedContext {
|
||||||
if matches!(w, Some("yaml" | "yml" | "bib")) {
|
if matches!(w, Some("yaml" | "yml" | "bib")) {
|
||||||
let bytes = self.file_by_id(fid).ok()?;
|
let bytes = self.file_by_id(fid).ok()?;
|
||||||
let bytes_len = bytes.len();
|
let bytes_len = bytes.len();
|
||||||
let loc = get_loc_info(bytes)?;
|
let loc = loc_info(bytes)?;
|
||||||
// binary search
|
// binary search
|
||||||
let start = find_loc(bytes_len, &loc, position.start, self.position_encoding())?;
|
let start = find_loc(bytes_len, &loc, position.start, self.position_encoding())?;
|
||||||
let end = find_loc(bytes_len, &loc, position.end, self.position_encoding())?;
|
let end = find_loc(bytes_len, &loc, position.end, self.position_encoding())?;
|
||||||
|
@ -579,6 +551,12 @@ impl SharedContext {
|
||||||
|
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve extra font information.
|
||||||
|
pub fn font_info(&self, font: typst::text::Font) -> Option<Arc<DataSource>> {
|
||||||
|
self.world.font_resolver.describe_font(&font)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the local packages and their descriptions.
|
/// Get the local packages and their descriptions.
|
||||||
pub fn local_packages(&self) -> EcoVec<PackageSpec> {
|
pub fn local_packages(&self) -> EcoVec<PackageSpec> {
|
||||||
crate::package::list_package_by_namespace(&self.world.registry, eco_format!("local"))
|
crate::package::list_package_by_namespace(&self.world.registry, eco_format!("local"))
|
||||||
|
@ -587,37 +565,60 @@ impl SharedContext {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn type_of_func(self: &Arc<Self>, func: Func) -> Signature {
|
pub(crate) fn const_eval(rr: ast::Expr<'_>) -> Option<Value> {
|
||||||
log::debug!("convert runtime func {func:?}");
|
Some(match rr {
|
||||||
analyze_signature(self, SignatureTarget::Convert(func)).unwrap()
|
ast::Expr::None(_) => Value::None,
|
||||||
|
ast::Expr::Auto(_) => Value::Auto,
|
||||||
|
ast::Expr::Bool(v) => Value::Bool(v.get()),
|
||||||
|
ast::Expr::Int(v) => Value::Int(v.get()),
|
||||||
|
ast::Expr::Float(v) => Value::Float(v.get()),
|
||||||
|
ast::Expr::Numeric(v) => Value::numeric(v.get()),
|
||||||
|
ast::Expr::Str(v) => Value::Str(v.get().into()),
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn type_of_value(self: &Arc<Self>, val: &Value) -> Ty {
|
/// Get a module by file id.
|
||||||
log::debug!("convert runtime value {val:?}");
|
pub fn module_by_id(&self, fid: TypstFileId) -> SourceResult<Module> {
|
||||||
|
let source = self.source_by_id(fid).at(Span::detached())?;
|
||||||
|
self.module_by_src(source)
|
||||||
|
}
|
||||||
|
|
||||||
// todo: check performance on peeking signature source frequently
|
/// Get a module by string.
|
||||||
let cache_key = val;
|
pub fn module_by_str(&self, rr: String) -> Option<Module> {
|
||||||
let cached = self
|
let src = Source::new(*DETACHED_ENTRY, rr);
|
||||||
.analysis
|
self.module_by_src(src).ok()
|
||||||
.caches
|
}
|
||||||
.terms
|
|
||||||
.m
|
/// Get (Create) a module by source.
|
||||||
.get(&hash128(&cache_key))
|
pub fn module_by_src(&self, source: Source) -> SourceResult<Module> {
|
||||||
.and_then(|slot| (cache_key == &slot.1 .0).then_some(slot.1 .1.clone()));
|
let route = Route::default();
|
||||||
if let Some(cached) = cached {
|
let traced = Traced::default();
|
||||||
return cached;
|
let mut sink = Sink::default();
|
||||||
|
|
||||||
|
typst::eval::eval(
|
||||||
|
((&self.world) as &dyn World).track(),
|
||||||
|
traced.track(),
|
||||||
|
sink.track_mut(),
|
||||||
|
route.track(),
|
||||||
|
&source,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to load a module from the current source file.
|
||||||
|
pub fn module_by_syntax(&self, source: &SyntaxNode) -> Option<Value> {
|
||||||
|
let (src, scope) = self.analyze_import(source);
|
||||||
|
if let Some(scope) = scope {
|
||||||
|
return Some(scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = crate::analysis::term_value(self, val);
|
match src {
|
||||||
|
Some(Value::Str(s)) => {
|
||||||
self.analysis
|
let id = resolve_id_by_path(&self.world, source.span().id()?, s.as_str())?;
|
||||||
.caches
|
self.module_by_id(id).ok().map(Value::Module)
|
||||||
.terms
|
}
|
||||||
.m
|
_ => None,
|
||||||
.entry(hash128(&cache_key))
|
}
|
||||||
.or_insert_with(|| (self.lifetime, (cache_key.clone(), res.clone())));
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the expression information of a source file.
|
/// Get the expression information of a source file.
|
||||||
|
@ -691,6 +692,39 @@ impl SharedContext {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn type_of_func(self: &Arc<Self>, func: Func) -> Signature {
|
||||||
|
log::debug!("convert runtime func {func:?}");
|
||||||
|
analyze_signature(self, SignatureTarget::Convert(func)).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn type_of_value(self: &Arc<Self>, val: &Value) -> Ty {
|
||||||
|
log::debug!("convert runtime value {val:?}");
|
||||||
|
|
||||||
|
// todo: check performance on peeking signature source frequently
|
||||||
|
let cache_key = val;
|
||||||
|
let cached = self
|
||||||
|
.analysis
|
||||||
|
.caches
|
||||||
|
.terms
|
||||||
|
.m
|
||||||
|
.get(&hash128(&cache_key))
|
||||||
|
.and_then(|slot| (cache_key == &slot.1 .0).then_some(slot.1 .1.clone()));
|
||||||
|
if let Some(cached) = cached {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = crate::analysis::term_value(self, val);
|
||||||
|
|
||||||
|
self.analysis
|
||||||
|
.caches
|
||||||
|
.terms
|
||||||
|
.m
|
||||||
|
.entry(hash128(&cache_key))
|
||||||
|
.or_insert_with(|| (self.lifetime, (cache_key.clone(), res.clone())));
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn def_of_span(
|
pub(crate) fn def_of_span(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
source: &Source,
|
source: &Source,
|
||||||
|
@ -777,62 +811,6 @@ impl SharedContext {
|
||||||
token.enter(|| tooltip_(&self.world, document, source, cursor))
|
token.enter(|| tooltip_(&self.world, document, source, cursor))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn const_eval(rr: ast::Expr<'_>) -> Option<Value> {
|
|
||||||
Some(match rr {
|
|
||||||
ast::Expr::None(_) => Value::None,
|
|
||||||
ast::Expr::Auto(_) => Value::Auto,
|
|
||||||
ast::Expr::Bool(v) => Value::Bool(v.get()),
|
|
||||||
ast::Expr::Int(v) => Value::Int(v.get()),
|
|
||||||
ast::Expr::Float(v) => Value::Float(v.get()),
|
|
||||||
ast::Expr::Numeric(v) => Value::numeric(v.get()),
|
|
||||||
ast::Expr::Str(v) => Value::Str(v.get().into()),
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a module by file id.
|
|
||||||
pub fn module_by_id(&self, fid: TypstFileId) -> SourceResult<Module> {
|
|
||||||
let source = self.source_by_id(fid).at(Span::detached())?;
|
|
||||||
self.module_by_src(source)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a module by string.
|
|
||||||
pub fn module_by_str(&self, rr: String) -> Option<Module> {
|
|
||||||
let src = Source::new(*DETACHED_ENTRY, rr);
|
|
||||||
self.module_by_src(src).ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get (Create) a module by source.
|
|
||||||
pub fn module_by_src(&self, source: Source) -> SourceResult<Module> {
|
|
||||||
let route = Route::default();
|
|
||||||
let traced = Traced::default();
|
|
||||||
let mut sink = Sink::default();
|
|
||||||
|
|
||||||
typst::eval::eval(
|
|
||||||
((&self.world) as &dyn World).track(),
|
|
||||||
traced.track(),
|
|
||||||
sink.track_mut(),
|
|
||||||
route.track(),
|
|
||||||
&source,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Try to load a module from the current source file.
|
|
||||||
pub fn module_by_syntax(&self, source: &SyntaxNode) -> Option<Value> {
|
|
||||||
let (src, scope) = self.analyze_import(source);
|
|
||||||
if let Some(scope) = scope {
|
|
||||||
return Some(scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
match src {
|
|
||||||
Some(Value::Str(s)) => {
|
|
||||||
let id = resolve_id_by_path(&self.world, source.span().id()?, s.as_str())?;
|
|
||||||
self.module_by_id(id).ok().map(Value::Module)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the manifest of a package by file id.
|
/// Get the manifest of a package by file id.
|
||||||
pub fn get_manifest(&self, toml_id: TypstFileId) -> StrResult<PackageManifest> {
|
pub fn get_manifest(&self, toml_id: TypstFileId) -> StrResult<PackageManifest> {
|
||||||
crate::docs::get_manifest(&self.world, toml_id)
|
crate::docs::get_manifest(&self.world, toml_id)
|
||||||
|
@ -878,6 +856,16 @@ impl SharedContext {
|
||||||
res.get_or_init(|| compute(self)).clone()
|
res.get_or_init(|| compute(self)).clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn query_stat(&self, id: TypstFileId, query: &'static str) -> QueryStatGuard {
|
||||||
|
let stats = &self.analysis.stats.query_stats;
|
||||||
|
let entry = stats.entry(id).or_default();
|
||||||
|
let entry = entry.entry(query).or_default();
|
||||||
|
QueryStatGuard {
|
||||||
|
bucket: entry.clone(),
|
||||||
|
since: std::time::SystemTime::now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Check on a module before really needing them. But we likely use them
|
/// Check on a module before really needing them. But we likely use them
|
||||||
/// after a while.
|
/// after a while.
|
||||||
pub(crate) fn prefetch_type_check(self: &Arc<Self>, _fid: TypstFileId) {
|
pub(crate) fn prefetch_type_check(self: &Arc<Self>, _fid: TypstFileId) {
|
||||||
|
@ -1189,7 +1177,7 @@ fn bib_info(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[comemo::memoize]
|
#[comemo::memoize]
|
||||||
fn get_loc_info(bytes: Bytes) -> Option<EcoVec<(usize, String)>> {
|
fn loc_info(bytes: Bytes) -> Option<EcoVec<(usize, String)>> {
|
||||||
let mut loc = EcoVec::new();
|
let mut loc = EcoVec::new();
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
for line in bytes.split(|e| *e == b'\n') {
|
for line in bytes.split(|e| *e == b'\n') {
|
||||||
|
@ -1234,16 +1222,16 @@ fn find_loc(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The context for searching in the workspace.
|
/// The context for searching in the workspace.
|
||||||
pub struct SearchCtx<'a, 'w> {
|
pub struct SearchCtx<'a> {
|
||||||
/// The inner analysis context.
|
/// The inner analysis context.
|
||||||
pub ctx: &'a mut AnalysisContext<'w>,
|
pub ctx: &'a mut LocalContext,
|
||||||
/// The set of files that have been searched.
|
/// The set of files that have been searched.
|
||||||
pub searched: HashSet<TypstFileId>,
|
pub searched: HashSet<TypstFileId>,
|
||||||
/// The files that need to be searched.
|
/// The files that need to be searched.
|
||||||
pub worklist: Vec<TypstFileId>,
|
pub worklist: Vec<TypstFileId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SearchCtx<'_, '_> {
|
impl SearchCtx<'_> {
|
||||||
/// Push a file to the worklist.
|
/// Push a file to the worklist.
|
||||||
pub fn push(&mut self, id: TypstFileId) -> bool {
|
pub fn push(&mut self, id: TypstFileId) -> bool {
|
||||||
if self.searched.insert(id) {
|
if self.searched.insert(id) {
|
||||||
|
|
|
@ -6,14 +6,14 @@ use super::prelude::*;
|
||||||
use crate::path_to_url;
|
use crate::path_to_url;
|
||||||
|
|
||||||
/// Get link expressions from a source.
|
/// Get link expressions from a source.
|
||||||
pub fn get_link_exprs(ctx: &mut AnalysisContext, src: &Source) -> Option<Vec<(Range<usize>, Url)>> {
|
pub fn get_link_exprs(ctx: &mut LocalContext, src: &Source) -> Option<Vec<(Range<usize>, Url)>> {
|
||||||
let root = LinkedNode::new(src.root());
|
let root = LinkedNode::new(src.root());
|
||||||
get_link_exprs_in(ctx, &root)
|
get_link_exprs_in(ctx, &root)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get link expressions in a source node.
|
/// Get link expressions in a source node.
|
||||||
pub fn get_link_exprs_in(
|
pub fn get_link_exprs_in(
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
node: &LinkedNode,
|
node: &LinkedNode,
|
||||||
) -> Option<Vec<(Range<usize>, Url)>> {
|
) -> Option<Vec<(Range<usize>, Url)>> {
|
||||||
let mut worker = LinkStrWorker { ctx, links: vec![] };
|
let mut worker = LinkStrWorker { ctx, links: vec![] };
|
||||||
|
@ -21,12 +21,12 @@ pub fn get_link_exprs_in(
|
||||||
Some(worker.links)
|
Some(worker.links)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LinkStrWorker<'a, 'w> {
|
struct LinkStrWorker<'a> {
|
||||||
ctx: &'a mut AnalysisContext<'w>,
|
ctx: &'a mut LocalContext,
|
||||||
links: Vec<(Range<usize>, Url)>,
|
links: Vec<(Range<usize>, Url)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'w> LinkStrWorker<'a, 'w> {
|
impl<'a> LinkStrWorker<'a> {
|
||||||
fn collect_links(&mut self, node: &LinkedNode) -> Option<()> {
|
fn collect_links(&mut self, node: &LinkedNode) -> Option<()> {
|
||||||
match node.kind() {
|
match node.kind() {
|
||||||
// SyntaxKind::Link => { }
|
// SyntaxKind::Link => { }
|
||||||
|
|
|
@ -16,6 +16,6 @@ pub use typst_shim::syntax::LinkedNodeExt;
|
||||||
pub use typst_shim::utils::LazyHash;
|
pub use typst_shim::utils::LazyHash;
|
||||||
|
|
||||||
pub(crate) use super::StrRef;
|
pub(crate) use super::StrRef;
|
||||||
pub(crate) use super::{AnalysisContext, ToFunc};
|
pub(crate) use super::{LocalContext, ToFunc};
|
||||||
pub(crate) use crate::adt::interner::Interned;
|
pub(crate) use crate::adt::interner::Interned;
|
||||||
pub use crate::ty::Ty;
|
pub use crate::ty::Ty;
|
||||||
|
|
|
@ -74,7 +74,7 @@ pub struct CodeActionRequest {
|
||||||
impl SemanticRequest for CodeActionRequest {
|
impl SemanticRequest for CodeActionRequest {
|
||||||
type Response = Vec<CodeActionOrCommand>;
|
type Response = Vec<CodeActionOrCommand>;
|
||||||
|
|
||||||
fn request(self, ctx: &mut AnalysisContext) -> Option<Self::Response> {
|
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response> {
|
||||||
let source = ctx.source_by_path(&self.path).ok()?;
|
let source = ctx.source_by_path(&self.path).ok()?;
|
||||||
let range = ctx.to_typst_range(self.range, &source)?;
|
let range = ctx.to_typst_range(self.range, &source)?;
|
||||||
let cursor = (range.start + 1).min(source.text().len());
|
let cursor = (range.start + 1).min(source.text().len());
|
||||||
|
@ -89,15 +89,15 @@ impl SemanticRequest for CodeActionRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CodeActionWorker<'a, 'w> {
|
struct CodeActionWorker<'a> {
|
||||||
ctx: &'a mut AnalysisContext<'w>,
|
ctx: &'a mut LocalContext,
|
||||||
actions: Vec<CodeActionOrCommand>,
|
actions: Vec<CodeActionOrCommand>,
|
||||||
local_url: OnceCell<Option<Url>>,
|
local_url: OnceCell<Option<Url>>,
|
||||||
current: Source,
|
current: Source,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'w> CodeActionWorker<'a, 'w> {
|
impl<'a> CodeActionWorker<'a> {
|
||||||
fn new(ctx: &'a mut AnalysisContext<'w>, current: Source) -> Self {
|
fn new(ctx: &'a mut LocalContext, current: Source) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ctx,
|
ctx,
|
||||||
actions: Vec::new(),
|
actions: Vec::new(),
|
||||||
|
|
|
@ -15,7 +15,7 @@ pub struct CodeLensRequest {
|
||||||
impl SemanticRequest for CodeLensRequest {
|
impl SemanticRequest for CodeLensRequest {
|
||||||
type Response = Vec<CodeLens>;
|
type Response = Vec<CodeLens>;
|
||||||
|
|
||||||
fn request(self, ctx: &mut AnalysisContext) -> Option<Self::Response> {
|
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response> {
|
||||||
let source = ctx.source_by_path(&self.path).ok()?;
|
let source = ctx.source_by_path(&self.path).ok()?;
|
||||||
|
|
||||||
let doc_start = ctx.to_lsp_range(0..0, &source);
|
let doc_start = ctx.to_lsp_range(0..0, &source);
|
||||||
|
|
|
@ -61,7 +61,7 @@ impl StatefulRequest for CompletionRequest {
|
||||||
|
|
||||||
fn request(
|
fn request(
|
||||||
self,
|
self,
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
doc: Option<VersionedDocument>,
|
doc: Option<VersionedDocument>,
|
||||||
) -> Option<Self::Response> {
|
) -> Option<Self::Response> {
|
||||||
let doc = doc.as_ref().map(|doc| doc.document.as_ref());
|
let doc = doc.as_ref().map(|doc| doc.document.as_ref());
|
||||||
|
@ -331,8 +331,8 @@ mod tests {
|
||||||
pkg_mode: bool,
|
pkg_mode: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(c: TestConfig) -> impl Fn(&mut AnalysisContext, PathBuf) {
|
fn run(c: TestConfig) -> impl Fn(&mut LocalContext, PathBuf) {
|
||||||
fn test(ctx: &mut AnalysisContext, id: TypstFileId) {
|
fn test(ctx: &mut LocalContext, id: TypstFileId) {
|
||||||
let source = ctx.source_by_id(id).unwrap();
|
let source = ctx.source_by_id(id).unwrap();
|
||||||
let rng = find_test_range(&source);
|
let rng = find_test_range(&source);
|
||||||
let text = source.text()[rng.clone()].to_string();
|
let text = source.text()[rng.clone()].to_string();
|
||||||
|
|
|
@ -7,7 +7,7 @@ pub type DiagnosticsMap = HashMap<Url, Vec<LspDiagnostic>>;
|
||||||
|
|
||||||
/// Converts a list of Typst diagnostics to LSP diagnostics.
|
/// Converts a list of Typst diagnostics to LSP diagnostics.
|
||||||
pub fn convert_diagnostics<'a>(
|
pub fn convert_diagnostics<'a>(
|
||||||
ctx: &AnalysisContext,
|
ctx: &LocalContext,
|
||||||
errors: impl IntoIterator<Item = &'a TypstDiagnostic>,
|
errors: impl IntoIterator<Item = &'a TypstDiagnostic>,
|
||||||
) -> DiagnosticsMap {
|
) -> DiagnosticsMap {
|
||||||
errors
|
errors
|
||||||
|
@ -15,7 +15,7 @@ pub fn convert_diagnostics<'a>(
|
||||||
.flat_map(|error| {
|
.flat_map(|error| {
|
||||||
convert_diagnostic(ctx, error)
|
convert_diagnostic(ctx, error)
|
||||||
.map_err(move |conversion_err| {
|
.map_err(move |conversion_err| {
|
||||||
error!("could not convert Typst error to diagnostic: {conversion_err:?} error to convert: {error:?}");
|
log::error!("could not convert Typst error to diagnostic: {conversion_err:?} error to convert: {error:?}");
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
@ -24,7 +24,7 @@ pub fn convert_diagnostics<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_diagnostic(
|
fn convert_diagnostic(
|
||||||
ctx: &AnalysisContext,
|
ctx: &LocalContext,
|
||||||
typst_diagnostic: &TypstDiagnostic,
|
typst_diagnostic: &TypstDiagnostic,
|
||||||
) -> anyhow::Result<(Url, LspDiagnostic)> {
|
) -> anyhow::Result<(Url, LspDiagnostic)> {
|
||||||
let uri;
|
let uri;
|
||||||
|
@ -64,7 +64,7 @@ fn convert_diagnostic(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tracepoint_to_relatedinformation(
|
fn tracepoint_to_relatedinformation(
|
||||||
project: &AnalysisContext,
|
project: &LocalContext,
|
||||||
tracepoint: &Spanned<Tracepoint>,
|
tracepoint: &Spanned<Tracepoint>,
|
||||||
position_encoding: PositionEncoding,
|
position_encoding: PositionEncoding,
|
||||||
) -> anyhow::Result<Option<DiagnosticRelatedInformation>> {
|
) -> anyhow::Result<Option<DiagnosticRelatedInformation>> {
|
||||||
|
@ -89,7 +89,7 @@ fn tracepoint_to_relatedinformation(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diagnostic_related_information(
|
fn diagnostic_related_information(
|
||||||
project: &AnalysisContext,
|
project: &LocalContext,
|
||||||
typst_diagnostic: &TypstDiagnostic,
|
typst_diagnostic: &TypstDiagnostic,
|
||||||
position_encoding: PositionEncoding,
|
position_encoding: PositionEncoding,
|
||||||
) -> anyhow::Result<Vec<DiagnosticRelatedInformation>> {
|
) -> anyhow::Result<Vec<DiagnosticRelatedInformation>> {
|
||||||
|
|
62
crates/tinymist-query/src/docs/convert.rs
Normal file
62
crates/tinymist-query/src/docs/convert.rs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
use std::sync::{Arc, LazyLock};
|
||||||
|
|
||||||
|
use ecow::{eco_format, EcoString};
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use tinymist_world::base::{EntryState, ShadowApi, TaskInputs};
|
||||||
|
use tinymist_world::LspWorld;
|
||||||
|
use typlite::scopes::Scopes;
|
||||||
|
use typlite::value::{Value, *};
|
||||||
|
use typst::foundations::Bytes;
|
||||||
|
use typst::{
|
||||||
|
diag::StrResult,
|
||||||
|
syntax::{FileId, VirtualPath},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Unfortunately, we have only 65536 possible file ids and we cannot revoke
|
||||||
|
// them. So we share a global file id for all docs conversion.
|
||||||
|
static DOCS_CONVERT_ID: LazyLock<Mutex<FileId>> =
|
||||||
|
LazyLock::new(|| Mutex::new(FileId::new(None, VirtualPath::new("__tinymist_docs__.typ"))));
|
||||||
|
|
||||||
|
pub(crate) fn convert_docs(world: &LspWorld, content: &str) -> StrResult<EcoString> {
|
||||||
|
static DOCS_LIB: LazyLock<Arc<Scopes<Value>>> = LazyLock::new(lib);
|
||||||
|
|
||||||
|
let conv_id = DOCS_CONVERT_ID.lock();
|
||||||
|
let entry = EntryState::new_rootless(conv_id.vpath().as_rooted_path().into()).unwrap();
|
||||||
|
let entry = entry.select_in_workspace(*conv_id);
|
||||||
|
|
||||||
|
let mut w = world.task(TaskInputs {
|
||||||
|
entry: Some(entry),
|
||||||
|
inputs: None,
|
||||||
|
});
|
||||||
|
w.map_shadow_by_id(*conv_id, Bytes::from(content.as_bytes().to_owned()))?;
|
||||||
|
// todo: bad performance
|
||||||
|
w.source_db.take_state();
|
||||||
|
|
||||||
|
let conv = typlite::Typlite::new(Arc::new(w))
|
||||||
|
.with_library(DOCS_LIB.clone())
|
||||||
|
.annotate_elements(true)
|
||||||
|
.convert()
|
||||||
|
.map_err(|e| eco_format!("failed to convert to markdown: {e}"))?;
|
||||||
|
|
||||||
|
Ok(conv.replace("```example", "```typ"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn lib() -> Arc<Scopes<Value>> {
|
||||||
|
let mut scopes = typlite::library::library();
|
||||||
|
|
||||||
|
// todo: how to import this function correctly?
|
||||||
|
scopes.define("example", example as RawFunc);
|
||||||
|
|
||||||
|
Arc::new(scopes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate a `example`.
|
||||||
|
pub fn example(mut args: Args) -> typlite::Result<Value> {
|
||||||
|
let body = get_pos_named!(args, body: Content).0;
|
||||||
|
let body = body.trim();
|
||||||
|
let ticks = body.chars().take_while(|t| *t == '`').collect::<String>();
|
||||||
|
let body = &body[ticks.len()..];
|
||||||
|
let body = eco_format!("{ticks}typ{body}");
|
||||||
|
|
||||||
|
Ok(Value::Content(body))
|
||||||
|
}
|
|
@ -1,21 +1,13 @@
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::sync::{Arc, OnceLock};
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use ecow::{eco_format, EcoString};
|
use ecow::EcoString;
|
||||||
use parking_lot::Mutex;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tinymist_world::base::{EntryState, ShadowApi, TaskInputs};
|
use typst::syntax::Span;
|
||||||
use tinymist_world::LspWorld;
|
|
||||||
use typst::foundations::Bytes;
|
|
||||||
use typst::{
|
|
||||||
diag::StrResult,
|
|
||||||
syntax::{FileId, Span, VirtualPath},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::tidy::*;
|
use super::tidy::*;
|
||||||
use crate::analysis::{ParamAttrs, ParamSpec, Signature};
|
use crate::analysis::{ParamAttrs, ParamSpec, Signature};
|
||||||
use crate::docs::library;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::ty::Ty;
|
use crate::ty::Ty;
|
||||||
use crate::ty::{DocSource, Interned};
|
use crate::ty::{DocSource, Interned};
|
||||||
|
@ -24,12 +16,12 @@ use crate::upstream::plain_docs_sentence;
|
||||||
type TypeRepr = Option<(/* short */ String, /* long */ String)>;
|
type TypeRepr = Option<(/* short */ String, /* long */ String)>;
|
||||||
type ShowTypeRepr<'a> = &'a mut dyn FnMut(Option<&Ty>) -> TypeRepr;
|
type ShowTypeRepr<'a> = &'a mut dyn FnMut(Option<&Ty>) -> TypeRepr;
|
||||||
|
|
||||||
/// Documentation about a symbol (without type information).
|
/// Documentation about a definition (without type information).
|
||||||
pub type UntypedDefDocs = DefDocsT<()>;
|
pub type UntypedDefDocs = DefDocsT<()>;
|
||||||
/// Documentation about a symbol.
|
/// Documentation about a definition.
|
||||||
pub type DefDocs = DefDocsT<TypeRepr>;
|
pub type DefDocs = DefDocsT<TypeRepr>;
|
||||||
|
|
||||||
/// Documentation about a symbol.
|
/// Documentation about a definition.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(tag = "kind")]
|
#[serde(tag = "kind")]
|
||||||
pub enum DefDocsT<T> {
|
pub enum DefDocsT<T> {
|
||||||
|
@ -263,7 +255,7 @@ fn format_ty(ty: Option<&Ty>, doc_ty: Option<&mut ShowTypeRepr>) -> TypeRepr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn variable_docs(ctx: &mut LocalContext, pos: Span) -> Option<VarDocs> {
|
pub(crate) fn var_docs(ctx: &mut LocalContext, pos: Span) -> Option<VarDocs> {
|
||||||
let source = ctx.source_by_id(pos.id()?).ok()?;
|
let source = ctx.source_by_id(pos.id()?).ok()?;
|
||||||
let type_info = ctx.type_check(&source);
|
let type_info = ctx.type_check(&source);
|
||||||
let ty = type_info.type_of_span(pos)?;
|
let ty = type_info.type_of_span(pos)?;
|
||||||
|
@ -299,10 +291,7 @@ pub(crate) fn variable_docs(ctx: &mut LocalContext, pos: Span) -> Option<VarDocs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn signature_docs(
|
pub(crate) fn sig_docs(sig: &Signature, mut doc_ty: Option<ShowTypeRepr>) -> Option<SignatureDocs> {
|
||||||
sig: &Signature,
|
|
||||||
mut doc_ty: Option<ShowTypeRepr>,
|
|
||||||
) -> Option<SignatureDocs> {
|
|
||||||
let type_sig = sig.type_sig().clone();
|
let type_sig = sig.type_sig().clone();
|
||||||
|
|
||||||
let pos_in = sig
|
let pos_in = sig
|
||||||
|
@ -344,34 +333,3 @@ pub(crate) fn signature_docs(
|
||||||
hover_docs: OnceLock::new(),
|
hover_docs: OnceLock::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unfortunately, we have only 65536 possible file ids and we cannot revoke
|
|
||||||
// them. So we share a global file id for all docs conversion.
|
|
||||||
static DOCS_CONVERT_ID: std::sync::LazyLock<Mutex<FileId>> = std::sync::LazyLock::new(|| {
|
|
||||||
Mutex::new(FileId::new(None, VirtualPath::new("__tinymist_docs__.typ")))
|
|
||||||
});
|
|
||||||
|
|
||||||
pub(crate) fn convert_docs(world: &LspWorld, content: &str) -> StrResult<EcoString> {
|
|
||||||
static DOCS_LIB: std::sync::LazyLock<Arc<typlite::scopes::Scopes<typlite::value::Value>>> =
|
|
||||||
std::sync::LazyLock::new(library::lib);
|
|
||||||
|
|
||||||
let conv_id = DOCS_CONVERT_ID.lock();
|
|
||||||
let entry = EntryState::new_rootless(conv_id.vpath().as_rooted_path().into()).unwrap();
|
|
||||||
let entry = entry.select_in_workspace(*conv_id);
|
|
||||||
|
|
||||||
let mut w = world.task(TaskInputs {
|
|
||||||
entry: Some(entry),
|
|
||||||
inputs: None,
|
|
||||||
});
|
|
||||||
w.map_shadow_by_id(*conv_id, Bytes::from(content.as_bytes().to_owned()))?;
|
|
||||||
// todo: bad performance
|
|
||||||
w.source_db.take_state();
|
|
||||||
|
|
||||||
let conv = typlite::Typlite::new(Arc::new(w))
|
|
||||||
.with_library(DOCS_LIB.clone())
|
|
||||||
.annotate_elements(true)
|
|
||||||
.convert()
|
|
||||||
.map_err(|e| eco_format!("failed to convert to markdown: {e}"))?;
|
|
||||||
|
|
||||||
Ok(conv.replace("```example", "```typ"))
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use ecow::eco_format;
|
|
||||||
use typlite::value::*;
|
|
||||||
|
|
||||||
pub(super) fn lib() -> Arc<typlite::scopes::Scopes<Value>> {
|
|
||||||
let mut scopes = typlite::library::library();
|
|
||||||
|
|
||||||
// todo: how to import this function correctly?
|
|
||||||
scopes.define("example", example as RawFunc);
|
|
||||||
|
|
||||||
Arc::new(scopes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate a `example`.
|
|
||||||
pub fn example(mut args: Args) -> typlite::Result<Value> {
|
|
||||||
let body = get_pos_named!(args, body: Content).0;
|
|
||||||
let body = body.trim();
|
|
||||||
let ticks = body.chars().take_while(|t| *t == '`').collect::<String>();
|
|
||||||
let body = &body[ticks.len()..];
|
|
||||||
let body = eco_format!("{ticks}typ{body}");
|
|
||||||
|
|
||||||
Ok(Value::Content(body))
|
|
||||||
}
|
|
|
@ -1,17 +1,18 @@
|
||||||
//! Documentation generation utilities.
|
//! Documentation utilities.
|
||||||
|
|
||||||
mod library;
|
mod convert;
|
||||||
|
mod def;
|
||||||
mod module;
|
mod module;
|
||||||
mod package;
|
mod package;
|
||||||
mod symbol;
|
|
||||||
mod tidy;
|
mod tidy;
|
||||||
|
|
||||||
use reflexo::path::unix_slash;
|
use reflexo::path::unix_slash;
|
||||||
use typst::syntax::FileId;
|
use typst::syntax::FileId;
|
||||||
|
|
||||||
|
pub(crate) use convert::convert_docs;
|
||||||
|
pub use def::*;
|
||||||
pub use module::*;
|
pub use module::*;
|
||||||
pub use package::*;
|
pub use package::*;
|
||||||
pub use symbol::*;
|
|
||||||
pub(crate) use tidy::*;
|
pub(crate) use tidy::*;
|
||||||
|
|
||||||
fn file_id_repr(k: FileId) -> String {
|
fn file_id_repr(k: FileId) -> String {
|
||||||
|
|
|
@ -13,12 +13,12 @@ use typst::syntax::FileId;
|
||||||
use crate::docs::file_id_repr;
|
use crate::docs::file_id_repr;
|
||||||
use crate::syntax::{Decl, DefKind, Expr, ExprInfo};
|
use crate::syntax::{Decl, DefKind, Expr, ExprInfo};
|
||||||
use crate::ty::Interned;
|
use crate::ty::Interned;
|
||||||
use crate::AnalysisContext;
|
use crate::LocalContext;
|
||||||
|
|
||||||
use super::{get_manifest_id, DefDocs, PackageInfo};
|
use super::{get_manifest_id, DefDocs, PackageInfo};
|
||||||
|
|
||||||
/// Get documentation of symbols in a package.
|
/// Get documentation of definitions in a package.
|
||||||
pub fn package_module_docs(ctx: &mut AnalysisContext, pkg: &PackageInfo) -> StrResult<SymbolsInfo> {
|
pub fn package_module_docs(ctx: &mut LocalContext, pkg: &PackageInfo) -> StrResult<PackageDefInfo> {
|
||||||
let toml_id = get_manifest_id(pkg)?;
|
let toml_id = get_manifest_id(pkg)?;
|
||||||
let manifest = ctx.get_manifest(toml_id)?;
|
let manifest = ctx.get_manifest(toml_id)?;
|
||||||
|
|
||||||
|
@ -26,12 +26,12 @@ pub fn package_module_docs(ctx: &mut AnalysisContext, pkg: &PackageInfo) -> StrR
|
||||||
module_docs(ctx, entry_point)
|
module_docs(ctx, entry_point)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get documentation of symbols in a module.
|
/// Get documentation of definitions in a module.
|
||||||
pub fn module_docs(ctx: &mut AnalysisContext, entry_point: FileId) -> StrResult<SymbolsInfo> {
|
pub fn module_docs(ctx: &mut LocalContext, entry_point: FileId) -> StrResult<PackageDefInfo> {
|
||||||
let mut aliases = HashMap::new();
|
let mut aliases = HashMap::new();
|
||||||
let mut extras = vec![];
|
let mut extras = vec![];
|
||||||
|
|
||||||
let mut scan_ctx = ScanSymbolCtx {
|
let mut scan_ctx = ScanDefCtx {
|
||||||
ctx,
|
ctx,
|
||||||
root: entry_point,
|
root: entry_point,
|
||||||
for_spec: entry_point.package(),
|
for_spec: entry_point.package(),
|
||||||
|
@ -43,7 +43,7 @@ pub fn module_docs(ctx: &mut AnalysisContext, entry_point: FileId) -> StrResult<
|
||||||
.ctx
|
.ctx
|
||||||
.expr_stage_by_id(entry_point)
|
.expr_stage_by_id(entry_point)
|
||||||
.ok_or("entry point not found")?;
|
.ok_or("entry point not found")?;
|
||||||
let mut symbols = scan_ctx.module_sym(eco_vec![], ei);
|
let mut defs = scan_ctx.defs(eco_vec![], ei);
|
||||||
|
|
||||||
let module_uses = aliases
|
let module_uses = aliases
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -55,72 +55,64 @@ pub fn module_docs(ctx: &mut AnalysisContext, entry_point: FileId) -> StrResult<
|
||||||
|
|
||||||
log::debug!("module_uses: {module_uses:#?}",);
|
log::debug!("module_uses: {module_uses:#?}",);
|
||||||
|
|
||||||
symbols.children.extend(extras);
|
defs.children.extend(extras);
|
||||||
|
|
||||||
Ok(SymbolsInfo {
|
Ok(PackageDefInfo {
|
||||||
root: symbols,
|
root: defs,
|
||||||
module_uses,
|
module_uses,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information about a symbol.
|
/// Information about a definition.
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
pub struct SymbolInfoHead {
|
pub struct DefInfo {
|
||||||
/// The name of the symbol.
|
/// The name of the definition.
|
||||||
pub name: EcoString,
|
pub name: EcoString,
|
||||||
/// The kind of the symbol.
|
/// The kind of the definition.
|
||||||
pub kind: DefKind,
|
pub kind: DefKind,
|
||||||
/// The location (file, start, end) of the symbol.
|
/// The location (file, start, end) of the definition.
|
||||||
pub loc: Option<(usize, usize, usize)>,
|
pub loc: Option<(usize, usize, usize)>,
|
||||||
/// Is the symbol reexport
|
/// Whether the definition external to the module.
|
||||||
pub export_again: bool,
|
pub is_external: bool,
|
||||||
/// Is the symbol reexport
|
/// The link to the definition if it is external.
|
||||||
pub external_link: Option<String>,
|
pub external_link: Option<String>,
|
||||||
/// The one-line documentation of the symbol.
|
/// The one-line documentation of the definition.
|
||||||
pub oneliner: Option<String>,
|
pub oneliner: Option<String>,
|
||||||
/// The raw documentation of the symbol.
|
/// The raw documentation of the definition.
|
||||||
pub docs: Option<EcoString>,
|
pub docs: Option<EcoString>,
|
||||||
/// The parsed documentation of the symbol.
|
/// The parsed documentation of the definition.
|
||||||
pub parsed_docs: Option<DefDocs>,
|
pub parsed_docs: Option<DefDocs>,
|
||||||
/// The value of the symbol.
|
/// The value of the definition.
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub constant: Option<EcoString>,
|
pub constant: Option<EcoString>,
|
||||||
/// The name range of the symbol.
|
/// The name range of the definition.
|
||||||
/// The value of the symbol.
|
/// The value of the definition.
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub decl: Option<Interned<Decl>>,
|
pub decl: Option<Interned<Decl>>,
|
||||||
|
/// The children of the definition.
|
||||||
|
pub children: EcoVec<DefInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information about a symbol.
|
/// Information about the definitions in a package.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct SymbolInfo {
|
pub struct PackageDefInfo {
|
||||||
/// The primary information about the symbol.
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub head: SymbolInfoHead,
|
|
||||||
/// The children of the symbol.
|
|
||||||
pub children: EcoVec<SymbolInfo>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information about the symbols in a package.
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct SymbolsInfo {
|
|
||||||
/// The root module information.
|
/// The root module information.
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub root: SymbolInfo,
|
pub root: DefInfo,
|
||||||
/// The module accessible paths.
|
/// The module accessible paths.
|
||||||
pub module_uses: HashMap<String, EcoVec<String>>,
|
pub module_uses: HashMap<String, EcoVec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ScanSymbolCtx<'a, 'w> {
|
struct ScanDefCtx<'a> {
|
||||||
ctx: &'a mut AnalysisContext<'w>,
|
ctx: &'a mut LocalContext,
|
||||||
for_spec: Option<&'a PackageSpec>,
|
for_spec: Option<&'a PackageSpec>,
|
||||||
aliases: &'a mut HashMap<FileId, Vec<String>>,
|
aliases: &'a mut HashMap<FileId, Vec<String>>,
|
||||||
extras: &'a mut Vec<SymbolInfo>,
|
extras: &'a mut Vec<DefInfo>,
|
||||||
root: FileId,
|
root: FileId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScanSymbolCtx<'_, '_> {
|
impl ScanDefCtx<'_> {
|
||||||
fn module_sym(&mut self, path: EcoVec<&str>, ei: Arc<ExprInfo>) -> SymbolInfo {
|
fn defs(&mut self, path: EcoVec<&str>, ei: Arc<ExprInfo>) -> DefInfo {
|
||||||
let name = {
|
let name = {
|
||||||
let stem = ei.fid.vpath().as_rooted_path().file_stem();
|
let stem = ei.fid.vpath().as_rooted_path().file_stem();
|
||||||
stem.and_then(|s| Some(Interned::new_str(s.to_str()?)))
|
stem.and_then(|s| Some(Interned::new_str(s.to_str()?)))
|
||||||
|
@ -129,7 +121,7 @@ impl ScanSymbolCtx<'_, '_> {
|
||||||
let module_decl = Decl::module(name.clone(), ei.fid).into();
|
let module_decl = Decl::module(name.clone(), ei.fid).into();
|
||||||
let site = Some(self.root);
|
let site = Some(self.root);
|
||||||
let p = path.clone();
|
let p = path.clone();
|
||||||
self.sym(&name, p, site.as_ref(), &module_decl, None)
|
self.def(&name, p, site.as_ref(), &module_decl, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr(
|
fn expr(
|
||||||
|
@ -138,9 +130,9 @@ impl ScanSymbolCtx<'_, '_> {
|
||||||
path: EcoVec<&str>,
|
path: EcoVec<&str>,
|
||||||
site: Option<&FileId>,
|
site: Option<&FileId>,
|
||||||
val: &Expr,
|
val: &Expr,
|
||||||
) -> SymbolInfo {
|
) -> DefInfo {
|
||||||
match val {
|
match val {
|
||||||
Expr::Decl(d) => self.sym(key, path, site, d, Some(val)),
|
Expr::Decl(d) => self.def(key, path, site, d, Some(val)),
|
||||||
Expr::Ref(r) if r.root.is_some() => {
|
Expr::Ref(r) if r.root.is_some() => {
|
||||||
self.expr(key, path, site, r.root.as_ref().unwrap())
|
self.expr(key, path, site, r.root.as_ref().unwrap())
|
||||||
}
|
}
|
||||||
|
@ -148,55 +140,38 @@ impl ScanSymbolCtx<'_, '_> {
|
||||||
Expr::Select(..) => {
|
Expr::Select(..) => {
|
||||||
let mut path = path.clone();
|
let mut path = path.clone();
|
||||||
path.push(key);
|
path.push(key);
|
||||||
let head = SymbolInfoHead {
|
DefInfo {
|
||||||
name: key.to_string().into(),
|
name: key.to_string().into(),
|
||||||
kind: DefKind::Module,
|
kind: DefKind::Module,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
|
||||||
SymbolInfo {
|
|
||||||
head,
|
|
||||||
children: eco_vec![],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// v => panic!("unexpected export: {key} -> {v}"),
|
// v => panic!("unexpected export: {key} -> {v}"),
|
||||||
_ => {
|
_ => {
|
||||||
let mut path = path.clone();
|
let mut path = path.clone();
|
||||||
path.push(key);
|
path.push(key);
|
||||||
let head = SymbolInfoHead {
|
DefInfo {
|
||||||
name: key.to_string().into(),
|
name: key.to_string().into(),
|
||||||
kind: DefKind::Constant,
|
kind: DefKind::Constant,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
|
||||||
SymbolInfo {
|
|
||||||
head,
|
|
||||||
children: eco_vec![],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sym(
|
fn def(
|
||||||
&mut self,
|
&mut self,
|
||||||
key: &str,
|
key: &str,
|
||||||
path: EcoVec<&str>,
|
path: EcoVec<&str>,
|
||||||
site: Option<&FileId>,
|
site: Option<&FileId>,
|
||||||
val: &Interned<Decl>,
|
decl: &Interned<Decl>,
|
||||||
expr: Option<&Expr>,
|
expr: Option<&Expr>,
|
||||||
) -> SymbolInfo {
|
) -> DefInfo {
|
||||||
let mut head = create_head(self.ctx, key, val, expr);
|
let def = self.ctx.def_of_decl(decl);
|
||||||
|
let def_docs = def.and_then(|def| self.ctx.def_docs(&def));
|
||||||
if !matches!(val.as_ref(), Decl::Module(..)) {
|
let docs = def_docs.as_ref().map(|d| d.docs().clone());
|
||||||
if let Some((span, mod_fid)) = head.decl.as_ref().and_then(|d| d.file_id()).zip(site) {
|
let children = match decl.as_ref() {
|
||||||
if span != *mod_fid {
|
Decl::Module(..) => decl.file_id().and_then(|fid| {
|
||||||
head.export_again = true;
|
|
||||||
head.oneliner = head.docs.as_deref().map(oneliner).map(|e| e.to_owned());
|
|
||||||
head.docs = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let children = match val.as_ref() {
|
|
||||||
Decl::Module(..) => val.file_id().and_then(|fid| {
|
|
||||||
// only generate docs for the same package
|
// only generate docs for the same package
|
||||||
if fid.package() != self.for_spec {
|
if fid.package() != self.for_spec {
|
||||||
return None;
|
return None;
|
||||||
|
@ -230,6 +205,28 @@ impl ScanSymbolCtx<'_, '_> {
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut head = DefInfo {
|
||||||
|
name: key.to_string().into(),
|
||||||
|
kind: decl.kind(),
|
||||||
|
constant: expr.map(|e| e.repr()),
|
||||||
|
docs,
|
||||||
|
parsed_docs: def_docs,
|
||||||
|
decl: Some(decl.clone()),
|
||||||
|
children: children.unwrap_or_default(),
|
||||||
|
loc: None,
|
||||||
|
is_external: false,
|
||||||
|
external_link: None,
|
||||||
|
oneliner: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some((span, mod_fid)) = head.decl.as_ref().and_then(|d| d.file_id()).zip(site) {
|
||||||
|
if span != *mod_fid {
|
||||||
|
head.is_external = true;
|
||||||
|
head.oneliner = head.docs.as_deref().map(oneliner).map(|e| e.to_owned());
|
||||||
|
head.docs = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Insert module that is not exported
|
// Insert module that is not exported
|
||||||
if let Some(fid) = head.decl.as_ref().and_then(|d| d.file_id()) {
|
if let Some(fid) = head.decl.as_ref().and_then(|d| d.file_id()) {
|
||||||
// only generate docs for the same package
|
// only generate docs for the same package
|
||||||
|
@ -243,37 +240,14 @@ impl ScanSymbolCtx<'_, '_> {
|
||||||
|
|
||||||
log::debug!("found internal module: {path:?}");
|
log::debug!("found internal module: {path:?}");
|
||||||
if let Some(m) = src {
|
if let Some(m) = src {
|
||||||
let msym = self.module_sym(path, m);
|
let msym = self.defs(path, m);
|
||||||
self.extras.push(msym)
|
self.extras.push(msym)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let children = children.unwrap_or_default();
|
head
|
||||||
SymbolInfo { head, children }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_head(
|
|
||||||
ctx: &mut AnalysisContext,
|
|
||||||
k: &str,
|
|
||||||
decl: &Interned<Decl>,
|
|
||||||
expr: Option<&Expr>,
|
|
||||||
) -> SymbolInfoHead {
|
|
||||||
let kind = decl.kind();
|
|
||||||
|
|
||||||
let parsed_docs = ctx.def_of_decl(decl).and_then(|def| ctx.def_docs(&def));
|
|
||||||
let docs = parsed_docs.as_ref().map(|d| d.docs().clone());
|
|
||||||
|
|
||||||
SymbolInfoHead {
|
|
||||||
name: k.to_string().into(),
|
|
||||||
kind,
|
|
||||||
constant: expr.map(|e| e.repr()),
|
|
||||||
docs,
|
|
||||||
parsed_docs,
|
|
||||||
decl: Some(decl.clone()),
|
|
||||||
..Default::default()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,11 +10,11 @@ use typst::syntax::package::{PackageManifest, PackageSpec};
|
||||||
use typst::syntax::{FileId, Span, VirtualPath};
|
use typst::syntax::{FileId, Span, VirtualPath};
|
||||||
use typst::World;
|
use typst::World;
|
||||||
|
|
||||||
use crate::docs::{file_id_repr, module_docs, DefDocs, SymbolsInfo};
|
use crate::docs::{file_id_repr, module_docs, DefDocs, PackageDefInfo};
|
||||||
use crate::AnalysisContext;
|
use crate::LocalContext;
|
||||||
|
|
||||||
/// Check Package.
|
/// Check Package.
|
||||||
pub fn check_package(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<()> {
|
pub fn check_package(ctx: &mut LocalContext, spec: &PackageInfo) -> StrResult<()> {
|
||||||
let toml_id = get_manifest_id(spec)?;
|
let toml_id = get_manifest_id(spec)?;
|
||||||
let manifest = ctx.get_manifest(toml_id)?;
|
let manifest = ctx.get_manifest(toml_id)?;
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ pub fn check_package(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate full documents in markdown format
|
/// Generate full documents in markdown format
|
||||||
pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<String> {
|
pub fn package_docs(ctx: &mut LocalContext, spec: &PackageInfo) -> StrResult<String> {
|
||||||
log::info!("generate_md_docs {spec:?}");
|
log::info!("generate_md_docs {spec:?}");
|
||||||
|
|
||||||
let mut md = String::new();
|
let mut md = String::new();
|
||||||
|
@ -37,7 +37,7 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
||||||
|
|
||||||
ctx.preload_package(entry_point);
|
ctx.preload_package(entry_point);
|
||||||
|
|
||||||
let SymbolsInfo { root, module_uses } = module_docs(ctx, entry_point)?;
|
let PackageDefInfo { root, module_uses } = module_docs(ctx, entry_point)?;
|
||||||
|
|
||||||
log::debug!("module_uses: {module_uses:#?}");
|
log::debug!("module_uses: {module_uses:#?}");
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
||||||
let package_meta = jbase64(&meta);
|
let package_meta = jbase64(&meta);
|
||||||
let _ = writeln!(md, "<!-- begin:package {package_meta} -->");
|
let _ = writeln!(md, "<!-- begin:package {package_meta} -->");
|
||||||
|
|
||||||
let mut modules_to_generate = vec![(root.head.name.clone(), root)];
|
let mut modules_to_generate = vec![(root.name.clone(), root)];
|
||||||
let mut generated_modules = HashSet::new();
|
let mut generated_modules = HashSet::new();
|
||||||
let mut file_ids: IndexSet<FileId> = IndexSet::new();
|
let mut file_ids: IndexSet<FileId> = IndexSet::new();
|
||||||
|
|
||||||
|
@ -82,11 +82,11 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
||||||
};
|
};
|
||||||
|
|
||||||
while !modules_to_generate.is_empty() {
|
while !modules_to_generate.is_empty() {
|
||||||
for (parent_ident, sym) in std::mem::take(&mut modules_to_generate) {
|
for (parent_ident, def) in std::mem::take(&mut modules_to_generate) {
|
||||||
// parent_ident, symbols
|
// parent_ident, symbols
|
||||||
let symbols = sym.children;
|
let children = def.children;
|
||||||
|
|
||||||
let module_val = sym.head.decl.as_ref().unwrap();
|
let module_val = def.decl.as_ref().unwrap();
|
||||||
let fid = module_val.file_id();
|
let fid = module_val.file_id();
|
||||||
let aka = fid.map(&mut akas).unwrap_or_default();
|
let aka = fid.map(&mut akas).unwrap_or_default();
|
||||||
|
|
||||||
|
@ -111,15 +111,15 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
||||||
}
|
}
|
||||||
let m = jbase64(&ModuleInfo {
|
let m = jbase64(&ModuleInfo {
|
||||||
prefix: primary.as_str().into(),
|
prefix: primary.as_str().into(),
|
||||||
name: sym.head.name.clone(),
|
name: def.name.clone(),
|
||||||
loc: persist_fid,
|
loc: persist_fid,
|
||||||
parent_ident: parent_ident.clone(),
|
parent_ident: parent_ident.clone(),
|
||||||
aka,
|
aka,
|
||||||
});
|
});
|
||||||
let _ = writeln!(md, "<!-- begin:module {primary} {m} -->");
|
let _ = writeln!(md, "<!-- begin:module {primary} {m} -->");
|
||||||
|
|
||||||
for mut sym in symbols {
|
for mut child in children {
|
||||||
let span = sym.head.decl.as_ref().map(|d| d.span());
|
let span = child.decl.as_ref().map(|d| d.span());
|
||||||
let fid_range = span.and_then(|v| {
|
let fid_range = span.and_then(|v| {
|
||||||
v.id().and_then(|e| {
|
v.id().and_then(|e| {
|
||||||
let fid = file_ids.insert_full(e).0;
|
let fid = file_ids.insert_full(e).0;
|
||||||
|
@ -128,13 +128,13 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
||||||
Some((fid, rng.start, rng.end))
|
Some((fid, rng.start, rng.end))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
let sym_fid = sym.head.decl.as_ref().and_then(|d| d.file_id());
|
let child_fid = child.decl.as_ref().and_then(|d| d.file_id());
|
||||||
let sym_fid = sym_fid.or_else(|| span.and_then(Span::id)).or(fid);
|
let child_fid = child_fid.or_else(|| span.and_then(Span::id)).or(fid);
|
||||||
let span = fid_range.or_else(|| {
|
let span = fid_range.or_else(|| {
|
||||||
let fid = sym_fid?;
|
let fid = child_fid?;
|
||||||
Some((file_ids.insert_full(fid).0, 0, 0))
|
Some((file_ids.insert_full(fid).0, 0, 0))
|
||||||
});
|
});
|
||||||
sym.head.loc = span;
|
child.loc = span;
|
||||||
// .ok_or_else(|| {
|
// .ok_or_else(|| {
|
||||||
// let err = format!("failed to convert docs in {title}").replace(
|
// let err = format!("failed to convert docs in {title}").replace(
|
||||||
// "-->", "—>", // avoid markdown comment
|
// "-->", "—>", // avoid markdown comment
|
||||||
|
@ -142,7 +142,7 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
||||||
// log::error!("{err}");
|
// log::error!("{err}");
|
||||||
// err
|
// err
|
||||||
// })
|
// })
|
||||||
let docs = sym.head.parsed_docs.clone();
|
let docs = child.parsed_docs.clone();
|
||||||
// Err(e) => {
|
// Err(e) => {
|
||||||
// let err = format!("failed to convert docs: {e}").replace(
|
// let err = format!("failed to convert docs: {e}").replace(
|
||||||
// "-->", "—>", // avoid markdown comment
|
// "-->", "—>", // avoid markdown comment
|
||||||
|
@ -154,8 +154,8 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
||||||
let convert_err = None::<EcoString>;
|
let convert_err = None::<EcoString>;
|
||||||
match &docs {
|
match &docs {
|
||||||
Some(docs) => {
|
Some(docs) => {
|
||||||
sym.head.parsed_docs = Some(docs.clone());
|
child.parsed_docs = Some(docs.clone());
|
||||||
sym.head.docs = None;
|
child.docs = None;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// let err = format!("failed to convert docs in {title}:
|
// let err = format!("failed to convert docs in {title}:
|
||||||
|
@ -168,53 +168,54 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
||||||
}
|
}
|
||||||
|
|
||||||
let ident = if !primary.is_empty() {
|
let ident = if !primary.is_empty() {
|
||||||
eco_format!("symbol-{}-{primary}.{}", sym.head.kind, sym.head.name)
|
eco_format!("symbol-{}-{primary}.{}", child.kind, child.name)
|
||||||
} else {
|
} else {
|
||||||
eco_format!("symbol-{}-{}", sym.head.kind, sym.head.name)
|
eco_format!("symbol-{}-{}", child.kind, child.name)
|
||||||
};
|
};
|
||||||
let _ = writeln!(md, "### {}: {} in {primary}", sym.head.kind, sym.head.name);
|
let _ = writeln!(md, "### {}: {} in {primary}", child.kind, child.name);
|
||||||
|
|
||||||
if sym.head.export_again {
|
if child.is_external {
|
||||||
if let Some(fid) = sym_fid {
|
if let Some(fid) = child_fid {
|
||||||
let lnk = if fid.package() == Some(for_spec) {
|
let lnk = if fid.package() == Some(for_spec) {
|
||||||
let sub_aka = akas(fid);
|
let sub_aka = akas(fid);
|
||||||
let sub_primary = sub_aka.first().cloned().unwrap_or_default();
|
let sub_primary = sub_aka.first().cloned().unwrap_or_default();
|
||||||
sym.head.external_link = Some(format!(
|
child.external_link = Some(format!(
|
||||||
"#symbol-{}-{sub_primary}.{}",
|
"#symbol-{}-{sub_primary}.{}",
|
||||||
sym.head.kind, sym.head.name
|
child.kind, child.name
|
||||||
));
|
));
|
||||||
format!("#{}-{}-in-{sub_primary}", sym.head.kind, sym.head.name)
|
format!("#{}-{}-in-{sub_primary}", child.kind, child.name)
|
||||||
.replace(".", "")
|
.replace(".", "")
|
||||||
} else if let Some(spec) = fid.package() {
|
} else if let Some(spec) = fid.package() {
|
||||||
let lnk = format!(
|
let lnk = format!(
|
||||||
"https://typst.app/universe/package/{}/{}",
|
"https://typst.app/universe/package/{}/{}",
|
||||||
spec.name, spec.version
|
spec.name, spec.version
|
||||||
);
|
);
|
||||||
sym.head.external_link = Some(lnk.clone());
|
child.external_link = Some(lnk.clone());
|
||||||
lnk
|
lnk
|
||||||
} else {
|
} else {
|
||||||
let lnk: String = "https://typst.app/docs".into();
|
let lnk: String = "https://typst.app/docs".into();
|
||||||
sym.head.external_link = Some(lnk.clone());
|
child.external_link = Some(lnk.clone());
|
||||||
lnk
|
lnk
|
||||||
};
|
};
|
||||||
let _ = writeln!(md, "[Symbol Docs]({lnk})\n");
|
let _ = writeln!(md, "[Symbol Docs]({lnk})\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let head = jbase64(&sym.head);
|
let child_children = std::mem::take(&mut child.children);
|
||||||
|
let head = jbase64(&child);
|
||||||
let _ = writeln!(md, "<!-- begin:symbol {ident} {head} -->");
|
let _ = writeln!(md, "<!-- begin:symbol {ident} {head} -->");
|
||||||
|
|
||||||
if let Some(DefDocs::Function(sig)) = &sym.head.parsed_docs {
|
if let Some(DefDocs::Function(sig)) = &child.parsed_docs {
|
||||||
let _ = writeln!(md, "<!-- begin:sig -->");
|
let _ = writeln!(md, "<!-- begin:sig -->");
|
||||||
let _ = writeln!(md, "```typc");
|
let _ = writeln!(md, "```typc");
|
||||||
let _ = write!(md, "let {}", sym.head.name);
|
let _ = write!(md, "let {}", child.name);
|
||||||
let _ = sig.print(&mut md);
|
let _ = sig.print(&mut md);
|
||||||
let _ = writeln!(md, ";");
|
let _ = writeln!(md, ";");
|
||||||
let _ = writeln!(md, "```");
|
let _ = writeln!(md, "```");
|
||||||
let _ = writeln!(md, "<!-- end:sig -->");
|
let _ = writeln!(md, "<!-- end:sig -->");
|
||||||
}
|
}
|
||||||
|
|
||||||
match (&sym.head.parsed_docs, convert_err) {
|
match (&child.parsed_docs, convert_err) {
|
||||||
(_, Some(err)) => {
|
(_, Some(err)) => {
|
||||||
let err = format!("failed to convert docs in {title}: {err}").replace(
|
let err = format!("failed to convert docs in {title}: {err}").replace(
|
||||||
"-->", "—>", // avoid markdown comment
|
"-->", "—>", // avoid markdown comment
|
||||||
|
@ -244,8 +245,8 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
||||||
(None, None) => {}
|
(None, None) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let plain_docs = sym.head.docs.as_deref();
|
let plain_docs = child.docs.as_deref();
|
||||||
let plain_docs = plain_docs.or(sym.head.oneliner.as_deref());
|
let plain_docs = plain_docs.or(child.oneliner.as_deref());
|
||||||
|
|
||||||
if let Some(docs) = plain_docs {
|
if let Some(docs) = plain_docs {
|
||||||
let contains_code = docs.contains("```");
|
let contains_code = docs.contains("```");
|
||||||
|
@ -258,9 +259,9 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sym.children.is_empty() {
|
if !child_children.is_empty() {
|
||||||
log::debug!("sub_fid: {sym_fid:?}");
|
log::debug!("sub_fid: {child_fid:?}");
|
||||||
match sym_fid {
|
match child_fid {
|
||||||
Some(fid) => {
|
Some(fid) => {
|
||||||
let aka = akas(fid);
|
let aka = akas(fid);
|
||||||
let primary = aka.first().cloned().unwrap_or_default();
|
let primary = aka.first().cloned().unwrap_or_default();
|
||||||
|
@ -268,7 +269,8 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
||||||
let _ = writeln!(md, "[Module Docs](#{link})\n");
|
let _ = writeln!(md, "[Module Docs](#{link})\n");
|
||||||
|
|
||||||
if generated_modules.insert(fid) {
|
if generated_modules.insert(fid) {
|
||||||
modules_to_generate.push((ident.clone(), sym));
|
child.children = child_children;
|
||||||
|
modules_to_generate.push((ident.clone(), child));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub struct DocumentColorRequest {
|
||||||
impl SemanticRequest for DocumentColorRequest {
|
impl SemanticRequest for DocumentColorRequest {
|
||||||
type Response = Vec<ColorInformation>;
|
type Response = Vec<ColorInformation>;
|
||||||
|
|
||||||
fn request(self, ctx: &mut AnalysisContext) -> Option<Self::Response> {
|
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response> {
|
||||||
let source = ctx.source_by_path(&self.path).ok()?;
|
let source = ctx.source_by_path(&self.path).ok()?;
|
||||||
get_color_exprs(ctx, &source)
|
get_color_exprs(ctx, &source)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub struct DocumentHighlightRequest {
|
||||||
impl SemanticRequest for DocumentHighlightRequest {
|
impl SemanticRequest for DocumentHighlightRequest {
|
||||||
type Response = Vec<DocumentHighlight>;
|
type Response = Vec<DocumentHighlight>;
|
||||||
|
|
||||||
fn request(self, ctx: &mut AnalysisContext) -> Option<Self::Response> {
|
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response> {
|
||||||
let source = ctx.source_by_path(&self.path).ok()?;
|
let source = ctx.source_by_path(&self.path).ok()?;
|
||||||
let cursor = ctx.to_typst_pos(self.position, &source)?;
|
let cursor = ctx.to_typst_pos(self.position, &source)?;
|
||||||
|
|
||||||
|
@ -44,15 +44,15 @@ impl SemanticRequest for DocumentHighlightRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DocumentHighlightWorker<'a, 'w> {
|
struct DocumentHighlightWorker<'a> {
|
||||||
ctx: &'a mut AnalysisContext<'w>,
|
ctx: &'a mut LocalContext,
|
||||||
current: &'a Source,
|
current: &'a Source,
|
||||||
highlights: Vec<DocumentHighlight>,
|
highlights: Vec<DocumentHighlight>,
|
||||||
worklist: Vec<LinkedNode<'a>>,
|
worklist: Vec<LinkedNode<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'w> DocumentHighlightWorker<'a, 'w> {
|
impl<'a> DocumentHighlightWorker<'a> {
|
||||||
fn new(ctx: &'a mut AnalysisContext<'w>, current: &'a Source) -> Self {
|
fn new(ctx: &'a mut LocalContext, current: &'a Source) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ctx,
|
ctx,
|
||||||
current,
|
current,
|
||||||
|
@ -138,7 +138,7 @@ impl<'a, 'w> DocumentHighlightWorker<'a, 'w> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn highlight_func_returns(
|
fn highlight_func_returns(
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
node: &LinkedNode,
|
node: &LinkedNode,
|
||||||
) -> Option<Vec<DocumentHighlight>> {
|
) -> Option<Vec<DocumentHighlight>> {
|
||||||
let _ = ctx;
|
let _ = ctx;
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub struct DocumentLinkRequest {
|
||||||
impl SemanticRequest for DocumentLinkRequest {
|
impl SemanticRequest for DocumentLinkRequest {
|
||||||
type Response = Vec<DocumentLink>;
|
type Response = Vec<DocumentLink>;
|
||||||
|
|
||||||
fn request(self, ctx: &mut AnalysisContext) -> Option<Self::Response> {
|
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response> {
|
||||||
let source = ctx.source_by_path(&self.path).ok()?;
|
let source = ctx.source_by_path(&self.path).ok()?;
|
||||||
let links = get_link_exprs(ctx, &source);
|
let links = get_link_exprs(ctx, &source);
|
||||||
links.map(|links| {
|
links.map(|links| {
|
||||||
|
|
|
@ -100,7 +100,7 @@ impl StatefulRequest for DocumentMetricsRequest {
|
||||||
|
|
||||||
fn request(
|
fn request(
|
||||||
self,
|
self,
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
doc: Option<VersionedDocument>,
|
doc: Option<VersionedDocument>,
|
||||||
) -> Option<Self::Response> {
|
) -> Option<Self::Response> {
|
||||||
let doc = doc?;
|
let doc = doc?;
|
||||||
|
@ -134,14 +134,14 @@ struct FontInfoValue {
|
||||||
first_occur_column: Option<u32>,
|
first_occur_column: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DocumentMetricsWorker<'a, 'w> {
|
struct DocumentMetricsWorker<'a> {
|
||||||
ctx: &'a mut AnalysisContext<'w>,
|
ctx: &'a mut LocalContext,
|
||||||
span_info: HashMap<Arc<DataSource>, u32>,
|
span_info: HashMap<Arc<DataSource>, u32>,
|
||||||
span_info2: Vec<DataSource>,
|
span_info2: Vec<DataSource>,
|
||||||
font_info: HashMap<Font, FontInfoValue>,
|
font_info: HashMap<Font, FontInfoValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'w> DocumentMetricsWorker<'a, 'w> {
|
impl<'a> DocumentMetricsWorker<'a> {
|
||||||
fn work(&mut self, doc: &Document) -> Option<()> {
|
fn work(&mut self, doc: &Document) -> Option<()> {
|
||||||
for page in &doc.pages {
|
for page in &doc.pages {
|
||||||
self.work_frame(&page.frame)?;
|
self.work_frame(&page.frame)?;
|
||||||
|
|
|
@ -30,14 +30,14 @@ pub struct GotoDeclarationRequest {
|
||||||
impl SemanticRequest for GotoDeclarationRequest {
|
impl SemanticRequest for GotoDeclarationRequest {
|
||||||
type Response = GotoDeclarationResponse;
|
type Response = GotoDeclarationResponse;
|
||||||
|
|
||||||
fn request(self, _ctx: &mut AnalysisContext) -> Option<Self::Response> {
|
fn request(self, _ctx: &mut LocalContext) -> Option<Self::Response> {
|
||||||
let _ = find_declarations;
|
let _ = find_declarations;
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_declarations(
|
fn find_declarations(
|
||||||
_ctx: &AnalysisContext,
|
_ctx: &LocalContext,
|
||||||
_expr_info: Arc<crate::syntax::ExprInfo>,
|
_expr_info: Arc<crate::syntax::ExprInfo>,
|
||||||
_deref_target: DerefTarget<'_>,
|
_deref_target: DerefTarget<'_>,
|
||||||
) -> Option<Vec<Range<usize>>> {
|
) -> Option<Vec<Range<usize>>> {
|
||||||
|
|
|
@ -28,7 +28,7 @@ impl StatefulRequest for GotoDefinitionRequest {
|
||||||
|
|
||||||
fn request(
|
fn request(
|
||||||
self,
|
self,
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
doc: Option<VersionedDocument>,
|
doc: Option<VersionedDocument>,
|
||||||
) -> Option<Self::Response> {
|
) -> Option<Self::Response> {
|
||||||
let source = ctx.source_by_path(&self.path).ok()?;
|
let source = ctx.source_by_path(&self.path).ok()?;
|
||||||
|
@ -38,9 +38,7 @@ impl StatefulRequest for GotoDefinitionRequest {
|
||||||
let def = ctx.def_of_syntax(&source, doc.as_ref(), deref_target)?;
|
let def = ctx.def_of_syntax(&source, doc.as_ref(), deref_target)?;
|
||||||
|
|
||||||
let (fid, def_range) = def.def_at(ctx.shared())?;
|
let (fid, def_range) = def.def_at(ctx.shared())?;
|
||||||
|
|
||||||
let uri = ctx.uri_for_id(fid).ok()?;
|
let uri = ctx.uri_for_id(fid).ok()?;
|
||||||
|
|
||||||
let range = ctx.to_lsp_range_(def_range, fid)?;
|
let range = ctx.to_lsp_range_(def_range, fid)?;
|
||||||
|
|
||||||
let res = Some(GotoDefinitionResponse::Link(vec![LocationLink {
|
let res = Some(GotoDefinitionResponse::Link(vec![LocationLink {
|
||||||
|
|
|
@ -1,17 +1,12 @@
|
||||||
use core::fmt::Write;
|
use core::fmt::{self, Write};
|
||||||
|
|
||||||
|
use typst::foundations::repr::separated_list;
|
||||||
use typst_shim::syntax::LinkedNodeExt;
|
use typst_shim::syntax::LinkedNodeExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::analysis::get_link_exprs_in;
|
||||||
analysis::{get_link_exprs_in, Definition},
|
use crate::jump_from_cursor;
|
||||||
docs::DefDocs,
|
use crate::prelude::*;
|
||||||
jump_from_cursor,
|
use crate::upstream::{expr_tooltip, route_of_value, truncated_repr, Tooltip};
|
||||||
prelude::*,
|
|
||||||
syntax::{get_deref_target, Decl, DefKind},
|
|
||||||
ty::PathPreference,
|
|
||||||
upstream::{expr_tooltip, route_of_value, truncated_repr, Tooltip},
|
|
||||||
LspHoverContents, StatefulRequest,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The [`textDocument/hover`] request asks the server for hover information at
|
/// The [`textDocument/hover`] request asks the server for hover information at
|
||||||
/// a given text document position.
|
/// a given text document position.
|
||||||
|
@ -33,7 +28,7 @@ impl StatefulRequest for HoverRequest {
|
||||||
|
|
||||||
fn request(
|
fn request(
|
||||||
self,
|
self,
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
doc: Option<VersionedDocument>,
|
doc: Option<VersionedDocument>,
|
||||||
) -> Option<Self::Response> {
|
) -> Option<Self::Response> {
|
||||||
let doc_ref = doc.as_ref().map(|doc| doc.document.as_ref());
|
let doc_ref = doc.as_ref().map(|doc| doc.document.as_ref());
|
||||||
|
@ -43,23 +38,18 @@ impl StatefulRequest for HoverRequest {
|
||||||
// the typst's cursor is 1-based, so we need to add 1 to the offset
|
// the typst's cursor is 1-based, so we need to add 1 to the offset
|
||||||
let cursor = offset + 1;
|
let cursor = offset + 1;
|
||||||
|
|
||||||
|
let node = LinkedNode::new(source.root()).leaf_at_compat(cursor)?;
|
||||||
|
let range = ctx.to_lsp_range(node.range(), &source);
|
||||||
|
|
||||||
let contents = def_tooltip(ctx, &source, doc.as_ref(), cursor)
|
let contents = def_tooltip(ctx, &source, doc.as_ref(), cursor)
|
||||||
.or_else(|| star_tooltip(ctx, &source, cursor))
|
.or_else(|| star_tooltip(ctx, &node))
|
||||||
.or_else(|| link_tooltip(ctx, &source, cursor));
|
.or_else(|| link_tooltip(ctx, &node, cursor))
|
||||||
|
.or_else(|| Some(to_lsp_tooltip(&ctx.tooltip(doc_ref, &source, cursor)?)))?;
|
||||||
let contents = contents.or_else(|| {
|
|
||||||
Some(typst_to_lsp::tooltip(
|
|
||||||
&ctx.tooltip(doc_ref, &source, cursor)?,
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let ast_node = LinkedNode::new(source.root()).leaf_at_compat(cursor)?;
|
|
||||||
let range = ctx.to_lsp_range(ast_node.range(), &source);
|
|
||||||
|
|
||||||
// Neovim shows ugly hover if the hover content is in array, so we join them
|
// Neovim shows ugly hover if the hover content is in array, so we join them
|
||||||
// manually with divider bars.
|
// manually with divider bars.
|
||||||
let mut contents = match contents {
|
let mut contents = match contents {
|
||||||
LspHoverContents::Array(contents) => contents
|
HoverContents::Array(contents) => contents
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|e| match e {
|
.map(|e| match e {
|
||||||
MarkedString::LanguageString(e) => {
|
MarkedString::LanguageString(e) => {
|
||||||
|
@ -68,8 +58,8 @@ impl StatefulRequest for HoverRequest {
|
||||||
MarkedString::String(e) => e,
|
MarkedString::String(e) => e,
|
||||||
})
|
})
|
||||||
.join("\n\n---\n"),
|
.join("\n\n---\n"),
|
||||||
LspHoverContents::Scalar(MarkedString::String(contents)) => contents,
|
HoverContents::Scalar(MarkedString::String(contents)) => contents,
|
||||||
LspHoverContents::Scalar(MarkedString::LanguageString(contents)) => {
|
HoverContents::Scalar(MarkedString::LanguageString(contents)) => {
|
||||||
format!("```{}\n{}\n```", contents.language, contents.value)
|
format!("```{}\n{}\n```", contents.language, contents.value)
|
||||||
}
|
}
|
||||||
lsp_types::HoverContents::Markup(e) => {
|
lsp_types::HoverContents::Markup(e) => {
|
||||||
|
@ -81,7 +71,7 @@ impl StatefulRequest for HoverRequest {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if ctx.analysis.enable_periscope {
|
if let Some(p) = ctx.analysis.periscope.clone() {
|
||||||
if let Some(doc) = doc.clone() {
|
if let Some(doc) = doc.clone() {
|
||||||
let position = jump_from_cursor(&doc.document, &source, cursor);
|
let position = jump_from_cursor(&doc.document, &source, cursor);
|
||||||
let position = position.or_else(|| {
|
let position = position.or_else(|| {
|
||||||
|
@ -106,7 +96,7 @@ impl StatefulRequest for HoverRequest {
|
||||||
});
|
});
|
||||||
|
|
||||||
log::info!("telescope position: {:?}", position);
|
log::info!("telescope position: {:?}", position);
|
||||||
let content = position.and_then(|pos| ctx.resources.periscope_at(ctx, doc, pos));
|
let content = position.and_then(|pos| p.periscope_at(ctx, doc, pos));
|
||||||
if let Some(preview_content) = content {
|
if let Some(preview_content) = content {
|
||||||
contents = format!("{preview_content}\n---\n{contents}");
|
contents = format!("{preview_content}\n---\n{contents}");
|
||||||
}
|
}
|
||||||
|
@ -114,114 +104,18 @@ impl StatefulRequest for HoverRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Hover {
|
Some(Hover {
|
||||||
contents: LspHoverContents::Scalar(MarkedString::String(contents)),
|
contents: HoverContents::Scalar(MarkedString::String(contents)),
|
||||||
range: Some(range),
|
range: Some(range),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn link_tooltip(
|
|
||||||
ctx: &mut AnalysisContext<'_>,
|
|
||||||
source: &Source,
|
|
||||||
cursor: usize,
|
|
||||||
) -> Option<HoverContents> {
|
|
||||||
let mut node = LinkedNode::new(source.root()).leaf_at_compat(cursor)?;
|
|
||||||
while !matches!(node.kind(), SyntaxKind::FuncCall) {
|
|
||||||
node = node.parent()?.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut links = get_link_exprs_in(ctx, &node)?;
|
|
||||||
links.retain(|link| link.0.contains(&cursor));
|
|
||||||
if links.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut results = vec![];
|
|
||||||
let mut actions = vec![];
|
|
||||||
for (_, target) in links {
|
|
||||||
// open file in tab or system application
|
|
||||||
actions.push(CommandLink {
|
|
||||||
title: Some("Open in Tab".to_string()),
|
|
||||||
command_or_links: vec![CommandOrLink::Command(Command {
|
|
||||||
id: "tinymist.openInternal".to_string(),
|
|
||||||
args: vec![JsonValue::String(target.to_string())],
|
|
||||||
})],
|
|
||||||
});
|
|
||||||
actions.push(CommandLink {
|
|
||||||
title: Some("Open Externally".to_string()),
|
|
||||||
command_or_links: vec![CommandOrLink::Command(Command {
|
|
||||||
id: "tinymist.openExternal".to_string(),
|
|
||||||
args: vec![JsonValue::String(target.to_string())],
|
|
||||||
})],
|
|
||||||
});
|
|
||||||
if let Some(kind) = PathPreference::from_ext(target.path()) {
|
|
||||||
let preview = format!("A `{kind:?}` file.");
|
|
||||||
results.push(MarkedString::String(preview));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
render_actions(&mut results, actions);
|
|
||||||
if results.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(LspHoverContents::Array(results))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn star_tooltip(
|
|
||||||
ctx: &mut AnalysisContext,
|
|
||||||
source: &Source,
|
|
||||||
cursor: usize,
|
|
||||||
) -> Option<HoverContents> {
|
|
||||||
let leaf = LinkedNode::new(source.root()).leaf_at_compat(cursor)?;
|
|
||||||
|
|
||||||
if !matches!(leaf.kind(), SyntaxKind::Star) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut leaf = &leaf;
|
|
||||||
while !matches!(leaf.kind(), SyntaxKind::ModuleImport) {
|
|
||||||
leaf = leaf.parent()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mi: ast::ModuleImport = leaf.cast()?;
|
|
||||||
let source = mi.source();
|
|
||||||
let module = ctx.analyze_import(source.to_untyped()).1;
|
|
||||||
log::debug!("star import: {source:?} => {:?}", module.is_some());
|
|
||||||
|
|
||||||
let i = module?;
|
|
||||||
let scope = i.scope()?;
|
|
||||||
|
|
||||||
let mut results = vec![];
|
|
||||||
|
|
||||||
let mut names = scope.iter().map(|(name, _, _)| name).collect::<Vec<_>>();
|
|
||||||
names.sort();
|
|
||||||
let items = typst::foundations::repr::separated_list(&names, "and");
|
|
||||||
|
|
||||||
results.push(MarkedString::String(format!("This star imports {items}")));
|
|
||||||
Some(LspHoverContents::Array(results))
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Command {
|
|
||||||
id: String,
|
|
||||||
args: Vec<JsonValue>,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum CommandOrLink {
|
|
||||||
Link(String),
|
|
||||||
Command(Command),
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CommandLink {
|
|
||||||
title: Option<String>,
|
|
||||||
command_or_links: Vec<CommandOrLink>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn def_tooltip(
|
fn def_tooltip(
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
source: &Source,
|
source: &Source,
|
||||||
document: Option<&VersionedDocument>,
|
document: Option<&VersionedDocument>,
|
||||||
cursor: usize,
|
cursor: usize,
|
||||||
) -> Option<LspHoverContents> {
|
) -> Option<HoverContents> {
|
||||||
let leaf = LinkedNode::new(source.root()).leaf_at_compat(cursor)?;
|
let leaf = LinkedNode::new(source.root()).leaf_at_compat(cursor)?;
|
||||||
let deref_target = get_deref_target(leaf.clone(), cursor)?;
|
let deref_target = get_deref_target(leaf.clone(), cursor)?;
|
||||||
let def = ctx.def_of_syntax(source, document, deref_target.clone())?;
|
let def = ctx.def_of_syntax(source, document, deref_target.clone())?;
|
||||||
|
@ -238,7 +132,7 @@ fn def_tooltip(
|
||||||
let c = truncated_repr(&c);
|
let c = truncated_repr(&c);
|
||||||
results.push(MarkedString::String(format!("{c}")));
|
results.push(MarkedString::String(format!("{c}")));
|
||||||
}
|
}
|
||||||
Some(LspHoverContents::Array(results))
|
Some(HoverContents::Array(results))
|
||||||
}
|
}
|
||||||
BibEntry(..) => {
|
BibEntry(..) => {
|
||||||
results.push(MarkedString::String(format!(
|
results.push(MarkedString::String(format!(
|
||||||
|
@ -246,7 +140,7 @@ fn def_tooltip(
|
||||||
def.name()
|
def.name()
|
||||||
)));
|
)));
|
||||||
|
|
||||||
Some(LspHoverContents::Array(results))
|
Some(HoverContents::Array(results))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let sym_docs = ctx.def_docs(&def);
|
let sym_docs = ctx.def_docs(&def);
|
||||||
|
@ -308,16 +202,82 @@ fn def_tooltip(
|
||||||
// results.push(MarkedString::String(doc));
|
// results.push(MarkedString::String(doc));
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if let Some(link) = ExternalDocLink::get(ctx, &def) {
|
if let Some(link) = ExternalDocLink::get(&def) {
|
||||||
actions.push(link);
|
actions.push(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
render_actions(&mut results, actions);
|
render_actions(&mut results, actions);
|
||||||
Some(LspHoverContents::Array(results))
|
Some(HoverContents::Array(results))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn star_tooltip(ctx: &mut LocalContext, mut node: &LinkedNode) -> Option<HoverContents> {
|
||||||
|
if !matches!(node.kind(), SyntaxKind::Star) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
while !matches!(node.kind(), SyntaxKind::ModuleImport) {
|
||||||
|
node = node.parent()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let import_node = node.cast::<ast::ModuleImport>()?;
|
||||||
|
let scope_val = ctx.analyze_import(import_node.source().to_untyped()).1?;
|
||||||
|
|
||||||
|
let scope_items = scope_val.scope()?.iter();
|
||||||
|
let mut names = scope_items.map(|item| item.0.as_str()).collect::<Vec<_>>();
|
||||||
|
names.sort();
|
||||||
|
|
||||||
|
let content = format!("This star imports {}", separated_list(&names, "and"));
|
||||||
|
Some(HoverContents::Scalar(MarkedString::String(content)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn link_tooltip(
|
||||||
|
ctx: &mut LocalContext,
|
||||||
|
mut node: &LinkedNode,
|
||||||
|
cursor: usize,
|
||||||
|
) -> Option<HoverContents> {
|
||||||
|
while !matches!(node.kind(), SyntaxKind::FuncCall) {
|
||||||
|
node = node.parent()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut links = get_link_exprs_in(ctx, node)?;
|
||||||
|
links.retain(|link| link.0.contains(&cursor));
|
||||||
|
if links.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut results = vec![];
|
||||||
|
let mut actions = vec![];
|
||||||
|
for (_, target) in links {
|
||||||
|
// open file in tab or system application
|
||||||
|
actions.push(CommandLink {
|
||||||
|
title: Some("Open in Tab".to_string()),
|
||||||
|
command_or_links: vec![CommandOrLink::Command {
|
||||||
|
id: "tinymist.openInternal".to_string(),
|
||||||
|
args: vec![JsonValue::String(target.to_string())],
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
actions.push(CommandLink {
|
||||||
|
title: Some("Open Externally".to_string()),
|
||||||
|
command_or_links: vec![CommandOrLink::Command {
|
||||||
|
id: "tinymist.openExternal".to_string(),
|
||||||
|
args: vec![JsonValue::String(target.to_string())],
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
if let Some(kind) = PathPreference::from_ext(target.path()) {
|
||||||
|
let preview = format!("A `{kind:?}` file.");
|
||||||
|
results.push(MarkedString::String(preview));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render_actions(&mut results, actions);
|
||||||
|
if results.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(HoverContents::Array(results))
|
||||||
|
}
|
||||||
|
|
||||||
fn push_result_ty(name: &str, ty_repr: Option<&(String, String)>, type_doc: &mut String) {
|
fn push_result_ty(name: &str, ty_repr: Option<&(String, String)>, type_doc: &mut String) {
|
||||||
let Some((short, _)) = ty_repr else {
|
let Some((short, _)) = ty_repr else {
|
||||||
return;
|
return;
|
||||||
|
@ -334,48 +294,14 @@ fn render_actions(results: &mut Vec<MarkedString>, actions: Vec<CommandLink>) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let g = actions
|
let g = actions.into_iter().join(" | ");
|
||||||
.into_iter()
|
|
||||||
.map(|action| {
|
|
||||||
// https://github.com/rust-lang/rust-analyzer/blob/1a5bb27c018c947dab01ab70ffe1d267b0481a17/editors/code/src/client.ts#L59
|
|
||||||
let title = action.title.unwrap_or("".to_owned());
|
|
||||||
let command_or_links = action
|
|
||||||
.command_or_links
|
|
||||||
.into_iter()
|
|
||||||
.map(|col| match col {
|
|
||||||
CommandOrLink::Link(link) => link,
|
|
||||||
CommandOrLink::Command(command) => {
|
|
||||||
let id = command.id;
|
|
||||||
// <https://code.visualstudio.com/api/extension-guides/command#command-uris>
|
|
||||||
if command.args.is_empty() {
|
|
||||||
format!("command:{id}")
|
|
||||||
} else {
|
|
||||||
let args = serde_json::to_string(&command.args).unwrap();
|
|
||||||
let args = percent_encoding::utf8_percent_encode(
|
|
||||||
&args,
|
|
||||||
percent_encoding::NON_ALPHANUMERIC,
|
|
||||||
);
|
|
||||||
format!("command:{id}?{args}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(" ");
|
|
||||||
format!("[{title}]({command_or_links})")
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(" | ");
|
|
||||||
results.push(MarkedString::String(g));
|
results.push(MarkedString::String(g));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExternalDocLink;
|
struct ExternalDocLink;
|
||||||
|
|
||||||
impl ExternalDocLink {
|
impl ExternalDocLink {
|
||||||
fn get(ctx: &mut AnalysisContext, def: &Definition) -> Option<CommandLink> {
|
fn get(def: &Definition) -> Option<CommandLink> {
|
||||||
self::ExternalDocLink::get_inner(ctx, def)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_inner(_ctx: &mut AnalysisContext, def: &Definition) -> Option<CommandLink> {
|
|
||||||
let value = def.value();
|
let value = def.value();
|
||||||
|
|
||||||
if matches!(value, Some(Value::Func(..))) {
|
if matches!(value, Some(Value::Func(..))) {
|
||||||
|
@ -386,9 +312,7 @@ impl ExternalDocLink {
|
||||||
|
|
||||||
value.and_then(|value| Self::builtin_value_tooltip("https://typst.app/docs/", &value))
|
value.and_then(|value| Self::builtin_value_tooltip("https://typst.app/docs/", &value))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl ExternalDocLink {
|
|
||||||
fn builtin_func_tooltip(base: &str, def: &Definition) -> Option<CommandLink> {
|
fn builtin_func_tooltip(base: &str, def: &Definition) -> Option<CommandLink> {
|
||||||
let Some(Value::Func(func)) = def.value() else {
|
let Some(Value::Func(func)) = def.value() else {
|
||||||
return None;
|
return None;
|
||||||
|
@ -422,6 +346,57 @@ impl ExternalDocLink {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CommandLink {
|
||||||
|
title: Option<String>,
|
||||||
|
command_or_links: Vec<CommandOrLink>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CommandLink {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
// https://github.com/rust-lang/rust-analyzer/blob/1a5bb27c018c947dab01ab70ffe1d267b0481a17/editors/code/src/client.ts#L59
|
||||||
|
let title = self.title.as_deref().unwrap_or("");
|
||||||
|
let command_or_links = self.command_or_links.iter().join(" ");
|
||||||
|
write!(f, "[{title}]({command_or_links})")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CommandOrLink {
|
||||||
|
Link(String),
|
||||||
|
Command { id: String, args: Vec<JsonValue> },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CommandOrLink {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Link(link) => f.write_str(link),
|
||||||
|
Self::Command { id, args } => {
|
||||||
|
// <https://code.visualstudio.com/api/extension-guides/command#command-uris>
|
||||||
|
if args.is_empty() {
|
||||||
|
return write!(f, "command:{id}");
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = serde_json::to_string(&args).unwrap();
|
||||||
|
let args = percent_encoding::utf8_percent_encode(
|
||||||
|
&args,
|
||||||
|
percent_encoding::NON_ALPHANUMERIC,
|
||||||
|
);
|
||||||
|
write!(f, "command:{id}?{args}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_lsp_tooltip(typst_tooltip: &Tooltip) -> HoverContents {
|
||||||
|
let lsp_marked_string = match typst_tooltip {
|
||||||
|
Tooltip::Text(text) => MarkedString::String(text.to_string()),
|
||||||
|
Tooltip::Code(code) => MarkedString::LanguageString(LanguageString {
|
||||||
|
language: "typc".to_owned(),
|
||||||
|
value: code.to_string(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
HoverContents::Scalar(lsp_marked_string)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -63,7 +63,7 @@ pub struct InlayHintRequest {
|
||||||
impl SemanticRequest for InlayHintRequest {
|
impl SemanticRequest for InlayHintRequest {
|
||||||
type Response = Vec<InlayHint>;
|
type Response = Vec<InlayHint>;
|
||||||
|
|
||||||
fn request(self, ctx: &mut AnalysisContext) -> Option<Self::Response> {
|
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response> {
|
||||||
let source = ctx.source_by_path(&self.path).ok()?;
|
let source = ctx.source_by_path(&self.path).ok()?;
|
||||||
let range = ctx.to_typst_range(self.range, &source)?;
|
let range = ctx.to_typst_range(self.range, &source)?;
|
||||||
|
|
||||||
|
@ -83,22 +83,22 @@ impl SemanticRequest for InlayHintRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inlay_hint(
|
fn inlay_hint(
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
source: &Source,
|
source: &Source,
|
||||||
range: Range<usize>,
|
range: Range<usize>,
|
||||||
encoding: PositionEncoding,
|
encoding: PositionEncoding,
|
||||||
) -> FileResult<Vec<InlayHint>> {
|
) -> FileResult<Vec<InlayHint>> {
|
||||||
const SMART: InlayHintConfig = InlayHintConfig::smart();
|
const SMART: InlayHintConfig = InlayHintConfig::smart();
|
||||||
|
|
||||||
struct InlayHintWorker<'a, 'w> {
|
struct InlayHintWorker<'a> {
|
||||||
ctx: &'a mut AnalysisContext<'w>,
|
ctx: &'a mut LocalContext,
|
||||||
source: &'a Source,
|
source: &'a Source,
|
||||||
range: Range<usize>,
|
range: Range<usize>,
|
||||||
encoding: PositionEncoding,
|
encoding: PositionEncoding,
|
||||||
hints: Vec<InlayHint>,
|
hints: Vec<InlayHint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InlayHintWorker<'_, '_> {
|
impl InlayHintWorker<'_> {
|
||||||
fn analyze(&mut self, node: LinkedNode) {
|
fn analyze(&mut self, node: LinkedNode) {
|
||||||
let rng = node.range();
|
let rng = node.range();
|
||||||
if rng.start >= self.range.end || rng.end <= self.range.start {
|
if rng.start >= self.range.end || rng.end <= self.range.start {
|
||||||
|
|
|
@ -15,7 +15,7 @@ pub mod syntax;
|
||||||
pub mod ty;
|
pub mod ty;
|
||||||
mod upstream;
|
mod upstream;
|
||||||
|
|
||||||
pub use analysis::AnalysisContext;
|
pub use analysis::{LocalContext, LocalContextGuard};
|
||||||
pub use upstream::with_vm;
|
pub use upstream::with_vm;
|
||||||
|
|
||||||
mod diagnostics;
|
mod diagnostics;
|
||||||
|
@ -119,7 +119,7 @@ pub trait SemanticRequest {
|
||||||
type Response;
|
type Response;
|
||||||
|
|
||||||
/// Request the information from the given context.
|
/// Request the information from the given context.
|
||||||
fn request(self, ctx: &mut AnalysisContext) -> Option<Self::Response>;
|
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A request handler with given (semantic) analysis context and a versioned
|
/// A request handler with given (semantic) analysis context and a versioned
|
||||||
|
@ -131,7 +131,7 @@ pub trait StatefulRequest {
|
||||||
/// Request the information from the given context.
|
/// Request the information from the given context.
|
||||||
fn request(
|
fn request(
|
||||||
self,
|
self,
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
doc: Option<VersionedDocument>,
|
doc: Option<VersionedDocument>,
|
||||||
) -> Option<Self::Response>;
|
) -> Option<Self::Response>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,7 @@
|
||||||
// todo: remove this
|
// todo: remove this
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use crate::prelude::*;
|
||||||
|
|
||||||
use lsp_types::{self, Url};
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use reflexo::path::PathClean;
|
use reflexo::path::PathClean;
|
||||||
|
|
||||||
pub type LspPosition = lsp_types::Position;
|
pub type LspPosition = lsp_types::Position;
|
||||||
|
@ -24,9 +21,6 @@ pub type TypstSpan = typst::syntax::Span;
|
||||||
pub type LspRange = lsp_types::Range;
|
pub type LspRange = lsp_types::Range;
|
||||||
pub type TypstRange = std::ops::Range<usize>;
|
pub type TypstRange = std::ops::Range<usize>;
|
||||||
|
|
||||||
pub type TypstTooltip = crate::upstream::Tooltip;
|
|
||||||
pub type LspHoverContents = lsp_types::HoverContents;
|
|
||||||
|
|
||||||
pub type LspDiagnostic = lsp_types::Diagnostic;
|
pub type LspDiagnostic = lsp_types::Diagnostic;
|
||||||
pub type TypstDiagnostic = typst::diag::SourceDiagnostic;
|
pub type TypstDiagnostic = typst::diag::SourceDiagnostic;
|
||||||
|
|
||||||
|
@ -64,7 +58,7 @@ impl From<PositionEncoding> for lsp_types::PositionEncodingKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
const UNTITLED_ROOT: &str = "/untitled";
|
const UNTITLED_ROOT: &str = "/untitled";
|
||||||
static EMPTY_URL: Lazy<Url> = Lazy::new(|| Url::parse("file://").unwrap());
|
static EMPTY_URL: LazyLock<Url> = LazyLock::new(|| Url::parse("file://").unwrap());
|
||||||
|
|
||||||
pub fn path_to_url(path: &Path) -> anyhow::Result<Url> {
|
pub fn path_to_url(path: &Path) -> anyhow::Result<Url> {
|
||||||
if let Ok(untitled) = path.strip_prefix(UNTITLED_ROOT) {
|
if let Ok(untitled) = path.strip_prefix(UNTITLED_ROOT) {
|
||||||
|
@ -215,10 +209,6 @@ pub mod lsp_to_typst {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod typst_to_lsp {
|
pub mod typst_to_lsp {
|
||||||
|
|
||||||
use lsp_types::{LanguageString, MarkedString};
|
|
||||||
use typst::syntax::Source;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn offset_to_position(
|
pub fn offset_to_position(
|
||||||
|
@ -270,23 +260,11 @@ pub mod typst_to_lsp {
|
||||||
|
|
||||||
LspRange::new(lsp_start, lsp_end)
|
LspRange::new(lsp_start, lsp_end)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tooltip(typst_tooltip: &TypstTooltip) -> LspHoverContents {
|
|
||||||
let lsp_marked_string = match typst_tooltip {
|
|
||||||
TypstTooltip::Text(text) => MarkedString::String(text.to_string()),
|
|
||||||
TypstTooltip::Code(code) => MarkedString::LanguageString(LanguageString {
|
|
||||||
language: "typc".to_owned(),
|
|
||||||
value: code.to_string(),
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
LspHoverContents::Scalar(lsp_marked_string)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use lsp_types::Position;
|
use lsp_types::Position;
|
||||||
use typst::syntax::Source;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
pub use std::{
|
pub use std::collections::HashMap;
|
||||||
collections::HashMap,
|
pub use std::iter;
|
||||||
iter,
|
pub use std::ops::Range;
|
||||||
ops::Range,
|
pub use std::path::{Path, PathBuf};
|
||||||
path::{Path, PathBuf},
|
pub use std::sync::{Arc, LazyLock};
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use ecow::eco_vec;
|
pub use ecow::{eco_vec, EcoVec};
|
||||||
pub use ecow::EcoVec;
|
|
||||||
pub use itertools::{Format, Itertools};
|
pub use itertools::{Format, Itertools};
|
||||||
pub use log::error;
|
|
||||||
pub use lsp_types::{
|
pub use lsp_types::{
|
||||||
request::GotoDeclarationResponse, CodeAction, CodeActionKind, CodeActionOrCommand, CodeLens,
|
request::GotoDeclarationResponse, CodeAction, CodeActionKind, CodeActionOrCommand, CodeLens,
|
||||||
ColorInformation, ColorPresentation, CompletionResponse, DiagnosticRelatedInformation,
|
ColorInformation, ColorPresentation, CompletionResponse, DiagnosticRelatedInformation,
|
||||||
|
@ -24,16 +20,18 @@ pub use reflexo::vector::ir::DefId;
|
||||||
pub use serde_json::Value as JsonValue;
|
pub use serde_json::Value as JsonValue;
|
||||||
pub use typst::diag::{EcoString, FileResult, Tracepoint};
|
pub use typst::diag::{EcoString, FileResult, Tracepoint};
|
||||||
pub use typst::foundations::Value;
|
pub use typst::foundations::Value;
|
||||||
pub use typst::syntax::FileId as TypstFileId;
|
pub use typst::syntax::ast::{self, AstNode};
|
||||||
pub use typst::syntax::{
|
pub use typst::syntax::{
|
||||||
ast::{self, AstNode},
|
FileId as TypstFileId, LinkedNode, Source, Spanned, SyntaxKind, SyntaxNode,
|
||||||
LinkedNode, Source, Spanned, SyntaxKind, SyntaxNode,
|
|
||||||
};
|
};
|
||||||
pub use typst::World;
|
pub use typst::World;
|
||||||
|
|
||||||
pub use crate::analysis::{AnalysisContext, LocalContext};
|
pub use crate::analysis::{Definition, LocalContext};
|
||||||
|
pub use crate::docs::DefDocs;
|
||||||
pub use crate::lsp_typst_boundary::{
|
pub use crate::lsp_typst_boundary::{
|
||||||
lsp_to_typst, path_to_url, typst_to_lsp, LspDiagnostic, LspRange, LspSeverity,
|
lsp_to_typst, path_to_url, typst_to_lsp, LspDiagnostic, LspRange, LspSeverity,
|
||||||
PositionEncoding, TypstDiagnostic, TypstSeverity, TypstSpan,
|
PositionEncoding, TypstDiagnostic, TypstSeverity, TypstSpan,
|
||||||
};
|
};
|
||||||
|
pub use crate::syntax::{get_deref_target, Decl, DefKind};
|
||||||
|
pub(crate) use crate::ty::PathPreference;
|
||||||
pub use crate::{SemanticRequest, StatefulRequest, VersionedDocument};
|
pub use crate::{SemanticRequest, StatefulRequest, VersionedDocument};
|
||||||
|
|
|
@ -35,7 +35,7 @@ impl StatefulRequest for PrepareRenameRequest {
|
||||||
|
|
||||||
fn request(
|
fn request(
|
||||||
self,
|
self,
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
doc: Option<VersionedDocument>,
|
doc: Option<VersionedDocument>,
|
||||||
) -> Option<Self::Response> {
|
) -> Option<Self::Response> {
|
||||||
let source = ctx.source_by_path(&self.path).ok()?;
|
let source = ctx.source_by_path(&self.path).ok()?;
|
||||||
|
@ -59,7 +59,7 @@ impl StatefulRequest for PrepareRenameRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn prepare_renaming(
|
pub(crate) fn prepare_renaming(
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
deref_target: &DerefTarget,
|
deref_target: &DerefTarget,
|
||||||
def: &Definition,
|
def: &Definition,
|
||||||
) -> Option<(String, Option<LspRange>)> {
|
) -> Option<(String, Option<LspRange>)> {
|
||||||
|
|
|
@ -26,7 +26,7 @@ impl StatefulRequest for ReferencesRequest {
|
||||||
|
|
||||||
fn request(
|
fn request(
|
||||||
self,
|
self,
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
doc: Option<VersionedDocument>,
|
doc: Option<VersionedDocument>,
|
||||||
) -> Option<Self::Response> {
|
) -> Option<Self::Response> {
|
||||||
let source = ctx.source_by_path(&self.path).ok()?;
|
let source = ctx.source_by_path(&self.path).ok()?;
|
||||||
|
@ -40,7 +40,7 @@ impl StatefulRequest for ReferencesRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn find_references(
|
pub(crate) fn find_references(
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
source: &Source,
|
source: &Source,
|
||||||
doc: Option<&VersionedDocument>,
|
doc: Option<&VersionedDocument>,
|
||||||
target: DerefTarget<'_>,
|
target: DerefTarget<'_>,
|
||||||
|
@ -69,13 +69,13 @@ pub(crate) fn find_references(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ReferencesWorker<'a, 'w> {
|
struct ReferencesWorker<'a> {
|
||||||
ctx: SearchCtx<'a, 'w>,
|
ctx: SearchCtx<'a>,
|
||||||
references: Vec<LspLocation>,
|
references: Vec<LspLocation>,
|
||||||
def: Definition,
|
def: Definition,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'w> ReferencesWorker<'a, 'w> {
|
impl<'a> ReferencesWorker<'a> {
|
||||||
fn label_root(mut self) -> Option<Vec<LspLocation>> {
|
fn label_root(mut self) -> Option<Vec<LspLocation>> {
|
||||||
let mut ids = vec![];
|
let mut ids = vec![];
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ impl StatefulRequest for RenameRequest {
|
||||||
|
|
||||||
fn request(
|
fn request(
|
||||||
self,
|
self,
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
doc: Option<VersionedDocument>,
|
doc: Option<VersionedDocument>,
|
||||||
) -> Option<Self::Response> {
|
) -> Option<Self::Response> {
|
||||||
let source = ctx.source_by_path(&self.path).ok()?;
|
let source = ctx.source_by_path(&self.path).ok()?;
|
||||||
|
@ -117,7 +117,7 @@ impl StatefulRequest for RenameRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn do_rename_file(
|
pub(crate) fn do_rename_file(
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
def_fid: TypstFileId,
|
def_fid: TypstFileId,
|
||||||
diff: PathBuf,
|
diff: PathBuf,
|
||||||
edits: &mut HashMap<Url, Vec<TextEdit>>,
|
edits: &mut HashMap<Url, Vec<TextEdit>>,
|
||||||
|
@ -167,7 +167,7 @@ pub(crate) fn edits_to_document_changes(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rename_importer(
|
fn rename_importer(
|
||||||
ctx: &AnalysisContext,
|
ctx: &LocalContext,
|
||||||
src: &Source,
|
src: &Source,
|
||||||
span: Span,
|
span: Span,
|
||||||
diff: &Path,
|
diff: &Path,
|
||||||
|
|
|
@ -25,7 +25,7 @@ impl SemanticRequest for SemanticTokensDeltaRequest {
|
||||||
type Response = SemanticTokensFullDeltaResult;
|
type Response = SemanticTokensFullDeltaResult;
|
||||||
/// Handles the request to compute the semantic tokens delta for a given
|
/// Handles the request to compute the semantic tokens delta for a given
|
||||||
/// document.
|
/// document.
|
||||||
fn request(self, ctx: &mut AnalysisContext) -> Option<Self::Response> {
|
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response> {
|
||||||
let source = ctx.source_by_path(&self.path).ok()?;
|
let source = ctx.source_by_path(&self.path).ok()?;
|
||||||
let ei = ctx.expr_stage(&source);
|
let ei = ctx.expr_stage(&source);
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ impl SemanticRequest for SemanticTokensFullRequest {
|
||||||
type Response = SemanticTokensResult;
|
type Response = SemanticTokensResult;
|
||||||
|
|
||||||
/// Handles the request to compute the semantic tokens for a given document.
|
/// Handles the request to compute the semantic tokens for a given document.
|
||||||
fn request(self, ctx: &mut AnalysisContext) -> Option<Self::Response> {
|
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response> {
|
||||||
let source = ctx.source_by_path(&self.path).ok()?;
|
let source = ctx.source_by_path(&self.path).ok()?;
|
||||||
let ei = ctx.expr_stage(&source);
|
let ei = ctx.expr_stage(&source);
|
||||||
let token_ctx = &ctx.analysis.tokens_ctx;
|
let token_ctx = &ctx.analysis.tokens_ctx;
|
||||||
|
|
|
@ -25,7 +25,7 @@ pub struct SignatureHelpRequest {
|
||||||
impl SemanticRequest for SignatureHelpRequest {
|
impl SemanticRequest for SignatureHelpRequest {
|
||||||
type Response = SignatureHelp;
|
type Response = SignatureHelp;
|
||||||
|
|
||||||
fn request(self, ctx: &mut AnalysisContext) -> Option<Self::Response> {
|
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response> {
|
||||||
let source = ctx.source_by_path(&self.path).ok()?;
|
let source = ctx.source_by_path(&self.path).ok()?;
|
||||||
let cursor = ctx.to_typst_pos(self.position, &source)? + 1;
|
let cursor = ctx.to_typst_pos(self.position, &source)? + 1;
|
||||||
|
|
||||||
|
@ -150,11 +150,11 @@ impl SemanticRequest for SignatureHelpRequest {
|
||||||
pub(crate) struct DocTooltip;
|
pub(crate) struct DocTooltip;
|
||||||
|
|
||||||
impl DocTooltip {
|
impl DocTooltip {
|
||||||
pub fn get(ctx: &mut AnalysisContext, def: &Definition) -> Option<String> {
|
pub fn get(ctx: &mut LocalContext, def: &Definition) -> Option<String> {
|
||||||
self::DocTooltip::get_inner(ctx, def).map(|s| "\n\n".to_owned() + &s)
|
self::DocTooltip::get_inner(ctx, def).map(|s| "\n\n".to_owned() + &s)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_inner(ctx: &mut AnalysisContext, def: &Definition) -> Option<String> {
|
fn get_inner(ctx: &mut LocalContext, def: &Definition) -> Option<String> {
|
||||||
let value = def.value();
|
let value = def.value();
|
||||||
if matches!(value, Some(Value::Func(..))) {
|
if matches!(value, Some(Value::Func(..))) {
|
||||||
if let Some(builtin) = Self::builtin_func_tooltip(def) {
|
if let Some(builtin) = Self::builtin_func_tooltip(def) {
|
||||||
|
|
|
@ -31,7 +31,7 @@ pub struct SymbolRequest {
|
||||||
impl SemanticRequest for SymbolRequest {
|
impl SemanticRequest for SymbolRequest {
|
||||||
type Response = Vec<SymbolInformation>;
|
type Response = Vec<SymbolInformation>;
|
||||||
|
|
||||||
fn request(self, ctx: &mut AnalysisContext) -> Option<Self::Response> {
|
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response> {
|
||||||
// todo: let typst.ts expose source
|
// todo: let typst.ts expose source
|
||||||
|
|
||||||
let mut symbols = vec![];
|
let mut symbols = vec![];
|
||||||
|
|
|
@ -22,16 +22,13 @@ pub use tinymist_world::{LspUniverse, LspUniverseBuilder};
|
||||||
use typst_shim::syntax::LinkedNodeExt;
|
use typst_shim::syntax::LinkedNodeExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
analysis::{Analysis, AnalysisResources},
|
analysis::Analysis, prelude::LocalContext, typst_to_lsp, LspPosition, PositionEncoding,
|
||||||
prelude::AnalysisContext,
|
VersionedDocument,
|
||||||
typst_to_lsp, LspPosition, PositionEncoding, VersionedDocument,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type CompileDriver<C> = CompileDriverImpl<C, tinymist_world::LspCompilerFeat>;
|
type CompileDriver<C> = CompileDriverImpl<C, tinymist_world::LspCompilerFeat>;
|
||||||
|
|
||||||
impl AnalysisResources for () {}
|
pub fn snapshot_testing(name: &str, f: &impl Fn(&mut LocalContext, PathBuf)) {
|
||||||
|
|
||||||
pub fn snapshot_testing(name: &str, f: &impl Fn(&mut AnalysisContext, PathBuf)) {
|
|
||||||
let mut settings = insta::Settings::new();
|
let mut settings = insta::Settings::new();
|
||||||
settings.set_prepend_module_to_snapshot(false);
|
settings.set_prepend_module_to_snapshot(false);
|
||||||
settings.set_snapshot_path(format!("fixtures/{name}/snaps"));
|
settings.set_snapshot_path(format!("fixtures/{name}/snaps"));
|
||||||
|
@ -52,7 +49,7 @@ pub fn snapshot_testing(name: &str, f: &impl Fn(&mut AnalysisContext, PathBuf))
|
||||||
pub fn run_with_ctx<T>(
|
pub fn run_with_ctx<T>(
|
||||||
w: &mut LspUniverse,
|
w: &mut LspUniverse,
|
||||||
p: PathBuf,
|
p: PathBuf,
|
||||||
f: &impl Fn(&mut AnalysisContext, PathBuf) -> T,
|
f: &impl Fn(&mut LocalContext, PathBuf) -> T,
|
||||||
) -> T {
|
) -> T {
|
||||||
let root = w.workspace_root().unwrap();
|
let root = w.workspace_root().unwrap();
|
||||||
let paths = w
|
let paths = w
|
||||||
|
@ -61,7 +58,7 @@ pub fn run_with_ctx<T>(
|
||||||
.map(|p| TypstFileId::new(None, VirtualPath::new(p.strip_prefix(&root).unwrap())))
|
.map(|p| TypstFileId::new(None, VirtualPath::new(p.strip_prefix(&root).unwrap())))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut ctx = Arc::new(Analysis::default()).snapshot(w.snapshot(), &());
|
let mut ctx = Arc::new(Analysis::default()).snapshot(w.snapshot());
|
||||||
ctx.test_completion_files(Vec::new);
|
ctx.test_completion_files(Vec::new);
|
||||||
ctx.test_files(|| paths);
|
ctx.test_files(|| paths);
|
||||||
f(&mut ctx, p)
|
f(&mut ctx, p)
|
||||||
|
@ -79,7 +76,7 @@ pub fn get_test_properties(s: &str) -> HashMap<&'_ str, &'_ str> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_doc_for_test(
|
pub fn compile_doc_for_test(
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
properties: &HashMap<&str, &str>,
|
properties: &HashMap<&str, &str>,
|
||||||
) -> Option<VersionedDocument> {
|
) -> Option<VersionedDocument> {
|
||||||
let must_compile = properties
|
let must_compile = properties
|
||||||
|
|
|
@ -17,7 +17,7 @@ use unscanny::Scanner;
|
||||||
use super::{plain_docs_sentence, summarize_font_family};
|
use super::{plain_docs_sentence, summarize_font_family};
|
||||||
use crate::adt::interner::Interned;
|
use crate::adt::interner::Interned;
|
||||||
use crate::analysis::{analyze_labels, DynLabel, Ty};
|
use crate::analysis::{analyze_labels, DynLabel, Ty};
|
||||||
use crate::AnalysisContext;
|
use crate::LocalContext;
|
||||||
|
|
||||||
mod ext;
|
mod ext;
|
||||||
pub use ext::complete_path;
|
pub use ext::complete_path;
|
||||||
|
@ -551,7 +551,7 @@ fn complete_imports(ctx: &mut CompletionContext) -> bool {
|
||||||
|
|
||||||
/// Add completions for all exports of a module.
|
/// Add completions for all exports of a module.
|
||||||
fn import_item_completions<'a>(
|
fn import_item_completions<'a>(
|
||||||
ctx: &mut CompletionContext<'a, '_>,
|
ctx: &mut CompletionContext<'a>,
|
||||||
existing: ast::ImportItems<'a>,
|
existing: ast::ImportItems<'a>,
|
||||||
source: &LinkedNode,
|
source: &LinkedNode,
|
||||||
) {
|
) {
|
||||||
|
@ -951,8 +951,8 @@ fn code_completions(ctx: &mut CompletionContext, hash: bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Context for autocompletion.
|
/// Context for autocompletion.
|
||||||
pub struct CompletionContext<'a, 'b> {
|
pub struct CompletionContext<'a> {
|
||||||
pub ctx: &'a mut AnalysisContext<'b>,
|
pub ctx: &'a mut LocalContext,
|
||||||
pub document: Option<&'a Document>,
|
pub document: Option<&'a Document>,
|
||||||
pub text: &'a str,
|
pub text: &'a str,
|
||||||
pub before: &'a str,
|
pub before: &'a str,
|
||||||
|
@ -974,11 +974,11 @@ pub struct CompletionContext<'a, 'b> {
|
||||||
pub seen_fields: HashSet<Interned<str>>,
|
pub seen_fields: HashSet<Interned<str>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'w> CompletionContext<'a, 'w> {
|
impl<'a> CompletionContext<'a> {
|
||||||
/// Create a new autocompletion context.
|
/// Create a new autocompletion context.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
ctx: &'a mut AnalysisContext<'w>,
|
ctx: &'a mut LocalContext,
|
||||||
document: Option<&'a Document>,
|
document: Option<&'a Document>,
|
||||||
source: &'a Source,
|
source: &'a Source,
|
||||||
cursor: usize,
|
cursor: usize,
|
||||||
|
|
|
@ -20,7 +20,7 @@ use crate::upstream::plain_docs_sentence;
|
||||||
|
|
||||||
use crate::{completion_kind, prelude::*, LspCompletion};
|
use crate::{completion_kind, prelude::*, LspCompletion};
|
||||||
|
|
||||||
impl<'a, 'w> CompletionContext<'a, 'w> {
|
impl<'a> CompletionContext<'a> {
|
||||||
pub fn world(&self) -> &LspWorld {
|
pub fn world(&self) -> &LspWorld {
|
||||||
self.ctx.world()
|
self.ctx.world()
|
||||||
}
|
}
|
||||||
|
@ -456,7 +456,7 @@ impl<'a, 'w> CompletionContext<'a, 'w> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn describe_value(ctx: &mut AnalysisContext, v: &Value) -> EcoString {
|
fn describe_value(ctx: &mut LocalContext, v: &Value) -> EcoString {
|
||||||
match v {
|
match v {
|
||||||
Value::Func(f) => {
|
Value::Func(f) => {
|
||||||
let mut f = f;
|
let mut f = f;
|
||||||
|
@ -555,7 +555,7 @@ pub fn value_to_completion_kind(value: &Value) -> CompletionKind {
|
||||||
|
|
||||||
/// Add completions for the parameters of a function.
|
/// Add completions for the parameters of a function.
|
||||||
pub fn param_completions<'a>(
|
pub fn param_completions<'a>(
|
||||||
ctx: &mut CompletionContext<'a, '_>,
|
ctx: &mut CompletionContext<'a>,
|
||||||
callee: ast::Expr<'a>,
|
callee: ast::Expr<'a>,
|
||||||
set: bool,
|
set: bool,
|
||||||
args: ast::Args<'a>,
|
args: ast::Args<'a>,
|
||||||
|
@ -695,7 +695,7 @@ pub fn param_completions<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_completion(
|
fn type_completion(
|
||||||
ctx: &mut CompletionContext<'_, '_>,
|
ctx: &mut CompletionContext<'_>,
|
||||||
infer_type: &Ty,
|
infer_type: &Ty,
|
||||||
docs: Option<&str>,
|
docs: Option<&str>,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
|
@ -963,7 +963,7 @@ fn type_completion(
|
||||||
|
|
||||||
/// Add completions for the values of a named function parameter.
|
/// Add completions for the values of a named function parameter.
|
||||||
pub fn named_param_value_completions<'a>(
|
pub fn named_param_value_completions<'a>(
|
||||||
ctx: &mut CompletionContext<'a, '_>,
|
ctx: &mut CompletionContext<'a>,
|
||||||
callee: ast::Expr<'a>,
|
callee: ast::Expr<'a>,
|
||||||
name: &Interned<str>,
|
name: &Interned<str>,
|
||||||
ty: Option<&Ty>,
|
ty: Option<&Ty>,
|
||||||
|
@ -1100,7 +1100,7 @@ pub(crate) fn complete_type(ctx: &mut CompletionContext) -> Option<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn complete_path(
|
pub fn complete_path(
|
||||||
ctx: &AnalysisContext,
|
ctx: &LocalContext,
|
||||||
v: Option<LinkedNode>,
|
v: Option<LinkedNode>,
|
||||||
source: &Source,
|
source: &Source,
|
||||||
cursor: usize,
|
cursor: usize,
|
||||||
|
|
|
@ -17,7 +17,7 @@ impl StatefulRequest for WillRenameFilesRequest {
|
||||||
|
|
||||||
fn request(
|
fn request(
|
||||||
self,
|
self,
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
_doc: Option<VersionedDocument>,
|
_doc: Option<VersionedDocument>,
|
||||||
) -> Option<Self::Response> {
|
) -> Option<Self::Response> {
|
||||||
let mut edits: HashMap<Url, Vec<TextEdit>> = HashMap::new();
|
let mut edits: HashMap<Url, Vec<TextEdit>> = HashMap::new();
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub struct WorkspaceLabelRequest {}
|
||||||
impl SemanticRequest for WorkspaceLabelRequest {
|
impl SemanticRequest for WorkspaceLabelRequest {
|
||||||
type Response = Vec<SymbolInformation>;
|
type Response = Vec<SymbolInformation>;
|
||||||
|
|
||||||
fn request(self, ctx: &mut AnalysisContext) -> Option<Self::Response> {
|
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response> {
|
||||||
// todo: let typst.ts expose source
|
// todo: let typst.ts expose source
|
||||||
|
|
||||||
let mut symbols = vec![];
|
let mut symbols = vec![];
|
||||||
|
|
|
@ -9,7 +9,7 @@ use core::fmt;
|
||||||
|
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use reflexo_vec2svg::{ExportFeature, SvgExporter, SvgText};
|
use reflexo_vec2svg::{ExportFeature, SvgExporter, SvgText};
|
||||||
use tinymist_query::{AnalysisContext, FramePosition, VersionedDocument};
|
use tinymist_query::{FramePosition, LocalContext, VersionedDocument};
|
||||||
|
|
||||||
struct PeriscopeExportFeature {}
|
struct PeriscopeExportFeature {}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ impl PeriscopeRenderer {
|
||||||
/// Render the periscope image for the given document into markdown format.
|
/// Render the periscope image for the given document into markdown format.
|
||||||
pub fn render_marked(
|
pub fn render_marked(
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut LocalContext,
|
||||||
doc: VersionedDocument,
|
doc: VersionedDocument,
|
||||||
pos: FramePosition,
|
pos: FramePosition,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
|
@ -93,7 +93,7 @@ impl PeriscopeRenderer {
|
||||||
/// Render the periscope image for the given document.
|
/// Render the periscope image for the given document.
|
||||||
pub fn render(
|
pub fn render(
|
||||||
&self,
|
&self,
|
||||||
_ctx: &mut AnalysisContext,
|
_ctx: &mut LocalContext,
|
||||||
doc: VersionedDocument,
|
doc: VersionedDocument,
|
||||||
pos: FramePosition,
|
pos: FramePosition,
|
||||||
) -> Option<(String, f32, f32)> {
|
) -> Option<(String, f32, f32)> {
|
||||||
|
|
|
@ -11,10 +11,11 @@ use std::sync::Arc;
|
||||||
use reflexo::ImmutPath;
|
use reflexo::ImmutPath;
|
||||||
use reflexo_typst::vfs::notify::{FileChangeSet, MemoryEvent};
|
use reflexo_typst::vfs::notify::{FileChangeSet, MemoryEvent};
|
||||||
use reflexo_typst::world::EntryState;
|
use reflexo_typst::world::EntryState;
|
||||||
use tinymist_query::analysis::Analysis;
|
use tinymist_query::analysis::{Analysis, PeriscopeProvider};
|
||||||
use tinymist_query::{ExportKind, SemanticTokenContext};
|
use tinymist_query::{ExportKind, LocalContext, SemanticTokenContext, VersionedDocument};
|
||||||
use tinymist_render::PeriscopeRenderer;
|
use tinymist_render::PeriscopeRenderer;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
use typst::layout::Position;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
task::{ExportConfig, ExportTask, ExportUserConfig},
|
task::{ExportConfig, ExportTask, ExportUserConfig},
|
||||||
|
@ -96,7 +97,6 @@ impl LanguageState {
|
||||||
// Create the compile handler for client consuming results.
|
// Create the compile handler for client consuming results.
|
||||||
let const_config = self.const_config();
|
let const_config = self.const_config();
|
||||||
let position_encoding = const_config.position_encoding;
|
let position_encoding = const_config.position_encoding;
|
||||||
let enable_periscope = self.compile_config().periscope_args.is_some();
|
|
||||||
let periscope_args = self.compile_config().periscope_args.clone();
|
let periscope_args = self.compile_config().periscope_args.clone();
|
||||||
let handle = Arc::new(CompileHandler {
|
let handle = Arc::new(CompileHandler {
|
||||||
#[cfg(feature = "preview")]
|
#[cfg(feature = "preview")]
|
||||||
|
@ -108,18 +108,20 @@ impl LanguageState {
|
||||||
stats: Default::default(),
|
stats: Default::default(),
|
||||||
analysis: Arc::new(Analysis {
|
analysis: Arc::new(Analysis {
|
||||||
position_encoding,
|
position_encoding,
|
||||||
enable_periscope,
|
periscope: periscope_args.map(|args| {
|
||||||
caches: Default::default(),
|
let r = TypstPeriscopeProvider(PeriscopeRenderer::new(args));
|
||||||
workers: Default::default(),
|
Arc::new(r) as Arc<dyn PeriscopeProvider + Send + Sync>
|
||||||
cache_grid: Default::default(),
|
}),
|
||||||
tokens_ctx: Arc::new(SemanticTokenContext::new(
|
tokens_ctx: Arc::new(SemanticTokenContext::new(
|
||||||
const_config.position_encoding,
|
const_config.position_encoding,
|
||||||
const_config.tokens_overlapping_token_support,
|
const_config.tokens_overlapping_token_support,
|
||||||
const_config.tokens_multiline_token_support,
|
const_config.tokens_multiline_token_support,
|
||||||
)),
|
)),
|
||||||
analysis_stats: Default::default(),
|
workers: Default::default(),
|
||||||
|
caches: Default::default(),
|
||||||
|
cache_grid: Default::default(),
|
||||||
|
stats: Default::default(),
|
||||||
}),
|
}),
|
||||||
periscope: PeriscopeRenderer::new(periscope_args.unwrap_or_default()),
|
|
||||||
|
|
||||||
notified_revision: parking_lot::Mutex::new(0),
|
notified_revision: parking_lot::Mutex::new(0),
|
||||||
});
|
});
|
||||||
|
@ -161,3 +163,17 @@ impl LanguageState {
|
||||||
client
|
client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TypstPeriscopeProvider(PeriscopeRenderer);
|
||||||
|
|
||||||
|
impl PeriscopeProvider for TypstPeriscopeProvider {
|
||||||
|
/// Resolve periscope image at the given position.
|
||||||
|
fn periscope_at(
|
||||||
|
&self,
|
||||||
|
ctx: &mut LocalContext,
|
||||||
|
doc: VersionedDocument,
|
||||||
|
pos: Position,
|
||||||
|
) -> Option<String> {
|
||||||
|
self.0.render_marked(ctx, doc, pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -32,13 +32,12 @@ use reflexo_typst::{
|
||||||
};
|
};
|
||||||
use sync_lsp::{just_future, QueryFuture};
|
use sync_lsp::{just_future, QueryFuture};
|
||||||
use tinymist_query::{
|
use tinymist_query::{
|
||||||
analysis::{Analysis, AnalysisContext, AnalysisResources},
|
analysis::{Analysis, LocalContextGuard},
|
||||||
CompilerQueryRequest, CompilerQueryResponse, DiagnosticsMap, ExportKind, SemanticRequest,
|
CompilerQueryRequest, CompilerQueryResponse, DiagnosticsMap, ExportKind, SemanticRequest,
|
||||||
ServerInfoResponse, StatefulRequest, VersionedDocument,
|
ServerInfoResponse, StatefulRequest, VersionedDocument,
|
||||||
};
|
};
|
||||||
use tinymist_render::PeriscopeRenderer;
|
|
||||||
use tokio::sync::{mpsc, oneshot};
|
use tokio::sync::{mpsc, oneshot};
|
||||||
use typst::{diag::SourceDiagnostic, layout::Position, World as TypstWorld};
|
use typst::{diag::SourceDiagnostic, World as TypstWorld};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
editor::{DocVersion, EditorRequest, TinymistCompileStatusEnum},
|
editor::{DocVersion, EditorRequest, TinymistCompileStatusEnum},
|
||||||
|
@ -59,7 +58,6 @@ pub struct CompileHandler {
|
||||||
pub(crate) diag_group: String,
|
pub(crate) diag_group: String,
|
||||||
pub(crate) analysis: Arc<Analysis>,
|
pub(crate) analysis: Arc<Analysis>,
|
||||||
pub(crate) stats: CompilerQueryStats,
|
pub(crate) stats: CompilerQueryStats,
|
||||||
pub(crate) periscope: PeriscopeRenderer,
|
|
||||||
|
|
||||||
#[cfg(feature = "preview")]
|
#[cfg(feature = "preview")]
|
||||||
pub(crate) inner: Arc<parking_lot::RwLock<Option<Arc<typst_preview::CompileWatcher>>>>,
|
pub(crate) inner: Arc<parking_lot::RwLock<Option<Arc<typst_preview::CompileWatcher>>>>,
|
||||||
|
@ -180,7 +178,7 @@ impl CompileHandler {
|
||||||
pub fn run_analysis<T>(
|
pub fn run_analysis<T>(
|
||||||
&self,
|
&self,
|
||||||
w: &LspWorld,
|
w: &LspWorld,
|
||||||
f: impl FnOnce(&mut AnalysisContext<'_>) -> T,
|
f: impl FnOnce(&mut LocalContextGuard) -> T,
|
||||||
) -> anyhow::Result<T> {
|
) -> anyhow::Result<T> {
|
||||||
let Some(main) = w.main_id() else {
|
let Some(main) = w.main_id() else {
|
||||||
error!("TypstActor: main file is not set");
|
error!("TypstActor: main file is not set");
|
||||||
|
@ -191,23 +189,7 @@ impl CompileHandler {
|
||||||
anyhow!("failed to get source: {err}")
|
anyhow!("failed to get source: {err}")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
struct Resource<'a>(&'a PeriscopeRenderer);
|
let mut analysis = self.analysis.snapshot(w.clone());
|
||||||
|
|
||||||
impl<'a> AnalysisResources for Resource<'a> {
|
|
||||||
/// Resolve periscope image at the given position.
|
|
||||||
fn periscope_at(
|
|
||||||
&self,
|
|
||||||
ctx: &mut AnalysisContext,
|
|
||||||
doc: VersionedDocument,
|
|
||||||
pos: Position,
|
|
||||||
) -> Option<String> {
|
|
||||||
self.0.render_marked(ctx, doc, pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let r = Resource(&self.periscope);
|
|
||||||
|
|
||||||
let mut analysis = self.analysis.snapshot(w.clone(), &r);
|
|
||||||
Ok(f(&mut analysis))
|
Ok(f(&mut analysis))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ use serde_json::Value as JsonValue;
|
||||||
use task::TraceParams;
|
use task::TraceParams;
|
||||||
use tinymist_assets::TYPST_PREVIEW_HTML;
|
use tinymist_assets::TYPST_PREVIEW_HTML;
|
||||||
use tinymist_query::docs::PackageInfo;
|
use tinymist_query::docs::PackageInfo;
|
||||||
use tinymist_query::{AnalysisContext, ExportKind, PageSelection};
|
use tinymist_query::{LocalContextGuard, ExportKind, PageSelection};
|
||||||
use typst::diag::{eco_format, EcoString, StrResult};
|
use typst::diag::{eco_format, EcoString, StrResult};
|
||||||
use typst::syntax::package::{PackageSpec, VersionlessPackageSpec};
|
use typst::syntax::package::{PackageSpec, VersionlessPackageSpec};
|
||||||
|
|
||||||
|
@ -617,7 +617,7 @@ impl LanguageState {
|
||||||
pub fn within_package<T>(
|
pub fn within_package<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
info: PackageInfo,
|
info: PackageInfo,
|
||||||
f: impl FnOnce(&mut AnalysisContext) -> LspResult<T> + Send + Sync,
|
f: impl FnOnce(&mut LocalContextGuard) -> LspResult<T> + Send + Sync,
|
||||||
) -> LspResult<impl Future<Output = LspResult<T>>> {
|
) -> LspResult<impl Future<Output = LspResult<T>>> {
|
||||||
let handle: std::sync::Arc<actor::typ_client::CompileHandler> =
|
let handle: std::sync::Arc<actor::typ_client::CompileHandler> =
|
||||||
self.primary().handle.clone();
|
self.primary().handle.clone();
|
||||||
|
|
|
@ -540,7 +540,6 @@ pub async fn preview_main(args: PreviewCliArgs) -> anyhow::Result<()> {
|
||||||
editor_tx,
|
editor_tx,
|
||||||
analysis: Arc::default(),
|
analysis: Arc::default(),
|
||||||
stats: Default::default(),
|
stats: Default::default(),
|
||||||
periscope: tinymist_render::PeriscopeRenderer::default(),
|
|
||||||
notified_revision: parking_lot::Mutex::new(0),
|
notified_revision: parking_lot::Mutex::new(0),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue