mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-03 09:52:27 +00:00
fix: open exported files using rust's open
crate (#838)
* fix: open exported files using rust's `open` crate * feat: explorer as file opener on windows * dev: link related issue
This commit is contained in:
parent
263458a80b
commit
78a4117ec6
5 changed files with 110 additions and 37 deletions
|
@ -208,8 +208,12 @@ mod polymorphic {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OnExportRequest {
|
||||
/// The path of the document to export.
|
||||
pub path: PathBuf,
|
||||
/// The kind of the export.
|
||||
pub kind: ExportKind,
|
||||
/// Whether to open the exported file(s) after the export is done.
|
||||
pub open: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
//!
|
||||
//! The [`CompileHandler`] will push information to other actors.
|
||||
|
||||
use std::{collections::HashMap, ops::Deref, path::PathBuf, sync::Arc};
|
||||
use std::{collections::HashMap, ops::Deref, sync::Arc};
|
||||
|
||||
use anyhow::bail;
|
||||
use log::{error, info, trace};
|
||||
|
@ -33,7 +33,7 @@ use reflexo_typst::{
|
|||
use sync_lsp::{just_future, QueryFuture};
|
||||
use tinymist_query::{
|
||||
analysis::{Analysis, AnalysisRevLock, LocalContextGuard},
|
||||
CompilerQueryRequest, CompilerQueryResponse, DiagnosticsMap, ExportKind, SemanticRequest,
|
||||
CompilerQueryRequest, CompilerQueryResponse, DiagnosticsMap, OnExportRequest, SemanticRequest,
|
||||
ServerInfoResponse, StatefulRequest, VersionedDocument,
|
||||
};
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
|
@ -317,7 +317,8 @@ impl CompileClientActor {
|
|||
self.handle.export.change_config(config);
|
||||
}
|
||||
|
||||
pub fn on_export(&self, kind: ExportKind, path: PathBuf) -> QueryFuture {
|
||||
pub fn on_export(&self, req: OnExportRequest) -> QueryFuture {
|
||||
let OnExportRequest { path, kind, open } = req;
|
||||
let snap = self.snapshot()?;
|
||||
|
||||
let entry = self.config.determine_entry(Some(path.as_path().into()));
|
||||
|
@ -325,6 +326,22 @@ impl CompileClientActor {
|
|||
just_future(async move {
|
||||
let res = export.await?;
|
||||
|
||||
// See https://github.com/Myriad-Dreamin/tinymist/issues/837
|
||||
// Also see https://github.com/Byron/open-rs/issues/105
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let do_open = ::open::that_detached;
|
||||
#[cfg(target_os = "windows")]
|
||||
fn do_open(path: impl AsRef<std::ffi::OsStr>) -> std::io::Result<()> {
|
||||
::open::with_detached(path, "explorer")
|
||||
}
|
||||
|
||||
if let Some(Some(path)) = open.then_some(res.as_ref()) {
|
||||
log::info!("open with system default apps: {path:?}");
|
||||
if let Err(e) = do_open(path) {
|
||||
log::error!("failed to open with system default apps: {e}");
|
||||
};
|
||||
}
|
||||
|
||||
log::info!("CompileActor: on export end: {path:?} as {res:?}");
|
||||
Ok(tinymist_query::CompilerQueryResponse::OnExport(res))
|
||||
})
|
||||
|
|
|
@ -20,14 +20,19 @@ use super::server::*;
|
|||
use super::*;
|
||||
use crate::tool::package::InitTask;
|
||||
|
||||
/// See [`ExportKind`].
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
struct ExportOpts {
|
||||
creation_timestamp: Option<String>,
|
||||
fill: Option<String>,
|
||||
ppi: Option<f64>,
|
||||
#[serde(default)]
|
||||
page: PageSelection,
|
||||
/// Whether to open the exported file(s) after the export is done.
|
||||
open: Option<bool>,
|
||||
}
|
||||
|
||||
/// See [`ExportKind`].
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct QueryOpts {
|
||||
|
@ -38,6 +43,8 @@ struct QueryOpts {
|
|||
selector: String,
|
||||
field: Option<String>,
|
||||
one: Option<bool>,
|
||||
/// Whether to open the exported file(s) after the export is done.
|
||||
open: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
|
@ -61,22 +68,49 @@ impl LanguageState {
|
|||
self.config.compile.determine_creation_timestamp()
|
||||
};
|
||||
|
||||
self.export(req_id, ExportKind::Pdf { creation_timestamp }, args)
|
||||
self.export(
|
||||
req_id,
|
||||
ExportKind::Pdf { creation_timestamp },
|
||||
opts.open.unwrap_or_default(),
|
||||
args,
|
||||
)
|
||||
}
|
||||
|
||||
/// Export the current document as HTML file(s).
|
||||
pub fn export_html(&mut self, req_id: RequestId, args: Vec<JsonValue>) -> ScheduledResult {
|
||||
self.export(req_id, ExportKind::Html {}, args)
|
||||
pub fn export_html(&mut self, req_id: RequestId, mut args: Vec<JsonValue>) -> ScheduledResult {
|
||||
let opts = get_arg_or_default!(args[1] as ExportOpts);
|
||||
self.export(
|
||||
req_id,
|
||||
ExportKind::Html {},
|
||||
opts.open.unwrap_or_default(),
|
||||
args,
|
||||
)
|
||||
}
|
||||
|
||||
/// Export the current document as Markdown file(s).
|
||||
pub fn export_markdown(&mut self, req_id: RequestId, args: Vec<JsonValue>) -> ScheduledResult {
|
||||
self.export(req_id, ExportKind::Markdown {}, args)
|
||||
pub fn export_markdown(
|
||||
&mut self,
|
||||
req_id: RequestId,
|
||||
mut args: Vec<JsonValue>,
|
||||
) -> ScheduledResult {
|
||||
let opts = get_arg_or_default!(args[1] as ExportOpts);
|
||||
self.export(
|
||||
req_id,
|
||||
ExportKind::Markdown {},
|
||||
opts.open.unwrap_or_default(),
|
||||
args,
|
||||
)
|
||||
}
|
||||
|
||||
/// Export the current document as Text file(s).
|
||||
pub fn export_text(&mut self, req_id: RequestId, args: Vec<JsonValue>) -> ScheduledResult {
|
||||
self.export(req_id, ExportKind::Text {}, args)
|
||||
pub fn export_text(&mut self, req_id: RequestId, mut args: Vec<JsonValue>) -> ScheduledResult {
|
||||
let opts = get_arg_or_default!(args[1] as ExportOpts);
|
||||
self.export(
|
||||
req_id,
|
||||
ExportKind::Text {},
|
||||
opts.open.unwrap_or_default(),
|
||||
args,
|
||||
)
|
||||
}
|
||||
|
||||
/// Query the current document and export the result as JSON file(s).
|
||||
|
@ -93,6 +127,7 @@ impl LanguageState {
|
|||
pretty: opts.pretty.unwrap_or(true),
|
||||
one: opts.one.unwrap_or(false),
|
||||
},
|
||||
opts.open.unwrap_or_default(),
|
||||
args,
|
||||
)
|
||||
}
|
||||
|
@ -100,7 +135,12 @@ impl LanguageState {
|
|||
/// Export the current document as Svg file(s).
|
||||
pub fn export_svg(&mut self, req_id: RequestId, mut args: Vec<JsonValue>) -> ScheduledResult {
|
||||
let opts = get_arg_or_default!(args[1] as ExportOpts);
|
||||
self.export(req_id, ExportKind::Svg { page: opts.page }, args)
|
||||
self.export(
|
||||
req_id,
|
||||
ExportKind::Svg { page: opts.page },
|
||||
opts.open.unwrap_or_default(),
|
||||
args,
|
||||
)
|
||||
}
|
||||
|
||||
/// Export the current document as Png file(s).
|
||||
|
@ -113,6 +153,7 @@ impl LanguageState {
|
|||
ppi: opts.ppi,
|
||||
page: opts.page,
|
||||
},
|
||||
opts.open.unwrap_or_default(),
|
||||
args,
|
||||
)
|
||||
}
|
||||
|
@ -123,11 +164,12 @@ impl LanguageState {
|
|||
&mut self,
|
||||
req_id: RequestId,
|
||||
kind: ExportKind,
|
||||
open: bool,
|
||||
mut args: Vec<JsonValue>,
|
||||
) -> ScheduledResult {
|
||||
let path = get_arg!(args[0] as PathBuf);
|
||||
|
||||
run_query!(req_id, self.OnExport(path, kind))
|
||||
run_query!(req_id, self.OnExport(path, open, kind))
|
||||
}
|
||||
|
||||
/// Export a range of the current document as Ansi highlighted text.
|
||||
|
|
|
@ -24,7 +24,7 @@ use sync_lsp::*;
|
|||
use task::{CacheTask, ExportUserConfig, FormatTask, FormatUserConfig, UserActionTask};
|
||||
use tinymist_query::PageSelection;
|
||||
use tinymist_query::{
|
||||
lsp_to_typst, CompilerQueryRequest, CompilerQueryResponse, FoldRequestFeature, OnExportRequest,
|
||||
lsp_to_typst, CompilerQueryRequest, CompilerQueryResponse, FoldRequestFeature,
|
||||
PositionEncoding, SyntaxRequest,
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
|
@ -1040,7 +1040,7 @@ impl LanguageState {
|
|||
DocumentSymbol(req) => query_source!(self, DocumentSymbol, req)?,
|
||||
OnEnter(req) => query_source!(self, OnEnter, req)?,
|
||||
ColorPresentation(req) => CompilerQueryResponse::ColorPresentation(req.request()),
|
||||
OnExport(OnExportRequest { kind, path }) => return primary().on_export(kind, path),
|
||||
OnExport(req) => return primary().on_export(req),
|
||||
ServerInfo(_) => return primary().collect_server_info(),
|
||||
_ => return Self::query_on(primary(), is_pinning, query),
|
||||
})
|
||||
|
|
|
@ -500,6 +500,23 @@ async function commandShow(kind: "Pdf" | "Svg" | "Png", extraOpts?: any): Promis
|
|||
return;
|
||||
}
|
||||
|
||||
const conf = vscode.workspace.getConfiguration("tinymist");
|
||||
const openIn: string = conf.get("showExportFileIn", "editorTab");
|
||||
|
||||
// Telling the language server to open the file instead of using
|
||||
// ```
|
||||
// vscode.env.openExternal(exportUri);
|
||||
// ```
|
||||
// , which is buggy.
|
||||
//
|
||||
// See https://github.com/Myriad-Dreamin/tinymist/issues/837
|
||||
// Also see https://github.com/microsoft/vscode/issues/85930
|
||||
const openBySystemDefault = openIn === "systemDefault";
|
||||
if (openBySystemDefault) {
|
||||
extraOpts = extraOpts || {};
|
||||
extraOpts.open = true;
|
||||
}
|
||||
|
||||
// only create pdf if it does not exist yet
|
||||
const exportPath = await commandExport(kind, extraOpts);
|
||||
|
||||
|
@ -509,37 +526,30 @@ async function commandShow(kind: "Pdf" | "Svg" | "Png", extraOpts?: any): Promis
|
|||
return;
|
||||
}
|
||||
|
||||
const exportUri = Uri.file(exportPath);
|
||||
|
||||
// find and replace exportUri
|
||||
// todo: we may find them in tabs
|
||||
vscode.window.tabGroups;
|
||||
|
||||
let uriToFind = exportUri.toString();
|
||||
findTab: for (const editor of vscode.window.tabGroups.all) {
|
||||
for (const tab of editor.tabs) {
|
||||
if ((tab.input as any)?.uri?.toString() === uriToFind) {
|
||||
await vscode.window.tabGroups.close(tab, true);
|
||||
break findTab;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const conf = vscode.workspace.getConfiguration("tinymist");
|
||||
const openIn: string = conf.get("showExportFileIn", "editorTab");
|
||||
|
||||
switch (openIn) {
|
||||
default:
|
||||
case "editorTab":
|
||||
case "systemDefault":
|
||||
break;
|
||||
case "editorTab": {
|
||||
// find and replace exportUri
|
||||
const exportUri = Uri.file(exportPath);
|
||||
let uriToFind = exportUri.toString();
|
||||
findTab: for (const editor of vscode.window.tabGroups.all) {
|
||||
for (const tab of editor.tabs) {
|
||||
if ((tab.input as any)?.uri?.toString() === uriToFind) {
|
||||
await vscode.window.tabGroups.close(tab, true);
|
||||
break findTab;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// here we can be sure that the pdf exists
|
||||
await commands.executeCommand("vscode.open", exportUri, {
|
||||
viewColumn: ViewColumn.Beside,
|
||||
preserveFocus: true,
|
||||
} as vscode.TextDocumentShowOptions);
|
||||
break;
|
||||
case "systemDefault":
|
||||
await vscode.env.openExternal(exportUri);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue