use css_variable_lsp::{lsp_server, runtime_config, VERSION};
use tower_lsp_server::{LspService, Server};
fn print_version() {
println!("css-variable-lsp {}", VERSION);
println!("LSP framework: tower-lsp-server");
println!("https://github.com/lmn451/css-lsp-rust");
}
fn print_help() {
let bin = "css-variable-lsp";
println!("{bin} — fast, Rust-based Language Server for CSS variables");
println!();
println!("USAGE:");
println!(" {bin} [OPTIONS]");
println!();
println!("OPTIONS (most flags accept --no-<flag> to disable, see FEATURE FLAGS):");
println!(" --version Print version and exit");
println!(" -h, --help Print this help message and exit");
println!();
println!("FEATURE FLAGS (toggle behaviour):");
println!(" --no-color-preview Disable color picker support");
println!(" --color-only-variables Only show colors on var() calls");
println!(" --no-suggest-add-fallback Disable \"Add fallback\" quickfix");
println!(" --no-suggest-exact-color-variables Disable color replacement suggestions");
println!();
println!("LOOKUP & IGNORES (comma-separated, repeated flags accumulate):");
println!(" --lookup-files <GLOBS> Glob patterns to scan for variables");
println!(" --ignore-globs <GLOBS> Glob patterns to exclude from scanning");
println!(" Singular forms: --lookup-file, --ignore-glob (repeatable)");
println!();
println!("DISPLAY:");
println!(" --path-display <MODE> relative|absolute|abbreviated[:N]");
println!(" --path-display-length <N> Number of leading chars in abbreviated mode");
println!();
println!("DIAGNOSTICS:");
println!(" --undefined-var-fallback <MODE> warning|info|off");
println!();
println!("ENVIRONMENT VARIABLES (equivalent to flags):");
println!(" CSS_LSP_COLOR_PREVIEW, CSS_LSP_COLOR_ONLY_VARIABLES");
println!(" CSS_LSP_LOOKUP_FILES, CSS_LSP_IGNORE_GLOBS");
println!(" CSS_LSP_PATH_DISPLAY, CSS_LSP_PATH_DISPLAY_LENGTH");
println!(" CSS_LSP_UNDEFINED_VAR_FALLBACK");
println!(" CSS_LSP_SUGGEST_ADD_FALLBACK, CSS_LSP_SUGGEST_EXACT_COLOR_VARIABLES");
println!(" CSS_LSP_ENABLE_LOGS=1 Enable tracing output to stderr");
println!();
println!("Examples:");
println!(" {bin} --version");
println!(" {bin} --no-color-preview");
println!(" CSS_LSP_LOOKUP_FILES='*.css,*.scss' {bin}");
println!(" {bin} --path-display=abbreviated:2 --no-suggest-add-fallback");
}
fn handle_meta_flags(args: &[String]) -> bool {
for arg in args {
if arg == "--version" {
print_version();
return true;
}
if arg == "--help" || arg == "-h" {
print_help();
return true;
}
}
false
}
fn init_tracing() {
if std::env::var_os("CSS_LSP_ENABLE_LOGS").is_none() {
return;
}
let _ = tracing_subscriber::fmt()
.with_writer(std::io::stderr)
.with_ansi(false)
.try_init();
}
#[tokio::main]
async fn main() {
init_tracing();
let args: Vec<String> = std::env::args().skip(1).collect();
if handle_meta_flags(&args) {
return;
}
let runtime_config = runtime_config::build_runtime_config(&args);
let stdin = tokio::io::stdin();
let stdout = tokio::io::stdout();
let (service, socket) =
LspService::new(|client| lsp_server::CssVariableLsp::new(client, runtime_config.clone()));
Server::new(stdin, stdout, socket).serve(service).await;
}
#[cfg(test)]
mod tests {
use super::*;
fn contains_help_text(text: &str) -> bool {
text.contains("USAGE") && text.contains("FEATURE FLAGS") && text.contains("ENVIRONMENT")
}
#[test]
fn print_help_mentions_known_flags() {
let bin = "css-variable-lsp";
let expected_flags = [
"--version",
"--help",
"--no-color-preview",
"--lookup-files",
"--path-display",
"--undefined-var-fallback",
];
for flag in &expected_flags {
assert!(
format!("{bin} {flag}").contains(flag),
"help message should reference {flag}"
);
}
}
#[test]
fn handle_meta_flags_detects_version() {
let args = vec!["--version".to_string()];
assert!(handle_meta_flags(&args));
}
#[test]
fn handle_meta_flags_detects_help() {
for flag in ["--help", "-h"] {
let args = vec![flag.to_string()];
assert!(handle_meta_flags(&args), "expected to handle {flag}");
}
}
#[test]
fn handle_meta_flags_ignores_runtime_flags() {
let args = vec![
"--no-color-preview".to_string(),
"--lookup-files".to_string(),
"*.css,*.scss".to_string(),
];
assert!(!handle_meta_flags(&args));
}
#[test]
fn contains_help_text_helper_is_consistent() {
assert!(contains_help_text(USAGE_AND_FLAGS_AND_ENV));
}
const USAGE_AND_FLAGS_AND_ENV: &str = "USAGE\n...\nFEATURE FLAGS\n...\nENVIRONMENT";
}