Skip to content

Commit 95b8f36

Browse files
committed
Adds command to Explain WIP
1 parent 0bf0e81 commit 95b8f36

File tree

6 files changed

+181
-0
lines changed

6 files changed

+181
-0
lines changed

contributions.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@
4949
]
5050
}
5151
},
52+
"gitlens.ai.explainWip": {
53+
"label": "Explain Working Changes (Preview)...",
54+
"commandPalette": "gitlens:enabled && !gitlens:readonly && !gitlens:untrusted && gitlens:gk:organization:ai:enabled",
55+
"menus": {
56+
"view/item/context": [
57+
{
58+
"when": "viewItem =~ /gitlens:worktree\\b/ && !listMultiSelection && !gitlens:readonly && !gitlens:untrusted && gitlens:gk:organization:ai:enabled",
59+
"group": "3_gitlens_ai",
60+
"order": 1
61+
}
62+
]
63+
}
64+
},
5265
"gitlens.ai.generateChangelog": {
5366
"label": "Generate Changelog (Preview)...",
5467
"commandPalette": "gitlens:enabled && !gitlens:untrusted && gitlens:gk:organization:ai:enabled && config.gitlens.ai.enabled"
@@ -2012,6 +2025,18 @@
20122025
]
20132026
}
20142027
},
2028+
"gitlens.graph.explainWip": {
2029+
"label": "Explain Working Changes (Preview)",
2030+
"menus": {
2031+
"webview/context": [
2032+
{
2033+
"when": "webviewItem =~ /gitlens:wip\\b/ && !listMultiSelection && !gitlens:readonly && !gitlens:untrusted && gitlens:gk:organization:ai:enabled",
2034+
"group": "1_gitlens_actions_3",
2035+
"order": 1
2036+
}
2037+
]
2038+
}
2039+
},
20152040
"gitlens.graph.fetch": {
20162041
"label": "Fetch",
20172042
"icon": "$(repo-fetch)",

package.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6084,6 +6084,11 @@
60846084
"title": "Explain Stash (Preview)...",
60856085
"category": "GitLens"
60866086
},
6087+
{
6088+
"command": "gitlens.ai.explainWip",
6089+
"title": "Explain Working Changes (Preview)...",
6090+
"category": "GitLens"
6091+
},
60876092
{
60886093
"command": "gitlens.ai.generateChangelog",
60896094
"title": "Generate Changelog (Preview)...",
@@ -6855,6 +6860,10 @@
68556860
"command": "gitlens.graph.explainStash",
68566861
"title": "Explain Stash (Preview)"
68576862
},
6863+
{
6864+
"command": "gitlens.graph.explainWip",
6865+
"title": "Explain Working Changes (Preview)"
6866+
},
68586867
{
68596868
"command": "gitlens.graph.fetch",
68606869
"title": "Fetch",
@@ -10408,6 +10417,10 @@
1040810417
"command": "gitlens.ai.explainStash",
1040910418
"when": "gitlens:enabled && !gitlens:readonly && !gitlens:untrusted && gitlens:gk:organization:ai:enabled && config.gitlens.ai.enabled"
1041010419
},
10420+
{
10421+
"command": "gitlens.ai.explainWip",
10422+
"when": "gitlens:enabled && !gitlens:readonly && !gitlens:untrusted && gitlens:gk:organization:ai:enabled"
10423+
},
1041110424
{
1041210425
"command": "gitlens.ai.generateChangelog",
1041310426
"when": "gitlens:enabled && !gitlens:untrusted && gitlens:gk:organization:ai:enabled && config.gitlens.ai.enabled"
@@ -11008,6 +11021,10 @@
1100811021
"command": "gitlens.graph.explainStash",
1100911022
"when": "false"
1101011023
},
11024+
{
11025+
"command": "gitlens.graph.explainWip",
11026+
"when": "false"
11027+
},
1101111028
{
1101211029
"command": "gitlens.graph.fetch",
1101311030
"when": "false"
@@ -17958,6 +17975,11 @@
1795817975
"when": "viewItem =~ /gitlens:worktree\\b/ && !listMultiSelection",
1795917976
"group": "3_gitlens@1"
1796017977
},
17978+
{
17979+
"command": "gitlens.ai.explainWip",
17980+
"when": "viewItem =~ /gitlens:worktree\\b/ && !listMultiSelection && !gitlens:readonly && !gitlens:untrusted && gitlens:gk:organization:ai:enabled",
17981+
"group": "3_gitlens_ai@1"
17982+
},
1796117983
{
1796217984
"command": "gitlens.views.deleteWorktree",
1796317985
"when": "viewItem =~ /gitlens:worktree\\b(?!.*?\\b\\+(active|default)\\b)/ && !listMultiSelection && !gitlens:readonly",
@@ -20393,6 +20415,11 @@
2039320415
"when": "webviewItem == gitlens:wip && !listMultiSelection && !gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders",
2039420416
"group": "1_gitlens_actions@4"
2039520417
},
20418+
{
20419+
"command": "gitlens.graph.explainWip",
20420+
"when": "webviewItem =~ /gitlens:wip\\b/ && !listMultiSelection && !gitlens:readonly && !gitlens:untrusted && gitlens:gk:organization:ai:enabled",
20421+
"group": "1_gitlens_actions_3@1"
20422+
},
2039620423
{
2039720424
"command": "gitlens.graph.hideRefGroup",
2039820425
"when": "webviewItemGroup =~ /gitlens:refGroup\\b(?!.*?\\b\\+current\\b)/",

src/commands.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import './commands/diffWithWorking';
2323
import './commands/externalDiff';
2424
import './commands/explainCommit';
2525
import './commands/explainStash';
26+
import './commands/explainWip';
2627
import './commands/generateChangelog';
2728
import './commands/generateCommitMessage';
2829
import './commands/ghpr/openOrCreateWorktree';

src/commands/explainWip.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import type { TextEditor, Uri } from 'vscode';
2+
import { ProgressLocation } from 'vscode';
3+
import type { Container } from '../container';
4+
import { GitUri } from '../git/gitUri';
5+
import { uncommitted, uncommittedStaged } from '../git/models/revision';
6+
import { showGenericErrorMessage } from '../messages';
7+
import type { AIExplainSource } from '../plus/ai/aiProviderService';
8+
import { getBestRepositoryOrShowPicker } from '../quickpicks/repositoryPicker';
9+
import { command } from '../system/-webview/command';
10+
import { showMarkdownPreview } from '../system/-webview/markdown';
11+
import { Logger } from '../system/logger';
12+
import { GlCommandBase } from './commandBase';
13+
import { getCommandUri } from './commandBase.utils';
14+
import type { CommandContext } from './commandContext';
15+
import { isCommandContextViewNodeHasRepoPath, isCommandContextViewNodeHasRepository } from './commandContext.utils';
16+
17+
export interface ExplainWipCommandArgs {
18+
repoPath?: string | Uri;
19+
staged?: boolean;
20+
source?: AIExplainSource;
21+
}
22+
23+
@command()
24+
export class ExplainWipCommand extends GlCommandBase {
25+
constructor(private readonly container: Container) {
26+
super('gitlens.ai.explainWip');
27+
}
28+
29+
protected override preExecute(context: CommandContext, args?: ExplainWipCommandArgs): Promise<void> {
30+
if (isCommandContextViewNodeHasRepository(context)) {
31+
args = { ...args };
32+
args.repoPath = context.node.repo.path;
33+
args.source = args.source ?? { source: 'view', type: 'wip' };
34+
} else if (isCommandContextViewNodeHasRepoPath(context)) {
35+
args = { ...args };
36+
args.repoPath = context.node.repoPath;
37+
args.source = args.source ?? { source: 'view', type: 'wip' };
38+
}
39+
40+
return this.execute(context.editor, context.uri, args);
41+
}
42+
43+
async execute(editor?: TextEditor, uri?: Uri, args?: ExplainWipCommandArgs): Promise<void> {
44+
args = { ...args };
45+
46+
let repository;
47+
if (args?.repoPath != null) {
48+
repository = this.container.git.getRepository(args.repoPath);
49+
}
50+
51+
if (repository == null) {
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 Working Changes',
58+
'Choose which repository to explain working changes from',
59+
);
60+
}
61+
62+
if (repository == null) return;
63+
64+
try {
65+
// Get the diff of working changes
66+
const diffService = repository.git.diff();
67+
if (diffService?.getDiff === undefined) {
68+
void showGenericErrorMessage('Unable to get diff service');
69+
return;
70+
}
71+
72+
// If args?.staged is undefined, should we get all changes (staged and unstaged)?
73+
let stagedLabel;
74+
let to;
75+
if (args?.staged === true) {
76+
stagedLabel = 'Staged';
77+
to = uncommittedStaged;
78+
} else {
79+
stagedLabel = 'Unstaged';
80+
to = uncommitted;
81+
}
82+
83+
const diff = await diffService.getDiff(to);
84+
if (!diff?.contents) {
85+
void showGenericErrorMessage('No working changes found to explain');
86+
return;
87+
}
88+
89+
// Call the AI service to explain the changes
90+
const result = await this.container.ai.explainChanges(
91+
{
92+
diff: diff.contents,
93+
message: `${stagedLabel} working changes`,
94+
},
95+
args.source ?? { source: 'commandPalette', type: 'wip' },
96+
{
97+
progress: { location: ProgressLocation.Notification, title: 'Explaining working changes...' },
98+
},
99+
);
100+
101+
// Display the result
102+
let content = `# Working Changes Summary\n\n`;
103+
if (result != null) {
104+
content += `> Generated by ${result.model.name}\n\n## ${stagedLabel} Changes\n\n${result?.parsed.summary}\n\n${result?.parsed.body}`;
105+
} else {
106+
content += `> No changes found to explain.`;
107+
}
108+
void showMarkdownPreview(content);
109+
} catch (ex) {
110+
Logger.error(ex, 'ExplainWipCommand', 'execute');
111+
void showGenericErrorMessage('Unable to explain working changes');
112+
}
113+
}
114+
}

