diff --git a/crates/ruff_server/docs/setup/HELIX.md b/crates/ruff_server/docs/setup/HELIX.md index 5af68932d7..e41fb2b8bd 100644 --- a/crates/ruff_server/docs/setup/HELIX.md +++ b/crates/ruff_server/docs/setup/HELIX.md @@ -95,5 +95,7 @@ environment = { "RUFF_TRACE" = "messages" } [language-server.ruff.config.settings] logLevel = "debug" -logFile = "/Users/developer/.cache/helix/ruff.log" +logFile = "~/.cache/helix/ruff.log" ``` + +The `logFile` path supports tildes and environment variables. diff --git a/crates/ruff_server/docs/setup/NEOVIM.md b/crates/ruff_server/docs/setup/NEOVIM.md index e0bd63ef93..d055c58e1a 100644 --- a/crates/ruff_server/docs/setup/NEOVIM.md +++ b/crates/ruff_server/docs/setup/NEOVIM.md @@ -85,8 +85,10 @@ require('lspconfig').ruff.setup { init_options = { settings = { logLevel = "debug", - logFile = "your/log/file/path/log.txt" + logFile = "~/.local/state/nvim/ruff.log" } } } ``` + +The `logFile` path supports tildes and environment variables. diff --git a/crates/ruff_server/src/session/settings.rs b/crates/ruff_server/src/session/settings.rs index 06a08ddfe1..0d3740d369 100644 --- a/crates/ruff_server/src/session/settings.rs +++ b/crates/ruff_server/src/session/settings.rs @@ -83,6 +83,7 @@ pub(crate) struct ClientSettings { #[serde(rename_all = "camelCase")] pub(crate) struct TracingSettings { pub(crate) log_level: Option, + /// Path to the log file - tildes and environment variables are supported. pub(crate) log_file: Option, } diff --git a/crates/ruff_server/src/trace.rs b/crates/ruff_server/src/trace.rs index 9910651210..eeac188377 100644 --- a/crates/ruff_server/src/trace.rs +++ b/crates/ruff_server/src/trace.rs @@ -16,7 +16,11 @@ //! A `logFile` path can also be specified in the settings, and output will be directed there instead. use lsp_types::TraceValue; use serde::Deserialize; -use std::sync::{Arc, Mutex, OnceLock}; +use std::{ + path::PathBuf, + str::FromStr, + sync::{Arc, Mutex, OnceLock}, +}; use tracing::level_filters::LevelFilter; use tracing_subscriber::{ fmt::{time::Uptime, writer::BoxMakeWriter}, @@ -48,13 +52,35 @@ pub(crate) fn init_tracing( .set(sender) .expect("logging sender should only be initialized once"); - let log_file = log_file.and_then(|path| { - std::fs::OpenOptions::new() - .create(true) - .append(true) - .open(path) - .ok() - }); + let log_file = log_file + .map(|path| { + // this expands `logFile` so that tildes and environment variables + // are replaced with their values, if possible. + if let Some(expanded) = shellexpand::full(&path.to_string_lossy()) + .ok() + .and_then(|path| PathBuf::from_str(&path).ok()) + { + expanded + } else { + path.to_path_buf() + } + }) + .and_then(|path| { + std::fs::OpenOptions::new() + .create(true) + .append(true) + .open(&path) + .map_err(|err| { + #[allow(clippy::print_stderr)] + { + eprintln!( + "Failed to open file at {} for logging: {err}", + path.display() + ); + } + }) + .ok() + }); let subscriber = tracing_subscriber::Registry::default().with( tracing_subscriber::fmt::layer()