Skip to content

Commit 1333c5a

Browse files
committed
Adds virtual document for explain summary (wip)
1 parent 0bdde89 commit 1333c5a

File tree

10 files changed

+154
-14
lines changed

10 files changed

+154
-14
lines changed

package.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23562,6 +23562,13 @@
2356223562
"tildify": true,
2356323563
"workspaceSuffix": "GitLens"
2356423564
}
23565+
},
23566+
{
23567+
"scheme": "gitlens-markdown",
23568+
"authority": "*",
23569+
"formatting": {
23570+
"label": "${query.label}"
23571+
}
2356523572
}
2356623573
],
2356723574
"viewsContainers": {

src/commands/explainBranch.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export class ExplainBranchCommand extends GlCommandBase {
139139

140140
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}`;
141141

142-
void showMarkdownPreview(content);
142+
showMarkdownPreview(content);
143143
} catch (ex) {
144144
Logger.error(ex, 'ExplainBranchCommand', 'execute');
145145
void showGenericErrorMessage('Unable to explain branch');

src/commands/explainCommit.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,29 @@ export class ExplainCommitCommand extends GlCommandBase {
108108
// Display the result
109109
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}`;
110110

111-
void showMarkdownPreview(content);
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+
},
129+
},
130+
},
131+
);
132+
133+
showMarkdownPreview(documentUri);
112134
} catch (ex) {
113135
Logger.error(ex, 'ExplainCommitCommand', 'execute');
114136
void showGenericErrorMessage('Unable to explain commit');

src/commands/explainStash.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ export class ExplainStashCommand extends GlCommandBase {
101101
commit.message || commit.ref
102102
}\n\n${result.parsed.summary}\n\n${result.parsed.body}`;
103103

104-
void showMarkdownPreview(content);
104+
// showMarkdownPreview(this.container, content, { filename: `stash-${commit.ref}` });
105+
showMarkdownPreview(content);
105106
} catch (ex) {
106107
Logger.error(ex, 'ExplainStashCommand', 'execute');
107108
void showGenericErrorMessage('Unable to explain stash');

src/commands/explainWip.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ export class ExplainWipCommand extends GlCommandBase {
132132
const title = `${capitalize(label)} Changes Summary${worktreeDisplayName}`;
133133
const content = `# ${title}\n\n> Generated by ${result.model.name}\n\n## ${label} Changes\n\n${result.parsed.summary}\n\n${result.parsed.body}`;
134134

135-
void showMarkdownPreview(content);
135+
showMarkdownPreview(content);
136136
} catch (ex) {
137137
Logger.error(ex, 'ExplainWipCommand', 'execute');
138138
void showGenericErrorMessage('Unable to explain working changes');

src/commands/generateRebase.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ export async function generateRebase(
191191
}
192192
}
193193

