feat: show main file in the status bar (#1147)

This commit is contained in:
Myriad-Dreamin 2025-01-11 17:16:03 +08:00 committed by GitHub
parent c0e0e330c3
commit 30f242d393
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 92 additions and 38 deletions

View file

@ -15,9 +15,16 @@ pub struct DocVersion {
pub revision: usize,
}
#[derive(Debug, Clone)]
pub struct CompileStatus {
pub group: String,
pub path: String,
pub status: TinymistCompileStatusEnum,
}
pub enum EditorRequest {
Diag(DocVersion, Option<DiagnosticsMap>),
Status(String, TinymistCompileStatusEnum),
Status(CompileStatus),
WordCount(String, WordsCount),
}
@ -46,7 +53,11 @@ impl EditorActor {
}
pub async fn run(mut self) {
let mut compile_status = TinymistCompileStatusEnum::Compiling;
let mut compile_status = CompileStatus {
group: "primary".to_owned(),
status: TinymistCompileStatusEnum::Compiling,
path: "".to_owned(),
};
let mut words_count = None;
while let Some(req) = self.editor_rx.recv().await {
match req {
@ -59,13 +70,14 @@ impl EditorActor {
self.publish(group, diagnostics).await;
}
EditorRequest::Status(group, status) => {
log::info!("received status request({group}) {status:?}");
if self.notify_compile_status && group == "primary" {
EditorRequest::Status(status) => {
log::info!("received status request({status:?})");
if self.notify_compile_status && status.group == "primary" {
compile_status = status;
self.client.send_notification::<TinymistCompileStatus>(
TinymistCompileStatus {
status: compile_status.clone(),
status: compile_status.status.clone(),
path: compile_status.path.clone(),
words_count: words_count.clone(),
},
);
@ -77,7 +89,8 @@ impl EditorActor {
words_count = Some(wc);
self.client.send_notification::<TinymistCompileStatus>(
TinymistCompileStatus {
status: compile_status.clone(),
status: compile_status.status.clone(),
path: compile_status.path.clone(),
words_count: words_count.clone(),
},
);
@ -156,8 +169,9 @@ pub enum TinymistCompileStatusEnum {
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TinymistCompileStatus {
struct TinymistCompileStatus {
pub status: TinymistCompileStatusEnum,
pub path: String,
pub words_count: Option<WordsCount>,
}

View file

@ -26,6 +26,7 @@ use std::{collections::HashMap, ops::Deref, sync::Arc};
use anyhow::bail;
use log::{error, info, trace};
use reflexo::path::unix_slash;
use reflexo_typst::{
error::prelude::*, typst::prelude::*, vfs::notify::MemoryEvent, world::EntryState,
CompileReport, EntryReader, Error, ImmutPath, TaskInputs,
@ -40,7 +41,7 @@ use tokio::sync::{mpsc, oneshot};
use typst::{diag::SourceDiagnostic, World};
use super::{
editor::{DocVersion, EditorRequest, TinymistCompileStatusEnum},
editor::{CompileStatus, DocVersion, EditorRequest, TinymistCompileStatusEnum},
typ_server::{
CompilationHandle, CompileSnapshot, CompiledArtifact, Interrupt, SucceededArtifact,
},
@ -182,9 +183,9 @@ impl CompileHandler {
}
impl CompilationHandle<LspCompilerFeat> for CompileHandler {
fn status(&self, revision: usize, _rep: CompileReport) {
fn status(&self, revision: usize, rep: CompileReport) {
// todo: seems to duplicate with CompileStatus
let status = match _rep {
let status = match rep {
CompileReport::Suspend => {
self.push_diagnostics(revision, None);
TinymistCompileStatusEnum::CompileSuccess
@ -198,14 +199,21 @@ impl CompilationHandle<LspCompilerFeat> for CompileHandler {
let this = &self;
this.editor_tx
.send(EditorRequest::Status(this.diag_group.clone(), status))
.send(EditorRequest::Status(CompileStatus {
group: this.diag_group.clone(),
path: rep
.compiling_id()
.map(|s| unix_slash(s.vpath().as_rooted_path()))
.unwrap_or_default(),
status,
}))
.unwrap();
#[cfg(feature = "preview")]
if let Some(inner) = this.inner.read().as_ref() {
use typst_preview::CompileStatus;
let status = match _rep {
let status = match rep {
CompileReport::Suspend => CompileStatus::CompileSuccess,
CompileReport::Stage(_, _, _) => CompileStatus::Compiling,
CompileReport::CompileSuccess(_, _, _) => CompileStatus::CompileSuccess,
@ -218,7 +226,7 @@ impl CompilationHandle<LspCompilerFeat> for CompileHandler {
}
}
fn notify_compile(&self, snap: &CompiledArtifact<LspCompilerFeat>, _rep: CompileReport) {
fn notify_compile(&self, snap: &CompiledArtifact<LspCompilerFeat>, rep: CompileReport) {
// todo: we need to manage the revision for fn status() as well
{
let mut n_rev = self.notified_revision.lock();
@ -241,14 +249,18 @@ impl CompilationHandle<LspCompilerFeat> for CompileHandler {
self.export.signal(snap, snap.signal);
self.editor_tx
.send(EditorRequest::Status(
self.diag_group.clone(),
if snap.doc.is_ok() {
.send(EditorRequest::Status(CompileStatus {
group: self.diag_group.clone(),
path: rep
.compiling_id()
.map(|s| unix_slash(s.vpath().as_rooted_path()))
.unwrap_or_default(),
status: if snap.doc.is_ok() {
TinymistCompileStatusEnum::CompileSuccess
} else {
TinymistCompileStatusEnum::CompileError
},
))
}))
.unwrap();
#[cfg(feature = "preview")]

View file

@ -81,6 +81,19 @@ In VSCode, enable compile status meaning that the extension will show the compil
- `disable`
- **Default**: `"enable"`
## `tinymist.statusBarFormat`
Set format string of the server status. For example, `{compileStatusIcon}{wordCount} [{fileName}]` will format the status as `$(check) 123 words [main]`. Valid placeholders are:
- `{compileStatusIcon}`: Icon indicating the compile status
- `{wordCount}`: Number of words in the document
- `{fileName}`: Name of the file being compiled
Note: The status bar will be hidden if the format string is empty.
- **Type**: `string`
- **Default**: `"{compileStatusIcon} {wordCount} [{fileName}]"`
## `tinymist.typstExtraArgs`
You can pass any arguments as you like, and we will try to follow behaviors of the **same version** of typst-cli. Note: the arguments may be overridden by other settings. For example, `--font-path` will be overridden by `tinymist.fontPaths`.

View file

@ -365,7 +365,7 @@
"default": null
},
"tinymist.compileStatus": {
"title": "Show/Report compilation status",
"title": "Show/Report Compile Status",
"description": "In VSCode, enable compile status meaning that the extension will show the compilation status in the status bar. Since Neovim and Helix don't have a such feature, it is disabled by default at the language server label.",
"type": "string",
"default": "enable",
@ -374,6 +374,12 @@
"disable"
]
},
"tinymist.statusBarFormat": {
"title": "Format of the Server Status in the Status Bar",
"markdownDescription": "Set format string of the server status. For example, `{compileStatusIcon}{wordCount} [{fileName}]` will format the status as `$(check) 123 words [main]`. Valid placeholders are:\n\n- `{compileStatusIcon}`: Icon indicating the compile status\n- `{wordCount}`: Number of words in the document\n- `{fileName}`: Name of the file being compiled\n\nNote: The status bar will be hidden if the format string is empty.",
"type": "string",
"default": "{compileStatusIcon} {wordCount} [{fileName}]"
},
"tinymist.typstExtraArgs": {
"title": "Specifies the arguments for Typst as same as typst-cli",
"description": "You can pass any arguments as you like, and we will try to follow behaviors of the **same version** of typst-cli. Note: the arguments may be overridden by other settings. For example, `--font-path` will be overridden by `tinymist.fontPaths`.",

View file

@ -516,8 +516,11 @@ async function commandActivateDocPath(
if (extensionState.mut.focusingDoc?.isClosed) {
extensionState.mut.focusingDoc = undefined;
}
const formatString = statusBarFormatString();
// remove the status bar until the last focusing file is closed
triggerStatusBar(!!(fsPath || extensionState.mut.focusingDoc?.isClosed === false));
triggerStatusBar(
!!formatString && !!(fsPath || extensionState.mut.focusingDoc?.isClosed === false),
);
await tinymist.executeCommand("tinymist.focusMain", [fsPath]);
}
@ -600,3 +603,11 @@ function triggerSuggestAndParameterHints() {
vscode.commands.executeCommand("editor.action.triggerSuggest");
vscode.commands.executeCommand("editor.action.triggerParameterHints");
}
export function statusBarFormatString() {
const formatter = (
(vscode.workspace.getConfiguration("tinymist").get("statusBarFormat") as string) || ""
).trim();
return formatter;
}

View file

@ -1,4 +1,5 @@
import * as vscode from "vscode";
import { statusBarFormatString } from "./extension";
let statusBarItem: vscode.StatusBarItem;
@ -23,6 +24,7 @@ interface WordsCount {
export interface TinymistStatus {
status: "compiling" | "compileSuccess" | "compileError";
path: string;
wordsCount: WordsCount;
}
@ -39,11 +41,13 @@ export function wordCountItemProcess(event: TinymistStatus) {
statusBarItem = statusBarItem || initWordCountItem();
const updateTooltip = () => {
statusBarItem.tooltip = `${words} ${plural("Word", words)}
statusBarItem.tooltip = `
Main file: ${event.path}
${words} ${plural("Word", words)}
${chars} ${plural("Character", chars)}
${spaces} ${plural("Space", spaces)}
${cjkChars} CJK ${plural("Character", cjkChars)}
[Click to show logs]`;
[Click to show logs]`.trim();
};
words = event.wordsCount?.words || 0;
@ -51,30 +55,24 @@ ${cjkChars} CJK ${plural("Character", cjkChars)}
spaces = event.wordsCount?.spaces || 0;
cjkChars = event.wordsCount?.cjkChars || 0;
const style: string = "errorStatus";
const fileName = event.path ? event.path.split("/").slice(-1)[0] : "";
const fileNameWithoutExt = fileName ? fileName.split(".").slice(0, -1).join(".") : "";
const formatString = statusBarFormatString()
.replace(/\{wordCount\}/g, `${words} ${plural("Word", words)}`)
.replace(/\{fileName\}/g, fileNameWithoutExt);
if (statusBarItem) {
if (event.status === "compiling") {
if (style === "compact") {
statusBarItem.text = "$(sync~spin)";
} else if (style === "errorStatus") {
statusBarItem.text = `$(sync~spin) ${words} ${plural("Word", words)}`;
}
statusBarItem.text = formatString.replace(/\{compileStatusIcon\}/g, "$(sync~spin)");
statusBarItem.backgroundColor = new vscode.ThemeColor("statusBarItem.prominentBackground");
updateTooltip();
} else if (event.status === "compileSuccess") {
if (style === "compact") {
statusBarItem.text = "$(typst-guy)";
} else if (style === "errorStatus") {
statusBarItem.text = `$(sync) ${words} ${plural("Word", words)}`;
}
statusBarItem.text = formatString.replace(/\{compileStatusIcon\}/g, "$(sync)");
statusBarItem.backgroundColor = new vscode.ThemeColor("statusBarItem.prominentBackground");
updateTooltip();
} else if (event.status === "compileError") {
if (style === "compact") {
statusBarItem.text = "$(typst-guy)";
} else if (style === "errorStatus") {
statusBarItem.text = `$(sync) ${words} ${plural("Word", words)}`;
}
statusBarItem.text = formatString.replace(/\{compileStatusIcon\}/g, "$(sync)");
statusBarItem.backgroundColor = new vscode.ThemeColor("statusBarItem.errorBackground");
updateTooltip();
}