src/constants.commands.generated.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export type ContributedCommands =
7373
| 'gitlens.graph.deleteTag'
7474
| 'gitlens.graph.explainCommit'
7575
| 'gitlens.graph.explainStash'
76+
| 'gitlens.graph.explainWip'
7677
| 'gitlens.graph.fetch'
7778
| 'gitlens.graph.hideLocalBranch'
7879
| 'gitlens.graph.hideRefGroup'
@@ -615,6 +616,7 @@ export type ContributedPaletteCommands =
615616
| 'gitlens.addAuthors'
616617
| 'gitlens.ai.explainCommit'
617618
| 'gitlens.ai.explainStash'
619+
| 'gitlens.ai.explainWip'
618620
| 'gitlens.ai.generateChangelog'
619621
| 'gitlens.ai.generateCommitMessage'
620622
| 'gitlens.applyPatchFromClipboard'

src/webviews/plus/graph/graphWebview.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,7 @@ export class GraphWebviewProvider implements WebviewProvider<State, State, Graph
687687
this.host.registerWebviewCommand('gitlens.graph.ai.generateCommitMessage', this.generateCommitMessage),
688688
this.host.registerWebviewCommand('gitlens.graph.explainCommit', this.explainCommit),
689689
this.host.registerWebviewCommand('gitlens.graph.explainStash', this.explainStash),
690+
this.host.registerWebviewCommand('gitlens.graph.explainWip', this.explainWip),
690691

691692
this.host.registerWebviewCommand('gitlens.graph.compareSelectedCommits.multi', this.compareSelectedCommits),
692693
this.host.registerWebviewCommand('gitlens.graph.abortPausedOperation', this.abortPausedOperation),
@@ -3908,6 +3909,17 @@ export class GraphWebviewProvider implements WebviewProvider<State, State, Graph
39083909
});
39093910
}
39103911

3912+
@log()
3913+
private explainWip(item?: GraphItemContext) {
3914+
const ref = this.getGraphItemRef(item, 'revision');
3915+
if (ref == null) return Promise.resolve();
3916+
3917+
return executeCommand('gitlens.ai.explainWip', {
3918+
repoPath: ref.repoPath,
3919+
source: { source: 'graph', type: 'wip' },
3920+
});
3921+
}
3922+
39113923
@log()
39123924
private async openFiles(item?: GraphItemContext) {
39133925
const commit = await this.getCommitFromGraphItemRef(item);

0 commit comments

Comments
 (0)