Bladeren bron

Adds full commit searching to Graph (wip)

main
Eric Amodio 2 jaren geleden
bovenliggende
commit
97ea2fdc27
11 gewijzigde bestanden met toevoegingen van 426 en 91 verwijderingen
  1. +4
    -1
      src/env/node/git/git.ts
  2. +162
    -70
      src/env/node/git/localGitProvider.ts
  3. +6
    -1
      src/git/gitProvider.ts
  4. +11
    -1
      src/git/gitProviderService.ts
  5. +12
    -2
      src/git/models/repository.ts
  6. +14
    -0
      src/git/search.ts
  7. +99
    -1
      src/plus/github/githubGitProvider.ts
  8. +50
    -12
      src/plus/webviews/graph/graphWebview.ts
  9. +15
    -0
      src/plus/webviews/graph/protocol.ts
  10. +20
    -1
      src/webviews/apps/plus/graph/GraphWrapper.tsx
  11. +33
    -2
      src/webviews/apps/plus/graph/graph.tsx

+ 4
- 1
src/env/node/git/git.ts Bestand weergeven

@ -867,10 +867,13 @@ export class Git {
params.push(options?.ref);
}
if (!params.includes('--')) {
params.push('--');
}
return this.git<string>(
{ cwd: repoPath, configs: options?.configs ?? gitLogDefaultConfigs, stdin: options?.stdin },
...params,
'--',
);
}

+ 162
- 70
src/env/node/git/localGitProvider.ts Bestand weergeven

@ -106,7 +106,7 @@ import type { RemoteProvider } from '../../../git/remotes/remoteProvider';
import type { RemoteProviders } from '../../../git/remotes/remoteProviders';
import { getRemoteProviderMatcher, loadRemoteProviders } from '../../../git/remotes/remoteProviders';
import type { RichRemoteProvider } from '../../../git/remotes/richRemoteProvider';
import type { SearchPattern } from '../../../git/search';
import type { GitSearch, SearchPattern } from '../../../git/search';
import { parseSearchOperations } from '../../../git/search';
import { Logger } from '../../../logger';
import type { LogScope } from '../../../logger';
@ -2638,90 +2638,107 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
@log()
async getLogForSearch(
async searchForCommitsSimple(
repoPath: string,
search: SearchPattern,
options?: { limit?: number; ordering?: 'date' | 'author-date' | 'topo' | null; skip?: number },
): Promise<GitLog | undefined> {
options?: { limit?: number; ordering?: 'date' | 'author-date' | 'topo' },
): Promise<GitSearch> {
search = { matchAll: false, matchCase: false, matchRegex: true, ...search };
try {
const { args: searchArgs, files, commits } = this.getArgsFromSearchPattern(search);
if (commits?.length) {
return {
repoPath: repoPath,
pattern: search,
results: commits,
};
}
const refParser = getGraphRefParser();
const limit = options?.limit ?? configuration.get('advanced.maxSearchItems') ?? 0;
const similarityThreshold = configuration.get('advanced.similarityThreshold');
const operations = parseSearchOperations(search.pattern);
const searchArgs = new Set<string>();
const files: string[] = [];
let useShow = false;
let op;
let values = operations.get('commit:');
if (values != null) {
useShow = true;
searchArgs.add('-m');
searchArgs.add(`-M${similarityThreshold == null ? '' : `${similarityThreshold}%`}`);
for (const value of values) {
searchArgs.add(value.replace(doubleQuoteRegex, ''));
}
} else {
searchArgs.add(`-M${similarityThreshold == null ? '' : `${similarityThreshold}%`}`);
searchArgs.add('--all');
searchArgs.add('--full-history');
searchArgs.add(search.matchRegex ? '--extended-regexp' : '--fixed-strings');
if (search.matchRegex && !search.matchCase) {
searchArgs.add('--regexp-ignore-case');
}
for ([op, values] of operations.entries()) {
switch (op) {
case 'message:':
searchArgs.add('-m');
if (search.matchAll) {
searchArgs.add('--all-match');
}
for (const value of values) {
searchArgs.add(
`--grep=${value.replace(doubleQuoteRegex, search.matchRegex ? '\\b' : '')}`,
);
}
const args = [
...refParser.arguments,
`-M${similarityThreshold == null ? '' : `${similarityThreshold}%`}`,
'--use-mailmap',
];
if (limit) {
args.push(`-n${limit + 1}`);
}
if (options?.ordering) {
args.push(`--${options.ordering}-order`);
}
break;
async function searchForCommitsCore(
this: LocalGitProvider,
limit: number,
cursor?: { sha: string; skip: number },
): Promise<GitSearch> {
const data = await this.git.log2(
repoPath,
undefined,
...args,
...(cursor?.skip ? [`--skip=${cursor.skip}`] : []),
...searchArgs,
'--',
...files,
);
const results = [...refParser.parse(data)];
case 'author:':
searchArgs.add('-m');
for (const value of values) {
searchArgs.add(
`--author=${value.replace(doubleQuoteRegex, search.matchRegex ? '\\b' : '')}`,
);
}
const last = results[results.length - 1];
cursor =
last != null
? {
sha: last,
skip: results.length,
}
: undefined;
break;
return {
repoPath: repoPath,
pattern: search,
results: results,
paging:
limit !== 0 && results.length > limit
? {
limit: limit,
startingCursor: cursor?.sha,
more: true,
}
: undefined,
more: async (limit: number): Promise<GitSearch | undefined> =>
searchForCommitsCore.call(this, limit, cursor),
};
}
case 'change:':
for (const value of values) {
searchArgs.add(
search.matchRegex
? `-G${value.replace(doubleQuoteRegex, '')}`
: `-S${value.replace(doubleQuoteRegex, '')}`,
);
}
return searchForCommitsCore.call(this, limit);
} catch (ex) {
// TODO@eamodio handle error reporting -- just invalid patterns? or more detailed?
return {
repoPath: repoPath,
pattern: search,
results: [],
};
}
}
break;
@log()
async getLogForSearch(
repoPath: string,
search: SearchPattern,
options?: { limit?: number; ordering?: 'date' | 'author-date' | 'topo' | null; skip?: number },
): Promise<GitLog | undefined> {
search = { matchAll: false, matchCase: false, matchRegex: true, ...search };
case 'file:':
for (const value of values) {
files.push(value.replace(doubleQuoteRegex, ''));
}
try {
const limit = options?.limit ?? configuration.get('advanced.maxSearchItems') ?? 0;
const similarityThreshold = configuration.get('advanced.similarityThreshold');
break;
}
}
}
const { args, files, commits } = this.getArgsFromSearchPattern(search);
const args = [...searchArgs.values(), '--'];
args.push(`-M${similarityThreshold == null ? '' : `${similarityThreshold}%`}`, '--');
if (files.length !== 0) {
args.push(...files);
}
@ -2730,7 +2747,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
ordering: configuration.get('advanced.commitOrdering'),
...options,
limit: limit,
useShow: useShow,
useShow: Boolean(commits?.length),
});
const log = GitLogParser.parse(
this.container,
@ -2759,6 +2776,81 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
}
private getArgsFromSearchPattern(search: SearchPattern): {
args: string[];
files: string[];
commits?: string[] | undefined;
} {
const operations = parseSearchOperations(search.pattern);
const searchArgs = new Set<string>();
const files: string[] = [];
let commits;
let op;
let values = operations.get('commit:');
if (values != null) {
// searchArgs.add('-m');
for (const value of values) {
searchArgs.add(value.replace(doubleQuoteRegex, ''));
}
commits = [...searchArgs.values()];
} else {
searchArgs.add('--all');
searchArgs.add('--full-history');
searchArgs.add(search.matchRegex ? '--extended-regexp' : '--fixed-strings');
if (search.matchRegex && !search.matchCase) {
searchArgs.add('--regexp-ignore-case');
}
for ([op, values] of operations.entries()) {
switch (op) {
case 'message:':
searchArgs.add('-m');
if (search.matchAll) {
searchArgs.add('--all-match');
}
for (const value of values) {
searchArgs.add(`--grep=${value.replace(doubleQuoteRegex, search.matchRegex ? '\\b' : '')}`);
}
break;
case 'author:':
searchArgs.add('-m');
for (const value of values) {
searchArgs.add(
`--author=${value.replace(doubleQuoteRegex, search.matchRegex ? '\\b' : '')}`,
);
}
break;
case 'change:':
for (const value of values) {
searchArgs.add(
search.matchRegex
? `-G${value.replace(doubleQuoteRegex, '')}`
: `-S${value.replace(doubleQuoteRegex, '')}`,
);
}
break;
case 'file:':
for (const value of values) {
files.push(value.replace(doubleQuoteRegex, ''));
}
break;
}
}
}
return { args: [...searchArgs.values()], files: files, commits: commits };
}
private getLogForSearchMoreFn(
log: GitLog,
search: SearchPattern,

+ 6
- 1
src/git/gitProvider.ts Bestand weergeven

@ -26,7 +26,7 @@ import type { GitWorktree } from './models/worktree';
import type { RemoteProvider } from './remotes/remoteProvider';
import type { RemoteProviders } from './remotes/remoteProviders';
import type { RichRemoteProvider } from './remotes/richRemoteProvider';
import type { SearchPattern } from './search';
import type { GitSearch, SearchPattern } from './search';
export const enum GitProviderId {
Git = 'git',
@ -296,6 +296,11 @@ export interface GitProvider extends Disposable {
since?: string | undefined;
},
): Promise<Set<string> | undefined>;
searchForCommitsSimple(
repoPath: string | Uri,
search: SearchPattern,
options?: { limit?: number; ordering?: 'date' | 'author-date' | 'topo' },
): Promise<GitSearch>;
getLogForSearch(
repoPath: string,
search: SearchPattern,

+ 11
- 1
src/git/gitProviderService.ts Bestand weergeven

@ -69,7 +69,7 @@ import type { GitWorktree } from './models/worktree';
import { RichRemoteProviders } from './remotes/remoteProviderConnections';
import type { RemoteProviders } from './remotes/remoteProviders';
import type { RichRemoteProvider } from './remotes/richRemoteProvider';
import type { SearchPattern } from './search';
import type { GitSearch, SearchPattern } from './search';
const maxDefaultBranchWeight = 100;
const weightedDefaultBranches = new Map<string, number>([
@ -1465,6 +1465,16 @@ export class GitProviderService implements Disposable {
}
@log()
searchForCommitsSimple(
repoPath: string | Uri,
search: SearchPattern,
options?: { limit?: number; ordering?: 'date' | 'author-date' | 'topo' },
): Promise<GitSearch> {
const { provider, path } = this.getProvider(repoPath);
return provider.searchForCommitsSimple(path, search, options);
}
@log()
async getLogForSearch(
repoPath: string | Uri,
search: SearchPattern,

+ 12
- 2
src/git/models/repository.ts Bestand weergeven

@ -23,7 +23,7 @@ import type { GitProviderDescriptor } from '../gitProvider';
import { loadRemoteProviders } from '../remotes/remoteProviders';
import type { RemoteProviders } from '../remotes/remoteProviders';
import type { RichRemoteProvider } from '../remotes/richRemoteProvider';
import type { SearchPattern } from '../search';
import type { GitSearch, SearchPattern } from '../search';
import type { BranchSortOptions, GitBranch } from './branch';
import { getBranchNameWithoutRemote, getRemoteNameFromBranchName } from './branch';
import type { GitCommit } from './commit';
@ -852,10 +852,20 @@ export class Repository implements Disposable {
this.runTerminalCommand('revert', ...args);
}
searchForCommits(search: SearchPattern, options?: { limit?: number; skip?: number }): Promise<GitLog | undefined> {
searchForCommits(
search: SearchPattern,
options?: { limit?: number; ordering?: 'date' | 'author-date' | 'topo'; skip?: number },
): Promise<GitLog | undefined> {
return this.container.git.getLogForSearch(this.path, search, options);
}
searchForCommitsSimple(
search: SearchPattern,
options?: { limit?: number; ordering?: 'date' | 'author-date' | 'topo' },
): Promise<GitSearch> {
return this.container.git.searchForCommitsSimple(this.path, search, options);
}
async setRemoteAsDefault(remote: GitRemote, value: boolean = true) {
await this.container.storage.storeWorkspace('remote:default', value ? remote.id : undefined);

+ 14
- 0
src/git/search.ts Bestand weergeven

@ -35,6 +35,20 @@ export interface SearchPattern {
matchRegex?: boolean;
}
export interface GitSearch {
repoPath: string;
pattern: SearchPattern;
results: string[];
readonly paging?: {
readonly limit: number | undefined;
readonly startingCursor: string | undefined;
readonly more: boolean;
};
more?(limit: number): Promise<GitSearch | undefined>;
}
export function getKeyForSearchPattern(search: SearchPattern) {
return `${search.pattern}|${search.matchAll ? 'A' : ''}${search.matchCase ? 'C' : ''}${
search.matchRegex ? 'R' : ''

+ 99
- 1
src/plus/github/githubGitProvider.ts Bestand weergeven

@ -72,7 +72,7 @@ import type { RemoteProvider } from '../../git/remotes/remoteProvider';
import type { RemoteProviders } from '../../git/remotes/remoteProviders';
import { getRemoteProviderMatcher, loadRemoteProviders } from '../../git/remotes/remoteProviders';
import type { RichRemoteProvider } from '../../git/remotes/richRemoteProvider';
import type { SearchPattern } from '../../git/search';
import type { GitSearch, SearchPattern } from '../../git/search';
import { parseSearchOperations } from '../../git/search';
import type { LogScope } from '../../logger';
import { Logger } from '../../logger';
@ -1581,6 +1581,104 @@ export class GitHubGitProvider implements GitProvider, Disposable {
}
@log()
async searchForCommitsSimple(
repoPath: string,
search: SearchPattern,
_options?: { limit?: number; ordering?: 'date' | 'author-date' | 'topo' },
): Promise<GitSearch> {
search = { matchAll: false, matchCase: false, matchRegex: true, ...search };
return {
repoPath: repoPath,
pattern: search,
results: [],
};
// try {
// const { args: searchArgs, files, commits } = this.getArgsFromSearchPattern(search);
// if (commits?.length) {
// return {
// repoPath: repoPath,
// pattern: search,
// results: commits,
// };
// }
// const refParser = getGraphRefParser();
// const limit = options?.limit ?? configuration.get('advanced.maxSearchItems') ?? 0;
// const similarityThreshold = configuration.get('advanced.similarityThreshold');
// const args = [
// 'log',
// ...refParser.arguments,
// `-M${similarityThreshold == null ? '' : `${similarityThreshold}%`}`,
// '--use-mailmap',
// ];
// if (limit) {
// args.push(`-n${limit + 1}`);
// }
// if (options?.ordering) {
// args.push(`--${options.ordering}-order`);
// }
// searchArgs.push(`-M${similarityThreshold == null ? '' : `${similarityThreshold}%`}`, '--');
// if (files.length !== 0) {
// searchArgs.push(...files);
// }
// async function searchForCommitsCore(
// this: LocalGitProvider,
// limit: number,
// cursor?: { sha: string; skip: number },
// ): Promise<GitSearch> {
// const data = await this.git.log2(
// repoPath,
// undefined,
// ...args,
// ...(cursor?.skip ? [`--skip=${cursor.skip}`] : []),
// ...searchArgs,
// '--',
// ...files,
// );
// const results = [...refParser.parse(data)];
// const last = results[results.length - 1];
// cursor =
// last != null
// ? {
// sha: last,
// skip: results.length,
// }
// : undefined;
// return {
// repoPath: repoPath,
// pattern: search,
// results: results,
// paging:
// limit !== 0 && results.length > limit
// ? {
// limit: limit,
// startingCursor: cursor?.sha,
// more: true,
// }
// : undefined,
// more: async (limit: number): Promise<GitSearch | undefined> =>
// searchForCommitsCore.call(this, limit, cursor),
// };
// }
// return searchForCommitsCore.call(this, limit);
// } catch (ex) {
// // TODO@eamodio handle error reporting -- just invalid patterns? or more detailed?
// return {
// repoPath: repoPath,
// pattern: search,
// results: [],
// };
// }
}
@log()
async getLogForSearch(
repoPath: string,
search: SearchPattern,

+ 50
- 12
src/plus/webviews/graph/graphWebview.ts Bestand weergeven

@ -14,6 +14,7 @@ import { GitGraphRowType } from '../../../git/models/graph';
import type { GitGraph } from '../../../git/models/graph';
import type { Repository, RepositoryChangeEvent } from '../../../git/models/repository';
import { RepositoryChange, RepositoryChangeComparisonMode } from '../../../git/models/repository';
import type { SearchPattern } from '../../../git/search';
import { registerCommand } from '../../../system/command';
import { gate } from '../../../system/decorators/gate';
import { debug } from '../../../system/decorators/log';
@ -35,9 +36,11 @@ import {
DidChangeNotificationType,
DidChangeSelectionNotificationType,
DidChangeSubscriptionNotificationType,
DidSearchCommitsNotificationType,
DismissBannerCommandType,
GetMissingAvatarsCommandType,
GetMoreCommitsCommandType,
SearchCommitsCommandType,
UpdateColumnCommandType,
UpdateSelectedRepositoryCommandType,
UpdateSelectionCommandType,
@ -194,7 +197,10 @@ export class GraphWebview extends WebviewBase {
onIpc(GetMissingAvatarsCommandType, e, params => this.onGetMissingAvatars(params.emails));
break;
case GetMoreCommitsCommandType.method:
onIpc(GetMoreCommitsCommandType, e, params => this.onGetMoreCommits(params.sha));
onIpc(GetMoreCommitsCommandType, e, params => this.onGetMoreCommits(params.sha, e.id));
break;
case SearchCommitsCommandType.method:
onIpc(SearchCommitsCommandType, e, params => this.onSearchCommits(params.search, e.id));
break;
case UpdateColumnCommandType.method:
onIpc(UpdateColumnCommandType, e, params => this.onColumnUpdated(params.name, params.config));
@ -363,7 +369,7 @@ export class GraphWebview extends WebviewBase {
}
@gate()
private async onGetMoreCommits(sha?: string) {
private async onGetMoreCommits(sha?: string, completionId?: string) {
if (this._graph?.more == null || this._repository?.etag !== this._etagRepository) {
this.updateState(true);
@ -378,7 +384,35 @@ export class GraphWebview extends WebviewBase {
debugger;
}
void this.notifyDidChangeCommits();
void this.notifyDidChangeCommits(completionId);
}
@gate()
private async onSearchCommits(searchPattern: SearchPattern, completionId?: string) {
// if (this._repository?.etag !== this._etagRepository) {
// this.updateState(true);
// return;
// }
if (this._repository == null) return;
const search = await this._repository.searchForCommitsSimple(searchPattern, {
limit: 100,
ordering: configuration.get('graph.commitOrdering'),
});
void this.notify(
DidSearchCommitsNotificationType,
{
ids: search.results,
paging: {
startingCursor: search.paging?.startingCursor,
more: search.paging?.more ?? false,
},
},
completionId,
);
}
private onRepositorySelectionChanged(path: string) {
@ -498,20 +532,24 @@ export class GraphWebview extends WebviewBase {
}
@debug()
private async notifyDidChangeCommits() {
private async notifyDidChangeCommits(completionId?: string) {
let success = false;
if (this.isReady && this.visible) {
const data = this._graph!;
success = await this.notify(DidChangeCommitsNotificationType, {
rows: data.rows,
avatars: Object.fromEntries(data.avatars),
selectedRows: this._selectedRows,
paging: {
startingCursor: data.paging?.startingCursor,
more: data.paging?.more ?? false,
success = await this.notify(
DidChangeCommitsNotificationType,
{
rows: data.rows,
avatars: Object.fromEntries(data.avatars),
selectedRows: this._selectedRows,
paging: {
startingCursor: data.paging?.startingCursor,
more: data.paging?.more ?? false,
},
},
});
completionId,
);
}
this._pendingNotifyCommits = !success;

+ 15
- 0
src/plus/webviews/graph/protocol.ts Bestand weergeven

@ -2,6 +2,7 @@ import type { GraphRow, Remote } from '@gitkraken/gitkraken-components';
import type { DateStyle, GraphColumnConfig } from '../../../config';
import type { RepositoryVisibility } from '../../../git/gitProvider';
import type { GitGraphRowType } from '../../../git/models/graph';
import type { SearchPattern } from '../../../git/search';
import type { Subscription } from '../../../subscription';
import type { DateTimeFormat } from '../../../system/date';
import { IpcCommandType, IpcNotificationType } from '../../../webviews/protocol';
@ -24,6 +25,7 @@ export interface State {
// Props below are computed in the webview (not passed)
mixedColumnColors?: Record<string, string>;
searchResults?: DidSearchCommitsParams;
}
export interface GraphPaging {
@ -87,6 +89,11 @@ export interface GetMoreCommitsParams {
}
export const GetMoreCommitsCommandType = new IpcCommandType<GetMoreCommitsParams>('graph/getMoreCommits');
export interface SearchCommitsParams {
search: SearchPattern;
}
export const SearchCommitsCommandType = new IpcCommandType<SearchCommitsParams>('graph/searchCommits');
export interface UpdateColumnParams {
name: string;
config: GraphColumnConfig;
@ -149,3 +156,11 @@ export interface DidChangeSelectionParams {
export const DidChangeSelectionNotificationType = new IpcNotificationType<DidChangeSelectionParams>(
'graph/selection/didChange',
);
export interface DidSearchCommitsParams {
ids: string[];
paging?: GraphPaging;
}
export const DidSearchCommitsNotificationType = new IpcNotificationType<DidSearchCommitsParams>(
'graph/commits/didSearch',
);

+ 20
- 1
src/webviews/apps/plus/graph/GraphWrapper.tsx Bestand weergeven

@ -14,6 +14,7 @@ import { DateStyle } from '../../../../config';
import type { GraphColumnConfig } from '../../../../config';
import { RepositoryVisibility } from '../../../../git/gitProvider';
import type { GitGraphRowType } from '../../../../git/models/graph';
import type { SearchPattern } from '../../../../git/search';
import type {
DismissBannerParams,
GraphComponentConfig,
@ -34,7 +35,8 @@ export interface GraphWrapperProps extends State {
onSelectRepository?: (repository: GraphRepository) => void;
onColumnChange?: (name: string, settings: GraphColumnConfig) => void;
onMissingAvatars?: (emails: { [email: string]: string }) => void;
onMoreCommits?: () => void;
onMoreCommits?: (id?: string) => void;
onSearchCommits?: (search: SearchPattern) => void; //Promise<DidSearchCommitsParams>;
onDismissBanner?: (key: DismissBannerParams['key']) => void;
onSelectionChange?: (selection: { id: string; type: GitGraphRowType }[]) => void;
}
@ -89,6 +91,16 @@ const getGraphDateFormatter = (config?: GraphComponentConfig): OnFormatCommitDat
return (commitDateTime: number) => formatCommitDateTime(commitDateTime, config?.dateStyle, config?.dateFormat);
};
const getSearchHighlights = (searchResults: State['searchResults']): { [id: string]: boolean } | undefined => {
if (!searchResults?.ids?.length) return undefined;
const highlights: { [id: string]: boolean } = {};
for (const sha of searchResults.ids) {
highlights[sha] = true;
}
return highlights;
};
type DebouncableFn = (...args: any) => void;
type DebouncedFn = (...args: any) => void;
const debounceFrame = (func: DebouncableFn): DebouncedFn => {
@ -188,10 +200,12 @@ export function GraphWrapper({
onColumnChange,
onMissingAvatars,
onMoreCommits,
onSearchCommits,
onSelectionChange,
nonce,
mixedColumnColors,
previewBanner = true,
searchResults: searchResults2,
trialBanner = true,
onDismissBanner,
}: GraphWrapperProps) {
@ -228,11 +242,13 @@ export function GraphWrapper({
const [searchValue, setSearchValue] = useState('');
const [searchResults, setSearchResults] = useState<GraphRow[]>([]);
const [searchResultKey, setSearchResultKey] = useState<string | undefined>(undefined);
const [searchHighlights, setSearchHighlights] = useState(getSearchHighlights(searchResults2));
useEffect(() => {
if (searchValue === '' || searchValue.length < 3 || graphRows.length < 1) {
setSearchResults([]);
setSearchResultKey(undefined);
setSearchHighlights(undefined);
return;
}
@ -276,6 +292,7 @@ export function GraphWrapper({
const currentValue = e.currentTarget.value;
setSearchValue(currentValue);
onSearchCommits?.({ pattern: currentValue });
};
useLayoutEffect(() => {
@ -310,6 +327,7 @@ export function GraphWrapper({
setSubscriptionSnapshot(state.subscription);
setIsPrivateRepo(state.selectedRepositoryVisibility === RepositoryVisibility.Private);
setIsLoading(state.loading);
setSearchHighlights(getSearchHighlights(state.searchResults));
}
useEffect(() => subscriber?.(transformData), []);
@ -576,6 +594,7 @@ export function GraphWrapper({
graphRows={graphRows}
hasMoreCommits={pagingState?.more}
height={mainHeight}
highlightedShas={searchHighlights}
// highlightRowssOnRefHover={graphConfig?.highlightRowsOnRefHover}
isLoadingRows={isLoading}
isSelectedBySha={graphSelectedRows}

+ 33
- 2
src/webviews/apps/plus/graph/graph.tsx Bestand weergeven

@ -4,6 +4,7 @@ import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import type { GraphColumnConfig } from '../../../../config';
import type { GitGraphRowType } from '../../../../git/models/graph';
import type { SearchPattern } from '../../../../git/search';
import type {
DismissBannerParams,
GraphRepository,
@ -17,9 +18,11 @@ import {
DidChangeNotificationType,
DidChangeSelectionNotificationType,
DidChangeSubscriptionNotificationType,
DidSearchCommitsNotificationType,
DismissBannerCommandType,
GetMissingAvatarsCommandType,
GetMoreCommitsCommandType,
SearchCommitsCommandType,
UpdateColumnCommandType,
UpdateSelectedRepositoryCommandType as UpdateRepositorySelectionCommandType,
UpdateSelectionCommandType,
@ -72,6 +75,7 @@ export class GraphApp extends App {
)}
onMissingAvatars={(...params) => this.onGetMissingAvatars(...params)}
onMoreCommits={(...params) => this.onGetMoreCommits(...params)}
onSearchCommits={(...params) => this.onSearchCommits(...params)}
onSelectionChange={debounce(
(selection: { id: string; type: GitGraphRowType }[]) => this.onSelectionChanged(selection),
250,
@ -183,6 +187,13 @@ export class GraphApp extends App {
});
break;
case DidSearchCommitsNotificationType.method:
onIpc(DidSearchCommitsNotificationType, msg, params => {
this.setState({ ...this.state, searchResults: params });
this.refresh(this.state);
});
break;
case DidChangeSelectionNotificationType.method:
onIpc(DidChangeSelectionNotificationType, msg, params => {
this.setState({ ...this.state, selectedRows: params.selection });
@ -274,8 +285,28 @@ export class GraphApp extends App {
this.sendCommand(GetMissingAvatarsCommandType, { emails: emails });
}
private onGetMoreCommits(sha?: string) {
this.sendCommand(GetMoreCommitsCommandType, { sha: sha });
private onGetMoreCommits(sha?: string, wait?: boolean) {
if (wait) {
return this.sendCommandWithCompletion(
GetMoreCommitsCommandType,
{ sha: sha },
DidChangeCommitsNotificationType,
);
}
return this.sendCommand(GetMoreCommitsCommandType, { sha: sha });
}
private onSearchCommits(search: SearchPattern, wait?: boolean) {
if (wait) {
return this.sendCommandWithCompletion(
SearchCommitsCommandType,
{ search: search },
DidSearchCommitsNotificationType,
);
}
return this.sendCommand(SearchCommitsCommandType, { search: search });
}
private onSelectionChanged(selection: { id: string; type: GitGraphRowType }[]) {

Laden…
Annuleren
Opslaan