Skip to content

Commit b4a1c06

Browse files
fix(query-language): don't treat dash before a non-prefix colon word as negation (#1301)
* fix(query-language): don't treat a dash before a non-prefix colon word as negation A query like `-foo:bar` or `-http://example.com` raised a SyntaxError. The negate tokenizer accepted `-` as negation whenever any colon appeared in the following word, but the grammar only allows a NegateExpr to wrap a known PrefixExpr. With no matching prefix, the strict parser used in search failed the whole query. Now negation is only emitted when the dash is immediately followed by a known prefix keyword; other colon-bearing words parse as a single Term. * fix changelog --------- Co-authored-by: Brendan Kellam <brendan@sourcebot.dev>
1 parent a6c7695 commit b4a1c06

3 files changed

Lines changed: 31 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- Upgraded `tar` to `^7.5.22`. [#1472](https://github.com/sourcebot-dev/sourcebot/pull/1472)
1313
- Fixed GitLab topic filters being incorrectly case-sensitive. [#1393](https://github.com/sourcebot-dev/sourcebot/pull/1393)
1414
- Fixed a crash when searching with `context:` referencing a search context that does not exist; it now returns a graceful error. [#1362](https://github.com/sourcebot-dev/sourcebot/pull/1362)
15+
- Fixed search queries failing to parse when negating a bare term that contains a colon (e.g. `-foo:bar`, `-http://example.com`). [#1301](https://github.com/sourcebot-dev/sourcebot/pull/1301)
1516

1617
## [5.1.5] - 2026-07-31
1718

packages/queryLanguage/src/tokens.ts

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -673,27 +673,12 @@ export const negateToken = new ExternalTokenizer((input, stack) => {
673673
}
674674
}
675675

676-
// Check if followed by a prefix keyword (by checking for keyword followed by colon)
677-
let foundColon = false;
678-
let peekOffset = offset;
679-
680-
while (true) {
681-
const ch = input.peek(peekOffset);
682-
if (ch === EOF) break;
683-
684-
if (ch === COLON) {
685-
foundColon = true;
686-
break;
687-
}
688-
// Hit a delimiter (whitespace, paren, or quote) - not a prefix keyword
689-
if (isWhitespace(ch) || ch === OPEN_PAREN || ch === CLOSE_PAREN || ch === QUOTE) {
690-
break;
691-
}
692-
peekOffset++;
693-
}
694-
695-
if (foundColon) {
696-
// It's a prefix keyword, accept as negate
676+
// Only accept as negate when the dash is immediately followed by a known
677+
// prefix keyword (e.g. `-file:`). A bare word that merely contains a colon
678+
// (e.g. `-foo:bar`, `-http://x`) is not a prefix and must be left for
679+
// wordToken; otherwise the grammar has no PrefixExpr to follow the negate
680+
// token and the (strict) parser throws a SyntaxError.
681+
if (startsWithPrefixAt(input, offset)) {
697682
input.advance();
698683
input.acceptToken(negate);
699684
return;

packages/queryLanguage/test/negation.txt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,3 +277,27 @@ chat lang:TypeScript -file:(test|spec)
277277
==>
278278

279279
Program(AndExpr(Term,PrefixExpr(LangExpr),NegateExpr(PrefixExpr(FileExpr))))
280+
281+
# Dash term with non-prefix colon word (e.g. a URL) stays a single Term
282+
283+
-http://example.com
284+
285+
==>
286+
287+
Program(Term)
288+
289+
# Dash term with unknown colon key stays a single Term
290+
291+
-time:12
292+
293+
==>
294+
295+
Program(Term)
296+
297+
# Prefix combined with a negated non-prefix colon word
298+
299+
repo:x -foo:bar
300+
301+
==>
302+
303+
Program(AndExpr(PrefixExpr(RepoExpr),Term))

0 commit comments

Comments
 (0)