Skip to content

Commit 3eea2e6

Browse files
author
SqlRush
committed
Support grep multiline aliases
1 parent bee46a9 commit 3eea2e6

4 files changed

Lines changed: 53 additions & 6 deletions

File tree

docs/cc-100-roadmap.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,8 @@ M7 补充:prompt history `LogEntry` 读取现在接受 `sessionID`/`session`/`
788788

789789
本轮补充:`Grep` 常用布尔参数继续补齐 ripgrep 长参数 aliases,覆盖 `--line-number``--ignore-case``--fixed-strings``--word-regexp``--invert-match``--only-matching`,并兼容 quoted semantic boolean。
790790

791+
本轮补充:`Grep` multiline 搜索现在支持 ripgrep 风格 `-U``--multiline``multiline-dotall``--multiline-dotall` aliases,统一映射到既有跨行 dotall 匹配逻辑并兼容 quoted semantic boolean。
792+
791793
本轮补充:`Grep` 搜索现在支持 `no_ignore`/`noIgnore`/`no-ignore`/`--no-ignore`,可跳过 `.gitignore`/`.ignore` 规则,同时继续排除 VCS metadata 目录并保留 `Read(...)` deny 额外 ignore 保护;`--no-ignore` 兼容 quoted boolean。
792794

793795
本轮补充:`Grep``files_with_matches` 输出现在按官方行为使用文件修改时间倒序排序,mtime 相同再按路径排序;分页和 `head_limit` 会在排序后应用。

docs/claude-code-go-rewrite-plan.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ test/parity/ # golden tests against TS/official behavior
190190
- 本轮补充:Grep 文件列表输出现在显式支持 `files_with_match(es)`/`filesWithMatch(es)`/`files-with-match(es)`/`--files-with-match(es)`/`-l`,并接受 `output_mode``files_with_match` alias,统一归一为 `files_with_matches`
191191
- 本轮补充:Grep 路径过滤现在支持 ripgrep 风格 `--glob`/`-g``--type`/`-t` aliases,执行和 structured content 都统一使用归一化后的 glob/type 过滤值。
192192
- 本轮补充:Grep 常用布尔参数继续补齐 ripgrep 长参数 aliases,覆盖 `--line-number``--ignore-case``--fixed-strings``--word-regexp``--invert-match``--only-matching`,并兼容 quoted semantic boolean。
193+
- 本轮补充:Grep multiline 搜索现在支持 ripgrep 风格 `-U``--multiline``multiline-dotall``--multiline-dotall` aliases,统一映射到既有跨行 dotall 匹配逻辑并兼容 quoted semantic boolean。
193194
- 本轮补充:Grep 搜索现在支持 `no_ignore`/`noIgnore`/`no-ignore`/`--no-ignore`,可跳过 `.gitignore`/`.ignore` 规则,同时继续排除 VCS metadata 目录并保留 Read deny 额外 ignore 保护;`--no-ignore` 兼容 quoted boolean。
194195
- 本轮补充:Grep 的 `files_with_matches` 输出现在按官方行为使用文件修改时间倒序排序,mtime 相同再按路径排序;分页和 `head_limit` 会在排序后应用。
195196
- 本轮补充:Glob/Grep 搜索遍历现在会读取 permission context 中的 `Read(...)` deny 规则,并把对应 basename/path/directory pattern 作为额外 ignore rule,避免被禁止读取的文件出现在搜索结果中。

internal/tools/file/search_tools.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ var allowedGrepInputKeys = map[string]struct{}{
3232
"max_columns": {}, "maxColumns": {}, "max-columns": {}, "--max-columns": {},
3333
"context": {}, "-C": {}, "before_context": {}, "beforeContext": {}, "-B": {}, "after_context": {}, "afterContext": {}, "-A": {}, "line_numbers": {}, "lineNumbers": {}, "line-number": {}, "--line-number": {}, "-n": {},
3434
"ignore_case": {}, "case_insensitive": {}, "caseInsensitive": {}, "ignore-case": {}, "--ignore-case": {}, "-i": {},
35-
"fixed_strings": {}, "fixedStrings": {}, "fixed-strings": {}, "--fixed-strings": {}, "-F": {}, "multiline": {},
35+
"fixed_strings": {}, "fixedStrings": {}, "fixed-strings": {}, "--fixed-strings": {}, "-F": {}, "multiline": {}, "--multiline": {}, "multiline-dotall": {}, "--multiline-dotall": {}, "-U": {},
3636
"word_regexp": {}, "wordRegexp": {}, "word-regexp": {}, "--word-regexp": {}, "-w": {},
3737
"invert_match": {}, "invertMatch": {}, "invert-match": {}, "--invert-match": {}, "-v": {},
3838
"only_matching": {}, "onlyMatching": {}, "only-matching": {}, "--only-matching": {}, "-o": {},
@@ -51,7 +51,7 @@ var grepSemanticNumberKeys = map[string]struct{}{
5151
var grepSemanticBooleanKeys = map[string]struct{}{
5252
"line_numbers": {}, "lineNumbers": {}, "line-number": {}, "--line-number": {}, "-n": {},
5353
"ignore_case": {}, "case_insensitive": {}, "caseInsensitive": {}, "ignore-case": {}, "--ignore-case": {}, "-i": {},
54-
"fixed_strings": {}, "fixedStrings": {}, "fixed-strings": {}, "--fixed-strings": {}, "-F": {}, "multiline": {},
54+
"fixed_strings": {}, "fixedStrings": {}, "fixed-strings": {}, "--fixed-strings": {}, "-F": {}, "multiline": {}, "--multiline": {}, "multiline-dotall": {}, "--multiline-dotall": {}, "-U": {},
5555
"word_regexp": {}, "wordRegexp": {}, "word-regexp": {}, "--word-regexp": {}, "-w": {},
5656
"invert_match": {}, "invertMatch": {}, "invert-match": {}, "--invert-match": {}, "-v": {},
5757
"only_matching": {}, "onlyMatching": {}, "only-matching": {}, "--only-matching": {}, "-o": {},
@@ -154,6 +154,10 @@ type grepInput struct {
154154
NoIgnoreDash bool `json:"no-ignore,omitempty"`
155155
LongNoIgnore bool `json:"--no-ignore,omitempty"`
156156
Multiline bool `json:"multiline,omitempty"`
157+
LongMultiline bool `json:"--multiline,omitempty"`
158+
MultilineDotall bool `json:"multiline-dotall,omitempty"`
159+
LongMultilineDotall bool `json:"--multiline-dotall,omitempty"`
160+
ShortMultiline bool `json:"-U,omitempty"`
157161
}
158162

159163
type fileSearchMatch struct {
@@ -352,6 +356,14 @@ func NewGrepTool() tool.Tool {
352356
"no-ignore": map[string]any{"type": "boolean"},
353357
"--no-ignore": map[string]any{"type": "boolean"},
354358
"multiline": map[string]any{"type": "boolean"},
359+
"--multiline": map[string]any{"type": "boolean"},
360+
"multiline-dotall": map[string]any{
361+
"type": "boolean",
362+
},
363+
"--multiline-dotall": map[string]any{
364+
"type": "boolean",
365+
},
366+
"-U": map[string]any{"type": "boolean"},
355367
},
356368
},
357369
},
@@ -520,7 +532,7 @@ func callGrep(ctx tool.Context, raw json.RawMessage, _ tool.ProgressSink) (contr
520532
BeforeContext: before,
521533
AfterContext: after,
522534
LineNumbers: grepLineNumbers(input, mode),
523-
Multiline: input.Multiline,
535+
Multiline: grepMultiline(input),
524536
InvertMatch: grepInvertMatch(input),
525537
OnlyMatching: onlyMatching,
526538
CountMatches: countMatches,
@@ -563,7 +575,7 @@ func callGrep(ctx tool.Context, raw json.RawMessage, _ tool.ProgressSink) (contr
563575
"files_without_match": mode == "files_without_matches",
564576
"count_matches": countMatches,
565577
"no_ignore": noIgnore,
566-
"multiline": input.Multiline,
578+
"multiline": grepMultiline(input),
567579
"truncated": truncated,
568580
},
569581
}, nil
@@ -1204,11 +1216,11 @@ func compileGrepPattern(input grepInput) (*regexp.Regexp, error) {
12041216
pattern = `\b(?:` + pattern + `)\b`
12051217
}
12061218
switch {
1207-
case grepCaseInsensitive(input) && input.Multiline:
1219+
case grepCaseInsensitive(input) && grepMultiline(input):
12081220
pattern = "(?is:" + pattern + ")"
12091221
case grepCaseInsensitive(input):
12101222
pattern = "(?i:" + pattern + ")"
1211-
case input.Multiline:
1223+
case grepMultiline(input):
12121224
pattern = "(?s:" + pattern + ")"
12131225
}
12141226
return regexp.Compile(pattern)
@@ -1275,6 +1287,14 @@ func grepOnlyMatching(input grepInput) bool {
12751287
input.ShortOnlyMatching
12761288
}
12771289

1290+
func grepMultiline(input grepInput) bool {
1291+
return input.Multiline ||
1292+
input.LongMultiline ||
1293+
input.MultilineDotall ||
1294+
input.LongMultilineDotall ||
1295+
input.ShortMultiline
1296+
}
1297+
12781298
func grepFilesWithMatches(input grepInput) bool {
12791299
return input.FilesWithMatch ||
12801300
input.FilesWithMatchAlt ||

internal/tools/file/tools_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2160,6 +2160,30 @@ func TestGrepToolMultiline(t *testing.T) {
21602160
t.Fatalf("multiline structured matches = %#v", matches)
21612161
}
21622162

2163+
shortMultilineResult, err := executor.Execute(ctx, contracts.ToolUse{
2164+
ID: "toolu_grep_multiline_short",
2165+
Name: "Grep",
2166+
Input: json.RawMessage(`{"pattern":"alpha.*gamma","output_mode":"content","-U":"true"}`),
2167+
}, nil)
2168+
if err != nil {
2169+
t.Fatal(err)
2170+
}
2171+
if shortMultilineResult.Content != want || shortMultilineResult.StructuredContent["multiline"] != true {
2172+
t.Fatalf("short multiline result = %#v", shortMultilineResult)
2173+
}
2174+
2175+
dotallResult, err := executor.Execute(ctx, contracts.ToolUse{
2176+
ID: "toolu_grep_multiline_dotall",
2177+
Name: "Grep",
2178+
Input: json.RawMessage(`{"pattern":"alpha.*gamma","output_mode":"content","--multiline-dotall":"true"}`),
2179+
}, nil)
2180+
if err != nil {
2181+
t.Fatal(err)
2182+
}
2183+
if dotallResult.Content != want || dotallResult.StructuredContent["multiline"] != true {
2184+
t.Fatalf("multiline-dotall result = %#v", dotallResult)
2185+
}
2186+
21632187
invertResult, err := executor.Execute(ctx, contracts.ToolUse{
21642188
ID: "toolu_grep_multiline_invert",
21652189
Name: "Grep",

0 commit comments

Comments
 (0)