From 69f12c7cbfb38b0e1da8b58b25edfa03f54e5b8b Mon Sep 17 00:00:00 2001 From: Gokul Soumya Date: Sun, 13 Feb 2022 12:18:20 +0530 Subject: [PATCH] Add language specific --checkhealth --- Cargo.lock | 1 + helix-core/src/syntax.rs | 2 +- helix-term/Cargo.toml | 2 + helix-term/src/args.rs | 14 ++++++- helix-term/src/health.rs | 84 +++++++++++++++++++++++++++++++++++++++- helix-term/src/main.rs | 19 +++++---- 6 files changed, 110 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cff9c9914dae4..6df3bdd418c5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -454,6 +454,7 @@ dependencies = [ "tokio", "tokio-stream", "toml", + "which", ] [[package]] diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index ccf91100f3fc9..4a516b8b9b2ae 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -213,7 +213,7 @@ impl TextObjectQuery { } } -fn load_runtime_file(language: &str, filename: &str) -> Result { +pub fn load_runtime_file(language: &str, filename: &str) -> Result { let path = crate::RUNTIME_DIR .join("queries") .join(language) diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index e62496f2932e8..d791724ff0130 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -30,6 +30,8 @@ helix-dap = { version = "0.6", path = "../helix-dap" } anyhow = "1" once_cell = "1.9" +which = "4.2" + tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] } num_cpus = "1" tui = { path = "../helix-tui", package = "helix-tui", default-features = false, features = ["crossterm"] } diff --git a/helix-term/src/args.rs b/helix-term/src/args.rs index a2711c3896620..a51de9eaa4940 100644 --- a/helix-term/src/args.rs +++ b/helix-term/src/args.rs @@ -7,6 +7,7 @@ pub struct Args { pub display_help: bool, pub display_version: bool, pub checkhealth: bool, + pub checkhealth_arg: Option, pub load_tutor: bool, pub verbosity: u64, pub files: Vec<(PathBuf, Position)>, @@ -20,13 +21,22 @@ impl Args { iter.next(); // skip the program, we don't care about that - for arg in &mut iter { + while let Some(arg) = iter.next() { match arg.as_str() { "--" => break, // stop parsing at this point treat the remaining as files "--version" => args.display_version = true, "--help" => args.display_help = true, "--tutor" => args.load_tutor = true, - "--checkhealth" => args.checkhealth = true, + "--checkhealth" => { + args.checkhealth = true; + let pos = argv.iter().position(|a| a == "--checkhealth").unwrap(); + if let Some(opt) = argv.get(pos + 1) { + if !opt.starts_with('-') { + args.checkhealth_arg = Some(opt.clone()); + iter.next(); // skip checkhealth arg + } + } + } arg if arg.starts_with("--") => { anyhow::bail!("unexpected double dash argument: {}", arg) } diff --git a/helix-term/src/health.rs b/helix-term/src/health.rs index c9f8407eb74dc..945148d97bb10 100644 --- a/helix-term/src/health.rs +++ b/helix-term/src/health.rs @@ -1,6 +1,11 @@ -pub fn general() { - use crossterm::style::Stylize; +use crossterm::style::Stylize; +use helix_core::{ + config::{default_syntax_loader, user_syntax_loader}, + syntax::load_runtime_file, +}; +/// Display general diagnostics. +pub fn general() { let config_file = helix_core::config_file(); let lang_file = helix_core::lang_config_file(); let log_file = helix_core::log_file(); @@ -30,3 +35,78 @@ pub fn general() { println!("{}", "Runtime directory is empty.".red()); } } + +/// Display diagnostics pertaining to a particular language (LSP, +/// highlight queries, etc). +pub fn language(lang_str: String) { + let syn_loader_conf = user_syntax_loader().unwrap_or_else(|err| { + eprintln!("{}: {}", "Error parsing user language config".red(), err); + eprintln!("{}", "Using default language config".yellow()); + default_syntax_loader() + }); + + let lang = match syn_loader_conf + .language + .iter() + .find(|l| l.language_id == lang_str) + { + Some(l) => l, + None => { + println!("{}", "Given language not found".red()); + let suggestions: Vec<&str> = syn_loader_conf + .language + .iter() + .filter(|l| l.language_id.starts_with(lang_str.chars().next().unwrap())) + .map(|l| l.language_id.as_str()) + .collect(); + if !suggestions.is_empty() { + let suggestions = suggestions.join(", "); + println!("Did you mean one of these: {} ?", suggestions.yellow()); + } + return; + } + }; + + probe_protocol("language server", || { + lang.language_server + .as_ref() + .map(|lsp| lsp.command.to_string()) + }); + + probe_protocol("debug adapter", || { + lang.debugger.as_ref().map(|dap| dap.command.to_string()) + }); + + probe_treesitter_feature(&lang_str, "Highlight", "highlights.scm"); + probe_treesitter_feature(&lang_str, "Textobject", "textobjects.scm"); + probe_treesitter_feature(&lang_str, "Indent", "indents.toml"); +} + +/// Display diagnostics about LSP and DAP. `get_server_command` +/// should return the command that launches the server. +fn probe_protocol(protocol_name: &str, server_command: impl Fn() -> Option) { + let server_cmd = server_command(); + let cmd_name = match server_cmd { + Some(ref cmd) => cmd.as_str().green(), + None => "None".yellow(), + }; + println!("Configured {}: {}", protocol_name, cmd_name); + + if let Some(cmd) = server_cmd { + let path = match which::which(&cmd) { + Ok(path) => path.display().to_string().green(), + Err(_) => "Not found in $PATH".to_string().red(), + }; + println!("Binary for {}: {}", protocol_name, path); + } +} + +/// Display diagnostics about a feature that requires tree-sitter +/// query files (highlights, textobjects, etc). +fn probe_treesitter_feature(lang: &str, feature: &str, query_filename: &str) { + let found = match load_runtime_file(lang, query_filename).is_ok() { + true => "Found".green(), + false => "Not found".red(), + }; + println!("{} queries: {}", feature, found); +} diff --git a/helix-term/src/main.rs b/helix-term/src/main.rs index c13d09895a7cc..4342b80ed2bda 100644 --- a/helix-term/src/main.rs +++ b/helix-term/src/main.rs @@ -59,12 +59,13 @@ ARGS: ... Sets the input file to use, position can also be specified via file[:row[:col]] FLAGS: - -h, --help Prints help information - --tutor Loads the tutorial - --checkhealth Checks for potential errors in editor setup - -v Increases logging verbosity each use for up to 3 times - (default file: {}) - -V, --version Prints version information + -h, --help Prints help information + --tutor Loads the tutorial + --checkhealth [LANG] Checks for potential errors in editor setup + If given, checks for config errors in language LANG + -v Increases logging verbosity each use for up to 3 times + (default file: {}) + -V, --version Prints version information ", env!("CARGO_PKG_NAME"), env!("VERSION_AND_GIT_HASH"), @@ -87,7 +88,11 @@ FLAGS: } if args.checkhealth { - helix_term::health::general(); + if let Some(lang) = args.checkhealth_arg { + helix_term::health::language(lang); + } else { + helix_term::health::general(); + } std::process::exit(0); }