From a92ada7727b2596414aafe204e5001eb066569cb Mon Sep 17 00:00:00 2001 From: Mohamed Bassem Date: Sat, 13 Sep 2025 21:37:56 +0100 Subject: feat(search): add title search qualifier (#1940) * fix(search): include link titles in title matcher * docs(search): add title qualifier * docs: remove title qualifier from v0.27 guide --- packages/shared/searchQueryParser.test.ts | 36 +++++++++++++++++++++++++++++++ packages/shared/searchQueryParser.ts | 7 +++++- packages/shared/types/search.ts | 8 +++++++ 3 files changed, 50 insertions(+), 1 deletion(-) (limited to 'packages/shared') diff --git a/packages/shared/searchQueryParser.test.ts b/packages/shared/searchQueryParser.test.ts index 7a86ecb5..3fe3f388 100644 --- a/packages/shared/searchQueryParser.test.ts +++ b/packages/shared/searchQueryParser.test.ts @@ -162,6 +162,42 @@ describe("Search Query Parser", () => { inverse: true, }, }); + expect(parseSearchQuery("title:example")).toEqual({ + result: "full", + text: "", + matcher: { + type: "title", + title: "example", + inverse: false, + }, + }); + expect(parseSearchQuery("-title:example")).toEqual({ + result: "full", + text: "", + matcher: { + type: "title", + title: "example", + inverse: true, + }, + }); + expect(parseSearchQuery('title:"my title"')).toEqual({ + result: "full", + text: "", + matcher: { + type: "title", + title: "my title", + inverse: false, + }, + }); + expect(parseSearchQuery('-title:"my title"')).toEqual({ + result: "full", + text: "", + matcher: { + type: "title", + title: "my title", + inverse: true, + }, + }); expect(parseSearchQuery("#my-tag")).toEqual({ result: "full", text: "", diff --git a/packages/shared/searchQueryParser.ts b/packages/shared/searchQueryParser.ts index 9a29a8b7..f919df96 100644 --- a/packages/shared/searchQueryParser.ts +++ b/packages/shared/searchQueryParser.ts @@ -41,7 +41,7 @@ const lexerRules: [RegExp, TokenType][] = [ [/^\s+or/i, TokenType.Or], [/^#/, TokenType.Hash], - [/^(is|url|list|after|before|age|feed):/, TokenType.Qualifier], + [/^(is|url|list|after|before|age|feed|title):/, TokenType.Qualifier], [/^"([^"]+)"/, TokenType.StringLiteral], @@ -195,6 +195,11 @@ MATCHER.setPattern( text: "", matcher: { type: "url", url: ident, inverse: !!minus }, }; + case "title:": + return { + text: "", + matcher: { type: "title", title: ident, inverse: !!minus }, + }; case "#": return { text: "", diff --git a/packages/shared/types/search.ts b/packages/shared/types/search.ts index 26d5bd42..caedcf94 100644 --- a/packages/shared/types/search.ts +++ b/packages/shared/types/search.ts @@ -31,6 +31,12 @@ const zUrlMatcher = z.object({ inverse: z.boolean(), }); +const zTitleMatcher = z.object({ + type: z.literal("title"), + title: z.string(), + inverse: z.boolean(), +}); + const zFavouritedMatcher = z.object({ type: z.literal("favourited"), favourited: z.boolean(), @@ -82,6 +88,7 @@ const zNonRecursiveMatcher = z.union([ zListNameMatcher, zArchivedMatcher, zUrlMatcher, + zTitleMatcher, zFavouritedMatcher, zDateAfterMatcher, zDateBeforeMatcher, @@ -104,6 +111,7 @@ export const zMatcherSchema: z.ZodType = z.lazy(() => { zListNameMatcher, zArchivedMatcher, zUrlMatcher, + zTitleMatcher, zFavouritedMatcher, zDateAfterMatcher, zDateBeforeMatcher, -- cgit v1.2.3-70-g09d2