Skip to content

Commit d92553e

Browse files
🔧 chore(scm): 优化版本控制系统检测和提交消息处理
- 【功能优化】改进提交消息生成后的处理流程 - 【新增功能】添加命令行SVN支持 - 【文案优化】更新中英文提示信息,使其更准确清晰 - 【SCM检测】增加目录结构和系统命令检测功能 - 【错误处理】完善写入失败时的降级策略 - 【国际化】新增多个本地化文案条目
1 parent d0f22e8 commit d92553e

File tree

5 files changed

+162
-47
lines changed

5 files changed

+162
-47
lines changed

i18n/en.json

+7-5
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
"scm.not.detected": "No supported version control system detected",
77
"no.changes": "No changes to commit",
88
"diff.too.long": "Changes are too long and exceed the model's maximum limit. Please reduce the number of selected files or content length.\nCurrent length: {0} characters\nMaximum limit: {1} characters",
9-
"commit.message.generated": "Commit message has been filled in {0} commit box (generated by {1} - {2})",
10-
"commit.message.write.failed": "Failed to write commit message: {0}",
11-
"commit.message.copied": "Commit message has been copied to clipboard",
9+
"commit.message.generated": "Commit message generated successfully (by {1} - {2})",
10+
"commit.message.write.failed": "Failed to write commit message to SCM input box: {0}",
11+
"commit.message.copied": "Commit message has been copied to clipboard. Please paste it into your version control system's commit input box",
1212
"commit.message.copy.failed": "Failed to copy commit message: {0}",
13-
"commit.message.manual.copy": "Commit message has been generated, please manually copy to commit box",
13+
"commit.message.manual.copy": "Commit message has been generated, please manually copy to commit box: \n {0}",
1414
"generate.commit.failed": "Failed to generate commit message: {0}",
1515
"get.models.failed": "Failed to get model list",
1616
"openai.config.required": "OpenAI API configuration is required to use this feature. Would you like to configure it now?",
@@ -59,6 +59,7 @@
5959
"openai.models.error": "Failed to get OpenAI model list",
6060
"model.not.found": "Selected model not found",
6161
"model.list.empty": "Model list is empty",
62+
"model.list.partial.failed": "Failed to fetch models from some providers: {0}",
6263
"no.commit.message.generated": "No commit message generated",
6364
"input.truncated": "Input content exceeds maximum character limit and has been truncated, this may affect the quality of generated results",
6465
"extension.activation.failed": "Extension activation failed: {0}",
@@ -113,5 +114,6 @@
113114
"codeReview.report.findings": "Detailed Findings",
114115
"codeReview.issue.label": "Issue:",
115116
"codeReview.suggestion.label": "Suggestion:",
116-
"codeReview.documentation.label": "Documentation"
117+
"codeReview.documentation.label": "Documentation",
118+
"cli.commit.input.not.supported": "Unable to access the commit message input box instance"
117119
}

i18n/zh-cn.json

+7-5
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
"scm.not.detected": "未检测到支持的版本控制系统",
77
"no.changes": "没有可提交的更改",
88
"diff.too.long": "变更内容过长,超出模型最大限制。请减少选中的文件数量或内容长度。\n当前长度: {0} 字符\n最大限制: {1} 字符",
9-
"commit.message.generated": "已将提交信息填入 {0} 提交框 (生成自 {1} - {2})",
10-
"commit.message.write.failed": "写入提交信息失败: {0}",
11-
"commit.message.copied": "提交信息已复制到剪贴板",
9+
"commit.message.generated": "提交信息生成成功 (生成自 {1} - {2})",
10+
"commit.message.write.failed": "写入提交信息到版本控制输入框失败: {0}",
11+
"commit.message.copied": "提交信息已复制到剪贴板,请粘贴到版本控制系统的提交输入框中",
1212
"commit.message.copy.failed": "复制提交信息失败: {0}",
13-
"commit.message.manual.copy": "提交信息已生成,请手动复制到提交框",
13+
"commit.message.manual.copy": "提交信息已生成,请手动复制到提交框: \n {0}",
1414
"generate.commit.failed": "生成提交信息失败: {0}",
1515
"get.models.failed": "获取模型列表失败",
1616
"openai.config.required": "需要配置 OpenAI API 信息才能使用该功能,是否现在配置?",
@@ -99,6 +99,7 @@
9999
"review.results.title": "代码评审结果",
100100
"codeReview.generation.failed": "代码评审生成失败: {0}",
101101
"model.list.empty": "模型列表为空",
102+
"model.list.partial.failed": "部分 AI 提供商模型列表获取失败: {0}",
102103
"no.changes.selected": "未选择任何待评审的文件",
103104
"please.select.valid.files": "选择的文件中包含无效项,请重新选择要评审的文件",
104105
"no.changes.found": "未找到需要评审的变更",
@@ -113,5 +114,6 @@
113114
"codeReview.report.findings": "详细问题",
114115
"codeReview.issue.label": "问题:",
115116
"codeReview.suggestion.label": "建议:",
116-
"codeReview.documentation.label": "相关文档"
117+
"codeReview.documentation.label": "相关文档",
118+
"cli.commit.input.not.supported": "无法获取提交信息输入框实例"
117119
}