194-
void showMarkdownPreview(markdownContent);
194+
showMarkdownPreview(markdownContent);
195195
} catch (ex) {
196196
Logger.error(ex, 'GenerateRebaseCommand', 'execute');
197197
void showGenericErrorMessage('Unable to parse rebase result');

src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ export const enum Schemes {
137137
Git = 'git',
138138
GitHub = 'github',
139139
GitLens = 'gitlens',
140+
GitLensMarkdown = 'gitlens-markdown',
140141
PRs = 'pr',
141142
Remote = 'vscode-remote',
142143
Vsls = 'vsls',

src/container.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import type { DateStyle, FileAnnotationType, Mode } from './config';
1919
import { fromOutputLevel } from './config';
2020
import { extensionPrefix } from './constants';
2121
import type { GlCommands } from './constants.commands';
22+
import { MarkdownContentProvider } from './documents/markdown';
2223
import { EventBus } from './eventBus';
2324
import { GitFileSystemProvider } from './git/fsProvider';
2425
import { GitProviderService } from './git/gitProviderService';
@@ -219,6 +220,7 @@ export class Container {
219220
this._disposables.push((this._vsls = new VslsController(this)));
220221
this._disposables.push((this._eventBus = new EventBus()));
221222
this._disposables.push((this._launchpadProvider = new LaunchpadProvider(this)));
223+
this._disposables.push((this._markdownProvider = new MarkdownContentProvider()));
222224

223225
this._disposables.push((this._fileAnnotationController = new FileAnnotationController(this)));
224226
this._disposables.push((this._lineAnnotationController = new LineAnnotationController(this)));
@@ -470,6 +472,11 @@ export class Container {
470472
return this._launchpadProvider;
471473
}
472474

475+
private readonly _markdownProvider: MarkdownContentProvider;
476+
get markdown(): MarkdownContentProvider {
477+
return this._markdownProvider;
478+
}
479+
473480
private readonly _git: GitProviderService;
474481
get git(): GitProviderService {
475482
return this._git;

src/documents/markdown.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import type { Disposable, Event, TextDocumentContentProvider } from 'vscode';
2+
import { EventEmitter, Uri, workspace } from 'vscode';
3+
import { Schemes } from '../constants';
4+
import type { GlCommands } from '../constants.commands';
5+
import { decodeGitLensRevisionUriAuthority, encodeGitLensRevisionUriAuthority } from '../git/gitUri.authority';
6+
import { createMarkdownCommandLink } from '../system/commands';
7+
8+
// gitlens-markdown:{explain}/{entity}/{entityID}/{model}[{/friendlyName}].md
9+
10+
export interface MarkdownContentMetadata {
11+
header: {
12+
title: string;
13+
subtitle?: string;
14+
aiModel?: string;
15+
};
16+
command?: {
17+
label: string;
18+
name: GlCommands;
19+
args?: Record<string, unknown>;
20+
};
21+
}
22+
23+
export class MarkdownContentProvider implements TextDocumentContentProvider {
24+
private contents = new Map<string, string>();
25+
private registration: Disposable;
26+
27+
private _onDidChange = new EventEmitter<Uri>();
28+
get onDidChange(): Event<Uri> {
29+
return this._onDidChange.event;
30+
}
31+
32+
constructor() {
33+
this.registration = workspace.registerTextDocumentContentProvider(Schemes.GitLensMarkdown, this);
34+
35+
workspace.onDidCloseTextDocument(document => {
36+
if (document.uri.scheme === Schemes.GitLensMarkdown) {
37+
this.contents.delete(document.uri.toString());
38+
}
39+
});
40+
}
41+
42+
provideTextDocumentContent(uri: Uri): string | undefined {
43+
let contents = this.contents.get(uri.toString());
44+
if (contents != null) return contents;
45+
46+
contents = getContentFromMarkdownUri(uri);
47+
if (contents != null) return contents;
48+
49+
return `# ${uri.path}\n\nNo content available.`;
50+
}
51+
52+
openDocument(content: string, path: string, label: string, metadata?: Record<string, unknown>): Uri {
53+
const uri = Uri.from({
54+
scheme: Schemes.GitLensMarkdown,
55+
authority: metadata ? encodeGitLensRevisionUriAuthority(metadata) : undefined,
56+
path: `${path}.md`,
57+
query: JSON.stringify({ label: label }),
58+
});
59+
this.contents.set(uri.toString(), content);
60+
return uri;
61+
}
62+
63+
updateDocument(uri: Uri, content: string): void {
64+
this.contents.set(uri.toString(), content);
65+
this._onDidChange.fire(uri);
66+
}
67+
68+
closeDocument(uri: Uri): void {
69+
this.contents.delete(uri.toString());
70+
}
71+
72+
dispose(): void {
73+
this.contents.clear();
74+
this.registration.dispose();
75+
}
76+
}
77+
78+
function getContentFromMarkdownUri(uri: Uri): string | undefined {
79+
if (!uri.path.startsWith('/explain')) return undefined;
80+
81+
const authority = uri.authority;
82+
if (authority == null || authority.length === 0) return undefined;
83+
84+
const metadata = decodeGitLensRevisionUriAuthority<MarkdownContentMetadata>(authority);
85+
86+
if (metadata.header == null) return undefined;
87+
88+
let headerContent = `# ${metadata.header.title}`;
89+
if (metadata.header.aiModel != null) {
90+
headerContent += `\n\n> Generated by ${metadata.header.aiModel}`;
91+
}
92+
if (metadata.header.subtitle != null) {
93+
headerContent += `\n\n## ${metadata.header.subtitle}`;
94+
}
95+
96+
if (metadata.command == null) return `${headerContent}\n\nNo content available.`;
97+
98+
const commandContent = `\n\n\n\n[${metadata.command.label}](${createMarkdownCommandLink(
99+
metadata.command.name,
100+
metadata.command.args,
101+
)})`;
102+
103+
return `${headerContent}\n\n${commandContent}`;
104+
}

src/system/-webview/markdown.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,22 @@
11
import type { TextDocumentShowOptions, Uri } from 'vscode';
2-
import { workspace } from 'vscode';
2+
import type { Container } from '../../container';
33
import { Logger } from '../logger';
44
import { executeCoreCommand } from './command';
55

6-
export async function showMarkdownPreview(
6+
export function showMarkdownPreview(
77
uriOrContent: Uri | string,
88
options: TextDocumentShowOptions = {
99
preview: false,
1010
},
11-
): Promise<void> {
11+
): void {
1212
try {
13-
if (typeof uriOrContent === 'string') {
14-
const document = await workspace.openTextDocument({ language: 'markdown', content: uriOrContent });
15-
16-
uriOrContent = document.uri;
17-
}
18-
1913
void executeCoreCommand('vscode.openWith', uriOrContent, 'vscode.markdown.preview.editor', options);
2014
} catch (ex) {
2115
Logger.error(ex, 'showMarkdownPreview');
2216
debugger;
2317
}
2418
}
19+
20+
export function createGLMarkdownDocument(container: Container, content: string, path: string, label: string): Uri {
21+
return container.markdown.openDocument(content, path, label);
22+
}

0 commit comments

Comments
 (0)