Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ and this project attempts to adhere to [Semantic Versioning](https://semver.org/
### Changed

- Changed user configuration directory paths to use application name only, removing organization identifiers
- Changed log directory to use XDG cache directories (e.g., `~/.cache/djls` on Linux) with `/tmp` fallback
- **Internal**: Refactored workspace to use domain types (`FileKind`) instead of LSP types (`LanguageId`)
- **Internal**: Added client detection for LSP-specific workarounds (e.g., Sublime Text's `html` language ID handling)

Expand Down
28 changes: 26 additions & 2 deletions crates/djls-conf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ pub mod tagspecs;
use std::fs;
use std::path::Path;

use anyhow::Context;
use camino::Utf8Path;
use camino::Utf8PathBuf;
use config::Config;
use config::ConfigError as ExternalConfigError;
use config::File;
Expand All @@ -19,6 +21,28 @@ pub use crate::tagspecs::SimpleArgTypeDef;
pub use crate::tagspecs::TagArgDef;
pub use crate::tagspecs::TagSpecDef;

pub(crate) fn project_dirs() -> Option<ProjectDirs> {
ProjectDirs::from("", "", "djls")
}

/// Get the log directory for the application and ensure it exists.
///
/// Returns the XDG cache directory (e.g., ~/.cache/djls on Linux) if available,
/// otherwise falls back to /tmp. Creates the directory if it doesn't exist.
///
/// # Errors
///
/// Returns an error if the directory cannot be created.
pub fn log_dir() -> anyhow::Result<Utf8PathBuf> {
let dir = project_dirs()
.and_then(|proj_dirs| Utf8PathBuf::from_path_buf(proj_dirs.cache_dir().to_path_buf()).ok())
.unwrap_or_else(|| Utf8PathBuf::from("/tmp"));

fs::create_dir_all(&dir).with_context(|| format!("Failed to create log directory: {dir}"))?;

Ok(dir)
}

#[derive(Error, Debug)]
pub enum ConfigError {
#[error("Configuration build/deserialize error")]
Expand All @@ -45,8 +69,8 @@ pub struct Settings {

impl Settings {
pub fn new(project_root: &Utf8Path, overrides: Option<Settings>) -> Result<Self, ConfigError> {
let user_config_file = ProjectDirs::from("", "", "djls")
.map(|proj_dirs| proj_dirs.config_dir().join("djls.toml"));
let user_config_file =
project_dirs().map(|proj_dirs| proj_dirs.config_dir().join("djls.toml"));

let mut settings = Self::load_from_paths(project_root, user_config_file.as_deref())?;

Expand Down
1 change: 1 addition & 0 deletions crates/djls-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use tower_lsp_server::Server;
pub use crate::server::DjangoLanguageServer;
pub use crate::session::Session;

/// Run the Django language server.
pub fn run() -> Result<()> {
if std::io::stdin().is_terminal() {
eprintln!(
Expand Down
26 changes: 19 additions & 7 deletions crates/djls-server/src/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,20 +105,32 @@ where
/// Initialize the dual-layer tracing subscriber.
///
/// Sets up:
/// - File layer: writes to /tmp/djls.log with daily rotation
/// - File layer: writes to XDG cache directory (e.g., ~/.cache/djls/djls.log on Linux) with daily rotation.
/// Falls back to /tmp/djls.log if XDG cache directory is not available.
/// If file logging cannot be initialized, falls back to stderr.
/// - LSP layer: forwards INFO+ messages to the client
/// - `EnvFilter`: respects `RUST_LOG` env var, defaults to "info"
///
/// Returns a `WorkerGuard` that must be kept alive for the file logging to work.
/// Returns a `WorkerGuard` that must be kept alive for the logging to work.
pub fn init_tracing<F>(send_message: F) -> WorkerGuard
where
F: Fn(lsp_types::MessageType, String) + Send + Sync + 'static,
{
let file_appender = tracing_appender::rolling::daily("/tmp", "djls.log");
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);

let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
let file_layer = fmt::layer()

let (non_blocking, guard) = match djls_conf::log_dir() {
Ok(log_dir) => {
let file_appender = tracing_appender::rolling::daily(log_dir.as_std_path(), "djls.log");
tracing_appender::non_blocking(file_appender)
}
Err(e) => {
eprintln!("Warning: Failed to initialize file logging: {e}");
eprintln!("Falling back to stderr logging...");
tracing_appender::non_blocking(std::io::stderr())
}
};

let log_layer = fmt::layer()
.with_writer(non_blocking)
.with_ansi(false)
.with_thread_ids(true)
Expand All @@ -131,7 +143,7 @@ where
let lsp_layer =
LspLayer::new(send_message).with_filter(tracing_subscriber::filter::LevelFilter::INFO);

Registry::default().with(file_layer).with(lsp_layer).init();
Registry::default().with(log_layer).with(lsp_layer).init();

guard
}