From 73559f84b34590255a51d1cee1adb236757c7793 Mon Sep 17 00:00:00 2001 From: wwenrr Date: Fri, 3 Apr 2026 14:54:24 +0000 Subject: [PATCH 1/2] fix(docs): validate --selection-by-title format early --- shortcuts/doc/docs_update.go | 17 ++++++++++++ shortcuts/doc/docs_update_test.go | 44 +++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/shortcuts/doc/docs_update.go b/shortcuts/doc/docs_update.go index 5c64b7cc..efe60d5e 100644 --- a/shortcuts/doc/docs_update.go +++ b/shortcuts/doc/docs_update.go @@ -62,6 +62,9 @@ var DocsUpdate = common.Shortcut{ if needsSelection[mode] && selEllipsis == "" && selTitle == "" { return common.FlagErrorf("--%s mode requires --selection-with-ellipsis or --selection-by-title", mode) } + if err := validateSelectionByTitle(selTitle); err != nil { + return err + } return nil }, @@ -156,3 +159,17 @@ func normalizeBoardTokens(raw interface{}) []string { return []string{} } } + +func validateSelectionByTitle(title string) error { + if title == "" { + return nil + } + trimmed := strings.TrimSpace(title) + if strings.HasPrefix(trimmed, "#") { + return nil + } + if strings.Contains(trimmed, "\n") || strings.Contains(trimmed, "\r") { + return common.FlagErrorf("--selection-by-title must be a single heading line (for example: '## Section')") + } + return common.FlagErrorf("--selection-by-title must include markdown heading prefix '#'. Example: --selection-by-title '## Section'") +} diff --git a/shortcuts/doc/docs_update_test.go b/shortcuts/doc/docs_update_test.go index d0845e36..92ac3cab 100644 --- a/shortcuts/doc/docs_update_test.go +++ b/shortcuts/doc/docs_update_test.go @@ -4,6 +4,7 @@ package doc import ( "reflect" + "strings" "testing" ) @@ -76,3 +77,46 @@ func TestNormalizeDocsUpdateResult(t *testing.T) { } }) } + +func TestValidateSelectionByTitle(t *testing.T) { + t.Run("empty title passes", func(t *testing.T) { + if err := validateSelectionByTitle(""); err != nil { + t.Fatalf("expected nil error, got %v", err) + } + }) + + t.Run("heading style title passes", func(t *testing.T) { + if err := validateSelectionByTitle("## 第二章"); err != nil { + t.Fatalf("expected nil error, got %v", err) + } + }) + + t.Run("plain text title fails with guidance", func(t *testing.T) { + err := validateSelectionByTitle("第二章") + if err == nil { + t.Fatalf("expected validation error") + } + if got := err.Error(); got == "" || !containsAll(got, "selection-by-title", "heading prefix") { + t.Fatalf("unexpected error: %v", err) + } + }) + + t.Run("multi-line title fails", func(t *testing.T) { + err := validateSelectionByTitle("第二章\n第三章") + if err == nil { + t.Fatalf("expected validation error") + } + if got := err.Error(); got == "" || !containsAll(got, "single heading line") { + t.Fatalf("unexpected error: %v", err) + } + }) +} + +func containsAll(s string, tokens ...string) bool { + for _, token := range tokens { + if !strings.Contains(s, token) { + return false + } + } + return true +} From e50a006851375540ddf88104cdb6bac7e908cda7 Mon Sep 17 00:00:00 2001 From: wwenrr Date: Fri, 3 Apr 2026 14:59:33 +0000 Subject: [PATCH 2/2] fix(docs): reject multiline selection-by-title before prefix check --- shortcuts/doc/docs_update.go | 6 +++--- shortcuts/doc/docs_update_test.go | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/shortcuts/doc/docs_update.go b/shortcuts/doc/docs_update.go index efe60d5e..64298721 100644 --- a/shortcuts/doc/docs_update.go +++ b/shortcuts/doc/docs_update.go @@ -165,11 +165,11 @@ func validateSelectionByTitle(title string) error { return nil } trimmed := strings.TrimSpace(title) - if strings.HasPrefix(trimmed, "#") { - return nil - } if strings.Contains(trimmed, "\n") || strings.Contains(trimmed, "\r") { return common.FlagErrorf("--selection-by-title must be a single heading line (for example: '## Section')") } + if strings.HasPrefix(trimmed, "#") { + return nil + } return common.FlagErrorf("--selection-by-title must include markdown heading prefix '#'. Example: --selection-by-title '## Section'") } diff --git a/shortcuts/doc/docs_update_test.go b/shortcuts/doc/docs_update_test.go index 92ac3cab..b4270def 100644 --- a/shortcuts/doc/docs_update_test.go +++ b/shortcuts/doc/docs_update_test.go @@ -101,6 +101,16 @@ func TestValidateSelectionByTitle(t *testing.T) { } }) + t.Run("multi-line heading still fails", func(t *testing.T) { + err := validateSelectionByTitle("## 第二章\n## 第三章") + if err == nil { + t.Fatalf("expected validation error") + } + if got := err.Error(); got == "" || !containsAll(got, "single heading line") { + t.Fatalf("unexpected error: %v", err) + } + }) + t.Run("multi-line title fails", func(t *testing.T) { err := validateSelectionByTitle("第二章\n第三章") if err == nil {