mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-03 01:42:14 +00:00
refactor: pick state changes (#338)
* dev: make clippy happy * refactor: pick state changes --------- Co-authored-by: QuarticCat <QuarticCat@pm.me>
This commit is contained in:
parent
7d65829ed7
commit
5e4e1e9877
25 changed files with 754 additions and 707 deletions
41
Cargo.lock
generated
41
Cargo.lock
generated
|
@ -174,6 +174,26 @@ version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-lsp"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "855d6246a5d31e6e469eeef718d9a098f2d99207985a00dfdd3f4b5c5003c09a"
|
||||||
|
dependencies = [
|
||||||
|
"futures",
|
||||||
|
"lsp-types",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rustix",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
"waitpid-any",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.80"
|
version = "0.1.80"
|
||||||
|
@ -3912,6 +3932,7 @@ name = "tinymist"
|
||||||
version = "0.11.11"
|
version = "0.11.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"async-lsp",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"await-tree",
|
"await-tree",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
|
@ -3937,6 +3958,7 @@ dependencies = [
|
||||||
"open",
|
"open",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"paste",
|
"paste",
|
||||||
|
"pin-project-lite",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tinymist-assets 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tinymist-assets 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -3945,6 +3967,8 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"toml 0.8.13",
|
"toml 0.8.13",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
"typst",
|
"typst",
|
||||||
"typst-assets",
|
"typst-assets",
|
||||||
"typst-pdf",
|
"typst-pdf",
|
||||||
|
@ -4123,6 +4147,7 @@ checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -4187,6 +4212,12 @@ dependencies = [
|
||||||
"winnow 0.6.8",
|
"winnow 0.6.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-layer"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-service"
|
name = "tower-service"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
@ -4854,6 +4885,16 @@ version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "waitpid-any"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0189157c93c54d86e5c61ddf0c1223baa25e5bfb2f6f9983c678985b028d7c12"
|
||||||
|
dependencies = [
|
||||||
|
"rustix",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "walkdir"
|
name = "walkdir"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
|
|
@ -58,6 +58,7 @@ typst-ts-svg-exporter = { version = "0.5.0-rc4" }
|
||||||
typstyle = "0.11.26"
|
typstyle = "0.11.26"
|
||||||
typstfmt_lib = { git = "https://github.com/astrale-sharp/typstfmt", tag = "0.2.7" }
|
typstfmt_lib = { git = "https://github.com/astrale-sharp/typstfmt", tag = "0.2.7" }
|
||||||
|
|
||||||
|
async-lsp = { version = "0.2.0", features = ["tokio"] }
|
||||||
lsp-server = "0.7.6"
|
lsp-server = "0.7.6"
|
||||||
lsp-types = { version = "=0.95.0", features = ["proposed"] }
|
lsp-types = { version = "=0.95.0", features = ["proposed"] }
|
||||||
crossbeam-channel = "0.5.12"
|
crossbeam-channel = "0.5.12"
|
||||||
|
@ -79,7 +80,7 @@ tokio = { version = "1.36.0", features = [
|
||||||
"rt-multi-thread",
|
"rt-multi-thread",
|
||||||
"io-std",
|
"io-std",
|
||||||
] }
|
] }
|
||||||
tokio-util = "0.7.10"
|
tokio-util = { version = "0.7.10", features = ["compat"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
serde_yaml = "0.9"
|
serde_yaml = "0.9"
|
||||||
|
|
|
@ -13,68 +13,67 @@ pub mod syntax;
|
||||||
pub mod ty;
|
pub mod ty;
|
||||||
mod upstream;
|
mod upstream;
|
||||||
|
|
||||||
pub(crate) mod diagnostics;
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub use analysis::AnalysisContext;
|
pub use analysis::AnalysisContext;
|
||||||
use typst::{model::Document as TypstDocument, syntax::Source};
|
use typst::{model::Document as TypstDocument, syntax::Source};
|
||||||
|
|
||||||
|
mod diagnostics;
|
||||||
pub use diagnostics::*;
|
pub use diagnostics::*;
|
||||||
pub(crate) mod code_action;
|
mod code_action;
|
||||||
pub use code_action::*;
|
pub use code_action::*;
|
||||||
pub(crate) mod code_context;
|
mod code_context;
|
||||||
pub use code_context::*;
|
pub use code_context::*;
|
||||||
pub(crate) mod code_lens;
|
mod code_lens;
|
||||||
pub use code_lens::*;
|
pub use code_lens::*;
|
||||||
pub(crate) mod completion;
|
mod completion;
|
||||||
pub use completion::*;
|
pub use completion::*;
|
||||||
pub(crate) mod color_presentation;
|
mod color_presentation;
|
||||||
pub use color_presentation::*;
|
pub use color_presentation::*;
|
||||||
pub(crate) mod document_color;
|
mod document_color;
|
||||||
pub use document_color::*;
|
pub use document_color::*;
|
||||||
pub(crate) mod document_highlight;
|
mod document_highlight;
|
||||||
pub use document_highlight::*;
|
pub use document_highlight::*;
|
||||||
pub(crate) mod document_symbol;
|
mod document_symbol;
|
||||||
pub use document_symbol::*;
|
pub use document_symbol::*;
|
||||||
pub(crate) mod document_metrics;
|
mod document_metrics;
|
||||||
pub use document_metrics::*;
|
pub use document_metrics::*;
|
||||||
pub(crate) mod folding_range;
|
mod folding_range;
|
||||||
pub use folding_range::*;
|
pub use folding_range::*;
|
||||||
pub(crate) mod goto_declaration;
|
mod goto_declaration;
|
||||||
pub use goto_declaration::*;
|
pub use goto_declaration::*;
|
||||||
pub(crate) mod goto_definition;
|
mod goto_definition;
|
||||||
pub use goto_definition::*;
|
pub use goto_definition::*;
|
||||||
pub(crate) mod hover;
|
mod hover;
|
||||||
pub use hover::*;
|
pub use hover::*;
|
||||||
pub(crate) mod inlay_hint;
|
mod inlay_hint;
|
||||||
pub use inlay_hint::*;
|
pub use inlay_hint::*;
|
||||||
pub(crate) mod jump;
|
mod jump;
|
||||||
pub use jump::*;
|
pub use jump::*;
|
||||||
pub(crate) mod rename;
|
mod rename;
|
||||||
pub use rename::*;
|
pub use rename::*;
|
||||||
pub(crate) mod selection_range;
|
mod selection_range;
|
||||||
pub use selection_range::*;
|
pub use selection_range::*;
|
||||||
pub(crate) mod semantic_tokens;
|
mod semantic_tokens;
|
||||||
pub use semantic_tokens::*;
|
pub use semantic_tokens::*;
|
||||||
pub(crate) mod semantic_tokens_full;
|
mod semantic_tokens_full;
|
||||||
pub use semantic_tokens_full::*;
|
pub use semantic_tokens_full::*;
|
||||||
pub(crate) mod semantic_tokens_delta;
|
mod semantic_tokens_delta;
|
||||||
pub use semantic_tokens_delta::*;
|
pub use semantic_tokens_delta::*;
|
||||||
pub(crate) mod signature_help;
|
mod signature_help;
|
||||||
pub use signature_help::*;
|
pub use signature_help::*;
|
||||||
pub(crate) mod symbol;
|
mod symbol;
|
||||||
pub use symbol::*;
|
pub use symbol::*;
|
||||||
pub(crate) mod on_enter;
|
mod on_enter;
|
||||||
pub use on_enter::*;
|
pub use on_enter::*;
|
||||||
pub(crate) mod prepare_rename;
|
mod prepare_rename;
|
||||||
pub use prepare_rename::*;
|
pub use prepare_rename::*;
|
||||||
pub(crate) mod references;
|
mod references;
|
||||||
pub use references::*;
|
pub use references::*;
|
||||||
|
|
||||||
pub mod lsp_typst_boundary;
|
mod lsp_typst_boundary;
|
||||||
pub use lsp_typst_boundary::*;
|
pub use lsp_typst_boundary::*;
|
||||||
pub(crate) mod lsp_features;
|
mod lsp_features;
|
||||||
pub use lsp_features::*;
|
pub use lsp_features::*;
|
||||||
|
|
||||||
mod prelude;
|
mod prelude;
|
||||||
|
@ -136,9 +135,10 @@ mod polymorphic {
|
||||||
use super::prelude::*;
|
use super::prelude::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub enum PageSelection {
|
pub enum PageSelection {
|
||||||
|
#[default]
|
||||||
First,
|
First,
|
||||||
Merged,
|
Merged,
|
||||||
}
|
}
|
||||||
|
@ -234,68 +234,69 @@ mod polymorphic {
|
||||||
pub fn fold_feature(&self) -> FoldRequestFeature {
|
pub fn fold_feature(&self) -> FoldRequestFeature {
|
||||||
use FoldRequestFeature::*;
|
use FoldRequestFeature::*;
|
||||||
match self {
|
match self {
|
||||||
CompilerQueryRequest::OnExport(..) => Mergeable,
|
Self::OnExport(..) => Mergeable,
|
||||||
CompilerQueryRequest::OnSaveExport(..) => Mergeable,
|
Self::OnSaveExport(..) => Mergeable,
|
||||||
CompilerQueryRequest::Hover(..) => PinnedFirst,
|
Self::Hover(..) => PinnedFirst,
|
||||||
CompilerQueryRequest::GotoDefinition(..) => PinnedFirst,
|
Self::GotoDefinition(..) => PinnedFirst,
|
||||||
CompilerQueryRequest::GotoDeclaration(..) => PinnedFirst,
|
Self::GotoDeclaration(..) => PinnedFirst,
|
||||||
CompilerQueryRequest::References(..) => PinnedFirst,
|
Self::References(..) => PinnedFirst,
|
||||||
CompilerQueryRequest::InlayHint(..) => Unique,
|
Self::InlayHint(..) => Unique,
|
||||||
CompilerQueryRequest::DocumentColor(..) => PinnedFirst,
|
Self::DocumentColor(..) => PinnedFirst,
|
||||||
CompilerQueryRequest::DocumentHighlight(..) => PinnedFirst,
|
Self::DocumentHighlight(..) => PinnedFirst,
|
||||||
CompilerQueryRequest::ColorPresentation(..) => ContextFreeUnique,
|
Self::ColorPresentation(..) => ContextFreeUnique,
|
||||||
CompilerQueryRequest::CodeAction(..) => Unique,
|
Self::CodeAction(..) => Unique,
|
||||||
CompilerQueryRequest::CodeLens(..) => Unique,
|
Self::CodeLens(..) => Unique,
|
||||||
CompilerQueryRequest::Completion(..) => Mergeable,
|
Self::Completion(..) => Mergeable,
|
||||||
CompilerQueryRequest::SignatureHelp(..) => PinnedFirst,
|
Self::SignatureHelp(..) => PinnedFirst,
|
||||||
CompilerQueryRequest::Rename(..) => Mergeable,
|
Self::Rename(..) => Mergeable,
|
||||||
CompilerQueryRequest::PrepareRename(..) => Mergeable,
|
Self::PrepareRename(..) => Mergeable,
|
||||||
CompilerQueryRequest::DocumentSymbol(..) => ContextFreeUnique,
|
Self::DocumentSymbol(..) => ContextFreeUnique,
|
||||||
CompilerQueryRequest::Symbol(..) => Mergeable,
|
Self::Symbol(..) => Mergeable,
|
||||||
CompilerQueryRequest::SemanticTokensFull(..) => ContextFreeUnique,
|
Self::SemanticTokensFull(..) => ContextFreeUnique,
|
||||||
CompilerQueryRequest::SemanticTokensDelta(..) => ContextFreeUnique,
|
Self::SemanticTokensDelta(..) => ContextFreeUnique,
|
||||||
CompilerQueryRequest::Formatting(..) => ContextFreeUnique,
|
Self::Formatting(..) => ContextFreeUnique,
|
||||||
CompilerQueryRequest::FoldingRange(..) => ContextFreeUnique,
|
Self::FoldingRange(..) => ContextFreeUnique,
|
||||||
CompilerQueryRequest::SelectionRange(..) => ContextFreeUnique,
|
Self::SelectionRange(..) => ContextFreeUnique,
|
||||||
CompilerQueryRequest::InteractCodeContext(..) => PinnedFirst,
|
Self::InteractCodeContext(..) => PinnedFirst,
|
||||||
|
|
||||||
CompilerQueryRequest::OnEnter(..) => ContextFreeUnique,
|
Self::OnEnter(..) => ContextFreeUnique,
|
||||||
|
|
||||||
CompilerQueryRequest::DocumentMetrics(..) => PinnedFirst,
|
Self::DocumentMetrics(..) => PinnedFirst,
|
||||||
CompilerQueryRequest::ServerInfo(..) => Mergeable,
|
Self::ServerInfo(..) => Mergeable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn associated_path(&self) -> Option<&Path> {
|
pub fn associated_path(&self) -> Option<&Path> {
|
||||||
Some(match self {
|
Some(match self {
|
||||||
CompilerQueryRequest::OnExport(..) => return None,
|
Self::OnExport(..) => return None,
|
||||||
CompilerQueryRequest::OnSaveExport(req) => &req.path,
|
Self::OnSaveExport(req) => &req.path,
|
||||||
CompilerQueryRequest::Hover(req) => &req.path,
|
Self::Hover(req) => &req.path,
|
||||||
CompilerQueryRequest::GotoDefinition(req) => &req.path,
|
Self::GotoDefinition(req) => &req.path,
|
||||||
CompilerQueryRequest::GotoDeclaration(req) => &req.path,
|
Self::GotoDeclaration(req) => &req.path,
|
||||||
CompilerQueryRequest::References(req) => &req.path,
|
Self::References(req) => &req.path,
|
||||||
CompilerQueryRequest::InlayHint(req) => &req.path,
|
Self::InlayHint(req) => &req.path,
|
||||||
CompilerQueryRequest::DocumentColor(req) => &req.path,
|
Self::DocumentColor(req) => &req.path,
|
||||||
CompilerQueryRequest::DocumentHighlight(req) => &req.path,
|
Self::DocumentHighlight(req) => &req.path,
|
||||||
CompilerQueryRequest::ColorPresentation(req) => &req.path,
|
Self::ColorPresentation(req) => &req.path,
|
||||||
CompilerQueryRequest::CodeAction(req) => &req.path,
|
Self::CodeAction(req) => &req.path,
|
||||||
CompilerQueryRequest::CodeLens(req) => &req.path,
|
Self::CodeLens(req) => &req.path,
|
||||||
CompilerQueryRequest::Completion(req) => &req.path,
|
Self::Completion(req) => &req.path,
|
||||||
CompilerQueryRequest::SignatureHelp(req) => &req.path,
|
Self::SignatureHelp(req) => &req.path,
|
||||||
CompilerQueryRequest::Rename(req) => &req.path,
|
Self::Rename(req) => &req.path,
|
||||||
CompilerQueryRequest::PrepareRename(req) => &req.path,
|
Self::PrepareRename(req) => &req.path,
|
||||||
CompilerQueryRequest::DocumentSymbol(req) => &req.path,
|
Self::DocumentSymbol(req) => &req.path,
|
||||||
CompilerQueryRequest::Symbol(..) => return None,
|
Self::Symbol(..) => return None,
|
||||||
CompilerQueryRequest::SemanticTokensFull(req) => &req.path,
|
Self::SemanticTokensFull(req) => &req.path,
|
||||||
CompilerQueryRequest::SemanticTokensDelta(req) => &req.path,
|
Self::SemanticTokensDelta(req) => &req.path,
|
||||||
CompilerQueryRequest::Formatting(req) => &req.path,
|
Self::Formatting(req) => &req.path,
|
||||||
CompilerQueryRequest::FoldingRange(req) => &req.path,
|
Self::FoldingRange(req) => &req.path,
|
||||||
CompilerQueryRequest::SelectionRange(req) => &req.path,
|
Self::SelectionRange(req) => &req.path,
|
||||||
CompilerQueryRequest::InteractCodeContext(req) => &req.path,
|
Self::InteractCodeContext(req) => &req.path,
|
||||||
CompilerQueryRequest::OnEnter(req) => &req.path,
|
|
||||||
|
|
||||||
CompilerQueryRequest::DocumentMetrics(req) => &req.path,
|
Self::OnEnter(req) => &req.path,
|
||||||
CompilerQueryRequest::ServerInfo(..) => return None,
|
|
||||||
|
Self::DocumentMetrics(req) => &req.path,
|
||||||
|
Self::ServerInfo(..) => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,7 @@ toml.workspace = true
|
||||||
walkdir.workspace = true
|
walkdir.workspace = true
|
||||||
typst-preview = { workspace = true, optional = true }
|
typst-preview = { workspace = true, optional = true }
|
||||||
lsp-server.workspace = true
|
lsp-server.workspace = true
|
||||||
|
async-lsp.workspace = true
|
||||||
crossbeam-channel.workspace = true
|
crossbeam-channel.workspace = true
|
||||||
lsp-types.workspace = true
|
lsp-types.workspace = true
|
||||||
dhat = { version = "0.3.3", optional = true }
|
dhat = { version = "0.3.3", optional = true }
|
||||||
|
@ -66,6 +67,9 @@ unicode-script = "0.5"
|
||||||
await-tree = "0.1.2"
|
await-tree = "0.1.2"
|
||||||
hyper = { version = "0.14", features = ["full"], optional = true }
|
hyper = { version = "0.14", features = ["full"], optional = true }
|
||||||
open = { version = "5.1.3", optional = true }
|
open = { version = "5.1.3", optional = true }
|
||||||
|
tower-layer = "0.3.2"
|
||||||
|
tower-service = "0.3.2"
|
||||||
|
pin-project-lite = "0.2.13"
|
||||||
base64.workspace = true
|
base64.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
|
@ -7,7 +7,7 @@ use lsp_types::{Diagnostic, Url};
|
||||||
use tinymist_query::{DiagnosticsMap, LspDiagnostic};
|
use tinymist_query::{DiagnosticsMap, LspDiagnostic};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use crate::{tools::word_count::WordsCount, LspHost, TypstLanguageServer};
|
use crate::{tools::word_count::WordsCount, LspHost, LanguageState};
|
||||||
|
|
||||||
pub enum EditorRequest {
|
pub enum EditorRequest {
|
||||||
Diag(String, Option<DiagnosticsMap>),
|
Diag(String, Option<DiagnosticsMap>),
|
||||||
|
@ -16,7 +16,7 @@ pub enum EditorRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EditorActor {
|
pub struct EditorActor {
|
||||||
host: LspHost<TypstLanguageServer>,
|
host: LspHost<LanguageState>,
|
||||||
editor_rx: mpsc::UnboundedReceiver<EditorRequest>,
|
editor_rx: mpsc::UnboundedReceiver<EditorRequest>,
|
||||||
|
|
||||||
diagnostics: HashMap<Url, HashMap<String, Vec<LspDiagnostic>>>,
|
diagnostics: HashMap<Url, HashMap<String, Vec<LspDiagnostic>>>,
|
||||||
|
@ -27,7 +27,7 @@ pub struct EditorActor {
|
||||||
|
|
||||||
impl EditorActor {
|
impl EditorActor {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
host: LspHost<TypstLanguageServer>,
|
host: LspHost<LanguageState>,
|
||||||
editor_rx: mpsc::UnboundedReceiver<EditorRequest>,
|
editor_rx: mpsc::UnboundedReceiver<EditorRequest>,
|
||||||
notify_compile_status: bool,
|
notify_compile_status: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use lsp_types::TextEdit;
|
||||||
use tinymist_query::{typst_to_lsp, PositionEncoding};
|
use tinymist_query::{typst_to_lsp, PositionEncoding};
|
||||||
use typst::syntax::Source;
|
use typst::syntax::Source;
|
||||||
|
|
||||||
use crate::{result_to_response, FormatterMode, LspHost, LspResult, TypstLanguageServer};
|
use crate::{result_to_response, FormatterMode, LspHost, LspResult, LanguageState};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FormatConfig {
|
pub struct FormatConfig {
|
||||||
|
@ -23,7 +23,7 @@ pub enum FormatRequest {
|
||||||
pub fn run_format_thread(
|
pub fn run_format_thread(
|
||||||
config: FormatConfig,
|
config: FormatConfig,
|
||||||
format_rx: crossbeam_channel::Receiver<FormatRequest>,
|
format_rx: crossbeam_channel::Receiver<FormatRequest>,
|
||||||
client: LspHost<TypstLanguageServer>,
|
client: LspHost<LanguageState>,
|
||||||
position_encoding: PositionEncoding,
|
position_encoding: PositionEncoding,
|
||||||
) {
|
) {
|
||||||
type FmtFn = Box<dyn Fn(Source) -> LspResult<Option<Vec<TextEdit>>>>;
|
type FmtFn = Box<dyn Fn(Source) -> LspResult<Option<Vec<TextEdit>>>>;
|
||||||
|
|
|
@ -24,12 +24,12 @@ use self::{
|
||||||
user_action::run_user_action_thread,
|
user_action::run_user_action_thread,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
compiler::CompileServer,
|
compile::CompileState,
|
||||||
world::{ImmutDict, LspWorldBuilder},
|
world::{ImmutDict, LspWorldBuilder},
|
||||||
TypstLanguageServer,
|
LanguageState,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl CompileServer {
|
impl CompileState {
|
||||||
pub fn restart_server(&mut self, group: &str) {
|
pub fn restart_server(&mut self, group: &str) {
|
||||||
let server = self.server(
|
let server = self.server(
|
||||||
group.to_owned(),
|
group.to_owned(),
|
||||||
|
@ -132,7 +132,7 @@ impl CompileServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypstLanguageServer {
|
impl LanguageState {
|
||||||
pub fn server(
|
pub fn server(
|
||||||
&self,
|
&self,
|
||||||
diag_group: String,
|
diag_group: String,
|
||||||
|
|
|
@ -65,7 +65,7 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
actor::export::ExportRequest,
|
actor::export::ExportRequest,
|
||||||
compiler_init::CompileConfig,
|
compile_init::CompileConfig,
|
||||||
tools::preview::{CompilationHandle, CompileStatus},
|
tools::preview::{CompilationHandle, CompileStatus},
|
||||||
utils,
|
utils,
|
||||||
world::{LspCompilerFeat, LspWorld},
|
world::{LspCompilerFeat, LspWorld},
|
||||||
|
|
|
@ -75,7 +75,7 @@ pub struct CompileServerActor<C: Compiler, F: CompilerFeat> {
|
||||||
estimated_shadow_files: HashSet<Arc<Path>>,
|
estimated_shadow_files: HashSet<Arc<Path>>,
|
||||||
/// The latest compiled document.
|
/// The latest compiled document.
|
||||||
pub(crate) latest_doc: Option<Arc<TypstDocument>>,
|
pub(crate) latest_doc: Option<Arc<TypstDocument>>,
|
||||||
/// The latest successly compiled document.
|
/// The latest successfully compiled document.
|
||||||
latest_success_doc: Option<Arc<TypstDocument>>,
|
latest_success_doc: Option<Arc<TypstDocument>>,
|
||||||
/// feature set for compile_once mode.
|
/// feature set for compile_once mode.
|
||||||
once_feature_set: Arc<FeatureSet>,
|
once_feature_set: Arc<FeatureSet>,
|
||||||
|
|
|
@ -8,7 +8,7 @@ use lsp_server::RequestId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use typst_ts_core::TypstDict;
|
use typst_ts_core::TypstDict;
|
||||||
|
|
||||||
use crate::{internal_error, result_to_response, LspHost, TypstLanguageServer};
|
use crate::{internal_error, result_to_response, LspHost, LanguageState};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
@ -26,7 +26,7 @@ pub enum UserActionRequest {
|
||||||
|
|
||||||
pub fn run_user_action_thread(
|
pub fn run_user_action_thread(
|
||||||
user_action_rx: crossbeam_channel::Receiver<UserActionRequest>,
|
user_action_rx: crossbeam_channel::Receiver<UserActionRequest>,
|
||||||
client: LspHost<TypstLanguageServer>,
|
client: LspHost<LanguageState>,
|
||||||
) {
|
) {
|
||||||
while let Ok(req) = user_action_rx.recv() {
|
while let Ok(req) = user_action_rx.recv() {
|
||||||
match req {
|
match req {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use tinymist::preview::PreviewCliArgs;
|
||||||
use tinymist::transport::MirrorArgs;
|
use tinymist::transport::MirrorArgs;
|
||||||
|
|
||||||
use tinymist::compiler_init::{CompileOnceArgs, FontArgs};
|
use tinymist::compile_init::{CompileOnceArgs, FontArgs};
|
||||||
use tinymist::preview::PreviewCliArgs;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(feature = "clap", derive(clap::Parser))]
|
#[cfg_attr(feature = "clap", derive(clap::Parser))]
|
||||||
|
|
|
@ -37,8 +37,8 @@ pub mod transport;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod world;
|
mod world;
|
||||||
pub use crate::harness::LspHost;
|
pub use crate::harness::LspHost;
|
||||||
pub use server::compiler;
|
pub use server::compile;
|
||||||
pub use server::compiler_init;
|
pub use server::compile_init;
|
||||||
pub use server::lsp::*;
|
pub use server::lsp::*;
|
||||||
pub use server::lsp_init::*;
|
pub use server::lsp_init::*;
|
||||||
pub use server::preview;
|
pub use server::preview;
|
||||||
|
@ -46,6 +46,7 @@ pub use world::{
|
||||||
CompileFontOpts, CompileOnceOpts, CompileOpts, LspUniverse, LspWorld, LspWorldBuilder,
|
CompileFontOpts, CompileOnceOpts, CompileOpts, LspUniverse, LspWorld, LspWorldBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// use async_lsp::ClientSocket;
|
||||||
use lsp_server::ResponseError;
|
use lsp_server::ResponseError;
|
||||||
|
|
||||||
type LspResult<Res> = Result<Res, ResponseError>;
|
type LspResult<Res> = Result<Res, ResponseError>;
|
||||||
|
|
|
@ -11,11 +11,11 @@ use lsp_types::{InitializeParams, InitializedParams};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use tinymist::{
|
use tinymist::{
|
||||||
compiler_init::{CompileInit, CompileInitializeParams},
|
compile_init::{CompileInit, CompileInitializeParams},
|
||||||
harness::{lsp_harness, InitializedLspDriver, LspDriver, LspHost},
|
harness::{lsp_harness, InitializedLspDriver, LspDriver, LspHost},
|
||||||
preview::preview_main,
|
preview::preview_main,
|
||||||
transport::with_stdio_transport,
|
transport::with_stdio_transport,
|
||||||
CompileFontOpts, Init, LspWorld, TypstLanguageServer,
|
CompileFontOpts, Init, LspWorld, LanguageState,
|
||||||
};
|
};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use typst::World;
|
use typst::World;
|
||||||
|
@ -90,7 +90,7 @@ pub fn lsp_main(args: LspArgs) -> anyhow::Result<()> {
|
||||||
impl LspDriver for Lsp {
|
impl LspDriver for Lsp {
|
||||||
type InitParams = InitializeParams;
|
type InitParams = InitializeParams;
|
||||||
type InitResult = lsp_types::InitializeResult;
|
type InitResult = lsp_types::InitializeResult;
|
||||||
type InitializedSelf = TypstLanguageServer;
|
type InitializedSelf = LanguageState;
|
||||||
|
|
||||||
fn initialize(
|
fn initialize(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -14,7 +14,7 @@ mod prelude {
|
||||||
pub use typst_ts_svg_exporter::ir::{GlyphItem, GlyphRef};
|
pub use typst_ts_svg_exporter::ir::{GlyphItem, GlyphRef};
|
||||||
pub use typst_ts_svg_exporter::{DefaultExportFeature, SvgTask, SvgText};
|
pub use typst_ts_svg_exporter::{DefaultExportFeature, SvgTask, SvgText};
|
||||||
|
|
||||||
pub use crate::TypstLanguageServer;
|
pub use crate::LanguageState;
|
||||||
|
|
||||||
pub type Svg<'a> = SvgTask<'a, DefaultExportFeature>;
|
pub type Svg<'a> = SvgTask<'a, DefaultExportFeature>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,7 @@ static CAT_MAP: Lazy<HashMap<&str, SymCategory>> = Lazy::new(|| {
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
|
|
||||||
impl TypstLanguageServer {
|
impl LanguageState {
|
||||||
/// Get the all valid symbols
|
/// Get the all valid symbols
|
||||||
pub fn get_symbol_resources(&self) -> ZResult<JsonValue> {
|
pub fn get_symbol_resources(&self) -> ZResult<JsonValue> {
|
||||||
let mut symbols = ResourceSymbolMap::new();
|
let mut symbols = ResourceSymbolMap::new();
|
||||||
|
|
|
@ -4,40 +4,30 @@ use std::{collections::HashMap, path::Path, sync::Arc, time::Instant};
|
||||||
use crossbeam_channel::{select, Receiver};
|
use crossbeam_channel::{select, Receiver};
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use lsp_server::{ErrorCode, Message, Notification, Request, RequestId, Response, ResponseError};
|
use lsp_server::{ErrorCode, Message, Notification, Request, RequestId, Response, ResponseError};
|
||||||
use lsp_types::{notification::Notification as _, ExecuteCommandParams};
|
use lsp_types::notification::Notification as _;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_json::{Map, Value as JsonValue};
|
use serde_json::{Map, Value as JsonValue};
|
||||||
use tinymist_query::{ExportKind, PageSelection};
|
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use typst::{diag::FileResult, syntax::Source};
|
use typst::{diag::FileResult, syntax::Source};
|
||||||
use typst_ts_compiler::vfs::notify::FileChangeSet;
|
use typst_ts_compiler::vfs::notify::FileChangeSet;
|
||||||
use typst_ts_core::{config::compiler::DETACHED_ENTRY, ImmutPath};
|
use typst_ts_core::config::compiler::DETACHED_ENTRY;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
actor::{editor::EditorRequest, export::ExportConfig, typ_client::CompileClientActor},
|
actor::{editor::EditorRequest, export::ExportConfig, typ_client::CompileClientActor},
|
||||||
compiler_init::{CompileConfig, CompilerConstConfig},
|
compile_init::{CompileConfig, ConstCompileConfig},
|
||||||
harness::InitializedLspDriver,
|
harness::InitializedLspDriver,
|
||||||
internal_error, invalid_params, method_not_found, run_query,
|
invalid_params,
|
||||||
state::MemoryFileMeta,
|
state::MemoryFileMeta,
|
||||||
LspHost, LspResult,
|
LspHost, LspResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
type LspMethod<Res> = fn(srv: &mut CompileServer, args: JsonValue) -> LspResult<Res>;
|
type LspMethod<Res> = fn(srv: &mut CompileState, args: JsonValue) -> LspResult<Res>;
|
||||||
type LspHandler<Req, Res> = fn(srv: &mut CompileServer, args: Req) -> LspResult<Res>;
|
pub(crate) type LspHandler<Req, Res> = fn(srv: &mut CompileState, args: Req) -> LspResult<Res>;
|
||||||
|
|
||||||
type ExecuteCmdMap = HashMap<&'static str, LspHandler<Vec<JsonValue>, JsonValue>>;
|
type ExecuteCmdMap = HashMap<&'static str, LspHandler<Vec<JsonValue>, JsonValue>>;
|
||||||
type NotifyCmdMap = HashMap<&'static str, LspMethod<()>>;
|
type NotifyCmdMap = HashMap<&'static str, LspMethod<()>>;
|
||||||
type RegularCmdMap = HashMap<&'static str, LspMethod<JsonValue>>;
|
type RegularCmdMap = HashMap<&'static str, LspMethod<JsonValue>>;
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! exec_fn {
|
|
||||||
($ty: ty, Self::$method: ident, $($arg_key:ident),+ $(,)?) => {{
|
|
||||||
const E: $ty = |this, $($arg_key),+| this.$method($($arg_key),+);
|
|
||||||
E
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! request_fn {
|
macro_rules! request_fn {
|
||||||
($desc: ty, Self::$method: ident) => {
|
($desc: ty, Self::$method: ident) => {
|
||||||
|
@ -67,9 +57,9 @@ macro_rules! notify_fn {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The object providing the language server functionality.
|
/// The object providing the language server functionality.
|
||||||
pub struct CompileServer {
|
pub struct CompileState {
|
||||||
/// The language server client.
|
/// The language server client.
|
||||||
pub client: LspHost<CompileServer>,
|
pub client: LspHost<CompileState>,
|
||||||
|
|
||||||
// State to synchronize with the client.
|
// State to synchronize with the client.
|
||||||
/// Whether the server is shutting down.
|
/// Whether the server is shutting down.
|
||||||
|
@ -80,7 +70,7 @@ pub struct CompileServer {
|
||||||
pub config: CompileConfig,
|
pub config: CompileConfig,
|
||||||
/// Const configuration initialized at the start of the session.
|
/// Const configuration initialized at the start of the session.
|
||||||
/// For example, the position encoding.
|
/// For example, the position encoding.
|
||||||
pub const_config: CompilerConstConfig,
|
pub const_config: ConstCompileConfig,
|
||||||
|
|
||||||
// Command maps
|
// Command maps
|
||||||
/// Extra commands provided with `textDocument/executeCommand`.
|
/// Extra commands provided with `textDocument/executeCommand`.
|
||||||
|
@ -101,15 +91,15 @@ pub struct CompileServer {
|
||||||
pub compiler: Option<CompileClientActor>,
|
pub compiler: Option<CompileClientActor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompileServer {
|
impl CompileState {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
client: LspHost<CompileServer>,
|
client: LspHost<CompileState>,
|
||||||
compile_config: CompileConfig,
|
compile_config: CompileConfig,
|
||||||
const_config: CompilerConstConfig,
|
const_config: ConstCompileConfig,
|
||||||
editor_tx: mpsc::UnboundedSender<EditorRequest>,
|
editor_tx: mpsc::UnboundedSender<EditorRequest>,
|
||||||
handle: tokio::runtime::Handle,
|
handle: tokio::runtime::Handle,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
CompileServer {
|
CompileState {
|
||||||
client,
|
client,
|
||||||
editor_tx,
|
editor_tx,
|
||||||
shutdown_requested: false,
|
shutdown_requested: false,
|
||||||
|
@ -125,7 +115,7 @@ impl CompileServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn const_config(&self) -> &CompilerConstConfig {
|
pub fn const_config(&self) -> &ConstCompileConfig {
|
||||||
&self.const_config
|
&self.const_config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +193,7 @@ impl fmt::Display for Event {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InitializedLspDriver for CompileServer {
|
impl InitializedLspDriver for CompileState {
|
||||||
fn initialized(&mut self, _params: lsp_types::InitializedParams) {}
|
fn initialized(&mut self, _params: lsp_types::InitializedParams) {}
|
||||||
|
|
||||||
fn main_loop(&mut self, inbox: crossbeam_channel::Receiver<Message>) -> anyhow::Result<()> {
|
fn main_loop(&mut self, inbox: crossbeam_channel::Receiver<Message>) -> anyhow::Result<()> {
|
||||||
|
@ -223,7 +213,7 @@ impl InitializedLspDriver for CompileServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompileServer {
|
impl CompileState {
|
||||||
fn next_event(&self, inbox: &Receiver<Message>) -> Option<Event> {
|
fn next_event(&self, inbox: &Receiver<Message>) -> Option<Event> {
|
||||||
select! {
|
select! {
|
||||||
recv(inbox) -> msg =>
|
recv(inbox) -> msg =>
|
||||||
|
@ -314,7 +304,7 @@ impl CompileServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompileServer {
|
impl CompileState {
|
||||||
pub fn on_changed_configuration(&mut self, values: Map<String, JsonValue>) -> LspResult<()> {
|
pub fn on_changed_configuration(&mut self, values: Map<String, JsonValue>) -> LspResult<()> {
|
||||||
let config = self.config.clone();
|
let config = self.config.clone();
|
||||||
match self.config.update_by_map(&values) {
|
match self.config.update_by_map(&values) {
|
||||||
|
@ -357,121 +347,3 @@ impl CompileServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Cancelled;
|
struct Cancelled;
|
||||||
|
|
||||||
impl CompileServer {
|
|
||||||
fn get_exec_commands() -> ExecuteCmdMap {
|
|
||||||
macro_rules! redirected_command {
|
|
||||||
($key: expr, Self::$method: ident) => {
|
|
||||||
(
|
|
||||||
$key,
|
|
||||||
exec_fn!(LspHandler<Vec<JsonValue>, JsonValue>, Self::$method, inputs),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ExecuteCmdMap::from_iter([
|
|
||||||
redirected_command!("tinymist.exportPdf", Self::export_pdf),
|
|
||||||
redirected_command!("tinymist.exportSvg", Self::export_svg),
|
|
||||||
redirected_command!("tinymist.exportPng", Self::export_png),
|
|
||||||
redirected_command!("tinymist.doClearCache", Self::clear_cache),
|
|
||||||
redirected_command!("tinymist.changeEntry", Self::change_entry),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The entry point for the `workspace/executeCommand` request.
|
|
||||||
fn execute_command(&mut self, params: ExecuteCommandParams) -> LspResult<JsonValue> {
|
|
||||||
let ExecuteCommandParams {
|
|
||||||
command,
|
|
||||||
arguments,
|
|
||||||
work_done_progress_params: _,
|
|
||||||
} = params;
|
|
||||||
let Some(handler) = self.exec_cmds.get(command.as_str()) else {
|
|
||||||
error!("asked to execute unknown command");
|
|
||||||
return Err(method_not_found());
|
|
||||||
};
|
|
||||||
handler(self, arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Export the current document as a PDF file.
|
|
||||||
pub fn export_pdf(&self, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
self.export(ExportKind::Pdf, arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Export the current document as a Svg file.
|
|
||||||
pub fn export_svg(&self, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
let opts = parse_opts(arguments.get(1))?;
|
|
||||||
self.export(ExportKind::Svg { page: opts.page }, arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Export the current document as a Png file.
|
|
||||||
pub fn export_png(&self, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
let opts = parse_opts(arguments.get(1))?;
|
|
||||||
self.export(ExportKind::Png { page: opts.page }, arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Export the current document as some format. The client is responsible
|
|
||||||
/// for passing the correct absolute path of typst document.
|
|
||||||
pub fn export(&self, kind: ExportKind, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
let path = parse_path(arguments.first())?.as_ref().to_owned();
|
|
||||||
|
|
||||||
let res = run_query!(self.OnExport(path, kind))?;
|
|
||||||
let res = serde_json::to_value(res).map_err(|_| internal_error("Cannot serialize path"))?;
|
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clear all cached resources.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// Errors if the cache could not be cleared.
|
|
||||||
pub fn clear_cache(&self, _arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
comemo::evict(0);
|
|
||||||
Ok(JsonValue::Null)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Focus main file to some path.
|
|
||||||
pub fn change_entry(&mut self, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
let new_entry = parse_path_or_null(arguments.first())?;
|
|
||||||
|
|
||||||
let update_result = self.do_change_entry(new_entry.clone());
|
|
||||||
update_result.map_err(|err| internal_error(format!("could not focus file: {err}")))?;
|
|
||||||
|
|
||||||
info!("entry changed: {entry:?}", entry = new_entry);
|
|
||||||
Ok(JsonValue::Null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
struct ExportOpts {
|
|
||||||
page: PageSelection,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_opts(v: Option<&JsonValue>) -> LspResult<ExportOpts> {
|
|
||||||
Ok(match v {
|
|
||||||
Some(opts) => serde_json::from_value::<ExportOpts>(opts.clone())
|
|
||||||
.map_err(|_| invalid_params("The third argument is not a valid object"))?,
|
|
||||||
_ => ExportOpts {
|
|
||||||
page: PageSelection::First,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_path(v: Option<&JsonValue>) -> LspResult<ImmutPath> {
|
|
||||||
let new_entry = match v {
|
|
||||||
Some(JsonValue::String(s)) => Path::new(s).into(),
|
|
||||||
_ => {
|
|
||||||
return Err(invalid_params(
|
|
||||||
"The first parameter is not a valid path or null",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(new_entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_path_or_null(v: Option<&JsonValue>) -> LspResult<Option<ImmutPath>> {
|
|
||||||
match v {
|
|
||||||
Some(JsonValue::Null) => Ok(None),
|
|
||||||
v => Ok(Some(parse_path(v)?)),
|
|
||||||
}
|
|
||||||
}
|
|
110
crates/tinymist/src/server/compile_cmd.rs
Normal file
110
crates/tinymist/src/server/compile_cmd.rs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
|
use log::{error, info};
|
||||||
|
use lsp_types::ExecuteCommandParams;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_json::Value as JsonValue;
|
||||||
|
use tinymist_query::{ExportKind, PageSelection};
|
||||||
|
|
||||||
|
use crate::{internal_error, invalid_params, method_not_found, run_query, LspResult};
|
||||||
|
|
||||||
|
use super::compile::*;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
macro_rules! exec_fn {
|
||||||
|
($ty: ty, Self::$method: ident, $($arg_key:ident),+ $(,)?) => {{
|
||||||
|
const E: $ty = |this, $($arg_key),+| this.$method($($arg_key),+);
|
||||||
|
E
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Deserialize)]
|
||||||
|
struct ExportOpts {
|
||||||
|
page: PageSelection,
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExecuteCmdMap = HashMap<&'static str, LspHandler<Vec<JsonValue>, JsonValue>>;
|
||||||
|
|
||||||
|
impl CompileState {
|
||||||
|
pub fn get_exec_commands() -> ExecuteCmdMap {
|
||||||
|
macro_rules! redirected_command {
|
||||||
|
($key: expr, Self::$method: ident) => {
|
||||||
|
(
|
||||||
|
$key,
|
||||||
|
exec_fn!(LspHandler<Vec<JsonValue>, JsonValue>, Self::$method, inputs),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecuteCmdMap::from_iter([
|
||||||
|
redirected_command!("tinymist.exportPdf", Self::export_pdf),
|
||||||
|
redirected_command!("tinymist.exportSvg", Self::export_svg),
|
||||||
|
redirected_command!("tinymist.exportPng", Self::export_png),
|
||||||
|
redirected_command!("tinymist.doClearCache", Self::clear_cache),
|
||||||
|
redirected_command!("tinymist.changeEntry", Self::change_entry),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The entry point for the `workspace/executeCommand` request.
|
||||||
|
pub fn execute_command(&mut self, params: ExecuteCommandParams) -> LspResult<JsonValue> {
|
||||||
|
let ExecuteCommandParams {
|
||||||
|
command,
|
||||||
|
arguments: args,
|
||||||
|
work_done_progress_params: _,
|
||||||
|
} = params;
|
||||||
|
let Some(handler) = self.exec_cmds.get(command.as_str()) else {
|
||||||
|
error!("asked to execute unknown command");
|
||||||
|
return Err(method_not_found());
|
||||||
|
};
|
||||||
|
handler(self, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Export the current document as a PDF file.
|
||||||
|
pub fn export_pdf(&self, args: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
self.export(ExportKind::Pdf, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Export the current document as a Svg file.
|
||||||
|
pub fn export_svg(&self, mut args: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
let opts = get_arg_or_default!(args[1] as ExportOpts);
|
||||||
|
self.export(ExportKind::Svg { page: opts.page }, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Export the current document as a Png file.
|
||||||
|
pub fn export_png(&self, mut args: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
let opts = get_arg_or_default!(args[1] as ExportOpts);
|
||||||
|
self.export(ExportKind::Png { page: opts.page }, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Export the current document as some format. The client is responsible
|
||||||
|
/// for passing the correct absolute path of typst document.
|
||||||
|
pub fn export(&self, kind: ExportKind, mut args: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
let path = get_arg!(args[0] as PathBuf);
|
||||||
|
|
||||||
|
let res = run_query!(self.OnExport(path, kind))?;
|
||||||
|
let res = serde_json::to_value(res).map_err(|_| internal_error("Cannot serialize path"))?;
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear all cached resources.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Errors if the cache could not be cleared.
|
||||||
|
pub fn clear_cache(&self, _arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
comemo::evict(0);
|
||||||
|
self.compiler().clear_cache();
|
||||||
|
Ok(JsonValue::Null)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Focus main file to some path.
|
||||||
|
pub fn change_entry(&mut self, mut args: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
let entry = get_arg!(args[0] as Option<PathBuf>).map(From::from);
|
||||||
|
|
||||||
|
let update_result = self.do_change_entry(entry.clone());
|
||||||
|
update_result.map_err(|err| internal_error(format!("could not focus file: {err}")))?;
|
||||||
|
|
||||||
|
info!("entry changed: {entry:?}");
|
||||||
|
Ok(JsonValue::Null)
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ use typst_ts_core::config::compiler::EntryState;
|
||||||
use typst_ts_core::{ImmutPath, TypstDict};
|
use typst_ts_core::{ImmutPath, TypstDict};
|
||||||
|
|
||||||
use crate::actor::editor::EditorRequest;
|
use crate::actor::editor::EditorRequest;
|
||||||
use crate::compiler::CompileServer;
|
use crate::compile::CompileState;
|
||||||
use crate::harness::LspDriver;
|
use crate::harness::LspDriver;
|
||||||
use crate::utils::{try_, try_or_default};
|
use crate::utils::{try_, try_or_default};
|
||||||
use crate::world::{ImmutDict, SharedFontResolver};
|
use crate::world::{ImmutDict, SharedFontResolver};
|
||||||
|
@ -372,21 +372,13 @@ impl CompileConfig {
|
||||||
|
|
||||||
/// Configuration set at initialization that won't change within a single
|
/// Configuration set at initialization that won't change within a single
|
||||||
/// session.
|
/// session.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct CompilerConstConfig {
|
pub struct ConstCompileConfig {
|
||||||
/// Determined position encoding, either UTF-8 or UTF-16.
|
/// Determined position encoding, either UTF-8 or UTF-16.
|
||||||
/// Defaults to UTF-16 if not specified.
|
/// Defaults to UTF-16 if not specified.
|
||||||
pub position_encoding: PositionEncoding,
|
pub position_encoding: PositionEncoding,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CompilerConstConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
position_encoding: PositionEncoding::Utf16,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CompileInit {
|
pub struct CompileInit {
|
||||||
pub handle: tokio::runtime::Handle,
|
pub handle: tokio::runtime::Handle,
|
||||||
pub font: CompileFontOpts,
|
pub font: CompileFontOpts,
|
||||||
|
@ -402,7 +394,7 @@ pub struct CompileInitializeParams {
|
||||||
impl LspDriver for CompileInit {
|
impl LspDriver for CompileInit {
|
||||||
type InitParams = CompileInitializeParams;
|
type InitParams = CompileInitializeParams;
|
||||||
type InitResult = ();
|
type InitResult = ();
|
||||||
type InitializedSelf = CompileServer;
|
type InitializedSelf = CompileState;
|
||||||
|
|
||||||
fn initialize(
|
fn initialize(
|
||||||
self,
|
self,
|
||||||
|
@ -418,10 +410,10 @@ impl LspDriver for CompileInit {
|
||||||
};
|
};
|
||||||
compile_config.update(¶ms.config).unwrap();
|
compile_config.update(¶ms.config).unwrap();
|
||||||
|
|
||||||
let mut service = CompileServer::new(
|
let mut service = CompileState::new(
|
||||||
client,
|
client,
|
||||||
compile_config,
|
compile_config,
|
||||||
CompilerConstConfig {
|
ConstCompileConfig {
|
||||||
position_encoding: params
|
position_encoding: params
|
||||||
.position_encoding
|
.position_encoding
|
||||||
.map(|x| match x.as_str() {
|
.map(|x| match x.as_str() {
|
|
@ -1,7 +1,5 @@
|
||||||
//! tinymist LSP mode
|
//! tinymist LSP mode
|
||||||
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{collections::HashMap, path::PathBuf};
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
@ -18,48 +16,37 @@ use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{Map, Value as JsonValue};
|
use serde_json::{Map, Value as JsonValue};
|
||||||
use tinymist_query::{
|
use tinymist_query::{
|
||||||
get_semantic_tokens_options, get_semantic_tokens_registration,
|
get_semantic_tokens_options, get_semantic_tokens_registration,
|
||||||
get_semantic_tokens_unregistration, ExportKind, PageSelection, SemanticTokenContext,
|
get_semantic_tokens_unregistration, PageSelection, SemanticTokenContext,
|
||||||
};
|
};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use typst::diag::StrResult;
|
use typst_ts_core::ImmutPath;
|
||||||
use typst::syntax::package::{PackageSpec, VersionlessPackageSpec};
|
|
||||||
use typst_ts_core::path::PathClean;
|
|
||||||
use typst_ts_core::{error::prelude::*, ImmutPath};
|
|
||||||
|
|
||||||
use super::lsp_init::*;
|
use super::lsp_init::*;
|
||||||
use crate::actor::editor::EditorRequest;
|
use crate::actor::editor::EditorRequest;
|
||||||
use crate::actor::format::{FormatConfig, FormatRequest};
|
use crate::actor::format::{FormatConfig, FormatRequest};
|
||||||
use crate::actor::typ_client::CompileClientActor;
|
use crate::actor::typ_client::CompileClientActor;
|
||||||
use crate::actor::user_action::{TraceParams, UserActionRequest};
|
use crate::actor::user_action::UserActionRequest;
|
||||||
use crate::compiler::CompileServer;
|
use crate::compile::CompileState;
|
||||||
use crate::compiler_init::CompilerConstConfig;
|
use crate::compile_init::ConstCompileConfig;
|
||||||
use crate::harness::{InitializedLspDriver, LspHost};
|
use crate::harness::{InitializedLspDriver, LspHost};
|
||||||
use crate::tools::package::InitTask;
|
|
||||||
use crate::{run_query, LspResult};
|
use crate::{run_query, LspResult};
|
||||||
|
|
||||||
pub type MaySyncResult<'a> = Result<JsonValue, BoxFuture<'a, JsonValue>>;
|
pub type MaySyncResult<'a> = Result<JsonValue, BoxFuture<'a, JsonValue>>;
|
||||||
|
|
||||||
type LspMethod<Res> = fn(srv: &mut TypstLanguageServer, args: JsonValue) -> LspResult<Res>;
|
type LspMethod<Res> = fn(srv: &mut LanguageState, args: JsonValue) -> LspResult<Res>;
|
||||||
type LspHandler<Req, Res> = fn(srv: &mut TypstLanguageServer, args: Req) -> LspResult<Res>;
|
type LspHandler<Req, Res> = fn(srv: &mut LanguageState, args: Req) -> LspResult<Res>;
|
||||||
|
|
||||||
/// Returns Ok(Some()) -> Already responded
|
/// Returns Ok(Some()) -> Already responded
|
||||||
/// Returns Ok(None) -> Need to respond none
|
/// Returns Ok(None) -> Need to respond none
|
||||||
/// Returns Err(..) -> Need to respond error
|
/// Returns Err(..) -> Need to respond error
|
||||||
type LspRawHandler<T> =
|
type LspRawHandler<T> =
|
||||||
fn(srv: &mut TypstLanguageServer, req_id: RequestId, args: T) -> LspResult<Option<()>>;
|
fn(srv: &mut LanguageState, req_id: RequestId, args: T) -> LspResult<Option<()>>;
|
||||||
|
|
||||||
type ExecuteCmdMap = HashMap<&'static str, LspRawHandler<Vec<JsonValue>>>;
|
type ExecuteCmdMap = HashMap<&'static str, LspRawHandler<Vec<JsonValue>>>;
|
||||||
type NotifyCmdMap = HashMap<&'static str, LspMethod<()>>;
|
type NotifyCmdMap = HashMap<&'static str, LspMethod<()>>;
|
||||||
type RegularCmdMap = HashMap<&'static str, LspRawHandler<JsonValue>>;
|
type RegularCmdMap = HashMap<&'static str, LspRawHandler<JsonValue>>;
|
||||||
type ResourceMap = HashMap<ImmutPath, LspHandler<Vec<JsonValue>, JsonValue>>;
|
type ResourceMap = HashMap<ImmutPath, LspHandler<Vec<JsonValue>, JsonValue>>;
|
||||||
|
|
||||||
macro_rules! resource_fn {
|
|
||||||
($ty: ty, Self::$method: ident, $($arg_key:ident),+ $(,)?) => {{
|
|
||||||
const E: $ty = |this, $($arg_key),+| this.$method($($arg_key),+);
|
|
||||||
E
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! request_fn_ {
|
macro_rules! request_fn_ {
|
||||||
($desc: ty, Self::$method: ident) => {
|
($desc: ty, Self::$method: ident) => {
|
||||||
(<$desc>::METHOD, {
|
(<$desc>::METHOD, {
|
||||||
|
@ -90,28 +77,6 @@ macro_rules! request_fn {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! exec_fn_ {
|
|
||||||
($key: expr, Self::$method: ident) => {
|
|
||||||
($key, {
|
|
||||||
const E: LspRawHandler<Vec<JsonValue>> = |this, req_id, req| this.$method(req_id, req);
|
|
||||||
E
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! exec_fn {
|
|
||||||
($key: expr, Self::$method: ident) => {
|
|
||||||
($key, {
|
|
||||||
const E: LspRawHandler<Vec<JsonValue>> = |this, req_id, args| {
|
|
||||||
let res = this.$method(args);
|
|
||||||
this.client.respond(result_to_response(req_id, res));
|
|
||||||
Ok(Some(()))
|
|
||||||
};
|
|
||||||
E
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! notify_fn {
|
macro_rules! notify_fn {
|
||||||
($desc: ty, Self::$method: ident) => {
|
($desc: ty, Self::$method: ident) => {
|
||||||
(<$desc>::METHOD, {
|
(<$desc>::METHOD, {
|
||||||
|
@ -125,11 +90,11 @@ macro_rules! notify_fn {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_path(inp: TextDocumentIdentifier) -> PathBuf {
|
pub(super) fn as_path(inp: TextDocumentIdentifier) -> PathBuf {
|
||||||
as_path_(inp.uri)
|
as_path_(inp.uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_path_(uri: Url) -> PathBuf {
|
pub(super) fn as_path_(uri: Url) -> PathBuf {
|
||||||
tinymist_query::url_to_path(uri)
|
tinymist_query::url_to_path(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,9 +103,9 @@ fn as_path_pos(inp: TextDocumentPositionParams) -> (PathBuf, Position) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The object providing the language server functionality.
|
/// The object providing the language server functionality.
|
||||||
pub struct TypstLanguageServer {
|
pub struct LanguageState {
|
||||||
/// The language server client.
|
/// The language server client.
|
||||||
pub client: LspHost<TypstLanguageServer>,
|
pub client: LspHost<LanguageState>,
|
||||||
|
|
||||||
// State to synchronize with the client.
|
// State to synchronize with the client.
|
||||||
/// Whether the server is shutting down.
|
/// Whether the server is shutting down.
|
||||||
|
@ -173,15 +138,15 @@ pub struct TypstLanguageServer {
|
||||||
/// Regular commands for dispatching.
|
/// Regular commands for dispatching.
|
||||||
pub regular_cmds: RegularCmdMap,
|
pub regular_cmds: RegularCmdMap,
|
||||||
/// Regular commands for dispatching.
|
/// Regular commands for dispatching.
|
||||||
pub resources_routes: ResourceMap,
|
pub resource_routes: ResourceMap,
|
||||||
|
|
||||||
// Resources
|
// Resources
|
||||||
/// The semantic token context.
|
/// The semantic token context.
|
||||||
pub tokens_ctx: SemanticTokenContext,
|
pub tokens_ctx: SemanticTokenContext,
|
||||||
/// The compiler for general purpose.
|
/// The compiler for general purpose.
|
||||||
pub primary: CompileServer,
|
pub primary: CompileState,
|
||||||
/// The compilers for tasks
|
/// The compilers for tasks
|
||||||
pub dedicates: Vec<CompileServer>,
|
pub dedicates: Vec<CompileState>,
|
||||||
/// The formatter thread running in backend.
|
/// The formatter thread running in backend.
|
||||||
/// Note: The thread will exit if you drop the sender.
|
/// Note: The thread will exit if you drop the sender.
|
||||||
pub format_thread: Option<crossbeam_channel::Sender<FormatRequest>>,
|
pub format_thread: Option<crossbeam_channel::Sender<FormatRequest>>,
|
||||||
|
@ -191,10 +156,10 @@ pub struct TypstLanguageServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Getters and the main loop.
|
/// Getters and the main loop.
|
||||||
impl TypstLanguageServer {
|
impl LanguageState {
|
||||||
/// Create a new language server.
|
/// Create a new language server.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
client: LspHost<TypstLanguageServer>,
|
client: LspHost<LanguageState>,
|
||||||
const_config: ConstConfig,
|
const_config: ConstConfig,
|
||||||
editor_tx: mpsc::UnboundedSender<EditorRequest>,
|
editor_tx: mpsc::UnboundedSender<EditorRequest>,
|
||||||
handle: tokio::runtime::Handle,
|
handle: tokio::runtime::Handle,
|
||||||
|
@ -206,10 +171,10 @@ impl TypstLanguageServer {
|
||||||
);
|
);
|
||||||
Self {
|
Self {
|
||||||
client,
|
client,
|
||||||
primary: CompileServer::new(
|
primary: CompileState::new(
|
||||||
LspHost::new(Arc::new(RwLock::new(None))),
|
LspHost::new(Arc::new(RwLock::new(None))),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
CompilerConstConfig {
|
ConstCompileConfig {
|
||||||
position_encoding: const_config.position_encoding,
|
position_encoding: const_config.position_encoding,
|
||||||
},
|
},
|
||||||
editor_tx,
|
editor_tx,
|
||||||
|
@ -227,7 +192,7 @@ impl TypstLanguageServer {
|
||||||
exec_cmds: Self::get_exec_commands(),
|
exec_cmds: Self::get_exec_commands(),
|
||||||
regular_cmds: Self::get_regular_cmds(),
|
regular_cmds: Self::get_regular_cmds(),
|
||||||
notify_cmds: Self::get_notify_cmds(),
|
notify_cmds: Self::get_notify_cmds(),
|
||||||
resources_routes: Self::get_resources_routes(),
|
resource_routes: Self::get_resource_routes(),
|
||||||
|
|
||||||
pinning: false,
|
pinning: false,
|
||||||
focusing: None,
|
focusing: None,
|
||||||
|
@ -294,7 +259,7 @@ impl TypstLanguageServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InitializedLspDriver for TypstLanguageServer {
|
impl InitializedLspDriver for LanguageState {
|
||||||
/// The [`initialized`] notification is sent from the client to the server
|
/// The [`initialized`] notification is sent from the client to the server
|
||||||
/// after the client received the result of the initialize request but
|
/// after the client received the result of the initialize request but
|
||||||
/// before the client sends anything else.
|
/// before the client sends anything else.
|
||||||
|
@ -383,7 +348,7 @@ impl InitializedLspDriver for TypstLanguageServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypstLanguageServer {
|
impl LanguageState {
|
||||||
/// Registers and handles a request. This should only be called once per
|
/// Registers and handles a request. This should only be called once per
|
||||||
/// incoming request.
|
/// incoming request.
|
||||||
fn on_request(&mut self, request_received: Instant, req: Request) {
|
fn on_request(&mut self, request_received: Instant, req: Request) {
|
||||||
|
@ -533,7 +498,7 @@ impl TypstLanguageServer {
|
||||||
/// low-level implementation details.
|
/// low-level implementation details.
|
||||||
///
|
///
|
||||||
/// [Language Server Protocol]: https://microsoft.github.io/language-server-protocol/
|
/// [Language Server Protocol]: https://microsoft.github.io/language-server-protocol/
|
||||||
impl TypstLanguageServer {
|
impl LanguageState {
|
||||||
/// The [`shutdown`] request asks the server to gracefully shut down, but to
|
/// The [`shutdown`] request asks the server to gracefully shut down, but to
|
||||||
/// not exit.
|
/// not exit.
|
||||||
///
|
///
|
||||||
|
@ -553,346 +518,8 @@ impl TypstLanguageServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Here are implemented the handlers for each command.
|
|
||||||
impl TypstLanguageServer {
|
|
||||||
fn get_exec_commands() -> ExecuteCmdMap {
|
|
||||||
ExecuteCmdMap::from_iter([
|
|
||||||
exec_fn!("tinymist.exportPdf", Self::export_pdf),
|
|
||||||
exec_fn!("tinymist.exportSvg", Self::export_svg),
|
|
||||||
exec_fn!("tinymist.exportPng", Self::export_png),
|
|
||||||
exec_fn!("tinymist.doClearCache", Self::clear_cache),
|
|
||||||
exec_fn!("tinymist.pinMain", Self::pin_document),
|
|
||||||
exec_fn!("tinymist.focusMain", Self::focus_document),
|
|
||||||
exec_fn!("tinymist.doInitTemplate", Self::init_template),
|
|
||||||
exec_fn!("tinymist.doGetTemplateEntry", Self::do_get_template_entry),
|
|
||||||
exec_fn!("tinymist.interactCodeContext", Self::interact_code_context),
|
|
||||||
exec_fn_!("tinymist.getDocumentTrace", Self::get_document_trace),
|
|
||||||
exec_fn!("tinymist.getDocumentMetrics", Self::get_document_metrics),
|
|
||||||
exec_fn!("tinymist.getServerInfo", Self::get_server_info),
|
|
||||||
// For Documentations
|
|
||||||
exec_fn!("tinymist.getResources", Self::get_resources),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Export the current document as a PDF file.
|
|
||||||
pub fn export_pdf(&mut self, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
self.export(ExportKind::Pdf, arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Export the current document as a Svg file.
|
|
||||||
pub fn export_svg(&mut self, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
let opts = parse_opts(arguments.get(1))?;
|
|
||||||
self.export(ExportKind::Svg { page: opts.page }, arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Export the current document as a Png file.
|
|
||||||
pub fn export_png(&mut self, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
let opts = parse_opts(arguments.get(1))?;
|
|
||||||
self.export(ExportKind::Png { page: opts.page }, arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Export the current document as some format. The client is responsible
|
|
||||||
/// for passing the correct absolute path of typst document.
|
|
||||||
pub fn export(&mut self, kind: ExportKind, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
let path = parse_path(arguments.first())?.as_ref().to_owned();
|
|
||||||
|
|
||||||
let res = run_query!(self.OnExport(path, kind))?;
|
|
||||||
let res = serde_json::to_value(res).map_err(|_| internal_error("Cannot serialize path"))?;
|
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Interact with the code context at the source file.
|
|
||||||
pub fn interact_code_context(&mut self, _arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
let queries = _arguments.into_iter().next().ok_or_else(|| {
|
|
||||||
invalid_params("The first parameter is not a valid code context query array")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct InteractCodeContextParams {
|
|
||||||
pub text_document: TextDocumentIdentifier,
|
|
||||||
pub query: Vec<tinymist_query::InteractCodeContextQuery>,
|
|
||||||
}
|
|
||||||
|
|
||||||
let params: InteractCodeContextParams = serde_json::from_value(queries)
|
|
||||||
.map_err(|e| invalid_params(format!("Cannot parse code context queries: {e}")))?;
|
|
||||||
let path = as_path(params.text_document);
|
|
||||||
let query = params.query;
|
|
||||||
|
|
||||||
let res = run_query!(self.InteractCodeContext(path, query))?;
|
|
||||||
let res =
|
|
||||||
serde_json::to_value(res).map_err(|_| internal_error("Cannot serialize responses"))?;
|
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the trace data of the document.
|
|
||||||
pub fn get_document_trace(
|
|
||||||
&mut self,
|
|
||||||
req_id: RequestId,
|
|
||||||
arguments: Vec<JsonValue>,
|
|
||||||
) -> LspResult<Option<()>> {
|
|
||||||
let path = parse_path(arguments.first())?;
|
|
||||||
|
|
||||||
// get path to self program
|
|
||||||
let self_path = std::env::current_exe()
|
|
||||||
.map_err(|e| internal_error(format!("Cannot get typst compiler {e}")))?;
|
|
||||||
|
|
||||||
let thread = self.user_action_thread.clone();
|
|
||||||
let entry = self.config.compile.determine_entry(Some(path));
|
|
||||||
|
|
||||||
let res = self
|
|
||||||
.primary()
|
|
||||||
.steal(move |c| {
|
|
||||||
let verse = &c.verse;
|
|
||||||
|
|
||||||
// todo: rootless file
|
|
||||||
// todo: memory dirty file
|
|
||||||
let root = entry.root().ok_or_else(|| {
|
|
||||||
anyhow::anyhow!("root must be determined for trace, got {entry:?}")
|
|
||||||
})?;
|
|
||||||
let main = entry
|
|
||||||
.main()
|
|
||||||
.and_then(|e| e.vpath().resolve(&root))
|
|
||||||
.ok_or_else(|| anyhow::anyhow!("main file must be resolved, got {entry:?}"))?;
|
|
||||||
|
|
||||||
if let Some(f) = thread {
|
|
||||||
f.send(UserActionRequest::Trace(
|
|
||||||
req_id,
|
|
||||||
TraceParams {
|
|
||||||
compiler_program: self_path,
|
|
||||||
root: root.as_ref().to_owned(),
|
|
||||||
main,
|
|
||||||
inputs: verse.inputs().as_ref().deref().clone(),
|
|
||||||
font_paths: verse.font_resolver.font_paths().to_owned(),
|
|
||||||
},
|
|
||||||
))
|
|
||||||
.context("cannot send trace request")?;
|
|
||||||
} else {
|
|
||||||
bail!("user action thread is not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Some(()))
|
|
||||||
})
|
|
||||||
.context("cannot steal primary compiler");
|
|
||||||
|
|
||||||
let res = match res {
|
|
||||||
Ok(res) => res,
|
|
||||||
Err(res) => Err(res),
|
|
||||||
};
|
|
||||||
|
|
||||||
res.map_err(|e| internal_error(format!("could not get document trace: {e}")))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the metrics of the document.
|
|
||||||
pub fn get_document_metrics(&mut self, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
let path = parse_path(arguments.first())?.as_ref().to_owned();
|
|
||||||
|
|
||||||
let res = run_query!(self.DocumentMetrics(path))?;
|
|
||||||
let res = serde_json::to_value(res)
|
|
||||||
.map_err(|e| internal_error(format!("Cannot serialize response {e}")))?;
|
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the server info.
|
|
||||||
pub fn get_server_info(&mut self, _arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
let res = run_query!(self.ServerInfo())?;
|
|
||||||
|
|
||||||
let res = serde_json::to_value(res)
|
|
||||||
.map_err(|e| internal_error(format!("Cannot serialize response {e}")))?;
|
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clear all cached resources.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// Errors if the cache could not be cleared.
|
|
||||||
pub fn clear_cache(&self, _arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
comemo::evict(0);
|
|
||||||
for v in Some(self.primary())
|
|
||||||
.into_iter()
|
|
||||||
.chain(self.dedicates.iter().map(|v| v.compiler()))
|
|
||||||
{
|
|
||||||
v.clear_cache();
|
|
||||||
}
|
|
||||||
Ok(JsonValue::Null)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pin main file to some path.
|
|
||||||
pub fn pin_document(&mut self, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
let new_entry = parse_path_or_null(arguments.first())?;
|
|
||||||
|
|
||||||
let update_result = self.pin_entry(new_entry.clone());
|
|
||||||
update_result.map_err(|err| internal_error(format!("could not pin file: {err}")))?;
|
|
||||||
|
|
||||||
info!("file pinned: {entry:?}", entry = new_entry);
|
|
||||||
Ok(JsonValue::Null)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Focus main file to some path.
|
|
||||||
pub fn focus_document(&mut self, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
let new_entry = parse_path_or_null(arguments.first())?;
|
|
||||||
|
|
||||||
if !self.ever_manual_focusing {
|
|
||||||
self.ever_manual_focusing = true;
|
|
||||||
log::info!("first manual focusing is coming");
|
|
||||||
}
|
|
||||||
|
|
||||||
let ok = self.focus_entry(new_entry.clone());
|
|
||||||
let ok = ok.map_err(|err| internal_error(format!("could not focus file: {err}")))?;
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
info!("file focused: {new_entry:?}");
|
|
||||||
}
|
|
||||||
Ok(JsonValue::Null)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize a new template.
|
|
||||||
pub fn init_template(&self, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
use crate::tools::package::{self, determine_latest_version, TemplateSource};
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct InitResult {
|
|
||||||
entry_path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
let from_source = arguments
|
|
||||||
.first()
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.map(|s| s.to_owned())
|
|
||||||
.ok_or_else(|| invalid_params("The first parameter is not a valid source or null"))?;
|
|
||||||
let to_path = parse_path_or_null(arguments.get(1))?;
|
|
||||||
let res = self
|
|
||||||
.primary()
|
|
||||||
.steal(move |c| {
|
|
||||||
let world = c.verse.spawn();
|
|
||||||
// Parse the package specification. If the user didn't specify the version,
|
|
||||||
// we try to figure it out automatically by downloading the package index
|
|
||||||
// or searching the disk.
|
|
||||||
let spec: PackageSpec = from_source
|
|
||||||
.parse()
|
|
||||||
.or_else(|err| {
|
|
||||||
// Try to parse without version, but prefer the error message of the
|
|
||||||
// normal package spec parsing if it fails.
|
|
||||||
let spec: VersionlessPackageSpec = from_source.parse().map_err(|_| err)?;
|
|
||||||
let version = determine_latest_version(&c.verse, &spec)?;
|
|
||||||
StrResult::Ok(spec.at(version))
|
|
||||||
})
|
|
||||||
.map_err(map_string_err("failed to parse package spec"))?;
|
|
||||||
|
|
||||||
let from_source = TemplateSource::Package(spec);
|
|
||||||
|
|
||||||
let entry_path = package::init(
|
|
||||||
&world,
|
|
||||||
InitTask {
|
|
||||||
tmpl: from_source.clone(),
|
|
||||||
dir: to_path.clone(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.map_err(map_string_err("failed to initialize template"))?;
|
|
||||||
|
|
||||||
info!("template initialized: {from_source:?} to {to_path:?}");
|
|
||||||
|
|
||||||
ZResult::Ok(InitResult { entry_path })
|
|
||||||
})
|
|
||||||
.and_then(|e| e)
|
|
||||||
.map_err(|e| invalid_params(format!("failed to determine template source: {e}")))?;
|
|
||||||
|
|
||||||
serde_json::to_value(res).map_err(|_| internal_error("Cannot serialize path"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the entry of a template.
|
|
||||||
pub fn do_get_template_entry(&self, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
use crate::tools::package::{self, determine_latest_version, TemplateSource};
|
|
||||||
|
|
||||||
let from_source = arguments
|
|
||||||
.first()
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.map(|s| s.to_owned())
|
|
||||||
.ok_or_else(|| invalid_params("The first parameter is not a valid source or null"))?;
|
|
||||||
|
|
||||||
let entry = self
|
|
||||||
.primary()
|
|
||||||
.steal(move |c| {
|
|
||||||
// Parse the package specification. If the user didn't specify the version,
|
|
||||||
// we try to figure it out automatically by downloading the package index
|
|
||||||
// or searching the disk.
|
|
||||||
let spec: PackageSpec = from_source
|
|
||||||
.parse()
|
|
||||||
.or_else(|err| {
|
|
||||||
// Try to parse without version, but prefer the error message of the
|
|
||||||
// normal package spec parsing if it fails.
|
|
||||||
let spec: VersionlessPackageSpec = from_source.parse().map_err(|_| err)?;
|
|
||||||
let version = determine_latest_version(&c.verse, &spec)?;
|
|
||||||
StrResult::Ok(spec.at(version))
|
|
||||||
})
|
|
||||||
.map_err(map_string_err("failed to parse package spec"))?;
|
|
||||||
|
|
||||||
let from_source = TemplateSource::Package(spec);
|
|
||||||
|
|
||||||
let entry = package::get_entry(&c.verse, from_source)
|
|
||||||
.map_err(map_string_err("failed to get template entry"))?;
|
|
||||||
|
|
||||||
ZResult::Ok(entry)
|
|
||||||
})
|
|
||||||
.and_then(|e| e)
|
|
||||||
.map_err(|e| invalid_params(format!("failed to determine template entry: {e}")))?;
|
|
||||||
|
|
||||||
let entry = String::from_utf8(entry.to_vec())
|
|
||||||
.map_err(|_| invalid_params("template entry is not a valid UTF-8 string"))?;
|
|
||||||
|
|
||||||
Ok(JsonValue::String(entry))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypstLanguageServer {
|
|
||||||
fn get_resources_routes() -> ResourceMap {
|
|
||||||
macro_rules! resources_at {
|
|
||||||
($key: expr, Self::$method: ident) => {
|
|
||||||
(
|
|
||||||
Path::new($key).clean().as_path().into(),
|
|
||||||
resource_fn!(LspHandler<Vec<JsonValue>, JsonValue>, Self::$method, inputs),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ResourceMap::from_iter([
|
|
||||||
resources_at!("/symbols", Self::resources_alt_symbols),
|
|
||||||
resources_at!("/tutorial", Self::resource_tutoral),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get static resources with help of tinymist service, for example, a
|
|
||||||
/// static help pages for some typst function.
|
|
||||||
pub fn get_resources(&mut self, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
let u = parse_path(arguments.first())?;
|
|
||||||
|
|
||||||
let Some(handler) = self.resources_routes.get(u.as_ref()) else {
|
|
||||||
error!("asked for unknown resource: {u:?}");
|
|
||||||
return Err(method_not_found());
|
|
||||||
};
|
|
||||||
|
|
||||||
// Note our redirection will keep the first path argument in the arguments vec.
|
|
||||||
handler(self, arguments)
|
|
||||||
}
|
|
||||||
/// Get the all valid symbols
|
|
||||||
pub fn resources_alt_symbols(&self, _arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
let resp = self.get_symbol_resources();
|
|
||||||
resp.map_err(|e| internal_error(e.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get tutorial web page
|
|
||||||
pub fn resource_tutoral(&self, _arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
|
||||||
Err(method_not_found())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Document Synchronization
|
/// Document Synchronization
|
||||||
impl TypstLanguageServer {
|
impl LanguageState {
|
||||||
fn did_open(&mut self, params: DidOpenTextDocumentParams) -> LspResult<()> {
|
fn did_open(&mut self, params: DidOpenTextDocumentParams) -> LspResult<()> {
|
||||||
log::info!("did open {:?}", params.text_document.uri);
|
log::info!("did open {:?}", params.text_document.uri);
|
||||||
let path = as_path_(params.text_document.uri);
|
let path = as_path_(params.text_document.uri);
|
||||||
|
@ -1005,7 +632,7 @@ impl TypstLanguageServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Standard Language Features
|
/// Standard Language Features
|
||||||
impl TypstLanguageServer {
|
impl LanguageState {
|
||||||
fn goto_definition(
|
fn goto_definition(
|
||||||
&mut self,
|
&mut self,
|
||||||
params: GotoDefinitionParams,
|
params: GotoDefinitionParams,
|
||||||
|
@ -1195,32 +822,6 @@ struct ExportOpts {
|
||||||
page: PageSelection,
|
page: PageSelection,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_opts(v: Option<&JsonValue>) -> LspResult<ExportOpts> {
|
|
||||||
Ok(match v {
|
|
||||||
Some(opts) => serde_json::from_value::<ExportOpts>(opts.clone())
|
|
||||||
.map_err(|_| invalid_params("The third argument is not a valid object"))?,
|
|
||||||
_ => ExportOpts {
|
|
||||||
page: PageSelection::First,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_path(v: Option<&JsonValue>) -> LspResult<ImmutPath> {
|
|
||||||
let new_entry = match v {
|
|
||||||
Some(JsonValue::String(s)) => Path::new(s).clean().as_path().into(),
|
|
||||||
_ => return Err(invalid_params("The first parameter is not a valid path")),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(new_entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_path_or_null(v: Option<&JsonValue>) -> LspResult<Option<ImmutPath>> {
|
|
||||||
match v {
|
|
||||||
Some(JsonValue::Null) => Ok(None),
|
|
||||||
v => Ok(Some(parse_path(v)?)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn invalid_params(msg: impl Into<String>) -> ResponseError {
|
pub fn invalid_params(msg: impl Into<String>) -> ResponseError {
|
||||||
ResponseError {
|
ResponseError {
|
||||||
code: ErrorCode::InvalidParams as i32,
|
code: ErrorCode::InvalidParams as i32,
|
||||||
|
@ -1270,6 +871,9 @@ impl lsp_types::request::Request for OnEnter {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_as_path() {
|
fn test_as_path() {
|
||||||
|
use std::path::Path;
|
||||||
|
use typst_ts_core::path::PathClean;
|
||||||
|
|
||||||
let uri = Url::parse("untitled:/path/to/file").unwrap();
|
let uri = Url::parse("untitled:/path/to/file").unwrap();
|
||||||
assert_eq!(as_path_(uri), Path::new("/untitled/path/to/file").clean());
|
assert_eq!(as_path_(uri), Path::new("/untitled/path/to/file").clean());
|
||||||
|
|
||||||
|
|
387
crates/tinymist/src/server/lsp_cmd.rs
Normal file
387
crates/tinymist/src/server/lsp_cmd.rs
Normal file
|
@ -0,0 +1,387 @@
|
||||||
|
//! tinymist LSP mode
|
||||||
|
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
|
use anyhow::{bail, Context};
|
||||||
|
use log::{error, info};
|
||||||
|
use lsp_server::RequestId;
|
||||||
|
use lsp_types::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value as JsonValue;
|
||||||
|
use tinymist_query::ExportKind;
|
||||||
|
use typst::diag::StrResult;
|
||||||
|
use typst::syntax::package::{PackageSpec, VersionlessPackageSpec};
|
||||||
|
use typst_ts_core::path::PathClean;
|
||||||
|
use typst_ts_core::{error::prelude::*, ImmutPath};
|
||||||
|
|
||||||
|
use crate::actor::user_action::{TraceParams, UserActionRequest};
|
||||||
|
use crate::tools::package::InitTask;
|
||||||
|
use crate::{run_query, LspResult};
|
||||||
|
|
||||||
|
use super::lsp::*;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
macro_rules! exec_fn_ {
|
||||||
|
($key: expr, Self::$method: ident) => {
|
||||||
|
($key, {
|
||||||
|
const E: LspRawHandler<Vec<JsonValue>> = |this, req_id, req| this.$method(req_id, req);
|
||||||
|
E
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! exec_fn {
|
||||||
|
($key: expr, Self::$method: ident) => {
|
||||||
|
($key, {
|
||||||
|
const E: LspRawHandler<Vec<JsonValue>> = |this, req_id, args| {
|
||||||
|
let res = this.$method(args);
|
||||||
|
this.client.respond(result_to_response(req_id, res));
|
||||||
|
Ok(Some(()))
|
||||||
|
};
|
||||||
|
E
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! resource_fn {
|
||||||
|
($ty: ty, Self::$method: ident, $($arg_key:ident),+ $(,)?) => {{
|
||||||
|
const E: $ty = |this, $($arg_key),+| this.$method($($arg_key),+);
|
||||||
|
E
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
type LspHandler<Req, Res> = fn(srv: &mut LanguageState, args: Req) -> LspResult<Res>;
|
||||||
|
|
||||||
|
/// Returns Ok(Some()) -> Already responded
|
||||||
|
/// Returns Ok(None) -> Need to respond none
|
||||||
|
/// Returns Err(..) -> Need to respond error
|
||||||
|
type LspRawHandler<T> =
|
||||||
|
fn(srv: &mut LanguageState, req_id: RequestId, args: T) -> LspResult<Option<()>>;
|
||||||
|
|
||||||
|
type ExecuteCmdMap = HashMap<&'static str, LspRawHandler<Vec<JsonValue>>>;
|
||||||
|
type ResourceMap = HashMap<ImmutPath, LspHandler<Vec<JsonValue>, JsonValue>>;
|
||||||
|
|
||||||
|
/// Here are implemented the handlers for each command.
|
||||||
|
impl LanguageState {
|
||||||
|
pub fn get_exec_commands() -> ExecuteCmdMap {
|
||||||
|
ExecuteCmdMap::from_iter([
|
||||||
|
exec_fn!("tinymist.exportPdf", Self::export_pdf),
|
||||||
|
exec_fn!("tinymist.exportSvg", Self::export_svg),
|
||||||
|
exec_fn!("tinymist.exportPng", Self::export_png),
|
||||||
|
exec_fn!("tinymist.doClearCache", Self::clear_cache),
|
||||||
|
exec_fn!("tinymist.pinMain", Self::pin_document),
|
||||||
|
exec_fn!("tinymist.focusMain", Self::focus_document),
|
||||||
|
exec_fn!("tinymist.doInitTemplate", Self::init_template),
|
||||||
|
exec_fn!("tinymist.doGetTemplateEntry", Self::do_get_template_entry),
|
||||||
|
exec_fn!("tinymist.interactCodeContext", Self::interact_code_context),
|
||||||
|
exec_fn_!("tinymist.getDocumentTrace", Self::get_document_trace),
|
||||||
|
exec_fn!("tinymist.getDocumentMetrics", Self::get_document_metrics),
|
||||||
|
exec_fn!("tinymist.getServerInfo", Self::get_server_info),
|
||||||
|
// For Documentations
|
||||||
|
exec_fn!("tinymist.getResources", Self::get_resources),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Export the current document as a PDF file.
|
||||||
|
pub fn export_pdf(&mut self, args: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
self.primary.export_pdf(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Export the current document as a Svg file.
|
||||||
|
pub fn export_svg(&mut self, args: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
self.primary.export_svg(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Export the current document as a Png file.
|
||||||
|
pub fn export_png(&mut self, args: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
self.primary.export_png(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Export the current document as some format. The client is responsible
|
||||||
|
/// for passing the correct absolute path of typst document.
|
||||||
|
pub fn export(&mut self, kind: ExportKind, args: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
self.primary.export(kind, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear all cached resources.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Errors if the cache could not be cleared.
|
||||||
|
pub fn clear_cache(&self, _arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
comemo::evict(0);
|
||||||
|
for v in Some(self.primary())
|
||||||
|
.into_iter()
|
||||||
|
.chain(self.dedicates.iter().map(|v| v.compiler()))
|
||||||
|
{
|
||||||
|
v.clear_cache();
|
||||||
|
}
|
||||||
|
Ok(JsonValue::Null)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pin main file to some path.
|
||||||
|
pub fn pin_document(&mut self, mut args: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
let entry = get_arg!(args[0] as Option<PathBuf>).map(From::from);
|
||||||
|
|
||||||
|
let update_result = self.pin_entry(entry.clone());
|
||||||
|
update_result.map_err(|err| internal_error(format!("could not pin file: {err}")))?;
|
||||||
|
|
||||||
|
info!("file pinned: {entry:?}");
|
||||||
|
Ok(JsonValue::Null)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Focus main file to some path.
|
||||||
|
pub fn focus_document(&mut self, mut args: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
let entry = get_arg!(args[0] as Option<PathBuf>).map(From::from);
|
||||||
|
|
||||||
|
if !self.ever_manual_focusing {
|
||||||
|
self.ever_manual_focusing = true;
|
||||||
|
log::info!("first manual focusing is coming");
|
||||||
|
}
|
||||||
|
|
||||||
|
let ok = self.focus_entry(entry.clone());
|
||||||
|
let ok = ok.map_err(|err| internal_error(format!("could not focus file: {err}")))?;
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
info!("file focused: {entry:?}");
|
||||||
|
}
|
||||||
|
Ok(JsonValue::Null)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize a new template.
|
||||||
|
pub fn init_template(&self, mut args: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
use crate::tools::package::{self, determine_latest_version, TemplateSource};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct InitResult {
|
||||||
|
entry_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
let from_source = get_arg!(args[0] as String);
|
||||||
|
let to_path = get_arg!(args[1] as Option<PathBuf>).map(From::from);
|
||||||
|
let res = self
|
||||||
|
.primary()
|
||||||
|
.steal(move |c| {
|
||||||
|
let world = c.verse.spawn();
|
||||||
|
// Parse the package specification. If the user didn't specify the version,
|
||||||
|
// we try to figure it out automatically by downloading the package index
|
||||||
|
// or searching the disk.
|
||||||
|
let spec: PackageSpec = from_source
|
||||||
|
.parse()
|
||||||
|
.or_else(|err| {
|
||||||
|
// Try to parse without version, but prefer the error message of the
|
||||||
|
// normal package spec parsing if it fails.
|
||||||
|
let spec: VersionlessPackageSpec = from_source.parse().map_err(|_| err)?;
|
||||||
|
let version = determine_latest_version(&c.verse, &spec)?;
|
||||||
|
StrResult::Ok(spec.at(version))
|
||||||
|
})
|
||||||
|
.map_err(map_string_err("failed to parse package spec"))?;
|
||||||
|
|
||||||
|
let from_source = TemplateSource::Package(spec);
|
||||||
|
|
||||||
|
let entry_path = package::init(
|
||||||
|
&world,
|
||||||
|
InitTask {
|
||||||
|
tmpl: from_source.clone(),
|
||||||
|
dir: to_path.clone(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map_err(map_string_err("failed to initialize template"))?;
|
||||||
|
|
||||||
|
info!("template initialized: {from_source:?} to {to_path:?}");
|
||||||
|
|
||||||
|
ZResult::Ok(InitResult { entry_path })
|
||||||
|
})
|
||||||
|
.and_then(|e| e)
|
||||||
|
.map_err(|e| invalid_params(format!("failed to determine template source: {e}")))?;
|
||||||
|
|
||||||
|
serde_json::to_value(res).map_err(|_| internal_error("Cannot serialize path"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the entry of a template.
|
||||||
|
pub fn do_get_template_entry(&self, mut args: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
use crate::tools::package::{self, determine_latest_version, TemplateSource};
|
||||||
|
|
||||||
|
let from_source = get_arg!(args[0] as String);
|
||||||
|
let entry = self
|
||||||
|
.primary()
|
||||||
|
.steal(move |c| {
|
||||||
|
// Parse the package specification. If the user didn't specify the version,
|
||||||
|
// we try to figure it out automatically by downloading the package index
|
||||||
|
// or searching the disk.
|
||||||
|
let spec: PackageSpec = from_source
|
||||||
|
.parse()
|
||||||
|
.or_else(|err| {
|
||||||
|
// Try to parse without version, but prefer the error message of the
|
||||||
|
// normal package spec parsing if it fails.
|
||||||
|
let spec: VersionlessPackageSpec = from_source.parse().map_err(|_| err)?;
|
||||||
|
let version = determine_latest_version(&c.verse, &spec)?;
|
||||||
|
StrResult::Ok(spec.at(version))
|
||||||
|
})
|
||||||
|
.map_err(map_string_err("failed to parse package spec"))?;
|
||||||
|
|
||||||
|
let from_source = TemplateSource::Package(spec);
|
||||||
|
|
||||||
|
let entry = package::get_entry(&c.verse, from_source)
|
||||||
|
.map_err(map_string_err("failed to get template entry"))?;
|
||||||
|
|
||||||
|
ZResult::Ok(entry)
|
||||||
|
})
|
||||||
|
.and_then(|e| e)
|
||||||
|
.map_err(|e| invalid_params(format!("failed to determine template entry: {e}")))?;
|
||||||
|
|
||||||
|
let entry = String::from_utf8(entry.to_vec())
|
||||||
|
.map_err(|_| invalid_params("template entry is not a valid UTF-8 string"))?;
|
||||||
|
|
||||||
|
Ok(JsonValue::String(entry))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interact with the code context at the source file.
|
||||||
|
pub fn interact_code_context(&mut self, _arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
let queries = _arguments.into_iter().next().ok_or_else(|| {
|
||||||
|
invalid_params("The first parameter is not a valid code context query array")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct InteractCodeContextParams {
|
||||||
|
pub text_document: TextDocumentIdentifier,
|
||||||
|
pub query: Vec<tinymist_query::InteractCodeContextQuery>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let params: InteractCodeContextParams = serde_json::from_value(queries)
|
||||||
|
.map_err(|e| invalid_params(format!("Cannot parse code context queries: {e}")))?;
|
||||||
|
let path = as_path(params.text_document);
|
||||||
|
let query = params.query;
|
||||||
|
|
||||||
|
let res = run_query!(self.InteractCodeContext(path, query))?;
|
||||||
|
let res =
|
||||||
|
serde_json::to_value(res).map_err(|_| internal_error("Cannot serialize responses"))?;
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the trace data of the document.
|
||||||
|
pub fn get_document_trace(
|
||||||
|
&mut self,
|
||||||
|
req_id: RequestId,
|
||||||
|
mut args: Vec<JsonValue>,
|
||||||
|
) -> LspResult<Option<()>> {
|
||||||
|
let path = get_arg!(args[0] as PathBuf).into();
|
||||||
|
|
||||||
|
// get path to self program
|
||||||
|
let self_path = std::env::current_exe()
|
||||||
|
.map_err(|e| internal_error(format!("Cannot get typst compiler {e}")))?;
|
||||||
|
|
||||||
|
let thread = self.user_action_thread.clone();
|
||||||
|
let entry = self.config.compile.determine_entry(Some(path));
|
||||||
|
|
||||||
|
let res = self
|
||||||
|
.primary()
|
||||||
|
.steal(move |c| {
|
||||||
|
let verse = &c.verse;
|
||||||
|
|
||||||
|
// todo: rootless file
|
||||||
|
// todo: memory dirty file
|
||||||
|
let root = entry.root().ok_or_else(|| {
|
||||||
|
anyhow::anyhow!("root must be determined for trace, got {entry:?}")
|
||||||
|
})?;
|
||||||
|
let main = entry
|
||||||
|
.main()
|
||||||
|
.and_then(|e| e.vpath().resolve(&root))
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("main file must be resolved, got {entry:?}"))?;
|
||||||
|
|
||||||
|
if let Some(f) = thread {
|
||||||
|
f.send(UserActionRequest::Trace(
|
||||||
|
req_id,
|
||||||
|
TraceParams {
|
||||||
|
compiler_program: self_path,
|
||||||
|
root: root.as_ref().to_owned(),
|
||||||
|
main,
|
||||||
|
inputs: verse.inputs().as_ref().deref().clone(),
|
||||||
|
font_paths: verse.font_resolver.font_paths().to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.context("cannot send trace request")?;
|
||||||
|
} else {
|
||||||
|
bail!("user action thread is not available");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(()))
|
||||||
|
})
|
||||||
|
.context("cannot steal primary compiler");
|
||||||
|
|
||||||
|
let res = match res {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(res) => Err(res),
|
||||||
|
};
|
||||||
|
|
||||||
|
res.map_err(|e| internal_error(format!("could not get document trace: {e}")))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the metrics of the document.
|
||||||
|
pub fn get_document_metrics(&mut self, mut args: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
let path = get_arg!(args[0] as PathBuf);
|
||||||
|
|
||||||
|
let res = run_query!(self.DocumentMetrics(path))?;
|
||||||
|
let res = serde_json::to_value(res)
|
||||||
|
.map_err(|e| internal_error(format!("Cannot serialize response {e}")))?;
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the server info.
|
||||||
|
pub fn get_server_info(&mut self, _arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
let res = run_query!(self.ServerInfo())?;
|
||||||
|
|
||||||
|
let res = serde_json::to_value(res)
|
||||||
|
.map_err(|e| internal_error(format!("Cannot serialize response {e}")))?;
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get static resources with help of tinymist service, for example, a
|
||||||
|
/// static help pages for some typst function.
|
||||||
|
pub fn get_resources(&mut self, mut args: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
let path = get_arg!(args[0] as PathBuf);
|
||||||
|
|
||||||
|
let Some(handler) = self.resource_routes.get(path.as_path()) else {
|
||||||
|
error!("asked for unknown resource: {path:?}");
|
||||||
|
return Err(method_not_found());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Note our redirection will keep the first path argument in the args vec.
|
||||||
|
handler(self, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LanguageState {
|
||||||
|
pub fn get_resource_routes() -> ResourceMap {
|
||||||
|
macro_rules! resources_at {
|
||||||
|
($key: expr, Self::$method: ident) => {
|
||||||
|
(
|
||||||
|
Path::new($key).clean().as_path().into(),
|
||||||
|
resource_fn!(LspHandler<Vec<JsonValue>, JsonValue>, Self::$method, inputs),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceMap::from_iter([
|
||||||
|
resources_at!("/symbols", Self::resource_symbols),
|
||||||
|
resources_at!("/tutorial", Self::resource_tutoral),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the all valid symbols
|
||||||
|
pub fn resource_symbols(&self, _arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
let resp = self.get_symbol_resources();
|
||||||
|
resp.map_err(|e| internal_error(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get tutorial web page
|
||||||
|
pub fn resource_tutoral(&self, _arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||||
|
Err(method_not_found())
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,11 +11,11 @@ use tokio::sync::mpsc;
|
||||||
use typst_ts_core::ImmutPath;
|
use typst_ts_core::ImmutPath;
|
||||||
|
|
||||||
use crate::actor::editor::EditorActor;
|
use crate::actor::editor::EditorActor;
|
||||||
use crate::compiler_init::CompileConfig;
|
use crate::compile_init::CompileConfig;
|
||||||
use crate::harness::LspHost;
|
use crate::harness::LspHost;
|
||||||
use crate::utils::{try_, try_or};
|
use crate::utils::{try_, try_or};
|
||||||
use crate::world::ImmutDict;
|
use crate::world::ImmutDict;
|
||||||
use crate::{invalid_params, CompileFontOpts, LspResult, TypstLanguageServer};
|
use crate::{invalid_params, CompileFontOpts, LanguageState, LspResult};
|
||||||
|
|
||||||
// todo: svelte-language-server responds to a Goto Definition request with
|
// todo: svelte-language-server responds to a Goto Definition request with
|
||||||
// LocationLink[] even if the client does not report the
|
// LocationLink[] even if the client does not report the
|
||||||
|
@ -217,7 +217,7 @@ impl From<&InitializeParams> for ConstConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Init {
|
pub struct Init {
|
||||||
pub host: LspHost<TypstLanguageServer>,
|
pub host: LspHost<LanguageState>,
|
||||||
pub handle: tokio::runtime::Handle,
|
pub handle: tokio::runtime::Handle,
|
||||||
pub compile_opts: CompileFontOpts,
|
pub compile_opts: CompileFontOpts,
|
||||||
}
|
}
|
||||||
|
@ -241,7 +241,7 @@ impl Init {
|
||||||
pub fn initialize(
|
pub fn initialize(
|
||||||
mut self,
|
mut self,
|
||||||
params: InitializeParams,
|
params: InitializeParams,
|
||||||
) -> (TypstLanguageServer, LspResult<InitializeResult>) {
|
) -> (LanguageState, LspResult<InitializeResult>) {
|
||||||
// self.tracing_init();
|
// self.tracing_init();
|
||||||
|
|
||||||
// Initialize configurations
|
// Initialize configurations
|
||||||
|
@ -279,7 +279,7 @@ impl Init {
|
||||||
// Bootstrap server
|
// Bootstrap server
|
||||||
let (editor_tx, editor_rx) = mpsc::unbounded_channel();
|
let (editor_tx, editor_rx) = mpsc::unbounded_channel();
|
||||||
|
|
||||||
let mut service = TypstLanguageServer::new(
|
let mut service = LanguageState::new(
|
||||||
self.host.clone(),
|
self.host.clone(),
|
||||||
cc.clone(),
|
cc.clone(),
|
||||||
editor_tx,
|
editor_tx,
|
||||||
|
|
|
@ -1,7 +1,41 @@
|
||||||
pub mod lsp;
|
pub mod lsp;
|
||||||
|
pub mod lsp_cmd;
|
||||||
pub mod lsp_init;
|
pub mod lsp_init;
|
||||||
|
|
||||||
pub mod compiler;
|
pub mod compile;
|
||||||
pub mod compiler_init;
|
pub mod compile_cmd;
|
||||||
|
pub mod compile_init;
|
||||||
|
|
||||||
pub mod preview;
|
pub mod preview;
|
||||||
|
|
||||||
|
use serde_json::from_value;
|
||||||
|
|
||||||
|
/// Get a parsed command argument.
|
||||||
|
/// Return `INVALID_PARAMS` when no arg or parse failed.
|
||||||
|
macro_rules! get_arg {
|
||||||
|
($args:ident[$idx:expr] as $ty:ty) => {{
|
||||||
|
let arg = $args.get_mut($idx);
|
||||||
|
let arg = arg.and_then(|x| from_value::<$ty>(x.take()).ok());
|
||||||
|
match arg {
|
||||||
|
Some(v) => v,
|
||||||
|
None => {
|
||||||
|
let msg = concat!("expect ", stringify!($ty), "at args[", $idx, "]");
|
||||||
|
return Err(invalid_params(msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
use get_arg;
|
||||||
|
|
||||||
|
/// Get a parsed command argument or default if no arg.
|
||||||
|
/// Return `INVALID_PARAMS` when parse failed.
|
||||||
|
macro_rules! get_arg_or_default {
|
||||||
|
($args:ident[$idx:expr] as $ty:ty) => {{
|
||||||
|
if $idx >= $args.len() {
|
||||||
|
Default::default()
|
||||||
|
} else {
|
||||||
|
get_arg!($args[$idx] as $ty)
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
use get_arg_or_default;
|
||||||
|
|
|
@ -56,7 +56,7 @@ pub struct PreviewCliArgs {
|
||||||
mod compiler;
|
mod compiler;
|
||||||
use compiler::CompileServer;
|
use compiler::CompileServer;
|
||||||
|
|
||||||
use crate::compiler_init::CompileOnceArgs;
|
use crate::compile_init::CompileOnceArgs;
|
||||||
|
|
||||||
pub fn make_static_host(
|
pub fn make_static_host(
|
||||||
previewer: &Previewer,
|
previewer: &Previewer,
|
||||||
|
|
|
@ -16,7 +16,7 @@ use typst_preview::{CompilationHandle, CompileStatus};
|
||||||
|
|
||||||
use crate::actor::typ_client::CompileClientActorImpl;
|
use crate::actor::typ_client::CompileClientActorImpl;
|
||||||
use crate::actor::typ_server::CompileServerActor;
|
use crate::actor::typ_server::CompileServerActor;
|
||||||
use crate::compiler_init::CompileConfig;
|
use crate::compile_init::CompileConfig;
|
||||||
use crate::world::{LspCompilerFeat, LspWorld};
|
use crate::world::{LspCompilerFeat, LspWorld};
|
||||||
|
|
||||||
pub type CompileService<H> =
|
pub type CompileService<H> =
|
||||||
|
|
|
@ -15,9 +15,9 @@ use typst_ts_compiler::{
|
||||||
};
|
};
|
||||||
use typst_ts_core::{error::prelude::*, Bytes, Error, ImmutPath};
|
use typst_ts_core::{error::prelude::*, Bytes, Error, ImmutPath};
|
||||||
|
|
||||||
use crate::{actor::typ_client::CompileClientActor, compiler::CompileServer, TypstLanguageServer};
|
use crate::{actor::typ_client::CompileClientActor, compile::CompileState, LanguageState};
|
||||||
|
|
||||||
impl CompileServer {
|
impl CompileState {
|
||||||
/// Focus main file to some path.
|
/// Focus main file to some path.
|
||||||
pub fn do_change_entry(&mut self, new_entry: Option<ImmutPath>) -> Result<bool, Error> {
|
pub fn do_change_entry(&mut self, new_entry: Option<ImmutPath>) -> Result<bool, Error> {
|
||||||
self.compiler
|
self.compiler
|
||||||
|
@ -27,7 +27,7 @@ impl CompileServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypstLanguageServer {
|
impl LanguageState {
|
||||||
/// Pin the entry to the given path
|
/// Pin the entry to the given path
|
||||||
pub fn pin_entry(&mut self, new_entry: Option<ImmutPath>) -> Result<(), Error> {
|
pub fn pin_entry(&mut self, new_entry: Option<ImmutPath>) -> Result<(), Error> {
|
||||||
self.pinning = new_entry.is_some();
|
self.pinning = new_entry.is_some();
|
||||||
|
@ -98,11 +98,11 @@ pub struct MemoryFileMeta {
|
||||||
pub content: Source,
|
pub content: Source,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypstLanguageServer {
|
impl LanguageState {
|
||||||
fn update_source(&self, files: FileChangeSet) -> Result<(), Error> {
|
fn update_source(&self, files: FileChangeSet) -> Result<(), Error> {
|
||||||
let primary = Some(self.primary());
|
let primary = Some(self.primary());
|
||||||
let clients_to_notify =
|
let clients_to_notify =
|
||||||
(primary.into_iter()).chain(self.dedicates.iter().map(CompileServer::compiler));
|
(primary.into_iter()).chain(self.dedicates.iter().map(CompileState::compiler));
|
||||||
|
|
||||||
for client in clients_to_notify {
|
for client in clients_to_notify {
|
||||||
client.add_memory_changes(MemoryEvent::Update(files.clone()));
|
client.add_memory_changes(MemoryEvent::Update(files.clone()));
|
||||||
|
@ -240,7 +240,7 @@ macro_rules! query_world {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypstLanguageServer {
|
impl LanguageState {
|
||||||
pub fn query_source<T>(
|
pub fn query_source<T>(
|
||||||
&self,
|
&self,
|
||||||
path: ImmutPath,
|
path: ImmutPath,
|
||||||
|
@ -316,9 +316,9 @@ impl TypstLanguageServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompileServer {
|
impl CompileState {
|
||||||
pub fn query(&self, query: CompilerQueryRequest) -> anyhow::Result<CompilerQueryResponse> {
|
pub fn query(&self, query: CompilerQueryRequest) -> anyhow::Result<CompilerQueryResponse> {
|
||||||
let client = self.compiler.as_ref().unwrap();
|
let client = self.compiler.as_ref().unwrap();
|
||||||
TypstLanguageServer::query_on(client, query)
|
LanguageState::query_on(client, query)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue