Repository navigation
Expand file tree
/
Copy pathprDescription.ts
More file actions
165 lines (138 loc) · 4.7 KB
/
Copy pathprDescription.ts
File metadata and controls
165 lines (138 loc) · 4.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import * as vscode from 'vscode';
import { getRepository, getDiffAgainstBranch, getCommitsSinceBranch, getCurrentBranch, getLocalBranches } from './git';
import { callProvider, getProviderName } from './providers';
export async function generatePRDescription() {
const repo = getRepository();
if (!repo) {
vscode.window.showWarningMessage('Kein Git-Repository gefunden.');
return;
}
const config = vscode.workspace.getConfiguration('commitGen');
const language = config.get<string>('language', 'en');
const maxLines = config.get<number>('maxDiffLines', 500);
const defaultTarget = config.get<string>('pr.targetBranch', 'main');
const cwd = repo.rootUri.fsPath;
// Check: not on target branch
let currentBranch: string;
try {
currentBranch = await getCurrentBranch(cwd);
} catch {
vscode.window.showErrorMessage('Could not determine current branch.');
return;
}
// Let user pick target branch
let branches: string[];
try {
branches = await getLocalBranches(cwd);
} catch {
branches = [defaultTarget];
}
// Filter out current branch and sort: default first, then alphabetical
const otherBranches = branches
.filter((b) => b !== currentBranch)
.sort((a, b) => {
if (a === defaultTarget) return -1;
if (b === defaultTarget) return 1;
return a.localeCompare(b);
});
if (otherBranches.length === 0) {
vscode.window.showWarningMessage('No other branches found to compare against.');
return;
}
const targetBranch = await vscode.window.showQuickPick(otherBranches, {
placeHolder: `Select target branch (current: ${currentBranch})`,
title: 'PR Target Branch',
});
if (!targetBranch) {
return; // user cancelled
}
// Get commits and diff against target branch
let commits: string;
let diff: string;
try {
[commits, diff] = await Promise.all([
getCommitsSinceBranch(cwd, targetBranch),
getDiffAgainstBranch(cwd, targetBranch),
]);
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : String(err);
vscode.window.showErrorMessage(`Git Fehler: ${msg}`);
return;
}
if (!commits.trim() && !diff.trim()) {
vscode.window.showInformationMessage(`No changes found compared to "${targetBranch}".`);
return;
}
// Truncate diff if needed
const lines = diff.split('\n');
const diffText = lines.length > maxLines
? lines.slice(0, maxLines).join('\n') + `\n\n[... ${lines.length - maxLines} lines truncated]`
: diff;
const prompt = buildPRPrompt(currentBranch, targetBranch, commits, diffText, language);
await vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
title: 'Generating PR description...',
cancellable: false,
},
async () => {
try {
const description = await callProvider(prompt);
await showPRDescription(description.trim(), currentBranch, targetBranch);
} catch (err: unknown) {
const errorMsg = err instanceof Error ? err.message : String(err);
vscode.window.showErrorMessage(`${getProviderName()} Fehler: ${errorMsg}`);
}
}
);
}
async function showPRDescription(description: string, branch: string, target: string) {
const doc = await vscode.workspace.openTextDocument({
content: description,
language: 'markdown',
});
await vscode.window.showTextDocument(doc, { preview: true });
const choice = await vscode.window.showInformationMessage(
`PR description generated (${branch} → ${target})`,
'Copy to Clipboard'
);
if (choice === 'Copy to Clipboard') {
await vscode.env.clipboard.writeText(description);
vscode.window.showInformationMessage('PR description copied to clipboard.');
}
}
function buildPRPrompt(
currentBranch: string,
targetBranch: string,
commits: string,
diff: string,
language: string
): string {
const lang = language === 'de'
? 'Schreibe die PR-Beschreibung auf Deutsch.'
: 'Write the PR description in English.';
return `You are a pull request description generator. Generate a structured PR description in Markdown format.
${lang}
Branch: ${currentBranch} → ${targetBranch}
Output format:
## Title
<A concise PR title, max 70 characters>
## Summary
<2-4 bullet points describing the key changes>
## Changes
<Detailed list of what was changed and why, grouped by topic>
## Test Plan
<Checklist of items to verify, as markdown checkboxes>
Rules:
- Be specific and reference actual file/component names from the diff
- Focus on the "why" not just the "what"
- Keep the title short and descriptive
- No code fences around the entire output
- Output ONLY the PR description, nothing else
--- COMMITS ---
${commits}
--- END COMMITS ---
--- DIFF ---
${diff}
--- END DIFF ---`;
}