mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-03 17:58:17 +00:00
feat: fix show pdf and add export pdf code lens
This commit is contained in:
parent
b508843fc0
commit
e714ab462f
10 changed files with 218 additions and 55 deletions
41
crates/tinymist-query/src/code_lens.rs
Normal file
41
crates/tinymist-query/src/code_lens.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
use lsp_types::Command;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CodeLensRequest {
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
impl CodeLensRequest {
|
||||
pub fn request(
|
||||
self,
|
||||
world: &TypstSystemWorld,
|
||||
position_encoding: PositionEncoding,
|
||||
) -> Option<Vec<CodeLens>> {
|
||||
let source = get_suitable_source_in_workspace(world, &self.path).ok()?;
|
||||
|
||||
let doc_start = typst_to_lsp::range(0..0, &source, position_encoding);
|
||||
|
||||
let mut res = vec![];
|
||||
|
||||
let run_code_lens_cmd = |title: &str, args: Vec<JsonValue>| Command {
|
||||
title: title.to_string(),
|
||||
command: "tinymist.runCodeLens".to_string(),
|
||||
arguments: Some(args),
|
||||
};
|
||||
|
||||
let doc_lens = |title: &str, args: Vec<JsonValue>| CodeLens {
|
||||
range: doc_start,
|
||||
command: Some(run_code_lens_cmd(title, args)),
|
||||
data: None,
|
||||
};
|
||||
|
||||
res.push(doc_lens("Preview", vec!["preview".into()]));
|
||||
res.push(doc_lens("Preview in ..", vec!["preview-in".into()]));
|
||||
res.push(doc_lens("Export PDF", vec!["export-pdf".into()]));
|
||||
res.push(doc_lens("Export ..", vec!["export-as".into()]));
|
||||
|
||||
Some(res)
|
||||
}
|
||||
}
|
|
@ -31,6 +31,8 @@ pub(crate) mod prepare_rename;
|
|||
pub use prepare_rename::*;
|
||||
pub(crate) mod rename;
|
||||
pub use rename::*;
|
||||
pub(crate) mod code_lens;
|
||||
pub use code_lens::*;
|
||||
|
||||
pub mod lsp_typst_boundary;
|
||||
pub use lsp_typst_boundary::*;
|
||||
|
@ -41,6 +43,11 @@ mod polymorphic {
|
|||
use super::prelude::*;
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OnExportRequest {
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OnSaveExportRequest {
|
||||
pub path: PathBuf,
|
||||
|
@ -56,10 +63,12 @@ mod polymorphic {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CompilerQueryRequest {
|
||||
OnExport(OnExportRequest),
|
||||
OnSaveExport(OnSaveExportRequest),
|
||||
Hover(HoverRequest),
|
||||
GotoDefinition(GotoDefinitionRequest),
|
||||
InlayHint(InlayHintRequest),
|
||||
CodeLens(CodeLensRequest),
|
||||
Completion(CompletionRequest),
|
||||
SignatureHelp(SignatureHelpRequest),
|
||||
Rename(RenameRequest),
|
||||
|
@ -76,10 +85,12 @@ mod polymorphic {
|
|||
pub fn fold_feature(&self) -> FoldRequestFeature {
|
||||
use FoldRequestFeature::*;
|
||||
match self {
|
||||
CompilerQueryRequest::OnExport(..) => Mergable,
|
||||
CompilerQueryRequest::OnSaveExport(..) => Mergable,
|
||||
CompilerQueryRequest::Hover(..) => PinnedFirst,
|
||||
CompilerQueryRequest::GotoDefinition(..) => PinnedFirst,
|
||||
CompilerQueryRequest::InlayHint(..) => Unique,
|
||||
CompilerQueryRequest::CodeLens(..) => Unique,
|
||||
CompilerQueryRequest::Completion(..) => Mergable,
|
||||
CompilerQueryRequest::SignatureHelp(..) => PinnedFirst,
|
||||
CompilerQueryRequest::Rename(..) => Mergable,
|
||||
|
@ -95,10 +106,12 @@ mod polymorphic {
|
|||
|
||||
pub fn associated_path(&self) -> Option<&Path> {
|
||||
Some(match self {
|
||||
CompilerQueryRequest::OnExport(..) => return None,
|
||||
CompilerQueryRequest::OnSaveExport(req) => &req.path,
|
||||
CompilerQueryRequest::Hover(req) => &req.path,
|
||||
CompilerQueryRequest::GotoDefinition(req) => &req.path,
|
||||
CompilerQueryRequest::InlayHint(req) => &req.path,
|
||||
CompilerQueryRequest::CodeLens(req) => &req.path,
|
||||
CompilerQueryRequest::Completion(req) => &req.path,
|
||||
CompilerQueryRequest::SignatureHelp(req) => &req.path,
|
||||
CompilerQueryRequest::Rename(req) => &req.path,
|
||||
|
@ -115,10 +128,12 @@ mod polymorphic {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CompilerQueryResponse {
|
||||
OnExport(Option<PathBuf>),
|
||||
OnSaveExport(()),
|
||||
Hover(Option<Hover>),
|
||||
GotoDefinition(Option<GotoDefinitionResponse>),
|
||||
InlayHint(Option<Vec<InlayHint>>),
|
||||
CodeLens(Option<Vec<CodeLens>>),
|
||||
Completion(Option<CompletionResponse>),
|
||||
SignatureHelp(Option<SignatureHelp>),
|
||||
PrepareRename(Option<PrepareRenameResponse>),
|
||||
|
|
|
@ -9,12 +9,14 @@ pub use comemo::{Track, Tracked};
|
|||
pub use itertools::{Format, Itertools};
|
||||
pub use log::{error, trace};
|
||||
pub use lsp_types::{
|
||||
CompletionResponse, DiagnosticRelatedInformation, DocumentSymbol, DocumentSymbolResponse,
|
||||
Documentation, FoldingRange, GotoDefinitionResponse, Hover, InlayHint, Location as LspLocation,
|
||||
MarkupContent, MarkupKind, Position as LspPosition, PrepareRenameResponse, SelectionRange,
|
||||
SemanticTokens, SemanticTokensDelta, SemanticTokensFullDeltaResult, SemanticTokensResult,
|
||||
SignatureHelp, SignatureInformation, SymbolInformation, Url, WorkspaceEdit,
|
||||
CodeLens, CompletionResponse, DiagnosticRelatedInformation, DocumentSymbol,
|
||||
DocumentSymbolResponse, Documentation, FoldingRange, GotoDefinitionResponse, Hover, InlayHint,
|
||||
Location as LspLocation, MarkupContent, MarkupKind, Position as LspPosition,
|
||||
PrepareRenameResponse, SelectionRange, SemanticTokens, SemanticTokensDelta,
|
||||
SemanticTokensFullDeltaResult, SemanticTokensResult, SignatureHelp, SignatureInformation,
|
||||
SymbolInformation, Url, WorkspaceEdit,
|
||||
};
|
||||
pub use serde_json::Value as JsonValue;
|
||||
pub use typst::diag::{EcoString, FileError, FileResult, Tracepoint};
|
||||
pub use typst::foundations::{Func, ParamInfo, Value};
|
||||
pub use typst::syntax::{
|
||||
|
|
|
@ -32,17 +32,14 @@ use typst_ts_compiler::service::{
|
|||
WorkspaceProvider, WorldExporter,
|
||||
};
|
||||
|
||||
use crate::task::BorrowTask;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VersionedDocument {
|
||||
pub version: usize,
|
||||
pub document: Arc<TypstDocument>,
|
||||
}
|
||||
|
||||
/// A task that can be sent to the context (compiler thread)
|
||||
///
|
||||
/// The internal function will be dereferenced and called on the context.
|
||||
type BorrowTask<Ctx> = Box<dyn FnOnce(&mut Ctx) + Send + 'static>;
|
||||
|
||||
/// Interrupts for external sources
|
||||
enum ExternalInterrupt<Ctx> {
|
||||
/// Interrupted by task.
|
||||
|
|
|
@ -7,9 +7,10 @@ use std::{
|
|||
|
||||
use anyhow::Context;
|
||||
use log::{error, info};
|
||||
use parking_lot::Mutex;
|
||||
use tokio::sync::{
|
||||
broadcast::{self, error::RecvError},
|
||||
watch,
|
||||
oneshot, watch,
|
||||
};
|
||||
use typst::foundations::Smart;
|
||||
use typst_ts_core::{path::PathClean, ImmutPath, TypstDocument};
|
||||
|
@ -19,6 +20,8 @@ use crate::ExportPdfMode;
|
|||
#[derive(Debug, Clone)]
|
||||
pub enum RenderActorRequest {
|
||||
OnTyped,
|
||||
// todo: bad arch...
|
||||
DoExport(Arc<Mutex<Option<oneshot::Sender<Option<PathBuf>>>>>),
|
||||
OnSaved(PathBuf),
|
||||
ChangeExportPath(PdfPathVars),
|
||||
ChangeConfig(PdfExportConfig),
|
||||
|
@ -94,7 +97,20 @@ impl PdfExportActor {
|
|||
self.path = cfg.path;
|
||||
}
|
||||
_ => {
|
||||
self.check_mode_and_export(req).await;
|
||||
let sender = match &req {
|
||||
RenderActorRequest::DoExport(sender) => Some(sender.clone()),
|
||||
_ => None,
|
||||
};
|
||||
let resp = self.check_mode_and_export(req).await;
|
||||
if let Some(sender) = sender {
|
||||
let Some(sender) = sender.lock().take() else {
|
||||
error!("PdfRenderActor: sender is None");
|
||||
continue;
|
||||
};
|
||||
if let Err(e) = sender.send(resp) {
|
||||
error!("PdfRenderActor: failed to send response: {err:?}", err = e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,14 +118,15 @@ impl PdfExportActor {
|
|||
}
|
||||
}
|
||||
|
||||
async fn check_mode_and_export(&self, req: RenderActorRequest) {
|
||||
async fn check_mode_and_export(&self, req: RenderActorRequest) -> Option<PathBuf> {
|
||||
let Some(document) = self.document.borrow().clone() else {
|
||||
info!("PdfRenderActor: document is not ready");
|
||||
return;
|
||||
return None;
|
||||
};
|
||||
|
||||
let eq_mode = match req {
|
||||
RenderActorRequest::OnTyped => ExportPdfMode::OnType,
|
||||
RenderActorRequest::DoExport(..) => ExportPdfMode::OnSave,
|
||||
RenderActorRequest::OnSaved(..) => ExportPdfMode::OnSave,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
@ -119,11 +136,17 @@ impl PdfExportActor {
|
|||
self.path, self.substitute_pattern
|
||||
);
|
||||
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;
|
||||
};
|
||||
let should_do = matches!(req, RenderActorRequest::DoExport(..));
|
||||
let should_do = should_do || get_mode(self.mode) == eq_mode;
|
||||
let should_do = should_do || validate_document(&req, self.mode, &document);
|
||||
if should_do {
|
||||
return match self.export_pdf(&document, path).await {
|
||||
Ok(pdf) => Some(pdf),
|
||||
Err(err) => {
|
||||
error!("PdfRenderActor: failed to export PDF: {err}", err = err);
|
||||
None
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,9 +173,11 @@ impl PdfExportActor {
|
|||
|
||||
false
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
async fn export_pdf(&self, doc: &TypstDocument, path: &Path) -> anyhow::Result<()> {
|
||||
async fn export_pdf(&self, doc: &TypstDocument, path: &Path) -> anyhow::Result<PathBuf> {
|
||||
let Some(to) = substitute_path(&self.substitute_pattern, &self.root, path) else {
|
||||
return Err(anyhow::anyhow!("failed to substitute path"));
|
||||
};
|
||||
|
@ -176,10 +201,10 @@ impl PdfExportActor {
|
|||
// todo: timestamp world.now()
|
||||
let data = typst_pdf::pdf(doc, Smart::Auto, None);
|
||||
|
||||
std::fs::write(to, data).context("failed to export PDF")?;
|
||||
std::fs::write(&to, data).context("failed to export PDF")?;
|
||||
|
||||
info!("PDF export complete");
|
||||
Ok(())
|
||||
Ok(to)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@ use log::{debug, error, info, trace, warn};
|
|||
use parking_lot::Mutex;
|
||||
use tinymist_query::{
|
||||
CompilerQueryRequest, CompilerQueryResponse, DiagnosticsMap, FoldRequestFeature,
|
||||
OnSaveExportRequest, PositionEncoding,
|
||||
OnExportRequest, OnSaveExportRequest, PositionEncoding,
|
||||
};
|
||||
use tokio::sync::{broadcast, mpsc, watch};
|
||||
use tokio::sync::{broadcast, mpsc, oneshot, watch};
|
||||
use typst::{
|
||||
diag::{SourceDiagnostic, SourceResult},
|
||||
layout::Position,
|
||||
|
@ -579,6 +579,9 @@ impl CompileActor {
|
|||
assert!(query.fold_feature() != FoldRequestFeature::ContextFreeUnique);
|
||||
|
||||
match query {
|
||||
CompilerQueryRequest::OnExport(OnExportRequest { path }) => {
|
||||
Ok(CompilerQueryResponse::OnExport(self.on_export(path)?))
|
||||
}
|
||||
CompilerQueryRequest::OnSaveExport(OnSaveExportRequest { path }) => {
|
||||
self.on_save_export(path)?;
|
||||
Ok(CompilerQueryResponse::OnSaveExport(()))
|
||||
|
@ -586,6 +589,7 @@ impl CompileActor {
|
|||
Hover(req) => query_state!(self, Hover, req),
|
||||
GotoDefinition(req) => query_world!(self, GotoDefinition, req),
|
||||
InlayHint(req) => query_world!(self, InlayHint, req),
|
||||
CodeLens(req) => query_world!(self, CodeLens, req),
|
||||
Completion(req) => query_state!(self, Completion, req),
|
||||
SignatureHelp(req) => query_world!(self, SignatureHelp, req),
|
||||
Rename(req) => query_world!(self, Rename, req),
|
||||
|
@ -599,6 +603,40 @@ impl CompileActor {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn sync_render<Ret: Send + 'static>(&self, f: oneshot::Receiver<Ret>) -> ZResult<Ret> {
|
||||
// get current async handle
|
||||
if let Ok(e) = tokio::runtime::Handle::try_current() {
|
||||
// todo: remove blocking
|
||||
return std::thread::spawn(move || {
|
||||
e.block_on(f)
|
||||
.map_err(map_string_err("failed to sync_render"))
|
||||
})
|
||||
.join()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
f.blocking_recv()
|
||||
.map_err(map_string_err("failed to recv from sync_render"))
|
||||
}
|
||||
|
||||
fn on_export(&self, path: PathBuf) -> anyhow::Result<Option<PathBuf>> {
|
||||
info!("CompileActor: on export: {}", path.display());
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
let task = Arc::new(Mutex::new(Some(tx)));
|
||||
|
||||
self.render_tx
|
||||
.send(RenderActorRequest::DoExport(task))
|
||||
.map_err(map_string_err("failed to send to sync_render"))?;
|
||||
|
||||
let res: Option<PathBuf> = self.sync_render(rx)?;
|
||||
|
||||
info!("CompileActor: on export end: {path:?} as {res:?}");
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn on_save_export(&self, path: PathBuf) -> anyhow::Result<()> {
|
||||
info!("CompileActor: on save export: {}", path.display());
|
||||
let _ = self.render_tx.send(RenderActorRequest::OnSaved(path));
|
||||
|
|
|
@ -585,6 +585,9 @@ impl Init {
|
|||
}),
|
||||
document_formatting_provider,
|
||||
inlay_hint_provider: Some(OneOf::Left(true)),
|
||||
code_lens_provider: Some(CodeLensOptions {
|
||||
resolve_provider: Some(false),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
mod actor;
|
||||
pub mod init;
|
||||
mod query;
|
||||
mod task;
|
||||
pub mod transport;
|
||||
|
||||
use core::fmt;
|
||||
|
@ -210,7 +211,7 @@ type LspResult<Res> = Result<Res, ResponseError>;
|
|||
type LspMethod<Res> = fn(srv: &mut TypstLanguageServer, args: JsonValue) -> LspResult<Res>;
|
||||
type LspHandler<Req, Res> = fn(srv: &mut TypstLanguageServer, args: Req) -> LspResult<Res>;
|
||||
|
||||
type ExecuteCmdMap = HashMap<&'static str, LspHandler<Vec<JsonValue>, ()>>;
|
||||
type ExecuteCmdMap = HashMap<&'static str, LspHandler<Vec<JsonValue>, JsonValue>>;
|
||||
type NotifyCmdMap = HashMap<&'static str, LspMethod<()>>;
|
||||
type RegularCmdMap = HashMap<&'static str, LspMethod<JsonValue>>;
|
||||
|
||||
|
@ -344,11 +345,12 @@ impl TypstLanguageServer {
|
|||
request_fn!(SemanticTokensFullRequest, Self::semantic_tokens_full),
|
||||
request_fn!(SemanticTokensFullDeltaRequest, Self::semantic_tokens_full_delta),
|
||||
request_fn!(DocumentSymbolRequest, Self::document_symbol),
|
||||
request_fn!(InlayHintRequest, Self::inlay_hint),
|
||||
// Sync for low latency
|
||||
request_fn!(SelectionRangeRequest, Self::selection_range),
|
||||
// latency insensitive
|
||||
request_fn!(InlayHintRequest, Self::inlay_hint),
|
||||
request_fn!(HoverRequest, Self::hover),
|
||||
request_fn!(CodeLensRequest, Self::code_lens),
|
||||
request_fn!(FoldingRangeRequest, Self::folding_range),
|
||||
request_fn!(SignatureHelpRequest, Self::signature_help),
|
||||
request_fn!(PrepareRenameRequest, Self::prepare_rename),
|
||||
|
@ -615,13 +617,13 @@ impl TypstLanguageServer {
|
|||
($key: expr, Self::$method: ident) => {
|
||||
(
|
||||
$key,
|
||||
exec_fn!(LspHandler<Vec<JsonValue>, ()>, Self::$method, inputs),
|
||||
exec_fn!(LspHandler<Vec<JsonValue>, JsonValue>, Self::$method, inputs),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
ExecuteCmdMap::from_iter([
|
||||
redirected_command!("tinymist.doPdfExport", Self::export_pdf),
|
||||
redirected_command!("tinymist.exportPdf", Self::export_pdf),
|
||||
redirected_command!("tinymist.doClearCache", Self::clear_cache),
|
||||
redirected_command!("tinymist.doPinMain", Self::pin_main),
|
||||
redirected_command!("tinymist.doActivateDoc", Self::activate_doc),
|
||||
|
@ -640,8 +642,7 @@ impl TypstLanguageServer {
|
|||
return Err(method_not_found());
|
||||
};
|
||||
|
||||
handler(self, arguments)?;
|
||||
Ok(Some(JsonValue::Null))
|
||||
Ok(Some(handler(self, arguments)?))
|
||||
}
|
||||
|
||||
/// Export the current document as a PDF file. The client is responsible for
|
||||
|
@ -649,7 +650,7 @@ impl TypstLanguageServer {
|
|||
///
|
||||
/// # Errors
|
||||
/// Errors if a provided file URI is not a valid file URI.
|
||||
pub fn export_pdf(&self, arguments: Vec<JsonValue>) -> LspResult<()> {
|
||||
pub fn export_pdf(&self, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||
if arguments.is_empty() {
|
||||
return Err(invalid_params("Missing file URI argument"));
|
||||
}
|
||||
|
@ -662,25 +663,26 @@ impl TypstLanguageServer {
|
|||
.to_file_path()
|
||||
.map_err(|_| invalid_params("URI is not a file URI"))?;
|
||||
|
||||
let _ = run_query!(self.OnSaveExport(path));
|
||||
let res = run_query!(self.OnExport(path))?;
|
||||
let res = serde_json::to_value(res).map_err(|_| internal_error("Cannot serialize path"))?;
|
||||
|
||||
Ok(())
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Clear all cached resources.
|
||||
///
|
||||
/// # Errors
|
||||
/// Errors if the cache could not be cleared.
|
||||
pub fn clear_cache(&self, _arguments: Vec<JsonValue>) -> LspResult<()> {
|
||||
pub fn clear_cache(&self, _arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||
comemo::evict(0);
|
||||
Ok(())
|
||||
Ok(JsonValue::Null)
|
||||
}
|
||||
|
||||
/// Pin main file to some path.
|
||||
///
|
||||
/// # Errors
|
||||
/// Errors if a provided file URI is not a valid file URI.
|
||||
pub fn pin_main(&mut self, arguments: Vec<JsonValue>) -> LspResult<()> {
|
||||
pub fn pin_main(&mut self, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||
let Some(file_uri) = arguments.first().and_then(|v| v.as_str()) else {
|
||||
return Err(invalid_params("Missing file path as the first argument"));
|
||||
};
|
||||
|
@ -728,7 +730,7 @@ impl TypstLanguageServer {
|
|||
})?;
|
||||
|
||||
info!("main file pinned: {main_url:?}", main_url = file_uri);
|
||||
Ok(())
|
||||
Ok(JsonValue::Null)
|
||||
}
|
||||
|
||||
/// Change the actived document.
|
||||
|
@ -736,7 +738,7 @@ impl TypstLanguageServer {
|
|||
/// # Errors
|
||||
/// Errors if a provided file URI is not a valid path string.
|
||||
/// Errors if the document could not be activated.
|
||||
pub fn activate_doc(&self, arguments: Vec<JsonValue>) -> LspResult<()> {
|
||||
pub fn activate_doc(&self, arguments: Vec<JsonValue>) -> LspResult<JsonValue> {
|
||||
let Some(path) = arguments.first() else {
|
||||
return Err(invalid_params("Missing path argument"));
|
||||
};
|
||||
|
@ -762,7 +764,7 @@ impl TypstLanguageServer {
|
|||
// })?;
|
||||
|
||||
info!("active document set: {path:?}", path = path);
|
||||
Ok(())
|
||||
Ok(JsonValue::Null)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -928,6 +930,11 @@ impl TypstLanguageServer {
|
|||
run_query!(self.InlayHint(path, range))
|
||||
}
|
||||
|
||||
fn code_lens(&self, params: CodeLensParams) -> LspResult<Option<Vec<CodeLens>>> {
|
||||
let path = as_path(params.text_document);
|
||||
run_query!(self.CodeLens(path))
|
||||
}
|
||||
|
||||
fn completion(&self, params: CompletionParams) -> LspResult<Option<CompletionResponse>> {
|
||||
let (path, position) = as_path_pos(params.text_document_position);
|
||||
let explicit = params
|
||||
|
|
4
crates/tinymist/src/task.rs
Normal file
4
crates/tinymist/src/task.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
/// A task that can be sent to the context (compiler/render thread)
|
||||
///
|
||||
/// The internal function will be dereferenced and called on the context.
|
||||
pub type BorrowTask<Ctx> = Box<dyn FnOnce(&mut Ctx) + Send + 'static>;
|
|
@ -84,6 +84,9 @@ async function startClient(context: ExtensionContext): Promise<void> {
|
|||
);
|
||||
context.subscriptions.push(commands.registerCommand("tinymist.showPdf", commandShowPdf));
|
||||
context.subscriptions.push(commands.registerCommand("tinymist.clearCache", commandClearCache));
|
||||
context.subscriptions.push(
|
||||
commands.registerCommand("tinymist.runCodeLens", commandRunCodeLens)
|
||||
);
|
||||
|
||||
return client.start();
|
||||
}
|
||||
|
@ -149,7 +152,7 @@ function validateServer(path: string): { valid: true } | { valid: false; message
|
|||
}
|
||||
}
|
||||
|
||||
async function commandExportCurrentPdf(): Promise<void> {
|
||||
async function commandExportCurrentPdf(): Promise<string | undefined> {
|
||||
const activeEditor = window.activeTextEditor;
|
||||
if (activeEditor === undefined) {
|
||||
return;
|
||||
|
@ -157,10 +160,15 @@ async function commandExportCurrentPdf(): Promise<void> {
|
|||
|
||||
const uri = activeEditor.document.uri.toString();
|
||||
|
||||
await client?.sendRequest("workspace/executeCommand", {
|
||||
command: "tinymist.doPdfExport",
|
||||
const res = await client?.sendRequest<string | null>("workspace/executeCommand", {
|
||||
command: "tinymist.exportPdf",
|
||||
arguments: [uri],
|
||||
});
|
||||
console.log("export pdf", res);
|
||||
if (res === null) {
|
||||
return undefined;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -173,22 +181,19 @@ async function commandShowPdf(): Promise<void> {
|
|||
return;
|
||||
}
|
||||
|
||||
// todo: this is wrong
|
||||
const uri = activeEditor.document.uri;
|
||||
// change the file extension to `.pdf` as we want to open the pdf file
|
||||
// and not the currently opened `.typ` file.
|
||||
const n = uri.toString().lastIndexOf(".");
|
||||
const pdf_uri = Uri.parse(uri.toString().slice(0, n) + ".pdf");
|
||||
|
||||
try {
|
||||
await workspace.fs.stat(pdf_uri);
|
||||
} catch {
|
||||
// only create pdf if it does not exist yet
|
||||
await commandExportCurrentPdf();
|
||||
} finally {
|
||||
// here we can be sure that the pdf exists
|
||||
await commands.executeCommand("vscode.open", pdf_uri, ViewColumn.Beside);
|
||||
const pdfPath = await commandExportCurrentPdf();
|
||||
|
||||
if (pdfPath === undefined) {
|
||||
// show error message
|
||||
await window.showErrorMessage("Failed to create PDF");
|
||||
return;
|
||||
}
|
||||
|
||||
const pdfUri = Uri.file(pdfPath);
|
||||
|
||||
// here we can be sure that the pdf exists
|
||||
await commands.executeCommand("vscode.open", pdfUri, ViewColumn.Beside);
|
||||
}
|
||||
|
||||
async function commandClearCache(): Promise<void> {
|
||||
|
@ -233,3 +238,29 @@ async function commandActivateDoc(editor: TextEditor | undefined): Promise<void>
|
|||
arguments: [editor?.document.uri.fsPath],
|
||||
});
|
||||
}
|
||||
|
||||
async function commandRunCodeLens(...args: string[]): Promise<void> {
|
||||
console.log("run code lens", args);
|
||||
if (args.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args[0]) {
|
||||
case "preview": {
|
||||
break;
|
||||
}
|
||||
case "preview-in": {
|
||||
break;
|
||||
}
|
||||
case "export-pdf": {
|
||||
await commandShowPdf();
|
||||
break;
|
||||
}
|
||||
case "export-as": {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.error("unknown code lens command", args[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue