Skip to content

Commit c3964ae

Browse files
committed
Refactors explain commands
- Adds ExplainCommandBase - Orients to GitRepositoryService - Simplifies markdown generation
1 parent 432475f commit c3964ae

File tree

7 files changed

+196
-259
lines changed

7 files changed

+196
-259
lines changed

src/commands/explainBase.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import type { TextEditor, Uri } from 'vscode';
2+
import type { GlCommands } from '../constants.commands';
3+
import type { Container } from '../container';
4+
import type { MarkdownContentMetadata } from '../documents/markdown';
5+
import { getMarkdownHeaderContent } from '../documents/markdown';
6+
import type { GitRepositoryService } from '../git/gitRepositoryService';
7+
import { GitUri } from '../git/gitUri';
8+
import type { AIExplainSource, AISummarizeResult } from '../plus/ai/aiProviderService';
9+
import { getBestRepositoryOrShowPicker } from '../quickpicks/repositoryPicker';
10+
import { showMarkdownPreview } from '../system/-webview/markdown';
11+
import { GlCommandBase } from './commandBase';
12+
import { getCommandUri } from './commandBase.utils';
13+
14+
export interface ExplainBaseArgs {
15+
worktreePath?: string | Uri;
16+
repoPath?: string | Uri;
17+
source?: AIExplainSource;
18+
}
19+
20+
export abstract class ExplainCommandBase extends GlCommandBase {
21+
abstract pickerTitle: string;
22+
abstract repoPickerPlaceholder: string;
23+
24+
constructor(
25+
protected readonly container: Container,
26+
command: GlCommands | GlCommands[],
27+
) {
28+
super(command);
29+
}
30+
31+
protected async getRepositoryService(
32+
editor?: TextEditor,
33+
uri?: Uri,
34+
args?: ExplainBaseArgs,
35+
): Promise<GitRepositoryService | undefined> {
36+
let svc;
37+
if (args?.worktreePath) {
38+
svc = this.container.git.getRepositoryService(args.worktreePath);
39+
} else if (args?.repoPath) {
40+
svc = this.container.git.getRepositoryService(args.repoPath);
41+
} else {
42+
uri = getCommandUri(uri, editor);
43+
const gitUri = uri != null ? await GitUri.fromUri(uri) : undefined;
44+
const repository = await getBestRepositoryOrShowPicker(
45+
gitUri,
46+
editor,
47+
this.pickerTitle,
48+
this.repoPickerPlaceholder,
49+
);
50+
51+
svc = repository?.git;
52+
}
53+
54+
return svc;
55+
}
56+
57+
protected openDocument(result: AISummarizeResult, path: string, metadata: MarkdownContentMetadata): void {
58+
const content = `${getMarkdownHeaderContent(metadata)}\n\n${result.parsed.summary}\n\n${result.parsed.body}`;
59+
60+
const documentUri = this.container.markdown.openDocument(content, path, metadata.header.title, metadata);
61+
62+
showMarkdownPreview(documentUri);
63+
}
64+
}

src/commands/explainBranch.ts

Lines changed: 23 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,30 @@
11
import type { TextEditor, Uri } from 'vscode';
22
import { ProgressLocation } from 'vscode';
33
import type { Container } from '../container';
4-
import type { MarkdownContentMetadata } from '../documents/markdown';
5-
import { GitUri } from '../git/gitUri';
64
import type { GitBranchReference } from '../git/models/reference';
75
import { getBranchMergeTargetName } from '../git/utils/-webview/branch.utils';
86
import { showGenericErrorMessage } from '../messages';
9-
import type { AIExplainSource } from '../plus/ai/aiProviderService';
107
import { prepareCompareDataForAIRequest } from '../plus/ai/aiProviderService';
118
import { ReferencesQuickPickIncludes, showReferencePicker } from '../quickpicks/referencePicker';
12-
import { getBestRepositoryOrShowPicker } from '../quickpicks/repositoryPicker';
139
import { command } from '../system/-webview/command';
14-
import { showMarkdownPreview } from '../system/-webview/markdown';
1510
import { Logger } from '../system/logger';
1611
import { getNodeRepoPath } from '../views/nodes/abstract/viewNode';
17-
import { GlCommandBase } from './commandBase';
18-
import { getCommandUri } from './commandBase.utils';
1912
import type { CommandContext } from './commandContext';
2013
import { isCommandContextViewNodeHasBranch } from './commandContext.utils';
14+
import type { ExplainBaseArgs } from './explainBase';
15+
import { ExplainCommandBase } from './explainBase';
2116

