mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-11-26 22:04:56 +00:00
feat: support pdf export
This commit is contained in:
parent
7079360096
commit
e3f4588c96
6 changed files with 173 additions and 50 deletions
|
|
@ -12,7 +12,7 @@ use tokio::sync::{broadcast, watch};
|
||||||
use typst_ts_core::config::CompileOpts;
|
use typst_ts_core::config::CompileOpts;
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
render::PdfExportActor,
|
render::{PdfExportActor, PdfExportConfig},
|
||||||
typst::{create_server, CompileActor},
|
typst::{create_server, CompileActor},
|
||||||
};
|
};
|
||||||
use crate::TypstLanguageServer;
|
use crate::TypstLanguageServer;
|
||||||
|
|
@ -23,7 +23,19 @@ impl TypstLanguageServer {
|
||||||
let (render_tx, _) = broadcast::channel(10);
|
let (render_tx, _) = broadcast::channel(10);
|
||||||
|
|
||||||
// Run the PDF export actor before preparing cluster to avoid loss of events
|
// Run the PDF export actor before preparing cluster to avoid loss of events
|
||||||
tokio::spawn(PdfExportActor::new(doc_rx.clone(), render_tx.subscribe()).run());
|
tokio::spawn(
|
||||||
|
PdfExportActor::new(
|
||||||
|
doc_rx.clone(),
|
||||||
|
render_tx.subscribe(),
|
||||||
|
Some(PdfExportConfig {
|
||||||
|
path: entry
|
||||||
|
.as_ref()
|
||||||
|
.map(|e| e.clone().with_extension("pdf").into()),
|
||||||
|
mode: self.config.export_pdf,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.run(),
|
||||||
|
);
|
||||||
|
|
||||||
let roots = self.roots.clone();
|
let roots = self.roots.clone();
|
||||||
let opts = CompileOpts {
|
let opts = CompileOpts {
|
||||||
|
|
|
||||||
|
|
@ -6,45 +6,49 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use log::info;
|
use log::{error, info};
|
||||||
use tokio::sync::{
|
use tokio::sync::{
|
||||||
broadcast::{self, error::RecvError},
|
broadcast::{self, error::RecvError},
|
||||||
watch,
|
watch,
|
||||||
};
|
};
|
||||||
use typst::foundations::Smart;
|
use typst::foundations::Smart;
|
||||||
use typst_ts_core::TypstDocument;
|
use typst_ts_core::{ImmutPath, TypstDocument};
|
||||||
|
|
||||||
use crate::ExportPdfMode;
|
use crate::ExportPdfMode;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum RenderActorRequest {
|
pub enum RenderActorRequest {
|
||||||
Render,
|
OnTyped,
|
||||||
// ChangeConfig(PdfExportConfig),
|
OnSaved(PathBuf),
|
||||||
|
ChangeExportPath(Option<ImmutPath>),
|
||||||
|
ChangeConfig(PdfExportConfig),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PdfExportConfig {
|
pub struct PdfExportConfig {
|
||||||
path: PathBuf,
|
pub path: Option<ImmutPath>,
|
||||||
mode: ExportPdfMode,
|
pub mode: ExportPdfMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PdfExportActor {
|
pub struct PdfExportActor {
|
||||||
render_rx: broadcast::Receiver<RenderActorRequest>,
|
render_rx: broadcast::Receiver<RenderActorRequest>,
|
||||||
document: watch::Receiver<Option<Arc<TypstDocument>>>,
|
document: watch::Receiver<Option<Arc<TypstDocument>>>,
|
||||||
|
|
||||||
config: Option<PdfExportConfig>,
|
pub path: Option<ImmutPath>,
|
||||||
|
pub mode: ExportPdfMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PdfExportActor {
|
impl PdfExportActor {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
document: watch::Receiver<Option<Arc<TypstDocument>>>,
|
document: watch::Receiver<Option<Arc<TypstDocument>>>,
|
||||||
render_rx: broadcast::Receiver<RenderActorRequest>,
|
render_rx: broadcast::Receiver<RenderActorRequest>,
|
||||||
|
config: Option<PdfExportConfig>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
render_rx,
|
render_rx,
|
||||||
document,
|
document,
|
||||||
|
path: config.as_ref().and_then(|c| c.path.clone()),
|
||||||
config: None,
|
mode: config.map(|c| c.mode).unwrap_or(ExportPdfMode::Auto),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,28 +69,71 @@ impl PdfExportActor {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
info!("PdfRenderActor: received request: {req:?}", req = req);
|
||||||
match req {
|
match req {
|
||||||
RenderActorRequest::Render => {
|
RenderActorRequest::ChangeConfig(cfg) => {
|
||||||
let Some(document) = self.document.borrow().clone() else {
|
self.path = cfg.path;
|
||||||
info!("PdfRenderActor: document is not ready");
|
self.mode = cfg.mode;
|
||||||
continue;
|
}
|
||||||
};
|
RenderActorRequest::ChangeExportPath(cfg) => {
|
||||||
|
self.path = cfg;
|
||||||
if let Some(cfg) = self.config.as_ref() {
|
}
|
||||||
if cfg.mode == ExportPdfMode::OnType {
|
_ => {
|
||||||
self.export_pdf(&document, &cfg.path).await.unwrap();
|
self.check_mode_and_export(req).await;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// RenderActorRequest::ChangeConfig(config) => {
|
|
||||||
// self.config = Some(config);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn check_mode_and_export(&self, req: RenderActorRequest) {
|
||||||
|
let Some(document) = self.document.borrow().clone() else {
|
||||||
|
info!("PdfRenderActor: document is not ready");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let eq_mode = match req {
|
||||||
|
RenderActorRequest::OnTyped => ExportPdfMode::OnType,
|
||||||
|
RenderActorRequest::OnSaved(..) => ExportPdfMode::OnSave,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
info!("PdfRenderActor: check path {:?}", self.path);
|
||||||
|
if let Some(path) = self.path.as_ref() {
|
||||||
|
if (get_mode(self.mode) == eq_mode) || validate_document(&req, self.mode, &document) {
|
||||||
|
let Err(err) = self.export_pdf(&document, path).await else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
error!("PdfRenderActor: failed to export PDF: {err}", err = err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mode(mode: ExportPdfMode) -> ExportPdfMode {
|
||||||
|
if mode == ExportPdfMode::Auto {
|
||||||
|
return ExportPdfMode::Never;
|
||||||
|
}
|
||||||
|
|
||||||
|
mode
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_document(
|
||||||
|
req: &RenderActorRequest,
|
||||||
|
mode: ExportPdfMode,
|
||||||
|
document: &TypstDocument,
|
||||||
|
) -> bool {
|
||||||
|
info!(
|
||||||
|
"PdfRenderActor: validating document for export mode {mode:?} title is {title}",
|
||||||
|
title = document.title.is_some()
|
||||||
|
);
|
||||||
|
if mode == ExportPdfMode::OnDocumentHasTitle {
|
||||||
|
return document.title.is_some() && matches!(req, RenderActorRequest::OnSaved(..));
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn export_pdf(&self, doc: &TypstDocument, path: &Path) -> anyhow::Result<()> {
|
async fn export_pdf(&self, doc: &TypstDocument, path: &Path) -> anyhow::Result<()> {
|
||||||
// todo: Some(pdf_uri.as_str())
|
// todo: Some(pdf_uri.as_str())
|
||||||
// todo: timestamp world.now()
|
// todo: timestamp world.now()
|
||||||
|
|
@ -97,7 +144,6 @@ impl PdfExportActor {
|
||||||
std::fs::write(path, data).context("failed to export PDF")?;
|
std::fs::write(path, data).context("failed to export PDF")?;
|
||||||
|
|
||||||
info!("PDF export complete");
|
info!("PDF export complete");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ use std::{
|
||||||
sync::{Arc, Mutex as SyncMutex},
|
sync::{Arc, Mutex as SyncMutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use log::{debug, error, trace, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
|
use parking_lot::Mutex;
|
||||||
use tinymist_query::{
|
use tinymist_query::{
|
||||||
CompilerQueryRequest, CompilerQueryResponse, DiagnosticsMap, FoldRequestFeature,
|
CompilerQueryRequest, CompilerQueryResponse, DiagnosticsMap, FoldRequestFeature,
|
||||||
OnSaveExportRequest, PositionEncoding,
|
OnSaveExportRequest, PositionEncoding,
|
||||||
|
|
@ -34,8 +35,8 @@ use typst_ts_core::{
|
||||||
Bytes, Error, ImmutPath, TypstDocument, TypstWorld,
|
Bytes, Error, ImmutPath, TypstDocument, TypstWorld,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::compile::CompileActor as CompileActorInner;
|
|
||||||
use super::compile::CompileClient as TsCompileClient;
|
use super::compile::CompileClient as TsCompileClient;
|
||||||
|
use super::{compile::CompileActor as CompileActorInner, render::PdfExportConfig};
|
||||||
use crate::actor::render::RenderActorRequest;
|
use crate::actor::render::RenderActorRequest;
|
||||||
use crate::ConstConfig;
|
use crate::ConstConfig;
|
||||||
|
|
||||||
|
|
@ -62,11 +63,12 @@ pub fn create_server(
|
||||||
let root = compiler_driver.inner.world.root.as_ref().to_owned();
|
let root = compiler_driver.inner.world.root.as_ref().to_owned();
|
||||||
let handler: CompileHandler = compiler_driver.handler.clone();
|
let handler: CompileHandler = compiler_driver.handler.clone();
|
||||||
|
|
||||||
|
let ontyped_render_tx = render_tx.clone();
|
||||||
let driver = CompileExporter::new(compiler_driver).with_exporter(Box::new(
|
let driver = CompileExporter::new(compiler_driver).with_exporter(Box::new(
|
||||||
move |_w: &dyn TypstWorld, doc| {
|
move |_w: &dyn TypstWorld, doc| {
|
||||||
let _ = doc_sender.send(Some(doc));
|
let _ = doc_sender.send(Some(doc));
|
||||||
// todo: is it right that ignore zero broadcast receiver?
|
// todo: is it right that ignore zero broadcast receiver?
|
||||||
let _ = render_tx.send(RenderActorRequest::Render);
|
let _ = ontyped_render_tx.send(RenderActorRequest::OnTyped);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
|
@ -84,11 +86,17 @@ pub fn create_server(
|
||||||
|
|
||||||
current_runtime.spawn(server.spawn());
|
current_runtime.spawn(server.spawn());
|
||||||
|
|
||||||
let this = CompileActor::new(diag_group, cfg.position_encoding, handler, client);
|
let this = CompileActor::new(
|
||||||
|
diag_group,
|
||||||
|
cfg.position_encoding,
|
||||||
|
handler,
|
||||||
|
client,
|
||||||
|
render_tx,
|
||||||
|
);
|
||||||
|
|
||||||
// todo: less bug-prone code
|
// todo: less bug-prone code
|
||||||
if let Some(entry) = entry {
|
if let Some(entry) = entry {
|
||||||
this.entry.lock().unwrap().replace(entry.into());
|
this.entry.lock().replace(entry.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
this
|
this
|
||||||
|
|
@ -289,8 +297,9 @@ pub struct CompileActor {
|
||||||
diag_group: String,
|
diag_group: String,
|
||||||
position_encoding: PositionEncoding,
|
position_encoding: PositionEncoding,
|
||||||
handler: CompileHandler,
|
handler: CompileHandler,
|
||||||
entry: Arc<SyncMutex<Option<ImmutPath>>>,
|
entry: Arc<Mutex<Option<ImmutPath>>>,
|
||||||
pub inner: CompileClient<CompileHandler>,
|
pub inner: CompileClient<CompileHandler>,
|
||||||
|
render_tx: broadcast::Sender<RenderActorRequest>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: remove unsafe impl send
|
// todo: remove unsafe impl send
|
||||||
|
|
@ -356,7 +365,7 @@ impl CompileActor {
|
||||||
// todo: more robust rollback logic
|
// todo: more robust rollback logic
|
||||||
let entry = self.entry.clone();
|
let entry = self.entry.clone();
|
||||||
let should_change = {
|
let should_change = {
|
||||||
let mut entry = entry.lock().unwrap();
|
let mut entry = entry.lock();
|
||||||
let should_change = entry.as_ref().map(|e| e != &path).unwrap_or(true);
|
let should_change = entry.as_ref().map(|e| e != &path).unwrap_or(true);
|
||||||
let prev = entry.clone();
|
let prev = entry.clone();
|
||||||
*entry = Some(path.clone());
|
*entry = Some(path.clone());
|
||||||
|
|
@ -373,6 +382,12 @@ impl CompileActor {
|
||||||
next.display()
|
next.display()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
self.render_tx
|
||||||
|
.send(RenderActorRequest::ChangeExportPath(Some(
|
||||||
|
next.with_extension("pdf").into(),
|
||||||
|
)))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// todo
|
// todo
|
||||||
let res = self.steal(move |compiler| {
|
let res = self.steal(move |compiler| {
|
||||||
let root = compiler.compiler.world().workspace_root();
|
let root = compiler.compiler.world().workspace_root();
|
||||||
|
|
@ -386,7 +401,13 @@ impl CompileActor {
|
||||||
});
|
});
|
||||||
|
|
||||||
if res.is_err() {
|
if res.is_err() {
|
||||||
let mut entry = entry.lock().unwrap();
|
self.render_tx
|
||||||
|
.send(RenderActorRequest::ChangeExportPath(
|
||||||
|
prev.clone().map(|e| e.with_extension("pdf").into()),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut entry = entry.lock();
|
||||||
if *entry == Some(next) {
|
if *entry == Some(next) {
|
||||||
*entry = prev;
|
*entry = prev;
|
||||||
}
|
}
|
||||||
|
|
@ -396,11 +417,25 @@ impl CompileActor {
|
||||||
|
|
||||||
// todo: trigger recompile
|
// todo: trigger recompile
|
||||||
let files = FileChangeSet::new_inserts(vec![]);
|
let files = FileChangeSet::new_inserts(vec![]);
|
||||||
self.inner.add_memory_changes(MemoryEvent::Update(files))
|
self.inner.add_memory_changes(MemoryEvent::Update(files));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn change_export_pdf(&self, export_pdf: crate::ExportPdfMode) {
|
||||||
|
let entry = self.entry.lock();
|
||||||
|
let path = entry
|
||||||
|
.as_ref()
|
||||||
|
.map(|e| e.clone().with_extension("pdf").into());
|
||||||
|
let _ = self
|
||||||
|
.render_tx
|
||||||
|
.send(RenderActorRequest::ChangeConfig(PdfExportConfig {
|
||||||
|
path,
|
||||||
|
mode: export_pdf,
|
||||||
|
}))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceFileServer for CompileActor {
|
impl SourceFileServer for CompileActor {
|
||||||
|
|
@ -494,13 +529,15 @@ impl CompileActor {
|
||||||
position_encoding: PositionEncoding,
|
position_encoding: PositionEncoding,
|
||||||
handler: CompileHandler,
|
handler: CompileHandler,
|
||||||
inner: CompileClient<CompileHandler>,
|
inner: CompileClient<CompileHandler>,
|
||||||
|
render_tx: broadcast::Sender<RenderActorRequest>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
diag_group,
|
diag_group,
|
||||||
position_encoding,
|
position_encoding,
|
||||||
handler,
|
handler,
|
||||||
entry: Arc::new(SyncMutex::new(None)),
|
entry: Arc::new(Mutex::new(None)),
|
||||||
inner,
|
inner,
|
||||||
|
render_tx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -529,7 +566,10 @@ impl CompileActor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_save_export(&self, _path: PathBuf) -> anyhow::Result<()> {
|
fn on_save_export(&self, path: PathBuf) -> anyhow::Result<()> {
|
||||||
|
info!("CompileActor: on save export: {}", path.display());
|
||||||
|
let _ = self.render_tx.send(RenderActorRequest::OnSaved(path));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use log::info;
|
||||||
use lsp_types::*;
|
use lsp_types::*;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::{Map, Value as JsonValue};
|
use serde_json::{Map, Value as JsonValue};
|
||||||
|
|
@ -114,14 +115,18 @@ pub enum ExperimentalFormatterMode {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub enum ExportPdfMode {
|
pub enum ExportPdfMode {
|
||||||
/// Don't export PDF automatically.
|
#[default]
|
||||||
|
Auto,
|
||||||
|
/// Select best solution automatically. (Recommended)
|
||||||
Never,
|
Never,
|
||||||
/// Export PDF on saving the document, i.e. on `textDocument/didSave`
|
/// Export PDF on saving the document, i.e. on `textDocument/didSave`
|
||||||
/// events.
|
/// events.
|
||||||
#[default]
|
|
||||||
OnSave,
|
OnSave,
|
||||||
/// Export PDF on typing, i.e. on `textDocument/didChange` events.
|
/// Export PDF on typing, i.e. on `textDocument/didChange` events.
|
||||||
OnType,
|
OnType,
|
||||||
|
/// Export PDFs when a document has a title, which is useful to filter out
|
||||||
|
/// template files.
|
||||||
|
OnDocumentHasTitle,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The mode of semantic tokens.
|
/// The mode of semantic tokens.
|
||||||
|
|
@ -346,6 +351,11 @@ impl Init {
|
||||||
|
|
||||||
// Initialize configurations
|
// Initialize configurations
|
||||||
let cc = ConstConfig::from(¶ms);
|
let cc = ConstConfig::from(¶ms);
|
||||||
|
info!(
|
||||||
|
"initialized with const_config {const_config:?}",
|
||||||
|
const_config = cc
|
||||||
|
);
|
||||||
|
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
|
|
||||||
// Bootstrap server
|
// Bootstrap server
|
||||||
|
|
@ -364,6 +374,8 @@ impl Init {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info!("initialized with config {config:?}", config = config);
|
||||||
|
|
||||||
let cluster_actor = CompileClusterActor {
|
let cluster_actor = CompileClusterActor {
|
||||||
host: self.host.clone(),
|
host: self.host.clone(),
|
||||||
diag_rx,
|
diag_rx,
|
||||||
|
|
|
||||||
|
|
@ -788,16 +788,25 @@ impl TypstLanguageServer {
|
||||||
let uri = params.text_document.uri;
|
let uri = params.text_document.uri;
|
||||||
let path = uri.to_file_path().unwrap();
|
let path = uri.to_file_path().unwrap();
|
||||||
|
|
||||||
if self.config.export_pdf == ExportPdfMode::OnSave {
|
let _ = run_query!(self.OnSaveExport(path));
|
||||||
let _ = run_query!(self.OnSaveExport(path));
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_changed_configuration(&mut self, values: Map<String, JsonValue>) -> LspResult<()> {
|
fn on_changed_configuration(&mut self, values: Map<String, JsonValue>) -> LspResult<()> {
|
||||||
|
let export_pdf = self.config.export_pdf;
|
||||||
match self.config.update_by_map(&values) {
|
match self.config.update_by_map(&values) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
info!("new settings applied");
|
info!("new settings applied");
|
||||||
|
|
||||||
|
if export_pdf != self.config.export_pdf {
|
||||||
|
self.primary().change_export_pdf(self.config.export_pdf);
|
||||||
|
{
|
||||||
|
let m = self.main.lock();
|
||||||
|
if let Some(main) = m.as_ref() {
|
||||||
|
main.wait().change_export_pdf(self.config.export_pdf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("error applying new settings: {err}");
|
error!("error applying new settings: {err}");
|
||||||
|
|
@ -829,8 +838,8 @@ impl TypstLanguageServer {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let resp = serde_json::from_value(result).unwrap();
|
let resp: Vec<JsonValue> = serde_json::from_value(result).unwrap();
|
||||||
let _ = this.on_changed_configuration(resp);
|
let _ = this.on_changed_configuration(Config::values_to_map(resp));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,16 +27,20 @@
|
||||||
"title": "Export PDF",
|
"title": "Export PDF",
|
||||||
"description": "The extension can export PDFs of your Typst files. This setting controls whether this feature is enabled and how often it runs.",
|
"description": "The extension can export PDFs of your Typst files. This setting controls whether this feature is enabled and how often it runs.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "onSave",
|
"default": "auto",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
"auto",
|
||||||
"never",
|
"never",
|
||||||
"onSave",
|
"onSave",
|
||||||
"onType"
|
"onType",
|
||||||
|
"onDocumentHasTitle"
|
||||||
],
|
],
|
||||||
"enumDescriptions": [
|
"enumDescriptions": [
|
||||||
|
"Select best solution automatically. (Recommended)",
|
||||||
"Never export PDFs, you will manually run typst.",
|
"Never export PDFs, you will manually run typst.",
|
||||||
"Export PDFs when you save a file.",
|
"Export PDFs when you save a file.",
|
||||||
"Export PDFs as you type in a file."
|
"Export PDFs as you type in a file.",
|
||||||
|
"Export PDFs when a document has a title (and save a file), which is useful to filter out template files."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tinymist.rootPath": {
|
"tinymist.rootPath": {
|
||||||
|
|
@ -86,10 +90,10 @@
|
||||||
"title": "Enable Experimental Formatter",
|
"title": "Enable Experimental Formatter",
|
||||||
"description": "The extension can format Typst files using typstfmt (experimental).",
|
"description": "The extension can format Typst files using typstfmt (experimental).",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "off",
|
"default": "disable",
|
||||||
"enum": [
|
"enum": [
|
||||||
"off",
|
"disable",
|
||||||
"on"
|
"enable"
|
||||||
],
|
],
|
||||||
"enumDescriptions": [
|
"enumDescriptions": [
|
||||||
"Formatter is not activated.",
|
"Formatter is not activated.",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue