mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-11-20 11:55:34 +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
|
||||
#[cfg(feature = "l10n")]
|
||||
set_translations(load_translations(tinymist_assets::L10N_DATA)?);
|
||||
|
||||
// Starts logging
|
||||
let _ = {
|
||||
use log::LevelFilter::*;
|
||||
|
||||
let is_transient_cmd = matches!(cmd, Commands::Compile(..));
|
||||
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()
|
||||
};
|
||||
let _ = tinymist::init_log(tinymist::InitLogOpts {
|
||||
is_transient_cmd: matches!(cmd, Commands::Compile(..)),
|
||||
is_test_no_verbose: matches!(&cmd, Commands::Test(test) if !test.verbose),
|
||||
output: None,
|
||||
});
|
||||
|
||||
match cmd {
|
||||
Commands::Probe => Ok(()),
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ impl SemanticRequest for CodeActionRequest {
|
|||
type Response = Vec<CodeAction>;
|
||||
|
||||
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 range = ctx.to_typst_range(self.range, &source)?;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ serde_json.workspace = true
|
|||
serde_with.workspace = true
|
||||
siphasher.workspace = true
|
||||
web-time = { workspace = true, optional = true }
|
||||
time.workspace = true
|
||||
time = { workspace = true, features = ["formatting"] }
|
||||
lsp-types.workspace = true
|
||||
tempfile = { workspace = true, optional = true }
|
||||
same-file = { workspace = true, optional = true }
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ pub fn now() -> Time {
|
|||
Time::UNIX_EPOCH
|
||||
}
|
||||
|
||||
pub use time::format_description::well_known::Rfc3339;
|
||||
|
||||
/// The trait helping convert to a [`UtcDateTime`].
|
||||
pub trait ToUtcDateTime {
|
||||
/// Converts to a [`UtcDateTime`].
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
name = "tinymist"
|
||||
description = "An integrated language service for Typst."
|
||||
categories = ["compilers", "command-line-utilities"]
|
||||
keywords = ["api", "language", "typst"]
|
||||
keywords = ["api", "lsp", "language", "typst"]
|
||||
authors.workspace = true
|
||||
version.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 log_::*;
|
||||
pub use lsp::init::*;
|
||||
pub use server::*;
|
||||
pub use sync_ls::LspClient;
|
||||
|
|
@ -23,22 +42,27 @@ pub use dap::SuperInit as DapSuperInit;
|
|||
pub mod project;
|
||||
pub mod tool;
|
||||
|
||||
pub(crate) mod config;
|
||||
#[cfg(feature = "dap")]
|
||||
pub(crate) mod dap;
|
||||
pub(crate) mod input;
|
||||
pub(crate) mod lsp;
|
||||
#[cfg(feature = "lock")]
|
||||
pub(crate) mod route;
|
||||
#[cfg(feature = "web")]
|
||||
pub mod web;
|
||||
|
||||
mod actor;
|
||||
mod cmd;
|
||||
mod config;
|
||||
mod input;
|
||||
#[path = "log.rs"]
|
||||
mod log_;
|
||||
mod lsp;
|
||||
mod resource;
|
||||
mod server;
|
||||
mod stats;
|
||||
mod task;
|
||||
mod utils;
|
||||
|
||||
#[cfg(feature = "dap")]
|
||||
mod dap;
|
||||
#[cfg(feature = "lock")]
|
||||
mod route;
|
||||
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use lsp::query::QueryFuture;
|
||||
|
|
@ -70,6 +94,3 @@ 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