From a208689b8d11a2937dfc95a2e3ead7bd36c87098 Mon Sep 17 00:00:00 2001 From: Wenzhuo Liu Date: Sun, 6 Aug 2023 17:41:51 +0800 Subject: [PATCH] rebrand to typst-preview and adjust cli args (#72) --- .github/workflows/release.yml | 20 +++--- .vscode/tasks.json | 6 +- Cargo.lock | 70 +++++++++---------- Cargo.toml | 4 +- addons/vscode/package.json | 8 +-- addons/vscode/src/extension.ts | 36 +++++----- .../vscode/src/test/suite/extension.test.ts | 34 ++++----- src/args.rs | 49 +++++-------- src/main.rs | 49 ++++++------- 9 files changed, 129 insertions(+), 147 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index aa5ea712..4a9c01ad 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -81,9 +81,9 @@ jobs: sudo apt-get install gcc-arm-linux-gnueabihf - shell: pwsh run: | - cargo build --release -p typst-ws --target ${{ matrix.rust-target }} - cp "target/${{ matrix.rust-target }}/release/typst-ws$(If ('${{ matrix.platform }}' -eq 'win32') { '.exe' } else { '' } )" "addons/vscode/out/" - cp "target/${{ matrix.rust-target }}/release/typst-ws$(If ('${{ matrix.platform }}' -eq 'win32') { '.exe' } else { '' } )" "typst-ws-${{ env.target }}$(If ('${{ matrix.platform }}' -eq 'win32') { '.exe' } else { '' } )" + cargo build --release -p typst-preview --target ${{ matrix.rust-target }} + cp "target/${{ matrix.rust-target }}/release/typst-preview$(If ('${{ matrix.platform }}' -eq 'win32') { '.exe' } else { '' } )" "addons/vscode/out/" + cp "target/${{ matrix.rust-target }}/release/typst-preview$(If ('${{ matrix.platform }}' -eq 'win32') { '.exe' } else { '' } )" "typst-preview-${{ env.target }}$(If ('${{ matrix.platform }}' -eq 'win32') { '.exe' } else { '' } )" - shell: pwsh run: yarn run package -- --target ${{ env.target }} -o typst-preview-${{ env.target }}.vsix working-directory: ./addons/vscode @@ -93,8 +93,8 @@ jobs: path: addons/vscode/typst-preview-${{ env.target }}.vsix - uses: actions/upload-artifact@v2 with: - name: typst-ws-${{ env.target }} - path: typst-ws-${{ env.target }}${{ fromJSON('["", ".exe"]')[matrix.platform == 'win32'] }} + name: typst-preview-${{ env.target }} + path: typst-preview-${{ env.target }}${{ fromJSON('["", ".exe"]')[matrix.platform == 'win32'] }} build_alpine: name: build (x86_64-unknown-linux-musl) @@ -122,10 +122,10 @@ jobs: working-directory: ./addons/vscode - name: build server binary run: | - cargo build --release -p typst-ws --target $RUST_TARGET + cargo build --release -p typst-preview --target $RUST_TARGET mkdir -p addons/vscode/out - cp "target/$RUST_TARGET/release/typst-ws" "addons/vscode/out/" - cp "target/$RUST_TARGET/release/typst-ws" "typst-ws-alpine-x64" + cp "target/$RUST_TARGET/release/typst-preview" "addons/vscode/out/" + cp "target/$RUST_TARGET/release/typst-preview" "typst-preview-alpine-x64" - name: package extension run: yarn run package -- --target ${{ env.target }} -o typst-preview-${{ env.target }}.vsix working-directory: ./addons/vscode @@ -135,8 +135,8 @@ jobs: path: addons/vscode/typst-preview-${{ env.target }}.vsix - uses: actions/upload-artifact@v2 with: - name: typst-ws-${{ env.target }} - path: typst-ws-${{ env.target }} + name: typst-preview-${{ env.target }} + path: typst-preview-${{ env.target }} release: runs-on: ubuntu-latest diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 7cdb538a..35696c37 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -37,21 +37,21 @@ "windows": { "command": "cp", "args": [ - "${workspaceFolder}\\target\\release\\typst-ws.exe", + "${workspaceFolder}\\target\\release\\typst-preview.exe", "${workspaceFolder}\\addons\\vscode\\out\\" ] }, "linux": { "command": "cp", "args": [ - "${workspaceFolder}/target/release/typst-ws", + "${workspaceFolder}/target/release/typst-preview", "${workspaceFolder}/addons/vscode/out/" ] }, "osx": { "command": "cp", "args": [ - "${workspaceFolder}/target/release/typst-ws", + "${workspaceFolder}/target/release/typst-preview", "${workspaceFolder}/addons/vscode/out/" ] } diff --git a/Cargo.lock b/Cargo.lock index 21054627..bc068e19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3230,6 +3230,41 @@ dependencies = [ "syn 2.0.16", ] +[[package]] +name = "typst-preview" +version = "0.6.3" +dependencies = [ + "chrono", + "clap", + "clap_complete", + "clap_mangen", + "codespan-reporting", + "comemo", + "dirs", + "elsa", + "env_logger", + "futures", + "hyper", + "log", + "memmap2 0.5.10", + "notify", + "once_cell", + "open", + "same-file", + "serde", + "serde_json", + "siphasher", + "tiny-skia", + "tokio", + "tokio-tungstenite", + "typst", + "typst-library", + "typst-ts-compiler", + "typst-ts-core", + "typst-ts-svg-exporter", + "walkdir", +] + [[package]] name = "typst-ts-compiler" version = "0.3.0" @@ -3310,41 +3345,6 @@ dependencies = [ "typst-ts-core", ] -[[package]] -name = "typst-ws" -version = "0.6.3" -dependencies = [ - "chrono", - "clap", - "clap_complete", - "clap_mangen", - "codespan-reporting", - "comemo", - "dirs", - "elsa", - "env_logger", - "futures", - "hyper", - "log", - "memmap2 0.5.10", - "notify", - "once_cell", - "open", - "same-file", - "serde", - "serde_json", - "siphasher", - "tiny-skia", - "tokio", - "tokio-tungstenite", - "typst", - "typst-library", - "typst-ts-compiler", - "typst-ts-core", - "typst-ts-svg-exporter", - "walkdir", -] - [[package]] name = "unic-langid" version = "0.9.1" diff --git a/Cargo.toml b/Cargo.toml index 6650cb6c..8f6e7478 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "typst-ws" +name = "typst-preview" version = "0.6.3" authors = ["The Typst Project Developers"] edition = "2021" [[bin]] -name = "typst-ws" +name = "typst-preview" path = "src/main.rs" test = false doctest = false diff --git a/addons/vscode/package.json b/addons/vscode/package.json index 1001521f..4b2ca10e 100644 --- a/addons/vscode/package.json +++ b/addons/vscode/package.json @@ -30,13 +30,13 @@ { "command": "typst-preview.preview", "title": "Typst Preview: Preview current file", - "description": "Launch typst-ws server", + "description": "Launch typst-preview server", "icon": "$(open-preview)" }, { "command": "typst-preview.browser", "title": "Typst Preview: Preview current file in browser", - "description": "Launch typst-ws server and open the preview in your browser", + "description": "Launch typst-preview server and open the preview in your browser", "icon": "$(open-preview)" }, { @@ -60,7 +60,7 @@ "typst-preview.executable": { "type": "string", "default": "", - "description": "Path to typst-ws executable. If not set, the extension will use bundled typst-ws. If bundled binary is not found, it will use typst-ws installed in PATH" + "description": "Path to typst-preview executable. If not set, the extension will use bundled typst-preview. If bundled binary is not found, it will use typst-preview installed in PATH" }, "typst-preview.fontPaths": { "type": "array", @@ -70,7 +70,7 @@ "description": "Absolute path to a directory or file containing font assets." }, "default": [], - "description": "List of *additional* paths to font assets used by typst-ws." + "description": "List of *additional* paths to font assets used by typst-preview." }, "typst-preview.refresh": { "title": "Refresh preview", diff --git a/addons/vscode/src/extension.ts b/addons/vscode/src/extension.ts index 00e3112f..a5ede996 100644 --- a/addons/vscode/src/extension.ts +++ b/addons/vscode/src/extension.ts @@ -15,9 +15,9 @@ async function loadHTMLFile(context: vscode.ExtensionContext, relativePath: stri return fileContents; } -export async function getTypstWsPath(extensionPath?: string): Promise { - const state = getTypstWsPath as unknown as any; - (!state.BINARY_NAME) && (state.BINARY_NAME = "typst-ws"); +export async function getCliPath(extensionPath?: string): Promise { + const state = getCliPath as unknown as any; + (!state.BINARY_NAME) && (state.BINARY_NAME = "typst-preview"); (!state.getConfig) && (state.getConfig = ( () => vscode.workspace.getConfiguration().get('typst-preview.executable'))); @@ -25,7 +25,7 @@ export async function getTypstWsPath(extensionPath?: string): Promise { const configPath = state.getConfig(); if (state.bundledPath === bundledPath && state.configPath === configPath) { - // console.log('getTypstWsPath cached', state.resolved); + // console.log('getCliPath cached', state.resolved); return state.resolved; } state.bundledPath = bundledPath; @@ -46,7 +46,7 @@ export async function getTypstWsPath(extensionPath?: string): Promise { }; const resolvePath = async () => { - console.log('getTypstWsPath resolving', bundledPath, configPath); + console.log('getCliPath resolving', bundledPath, configPath); if (configPath?.length) { return configPath; @@ -65,12 +65,12 @@ export async function getTypstWsPath(extensionPath?: string): Promise { return (state.resolved = await resolvePath()); } -export function getTypstWsFontArgs(fontPaths?: string[]): string[] { +export function getCliFontArgs(fontPaths?: string[]): string[] { return (fontPaths ?? []).flatMap((fontPath) => ["--font-path", fontPath]); } -export function codeGetTypstWsFontArgs(): string[] { - return getTypstWsFontArgs(vscode.workspace.getConfiguration().get( +export function codeGetCliFontArgs(): string[] { + return getCliFontArgs(vscode.workspace.getConfiguration().get( 'typst-preview.fontPaths')); } @@ -109,7 +109,7 @@ const panelScrollTo = async (bindDocument: vscode.TextDocument, activeEditor: vs interface TaskControlBlock { /// related panel panel?: vscode.WebviewPanel; - /// channel to communicate with typst-ws + /// channel to communicate with typst-preview addonΠserver: WebSocket; } @@ -160,7 +160,7 @@ function runServer(command: string, args: string[], outputChannel: vscode.Output }); serverProcess.on('error', (err: any) => { console.error('Failed to start server process'); - vscode.window.showErrorMessage(`Failed to start typst-ws(${command}) process: ${err}`); + vscode.window.showErrorMessage(`Failed to start typst-preview(${command}) process: ${err}`); }); serverProcess.stdout.on('data', (data: Buffer) => { outputChannel.append(data.toString()); @@ -170,7 +170,7 @@ function runServer(command: string, args: string[], outputChannel: vscode.Output }); serverProcess.on('exit', (code: any) => { if (code !== null && code !== 0) { - vscode.window.showErrorMessage(`typst-ws process exited with code ${code}`); + vscode.window.showErrorMessage(`typst-preview process exited with code ${code}`); } console.log(`child process exited with code ${code}`); }); @@ -228,7 +228,7 @@ const launchPreview = async (task: LaunchInBrowserTask | LaunchInWebViewTask) => const scrollSyncMode = vscode.workspace.getConfiguration().get('typst-preview.scrollSync') || "never"; const fontendPath = path.resolve(context.extensionPath, "out/frontend"); await watchEditorFiles(); - const { serverProcess, controlPlanePort, dataPlanePort } = await launchTypstWs(task.kind === 'browser'); + const { serverProcess, controlPlanePort, dataPlanePort } = await launchCli(task.kind === 'browser'); const addonΠserver = new WebSocket(`ws://127.0.0.1:${controlPlanePort}`); addonΠserver.addEventListener("message", async (message) => { @@ -284,7 +284,7 @@ const launchPreview = async (task: LaunchInBrowserTask | LaunchInWebViewTask) => const basename = path.basename(activeEditor.document.fileName); // Create and show a new WebView const panel = vscode.window.createWebviewPanel( - 'typst-ws-preview', // 标识符 + 'typst-preview', // 标识符 `${basename} (Preview)`, // 面板标题 vscode.ViewColumn.Beside, // 显示在编辑器的哪一侧 { @@ -343,12 +343,12 @@ const launchPreview = async (task: LaunchInBrowserTask | LaunchInWebViewTask) => } }; - async function launchTypstWs(serveStaticFile: boolean) { - const serverPath = await getTypstWsPath(context.extensionPath); + async function launchCli(openInBrowser: boolean) { + const serverPath = await getCliPath(context.extensionPath); console.log(`Watching ${filePath} for changes`); const projectRoot = getProjectRoot(filePath); const rootArgs = projectRoot ? ["--root", projectRoot] : []; - const staticFileArgs = serveStaticFile ? ["--serve-static-file"] : []; + const staticFileArgs = openInBrowser ? ["--open-in-browser", "--open-in-browser-host", "127.0.0.1:0"] : []; const partialRenderingArgs = vscode.workspace.getConfiguration().get('typst-preview.partialRendering') ? ["--partial-rendering"] : []; const [dataPlanePort, controlPlanePort, serverProcess] = await runServer(serverPath, [ "--data-plane-host", "127.0.0.1:0", @@ -356,8 +356,8 @@ const launchPreview = async (task: LaunchInBrowserTask | LaunchInWebViewTask) => ...rootArgs, ...staticFileArgs, ...partialRenderingArgs, - ...codeGetTypstWsFontArgs(), - "watch", filePath, + ...codeGetCliFontArgs(), + filePath, ], outputChannel); console.log(`Launched server, data plane port:${dataPlanePort}, control plane port:${controlPlanePort}`); // window.typstWebsocket.send("current"); diff --git a/addons/vscode/src/test/suite/extension.test.ts b/addons/vscode/src/test/suite/extension.test.ts index 5637c8e3..63dd387f 100644 --- a/addons/vscode/src/test/suite/extension.test.ts +++ b/addons/vscode/src/test/suite/extension.test.ts @@ -19,36 +19,36 @@ suite('Extension Test Suite', () => { test('Executable Configuration Test', async () => { assert.strictEqual('', vscode.workspace.getConfiguration().get('typst-preview.executable'), 'default path'); - assert.notStrictEqual('', await ext.getTypstWsPath(), 'never resolve empty string'); - assert.notStrictEqual(undefined, await ext.getTypstWsPath(), 'never resolve undefined'); + assert.notStrictEqual('', await ext.getCliPath(), 'never resolve empty string'); + assert.notStrictEqual(undefined, await ext.getCliPath(), 'never resolve undefined'); - const state = ext.getTypstWsPath as unknown as any; + const state = ext.getCliPath as unknown as any; let resolved: string; const BINARY_NAME = state.BINARY_NAME; - assert.strictEqual('typst-ws', BINARY_NAME, 'default binary path is typst-ws'); + assert.strictEqual('typst-preview', BINARY_NAME, 'default binary path is typst-preview'); - resolved = await ext.getTypstWsPath(); + resolved = await ext.getCliPath(); assert.strictEqual(state.bundledPath, resolved, 'the bundle path exists and detected'); - state.BINARY_NAME = 'bad-typst-ws'; - assert.strictEqual('bad-typst-ws', await ext.getTypstWsPath(), 'fallback to binary name if not exists'); + state.BINARY_NAME = 'bad-typst-preview'; + assert.strictEqual('bad-typst-preview', await ext.getCliPath(), 'fallback to binary name if not exists'); const oldGetConfig = state.getConfig; - state.getConfig = () => 'config-typst-ws'; - assert.strictEqual('config-typst-ws', await ext.getTypstWsPath(), 'use config if set'); + state.getConfig = () => 'config-typst-preview'; + assert.strictEqual('config-typst-preview', await ext.getCliPath(), 'use config if set'); - state.BINARY_NAME = 'typst-ws'; + state.BINARY_NAME = 'typst-preview'; state.getConfig = oldGetConfig; - resolved = await ext.getTypstWsPath(); + resolved = await ext.getCliPath(); assert.strictEqual(state.bundledPath, resolved, 'reactive state'); - resolved = await ext.getTypstWsPath(); + resolved = await ext.getCliPath(); assert.strictEqual(true, resolved.endsWith(state.BINARY_NAME), 'exact file suffix'); /// fast path should hit for (let i = 0; i < 1000; i++) { - await ext.getTypstWsPath(); + await ext.getCliPath(); } }); @@ -61,16 +61,16 @@ suite('Extension Test Suite', () => { ); jsonIs(assert.strictEqual)( - [], ext.getTypstWsFontArgs(undefined)); + [], ext.getCliFontArgs(undefined)); jsonIs(assert.strictEqual)( - [], ext.getTypstWsFontArgs([])); + [], ext.getCliFontArgs([])); jsonIs(assert.strictEqual)( - [], ext.codeGetTypstWsFontArgs()); + [], ext.codeGetCliFontArgs()); jsonIs(assert.strictEqual)( ["--font-path", "/path/to/font1", "--font-path", "/path/to/font2"], - ext.getTypstWsFontArgs(["/path/to/font1", "/path/to/font2"])); + ext.getCliFontArgs(["/path/to/font1", "/path/to/font2"])); }); }); diff --git a/src/args.rs b/src/args.rs index 8efa8182..ad18567c 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,53 +1,38 @@ use std::path::PathBuf; -use clap::{ArgAction, Parser, Subcommand}; +use clap::{ArgAction, Parser}; -/// typst creates PDF files from .typ files #[derive(Debug, Clone, Parser)] -#[clap(name = "typst-ws", author)] +#[clap(name = "typst-preview", author)] pub struct CliArguments { /// Add additional directories to search for fonts #[clap(long = "font-path", value_name = "DIR", action = ArgAction::Append)] pub font_paths: Vec, - /// Configure the root for absolute paths + /// Root directory for your project #[clap(long = "root", value_name = "DIR")] pub root: Option, - /// Configure the websocket path - #[clap(long = "data-plane-host")] - pub data_plane_host: Option, + /// Data plane server will bind to this address + #[clap(long = "data-plane-host", default_value = "127.0.0.1:23625", value_name = "HOST")] + pub data_plane_host: String, - /// Configure the websocket path - #[clap(long = "control-plane-host")] + /// Control plane server will bind to this address + #[clap(long = "control-plane-host", default_value = "127.0.0.1:23626", value_name = "HOST")] pub control_plane_host: Option, - #[clap(long = "static-file-host")] - pub static_file_host: Option, + /// Host to open the preview in the browser. + #[clap(long = "open-in-browser-host", value_name = "HOST", default_value = "127.0.0.1:23267")] + pub open_in_browser_host: Option, - #[clap(long = "serve-static-file")] - pub serve_static_file: bool, + /// Open the preview in the browser after compilation. + #[clap(long = "open-in-browser")] + pub open_in_browser: bool, + /// Only render visible part of the document. This can improve performance but still being experimental. #[clap(long = "partial-rendering")] pub enable_partial_rendering: bool, - - /// The typst command to run - #[command(subcommand)] - pub command: Command, -} - -/// What to do. -#[derive(Debug, Clone, Subcommand)] -#[command()] -pub enum Command { - /// Watches the input file and recompiles on changes - #[command(visible_alias = "w")] - Watch(CompileCommand), -} - -/// Compiles the input file into a PDF file -#[derive(Debug, Clone, Parser)] -pub struct CompileCommand { - /// Path to input Typst file + pub input: PathBuf, } + diff --git a/src/main.rs b/src/main.rs index 79d21b97..e6179cb8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,7 +27,7 @@ use tokio::net::{TcpListener, TcpStream}; use typst::doc::{Document, Frame, FrameItem, Position}; use typst_ts_svg_exporter::IncrSvgDocServer; -use crate::args::{CliArguments, Command, CompileCommand}; +use crate::args::CliArguments; use crate::publisher::PublisherImpl; use tokio::sync::Mutex; use tokio_tungstenite::tungstenite::Message; @@ -78,9 +78,7 @@ impl CompileSettings { /// # Panics /// Panics if the command is not a compile or watch command. pub fn with_arguments(args: CliArguments) -> Self { - let _watch = matches!(args.command, Command::Watch(_)); - let Command::Watch(CompileCommand { input }) = args.command; - Self::new(input, true, args.root, args.font_paths) + Self::new(args.input, true, args.root, args.font_paths) } } @@ -184,7 +182,7 @@ const HTML: &str = include_str!("../addons/vscode/out/frontend/index.html"); #[tokio::main] async fn main() { let _ = env_logger::builder() - .filter_module("typst_ws", log::LevelFilter::Info) + .filter_module("typst_preview", log::LevelFilter::Info) .filter_module("typst_ts", log::LevelFilter::Info) .try_init(); let arguments = CliArguments::parse(); @@ -192,20 +190,24 @@ async fn main() { let doc_publisher: Publisher = PublisherImpl::new().into(); let command = CompileSettings::with_arguments(arguments.clone()); let enable_partial_rendering = arguments.enable_partial_rendering; + let entry = if command.input.is_absolute() { + command.input.clone() + } else { + std::env::current_dir().unwrap().join(command.input) + }; let root = if let Some(root) = &command.root { - root.clone() - } else if let Some(dir) = command - .input - .canonicalize() - .ok() - .as_ref() - .and_then(|path| path.parent()) - { - dir.into() + if root.is_absolute() { + root.clone() + } else { + std::env::current_dir().unwrap().join(root) + } } else { - PathBuf::new() + std::env::current_dir().unwrap() }; - + if !entry.starts_with(&root) { + error!("entry file must be in the root directory"); + std::process::exit(1); + } let compile_driver = { let world = TypstSystemWorld::new(CompileOpts { root_dir: root.clone(), @@ -215,19 +217,16 @@ async fn main() { }) .expect("incorrect options"); - CompileDriver::new(world).with_entry_file(command.input.clone()) + CompileDriver::new(world).with_entry_file(entry) }; // Create the world that serves sources, fonts and files. let world = Arc::new(Mutex::new(compile_driver)); { - let arguments = arguments.clone(); let publisher = doc_publisher.clone(); let world = world.clone(); tokio::spawn(async move { - let res = match &arguments.command { - Command::Watch(_) => watch(world, publisher).await, - }; + let res = watch(world, publisher).await; if let Err(msg) = res { print_error(&msg).expect("failed to print error"); @@ -242,9 +241,7 @@ async fn main() { }); let (data_plane_port_tx, data_plane_port_rx) = tokio::sync::oneshot::channel(); - let data_plane_addr = arguments - .data_plane_host - .unwrap_or_else(|| "127.0.0.1:23625".to_string()); + let data_plane_addr = arguments.data_plane_host; let (doc_to_src_jump_tx, mut doc_to_src_jump_rx) = tokio::sync::mpsc::channel(8); let src_to_doc_jump_publisher: Publisher = PublisherImpl::new().into(); let data_plane_handle = { @@ -488,9 +485,9 @@ async fn main() { } }); let static_file_addr = arguments - .static_file_host + .open_in_browser_host .unwrap_or_else(|| "127.0.0.1:23267".to_string()); - if arguments.serve_static_file { + if arguments.open_in_browser { let data_plane_port = data_plane_port_rx.await.unwrap(); let make_service = make_service_fn(|_| { let data_plane_port = data_plane_port;