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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 0 additions & 28 deletions .github/workflows/test-backend.yml

This file was deleted.

15 changes: 7 additions & 8 deletions .github/workflows/test-web.yml → .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
name: Test Web
name: Test

on:
pull_request:
branches: ["main"]


jobs:
build:
test:
runs-on: ubuntu-latest
permissions:
contents: read
Expand All @@ -15,14 +15,13 @@ jobs:
uses: actions/checkout@v4
with:
submodules: "true"
- name: Use Node.Js
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
node-version: '20.x'

- name: Install
run: yarn install --frozen-lockfile

- name: Test
run: yarn workspace @sourcebot/web test

run: yarn test
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed
- Fixed search query parser rejecting parenthesized regex alternation in filter values (e.g. `file:(test|spec)`, `-file:(test|spec)`). [#946](https://github.com/sourcebot-dev/sourcebot/pull/946)

## [4.12.0] - 2026-02-26

### Added
Expand Down
41 changes: 36 additions & 5 deletions packages/queryLanguage/src/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,14 +313,44 @@ export const wordToken = new ExternalTokenizer((input, stack) => {
return;
}

// If starts with '(' and has balanced parens, don't consume as word
// (let parenToken handle it)
// If starts with '(' and has balanced parens, determine whether this is a
// regex alternation value (e.g. file:(test|spec)) or a ParenExpr grouping.
// We're in a value context when the immediately preceding non-whitespace char
// is ':', meaning we're right after a prefix keyword. In that case consume the
// entire '(...)' as a word using depth-tracking so the consuming loop doesn't
// stop early at ')'. Otherwise defer to parenToken for grouping.
let inValueParenContext = false;
if (input.next === OPEN_PAREN && hasBalancedParensAt(input, 0)) {
return;
let backOffset = -1;
while (isWhitespace(input.peek(backOffset))) {
backOffset--;
}
if (input.peek(backOffset) === COLON) {
inValueParenContext = true;
} else {
return; // Not a value context — defer to parenToken for grouping
}
}

const startPos = input.pos;

if (inValueParenContext) {
// Consume the parenthesized pattern with depth tracking so we consume
// the matching ')' without stopping early. A ')' at depth 0 means we've
// hit an outer ParenExpr closing paren — stop without consuming it.
let depth = 0;
while (input.next !== EOF) {
const ch = input.next;
if (isWhitespace(ch)) break;
if (ch === OPEN_PAREN) {
depth++;
} else if (ch === CLOSE_PAREN) {
if (depth === 0) break; // outer ParenExpr closing — don't consume
depth--;
}
input.advance();
}
} else {
// Consume characters
while (input.next !== EOF) {
const ch = input.next;
Expand All @@ -339,7 +369,8 @@ export const wordToken = new ExternalTokenizer((input, stack) => {

input.advance();
}

}

if (input.pos > startPos) {
input.acceptToken(word);
}
Expand Down
24 changes: 24 additions & 0 deletions packages/queryLanguage/test/negation.txt
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,27 @@ Program(NegateExpr(ParenExpr))
==>

Program(NegateExpr(PrefixExpr(FileExpr)))

# Negate file with regex alternation in value

-file:(test|spec)

==>

Program(NegateExpr(PrefixExpr(FileExpr)))

# Negate repo with regex alternation in value

-repo:(org1|org2)

==>

Program(NegateExpr(PrefixExpr(RepoExpr)))

# Complex query with negated file alternation

chat lang:TypeScript -file:(test|spec)

==>

Program(AndExpr(Term,PrefixExpr(LangExpr),NegateExpr(PrefixExpr(FileExpr))))
40 changes: 40 additions & 0 deletions packages/queryLanguage/test/prefixes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -334,3 +334,43 @@ Program(ParenExpr(PrefixExpr(FileExpr)))

Program(ParenExpr(AndExpr(PrefixExpr(FileExpr),PrefixExpr(LangExpr))))

# File with regex alternation in value

file:(test|spec)

==>

Program(PrefixExpr(FileExpr))

# Repo with regex alternation in value

repo:(org1|org2)

==>

Program(PrefixExpr(RepoExpr))

# Sym with regex alternation in value

sym:(Foo|Bar)

==>

Program(PrefixExpr(SymExpr))

# Content with regex alternation in value

content:(error|warning)

==>

Program(PrefixExpr(ContentExpr))

# File alternation combined with other filters

file:(test|spec) lang:TypeScript

==>

Program(AndExpr(PrefixExpr(FileExpr),PrefixExpr(LangExpr)))