Compare commits

...
Author SHA1 Message Date
abeatrix 6ffba23d3d refactor: convert GitCommitGenerator from module to class
- Convert module pattern to class-based implementation
- Move abort controller to private instance variable
- Add proper method documentation with JSDoc
- Improve encapsulation and code organization
2025-09-02 14:24:11 -07:00
2 changed files with 104 additions and 85 deletions
+4 -2
View File
@@ -481,12 +481,14 @@ export async function activate(context: vscode.ExtensionContext) {
) )
// Register the generateGitCommitMessage command handler // Register the generateGitCommitMessage command handler
const commitGenerator = new GitCommitGenerator()
context.subscriptions.push( context.subscriptions.push(
commitGenerator,
vscode.commands.registerCommand("cline.generateGitCommitMessage", async (scm) => { vscode.commands.registerCommand("cline.generateGitCommitMessage", async (scm) => {
await GitCommitGenerator?.generate?.(context, scm) await commitGenerator.generate(context, scm)
}), }),
vscode.commands.registerCommand("cline.abortGitCommitMessage", () => { vscode.commands.registerCommand("cline.abortGitCommitMessage", () => {
GitCommitGenerator?.abort?.() commitGenerator.abort()
}), }),
) )
+100 -83
View File
@@ -7,60 +7,68 @@ import { getWorkingState } from "@/utils/git"
import { getCwd } from "@/utils/path" import { getCwd } from "@/utils/path"
/** /**
* Git commit message generator module * Git commit message generator class
*/ */
export const GitCommitGenerator = { export class GitCommitGenerator {
generate, private commitGenerationAbortController: AbortController | undefined
abort,
}
let commitGenerationAbortController: AbortController | undefined /**
* Generates a commit message based on the current git diff
* @param context VSCode extension context
* @param scm Source control instance
*/
async generate(context: vscode.ExtensionContext, scm?: vscode.SourceControl): Promise<void> {
const cwd = await getCwd()
if (!context || !cwd) {
HostProvider.window.showMessage({
type: ShowMessageType.ERROR,
message: "No workspace folder open",
})
return
}
async function generate(context: vscode.ExtensionContext, scm?: vscode.SourceControl) { const gitDiff = await getWorkingState(cwd)
const cwd = await getCwd() if (gitDiff === "No changes in working directory") {
if (!context || !cwd) { HostProvider.window.showMessage({
HostProvider.window.showMessage({ type: ShowMessageType.INFORMATION,
type: ShowMessageType.ERROR, message: "No changes in workspace for commit message",
message: "No workspace folder open", })
}) return
return }
const inputBox = scm?.inputBox
if (!inputBox) {
HostProvider.window.showMessage({
type: ShowMessageType.ERROR,
message: "Git extension not found or no repositories available",
})
return
}
await vscode.window.withProgress(
{
location: vscode.ProgressLocation.SourceControl,
title: "Generating commit message...",
cancellable: true,
},
() => this.performCommitGeneration(context, gitDiff, inputBox),
)
} }
const gitDiff = await getWorkingState(cwd) /**
if (gitDiff === "No changes in working directory") { * Performs the actual commit message generation
HostProvider.window.showMessage({ * @param context VSCode extension context
type: ShowMessageType.INFORMATION, * @param gitDiff The git diff to generate a message for
message: "No changes in workspace for commit message", * @param inputBox The SCM input box to populate
}) */
return private async performCommitGeneration(context: vscode.ExtensionContext, gitDiff: string, inputBox: any): Promise<void> {
} try {
vscode.commands.executeCommand("setContext", "cline.isGeneratingCommit", true)
const inputBox = scm?.inputBox const truncatedDiff =
if (!inputBox) { gitDiff.length > 5000 ? gitDiff.substring(0, 5000) + "\n\n[Diff truncated due to size]" : gitDiff
HostProvider.window.showMessage({
type: ShowMessageType.ERROR,
message: "Git extension not found or no repositories available",
})
return
}
await vscode.window.withProgress( const prompt = `Based on the following git diff, generate a concise and descriptive commit message:
{
location: vscode.ProgressLocation.SourceControl,
title: "Generating commit message...",
cancellable: true,
},
() => performCommitGeneration(context, gitDiff, inputBox),
)
}
async function performCommitGeneration(context: vscode.ExtensionContext, gitDiff: string, inputBox: any) {
try {
vscode.commands.executeCommand("setContext", "cline.isGeneratingCommit", true)
const truncatedDiff = gitDiff.length > 5000 ? gitDiff.substring(0, 5000) + "\n\n[Diff truncated due to size]" : gitDiff
const prompt = `Based on the following git diff, generate a concise and descriptive commit message:
${truncatedDiff} ${truncatedDiff}
The commit message should: The commit message should:
1. Start with a short summary (50-72 characters) 1. Start with a short summary (50-72 characters)
@@ -69,53 +77,62 @@ The commit message should:
4. Be clear and descriptive 4. Be clear and descriptive
Commit message:` Commit message:`
// Get the current API configuration // Get the current API configuration
const stateManager = new StateManager(context) const stateManager = new StateManager(context)
await stateManager.initialize() await stateManager.initialize()
const apiConfiguration = stateManager.getApiConfiguration() const apiConfiguration = stateManager.getApiConfiguration()
// Set to use Act mode for now by default // Set to use Act mode for now by default
// TODO: A new mode for commit generation // TODO: A new mode for commit generation
const currentMode = "act" const currentMode = "act"
// Build the API handler // Build the API handler
const apiHandler = buildApiHandler(apiConfiguration, currentMode) const apiHandler = buildApiHandler(apiConfiguration, currentMode)
// Create a system prompt // Create a system prompt
const systemPrompt = const systemPrompt =
"You are a helpful assistant that generates concise and descriptive git commit messages based on git diffs." "You are a helpful assistant that generates concise and descriptive git commit messages based on git diffs."
// Create a message for the API // Create a message for the API
const messages = [{ role: "user" as const, content: prompt }] const messages = [{ role: "user" as const, content: prompt }]
commitGenerationAbortController = new AbortController() this.commitGenerationAbortController = new AbortController()
const stream = apiHandler.createMessage(systemPrompt, messages) const stream = apiHandler.createMessage(systemPrompt, messages)
let response = "" let response = ""
for await (const chunk of stream) { for await (const chunk of stream) {
commitGenerationAbortController.signal.throwIfAborted() this.commitGenerationAbortController.signal.throwIfAborted()
if (chunk.type === "text") { if (chunk.type === "text") {
response += chunk.text response += chunk.text
inputBox.value = extractCommitMessage(response) inputBox.value = extractCommitMessage(response)
}
} }
}
if (!inputBox.value) { if (!inputBox.value) {
throw new Error("empty API response") throw new Error("empty API response")
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
HostProvider.window.showMessage({
type: ShowMessageType.ERROR,
message: `Failed to generate commit message: ${errorMessage}`,
})
} finally {
vscode.commands.executeCommand("setContext", "cline.isGeneratingCommit", false)
} }
} catch (error) { }
const errorMessage = error instanceof Error ? error.message : String(error)
HostProvider.window.showMessage({ /**
type: ShowMessageType.ERROR, * Aborts the current commit message generation
message: `Failed to generate commit message: ${errorMessage}`, */
}) abort(): void {
} finally { this.commitGenerationAbortController?.abort()
vscode.commands.executeCommand("setContext", "cline.isGeneratingCommit", false) vscode.commands.executeCommand("setContext", "cline.isGeneratingCommit", false)
} }
}
function abort() { dispose(): void {
commitGenerationAbortController?.abort() this.commitGenerationAbortController?.abort()
vscode.commands.executeCommand("setContext", "cline.isGeneratingCommit", false) vscode.commands.executeCommand("setContext", "cline.isGeneratingCommit", false)
}
} }
/** /**