|
1 | 1 | import type { SearchOperators, SearchOperatorsLongForm, SearchQuery } from '../constants.search'; |
2 | | -import { searchOperationRegex, searchOperatorsToLongFormMap } from '../constants.search'; |
| 2 | +import { searchOperators, searchOperatorsToLongFormMap } from '../constants.search'; |
3 | 3 | import type { StoredSearchQuery } from '../constants.storage'; |
4 | 4 | import type { GitRevisionReference } from './models/reference'; |
5 | 5 | import type { GitUser } from './models/user'; |
@@ -63,35 +63,101 @@ export function createSearchQueryForCommits(refsOrCommits: (string | GitRevision |
63 | 63 |
|
64 | 64 | export function parseSearchQuery(search: SearchQuery): Map<SearchOperatorsLongForm, Set<string>> { |
65 | 65 | const operations = new Map<SearchOperatorsLongForm, Set<string>>(); |
| 66 | + const query = search.query.trim(); |
66 | 67 |
|
67 | | - let op: SearchOperators | undefined; |
68 | | - let value: string | undefined; |
69 | | - let text: string | undefined; |
| 68 | + let pos = 0; |
70 | 69 |
|
71 | | - let match; |
72 | | - do { |
73 | | - match = searchOperationRegex.exec(search.query); |
74 | | - if (match?.groups == null) break; |
| 70 | + while (pos < query.length) { |
| 71 | + // Skip whitespace |
| 72 | + if (/\s/.test(query[pos])) { |
| 73 | + pos++; |
| 74 | + continue; |
| 75 | + } |
75 | 76 |
|
76 | | - op = searchOperatorsToLongFormMap.get(match.groups.op as SearchOperators); |
77 | | - ({ value, text } = match.groups); |
| 77 | + // Try to match an operator |
| 78 | + let matchedOperator = false; |
| 79 | + let op: SearchOperators | undefined; |
| 80 | + let value: string | undefined; |
| 81 | + |
| 82 | + // Check for operators (starting with longer ones first to avoid partial matches) |
| 83 | + for (const operator of searchOperators) { |
| 84 | + if (!operator.length) continue; |
| 85 | + |
| 86 | + if (query.startsWith(operator, pos)) { |
| 87 | + op = operator as SearchOperators; |
| 88 | + const startPos = pos + operator.length; |
| 89 | + pos = startPos; |
| 90 | + |
| 91 | + // Skip optional space after operator |
| 92 | + if (query[pos] === ' ') { |
| 93 | + pos++; |
| 94 | + } |
| 95 | + |
| 96 | + // Extract the value and check if it is quoted |
| 97 | + if (query[pos] === '"') { |
| 98 | + const endQuotePos = query.indexOf('"', pos + 1); |
| 99 | + if (endQuotePos !== -1) { |
| 100 | + value = query.substring(pos, endQuotePos + 1); |
| 101 | + pos = endQuotePos + 1; |
| 102 | + } else { |
| 103 | + // Unterminated quote, take the rest of the string |
| 104 | + value = query.substring(pos); |
| 105 | + pos = query.length; |
| 106 | + } |
| 107 | + } else { |
| 108 | + // Unquoted value - take until whitespace |
| 109 | + const nextSpacePos = query.indexOf(' ', pos); |
| 110 | + const valueEndPos = nextSpacePos !== -1 ? nextSpacePos : query.length; |
| 111 | + value = query.substring(pos, valueEndPos); |
| 112 | + pos = valueEndPos; |
| 113 | + } |
| 114 | + |
| 115 | + matchedOperator = true; |
| 116 | + break; |
| 117 | + } |
| 118 | + } |
78 | 119 |
|
79 | | - if (text) { |
80 | | - if (!searchOperatorsToLongFormMap.has(text.trim() as SearchOperators)) { |
81 | | - op = text === '@me' ? 'author:' : isSha(text) ? 'commit:' : 'message:'; |
82 | | - value = text; |
| 120 | + if (!matchedOperator) { |
| 121 | + // No operator found, parse as text |
| 122 | + let text: string; |
| 123 | + |
| 124 | + // Check if text is quoted |
| 125 | + if (query[pos] === '"') { |
| 126 | + const endQuotePos = query.indexOf('"', pos + 1); |
| 127 | + if (endQuotePos !== -1) { |
| 128 | + text = query.substring(pos, endQuotePos + 1); |
| 129 | + pos = endQuotePos + 1; |
| 130 | + } else { |
| 131 | + // Unterminated quote, take the rest of the string |
| 132 | + text = query.substring(pos); |
| 133 | + pos = query.length; |
| 134 | + } |
| 135 | + } else { |
| 136 | + // Unquoted text - take until whitespace |
| 137 | + const nextSpacePos = query.indexOf(' ', pos); |
| 138 | + const valueEndPos = nextSpacePos !== -1 ? nextSpacePos : query.length; |
| 139 | + text = query.substring(pos, valueEndPos); |
| 140 | + pos = valueEndPos; |
83 | 141 | } |
| 142 | + |
| 143 | + // Handle special text tokens (@me, SHA) |
| 144 | + op = text === '@me' ? 'author:' : isSha(text) ? 'commit:' : 'message:'; |
| 145 | + value = text; |
84 | 146 | } |
85 | 147 |
|
| 148 | + // Add the discovered operation to our map |
86 | 149 | if (op && value) { |
87 | | - let values = operations.get(op); |
88 | | - if (values == null) { |
89 | | - values = new Set(); |
90 | | - operations.set(op, values); |
| 150 | + const longFormOp = searchOperatorsToLongFormMap.get(op); |
| 151 | + if (longFormOp) { |
| 152 | + let values = operations.get(longFormOp); |
| 153 | + if (values == null) { |
| 154 | + values = new Set(); |
| 155 | + operations.set(longFormOp, values); |
| 156 | + } |
| 157 | + values.add(value); |
91 | 158 | } |
92 | | - values.add(value); |
93 | 159 | } |
94 | | - } while (match != null); |
| 160 | + } |
95 | 161 |
|
96 | 162 | return operations; |
97 | 163 | } |
|
0 commit comments