diff --git a/src/git/search.ts b/src/git/search.ts index dfd6614..c6ec18c 100644 --- a/src/git/search.ts +++ b/src/git/search.ts @@ -37,11 +37,22 @@ export interface SearchPattern { } export namespace SearchPattern { - const emptyStr = ''; + const normalizeSearchOperatorsMap = new Map([ + ['', 'message:'], + ['=:', 'message:'], + ['message:', 'message:'], + ['@:', 'author:'], + ['author:', 'author:'], + ['#:', 'commit:'], + ['commit:', 'commit:'], + ['?:', 'file:'], + ['file:', 'file:'], + ['~:', 'change:'], + ['change:', 'change:'], + ]); - const searchMessageValuesRegex = /(".+"|[^\b\s]+)/g; const searchOperationRegex = - /((?:=|message|@|author|#|commit|\?|file|~|change):)\s?(.*?)(?=\s?(?:(?:=:|message:|@:|author:|#:|commit:|\?:|file:|~:|change:)|$))/g; + /(?:(?=:|message:|@:|author:|#:|commit:|\?:|file:|~:|change:)\s?(?".+?"|\S+\b))|(?\S+)(?!(?:=|message|@|author|#|commit|\?|file|~|change):)/gi; export function fromCommit(ref: string): string; export function fromCommit(commit: GitRevisionReference): string; @@ -58,91 +69,39 @@ export namespace SearchPattern { export function parseSearchOperations(search: string): Map { const operations = new Map(); - let op; - let value; - let freeTextTerm; + let op: SearchOperators | undefined; + let value: string | undefined; + let text: string | undefined; - let match = searchOperationRegex.exec(search); - - if (match == null || match.index > 0) { - freeTextTerm = search.substring(0, match?.index).trimEnd(); - } + let match; + do { + match = searchOperationRegex.exec(search); + if (match?.groups == null) break; - while (match != null) { - [, op, value] = match; + op = normalizeSearchOperatorsMap.get(match.groups.op as SearchOperators); + ({ value, text } = match.groups); - if (op !== undefined) { - op = normalizeSearchOperatorsMap.get(op as SearchOperators)!; - let firstMessageMatch; + if (text) { + op = GitRevision.isSha(text) ? 'commit:' : 'message:'; + value = text; + } - if (op === 'message:') { - parseSearchMessageOperations(value, operations); - } else if ( - !freeTextTerm && - match.index + match[0].length === search.length && - (firstMessageMatch = new RegExp(searchMessageValuesRegex).exec(value)) != null - ) { - const [, firstMessage] = firstMessageMatch; - addSearchOperationValue(op, firstMessage, operations); - freeTextTerm = value.substring(firstMessage.length).trimStart(); + if (op && value) { + const values = operations.get(op); + if (values == null) { + operations.set(op, [value]); } else { - addSearchOperationValue(op, value, operations); + values.push(value); } } - - match = searchOperationRegex.exec(search); - } - - if (freeTextTerm) { - if (GitRevision.isSha(freeTextTerm)) { - addSearchOperationValue('commit:', freeTextTerm, operations); - } else { - parseSearchMessageOperations(freeTextTerm, operations); - } - } + } while (match != null); return operations; } - function addSearchOperationValue(op: SearchOperators, value: string, operations: Map) { - let values = operations.get(op); - if (values === undefined) { - values = [value]; - operations.set(op, values); - } else { - values.push(value); - } - } - - function parseSearchMessageOperations(message: string, operations: Map) { - if (message === emptyStr) { - addSearchOperationValue('message:', emptyStr, operations); - return; - } - - let match; - while ((match = searchMessageValuesRegex.exec(message)) != null) { - const [, value] = match; - addSearchOperationValue('message:', value, operations); - } - } - export function toKey(search: SearchPattern) { return `${search.pattern}|${search.matchAll ? 'A' : ''}${search.matchCase ? 'C' : ''}${ search.matchRegex ? 'R' : '' }`; } } -export const normalizeSearchOperatorsMap = new Map([ - ['', 'message:'], - ['=:', 'message:'], - ['message:', 'message:'], - ['@:', 'author:'], - ['author:', 'author:'], - ['#:', 'commit:'], - ['commit:', 'commit:'], - ['?:', 'file:'], - ['file:', 'file:'], - ['~:', 'change:'], - ['change:', 'change:'], -]);