Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions changelog.d/unreleased/1525.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
category: fixed
issues:
- 1525
affected:
- src/CodeIndex/Cli/ConsoleUi.cs
- tests/CodeIndex.Tests/ConsoleUiTests.cs
---

## English

- **`cdidx --completions fish` now lists `--start` / `--end` for `excerpt`, restoring shell parity (#1525)** — the bash and zsh completion scripts already enumerated every `excerpt` flag, but the fish completion omitted `--start` and `--end`, so fish users typing `cdidx excerpt … --st<Tab>` or `--en<Tab>` got no suggestion even though both flags are required by the documented `cdidx excerpt <path> --start <line> [--end <line>] …` signature. The fish script now emits `complete -c cdidx -n '__fish_seen_subcommand_from excerpt' -l start` / `-l end` entries that mirror the bash/zsh tables, and a new `PrintCompletions_ExcerptFlagSetsMatchAcrossShells` test parses each shell's completion output for the `excerpt` branch and asserts the bash, zsh, and fish flag sets are identical (modulo the universal `--help`), so future shell-completion drift is caught at build time instead of in user reports.

## 日本語

- **`cdidx --completions fish` の `excerpt` 補完に `--start` / `--end` を追加し、シェル間で同等の補完を提供するようにしました (#1525)** — bash / zsh の補完スクリプトは `excerpt` の全フラグを列挙していましたが、fish 補完だけ `--start` / `--end` が抜けており、fish ユーザーが `cdidx excerpt … --st<Tab>` や `--en<Tab>` と入力しても補完候補が出ませんでした (これらは `cdidx excerpt <path> --start <line> [--end <line>] …` のドキュメント上必須のフラグです)。fish スクリプトに `complete -c cdidx -n '__fish_seen_subcommand_from excerpt' -l start` / `-l end` を追加して bash / zsh と揃え、さらに新しい `PrintCompletions_ExcerptFlagSetsMatchAcrossShells` テストが各シェルの `excerpt` 分岐から補完対象のフラグ集合を抽出し、bash / zsh / fish のフラグ集合が一致すること (どこでも有効な `--help` は除外) を検証することで、将来の補完スクリプト乖離をユーザー報告ではなくビルド時点で検出できるようにしました。
2 changes: 2 additions & 0 deletions src/CodeIndex/Cli/ConsoleUi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,8 @@ private static string GetFishCompletions()
lines.Add("complete -c cdidx -n '__fish_seen_subcommand_from find' -l query -r -d 'Literal query'");
lines.Add("complete -c cdidx -n '__fish_seen_subcommand_from find excerpt' -l before -r -d 'Context lines before'");
lines.Add("complete -c cdidx -n '__fish_seen_subcommand_from find excerpt' -l after -r -d 'Context lines after'");
lines.Add("complete -c cdidx -n '__fish_seen_subcommand_from excerpt' -l start -r -d 'Start line'");
lines.Add("complete -c cdidx -n '__fish_seen_subcommand_from excerpt' -l end -r -d 'End line'");
lines.Add("complete -c cdidx -n '__fish_seen_subcommand_from search references excerpt find inspect' -l max-line-width -r -d 'Clamp long single-line payloads (0 disables clamping)'");
lines.Add("complete -c cdidx -n '__fish_seen_subcommand_from excerpt' -l focus-line -r -d 'Focused line to keep visible when clamping'");
lines.Add("complete -c cdidx -n '__fish_seen_subcommand_from excerpt' -l focus-column -r -d 'Focused column to keep visible when clamping'");
Expand Down
67 changes: 67 additions & 0 deletions tests/CodeIndex.Tests/ConsoleUiTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Text.RegularExpressions;
using CodeIndex.Cli;

namespace CodeIndex.Tests;
Expand Down Expand Up @@ -293,6 +294,72 @@ public void PrintCompletions_BashAndZshKeepFocusOptionsExcerptOnly(string shell)
}
}

[Fact]
public void PrintCompletions_ExcerptFlagSetsMatchAcrossShells()
{
var bashExcerpt = ExtractBashSubcommandFlags(ConsoleUi.GetCompletionScript("bash"), "excerpt", "references");
var zshExcerpt = ExtractZshSubcommandFlags(ConsoleUi.GetCompletionScript("zsh"), "excerpt", "references");
var fishExcerpt = ExtractFishSubcommandFlags(ConsoleUi.GetCompletionScript("fish"), "excerpt");

// --help is universal in bash but is not enumerated by the zsh/fish scripts;
// exclude it from the parity comparison so the flag sets line up cleanly.
bashExcerpt.Remove("help");

Assert.Equal(bashExcerpt, zshExcerpt);
Assert.Equal(bashExcerpt, fishExcerpt);
// Sanity check: required excerpt flags are present in every shell.
foreach (var flag in new[] { "db", "json", "start", "end", "before", "after", "max-line-width", "focus-line", "focus-column", "focus-length" })
{
Assert.Contains(flag, bashExcerpt);
Assert.Contains(flag, zshExcerpt);
Assert.Contains(flag, fishExcerpt);
}
}

private static SortedSet<string> ExtractBashSubcommandFlags(string script, string subcommand, string nextSubcommand)
{
var startMarker = $"[ \"$cmd\" = \"{subcommand}\" ]; then";
var endMarker = $"[ \"$cmd\" = \"{nextSubcommand}\" ]; then";
var branch = ExtractBetween(script, startMarker, endMarker);
var quoted = Regex.Match(branch, "compgen -W \"(?<flags>[^\"]*)\"");
Assert.True(quoted.Success, $"bash branch for {subcommand} did not contain a compgen list");
var flags = new SortedSet<string>(StringComparer.Ordinal);
foreach (var token in quoted.Groups["flags"].Value.Split(' ', StringSplitOptions.RemoveEmptyEntries))
{
if (token.StartsWith("--", StringComparison.Ordinal) && token.Length > 2)
flags.Add(token[2..]);
}
return flags;
}

private static SortedSet<string> ExtractZshSubcommandFlags(string script, string subcommand, string nextSubcommand)
{
var startMarker = $"[[ $subcmd == {subcommand} ]]; then";
var endMarker = $"[[ $subcmd == {nextSubcommand} ]]; then";
var branch = ExtractBetween(script, startMarker, endMarker);
var flags = new SortedSet<string>(StringComparer.Ordinal);
foreach (Match match in Regex.Matches(branch, @"'--(?<name>[a-z][a-z0-9-]*)\["))
flags.Add(match.Groups["name"].Value);
return flags;
}

private static SortedSet<string> ExtractFishSubcommandFlags(string script, string subcommand)
{
var flags = new SortedSet<string>(StringComparer.Ordinal);
var pattern = new Regex($@"__fish_seen_subcommand_from\s+(?<list>[^']+)'\s+-l\s+(?<flag>[a-z][a-z0-9-]*)\b");
foreach (var line in script.Split('\n'))
{
var match = pattern.Match(line);
if (!match.Success)
continue;
var subcmds = match.Groups["list"].Value.Split(' ', StringSplitOptions.RemoveEmptyEntries);
if (Array.IndexOf(subcmds, subcommand) < 0)
continue;
flags.Add(match.Groups["flag"].Value);
}
return flags;
}

[Fact]
public void GetCompletionLangs_IncludesWindowsBatchAndYamlAliases()
{
Expand Down
Loading