22-
export interface ExplainBranchCommandArgs {
23-
repoPath?: string | Uri;
17+
export interface ExplainBranchCommandArgs extends ExplainBaseArgs {
2418
ref?: string;
25-
source?: AIExplainSource;
2619
}
2720

2821
@command()
29-
export class ExplainBranchCommand extends GlCommandBase {
30-
constructor(private readonly container: Container) {
31-
super(['gitlens.ai.explainBranch', 'gitlens.ai.explainBranch:views']);
22+
export class ExplainBranchCommand extends ExplainCommandBase {
23+
pickerTitle = 'Explain Branch Changes';
24+
repoPickerPlaceholder = 'Choose which repository to explain a branch from';
25+
26+
constructor(container: Container) {
27+
super(container, ['gitlens.ai.explainBranch', 'gitlens.ai.explainBranch:views']);
3228
}
3329

3430
protected override preExecute(context: CommandContext, args?: ExplainBranchCommandArgs): Promise<void> {
@@ -45,41 +41,26 @@ export class ExplainBranchCommand extends GlCommandBase {
4541
async execute(editor?: TextEditor, uri?: Uri, args?: ExplainBranchCommandArgs): Promise<void> {
4642
args = { ...args };
4743

48-
let repository;
49-
if (args?.repoPath != null) {
50-
repository = this.container.git.getRepository(args.repoPath);
51-
} else {
52-
uri = getCommandUri(uri, editor);
53-
const gitUri = uri != null ? await GitUri.fromUri(uri) : undefined;
54-
repository = await getBestRepositoryOrShowPicker(
55-
gitUri,
56-
editor,
57-
'Explain Branch Changes',
58-
'Choose which repository to explain a branch from',
59-
);
44+
const svc = await this.getRepositoryService(editor, uri, args);
45+
if (svc == null) {
46+
void showGenericErrorMessage('Unable to find a repository');
47+
return;
6048
}
6149

62-
if (repository == null) return;
63-
6450
try {
6551
// Clarifying the head branch
6652
if (args.ref == null) {
6753
// If no ref is provided, show a picker to select a branch
68-
const pick = (await showReferencePicker(
69-
repository.path,
70-
'Explain Branch Changes',
71-
'Choose a branch to explain',
72-
{
73-
include: ReferencesQuickPickIncludes.Branches,
74-
sort: { branches: { current: true } },
75-
},
76-
)) as GitBranchReference | undefined;
54+
const pick = (await showReferencePicker(svc.path, this.pickerTitle, 'Choose a branch to explain', {
55+
include: ReferencesQuickPickIncludes.Branches,
56+
sort: { branches: { current: true } },
57+
})) as GitBranchReference | undefined;
7758
if (pick?.ref == null) return;
7859
args.ref = pick.ref;
7960
}
8061

8162
// Get the branch
82-
const branch = await repository.git.branches.getBranch(args.ref);
63+
const branch = await svc.branches.getBranch(args.ref);
8364
if (branch == null) {
8465
void showGenericErrorMessage('Unable to find the specified branch');
8566
return;
@@ -89,7 +70,7 @@ export class ExplainBranchCommand extends GlCommandBase {
8970
const baseBranchNameResult = await getBranchMergeTargetName(this.container, branch);
9071
let baseBranch;
9172
if (!baseBranchNameResult.paused) {
92-
baseBranch = await repository.git.branches.getBranch(baseBranchNameResult.value);
73+
baseBranch = await svc.branches.getBranch(baseBranchNameResult.value);
9374
}
9475

9576
if (!baseBranch) {
@@ -98,7 +79,7 @@ export class ExplainBranchCommand extends GlCommandBase {
9879
}
9980

10081
// Get the diff between the branch and its upstream or base
101-
const compareData = await prepareCompareDataForAIRequest(repository, branch.ref, baseBranch.ref, {
82+
const compareData = await prepareCompareDataForAIRequest(svc, branch.ref, baseBranch.ref, {
10283
reportNoDiffService: () => void showGenericErrorMessage('Unable to get diff service'),
10384
reportNoCommitsService: () => void showGenericErrorMessage('Unable to get commits service'),
10485
reportNoChanges: () => void showGenericErrorMessage('No changes found to explain'),
@@ -138,9 +119,7 @@ export class ExplainBranchCommand extends GlCommandBase {
138119
return;
139120
}
140121

141-
const content = `# Branch Summary\n\n> Generated by ${result.model.name}\n\n## ${branch.name}\n\n${result.parsed.summary}\n\n${result.parsed.body}`;
142-
143-
const contentMetadata = {
122+
this.openDocument(result, `/explain/branch/${branch.ref}/${result.model.id}`, {
144123
header: {
145124
title: 'Branch Summary',
146125
subtitle: branch.name,
@@ -149,18 +128,9 @@ export class ExplainBranchCommand extends GlCommandBase {
149128
command: {
150129
label: 'Explain Branch Changes',
151130
name: 'gitlens.ai.explainBranch',
152-
args: { repoPath: repository.path, ref: branch.ref, source: args.source },
131+
args: { repoPath: svc.path, ref: branch.ref, source: args.source },
153132
},
154-
};
155-
156-
const documentUri = this.container.markdown.openDocument(
157-
content,
158-
`/explain/branch/${branch.ref}/${result.model.id}`,
159-
branch.name,
160-
contentMetadata as MarkdownContentMetadata,
161-
);
162-
163-
showMarkdownPreview(documentUri);
133+
});
164134
} catch (ex) {
165135
Logger.error(ex, 'ExplainBranchCommand', 'execute');
166136
void showGenericErrorMessage('Unable to explain branch');

src/commands/explainCommit.ts

Lines changed: 37 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,37 @@
11
import type { TextEditor, Uri } from 'vscode';
22
import { ProgressLocation } from 'vscode';
33
import type { Container } from '../container';
4-
import { GitUri } from '../git/gitUri';
54
import type { GitCommit } from '../git/models/commit';
65
import { isStash } from '../git/models/commit';
76
import { showGenericErrorMessage } from '../messages';
8-
import type { AIExplainSource } from '../plus/ai/aiProviderService';
97
import { showCommitPicker } from '../quickpicks/commitPicker';
10-
import { getBestRepositoryOrShowPicker } from '../quickpicks/repositoryPicker';
118
import { command } from '../system/-webview/command';
12-
import { showMarkdownPreview } from '../system/-webview/markdown';
139
import { createMarkdownCommandLink } from '../system/commands';
1410
import { Logger } from '../system/logger';
1511
import { getNodeRepoPath } from '../views/nodes/abstract/viewNode';
16-
import { GlCommandBase } from './commandBase';
17-
import { getCommandUri } from './commandBase.utils';
1812
import type { CommandContext } from './commandContext';
1913
import { isCommandContextViewNodeHasCommit } from './commandContext.utils';
14+
import type { ExplainBaseArgs } from './explainBase';
15+
import { ExplainCommandBase } from './explainBase';
2016

21-
export interface ExplainCommitCommandArgs {
22-
repoPath?: string | Uri;
17+
export interface ExplainCommitCommandArgs extends ExplainBaseArgs {
2318
rev?: string;
24-
source?: AIExplainSource;
2519
}
2620

2721
@command()
28-
export class ExplainCommitCommand extends GlCommandBase {
22+
export class ExplainCommitCommand extends ExplainCommandBase {
23+
pickerTitle = 'Explain Commit Changes';
24+
repoPickerPlaceholder = 'Choose which repository to explain a commit from';
2925
static createMarkdownCommandLink(args: ExplainCommitCommandArgs): string {
3026
return createMarkdownCommandLink<ExplainCommitCommandArgs>('gitlens.ai.explainCommit:editor', args);
3127
}
3228

33-
constructor(private readonly container: Container) {
34-
super(['gitlens.ai.explainCommit', 'gitlens.ai.explainCommit:editor', 'gitlens.ai.explainCommit:views']);
29+
constructor(container: Container) {
30+
super(container, [
31+
'gitlens.ai.explainCommit',
32+
'gitlens.ai.explainCommit:editor',
33+
'gitlens.ai.explainCommit:views',
34+
]);
3535
}
3636

3737
protected override preExecute(context: CommandContext, args?: ExplainCommitCommandArgs): Promise<void> {
@@ -52,35 +52,25 @@ export class ExplainCommitCommand extends GlCommandBase {
5252
async execute(editor?: TextEditor, uri?: Uri, args?: ExplainCommitCommandArgs): Promise<void> {
5353
args = { ...args };
5454

55-
let repository;
56-
if (args?.repoPath != null) {
57-
repository = this.container.git.getRepository(args.repoPath);
55+
const svc = await this.getRepositoryService(editor, uri, args);
56+
if (svc == null) {
57+
void showGenericErrorMessage('Unable to find a repository');
58+
return;
5859
}
5960

60-
if (repository == null) {
61-
uri = getCommandUri(uri, editor);
62-
const gitUri = uri != null ? await GitUri.fromUri(uri) : undefined;
63-
repository = await getBestRepositoryOrShowPicker(
64-
gitUri,
65-
editor,
66-
'Explain Commit Changes',
67-
'Choose which repository to explain a commit from',
68-
);
69-
}
70-
71-
if (repository == null) return;
72-
7361
try {
62+
const commitsProvider = svc.commits;
63+
7464
let commit: GitCommit | undefined;
7565
if (args.rev == null) {
76-
const log = await repository.git.commits.getLog();
77-
const pick = await showCommitPicker(log, 'Explain Commit Changes', 'Choose a commit to explain');
66+
const log = await commitsProvider.getLog();
67+
const pick = await showCommitPicker(log, this.pickerTitle, 'Choose a commit to explain');
7868
if (pick?.sha == null) return;
7969
args.rev = pick.sha;
8070
commit = pick;
8171
} else {
8272
// Get the commit
83-
commit = await repository.git.commits.getCommit(args.rev);
73+
commit = await commitsProvider.getCommit(args.rev);
8474
if (commit == null) {
8575
void showGenericErrorMessage('Unable to find the specified commit');
8676
return;
@@ -93,44 +83,34 @@ export class ExplainCommitCommand extends GlCommandBase {
9383
{
9484
...args.source,
9585
source: args.source?.source ?? 'commandPalette',
96-
type: 'commit',
86+
type: args.source?.type ?? 'commit',
9787
},
9888
{
9989
progress: { location: ProgressLocation.Notification, title: 'Explaining commit...' },
10090
},
10191
);
10292

10393
if (result == null) {
104-
void showGenericErrorMessage('No changes found to explain for commit');
94+
void showGenericErrorMessage('Unable to explain commit');
10595
return;
10696
}
10797

108-
// Display the result
109-
const content = `# Commit Summary\n\n> Generated by ${result.model.name}\n\n## ${commit.summary} (${commit.shortSha})\n\n${result.parsed.summary}\n\n${result.parsed.body}`;
110-
111-
const documentUri = this.container.markdown.openDocument(
112-
content,
113-
`/explain/commit/${commit.ref}/${result.model.id}`,
114-
`${commit.shortSha} - ${commit.summary}`,
115-
{
116-
header: {
117-
title: 'Commit Summary',
118-
aiModel: result.model.name,
119-
subtitle: `${commit.summary} (${commit.shortSha})`,
120-
},
121-
command: {
122-
label: 'Regenerate Commit Summary',
123-
name: 'gitlens.ai.explainCommit',
124-
args: {
125-
repoPath: repository.path,
126-
rev: commit.ref,
127-
source: args.source,
128-
},
98+
this.openDocument(result, `/explain/commit/${commit.ref}/${result.model.id}`, {
99+
header: {
100+
title: 'Commit Summary',
101+
aiModel: result.model.name,
102+
subtitle: `${commit.summary} (${commit.shortSha})`,
103+
},
104+
command: {
105+
label: 'Explain Commit Summary',
106+
name: 'gitlens.ai.explainCommit',
107+
args: {
108+
repoPath: svc.path,
109+
rev: commit.ref,
110+
source: args.source,
129111
},
130112
},
131-
);
132-
133-
showMarkdownPreview(documentUri);
113+
});
134114
} catch (ex) {
135115
Logger.error(ex, 'ExplainCommitCommand', 'execute');
136116
void showGenericErrorMessage('Unable to explain commit');

0 commit comments

Comments
 (0)