src/commands/GenerateCommitCommand.ts

+13-16
Original file line numberDiff line numberDiff line change
@@ -220,36 +220,33 @@ export class GenerateCommitCommand extends BaseCommand {
220220

221221
// 尝试设置提交信息
222222
if (response?.content) {
223+
NotificationHandler.info(
224+
locManager.format(
225+
"commit.message.generated",
226+
scmProvider.type.toUpperCase(),
227+
provider,
228+
model
229+
)
230+
);
223231
try {
224232
await scmProvider.setCommitInput(response.content);
225-
NotificationHandler.info(
226-
locManager.format(
227-
"commit.message.generated",
228-
scmProvider.type.toUpperCase(),
229-
provider,
230-
model
231-
)
232-
);
233233
} catch (error) {
234-
// 处理写入失败的情况
234+
// 写入失败,尝试复制到剪贴板
235235
if (error instanceof Error) {
236-
NotificationHandler.error(
237-
locManager.format("commit.message.write.failed", error.message)
238-
);
239-
240-
// 尝试复制到剪贴板
241236
try {
242237
await vscode.env.clipboard.writeText(response.content);
238+
NotificationHandler.error(
239+
locManager.format("commit.message.write.failed", error.message)
240+
);
243241
NotificationHandler.info(
244242
locManager.getMessage("commit.message.copied")
245243
);
246244
} catch (error) {
247-
// 处理复制失败的情况
245+
// 复制也失败了,显示消息内容
248246
if (error instanceof Error) {
249247
NotificationHandler.error(
250248
locManager.format("commit.message.copy.failed", error.message)
251249
);
252-
// 提示手动复制
253250
vscode.window.showInformationMessage(
254251
locManager.getMessage("commit.message.manual.copy"),
255252
response.content

src/scm/CliSvnProvider.ts

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { exec } from "child_process";
2+
import { promisify } from "util";
3+
import { ISCMProvider } from "./SCMProvider";
4+
import { LocalizationManager } from "../utils/LocalizationManager";
5+
6+
const execAsync = promisify(exec);
7+
8+
export class CliSvnProvider implements ISCMProvider {
9+
type: "svn" = "svn";
10+
private workspaceRoot: string;
11+
12+
constructor(workspaceRoot: string) {
13+
this.workspaceRoot = workspaceRoot;
14+
}
15+
16+
async isAvailable(): Promise<boolean> {
17+
try {
18+
await execAsync("svn --version");
19+
return true;
20+
} catch {
21+
return false;
22+
}
23+
}
24+
25+
async getDiff(files?: string[]): Promise<string | undefined> {
26+
try {
27+
const filePaths = files?.join(" ") || ".";
28+
const { stdout } = await execAsync(`svn diff ${filePaths}`, {
29+
cwd: this.workspaceRoot,
30+
});
31+
return stdout;
32+
} catch (error) {
33+
console.error("Failed to get SVN diff:", error);
34+
return undefined;
35+
}
36+
}
37+
38+
async commit(message: string, files?: string[]): Promise<void> {
39+
const filePaths = files?.join(" ") || ".";
40+
await execAsync(`svn commit -m "${message}" ${filePaths}`, {
41+
cwd: this.workspaceRoot,
42+
});
43+
}
44+
45+
// 由于是命令行方式,这两个方法可能用不到,但需要实现接口
46+
async setCommitInput(message: string): Promise<void> {
47+
throw new Error(
48+
LocalizationManager.getInstance().getMessage(
49+
"cli.commit.input.not.supported"
50+
)
51+
);
52+
}
53+
54+
async getCommitInput(): Promise<string> {
55+
return "";
56+
}
57+
}

src/scm/SCMProvider.ts

+78-21
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import * as vscode from "vscode";
2+
import * as fs from "fs";
3+
import * as path from "path";
4+
import { exec } from "child_process";
25
import { GitProvider } from "./GitProvider";
36
import { SvnProvider } from "./SvnProvider";
47
import { LocalizationManager } from "../utils/LocalizationManager";
8+
import { CliSvnProvider } from "./CliSvnProvider";
59

610
/**
711
* 源代码管理提供者接口
@@ -35,6 +39,42 @@ export class SCMFactory {
3539
/** 当前激活的SCM提供者实例 */
3640
private static currentProvider: ISCMProvider | undefined;
3741

42+
/**
43+
* 通过项目目录检测SCM类型
44+
* @param workspaceRoot 工作区根目录
45+
* @returns {"git" | "svn" | undefined} SCM类型
46+
*/
47+
private static detectSCMFromDir(workspaceRoot: string): "git" | "svn" | undefined {
48+
try {
49+
const gitPath = path.join(workspaceRoot, ".git");
50+
const svnPath = path.join(workspaceRoot, ".svn");
51+
52+
if (fs.existsSync(gitPath)) {
53+
return "git";
54+
}
55+
if (fs.existsSync(svnPath)) {
56+
return "svn";
57+
}
58+
return undefined;
59+
} catch (error) {
60+
console.error("Failed to detect SCM from directory:", error);
61+
return undefined;
62+
}
63+
}
64+
65+
/**
66+
* 检测系统是否安装了指定的SCM命令
67+
* @param cmd 要检测的命令
68+
* @returns {Promise<boolean>} 命令是否可用
69+
*/
70+
private static async checkSCMCommand(cmd: string): Promise<boolean> {
71+
return new Promise((resolve) => {
72+
exec(`${cmd} --version`, (error) => {
73+
resolve(!error);
74+
});
75+
});
76+
}
77+
3878
/**
3979
* 检测并创建可用的SCM提供者
4080
* @returns {Promise<ISCMProvider | undefined>} 返回可用的SCM提供者实例,如果没有可用的提供者则返回undefined
@@ -45,31 +85,48 @@ export class SCMFactory {
4585
return this.currentProvider;
4686
}
4787

88+
// 获取工作区根目录
89+
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
90+
if (!workspaceRoot) {
91+
return undefined;
92+
}
93+
94+
// 首先通过目录检测
95+
const scmType = this.detectSCMFromDir(workspaceRoot);
96+
4897
const gitExtension = vscode.extensions.getExtension("vscode.git");
49-
const svnExtension = vscode.extensions.getExtension(
50-
"littleCareless.svn-scm-ai"
51-
);
98+
const svnExtension = vscode.extensions.getExtension("littleCareless.svn-scm-ai");
5299

53-
// if (!gitExtension && !svnExtension) {
54-
// throw new Error(
55-
// LocalizationManager.getInstance().getMessage("scm.no.provider")
56-
// );
57-
// }
58-
59-
const git = gitExtension?.exports
60-
? new GitProvider(gitExtension.exports)
61-
: undefined;
62-
if (git && (await git.isAvailable())) {
63-
this.currentProvider = git;
64-
return git;
100+
// 如果检测到Git
101+
if (scmType === "git") {
102+
const git = gitExtension?.exports
103+
? new GitProvider(gitExtension.exports)
104+
: undefined;
105+
if (git && (await git.isAvailable())) {
106+
this.currentProvider = git;
107+
return git;
108+
}
65109
}
66110

67-
const svn = svnExtension?.exports
68-
? new SvnProvider(svnExtension.exports)
69-
: undefined;
70-
if (svn && (await svn.isAvailable())) {
71-
this.currentProvider = svn;
72-
return svn;
111+
// 如果检测到SVN
112+
if (scmType === "svn") {
113+
// 先尝试使用SVN插件
114+
const svn = svnExtension?.exports
115+
? new SvnProvider(svnExtension.exports)
116+
: undefined;
117+
if (svn && (await svn.isAvailable())) {
118+
this.currentProvider = svn;
119+
return svn;
120+
}
121+
122+
// 如果没有插件但系统有SVN命令,使用命令行方式
123+
if (await this.checkSCMCommand("svn")) {
124+
const cliSvn = new CliSvnProvider(workspaceRoot);
125+
if (await cliSvn.isAvailable()) {
126+
this.currentProvider = cliSvn;
127+
return cliSvn;
128+
}
129+
}
73130
}
74131

75132
return undefined;

0 commit comments

Comments
 (0)