fix(queryLanguage): allow parenthesized regex alternation in filter values - #946

Merged
brendan-kellam merged 4 commits into
mainfrom
brendan/fix-query-parser-paren-value
Feb 26, 2026
Merged

fix(queryLanguage): allow parenthesized regex alternation in filter values#946
brendan-kellam merged 4 commits into
mainfrom
brendan/fix-query-parser-paren-value

Conversation

@brendan-kellam

@brendan-kellambrendan-kellam commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Queries like file:(test|spec), -file:(test|spec), repo:(org1|org2), and sym:(Foo|Bar) previously failed with a parse error because the word tokenizer unconditionally deferred to parenToken whenever a token started with balanced parentheses, even in filter value contexts
  • Fixed by detecting value context (preceding non-whitespace char is :) and using depth-tracking to consume the entire (...) as a word rather than deferring
  • Added 8 test cases covering filter alternation for file:, repo:, sym:, content:, negated alternation, and combined queries

Test plan

  • file:(test|spec) parses as PrefixExpr(FileExpr)
  • -file:(test|spec) parses as NegateExpr(PrefixExpr(FileExpr))
  • chat lang:TypeScript -file:(test|spec) parses successfully ✓
  • Existing paren grouping (foo bar) still works as ParenExpr
  • -(file:test or file:spec) still works as NegateExpr(ParenExpr(...))
  • All 248 existing tests pass ✓

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Search query parser now accepts parenthesized regex alternation in filter values (e.g., file:(test|spec), -file:(test|spec), repo:(org1|org2)).
  • Tests

    • Added test cases for alternation in prefix values, negated prefixes, and combined prefix scenarios.
  • Chores

    • Updated CI workflow definitions and job naming.

…alues
Queries like `file:(test|spec)` or `-file:(test|spec)` previously failed
with "No parse at N" because the word tokenizer unconditionally deferred
to parenToken whenever a token started with balanced parentheses, even in
value contexts (right after a prefix keyword colon like `file:`, `repo:`,
`sym:`, etc.).
The fix detects value context by looking backward for a preceding ':' and,
when found, uses depth-tracking to consume the entire '(...)' as a word
instead of deferring. This correctly handles nested parens, stops at an
outer ParenExpr closing paren, and leaves all existing parse behaviour
unchanged for non-value contexts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between ff4fa79 and aa44ab9.

📒 Files selected for processing (2)
  • .github/workflows/test-backend.yml
  • .github/workflows/test.yml

✏️ Tip: You can disable in-progress messages and the fortune message in your review settings.

Walkthrough

Adds support for parenthesized regex alternation in search filter values (e.g., file:(test|spec), -repo:(org1|org2)). The tokenizer distinguishes parentheses used for value contexts (after :) from grouping, and tests and changelog entries were added.

Changes

