Handle BrokenPipe when piping hx --health through head (#1876)

Co-authored-by: Gokul Soumya <gokulps15@gmail.com>
imgbot
Nirmal Patel 3 years ago committed by GitHub
parent 7cd6050235
commit 8702aaaefc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,6 +1,7 @@
use crossterm::style::{Color, Print, Stylize}; use crossterm::style::{Color, Print, Stylize};
use helix_core::config::{default_syntax_loader, user_syntax_loader}; use helix_core::config::{default_syntax_loader, user_syntax_loader};
use helix_loader::grammar::load_runtime_file; use helix_loader::grammar::load_runtime_file;
use std::io::Write;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum TsFeature { pub enum TsFeature {
@ -40,43 +41,62 @@ impl TsFeature {
} }
/// Display general diagnostics. /// Display general diagnostics.
pub fn general() { pub fn general() -> std::io::Result<()> {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
let config_file = helix_loader::config_file(); let config_file = helix_loader::config_file();
let lang_file = helix_loader::lang_config_file(); let lang_file = helix_loader::lang_config_file();
let log_file = helix_loader::log_file(); let log_file = helix_loader::log_file();
let rt_dir = helix_loader::runtime_dir(); let rt_dir = helix_loader::runtime_dir();
if config_file.exists() { if config_file.exists() {
println!("Config file: {}", config_file.display()); writeln!(stdout, "Config file: {}", config_file.display())?;
} else { } else {
println!("Config file: default") writeln!(stdout, "Config file: default")?;
} }
if lang_file.exists() { if lang_file.exists() {
println!("Language file: {}", lang_file.display()); writeln!(stdout, "Language file: {}", lang_file.display())?;
} else { } else {
println!("Language file: default") writeln!(stdout, "Language file: default")?;
} }
println!("Log file: {}", log_file.display()); writeln!(stdout, "Log file: {}", log_file.display())?;
println!("Runtime directory: {}", rt_dir.display()); writeln!(stdout, "Runtime directory: {}", rt_dir.display())?;
if let Ok(path) = std::fs::read_link(&rt_dir) { if let Ok(path) = std::fs::read_link(&rt_dir) {
let msg = format!("Runtime directory is symlinked to {}", path.display()); let msg = format!("Runtime directory is symlinked to {}", path.display());
println!("{}", msg.yellow()); writeln!(stdout, "{}", msg.yellow())?;
} }
if !rt_dir.exists() { if !rt_dir.exists() {
println!("{}", "Runtime directory does not exist.".red()); writeln!(stdout, "{}", "Runtime directory does not exist.".red())?;
} }
if rt_dir.read_dir().ok().map(|it| it.count()) == Some(0) { if rt_dir.read_dir().ok().map(|it| it.count()) == Some(0) {
println!("{}", "Runtime directory is empty.".red()); writeln!(stdout, "{}", "Runtime directory is empty.".red())?;
} }
Ok(())
} }
pub fn languages_all() { pub fn languages_all() -> std::io::Result<()> {
let mut syn_loader_conf = user_syntax_loader().unwrap_or_else(|err| { let stdout = std::io::stdout();
eprintln!("{}: {}", "Error parsing user language config".red(), err); let mut stdout = stdout.lock();
eprintln!("{}", "Using default language config".yellow());
default_syntax_loader() let mut syn_loader_conf = match user_syntax_loader() {
}); Ok(conf) => conf,
Err(err) => {
let stderr = std::io::stderr();
let mut stderr = stderr.lock();
writeln!(
stderr,
"{}: {}",
"Error parsing user language config".red(),
err
)?;
writeln!(stderr, "{}", "Using default language config".yellow())?;
default_syntax_loader()
}
};
let mut headings = vec!["Language", "LSP", "DAP"]; let mut headings = vec!["Language", "LSP", "DAP"];
@ -106,7 +126,7 @@ pub fn languages_all() {
for heading in headings { for heading in headings {
column(heading, Color::White); column(heading, Color::White);
} }
println!(); writeln!(stdout)?;
syn_loader_conf syn_loader_conf
.language .language
@ -139,18 +159,34 @@ pub fn languages_all() {
} }
} }
println!(); writeln!(stdout)?;
} }
Ok(())
} }
/// Display diagnostics pertaining to a particular language (LSP, /// Display diagnostics pertaining to a particular language (LSP,
/// highlight queries, etc). /// highlight queries, etc).
pub fn language(lang_str: String) { pub fn language(lang_str: String) -> std::io::Result<()> {
let syn_loader_conf = user_syntax_loader().unwrap_or_else(|err| { let stdout = std::io::stdout();
eprintln!("{}: {}", "Error parsing user language config".red(), err); let mut stdout = stdout.lock();
eprintln!("{}", "Using default language config".yellow());
default_syntax_loader() let syn_loader_conf = match user_syntax_loader() {
}); Ok(conf) => conf,
Err(err) => {
let stderr = std::io::stderr();
let mut stderr = stderr.lock();
writeln!(
stderr,
"{}: {}",
"Error parsing user language config".red(),
err
)?;
writeln!(stderr, "{}", "Using default language config".yellow())?;
default_syntax_loader()
}
};
let lang = match syn_loader_conf let lang = match syn_loader_conf
.language .language
@ -160,7 +196,7 @@ pub fn language(lang_str: String) {
Some(l) => l, Some(l) => l,
None => { None => {
let msg = format!("Language '{}' not found", lang_str); let msg = format!("Language '{}' not found", lang_str);
println!("{}", msg.red()); writeln!(stdout, "{}", msg.red())?;
let suggestions: Vec<&str> = syn_loader_conf let suggestions: Vec<&str> = syn_loader_conf
.language .language
.iter() .iter()
@ -169,9 +205,13 @@ pub fn language(lang_str: String) {
.collect(); .collect();
if !suggestions.is_empty() { if !suggestions.is_empty() {
let suggestions = suggestions.join(", "); let suggestions = suggestions.join(", ");
println!("Did you mean one of these: {} ?", suggestions.yellow()); writeln!(
stdout,
"Did you mean one of these: {} ?",
suggestions.yellow()
)?;
} }
return; return Ok(());
} }
}; };
@ -180,41 +220,66 @@ pub fn language(lang_str: String) {
lang.language_server lang.language_server
.as_ref() .as_ref()
.map(|lsp| lsp.command.to_string()), .map(|lsp| lsp.command.to_string()),
); )?;
probe_protocol( probe_protocol(
"debug adapter", "debug adapter",
lang.debugger.as_ref().map(|dap| dap.command.to_string()), lang.debugger.as_ref().map(|dap| dap.command.to_string()),
); )?;
for ts_feat in TsFeature::all() { for ts_feat in TsFeature::all() {
probe_treesitter_feature(&lang_str, *ts_feat) probe_treesitter_feature(&lang_str, *ts_feat)?
} }
Ok(())
} }
/// Display diagnostics about LSP and DAP. /// Display diagnostics about LSP and DAP.
fn probe_protocol(protocol_name: &str, server_cmd: Option<String>) { fn probe_protocol(protocol_name: &str, server_cmd: Option<String>) -> std::io::Result<()> {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
let cmd_name = match server_cmd { let cmd_name = match server_cmd {
Some(ref cmd) => cmd.as_str().green(), Some(ref cmd) => cmd.as_str().green(),
None => "None".yellow(), None => "None".yellow(),
}; };
println!("Configured {}: {}", protocol_name, cmd_name); writeln!(stdout, "Configured {}: {}", protocol_name, cmd_name)?;
if let Some(cmd) = server_cmd { if let Some(cmd) = server_cmd {
let path = match which::which(&cmd) { let path = match which::which(&cmd) {
Ok(path) => path.display().to_string().green(), Ok(path) => path.display().to_string().green(),
Err(_) => "Not found in $PATH".to_string().red(), Err(_) => "Not found in $PATH".to_string().red(),
}; };
println!("Binary for {}: {}", protocol_name, path); writeln!(stdout, "Binary for {}: {}", protocol_name, path)?;
} }
Ok(())
} }
/// Display diagnostics about a feature that requires tree-sitter /// Display diagnostics about a feature that requires tree-sitter
/// query files (highlights, textobjects, etc). /// query files (highlights, textobjects, etc).
fn probe_treesitter_feature(lang: &str, feature: TsFeature) { fn probe_treesitter_feature(lang: &str, feature: TsFeature) -> std::io::Result<()> {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
let found = match load_runtime_file(lang, feature.runtime_filename()).is_ok() { let found = match load_runtime_file(lang, feature.runtime_filename()).is_ok() {
true => "Found".green(), true => "Found".green(),
false => "Not found".red(), false => "Not found".red(),
}; };
println!("{} queries: {}", feature.short_title(), found); writeln!(stdout, "{} queries: {}", feature.short_title(), found)?;
Ok(())
}
pub fn print_health(health_arg: Option<String>) -> std::io::Result<()> {
match health_arg.as_deref() {
Some("all") => languages_all()?,
Some(lang) => language(lang.to_string())?,
None => {
general()?;
writeln!(std::io::stdout().lock())?;
languages_all()?;
}
}
Ok(())
} }

@ -88,16 +88,14 @@ FLAGS:
} }
if args.health { if args.health {
if let Some(lang) = args.health_arg { if let Err(err) = helix_term::health::print_health(args.health_arg) {
match lang.as_str() { // Piping to for example `head -10` requires special handling:
"all" => helix_term::health::languages_all(), // https://stackoverflow.com/a/65760807/7115678
_ => helix_term::health::language(lang), if err.kind() != std::io::ErrorKind::BrokenPipe {
return Err(err.into());
} }
} else {
helix_term::health::general();
println!();
helix_term::health::languages_all();
} }
std::process::exit(0); std::process::exit(0);
} }

Loading…
Cancel
Save