From bdf8eefd9a698a24d9f94b590884de42691b3786 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 15 Jan 2026 04:01:07 +0000 Subject: [PATCH] fix: trim whitespace from prefix expression values in query parser This fixes an issue where whitespace between a prefix keyword and its value (e.g., 'repo: localhost:12030/testrepo' with a space after 'repo:') would cause the value to include the leading space, resulting in failed searches. The grammar captures text from the start of the prefix keyword to the end of the value, so any whitespace between them was included. By trimming the extracted value, we ensure proper matching. Also adds test cases for: - Repos with ports in URLs (e.g., localhost:12030/testrepo) - Multiple repos with ports in reposet filters - Combined repoNamesFilter and repoNamesFilterRegexp with ports Co-authored-by: michael --- packages/queryLanguage/test/prefixes.txt | 48 ++++++++++++++++++++ packages/web/src/features/chat/utils.test.ts | 37 +++++++++++++++ packages/web/src/features/search/parser.ts | 6 ++- 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/packages/queryLanguage/test/prefixes.txt b/packages/queryLanguage/test/prefixes.txt index 00533ec03..ddbd6315c 100644 --- a/packages/queryLanguage/test/prefixes.txt +++ b/packages/queryLanguage/test/prefixes.txt @@ -334,3 +334,51 @@ Program(ParenExpr(PrefixExpr(FileExpr))) Program(ParenExpr(AndExpr(PrefixExpr(FileExpr),PrefixExpr(LangExpr)))) +# Repo with port in URL + +repo:localhost:12030/testrepo + +==> + +Program(PrefixExpr(RepoExpr)) + +# Repo short form with port in URL + +r:myserver:3000/project + +==> + +Program(PrefixExpr(RepoExpr)) + +# RepoSet with port in URL + +reposet:localhost:12030/testrepo + +==> + +Program(PrefixExpr(RepoSetExpr)) + +# RepoSet with multiple repos with ports + +reposet:myserver:12030/repo1,localhost:3000/repo2 + +==> + +Program(PrefixExpr(RepoSetExpr)) + +# Term and repo with port + +test.cpp repo:localhost:12030/testrepo + +==> + +Program(AndExpr(Term,PrefixExpr(RepoExpr))) + +# Term and reposet with port + +test.cpp reposet:myserver:12030/testrepo + +==> + +Program(AndExpr(Term,PrefixExpr(RepoSetExpr))) + diff --git a/packages/web/src/features/chat/utils.test.ts b/packages/web/src/features/chat/utils.test.ts index 698fbe421..484b2ad36 100644 --- a/packages/web/src/features/chat/utils.test.ts +++ b/packages/web/src/features/chat/utils.test.ts @@ -511,4 +511,41 @@ test('buildSearchQuery handles query with special characters', () => { }); expect(result).toBe('console.log("hello world") reposet:test-repo'); +}); + +test('buildSearchQuery handles repoNamesFilter with port in URL', () => { + const result = buildSearchQuery({ + query: 'test.cpp', + repoNamesFilter: ['localhost:12030/testrepo'] + }); + + expect(result).toBe('test.cpp reposet:localhost:12030/testrepo'); +}); + +test('buildSearchQuery handles multiple repoNamesFilter with ports', () => { + const result = buildSearchQuery({ + query: 'function test', + repoNamesFilter: ['myserver:12030/repo1', 'localhost:3000/repo2'] + }); + + expect(result).toBe('function test reposet:myserver:12030/repo1,localhost:3000/repo2'); +}); + +test('buildSearchQuery handles repoNamesFilterRegexp with port in URL', () => { + const result = buildSearchQuery({ + query: 'test.cpp', + repoNamesFilterRegexp: ['localhost:12030/testrepo'] + }); + + expect(result).toBe('test.cpp ( repo:localhost:12030/testrepo )'); +}); + +test('buildSearchQuery combines repoNamesFilter and repoNamesFilterRegexp with ports', () => { + const result = buildSearchQuery({ + query: 'test.cpp', + repoNamesFilter: ['myserver:12030/testrepo'], + repoNamesFilterRegexp: ['localhost:12030/other'] + }); + + expect(result).toBe('test.cpp reposet:myserver:12030/testrepo ( repo:localhost:12030/other )'); }); \ No newline at end of file diff --git a/packages/web/src/features/search/parser.ts b/packages/web/src/features/search/parser.ts index e3e9d41a9..eb2447c70 100644 --- a/packages/web/src/features/search/parser.ts +++ b/packages/web/src/features/search/parser.ts @@ -223,8 +223,10 @@ const transformTreeToIR = async ({ throw new Error(`${prefixNode.type.name} missing colon`); } - // Get the value part after the colon and remove quotes if present - const value = fullText.substring(colonIndex + 1).replace(/^"|"$/g, ''); + // Get the value part after the colon, remove quotes if present, and trim whitespace. + // Trimming is necessary because the grammar may capture spaces between the prefix + // keyword and the value (e.g., "repo: localhost" instead of "repo:localhost"). + const value = fullText.substring(colonIndex + 1).replace(/^"|"$/g, '').trim(); switch (prefixTypeId) { case FileExpr: