Skip to content

Commit c33e714

Browse files
committed
Reflects search results in graph details
1 parent ac02641 commit c33e714

File tree

18 files changed

+404
-35
lines changed

18 files changed

+404
-35
lines changed

src/env/node/git/sub-providers/graph.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import type { GitWorktree } from '../../../../git/models/worktree';
2525
import {
2626
getGraphParser,
2727
getShaAndDatesLogParser,
28+
getShaAndDatesWithFilesLogParser,
2829
getShaAndStatsLogParser,
2930
getShaLogParser,
3031
} from '../../../../git/parsers/logParser';
@@ -646,7 +647,9 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
646647
const { args: searchArgs, files, shas, filters } = parseSearchQueryGitCommand(search, currentUser);
647648

648649
const tipsOnly = filters.type === 'tip';
649-
const parser = getShaAndDatesLogParser(tipsOnly);
650+
const parser = filters.files
651+
? getShaAndDatesWithFilesLogParser(tipsOnly)
652+
: getShaAndDatesLogParser(tipsOnly);
650653

651654
const similarityThreshold = configuration.get('advanced.similarityThreshold');
652655
const args = [
@@ -745,6 +748,7 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
745748
results.set(sha, {
746749
i: results.size,
747750
date: Number(options?.ordering === 'author-date' ? r.authorDate : r.committerDate) * 1000,
751+
files: r.files,
748752
});
749753
}
750754

@@ -755,6 +759,7 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
755759
return {
756760
repoPath: repoPath,
757761
query: search,
762+
queryFilters: filters,
758763
comparisonKey: comparisonKey,
759764
results: results,
760765
paging: limit ? { limit: limit, hasMore: hasMore } : undefined,
@@ -763,7 +768,13 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
763768
};
764769
} catch (ex) {
765770
if (isCancellationError(ex) || cancellation?.isCancellationRequested) {
766-
return { repoPath: repoPath, query: search, comparisonKey: comparisonKey, results: results };
771+
return {
772+
repoPath: repoPath,
773+
query: search,
774+
queryFilters: filters,
775+
comparisonKey: comparisonKey,
776+
results: results,
777+
};
767778
}
768779

769780
throw new GitSearchError(ex);

src/eventBus.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import type { Disposable, Uri } from 'vscode';
22
import { EventEmitter } from 'vscode';
3+
import type { SearchQuery } from './constants.search';
34
import type { CustomEditorIds, ViewIds, WebviewIds } from './constants.views';
45
import type { CachedGitTypes } from './git/gitProvider';
56
import type { GitCommit } from './git/models/commit';
67
import type { GitRevisionReference } from './git/models/reference';
78
import type { RepositoryChange } from './git/models/repository';
9+
import type { SearchQueryFilters } from './git/search';
810
import type { Draft, LocalDraft } from './plus/drafts/models/drafts';
911

1012
export type CommitSelectedEvent = EventBusEvent<'commit:selected'>;
@@ -13,6 +15,11 @@ interface CommitSelectedEventArgs {
1315
readonly interaction: 'active' | 'passive';
1416
readonly preserveFocus?: boolean;
1517
readonly preserveVisibility?: boolean;
18+
readonly searchContext?: {
19+
readonly query: SearchQuery;
20+
readonly queryFilters: SearchQueryFilters;
21+
readonly matchedFiles: ReadonlyArray<Readonly<{ readonly path: string }>>;
22+
};
1623
}
1724

1825
export type DraftSelectedEvent = EventBusEvent<'draft:selected'>;

src/git/parsers/logParser.ts

Lines changed: 144 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,24 @@ export function getShaAndDatesLogParser(includeTips?: boolean): ShaAndDatesLogPa
116116
return _shaAndDatesParser;
117117
}
118118

119+
type ShaAndDatesWithFilesLogParser = LogParserWithFiles<typeof shaAndDateMapping & { tips?: string }>;
120+
let _shaAndDatesWithFilesParser: ShaAndDatesWithFilesLogParser | undefined;
121+
122+
type ShaAndDatesAndTipsWithFilesLogParser = LogParserWithFiles<typeof shaAndDateAndTipsMapping>;
123+
let _shaAndDatesAndTipsWithFilesParser: ShaAndDatesAndTipsWithFilesLogParser | undefined;
124+
125+
export function getShaAndDatesWithFilesLogParser(
126+
includeTips?: boolean,
127+
): ShaAndDatesWithFilesLogParser | ShaAndDatesAndTipsWithFilesLogParser {
128+
if (includeTips) {
129+
_shaAndDatesAndTipsWithFilesParser ??= createLogParserWithFiles(shaAndDateAndTipsMapping);
130+
return _shaAndDatesAndTipsWithFilesParser;
131+
}
132+
133+
_shaAndDatesWithFilesParser ??= createLogParserWithFiles(shaAndDateMapping);
134+
return _shaAndDatesWithFilesParser;
135+
}
136+
119137
const shaMapping = { sha: '%H' };
120138

121139
type ShaAndFilesAndStatsLogParser = LogParserWithFilesAndStats<typeof shaMapping>;
@@ -208,7 +226,7 @@ type LogParsedEntryWithStats<T> = { [K in keyof T]: string } & { stats: LogParse
208226

209227
// Parsed types
210228
export interface LogParsedFile {
211-
status: string;
229+
status?: string;
212230
path: string;
213231
originalPath?: string;
214232
additions?: never;
@@ -1080,3 +1098,128 @@ function createLogParserWithStats<T extends Record<string, string>>(
10801098
parseAsync: parseAsync,
10811099
};
10821100
}
1101+
1102+
function createLogParserWithFiles<T extends Record<string, string>>(
1103+
mapping: ExtractAll<T, string>,
1104+
): LogParserWithFiles<T> {
1105+
let format = recordFormatSep;
1106+
const keys: (keyof ExtractAll<T, string>)[] = [];
1107+
for (const key in mapping) {
1108+
keys.push(key);
1109+
format += `${mapping[key]}${fieldFormatSep}`;
1110+
}
1111+
1112+
const args = [`--format=${format}`, '--name-only'];
1113+
1114+
function parseFileNames(content: string): LogParsedFile[] {
1115+
const files: LogParsedFile[] = [];
1116+
if (!content?.length) return files;
1117+
1118+
for (const line of iterateByDelimiter(content, '\n')) {
1119+
const trimmed = line.trim();
1120+
if (!trimmed) continue;
1121+
1122+
files.push({ path: trimmed });
1123+
}
1124+
1125+
return files;
1126+
}
1127+
1128+
function* parse(data: string | Iterable<string> | undefined): Generator<LogParsedEntryWithFiles<T>> {
1129+
using sw = maybeStopWatch('Git.createLogParserWithFiles.parse', { log: false, logLevel: 'debug' });
1130+
1131+
if (!data) {
1132+
sw?.stop({ suffix: ` no data` });
1133+
return;
1134+
}
1135+
1136+
const records = iterateByDelimiter(data, recordSep);
1137+
1138+
let count = 0;
1139+
let entry: LogParsedEntryWithFiles<T>;
1140+
let fields: IterableIterator<string>;
1141+
1142+
for (const record of records) {
1143+
if (!record.length) continue;
1144+
1145+
count++;
1146+
entry = {} as unknown as LogParsedEntryWithFiles<T>;
1147+
fields = iterateByDelimiter(record, fieldSep);
1148+
1149+
let fieldCount = 0;
1150+
let field;
1151+
while (true) {
1152+
field = fields.next();
1153+
if (field.done) break;
1154+
1155+
if (fieldCount < keys.length) {
1156+
entry[keys[fieldCount++]] = field.value as LogParsedEntryWithFiles<T>[keyof T];
1157+
} else if (fieldCount === keys.length) {
1158+
// Slice off the first newlines between the commit data and files, if any
1159+
const files = field.value.startsWith('\n\n')
1160+
? field.value.substring(2)
1161+
: field.value.startsWith('\n')
1162+
? field.value.substring(1)
1163+
: field.value;
1164+
entry.files = parseFileNames(files);
1165+
} else {
1166+
debugger;
1167+
}
1168+
}
1169+
1170+
yield entry;
1171+
}
1172+
1173+
sw?.stop({ suffix: ` parsed ${count} records` });
1174+
}
1175+
1176+
async function* parseAsync(stream: AsyncGenerator<string>): AsyncGenerator<LogParsedEntryWithFiles<T>> {
1177+
using sw = maybeStopWatch('Git.createLogParserWithFiles.parseAsync', { log: false, logLevel: 'debug' });
1178+
1179+
const records = iterateAsyncByDelimiter(stream, recordSep);
1180+
1181+
let count = 0;
1182+
let entry: LogParsedEntryWithFiles<T>;
1183+
let fields: IterableIterator<string>;
1184+
1185+
for await (const record of records) {
1186+
if (!record.length) continue;
1187+
1188+
count++;
1189+
entry = {} as unknown as LogParsedEntryWithFiles<T>;
1190+
fields = iterateByDelimiter(record, fieldSep);
1191+
1192+
let fieldCount = 0;
1193+
let field;
1194+
while (true) {
1195+
field = fields.next();
1196+
if (field.done) break;
1197+
1198+
if (fieldCount < keys.length) {
1199+
entry[keys[fieldCount++]] = field.value as LogParsedEntryWithFiles<T>[keyof T];
1200+
} else if (fieldCount === keys.length) {
1201+
// Slice off the first newlines between the commit data and files, if any
1202+
const files = field.value.startsWith('\n\n')
1203+
? field.value.substring(2)
1204+
: field.value.startsWith('\n')
1205+
? field.value.substring(1)
1206+
: field.value;
1207+
entry.files = parseFileNames(files);
1208+
} else {
1209+
debugger;
1210+
}
1211+
}
1212+
1213+
yield entry;
1214+
}
1215+
1216+
sw?.stop({ suffix: ` parsed ${count} records` });
1217+
}
1218+
1219+
return {
1220+
arguments: args,
1221+
separators: { record: recordSep, field: fieldSep },
1222+
parse: parse,
1223+
parseAsync: parseAsync,
1224+
};
1225+
}

src/git/search.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ import { isSha, shortenRevision } from './utils/revision.utils';
99
export interface GitGraphSearchResultData {
1010
readonly date: number;
1111
readonly i: number;
12+
readonly files?: ReadonlyArray<Readonly<{ readonly path: string }>>;
1213
}
1314
export type GitGraphSearchResults = Map<string, GitGraphSearchResultData>;
1415

1516
export interface GitGraphSearch {
1617
readonly repoPath: string;
1718
readonly query: SearchQuery;
19+
readonly queryFilters: SearchQueryFilters;
1820
readonly comparisonKey: string;
1921
readonly results: GitGraphSearchResults;
2022

src/plus/integrations/providers/github/sub-providers/graph.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
518518
: undefined;
519519

520520
const results: GitGraphSearchResults = new Map<string, GitGraphSearchResultData>();
521-
const { args: queryArgs, operations } = parseSearchQueryGitHubCommand(search, currentUser);
521+
const { args: queryArgs, filters, operations } = parseSearchQueryGitHubCommand(search, currentUser);
522522

523523
const values = operations.get('commit:');
524524
if (values != null) {
@@ -534,12 +534,14 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
534534
results.set(commit.sha, {
535535
i: i++,
536536
date: Number(options?.ordering === 'author-date' ? commit.author.date : commit.committer.date),
537+
files: commit.fileset?.files,
537538
});
538539
}
539540

540541
return {
541542
repoPath: repoPath,
542543
query: search,
544+
queryFilters: filters,
543545
comparisonKey: comparisonKey,
544546
results: results,
545547
};
@@ -549,6 +551,7 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
549551
return {
550552
repoPath: repoPath,
551553
query: search,
554+
queryFilters: filters,
552555
comparisonKey: comparisonKey,
553556
results: results,
554557
};
@@ -564,7 +567,13 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
564567
cursor?: string,
565568
): Promise<GitGraphSearch> {
566569
if (cancellation?.isCancellationRequested) {
567-
return { repoPath: repoPath, query: search, comparisonKey: comparisonKey, results: results };
570+
return {
571+
repoPath: repoPath,
572+
query: search,
573+
queryFilters: filters,
574+
comparisonKey: comparisonKey,
575+
results: results,
576+
};
568577
}
569578

570579
limit = this.provider.getPagingLimit(limit ?? configuration.get('advanced.maxSearchItems'));
@@ -580,13 +589,20 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
580589
});
581590

582591
if (result == null || cancellation?.isCancellationRequested) {
583-
return { repoPath: repoPath, query: search, comparisonKey: comparisonKey, results: results };
592+
return {
593+
repoPath: repoPath,
594+
query: search,
595+
queryFilters: filters,
596+
comparisonKey: comparisonKey,
597+
results: results,
598+
};
584599
}
585600

586601
for (const commit of result.values) {
587602
results.set(commit.sha, {
588603
i: results.size,
589604
date: Number(options?.ordering === 'author-date' ? commit.authorDate : commit.committerDate),
605+
files: undefined,
590606
});
591607
}
592608

@@ -595,14 +611,10 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
595611
return {
596612
repoPath: repoPath,
597613
query: search,
614+
queryFilters: filters,
598615
comparisonKey: comparisonKey,
599616
results: results,
600-
paging: result.pageInfo?.hasNextPage
601-
? {
602-
limit: limit,
603-
hasMore: true,
604-
}
605-
: undefined,
617+
paging: result.pageInfo?.hasNextPage ? { limit: limit, hasMore: true } : undefined,
606618
more: async (limit: number): Promise<GitGraphSearch> => searchGraphCore.call(this, limit, cursor),
607619
};
608620
}

src/webviews/apps/commitDetails/commitDetails.scss

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,24 @@
99
--gl-color-background-counter: #000;
1010
}
1111

12+
// Vertical split mask for mixed filter mode icon
13+
action-item.filter-mode-mixed::part(icon) {
14+
-webkit-mask: linear-gradient(
15+
to right,
16+
rgba(0, 0, 0, 1) 0%,
17+
rgba(0, 0, 0, 1) 50%,
18+
rgba(0, 0, 0, 0.4) 50%,
19+
rgba(0, 0, 0, 0.4) 100%
20+
);
21+
mask: linear-gradient(
22+
to right,
23+
rgba(0, 0, 0, 1) 0%,
24+
rgba(0, 0, 0, 1) 50%,
25+
rgba(0, 0, 0, 0.4) 50%,
26+
rgba(0, 0, 0, 0.4) 100%
27+
);
28+
}
29+
1230
.commit-detail-panel {
1331
height: 100vh;
1432
display: flex;

src/webviews/apps/commitDetails/commitDetails.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ export class GlCommitDetailsApp extends GlAppHost<IpcSerialized<State>> {
401401
.preferences=${this.state?.preferences}
402402
.orgSettings=${this.state?.orgSettings}
403403
.isUncommitted=${this.isUncommitted}
404+
.searchContext=${this.state?.searchContext}
404405
></gl-commit-details>`,
405406
() =>
406407
html`<gl-wip-details

src/webviews/apps/commitDetails/components/gl-commit-details.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,11 +434,17 @@ export class GlCommitDetails extends GlDetailsBase {
434434
if (stats?.files == null) return undefined;
435435

436436
if (typeof stats.files === 'number') {
437-
return html`<commit-stats added="?" modified="${stats.files}" removed="?"></commit-stats>`;
437+
return html`<commit-stats modified="${stats.files}" symbol="icons" appearance="pill"></commit-stats>`;
438438
}
439439

440440
const { added, deleted, changed } = stats.files;
441-
return html`<commit-stats added="${added}" modified="${changed}" removed="${deleted}"></commit-stats>`;
441+
return html`<commit-stats
442+
added="${added}"
443+
modified="${changed}"
444+
removed="${deleted}"
445+
symbol="icons"
446+
appearance="pill"
447+
></commit-stats>`;
442448
}
443449

444450
override getFileActions(_file: File, _options?: Partial<TreeItemBase>): TreeItemAction[] {

0 commit comments

Comments
 (0)