From af29012b47eaaaac6c3188a3a06e7b25135bea29 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Tue, 25 Oct 2022 23:29:33 -0700 Subject: [PATCH] Adds @me support to commit search - @me automatically searches for commits authored by the current user Allows (strips) `@`-prefix for author search on non-virtual repos --- src/env/node/git/localGitProvider.ts | 10 +- src/git/search.ts | 19 +++- src/plus/github/githubGitProvider.ts | 143 +++++++++++++-------------- 3 files changed, 94 insertions(+), 78 deletions(-) diff --git a/src/env/node/git/localGitProvider.ts b/src/env/node/git/localGitProvider.ts index 0cd6f92cf2e96..7168e96654cab 100644 --- a/src/env/node/git/localGitProvider.ts +++ b/src/env/node/git/localGitProvider.ts @@ -4242,7 +4242,9 @@ export class LocalGitProvider implements GitProvider, Disposable { const limit = options?.limit ?? configuration.get('advanced.maxSearchItems') ?? 0; const similarityThreshold = configuration.get('advanced.similarityThreshold'); - const { args, files, shas } = getGitArgsFromSearchQuery(search); + const currentUser = await this.getCurrentUser(repoPath); + + const { args, files, shas } = getGitArgsFromSearchQuery(search, currentUser); args.push(`-M${similarityThreshold == null ? '' : `${similarityThreshold}%`}`, '--'); if (files.length !== 0) { @@ -4262,7 +4264,7 @@ export class LocalGitProvider implements GitProvider, Disposable { repoPath, undefined, undefined, - await this.getCurrentUser(repoPath), + currentUser, limit, false, undefined, @@ -4334,7 +4336,9 @@ export class LocalGitProvider implements GitProvider, Disposable { try { const refAndDateParser = getRefAndDateParser(); - const { args: searchArgs, files, shas } = getGitArgsFromSearchQuery(search); + const currentUser = search.query.includes('@me') ? await this.getCurrentUser(repoPath) : undefined; + + const { args: searchArgs, files, shas } = getGitArgsFromSearchQuery(search, currentUser); if (shas?.size) { const data = await this.git.show2( repoPath, diff --git a/src/git/search.ts b/src/git/search.ts index 8f8625d664f2d..55a0d508a7be5 100644 --- a/src/git/search.ts +++ b/src/git/search.ts @@ -1,5 +1,6 @@ import type { GitRevisionReference } from './models/reference'; import { GitRevision } from './models/reference'; +import type { GitUser } from './models/user'; export type SearchOperators = | '' @@ -132,7 +133,7 @@ export function parseSearchQuery(search: SearchQuery): Map { ({ value, text } = match.groups); if (text) { - op = GitRevision.isSha(text) ? 'commit:' : 'message:'; + op = text === '@me' ? 'author:' : GitRevision.isSha(text) ? 'commit:' : 'message:'; value = text; } @@ -151,7 +152,10 @@ export function parseSearchQuery(search: SearchQuery): Map { const doubleQuoteRegex = /"/g; -export function getGitArgsFromSearchQuery(search: SearchQuery): { +export function getGitArgsFromSearchQuery( + search: SearchQuery, + currentUser: GitUser | undefined, +): { args: string[]; files: string[]; shas?: Set | undefined; @@ -199,6 +203,17 @@ export function getGitArgsFromSearchQuery(search: SearchQuery): { value = value.replace(doubleQuoteRegex, search.matchRegex ? '\\b' : ''); if (!value) continue; + if (value === '@me') { + if (currentUser?.name == null) continue; + + value = currentUser.name; + } + + if (value.startsWith('@')) { + searchArgs.add(`--author=${value.slice(1)}`); + continue; + } + searchArgs.add(`--author=${value}`); } diff --git a/src/plus/github/githubGitProvider.ts b/src/plus/github/githubGitProvider.ts index a561d8c2d9b1c..5bb88b4e807e0 100644 --- a/src/plus/github/githubGitProvider.ts +++ b/src/plus/github/githubGitProvider.ts @@ -2563,8 +2563,7 @@ export class GitHubGitProvider implements GitProvider, Disposable { const operations = parseSearchQuery(search); - let op; - let values = operations.get('commit:'); + const values = operations.get('commit:'); if (values != null) { const commit = await this.getCommit(repoPath, values[0]); if (commit == null) return undefined; @@ -2580,53 +2579,26 @@ export class GitHubGitProvider implements GitProvider, Disposable { }; } - const query = []; - - for ([op, values] of operations.entries()) { - switch (op) { - case 'message:': - query.push(...values.map(m => m.replace(/ /g, '+'))); - break; - - case 'author:': - query.push( - ...values.map(a => { - a = a.replace(/ /g, '+'); - if (a.startsWith('@')) return `author:${a.slice(1)}`; - if (a.startsWith('"@')) return `author:"${a.slice(2)}`; - if (a.includes('@')) return `author-email:${a}`; - return `author-name:${a}`; - }), - ); - break; - - // case 'change:': - // case 'file:': - // break; - } - } - - if (query.length === 0) return undefined; + const queryArgs = await this.getQueryArgsFromSearchQuery(search, operations, repoPath); + if (queryArgs.length === 0) return undefined; const limit = this.getPagingLimit(options?.limit); try { const { metadata, github, session } = await this.ensureRepositoryContext(repoPath); - const result = await github.searchCommits( - session.accessToken, - `repo:${metadata.repo.owner}/${metadata.repo.name}+${query.join('+').trim()}`, - { - cursor: options?.cursor, - limit: limit, - sort: - options?.ordering === 'date' - ? 'committer-date' - : options?.ordering === 'author-date' - ? 'author-date' - : undefined, - }, - ); + const query = `repo:${metadata.repo.owner}/${metadata.repo.name}+${queryArgs.join('+').trim()}`; + + const result = await github.searchCommits(session.accessToken, query, { + cursor: options?.cursor, + limit: limit, + sort: + options?.ordering === 'date' + ? 'committer-date' + : options?.ordering === 'author-date' + ? 'author-date' + : undefined, + }); if (result == null) return undefined; const commits = new Map(); @@ -2760,8 +2732,7 @@ export class GitHubGitProvider implements GitProvider, Disposable { const results: GitSearchResults = new Map(); const operations = parseSearchQuery(search); - let op; - let values = operations.get('commit:'); + const values = operations.get('commit:'); if (values != null) { const commitsResults = await Promise.allSettled[]>( values.map(v => this.getCommit(repoPath, v.replace(doubleQuoteRegex, ''))), @@ -2786,33 +2757,8 @@ export class GitHubGitProvider implements GitProvider, Disposable { }; } - const queryValues: string[] = []; - - for ([op, values] of operations.entries()) { - switch (op) { - case 'message:': - queryValues.push(...values.map(m => m.replace(/ /g, '+'))); - break; - - case 'author:': - queryValues.push( - ...values.map(a => { - a = a.replace(/ /g, '+'); - if (a.startsWith('@')) return `author:${a.slice(1)}`; - if (a.startsWith('"@')) return `author:"${a.slice(2)}`; - if (a.includes('@')) return `author-email:${a}`; - return `author-name:${a}`; - }), - ); - break; - - // case 'change:': - // case 'file:': - // break; - } - } - - if (queryValues.length === 0) { + const queryArgs = await this.getQueryArgsFromSearchQuery(search, operations, repoPath); + if (queryArgs.length === 0) { return { repoPath: repoPath, query: search, @@ -2823,7 +2769,7 @@ export class GitHubGitProvider implements GitProvider, Disposable { const { metadata, github, session } = await this.ensureRepositoryContext(repoPath); - const query = `repo:${metadata.repo.owner}/${metadata.repo.name}+${queryValues.join('+').trim()}`; + const query = `repo:${metadata.repo.owner}/${metadata.repo.name}+${queryArgs.join('+').trim()}`; async function searchForCommitsCore( this: GitHubGitProvider, @@ -3182,6 +3128,57 @@ export class GitHubGitProvider implements GitProvider, Disposable { return ref; } + + private async getQueryArgsFromSearchQuery( + search: SearchQuery, + operations: Map, + repoPath: string, + ) { + const query = []; + + for (const [op, values] of operations.entries()) { + switch (op) { + case 'message:': + query.push(...values.map(m => m.replace(/ /g, '+'))); + break; + + case 'author:': { + let currentUser: GitUser | undefined; + if (values.includes('@me')) { + currentUser = await this.getCurrentUser(repoPath); + } + + for (let value of values) { + if (!value) continue; + value = value.replace(doubleQuoteRegex, search.matchRegex ? '\\b' : ''); + if (!value) continue; + + if (value === '@me') { + if (currentUser?.username == null) continue; + + value = `@${currentUser.username}`; + } + + value = value.replace(/ /g, '+'); + if (value.startsWith('@')) { + query.push(`author:${value.slice(1)}`); + } else if (value.includes('@')) { + query.push(`author-email:${value}`); + } else { + query.push(`author-name:${value}`); + } + } + + break; + } + // case 'change:': + // case 'file:': + // break; + } + } + + return query; + } } function encodeAuthority(scheme: string, metadata?: T): string {