Cohort / File(s)Summary
Changelog
CHANGELOG.md
Adds a Fixed entry documenting acceptance of parenthesized regex alternation in filter values.
Tokenizer Logic
packages/queryLanguage/src/tokens.ts
Updates wordToken to detect an opening ( in value contexts (preceded by :), consume balanced parentheses with depth tracking as part of the word token, and otherwise defer to existing paren tokenization.
Tests
packages/queryLanguage/test/prefixes.txt, packages/queryLanguage/test/negation.txt
Adds test cases covering parenthesized alternation in prefix values (file, repo, sym, content), combined prefix scenarios, and negated-prefix examples like `-file:(test

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately and concisely summarizes the main change: enabling parenthesized regex alternation in filter values.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch brendan/fix-query-parser-paren-value

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment threadCHANGELOG.md Outdated
Co-authored-by: Brendan Kellam <brendan-kellam@users.noreply.github.com>

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/queryLanguage/src/tokens.ts (1)

337-352: Consider handling escaped parentheses for consistency.

The depth-tracking loop doesn't skip escaped parentheses (e.g., \( or \)), while hasBalancedParensAt() at lines 124-127 does handle escapes. This inconsistency could cause unexpected behavior for patterns containing escaped parens like file:(test\)|spec).

If escaped parens in filter values are not a supported use case, this is fine. Otherwise, consider adding escape handling:

♻️ Proposed fix to handle escaped parens
 let depth = 0;
while (input.next !== EOF) {
const ch = input.next;
if (isWhitespace(ch)) break;
+ // Handle escaped characters - skip the next character after a backslash+ if (ch === 92 /* backslash */) {+ input.advance();+ if (input.next !== EOF) input.advance();+ continue;+ }
if (ch === OPEN_PAREN) {
depth++;
} else if (ch === CLOSE_PAREN) {
if (depth === 0) break; // outer ParenExpr closing — don't consume
depth--;
}
input.advance();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/queryLanguage/src/tokens.ts` around lines 337 - 352, The
depth-tracking loop inside the inValueParenContext branch doesn't account for
escaped parens, causing mismatched depth when encountering \(' or \)'; update
the loop (the code using input.next, input.advance, OPEN_PAREN and CLOSE_PAREN)
to detect and skip escaped characters (e.g., when a backslash precedes a paren)
so escaped '(' or ')' do not change depth or terminate the loop, and ensure this
behavior mirrors hasBalancedParensAt's escape handling for consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/queryLanguage/src/tokens.ts`:
- Around line 337-352: The depth-tracking loop inside the inValueParenContext
branch doesn't account for escaped parens, causing mismatched depth when
encountering \(' or \)'; update the loop (the code using input.next,
input.advance, OPEN_PAREN and CLOSE_PAREN) to detect and skip escaped characters
(e.g., when a backslash precedes a paren) so escaped '(' or ')' do not change
depth or terminate the loop, and ensure this behavior mirrors
hasBalancedParensAt's escape handling for consistency.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e3e0a6 and 3f7be64.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • packages/queryLanguage/src/tokens.ts
  • packages/queryLanguage/test/negation.txt
  • packages/queryLanguage/test/prefixes.txt

…orkflow
Replaces the two separate workflows with a single `test.yml` that runs
`yarn test` at the repo root, which executes all workspace tests
topologically via `yarn workspaces foreach`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@brendan-kellam
brendan-kellam merged commit da26b90 into mainFeb 26, 2026
6 of 7 checks passed
@brendan-kellam
brendan-kellam deleted the brendan/fix-query-parser-paren-value branch February 26, 2026 20:12
@github-actionsgithub-actionsBot mentioned this pull request Feb 26, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(queryLanguage): allow parenthesized regex alternation in filter values - #946

Merged
brendan-kellam merged 4 commits into
mainfrom
brendan/fix-query-parser-paren-value
Feb 26, 2026
Merged

fix(queryLanguage): allow parenthesized regex alternation in filter values#946
brendan-kellam merged 4 commits into
mainfrom
brendan/fix-query-parser-paren-value

Conversation

@brendan-kellam

@brendan-kellambrendan-kellam commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Queries like file:(test|spec), -file:(test|spec), repo:(org1|org2), and sym:(Foo|Bar) previously failed with a parse error because the word tokenizer unconditionally deferred to parenToken whenever a token started with balanced parentheses, even in filter value contexts
  • Fixed by detecting value context (preceding non-whitespace char is :) and using depth-tracking to consume the entire (...) as a word rather than deferring
  • Added 8 test cases covering filter alternation for file:, repo:, sym:, content:, negated alternation, and combined queries

Test plan

  • file:(test|spec) parses as PrefixExpr(FileExpr)
  • -file:(test|spec) parses as NegateExpr(PrefixExpr(FileExpr))
  • chat lang:TypeScript -file:(test|spec) parses successfully ✓
  • Existing paren grouping (foo bar) still works as ParenExpr
  • -(file:test or file:spec) still works as NegateExpr(ParenExpr(...))
  • All 248 existing tests pass ✓

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Search query parser now accepts parenthesized regex alternation in filter values (e.g., file:(test|spec), -file:(test|spec), repo:(org1|org2)).
  • Tests

    • Added test cases for alternation in prefix values, negated prefixes, and combined prefix scenarios.
  • Chores

    • Updated CI workflow definitions and job naming.

…alues
Queries like `file:(test|spec)` or `-file:(test|spec)` previously failed
with "No parse at N" because the word tokenizer unconditionally deferred
to parenToken whenever a token started with balanced parentheses, even in
value contexts (right after a prefix keyword colon like `file:`, `repo:`,
`sym:`, etc.).
The fix detects value context by looking backward for a preceding ':' and,
when found, uses depth-tracking to consume the entire '(...)' as a word
instead of deferring. This correctly handles nested parens, stops at an
outer ParenExpr closing paren, and leaves all existing parse behaviour
unchanged for non-value contexts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between ff4fa79 and aa44ab9.

📒 Files selected for processing (2)
  • .github/workflows/test-backend.yml
  • .github/workflows/test.yml

✏️ Tip: You can disable in-progress messages and the fortune message in your review settings.

Walkthrough

Adds support for parenthesized regex alternation in search filter values (e.g., file:(test|spec), -repo:(org1|org2)). The tokenizer distinguishes parentheses used for value contexts (after :) from grouping, and tests and changelog entries were added.

Changes

Cohort / File(s)Summary
Changelog
CHANGELOG.md
Adds a Fixed entry documenting acceptance of parenthesized regex alternation in filter values.
Tokenizer Logic
packages/queryLanguage/src/tokens.ts
Updates wordToken to detect an opening ( in value contexts (preceded by :), consume balanced parentheses with depth tracking as part of the word token, and otherwise defer to existing paren tokenization.
Tests
packages/queryLanguage/test/prefixes.txt, packages/queryLanguage/test/negation.txt
Adds test cases covering parenthesized alternation in prefix values (file, repo, sym, content), combined prefix scenarios, and negated-prefix examples like `-file:(test

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately and concisely summarizes the main change: enabling parenthesized regex alternation in filter values.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch brendan/fix-query-parser-paren-value

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment threadCHANGELOG.md Outdated
Co-authored-by: Brendan Kellam <brendan-kellam@users.noreply.github.com>

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/queryLanguage/src/tokens.ts (1)

337-352: Consider handling escaped parentheses for consistency.

The depth-tracking loop doesn't skip escaped parentheses (e.g., \( or \)), while hasBalancedParensAt() at lines 124-127 does handle escapes. This inconsistency could cause unexpected behavior for patterns containing escaped parens like file:(test\)|spec).

If escaped parens in filter values are not a supported use case, this is fine. Otherwise, consider adding escape handling:

♻️ Proposed fix to handle escaped parens
 let depth = 0;
while (input.next !== EOF) {
const ch = input.next;
if (isWhitespace(ch)) break;
+ // Handle escaped characters - skip the next character after a backslash+ if (ch === 92 /* backslash */) {+ input.advance();+ if (input.next !== EOF) input.advance();+ continue;+ }
if (ch === OPEN_PAREN) {
depth++;
} else if (ch === CLOSE_PAREN) {
if (depth === 0) break; // outer ParenExpr closing — don't consume
depth--;
}
input.advance();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/queryLanguage/src/tokens.ts` around lines 337 - 352, The
depth-tracking loop inside the inValueParenContext branch doesn't account for
escaped parens, causing mismatched depth when encountering \(' or \)'; update
the loop (the code using input.next, input.advance, OPEN_PAREN and CLOSE_PAREN)
to detect and skip escaped characters (e.g., when a backslash precedes a paren)
so escaped '(' or ')' do not change depth or terminate the loop, and ensure this
behavior mirrors hasBalancedParensAt's escape handling for consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/queryLanguage/src/tokens.ts`:
- Around line 337-352: The depth-tracking loop inside the inValueParenContext
branch doesn't account for escaped parens, causing mismatched depth when
encountering \(' or \)'; update the loop (the code using input.next,
input.advance, OPEN_PAREN and CLOSE_PAREN) to detect and skip escaped characters
(e.g., when a backslash precedes a paren) so escaped '(' or ')' do not change
depth or terminate the loop, and ensure this behavior mirrors
hasBalancedParensAt's escape handling for consistency.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e3e0a6 and 3f7be64.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • packages/queryLanguage/src/tokens.ts
  • packages/queryLanguage/test/negation.txt
  • packages/queryLanguage/test/prefixes.txt

…orkflow
Replaces the two separate workflows with a single `test.yml` that runs
`yarn test` at the repo root, which executes all workspace tests
topologically via `yarn workspaces foreach`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@brendan-kellam
brendan-kellam merged commit da26b90 into mainFeb 26, 2026
6 of 7 checks passed
@brendan-kellam
brendan-kellam deleted the brendan/fix-query-parser-paren-value branch February 26, 2026 20:12
@github-actionsgithub-actionsBot mentioned this pull request Feb 26, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(queryLanguage): allow parenthesized regex alternation in filter values - #946

Merged
brendan-kellam merged 4 commits into
mainfrom
brendan/fix-query-parser-paren-value
Feb 26, 2026
Merged

fix(queryLanguage): allow parenthesized regex alternation in filter values#946
brendan-kellam merged 4 commits into
mainfrom
brendan/fix-query-parser-paren-value

Conversation

@brendan-kellam

@brendan-kellambrendan-kellam commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Queries like file:(test|spec), -file:(test|spec), repo:(org1|org2), and sym:(Foo|Bar) previously failed with a parse error because the word tokenizer unconditionally deferred to parenToken whenever a token started with balanced parentheses, even in filter value contexts
  • Fixed by detecting value context (preceding non-whitespace char is :) and using depth-tracking to consume the entire (...) as a word rather than deferring
  • Added 8 test cases covering filter alternation for file:, repo:, sym:, content:, negated alternation, and combined queries

Test plan

  • file:(test|spec) parses as PrefixExpr(FileExpr)
  • -file:(test|spec) parses as NegateExpr(PrefixExpr(FileExpr))
  • chat lang:TypeScript -file:(test|spec) parses successfully ✓
  • Existing paren grouping (foo bar) still works as ParenExpr
  • -(file:test or file:spec) still works as NegateExpr(ParenExpr(...))
  • All 248 existing tests pass ✓

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Search query parser now accepts parenthesized regex alternation in filter values (e.g., file:(test|spec), -file:(test|spec), repo:(org1|org2)).
  • Tests

    • Added test cases for alternation in prefix values, negated prefixes, and combined prefix scenarios.
  • Chores

    • Updated CI workflow definitions and job naming.

…alues
Queries like `file:(test|spec)` or `-file:(test|spec)` previously failed
with "No parse at N" because the word tokenizer unconditionally deferred
to parenToken whenever a token started with balanced parentheses, even in
value contexts (right after a prefix keyword colon like `file:`, `repo:`,
`sym:`, etc.).
The fix detects value context by looking backward for a preceding ':' and,
when found, uses depth-tracking to consume the entire '(...)' as a word
instead of deferring. This correctly handles nested parens, stops at an
outer ParenExpr closing paren, and leaves all existing parse behaviour
unchanged for non-value contexts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between ff4fa79 and aa44ab9.

📒 Files selected for processing (2)
  • .github/workflows/test-backend.yml
  • .github/workflows/test.yml

✏️ Tip: You can disable in-progress messages and the fortune message in your review settings.

Walkthrough

Adds support for parenthesized regex alternation in search filter values (e.g., file:(test|spec), -repo:(org1|org2)). The tokenizer distinguishes parentheses used for value contexts (after :) from grouping, and tests and changelog entries were added.

Changes

Cohort / File(s)Summary
Changelog
CHANGELOG.md
Adds a Fixed entry documenting acceptance of parenthesized regex alternation in filter values.
Tokenizer Logic
packages/queryLanguage/src/tokens.ts
Updates wordToken to detect an opening ( in value contexts (preceded by :), consume balanced parentheses with depth tracking as part of the word token, and otherwise defer to existing paren tokenization.
Tests
packages/queryLanguage/test/prefixes.txt, packages/queryLanguage/test/negation.txt
Adds test cases covering parenthesized alternation in prefix values (file, repo, sym, content), combined prefix scenarios, and negated-prefix examples like `-file:(test

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately and concisely summarizes the main change: enabling parenthesized regex alternation in filter values.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch brendan/fix-query-parser-paren-value

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment threadCHANGELOG.md Outdated
Co-authored-by: Brendan Kellam <brendan-kellam@users.noreply.github.com>

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/queryLanguage/src/tokens.ts (1)

337-352: Consider handling escaped parentheses for consistency.

The depth-tracking loop doesn't skip escaped parentheses (e.g., \( or \)), while hasBalancedParensAt() at lines 124-127 does handle escapes. This inconsistency could cause unexpected behavior for patterns containing escaped parens like file:(test\)|spec).

If escaped parens in filter values are not a supported use case, this is fine. Otherwise, consider adding escape handling:

♻️ Proposed fix to handle escaped parens
 let depth = 0;
while (input.next !== EOF) {
const ch = input.next;
if (isWhitespace(ch)) break;
+ // Handle escaped characters - skip the next character after a backslash+ if (ch === 92 /* backslash */) {+ input.advance();+ if (input.next !== EOF) input.advance();+ continue;+ }
if (ch === OPEN_PAREN) {
depth++;
} else if (ch === CLOSE_PAREN) {
if (depth === 0) break; // outer ParenExpr closing — don't consume
depth--;
}
input.advance();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/queryLanguage/src/tokens.ts` around lines 337 - 352, The
depth-tracking loop inside the inValueParenContext branch doesn't account for
escaped parens, causing mismatched depth when encountering \(' or \)'; update
the loop (the code using input.next, input.advance, OPEN_PAREN and CLOSE_PAREN)
to detect and skip escaped characters (e.g., when a backslash precedes a paren)
so escaped '(' or ')' do not change depth or terminate the loop, and ensure this
behavior mirrors hasBalancedParensAt's escape handling for consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/queryLanguage/src/tokens.ts`:
- Around line 337-352: The depth-tracking loop inside the inValueParenContext
branch doesn't account for escaped parens, causing mismatched depth when
encountering \(' or \)'; update the loop (the code using input.next,
input.advance, OPEN_PAREN and CLOSE_PAREN) to detect and skip escaped characters
(e.g., when a backslash precedes a paren) so escaped '(' or ')' do not change
depth or terminate the loop, and ensure this behavior mirrors
hasBalancedParensAt's escape handling for consistency.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e3e0a6 and 3f7be64.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • packages/queryLanguage/src/tokens.ts
  • packages/queryLanguage/test/negation.txt
  • packages/queryLanguage/test/prefixes.txt

…orkflow
Replaces the two separate workflows with a single `test.yml` that runs
`yarn test` at the repo root, which executes all workspace tests
topologically via `yarn workspaces foreach`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@brendan-kellam
brendan-kellam merged commit da26b90 into mainFeb 26, 2026
6 of 7 checks passed
@brendan-kellam
brendan-kellam deleted the brendan/fix-query-parser-paren-value branch February 26, 2026 20:12
@github-actionsgithub-actionsBot mentioned this pull request Feb 26, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(queryLanguage): allow parenthesized regex alternation in filter values - #946

Merged
brendan-kellam merged 4 commits into
mainfrom
brendan/fix-query-parser-paren-value
Feb 26, 2026
Merged

fix(queryLanguage): allow parenthesized regex alternation in filter values#946
brendan-kellam merged 4 commits into
mainfrom
brendan/fix-query-parser-paren-value

Conversation

@brendan-kellam

@brendan-kellambrendan-kellam commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Queries like file:(test|spec), -file:(test|spec), repo:(org1|org2), and sym:(Foo|Bar) previously failed with a parse error because the word tokenizer unconditionally deferred to parenToken whenever a token started with balanced parentheses, even in filter value contexts
  • Fixed by detecting value context (preceding non-whitespace char is :) and using depth-tracking to consume the entire (...) as a word rather than deferring
  • Added 8 test cases covering filter alternation for file:, repo:, sym:, content:, negated alternation, and combined queries

Test plan

  • file:(test|spec) parses as PrefixExpr(FileExpr)
  • -file:(test|spec) parses as NegateExpr(PrefixExpr(FileExpr))
  • chat lang:TypeScript -file:(test|spec) parses successfully ✓
  • Existing paren grouping (foo bar) still works as ParenExpr
  • -(file:test or file:spec) still works as NegateExpr(ParenExpr(...))
  • All 248 existing tests pass ✓

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Search query parser now accepts parenthesized regex alternation in filter values (e.g., file:(test|spec), -file:(test|spec), repo:(org1|org2)).
  • Tests

    • Added test cases for alternation in prefix values, negated prefixes, and combined prefix scenarios.
  • Chores

    • Updated CI workflow definitions and job naming.

…alues
Queries like `file:(test|spec)` or `-file:(test|spec)` previously failed
with "No parse at N" because the word tokenizer unconditionally deferred
to parenToken whenever a token started with balanced parentheses, even in
value contexts (right after a prefix keyword colon like `file:`, `repo:`,
`sym:`, etc.).
The fix detects value context by looking backward for a preceding ':' and,
when found, uses depth-tracking to consume the entire '(...)' as a word
instead of deferring. This correctly handles nested parens, stops at an
outer ParenExpr closing paren, and leaves all existing parse behaviour
unchanged for non-value contexts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between ff4fa79 and aa44ab9.

📒 Files selected for processing (2)
  • .github/workflows/test-backend.yml
  • .github/workflows/test.yml

✏️ Tip: You can disable in-progress messages and the fortune message in your review settings.

Walkthrough

Adds support for parenthesized regex alternation in search filter values (e.g., file:(test|spec), -repo:(org1|org2)). The tokenizer distinguishes parentheses used for value contexts (after :) from grouping, and tests and changelog entries were added.

Changes

Cohort / File(s)Summary
Changelog
CHANGELOG.md
Adds a Fixed entry documenting acceptance of parenthesized regex alternation in filter values.
Tokenizer Logic
packages/queryLanguage/src/tokens.ts
Updates wordToken to detect an opening ( in value contexts (preceded by :), consume balanced parentheses with depth tracking as part of the word token, and otherwise defer to existing paren tokenization.
Tests
packages/queryLanguage/test/prefixes.txt, packages/queryLanguage/test/negation.txt
Adds test cases covering parenthesized alternation in prefix values (file, repo, sym, content), combined prefix scenarios, and negated-prefix examples like `-file:(test

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately and concisely summarizes the main change: enabling parenthesized regex alternation in filter values.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch brendan/fix-query-parser-paren-value

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment threadCHANGELOG.md Outdated
Co-authored-by: Brendan Kellam <brendan-kellam@users.noreply.github.com>

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/queryLanguage/src/tokens.ts (1)

337-352: Consider handling escaped parentheses for consistency.

The depth-tracking loop doesn't skip escaped parentheses (e.g., \( or \)), while hasBalancedParensAt() at lines 124-127 does handle escapes. This inconsistency could cause unexpected behavior for patterns containing escaped parens like file:(test\)|spec).

If escaped parens in filter values are not a supported use case, this is fine. Otherwise, consider adding escape handling:

♻️ Proposed fix to handle escaped parens
 let depth = 0;
while (input.next !== EOF) {
const ch = input.next;
if (isWhitespace(ch)) break;
+ // Handle escaped characters - skip the next character after a backslash+ if (ch === 92 /* backslash */) {+ input.advance();+ if (input.next !== EOF) input.advance();+ continue;+ }
if (ch === OPEN_PAREN) {
depth++;
} else if (ch === CLOSE_PAREN) {
if (depth === 0) break; // outer ParenExpr closing — don't consume
depth--;
}
input.advance();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/queryLanguage/src/tokens.ts` around lines 337 - 352, The
depth-tracking loop inside the inValueParenContext branch doesn't account for
escaped parens, causing mismatched depth when encountering \(' or \)'; update
the loop (the code using input.next, input.advance, OPEN_PAREN and CLOSE_PAREN)
to detect and skip escaped characters (e.g., when a backslash precedes a paren)
so escaped '(' or ')' do not change depth or terminate the loop, and ensure this
behavior mirrors hasBalancedParensAt's escape handling for consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/queryLanguage/src/tokens.ts`:
- Around line 337-352: The depth-tracking loop inside the inValueParenContext
branch doesn't account for escaped parens, causing mismatched depth when
encountering \(' or \)'; update the loop (the code using input.next,
input.advance, OPEN_PAREN and CLOSE_PAREN) to detect and skip escaped characters
(e.g., when a backslash precedes a paren) so escaped '(' or ')' do not change
depth or terminate the loop, and ensure this behavior mirrors
hasBalancedParensAt's escape handling for consistency.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e3e0a6 and 3f7be64.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • packages/queryLanguage/src/tokens.ts
  • packages/queryLanguage/test/negation.txt
  • packages/queryLanguage/test/prefixes.txt

…orkflow
Replaces the two separate workflows with a single `test.yml` that runs
`yarn test` at the repo root, which executes all workspace tests
topologically via `yarn workspaces foreach`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@brendan-kellam
brendan-kellam merged commit da26b90 into mainFeb 26, 2026
6 of 7 checks passed
@brendan-kellam
brendan-kellam deleted the brendan/fix-query-parser-paren-value branch February 26, 2026 20:12
@github-actionsgithub-actionsBot mentioned this pull request Feb 26, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(queryLanguage): allow parenthesized regex alternation in filter values - #946

Merged
brendan-kellam merged 4 commits into
mainfrom
brendan/fix-query-parser-paren-value
Feb 26, 2026
Merged

fix(queryLanguage): allow parenthesized regex alternation in filter values#946
brendan-kellam merged 4 commits into
mainfrom
brendan/fix-query-parser-paren-value

Conversation

@brendan-kellam

@brendan-kellambrendan-kellam commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Queries like file:(test|spec), -file:(test|spec), repo:(org1|org2), and sym:(Foo|Bar) previously failed with a parse error because the word tokenizer unconditionally deferred to parenToken whenever a token started with balanced parentheses, even in filter value contexts
  • Fixed by detecting value context (preceding non-whitespace char is :) and using depth-tracking to consume the entire (...) as a word rather than deferring
  • Added 8 test cases covering filter alternation for file:, repo:, sym:, content:, negated alternation, and combined queries

Test plan

  • file:(test|spec) parses as PrefixExpr(FileExpr)
  • -file:(test|spec) parses as NegateExpr(PrefixExpr(FileExpr))
  • chat lang:TypeScript -file:(test|spec) parses successfully ✓
  • Existing paren grouping (foo bar) still works as ParenExpr
  • -(file:test or file:spec) still works as NegateExpr(ParenExpr(...))
  • All 248 existing tests pass ✓

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Search query parser now accepts parenthesized regex alternation in filter values (e.g., file:(test|spec), -file:(test|spec), repo:(org1|org2)).
  • Tests

    • Added test cases for alternation in prefix values, negated prefixes, and combined prefix scenarios.
  • Chores

    • Updated CI workflow definitions and job naming.

…alues
Queries like `file:(test|spec)` or `-file:(test|spec)` previously failed
with "No parse at N" because the word tokenizer unconditionally deferred
to parenToken whenever a token started with balanced parentheses, even in
value contexts (right after a prefix keyword colon like `file:`, `repo:`,
`sym:`, etc.).
The fix detects value context by looking backward for a preceding ':' and,
when found, uses depth-tracking to consume the entire '(...)' as a word
instead of deferring. This correctly handles nested parens, stops at an
outer ParenExpr closing paren, and leaves all existing parse behaviour
unchanged for non-value contexts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between ff4fa79 and aa44ab9.

📒 Files selected for processing (2)
  • .github/workflows/test-backend.yml
  • .github/workflows/test.yml

✏️ Tip: You can disable in-progress messages and the fortune message in your review settings.

Walkthrough

Adds support for parenthesized regex alternation in search filter values (e.g., file:(test|spec), -repo:(org1|org2)). The tokenizer distinguishes parentheses used for value contexts (after :) from grouping, and tests and changelog entries were added.

Changes

Cohort / File(s)Summary
Changelog
CHANGELOG.md
Adds a Fixed entry documenting acceptance of parenthesized regex alternation in filter values.
Tokenizer Logic
packages/queryLanguage/src/tokens.ts
Updates wordToken to detect an opening ( in value contexts (preceded by :), consume balanced parentheses with depth tracking as part of the word token, and otherwise defer to existing paren tokenization.
Tests
packages/queryLanguage/test/prefixes.txt, packages/queryLanguage/test/negation.txt
Adds test cases covering parenthesized alternation in prefix values (file, repo, sym, content), combined prefix scenarios, and negated-prefix examples like `-file:(test

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately and concisely summarizes the main change: enabling parenthesized regex alternation in filter values.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch brendan/fix-query-parser-paren-value

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment threadCHANGELOG.md Outdated
Co-authored-by: Brendan Kellam <brendan-kellam@users.noreply.github.com>

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/queryLanguage/src/tokens.ts (1)

337-352: Consider handling escaped parentheses for consistency.

The depth-tracking loop doesn't skip escaped parentheses (e.g., \( or \)), while hasBalancedParensAt() at lines 124-127 does handle escapes. This inconsistency could cause unexpected behavior for patterns containing escaped parens like file:(test\)|spec).

If escaped parens in filter values are not a supported use case, this is fine. Otherwise, consider adding escape handling:

♻️ Proposed fix to handle escaped parens
 let depth = 0;
while (input.next !== EOF) {
const ch = input.next;
if (isWhitespace(ch)) break;
+ // Handle escaped characters - skip the next character after a backslash+ if (ch === 92 /* backslash */) {+ input.advance();+ if (input.next !== EOF) input.advance();+ continue;+ }
if (ch === OPEN_PAREN) {
depth++;
} else if (ch === CLOSE_PAREN) {
if (depth === 0) break; // outer ParenExpr closing — don't consume
depth--;
}
input.advance();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/queryLanguage/src/tokens.ts` around lines 337 - 352, The
depth-tracking loop inside the inValueParenContext branch doesn't account for
escaped parens, causing mismatched depth when encountering \(' or \)'; update
the loop (the code using input.next, input.advance, OPEN_PAREN and CLOSE_PAREN)
to detect and skip escaped characters (e.g., when a backslash precedes a paren)
so escaped '(' or ')' do not change depth or terminate the loop, and ensure this
behavior mirrors hasBalancedParensAt's escape handling for consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/queryLanguage/src/tokens.ts`:
- Around line 337-352: The depth-tracking loop inside the inValueParenContext
branch doesn't account for escaped parens, causing mismatched depth when
encountering \(' or \)'; update the loop (the code using input.next,
input.advance, OPEN_PAREN and CLOSE_PAREN) to detect and skip escaped characters
(e.g., when a backslash precedes a paren) so escaped '(' or ')' do not change
depth or terminate the loop, and ensure this behavior mirrors
hasBalancedParensAt's escape handling for consistency.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e3e0a6 and 3f7be64.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • packages/queryLanguage/src/tokens.ts
  • packages/queryLanguage/test/negation.txt
  • packages/queryLanguage/test/prefixes.txt

…orkflow
Replaces the two separate workflows with a single `test.yml` that runs
`yarn test` at the repo root, which executes all workspace tests
topologically via `yarn workspaces foreach`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@brendan-kellam
brendan-kellam merged commit da26b90 into mainFeb 26, 2026
6 of 7 checks passed
@brendan-kellam
brendan-kellam deleted the brendan/fix-query-parser-paren-value branch February 26, 2026 20:12
@github-actionsgithub-actionsBot mentioned this pull request Feb 26, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(queryLanguage): allow parenthesized regex alternation in filter values - #946

Merged
brendan-kellam merged 4 commits into
mainfrom
brendan/fix-query-parser-paren-value
Feb 26, 2026
Merged

fix(queryLanguage): allow parenthesized regex alternation in filter values#946
brendan-kellam merged 4 commits into
mainfrom
brendan/fix-query-parser-paren-value

Conversation

@brendan-kellam

@brendan-kellambrendan-kellam commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Queries like file:(test|spec), -file:(test|spec), repo:(org1|org2), and sym:(Foo|Bar) previously failed with a parse error because the word tokenizer unconditionally deferred to parenToken whenever a token started with balanced parentheses, even in filter value contexts
  • Fixed by detecting value context (preceding non-whitespace char is :) and using depth-tracking to consume the entire (...) as a word rather than deferring
  • Added 8 test cases covering filter alternation for file:, repo:, sym:, content:, negated alternation, and combined queries

Test plan

  • file:(test|spec) parses as PrefixExpr(FileExpr)
  • -file:(test|spec) parses as NegateExpr(PrefixExpr(FileExpr))
  • chat lang:TypeScript -file:(test|spec) parses successfully ✓
  • Existing paren grouping (foo bar) still works as ParenExpr
  • -(file:test or file:spec) still works as NegateExpr(ParenExpr(...))
  • All 248 existing tests pass ✓

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Search query parser now accepts parenthesized regex alternation in filter values (e.g., file:(test|spec), -file:(test|spec), repo:(org1|org2)).
  • Tests

    • Added test cases for alternation in prefix values, negated prefixes, and combined prefix scenarios.
  • Chores

    • Updated CI workflow definitions and job naming.

…alues
Queries like `file:(test|spec)` or `-file:(test|spec)` previously failed
with "No parse at N" because the word tokenizer unconditionally deferred
to parenToken whenever a token started with balanced parentheses, even in
value contexts (right after a prefix keyword colon like `file:`, `repo:`,
`sym:`, etc.).
The fix detects value context by looking backward for a preceding ':' and,
when found, uses depth-tracking to consume the entire '(...)' as a word
instead of deferring. This correctly handles nested parens, stops at an
outer ParenExpr closing paren, and leaves all existing parse behaviour
unchanged for non-value contexts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between ff4fa79 and aa44ab9.

📒 Files selected for processing (2)
  • .github/workflows/test-backend.yml
  • .github/workflows/test.yml

✏️ Tip: You can disable in-progress messages and the fortune message in your review settings.

Walkthrough

Adds support for parenthesized regex alternation in search filter values (e.g., file:(test|spec), -repo:(org1|org2)). The tokenizer distinguishes parentheses used for value contexts (after :) from grouping, and tests and changelog entries were added.

Changes

Cohort / File(s)Summary
Changelog
CHANGELOG.md
Adds a Fixed entry documenting acceptance of parenthesized regex alternation in filter values.
Tokenizer Logic
packages/queryLanguage/src/tokens.ts
Updates wordToken to detect an opening ( in value contexts (preceded by :), consume balanced parentheses with depth tracking as part of the word token, and otherwise defer to existing paren tokenization.
Tests
packages/queryLanguage/test/prefixes.txt, packages/queryLanguage/test/negation.txt
Adds test cases covering parenthesized alternation in prefix values (file, repo, sym, content), combined prefix scenarios, and negated-prefix examples like `-file:(test

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately and concisely summarizes the main change: enabling parenthesized regex alternation in filter values.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch brendan/fix-query-parser-paren-value

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment threadCHANGELOG.md Outdated
Co-authored-by: Brendan Kellam <brendan-kellam@users.noreply.github.com>

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/queryLanguage/src/tokens.ts (1)

337-352: Consider handling escaped parentheses for consistency.

The depth-tracking loop doesn't skip escaped parentheses (e.g., \( or \)), while hasBalancedParensAt() at lines 124-127 does handle escapes. This inconsistency could cause unexpected behavior for patterns containing escaped parens like file:(test\)|spec).

If escaped parens in filter values are not a supported use case, this is fine. Otherwise, consider adding escape handling:

♻️ Proposed fix to handle escaped parens
 let depth = 0;
while (input.next !== EOF) {
const ch = input.next;
if (isWhitespace(ch)) break;
+ // Handle escaped characters - skip the next character after a backslash+ if (ch === 92 /* backslash */) {+ input.advance();+ if (input.next !== EOF) input.advance();+ continue;+ }
if (ch === OPEN_PAREN) {
depth++;
} else if (ch === CLOSE_PAREN) {
if (depth === 0) break; // outer ParenExpr closing — don't consume
depth--;
}
input.advance();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/queryLanguage/src/tokens.ts` around lines 337 - 352, The
depth-tracking loop inside the inValueParenContext branch doesn't account for
escaped parens, causing mismatched depth when encountering \(' or \)'; update
the loop (the code using input.next, input.advance, OPEN_PAREN and CLOSE_PAREN)
to detect and skip escaped characters (e.g., when a backslash precedes a paren)
so escaped '(' or ')' do not change depth or terminate the loop, and ensure this
behavior mirrors hasBalancedParensAt's escape handling for consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/queryLanguage/src/tokens.ts`:
- Around line 337-352: The depth-tracking loop inside the inValueParenContext
branch doesn't account for escaped parens, causing mismatched depth when
encountering \(' or \)'; update the loop (the code using input.next,
input.advance, OPEN_PAREN and CLOSE_PAREN) to detect and skip escaped characters
(e.g., when a backslash precedes a paren) so escaped '(' or ')' do not change
depth or terminate the loop, and ensure this behavior mirrors
hasBalancedParensAt's escape handling for consistency.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e3e0a6 and 3f7be64.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • packages/queryLanguage/src/tokens.ts
  • packages/queryLanguage/test/negation.txt
  • packages/queryLanguage/test/prefixes.txt

…orkflow
Replaces the two separate workflows with a single `test.yml` that runs
`yarn test` at the repo root, which executes all workspace tests
topologically via `yarn workspaces foreach`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@brendan-kellam
brendan-kellam merged commit da26b90 into mainFeb 26, 2026
6 of 7 checks passed
@brendan-kellam
brendan-kellam deleted the brendan/fix-query-parser-paren-value branch February 26, 2026 20:12
@github-actionsgithub-actionsBot mentioned this pull request Feb 26, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(queryLanguage): allow parenthesized regex alternation in filter values - #946

Merged
brendan-kellam merged 4 commits into
mainfrom
brendan/fix-query-parser-paren-value
Feb 26, 2026
Merged

fix(queryLanguage): allow parenthesized regex alternation in filter values#946
brendan-kellam merged 4 commits into
mainfrom
brendan/fix-query-parser-paren-value

Conversation

@brendan-kellam

@brendan-kellambrendan-kellam commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Queries like file:(test|spec), -file:(test|spec), repo:(org1|org2), and sym:(Foo|Bar) previously failed with a parse error because the word tokenizer unconditionally deferred to parenToken whenever a token started with balanced parentheses, even in filter value contexts
  • Fixed by detecting value context (preceding non-whitespace char is :) and using depth-tracking to consume the entire (...) as a word rather than deferring
  • Added 8 test cases covering filter alternation for file:, repo:, sym:, content:, negated alternation, and combined queries

Test plan

  • file:(test|spec) parses as PrefixExpr(FileExpr)
  • -file:(test|spec) parses as NegateExpr(PrefixExpr(FileExpr))
  • chat lang:TypeScript -file:(test|spec) parses successfully ✓
  • Existing paren grouping (foo bar) still works as ParenExpr
  • -(file:test or file:spec) still works as NegateExpr(ParenExpr(...))
  • All 248 existing tests pass ✓

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Search query parser now accepts parenthesized regex alternation in filter values (e.g., file:(test|spec), -file:(test|spec), repo:(org1|org2)).
  • Tests

    • Added test cases for alternation in prefix values, negated prefixes, and combined prefix scenarios.
  • Chores

    • Updated CI workflow definitions and job naming.

…alues
Queries like `file:(test|spec)` or `-file:(test|spec)` previously failed
with "No parse at N" because the word tokenizer unconditionally deferred
to parenToken whenever a token started with balanced parentheses, even in
value contexts (right after a prefix keyword colon like `file:`, `repo:`,
`sym:`, etc.).
The fix detects value context by looking backward for a preceding ':' and,
when found, uses depth-tracking to consume the entire '(...)' as a word
instead of deferring. This correctly handles nested parens, stops at an
outer ParenExpr closing paren, and leaves all existing parse behaviour
unchanged for non-value contexts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between ff4fa79 and aa44ab9.

📒 Files selected for processing (2)
  • .github/workflows/test-backend.yml
  • .github/workflows/test.yml

✏️ Tip: You can disable in-progress messages and the fortune message in your review settings.

Walkthrough

Adds support for parenthesized regex alternation in search filter values (e.g., file:(test|spec), -repo:(org1|org2)). The tokenizer distinguishes parentheses used for value contexts (after :) from grouping, and tests and changelog entries were added.

Changes

Cohort / File(s)Summary
Changelog
CHANGELOG.md
Adds a Fixed entry documenting acceptance of parenthesized regex alternation in filter values.
Tokenizer Logic
packages/queryLanguage/src/tokens.ts
Updates wordToken to detect an opening ( in value contexts (preceded by :), consume balanced parentheses with depth tracking as part of the word token, and otherwise defer to existing paren tokenization.
Tests
packages/queryLanguage/test/prefixes.txt, packages/queryLanguage/test/negation.txt
Adds test cases covering parenthesized alternation in prefix values (file, repo, sym, content), combined prefix scenarios, and negated-prefix examples like `-file:(test

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately and concisely summarizes the main change: enabling parenthesized regex alternation in filter values.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch brendan/fix-query-parser-paren-value

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment threadCHANGELOG.md Outdated
Co-authored-by: Brendan Kellam <brendan-kellam@users.noreply.github.com>

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/queryLanguage/src/tokens.ts (1)

337-352: Consider handling escaped parentheses for consistency.

The depth-tracking loop doesn't skip escaped parentheses (e.g., \( or \)), while hasBalancedParensAt() at lines 124-127 does handle escapes. This inconsistency could cause unexpected behavior for patterns containing escaped parens like file:(test\)|spec).

If escaped parens in filter values are not a supported use case, this is fine. Otherwise, consider adding escape handling:

♻️ Proposed fix to handle escaped parens
 let depth = 0;
while (input.next !== EOF) {
const ch = input.next;
if (isWhitespace(ch)) break;
+ // Handle escaped characters - skip the next character after a backslash+ if (ch === 92 /* backslash */) {+ input.advance();+ if (input.next !== EOF) input.advance();+ continue;+ }
if (ch === OPEN_PAREN) {
depth++;
} else if (ch === CLOSE_PAREN) {
if (depth === 0) break; // outer ParenExpr closing — don't consume
depth--;
}
input.advance();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/queryLanguage/src/tokens.ts` around lines 337 - 352, The
depth-tracking loop inside the inValueParenContext branch doesn't account for
escaped parens, causing mismatched depth when encountering \(' or \)'; update
the loop (the code using input.next, input.advance, OPEN_PAREN and CLOSE_PAREN)
to detect and skip escaped characters (e.g., when a backslash precedes a paren)
so escaped '(' or ')' do not change depth or terminate the loop, and ensure this
behavior mirrors hasBalancedParensAt's escape handling for consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/queryLanguage/src/tokens.ts`:
- Around line 337-352: The depth-tracking loop inside the inValueParenContext
branch doesn't account for escaped parens, causing mismatched depth when
encountering \(' or \)'; update the loop (the code using input.next,
input.advance, OPEN_PAREN and CLOSE_PAREN) to detect and skip escaped characters
(e.g., when a backslash precedes a paren) so escaped '(' or ')' do not change
depth or terminate the loop, and ensure this behavior mirrors
hasBalancedParensAt's escape handling for consistency.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e3e0a6 and 3f7be64.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • packages/queryLanguage/src/tokens.ts
  • packages/queryLanguage/test/negation.txt
  • packages/queryLanguage/test/prefixes.txt

…orkflow
Replaces the two separate workflows with a single `test.yml` that runs
`yarn test` at the repo root, which executes all workspace tests
topologically via `yarn workspaces foreach`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@brendan-kellam
brendan-kellam merged commit da26b90 into mainFeb 26, 2026
6 of 7 checks passed
@brendan-kellam
brendan-kellam deleted the brendan/fix-query-parser-paren-value branch February 26, 2026 20:12
@github-actionsgithub-actionsBot mentioned this pull request Feb 26, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(queryLanguage): allow parenthesized regex alternation in filter values - #946

Merged
brendan-kellam merged 4 commits into
mainfrom
brendan/fix-query-parser-paren-value
Feb 26, 2026
Merged

fix(queryLanguage): allow parenthesized regex alternation in filter values#946
brendan-kellam merged 4 commits into
mainfrom
brendan/fix-query-parser-paren-value

Conversation

@brendan-kellam

@brendan-kellambrendan-kellam commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Queries like file:(test|spec), -file:(test|spec), repo:(org1|org2), and sym:(Foo|Bar) previously failed with a parse error because the word tokenizer unconditionally deferred to parenToken whenever a token started with balanced parentheses, even in filter value contexts
  • Fixed by detecting value context (preceding non-whitespace char is :) and using depth-tracking to consume the entire (...) as a word rather than deferring
  • Added 8 test cases covering filter alternation for file:, repo:, sym:, content:, negated alternation, and combined queries

Test plan

  • file:(test|spec) parses as PrefixExpr(FileExpr)
  • -file:(test|spec) parses as NegateExpr(PrefixExpr(FileExpr))
  • chat lang:TypeScript -file:(test|spec) parses successfully ✓
  • Existing paren grouping (foo bar) still works as ParenExpr
  • -(file:test or file:spec) still works as NegateExpr(ParenExpr(...))
  • All 248 existing tests pass ✓

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Search query parser now accepts parenthesized regex alternation in filter values (e.g., file:(test|spec), -file:(test|spec), repo:(org1|org2)).
  • Tests

    • Added test cases for alternation in prefix values, negated prefixes, and combined prefix scenarios.
  • Chores

    • Updated CI workflow definitions and job naming.

…alues
Queries like `file:(test|spec)` or `-file:(test|spec)` previously failed
with "No parse at N" because the word tokenizer unconditionally deferred
to parenToken whenever a token started with balanced parentheses, even in
value contexts (right after a prefix keyword colon like `file:`, `repo:`,
`sym:`, etc.).
The fix detects value context by looking backward for a preceding ':' and,
when found, uses depth-tracking to consume the entire '(...)' as a word
instead of deferring. This correctly handles nested parens, stops at an
outer ParenExpr closing paren, and leaves all existing parse behaviour
unchanged for non-value contexts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between ff4fa79 and aa44ab9.

📒 Files selected for processing (2)
  • .github/workflows/test-backend.yml
  • .github/workflows/test.yml

✏️ Tip: You can disable in-progress messages and the fortune message in your review settings.

Walkthrough

Adds support for parenthesized regex alternation in search filter values (e.g., file:(test|spec), -repo:(org1|org2)). The tokenizer distinguishes parentheses used for value contexts (after :) from grouping, and tests and changelog entries were added.

Changes

Cohort / File(s)Summary
Changelog
CHANGELOG.md
Adds a Fixed entry documenting acceptance of parenthesized regex alternation in filter values.
Tokenizer Logic
packages/queryLanguage/src/tokens.ts
Updates wordToken to detect an opening ( in value contexts (preceded by :), consume balanced parentheses with depth tracking as part of the word token, and otherwise defer to existing paren tokenization.
Tests
packages/queryLanguage/test/prefixes.txt, packages/queryLanguage/test/negation.txt
Adds test cases covering parenthesized alternation in prefix values (file, repo, sym, content), combined prefix scenarios, and negated-prefix examples like `-file:(test

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately and concisely summarizes the main change: enabling parenthesized regex alternation in filter values.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch brendan/fix-query-parser-paren-value

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment threadCHANGELOG.md Outdated
Co-authored-by: Brendan Kellam <brendan-kellam@users.noreply.github.com>

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/queryLanguage/src/tokens.ts (1)

337-352: Consider handling escaped parentheses for consistency.

The depth-tracking loop doesn't skip escaped parentheses (e.g., \( or \)), while hasBalancedParensAt() at lines 124-127 does handle escapes. This inconsistency could cause unexpected behavior for patterns containing escaped parens like file:(test\)|spec).

If escaped parens in filter values are not a supported use case, this is fine. Otherwise, consider adding escape handling:

♻️ Proposed fix to handle escaped parens
 let depth = 0;
while (input.next !== EOF) {
const ch = input.next;
if (isWhitespace(ch)) break;
+ // Handle escaped characters - skip the next character after a backslash+ if (ch === 92 /* backslash */) {+ input.advance();+ if (input.next !== EOF) input.advance();+ continue;+ }
if (ch === OPEN_PAREN) {
depth++;
} else if (ch === CLOSE_PAREN) {
if (depth === 0) break; // outer ParenExpr closing — don't consume
depth--;
}
input.advance();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/queryLanguage/src/tokens.ts` around lines 337 - 352, The
depth-tracking loop inside the inValueParenContext branch doesn't account for
escaped parens, causing mismatched depth when encountering \(' or \)'; update
the loop (the code using input.next, input.advance, OPEN_PAREN and CLOSE_PAREN)
to detect and skip escaped characters (e.g., when a backslash precedes a paren)
so escaped '(' or ')' do not change depth or terminate the loop, and ensure this
behavior mirrors hasBalancedParensAt's escape handling for consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/queryLanguage/src/tokens.ts`:
- Around line 337-352: The depth-tracking loop inside the inValueParenContext
branch doesn't account for escaped parens, causing mismatched depth when
encountering \(' or \)'; update the loop (the code using input.next,
input.advance, OPEN_PAREN and CLOSE_PAREN) to detect and skip escaped characters
(e.g., when a backslash precedes a paren) so escaped '(' or ')' do not change
depth or terminate the loop, and ensure this behavior mirrors
hasBalancedParensAt's escape handling for consistency.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e3e0a6 and 3f7be64.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • packages/queryLanguage/src/tokens.ts
  • packages/queryLanguage/test/negation.txt
  • packages/queryLanguage/test/prefixes.txt

…orkflow
Replaces the two separate workflows with a single `test.yml` that runs
`yarn test` at the repo root, which executes all workspace tests
topologically via `yarn workspaces foreach`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@brendan-kellam
brendan-kellam merged commit da26b90 into mainFeb 26, 2026
6 of 7 checks passed
@brendan-kellam
brendan-kellam deleted the brendan/fix-query-parser-paren-value branch February 26, 2026 20:12
@github-actionsgithub-actionsBot mentioned this pull request Feb 26, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@brendan-kellam