Previously the three dynamic completion functions in cli_options.rs
(complete_preset, complete_mode, complete_backend) only returned
candidates whose names *started with* the typed prefix, and returned
no hint text alongside each candidate.
This meant that, for example, typing `hyfetch -p trans<TAB>` found
`transgender` but typing `hyfetch -p gender<TAB>` found nothing, even
though `gender` uniquely identifies `transgender`. The interactive flag
picker (used during `hyfetch --config`) already performs substring
matching; the shell completion was inconsistent with it.
Changes:
- Extracted a shared `ranked_completions()` helper that drives all
three functions. It uses `str::contains` instead of `str::starts_with`
so any substring of a candidate name triggers a match.
- Results are sorted so that prefix matches appear before other
substring matches, then by position of the match within the name,
then alphabetically — identical ordering to the interactive picker's
`filter_flag_indices`.
- Each completion entry now carries a short human-readable description
("pride flag preset", "color mode", "fetch backend") surfaced by
shells that display hints next to candidates (e.g. zsh with
`complete_help`, fish).
- Added three unit tests under `#[cfg(feature = "autocomplete")]`:
complete_preset_substring – "gender" matches "transgender"
complete_preset_prefix_ranked_first – "trans" puts prefix match first
complete_preset_descriptions – every result has a non-empty hint
All five tests (including the pre-existing check_options and
models::test) pass with `cargo test -p hyfetch --features autocomplete`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
termenv.py:
- unix_detect_ansi_mode: `TERM`/`COLORTERM` can be absent from the
environment (CI, cron, some IDEs). `os.environ.get('TERM')` returns
None in that case, so subsequent `term.startswith(...)` and
`'256color' in term` calls raised AttributeError. Changed to
`os.environ.get('TERM') or ''` for both variables.
- unix_read_osc: same None dereference on line 99 (`term.startswith`).
Applied the same `or ''` guard.
- unix_read_osc: `code.lstrip(start)` was used to strip the OSC prefix,
but `str.lstrip` strips individual *characters*, not a prefix string,
so it could silently over-consume leading bytes of the actual payload.
Replaced with an explicit `code[len(start):]` slice after a
`startswith` check.
- windows_detect_ansi_mode: `map(int, platform.version().split('.'))`
crashes with ValueError/TypeError on non-standard version strings.
Wrapped in try/except with a safe fallback of `'rgb'`.
`int(os.environ.get('ANSICON_VER'))` raised when the var was unset or
non-numeric; guarded with `.isdigit()` before converting.
color_util.py:
- RGB.to_ansi: for `mode == 'ansi'` it forwarded to `to_ansi_16` which
is an unimplemented stub (`raise NotImplementedError`). For
`mode == 'default'` and any other unknown mode the function fell
through and returned None, causing TypeError when callers concatenated
the result into strings (e.g. in presets.py). Both cases now fall back
to `to_ansi_8bit`, which is a correct and safe 256-color degradation.
Return type annotation updated to `str`.
neofetch_util.py:
- ensure_git_bash: `git_path` returned by the `if_file(...)` chain can
be None when no Git Bash installation is found on Windows. The
subsequent `git_path.is_file()` then raised AttributeError instead of
printing the friendly error message. Fixed to `if not git_path or not
git_path.is_file():`.
- get_distro_ascii: `run_neofetch_cmd` can return None when neofetch is
missing or exits non-zero without raising. The following `.replace()`
call would then crash with AttributeError. Added an explicit None
guard that prints an error and exits cleanly.
- run(): the backend dispatcher had no else/fallback branch, so an
unknown or mistyped backend silently returned None and produced no
output. Added `raise ValueError(f"Unknown backend: {backend!r}")`.
types.py:
- BackendLiteral was `Literal["neofetch", "fastfetch"]`, missing
`"qwqfetch"` and `"fastfetch-old"` which are both accepted by the
argparse parser and the run() dispatcher. Stale types caused false
type-checker warnings for valid inputs. Updated to include all four
supported backends.
main.py:
- select_lightness: the prompt advertises `.45` and `0.45` as valid
decimal inputs, but the parser called `int(lightness)` first, which
raises ValueError for any non-integer float, landing in the error
path before the `float()` branch was ever reached. Reordered to:
handle `%` suffix first, then `float()` for everything else, dividing
by 100 only when the value exceeds 1.
- June/pride-month check: `os.isatty(sys.stdout.fileno())` raises
io.UnsupportedOperation when stdout is piped or replaced (e.g. during
testing or when output is redirected). Replaced with the pattern
already used in termenv.py: `hasattr(sys.stdout, 'isatty') and
sys.stdout.isatty()`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* [+] Add paragender, paraboy, paragirl & paranonbinary flags
* [+] Add Cupio flags
* [C] Updated para- flags to be more faithful to the original designs using weights
* [+] Added alt versions of para- flags as faithful versions don't render well on some distro ascii
* [F] Fix color too thin will lead to being hidden
---------
Co-authored-by: obsoletedevgit <theohaines@yahoo.com>
Co-authored-by: Azalea <noreply@aza.moe>
* fix neowofetch failing to fetch system specs on Interix with a non-standard %WINDIR%
* fix memory being divided by 1024 too many times on Interix by neowofetch
* [F] Fix shell quoting
---------
Co-authored-by: Azalea <noreply@aza.moe>