|
@ -37,11 +37,22 @@ export interface SearchPattern { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
export namespace SearchPattern { |
|
|
export namespace SearchPattern { |
|
|
const emptyStr = ''; |
|
|
|
|
|
|
|
|
const normalizeSearchOperatorsMap = new Map<SearchOperators, SearchOperators>([ |
|
|
|
|
|
['', 'message:'], |
|
|
|
|
|
['=:', 'message:'], |
|
|
|
|
|
['message:', 'message:'], |
|
|
|
|
|
['@:', 'author:'], |
|
|
|
|
|
['author:', 'author:'], |
|
|
|
|
|
['#:', 'commit:'], |
|
|
|
|
|
['commit:', 'commit:'], |
|
|
|
|
|
['?:', 'file:'], |
|
|
|
|
|
['file:', 'file:'], |
|
|
|
|
|
['~:', 'change:'], |
|
|
|
|
|
['change:', 'change:'], |
|
|
|
|
|
]); |
|
|
|
|
|
|
|
|
const searchMessageValuesRegex = /(".+"|[^\b\s]+)/g; |
|
|
|
|
|
const searchOperationRegex = |
|
|
const searchOperationRegex = |
|
|
/((?:=|message|@|author|#|commit|\?|file|~|change):)\s?(.*?)(?=\s?(?:(?:=:|message:|@:|author:|#:|commit:|\?:|file:|~:|change:)|$))/g; |
|
|
|
|
|
|
|
|
/(?:(?<op>=:|message:|@:|author:|#:|commit:|\?:|file:|~:|change:)\s?(?<value>".+?"|\S+\b))|(?<text>\S+)(?!(?:=|message|@|author|#|commit|\?|file|~|change):)/gi; |
|
|
|
|
|
|
|
|
export function fromCommit(ref: string): string; |
|
|
export function fromCommit(ref: string): string; |
|
|
export function fromCommit(commit: GitRevisionReference): string; |
|
|
export function fromCommit(commit: GitRevisionReference): string; |
|
@ -58,91 +69,39 @@ export namespace SearchPattern { |
|
|
export function parseSearchOperations(search: string): Map<string, string[]> { |
|
|
export function parseSearchOperations(search: string): Map<string, string[]> { |
|
|
const operations = new Map<string, string[]>(); |
|
|
const operations = new Map<string, string[]>(); |
|
|
|
|
|
|
|
|
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 { |
|
|
} 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; |
|
|
return operations; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function addSearchOperationValue(op: SearchOperators, value: string, operations: Map<string, string[]>) { |
|
|
|
|
|
let values = operations.get(op); |
|
|
|
|
|
if (values === undefined) { |
|
|
|
|
|
values = [value]; |
|
|
|
|
|
operations.set(op, values); |
|
|
|
|
|
} else { |
|
|
|
|
|
values.push(value); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function parseSearchMessageOperations(message: string, operations: Map<string, string[]>) { |
|
|
|
|
|
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) { |
|
|
export function toKey(search: SearchPattern) { |
|
|
return `${search.pattern}|${search.matchAll ? 'A' : ''}${search.matchCase ? 'C' : ''}${ |
|
|
return `${search.pattern}|${search.matchAll ? 'A' : ''}${search.matchCase ? 'C' : ''}${ |
|
|
search.matchRegex ? 'R' : '' |
|
|
search.matchRegex ? 'R' : '' |
|
|
}`;
|
|
|
}`;
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
export const normalizeSearchOperatorsMap = new Map<SearchOperators, SearchOperators>([ |
|
|
|
|
|
['', 'message:'], |
|
|
|
|
|
['=:', 'message:'], |
|
|
|
|
|
['message:', 'message:'], |
|
|
|
|
|
['@:', 'author:'], |
|
|
|
|
|
['author:', 'author:'], |
|
|
|
|
|
['#:', 'commit:'], |
|
|
|
|
|
['commit:', 'commit:'], |
|
|
|
|
|
['?:', 'file:'], |
|
|
|
|
|
['file:', 'file:'], |
|
|
|
|
|
['~:', 'change:'], |
|
|
|
|
|
['change:', 'change:'], |
|
|
|
|
|
]); |
|
|
|