diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e43dd48c..4348570d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Upgraded `tar` to `^7.5.22`. [#1472](https://github.com/sourcebot-dev/sourcebot/pull/1472) - Fixed GitLab topic filters being incorrectly case-sensitive. [#1393](https://github.com/sourcebot-dev/sourcebot/pull/1393) - 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) +- 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) ## [5.1.5] - 2026-07-31 diff --git a/packages/queryLanguage/src/tokens.ts b/packages/queryLanguage/src/tokens.ts index 1bb69f531..043462200 100644 --- a/packages/queryLanguage/src/tokens.ts +++ b/packages/queryLanguage/src/tokens.ts @@ -673,27 +673,12 @@ export const negateToken = new ExternalTokenizer((input, stack) => { } } - // Check if followed by a prefix keyword (by checking for keyword followed by colon) - let foundColon = false; - let peekOffset = offset; - - while (true) { - const ch = input.peek(peekOffset); - if (ch === EOF) break; - - if (ch === COLON) { - foundColon = true; - break; - } - // Hit a delimiter (whitespace, paren, or quote) - not a prefix keyword - if (isWhitespace(ch) || ch === OPEN_PAREN || ch === CLOSE_PAREN || ch === QUOTE) { - break; - } - peekOffset++; - } - - if (foundColon) { - // It's a prefix keyword, accept as negate + // Only accept as negate when the dash is immediately followed by a known + // prefix keyword (e.g. `-file:`). A bare word that merely contains a colon + // (e.g. `-foo:bar`, `-http://x`) is not a prefix and must be left for + // wordToken; otherwise the grammar has no PrefixExpr to follow the negate + // token and the (strict) parser throws a SyntaxError. + if (startsWithPrefixAt(input, offset)) { input.advance(); input.acceptToken(negate); return; diff --git a/packages/queryLanguage/test/negation.txt b/packages/queryLanguage/test/negation.txt index 105c96347..b1d8755aa 100644 --- a/packages/queryLanguage/test/negation.txt +++ b/packages/queryLanguage/test/negation.txt @@ -277,3 +277,27 @@ chat lang:TypeScript -file:(test|spec) ==> Program(AndExpr(Term,PrefixExpr(LangExpr),NegateExpr(PrefixExpr(FileExpr)))) + +# Dash term with non-prefix colon word (e.g. a URL) stays a single Term + +-http://example.com + +==> + +Program(Term) + +# Dash term with unknown colon key stays a single Term + +-time:12 + +==> + +Program(Term) + +# Prefix combined with a negated non-prefix colon word + +repo:x -foo:bar + +==> + +Program(AndExpr(PrefixExpr(RepoExpr),Term))