mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-11-22 12:34:39 +00:00
feat: init log in web (#2068)
This commit is contained in:
parent
5ee01b59a9
commit
d85717f81e
7 changed files with 150 additions and 35 deletions
|
|
@ -132,28 +132,12 @@ fn main() -> Result<()> {
|
||||||
// Loads translations
|
// Loads translations
|
||||||
#[cfg(feature = "l10n")]
|
#[cfg(feature = "l10n")]
|
||||||
set_translations(load_translations(tinymist_assets::L10N_DATA)?);
|
set_translations(load_translations(tinymist_assets::L10N_DATA)?);
|
||||||
|
|
||||||
// Starts logging
|
// Starts logging
|
||||||
let _ = {
|
let _ = tinymist::init_log(tinymist::InitLogOpts {
|
||||||
use log::LevelFilter::*;
|
is_transient_cmd: matches!(cmd, Commands::Compile(..)),
|
||||||
|
is_test_no_verbose: matches!(&cmd, Commands::Test(test) if !test.verbose),
|
||||||
let is_transient_cmd = matches!(cmd, Commands::Compile(..));
|
output: None,
|
||||||
let is_test_no_verbose = matches!(&cmd, Commands::Test(test) if !test.verbose);
|
});
|
||||||
let base_no_info = is_transient_cmd || is_test_no_verbose;
|
|
||||||
let base_level = if base_no_info { Warn } else { Info };
|
|
||||||
let preview_level = if is_test_no_verbose { Warn } else { Debug };
|
|
||||||
let diag_level = if is_test_no_verbose { Warn } else { Info };
|
|
||||||
|
|
||||||
env_logger::builder()
|
|
||||||
.filter_module("tinymist", base_level)
|
|
||||||
.filter_module("tinymist_preview", preview_level)
|
|
||||||
.filter_module("typlite", base_level)
|
|
||||||
.filter_module("reflexo", base_level)
|
|
||||||
.filter_module("sync_ls", base_level)
|
|
||||||
.filter_module("reflexo_typst2vec::pass::span2vec", Error)
|
|
||||||
.filter_module("reflexo_typst::diag::console", diag_level)
|
|
||||||
.try_init()
|
|
||||||
};
|
|
||||||
|
|
||||||
match cmd {
|
match cmd {
|
||||||
Commands::Probe => Ok(()),
|
Commands::Probe => Ok(()),
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ impl SemanticRequest for CodeActionRequest {
|
||||||
type Response = Vec<CodeAction>;
|
type Response = Vec<CodeAction>;
|
||||||
|
|
||||||
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response> {
|
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response> {
|
||||||
log::info!("requested code action: {self:?}");
|
log::trace!("requested code action: {self:?}");
|
||||||
|
|
||||||
let source = ctx.source_by_path(&self.path).ok()?;
|
let source = ctx.source_by_path(&self.path).ok()?;
|
||||||
let range = ctx.to_typst_range(self.range, &source)?;
|
let range = ctx.to_typst_range(self.range, &source)?;
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ serde_json.workspace = true
|
||||||
serde_with.workspace = true
|
serde_with.workspace = true
|
||||||
siphasher.workspace = true
|
siphasher.workspace = true
|
||||||
web-time = { workspace = true, optional = true }
|
web-time = { workspace = true, optional = true }
|
||||||
time.workspace = true
|
time = { workspace = true, features = ["formatting"] }
|
||||||
lsp-types.workspace = true
|
lsp-types.workspace = true
|
||||||
tempfile = { workspace = true, optional = true }
|
tempfile = { workspace = true, optional = true }
|
||||||
same-file = { workspace = true, optional = true }
|
same-file = { workspace = true, optional = true }
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ pub fn now() -> Time {
|
||||||
Time::UNIX_EPOCH
|
Time::UNIX_EPOCH
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub use time::format_description::well_known::Rfc3339;
|
||||||
|
|
||||||
/// The trait helping convert to a [`UtcDateTime`].
|
/// The trait helping convert to a [`UtcDateTime`].
|
||||||
pub trait ToUtcDateTime {
|
pub trait ToUtcDateTime {
|
||||||
/// Converts to a [`UtcDateTime`].
|
/// Converts to a [`UtcDateTime`].
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name = "tinymist"
|
name = "tinymist"
|
||||||
description = "An integrated language service for Typst."
|
description = "An integrated language service for Typst."
|
||||||
categories = ["compilers", "command-line-utilities"]
|
categories = ["compilers", "command-line-utilities"]
|
||||||
keywords = ["api", "language", "typst"]
|
keywords = ["api", "lsp", "language", "typst"]
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,25 @@
|
||||||
//! Tinymist Core Library
|
//! # tinymist
|
||||||
|
//!
|
||||||
|
//! This crate provides a CLI that starts services for [Typst](https://typst.app/). It provides:
|
||||||
|
//! + `tinymist lsp`: A language server following the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/).
|
||||||
|
//! + `tinymist preview`: A preview server for Typst.
|
||||||
|
//!
|
||||||
|
//! ## Usage
|
||||||
|
//!
|
||||||
|
//! See [Features: Command Line Interface](https://myriad-dreamin.github.io/tinymist/feature/cli.html).
|
||||||
|
//!
|
||||||
|
//! ## Documentation
|
||||||
|
//!
|
||||||
|
//! See [Crate Docs](https://myriad-dreamin.github.io/tinymist/rs/tinymist/index.html).
|
||||||
|
//!
|
||||||
|
//! Also see [Developer Guide: Tinymist LSP](https://myriad-dreamin.github.io/tinymist/module/lsp.html).
|
||||||
|
//!
|
||||||
|
//! ## Contributing
|
||||||
|
//!
|
||||||
|
//! See [CONTRIBUTING.md](https://github.com/Myriad-Dreamin/tinymist/blob/main/CONTRIBUTING.md).
|
||||||
|
|
||||||
pub use config::*;
|
pub use config::*;
|
||||||
|
pub use log_::*;
|
||||||
pub use lsp::init::*;
|
pub use lsp::init::*;
|
||||||
pub use server::*;
|
pub use server::*;
|
||||||
pub use sync_ls::LspClient;
|
pub use sync_ls::LspClient;
|
||||||
|
|
@ -23,22 +42,27 @@ pub use dap::SuperInit as DapSuperInit;
|
||||||
pub mod project;
|
pub mod project;
|
||||||
pub mod tool;
|
pub mod tool;
|
||||||
|
|
||||||
pub(crate) mod config;
|
#[cfg(feature = "web")]
|
||||||
#[cfg(feature = "dap")]
|
pub mod web;
|
||||||
pub(crate) mod dap;
|
|
||||||
pub(crate) mod input;
|
|
||||||
pub(crate) mod lsp;
|
|
||||||
#[cfg(feature = "lock")]
|
|
||||||
pub(crate) mod route;
|
|
||||||
|
|
||||||
mod actor;
|
mod actor;
|
||||||
mod cmd;
|
mod cmd;
|
||||||
|
mod config;
|
||||||
|
mod input;
|
||||||
|
#[path = "log.rs"]
|
||||||
|
mod log_;
|
||||||
|
mod lsp;
|
||||||
mod resource;
|
mod resource;
|
||||||
mod server;
|
mod server;
|
||||||
mod stats;
|
mod stats;
|
||||||
mod task;
|
mod task;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
#[cfg(feature = "dap")]
|
||||||
|
mod dap;
|
||||||
|
#[cfg(feature = "lock")]
|
||||||
|
mod route;
|
||||||
|
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
use lsp::query::QueryFuture;
|
use lsp::query::QueryFuture;
|
||||||
|
|
@ -70,6 +94,3 @@ Typst Source: {}
|
||||||
env!("TYPST_SOURCE"),
|
env!("TYPST_SOURCE"),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature = "web")]
|
|
||||||
pub mod web;
|
|
||||||
|
|
|
||||||
108
crates/tinymist/src/log.rs
Normal file
108
crates/tinymist/src/log.rs
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
//! Logging Functionality
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Options for initializing the logger.
|
||||||
|
pub struct InitLogOpts {
|
||||||
|
/// Whether the command is transient (e.g., compile).
|
||||||
|
pub is_transient_cmd: bool,
|
||||||
|
/// Whether the command is a test without verbose output.
|
||||||
|
pub is_test_no_verbose: bool,
|
||||||
|
/// Redirects output via LSP/DAP notification.
|
||||||
|
pub output: Option<LspClient>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes the logger for the Tinymist library.
|
||||||
|
pub fn init_log(
|
||||||
|
InitLogOpts {
|
||||||
|
is_transient_cmd,
|
||||||
|
is_test_no_verbose,
|
||||||
|
output,
|
||||||
|
}: InitLogOpts,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
use log::LevelFilter::*;
|
||||||
|
|
||||||
|
let base_no_info = is_transient_cmd || is_test_no_verbose;
|
||||||
|
let base_level = if base_no_info { Warn } else { Info };
|
||||||
|
let preview_level = if is_test_no_verbose { Warn } else { Debug };
|
||||||
|
let diag_level = if is_test_no_verbose { Warn } else { Info };
|
||||||
|
|
||||||
|
let mut builder = env_logger::builder();
|
||||||
|
if let Some(output) = output {
|
||||||
|
builder.target(LogNotification::create(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
// In WebAssembly, we use a custom notification for logging.
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
{
|
||||||
|
builder.format(|f, record| {
|
||||||
|
use std::io::Write;
|
||||||
|
let ts = tinymist_std::time::utc_now();
|
||||||
|
|
||||||
|
write!(f, "[")?;
|
||||||
|
ts.format_into(f, &tinymist_std::time::Rfc3339)
|
||||||
|
.map_err(std::io::Error::other)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" {level:<5} {module_path} {file_path}:{line} {target}] {args}",
|
||||||
|
level = record.level(),
|
||||||
|
module_path = record.module_path().unwrap_or("unknown"),
|
||||||
|
file_path = record.file().unwrap_or("unknown"),
|
||||||
|
line = record.line().unwrap_or(0),
|
||||||
|
target = record.target(),
|
||||||
|
args = record.args()
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(builder
|
||||||
|
.filter_module("tinymist", base_level)
|
||||||
|
.filter_module("tinymist_preview", preview_level)
|
||||||
|
.filter_module("typlite", base_level)
|
||||||
|
.filter_module("reflexo", base_level)
|
||||||
|
.filter_module("sync_ls", base_level)
|
||||||
|
.filter_module("reflexo_typst2vec::pass::span2vec", Error)
|
||||||
|
.filter_module("reflexo_typst::diag::console", diag_level)
|
||||||
|
.try_init()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LogNotification(LspClient, Vec<u8>);
|
||||||
|
|
||||||
|
impl LogNotification {
|
||||||
|
/// Creates a new `LogNotification` with the given LSP client and an empty
|
||||||
|
/// buffer.
|
||||||
|
fn create(output: LspClient) -> env_logger::Target {
|
||||||
|
env_logger::Target::Pipe(Box::new(Self(output, vec![])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::io::Write for LogNotification {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
if buf.is_empty() {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.1.extend_from_slice(buf);
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: the from_utf8_lossy may break non-ascii characters and inefficient
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
let data = String::from_utf8_lossy(self.1.as_slice()).to_string();
|
||||||
|
self.1.clear();
|
||||||
|
self.0.send_notification::<Log>(&Log { data });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
struct Log {
|
||||||
|
data: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl lsp_types::notification::Notification for Log {
|
||||||
|
const METHOD: &'static str = "tmLog";
|
||||||
|
type Params = Self;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue