diff --git a/docs/onefetch.1 b/docs/onefetch.1 index 6875d068e..30adb170e 100644 --- a/docs/onefetch.1 +++ b/docs/onefetch.1 @@ -87,6 +87,8 @@ Count hidden files and directories .IP Filters output by language type .IP +If no language of the selected TYPE(s) is found, falls back to counting every language type instead of showing no languages at all +.IP [default: programming markup] [possible values: programming, markup, prose, data] .SS "TEXT FORMATTING:" diff --git a/docs/wiki/command-line-options.md b/docs/wiki/command-line-options.md index da83e822c..fa796ae34 100644 --- a/docs/wiki/command-line-options.md +++ b/docs/wiki/command-line-options.md @@ -63,6 +63,8 @@ INFO: -T, --type ... Filters output by language type + If no language of the selected TYPE(s) is found, falls back to counting every language type instead of showing no languages at all + [default: programming markup] [possible values: programming, markup, prose, data] diff --git a/src/cli.rs b/src/cli.rs index 68e548c66..d66960eb8 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -110,6 +110,9 @@ pub struct InfoCliOptions { #[arg(long)] pub include_hidden: bool, /// Filters output by language type + /// + /// If no language of the selected TYPE(s) is found, falls back to counting + /// every language type instead of showing no languages at all #[arg( long, num_args = 1.., diff --git a/src/info/langs/language.tera b/src/info/langs/language.tera index 48ad03958..d09edd3bd 100644 --- a/src/info/langs/language.tera +++ b/src/info/langs/language.tera @@ -13,7 +13,7 @@ pub struct Colors { const DEFAULT_CHIP_ICON: char = '\u{25CF}'; -#[derive(Clone, PartialEq, Eq, Debug, clap::ValueEnum)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, EnumIter, clap::ValueEnum)] pub enum LanguageType { Programming, Markup, diff --git a/src/info/langs/mod.rs b/src/info/langs/mod.rs index 5ee95c300..2b5966cde 100644 --- a/src/info/langs/mod.rs +++ b/src/info/langs/mod.rs @@ -13,6 +13,10 @@ pub fn get_main_language(loc_by_language: &[(Language, usize)]) -> Language { /// Returns a vector of tuples containing all the languages detected inside the repository. /// Each tuple is composed of the language and its corresponding loc (lines of code). /// The vector is sorted by loc in descending order. +/// +/// If the given `language_types` filter doesn't match any language in the repository, +/// this falls back to counting every language type instead of reporting nothing. +/// See . pub fn get_loc_by_language_sorted( dir: &Path, globs_to_exclude: &[String], @@ -20,10 +24,21 @@ pub fn get_loc_by_language_sorted( include_hidden: bool, ) -> Option> { let locs = get_locs(dir, globs_to_exclude, language_types, include_hidden); - let loc_by_language_opt = get_loc_by_language(&locs); + let loc_by_language_opt = get_loc_by_language(&locs).or_else(|| { + if all_types_selected(language_types) { + return None; + } + let all_types: Vec = LanguageType::iter().collect(); + let locs = get_locs(dir, globs_to_exclude, &all_types, include_hidden); + get_loc_by_language(&locs) + }); loc_by_language_opt.map(sort_by_loc) } +fn all_types_selected(language_types: &[LanguageType]) -> bool { + LanguageType::iter().all(|t| language_types.contains(&t)) +} + fn sort_by_loc(map: HashMap) -> Vec<(Language, usize)> { let mut vec: Vec<(Language, usize)> = map.into_iter().collect(); vec.sort_by_key(|b| cmp::Reverse(b.1)); @@ -200,4 +215,15 @@ mod test { let loc_by_language = [(Language::JavaScript, 100), (Language::Markdown, 300)]; assert_eq!(get_total_loc(&loc_by_language), 400); } + + #[test] + fn all_types_selected_is_true_for_every_language_type() { + let all_types: Vec = LanguageType::iter().collect(); + assert!(all_types_selected(&all_types)); + } + + #[test] + fn all_types_selected_is_false_for_a_partial_selection() { + assert!(!all_types_selected(&[LanguageType::Programming])); + } } diff --git a/tests/fixtures/make_repo_with_only_prose.sh b/tests/fixtures/make_repo_with_only_prose.sh new file mode 100644 index 000000000..54bf694b4 --- /dev/null +++ b/tests/fixtures/make_repo_with_only_prose.sh @@ -0,0 +1,21 @@ +set -eu -o pipefail + +git init -q + +# BOTH NAME AND EMAIL ARE NEEDED FOR RECOGNITION +git config --local --add "committer.name" "onefetch-committer-name" +git config --local --add "committer.email" "onefetch-committer-email@onefetch.com" + +git remote add origin https://github.com/user/repo.git + +git checkout -b main + +# Markdown is prose, so the default `--type programming markup` matches nothing +# here and onefetch has to fall back to every type (cf. #1705) +cat <<'EOF' > README.md +# Title + +Some prose, and no code at all. +EOF +git add README.md +git commit -q -m c1 diff --git a/tests/repo.rs b/tests/repo.rs index 376fa7735..e0c795c6f 100644 --- a/tests/repo.rs +++ b/tests/repo.rs @@ -1,6 +1,7 @@ use anyhow::Result; use gix::{Repository, ThreadSafeRepository, open}; use onefetch::cli::{CliOptions, InfoCliOptions, TextForamttingCliOptions}; +use onefetch::info::langs::language::Language; use onefetch::info::{build_info, get_work_dir}; fn repo(name: &str) -> Result { @@ -116,3 +117,18 @@ fn test_repo_without_code() -> Result<()> { let _info = build_info(&config).expect("no error"); Ok(()) } + +// https://github.com/o2sh/onefetch/issues/1705 +#[test] +fn test_repo_with_only_prose_falls_back_to_all_language_types() -> Result<()> { + let repo = repo("make_repo_with_only_prose.sh")?; + let config: CliOptions = CliOptions { + input: repo.path().to_path_buf(), + ..Default::default() + }; + let info = build_info(&config)?; + // The default `--type programming markup` matches nothing in this repo, so + // without the fallback there is no dominant language and no Languages field. + assert_eq!(info.dominant_language, Some(Language::Markdown)); + Ok(()) +}