feat: support vscode tasks for exporting html, md, and txt (#489)

* feat: support vscode tasks for exporting html, md, and txt

* chore: styling

* docs: update

* test: update snapshot
This commit is contained in:
Myriad-Dreamin 2024-08-05 01:00:25 +08:00 committed by GitHub
parent 140299f0ce
commit 56e20b2590
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 156 additions and 10 deletions

1
Cargo.lock generated
View file

@ -3777,6 +3777,7 @@ dependencies = [
"toml 0.8.14",
"tower-layer",
"tower-service",
"typlite",
"typst",
"typst-assets",
"typst-pdf",

View file

@ -105,6 +105,7 @@ typst-ts-compiler = { version = "0.5.0-rc5" }
typst-ts-svg-exporter = { version = "0.5.0-rc5" }
typstfmt_lib = { git = "https://github.com/astrale-sharp/typstfmt", tag = "0.2.7" }
typstyle = { version = "0.11.30", default-features = false }
typlite = { path = "./crates/typlite" }
# LSP
crossbeam-channel = "0.5.12"

View file

@ -150,6 +150,9 @@ mod polymorphic {
Pdf {
creation_timestamp: Option<chrono::DateTime<chrono::Utc>>,
},
Html {},
Markdown {},
Text {},
Svg {
page: PageSelection,
},
@ -172,6 +175,9 @@ mod polymorphic {
pub fn extension(&self) -> &str {
match self {
Self::Pdf { .. } => "pdf",
Self::Html { .. } => "html",
Self::Markdown { .. } => "md",
Self::Text { .. } => "txt",
Self::Svg { .. } => "svg",
Self::Png { .. } => "png",
}

View file

@ -15,6 +15,7 @@ repository.workspace = true
tinymist-assets = { workspace = true }
tinymist-query.workspace = true
tinymist-render.workspace = true
typlite.workspace = true
sync-lsp.workspace = true
chrono.workspace = true

View file

@ -44,6 +44,21 @@ impl LanguageState {
self.export(req_id, ExportKind::Pdf { creation_timestamp }, 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)
}
/// 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)
}
/// 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)
}
/// 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);

View file

@ -254,6 +254,9 @@ impl LanguageState {
.with_command_("tinymist.exportPdf", State::export_pdf)
.with_command_("tinymist.exportSvg", State::export_svg)
.with_command_("tinymist.exportPng", State::export_png)
.with_command_("tinymist.exportText", State::export_text)
.with_command_("tinymist.exportHtml", State::export_html)
.with_command_("tinymist.exportMarkdown", State::export_markdown)
.with_command("tinymist.doClearCache", State::clear_cache)
.with_command("tinymist.pinMain", State::pin_document)
.with_command("tinymist.focusMain", State::focus_document)

View file

@ -7,15 +7,18 @@ use anyhow::{bail, Context};
use once_cell::sync::Lazy;
use tinymist_query::{ExportKind, PageSelection};
use tokio::sync::mpsc;
use typlite::Typlite;
use typst::{
foundations::Smart,
layout::{Abs, Frame},
syntax::{ast, SyntaxNode},
visualize::Color,
World,
};
use typst_ts_compiler::{EntryReader, EntryState, TaskInputs};
use typst_ts_core::TypstDatetime;
use crate::tool::text::FullTextDigest;
use crate::{
actor::{
editor::EditorRequest,
@ -210,6 +213,16 @@ impl ExportConfig {
// todo: timestamp world.now()
typst_pdf::pdf(doc, Smart::Auto, timestamp)
}
Html {} => typst_ts_svg_exporter::render_svg_html(doc).into_bytes(),
Text {} => format!("{}", FullTextDigest(doc.clone())).into_bytes(),
Markdown {} => {
let main = artifact.world.main();
let conv = Typlite::new_with_src(main)
.convert()
.map_err(|e| anyhow::anyhow!("failed to convert to markdown: {e}"))?;
conv.as_bytes().to_owned()
}
Svg { page: First } => typst_svg::svg(first_frame()).into_bytes(),
Svg {
page: Merged { .. },

View file

@ -1,6 +1,7 @@
//! All the language tools provided by the `tinymist` crate.
pub mod package;
pub mod text;
pub mod word_count;
#[cfg(feature = "preview")]

View file

@ -0,0 +1,40 @@
//! Text export utilities.
use core::fmt;
use std::sync::Arc;
use typst_ts_core::TypstDocument;
/// A full text digest of a document.
pub struct FullTextDigest(pub Arc<TypstDocument>);
impl FullTextDigest {
fn export_frame(f: &mut fmt::Formatter<'_>, doc: &typst::layout::Frame) -> fmt::Result {
for (_, item) in doc.items() {
Self::export_item(f, item)?;
}
Ok(())
}
fn export_item(f: &mut fmt::Formatter<'_>, item: &typst::layout::FrameItem) -> fmt::Result {
use typst::introspection::Meta::*;
use typst::layout::FrameItem::*;
match item {
Group(g) => Self::export_frame(f, &g.frame),
Text(t) => f.write_str(t.text.as_str()),
#[cfg(not(feature = "no-content-hint"))]
Meta(ContentHint(c), _) => f.write_char(*c),
Meta(Link(..) | Elem(..) | Hide, _) | Shape(..) | Image(..) => Ok(()),
}
}
}
impl fmt::Display for FullTextDigest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for page in self.0.pages.iter() {
Self::export_frame(f, &page.frame)?;
}
Ok(())
}
}

View file

@ -15,7 +15,31 @@ Example:
"version": "2.0.0",
"tasks": [
{
"label": "Export SVG",
"label": "Export as Html",
"type": "typst",
"command": "export",
"export": {
"format": "html"
}
},
{
"label": "Export as Markdown",
"type": "typst",
"command": "export",
"export": {
"format": "markdown"
}
},
{
"label": "Export as Plain Text",
"type": "typst",
"command": "export",
"export": {
"format": "html"
}
},
{
"label": "Export as SVG",
"type": "typst",
"command": "export",
"export": {
@ -24,7 +48,7 @@ Example:
}
},
{
"label": "Export PNG",
"label": "Export as PNG",
"type": "typst",
"command": "export",
"export": {
@ -35,10 +59,11 @@ Example:
}
},
{
"label": "Export PNG and SVG",
"label": "Export as PNG and SVG",
"type": "typst",
"command": "export",
"export": {
// You can export multiple formats at once.
"format": ["png", "svg"],
// To make a visual effect, we set an obvious low resolution.
// For a nice result, you should set a higher resolution like 288.
@ -53,7 +78,11 @@ Example:
}
```
todo: documenting export options.
#let packages = json("/editors/vscode/package.json")
*todo: documenting export options.*
#raw(lang: "json", json.encode(packages.contributes.taskDefinitions, pretty: true), block: true)
After configuring the tasks, you can run them using the command palette.
+ Press `Ctrl+Shift+P` to open the command palette.
@ -66,5 +95,8 @@ You can call the following export commands.
- `tinymist.exportSvg`
- `tinymist.exportPng`
- `tinymist.exportPdf`
- `tinymist.exportHtml`
- `tinymist.exportMarkdown`
- `tinymist.exportText`
The first argument is the path to the file you want to export and the second argument is an object containing additional options.

View file

@ -108,12 +108,18 @@
"enum": [
"pdf",
"png",
"svg"
"svg",
"html",
"markdown",
"text"
],
"enumDescriptions": [
"PDF",
"PNG",
"SVG"
"SVG",
"HTML",
"Markdown",
"Plain Text"
],
"default": "pdf"
}

View file

@ -41,6 +41,15 @@ export const tinymist = {
exportPng(uri: string, extraOpts?: any) {
return doExport("tinymist.exportPng", uri, extraOpts);
},
exportHtml(uri: string, extraOpts?: any) {
return doExport("tinymist.exportHtml", uri, extraOpts);
},
exportMarkdown(uri: string, extraOpts?: any) {
return doExport("tinymist.exportMarkdown", uri, extraOpts);
},
exportText(uri: string, extraOpts?: any) {
return doExport("tinymist.exportText", uri, extraOpts);
},
};
function doExport(command: string, uri: string, extraOpts?: any): Promise<string> {

View file

@ -103,6 +103,24 @@ export async function callTypstExportCommand(): Promise<vscode.CustomExecution>
},
export: tinymist.exportSvg,
},
html: {
opts() {
return {};
},
export: tinymist.exportHtml,
},
markdown: {
opts() {
return {};
},
export: tinymist.exportMarkdown,
},
text: {
opts() {
return {};
},
export: tinymist.exportText,
},
};
return Promise.resolve({

View file

@ -374,7 +374,7 @@ fn e2e() {
});
let hash = replay_log(&tinymist_binary, &root.join("neovim"));
insta::assert_snapshot!(hash, @"siphash128_13:52e650bc01755a6389e98fbe14b4aeef");
insta::assert_snapshot!(hash, @"siphash128_13:a54cfbafc7174bc8465fc0adb99aa28d");
}
{
@ -385,7 +385,7 @@ fn e2e() {
});
let hash = replay_log(&tinymist_binary, &root.join("vscode"));
insta::assert_snapshot!(hash, @"siphash128_13:430e28c24ca45b226c12ce9bf6f55c91");
insta::assert_snapshot!(hash, @"siphash128_13:5c9f14019d55dc472177fff5f215fa40");
}
}