11// Project/List matcher for task input
22// Returns matched span + project info for highlighting and assignment.
33
4+ import { stripMatch } from './date-matcher.js'
5+
46/**
57 * Parse project match from input text
6- * Supports #project and #"project name" syntax
8+ * Supports #project name and #"project name" syntax
79 *
810 * @param {string } input - The task input text
911 * @param {Array<{id: string, name: string}> } projects - Available projects/lists
1214export function parseProjectMatch ( input , projects ) {
1315 if ( ! input || ! projects || projects . length === 0 ) return null
1416
15- const text = input
16- const lower = input . toLowerCase ( )
17-
1817 // Find all # symbols
1918 let index = 0
20- while ( index < text . length ) {
21- index = text . indexOf ( '#' , index )
19+ while ( index < input . length ) {
20+ index = input . indexOf ( '#' , index )
2221 if ( index === - 1 ) break
2322
2423 // Check if this is the start of a project reference
2524 // Must be at start or preceded by whitespace
26- if ( index > 0 && ! / \s / . test ( text [ index - 1 ] ) ) {
25+ if ( index > 0 && ! / \s / . test ( input [ index - 1 ] ) ) {
2726 index ++
2827 continue
2928 }
3029
31- let matchEnd = - 1
32- let matchText = ''
33-
3430 // Check for quoted syntax: #"project name"
35- if ( text [ index + 1 ] === '"' ) {
31+ if ( input [ index + 1 ] === '"' ) {
3632 const quoteStart = index + 2
37- const quoteEnd = text . indexOf ( '"' , quoteStart )
33+ const quoteEnd = input . indexOf ( '"' , quoteStart )
3834 if ( quoteEnd !== - 1 ) {
39- matchText = text . slice ( quoteStart , quoteEnd )
40- matchEnd = quoteEnd + 1
35+ const matchText = input . slice ( quoteStart , quoteEnd )
36+ const matchLower = matchText . toLowerCase ( )
37+ const project = projects . find (
38+ ( p ) => p . name . toLowerCase ( ) === matchLower
39+ )
40+
41+ if ( project ) {
42+ return {
43+ match : {
44+ start : index ,
45+ end : quoteEnd + 1 ,
46+ } ,
47+ projectId : project . id ,
48+ projectName : project . name ,
49+ }
50+ }
4151 }
4252 } else {
4353 // Unquoted syntax with greedy matching: #project or #project name
@@ -47,22 +57,22 @@ export function parseProjectMatch(input, projects) {
4757 let pos = index + 1
4858
4959 // Extract words and try matching after each word
50- while ( pos < text . length && text [ pos ] !== '#' ) {
60+ while ( pos < input . length && input [ pos ] !== '#' ) {
5161 // Skip whitespace
52- while ( pos < text . length && / \s / . test ( text [ pos ] ) ) {
62+ while ( pos < input . length && / \s / . test ( input [ pos ] ) ) {
5363 pos ++
5464 }
5565
56- if ( pos >= text . length || text [ pos ] === '#' ) break
66+ if ( pos >= input . length || input [ pos ] === '#' ) break
5767
5868 // Get next word
5969 let wordEnd = pos
60- while ( wordEnd < text . length && ! / [ \s # ] / . test ( text [ wordEnd ] ) ) {
70+ while ( wordEnd < input . length && ! / [ \s # ] / . test ( input [ wordEnd ] ) ) {
6171 wordEnd ++
6272 }
6373
6474 if ( wordEnd > pos ) {
65- words . push ( text . slice ( pos , wordEnd ) )
75+ words . push ( input . slice ( pos , wordEnd ) )
6676
6777 // Try matching accumulated words
6878 const candidate = words . join ( ' ' )
@@ -84,48 +94,14 @@ export function parseProjectMatch(input, projects) {
8494 }
8595
8696 if ( longestMatch ) {
87- // Found a match with greedy matching, return immediately
88- const result = {
97+ return {
8998 match : {
9099 start : index ,
91100 end : longestMatch . end ,
92101 } ,
93102 projectId : longestMatch . project . id ,
94103 projectName : longestMatch . project . name ,
95104 }
96-
97- // Debug logging
98- if ( input . includes ( '#list 2 de' ) ) {
99- console . log ( 'Project matcher debug:' , {
100- input,
101- matchedText : input . slice ( index , longestMatch . end ) ,
102- start : index ,
103- end : longestMatch . end ,
104- projectName : longestMatch . project . name ,
105- } )
106- }
107-
108- return result
109- }
110- }
111-
112- // For quoted syntax, we still need to match against available projects
113- if ( matchText && matchEnd !== - 1 ) {
114- // Try to match against available projects (case-insensitive)
115- const matchLower = matchText . toLowerCase ( )
116- const project = projects . find (
117- ( p ) => p . name . toLowerCase ( ) === matchLower
118- )
119-
120- if ( project ) {
121- return {
122- match : {
123- start : index ,
124- end : matchEnd ,
125- } ,
126- projectId : project . id ,
127- projectName : project . name ,
128- }
129105 }
130106 }
131107
@@ -136,16 +112,11 @@ export function parseProjectMatch(input, projects) {
136112}
137113
138114/**
139- * Strip project match from text
115+ * Strip project match from text (uses shared stripMatch utility)
140116 * @param {string } text - The original text
141117 * @param {Object } match - Match object with {start, end}
142118 * @returns {string } Text with project match removed
143119 */
144120export function stripProjectMatch ( text , match ) {
145- if ( ! match ) return text . trim ( )
146- const before = text . slice ( 0 , match . start ) . trimEnd ( )
147- const after = text . slice ( match . end ) . trimStart ( )
148- if ( ! before ) return after
149- if ( ! after ) return before
150- return `${ before } ${ after } `
121+ return stripMatch ( text , match )
151122}
0 commit comments