Skip to content

Commit 4367312

Browse files
perf(mcp): filter suffix globs while building search scope
Signed-off-by: Ertan <ertan.kucukoglu@gmail.com>
1 parent 010569f commit 4367312

3 files changed

Lines changed: 60 additions & 33 deletions

File tree

‎src/mcp/mcp.c‎

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8930,6 +8930,36 @@ bool cbm_search_code_file_pattern_can_prefilter(const char *file_pattern) {
89308930
return true;
89318931
}
89328932

8933+
bool cbm_search_code_windows_path_matches_prefilter(const char *path, const char *file_pattern) {
8934+
if (!path || !cbm_search_code_file_pattern_can_prefilter(file_pattern)) {
8935+
return false;
8936+
}
8937+
8938+
const char *suffix = file_pattern + 1;
8939+
size_t path_len = strlen(path);
8940+
size_t suffix_len = strlen(suffix);
8941+
if (path_len < suffix_len) {
8942+
return false;
8943+
}
8944+
8945+
const unsigned char *candidate = (const unsigned char *)path + path_len - suffix_len;
8946+
const unsigned char *expected = (const unsigned char *)suffix;
8947+
for (size_t i = 0; i < suffix_len; i++) {
8948+
unsigned char left = candidate[i];
8949+
unsigned char right = expected[i];
8950+
if (left >= 'A' && left <= 'Z') {
8951+
left = (unsigned char)(left - 'A' + 'a');
8952+
}
8953+
if (right >= 'A' && right <= 'Z') {
8954+
right = (unsigned char)(right - 'A' + 'a');
8955+
}
8956+
if (left != right) {
8957+
return false;
8958+
}
8959+
}
8960+
return true;
8961+
}
8962+
89338963
/* Build the grep/search command string based on scoped vs recursive mode.
89348964
* On Windows, uses PowerShell Select-String with tab-delimited output.
89358965
* On POSIX, uses grep with colon-delimited output. */
@@ -8950,30 +8980,16 @@ void cbm_search_code_build_grep_cmd(char *cmd, size_t cmd_sz, bool use_regex, bo
89508980
const char *sm = use_regex ? "" : " -SimpleMatch";
89518981
if (scoped) {
89528982
if (file_pattern) {
8953-
if (cbm_search_code_file_pattern_can_prefilter(file_pattern)) {
8954-
snprintf(
8955-
cmd, cmd_sz,
8956-
"powershell -Command \"" CBM_PS_UTF8_PRELUDE
8957-
"$pat = Get-Content -Encoding UTF8 -LiteralPath '%s'; "
8958-
"Get-Content -Encoding UTF8 -LiteralPath '%s'"
8959-
" | Where-Object { $_ -like '%s' }"
8960-
" | ForEach-Object { Select-String -LiteralPath $_ -Pattern $pat%s "
8961-
"-ErrorAction SilentlyContinue }"
8962-
" | Where-Object { $_.Path -like '*%s' }"
8963-
" | ForEach-Object { $_.Path + [char]9 + $_.LineNumber + [char]9 + $_.Line }\"",
8964-
tmpfile, filelist, file_pattern, sm, file_pattern);
8965-
} else {
8966-
snprintf(
8967-
cmd, cmd_sz,
8968-
"powershell -Command \"" CBM_PS_UTF8_PRELUDE
8969-
"$pat = Get-Content -Encoding UTF8 -LiteralPath '%s'; "
8970-
"Get-Content -Encoding UTF8 -LiteralPath '%s' | ForEach-Object { Select-String "
8971-
"-LiteralPath $_ -Pattern $pat%s "
8972-
"-ErrorAction SilentlyContinue }"
8973-
" | Where-Object { $_.Path -like '*%s' }"
8974-
" | ForEach-Object { $_.Path + [char]9 + $_.LineNumber + [char]9 + $_.Line }\"",
8975-
tmpfile, filelist, sm, file_pattern);
8976-
}
8983+
snprintf(
8984+
cmd, cmd_sz,
8985+
"powershell -Command \"" CBM_PS_UTF8_PRELUDE
8986+
"$pat = Get-Content -Encoding UTF8 -LiteralPath '%s'; "
8987+
"Get-Content -Encoding UTF8 -LiteralPath '%s' | ForEach-Object { Select-String "
8988+
"-LiteralPath $_ -Pattern $pat%s "
8989+
"-ErrorAction SilentlyContinue }"
8990+
" | Where-Object { $_.Path -like '*%s' }"
8991+
" | ForEach-Object { $_.Path + [char]9 + $_.LineNumber + [char]9 + $_.Line }\"",
8992+
tmpfile, filelist, sm, file_pattern);
89778993
} else {
89788994
snprintf(
89798995
cmd, cmd_sz,
@@ -9611,8 +9627,8 @@ static void classify_all_grep_hits(grep_match_t *gm, int gm_count, cbm_store_t *
96119627
* created inside the private scratch directory; this function never opens or
96129628
* closes it, so the list is never reachable through a predictable pathname. */
96139629
static bool write_scoped_filelist(cbm_mcp_server_t *srv, const char *project, const char *root_path,
9614-
FILE *fl, bool has_path_filter, cbm_regex_t *path_regex,
9615-
int *out_written) {
9630+
FILE *fl, const char *file_pattern, bool has_path_filter,
9631+
cbm_regex_t *path_regex, int *out_written) {
96169632
*out_written = 0;
96179633
cbm_store_t *pre_store = resolve_store(srv, project);
96189634
if (!pre_store) {
@@ -9644,6 +9660,12 @@ static bool write_scoped_filelist(cbm_mcp_server_t *srv, const char *project, co
96449660
continue;
96459661
}
96469662
}
9663+
#ifdef _WIN32
9664+
if (cbm_search_code_file_pattern_can_prefilter(file_pattern) &&
9665+
!cbm_search_code_windows_path_matches_prefilter(indexed_files[fi], file_pattern)) {
9666+
continue;
9667+
}
9668+
#endif
96479669
/* Write "<root>/<file>" piece-by-piece (no fixed-size buffer, so an
96489670
* arbitrarily long absolute path cannot overflow). Forward slash join
96499671
* so xargs doesn't treat Windows backslashes as escapes; binary mode
@@ -10046,8 +10068,9 @@ static char *handle_search_code(cbm_mcp_server_t *srv, const char *args) {
1004610068
int scoped_written = 0;
1004710069

1004810070
uint64_t scope_t0 = metrics.include_phase_timings ? cbm_now_ms() : 0;
10049-
scoped = write_scoped_filelist(srv, project, root_path, scratch.filelist, has_path_filter,
10050-
has_path_filter ? &path_regex : NULL, &scoped_written);
10071+
scoped = write_scoped_filelist(srv, project, root_path, scratch.filelist, file_pattern,
10072+
has_path_filter, has_path_filter ? &path_regex : NULL,
10073+
&scoped_written);
1005110074
/* Close before grep runs: this is what flushes the records the helper wrote
1005210075
* through the descriptor. Clearing the field hands ownership to
1005310076
* search_scratch_close, which still unlinks the file itself. */

‎src/mcp/mcp_internal.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ bool cbm_detect_node_in_hunks(const cbm_node_t *node, const cbm_changed_hunk_t *
5656
* PowerShell -like contract. Exposed for
5757
* direct boundary tests only. */
5858
bool cbm_search_code_file_pattern_can_prefilter(const char *file_pattern);
59+
bool cbm_search_code_windows_path_matches_prefilter(const char *path, const char *file_pattern);
5960

6061
/* Internal command builder exposed so tests can pin the PowerShell pipeline
6162
* ordering without

‎tests/test_mcp.c‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4944,22 +4944,25 @@ TEST(search_code_file_pattern_prefilter_boundaries) {
49444944
ASSERT_FALSE(cbm_search_code_file_pattern_can_prefilter("src\\*.pas"));
49454945
ASSERT_FALSE(cbm_search_code_file_pattern_can_prefilter("*.c++"));
49464946
ASSERT_FALSE(cbm_search_code_file_pattern_can_prefilter("*R&D*.go"));
4947+
4948+
ASSERT_TRUE(cbm_search_code_windows_path_matches_prefilter("src/UnitMain.PAS", "*.pas"));
4949+
ASSERT_TRUE(cbm_search_code_windows_path_matches_prefilter("types/index.D.TS", "*.d.ts"));
4950+
ASSERT_FALSE(cbm_search_code_windows_path_matches_prefilter("src/UnitMain.pas.bak", "*.pas"));
4951+
ASSERT_FALSE(cbm_search_code_windows_path_matches_prefilter("types/index.ts", "*.d.ts"));
49474952
PASS();
49484953
}
49494954

4950-
TEST(search_code_windows_prefilter_precedes_content_scan) {
4955+
TEST(search_code_windows_scope_prefilter_removes_pipeline_filter) {
49514956
#ifdef _WIN32
49524957
char command[CBM_SZ_4K];
49534958
cbm_search_code_build_grep_cmd(command, sizeof(command), false, true, "*.go", "C:/tmp/pattern",
49544959
"C:/tmp/filelist", "C:/tmp/root");
49554960

4956-
const char *prefilter = strstr(command, "Where-Object { $_ -like '*.go' }");
49574961
const char *content_scan = strstr(command, "ForEach-Object { Select-String");
49584962
const char *postfilter = strstr(command, "Where-Object { $_.Path -like '**.go' }");
4959-
ASSERT_NOT_NULL(prefilter);
4963+
ASSERT_NULL(strstr(command, "Where-Object { $_ -like '*.go' }"));
49604964
ASSERT_NOT_NULL(content_scan);
49614965
ASSERT_NOT_NULL(postfilter);
4962-
ASSERT_TRUE(prefilter < content_scan);
49634966
ASSERT_TRUE(content_scan < postfilter);
49644967

49654968
cbm_search_code_build_grep_cmd(command, sizeof(command), false, true, "*handler*.go",
@@ -11380,7 +11383,7 @@ SUITE(mcp) {
1138011383
RUN_TEST(search_code_path_filter_prefilter_keeps_matches);
1138111384
RUN_TEST(search_code_path_filter_matches_nothing);
1138211385
RUN_TEST(search_code_file_pattern_prefilter_boundaries);
11383-
RUN_TEST(search_code_windows_prefilter_precedes_content_scan);
11386+
RUN_TEST(search_code_windows_scope_prefilter_removes_pipeline_filter);
1138411387
RUN_TEST(search_code_windows_cancel_cleans_supervised_scan);
1138511388
RUN_TEST(search_code_windows_output_limit_fails_closed_and_cleans_scan);
1138611389
RUN_TEST(search_code_windows_scan_pins_utf8_output);

0 commit comments

Comments
 (0)