实战出真知!本阶段将通过5个完整的综合项目,带你体验从0到1开发VS Code扩展的全过程。从需求分析、架构设计到编码实现、测试发布,每个项目都会涉及不同的技术栈和应用场景,帮你成为VS Code扩展开发的全栈专家。


🎯 学习目标

完成本阶段学习后,你将能够:

  • 🏗️ 项目架构设计:独立完成扩展的技术选型和架构设计
  • 💻 全栈开发能力:前端UI + 后端服务 + 数据存储的完整技术栈
  • 🤖 AI工具开发:集成大模型API,开发智能化功能
  • 📊 数据可视化:构建复杂的数据展示和交互界面
  • 🚀 DevOps实践:自动化测试、持续集成、发布流程
  • 📈 产品思维:用户体验设计、功能迭代、市场推广

预计学习时间:3-4周(每天2-3小时)


🏗️ 项目概览与技术栈

项目选择策略

我们将开发5个不同类型的扩展,覆盖主流应用场景:

项目类型主要技术栈难度开发周期
AI Code AssistantAI工具TypeScript + OpenAI API + WebView⭐⭐⭐⭐5-7天
API Testing Tool开发工具React + Node.js + SQLite⭐⭐⭐4-5天
Data Visualizer数据工具Vue.js + D3.js + WebSocket⭐⭐⭐⭐6-8天
Project Manager效率工具TypeScript + IndexedDB + Tree View⭐⭐⭐4-6天
Code Quality Monitor质量工具LSP + Language Server + AST⭐⭐⭐⭐⭐7-10天

通用技术栈

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
// 基础技术栈
- TypeScript 5.0+ // 类型安全的JavaScript
- VS Code API 1.74+ // VS Code扩展API
- Node.js 18+ // 运行时环境

// 前端技术栈
- React 18 / Vue 3 // 现代前端框架
- WebView API // VS Code内嵌网页
- CSS3 + Tailwind // 样式方案

// 数据存储
- IndexedDB // 浏览器端数据库
- SQLite // 轻量级关系数据库
- File System API // 文件系统操作

// AI集成
- OpenAI API // GPT模型接口
- Local LLM // 本地大模型
- Prompt Engineering // 提示工程

// 工具链
- Webpack // 模块打包
- Jest + @vscode/test // 测试框架
- ESLint + Prettier // 代码质量
- GitHub Actions // CI/CD

🤖 项目一:AI Code Assistant(AI代码助手)

项目介绍

开发一个智能代码助手,集成多种AI能力,帮助开发者提高编码效率。

核心功能

  • 代码生成:根据自然语言描述生成代码
  • 代码解释:分析和解释复杂代码逻辑
  • 代码优化:提供性能和可读性改进建议
  • 文档生成:自动生成函数和类的文档
  • 单元测试生成:基于代码自动生成测试用例

1.1 需求分析与设计

用户故事

1
2
3
4
5
6
作为一个开发者,我希望能够:
1. 通过自然语言描述快速生成代码片段
2. 选中复杂代码后获得详细的解释说明
3. 获得代码优化和重构建议
4. 自动生成函数和API文档
5. 快速生成单元测试代码

架构设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
graph TB
A[VS Code Editor] --> B[Extension Host]
B --> C[AI Assistant Extension]
C --> D[Command Handler]
C --> E[WebView Provider]
C --> F[AI Service]

D --> G[Code Analysis]
D --> H[Selection Handler]

E --> I[React UI]
I --> J[Chat Interface]
I --> K[Code Preview]
I --> L[History Panel]

F --> M[OpenAI API]
F --> N[Local LLM]
F --> O[Prompt Manager]

G --> P[AST Parser]
G --> Q[Context Extractor]

1.2 项目实现

扩展清单配置

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
{
"name": "ai-code-assistant",
"displayName": "AI Code Assistant",
"description": "Intelligent code generation and analysis powered by AI",
"version": "1.0.0",
"engines": {
"vscode": "^1.74.0"
},
"categories": ["Other", "Machine Learning"],
"activationEvents": [
"onCommand:aiAssistant.generateCode",
"onCommand:aiAssistant.explainCode",
"onCommand:aiAssistant.optimizeCode",
"onLanguage:typescript",
"onLanguage:javascript",
"onLanguage:python"
],
"main": "./dist/extension.js",
"contributes": {
"commands": [
{
"command": "aiAssistant.generateCode",
"title": "Generate Code",
"category": "AI Assistant"
},
{
"command": "aiAssistant.explainCode",
"title": "Explain Code",
"category": "AI Assistant"
},
{
"command": "aiAssistant.optimizeCode",
"title": "Optimize Code",
"category": "AI Assistant"
},
{
"command": "aiAssistant.generateTests",
"title": "Generate Tests",
"category": "AI Assistant"
},
{
"command": "aiAssistant.generateDocs",
"title": "Generate Documentation",
"category": "AI Assistant"
},
{
"command": "aiAssistant.openChat",
"title": "Open AI Chat",
"category": "AI Assistant"
}
],
"menus": {
"editor/context": [
{
"command": "aiAssistant.explainCode",
"when": "editorHasSelection",
"group": "aiAssistant@1"
},
{
"command": "aiAssistant.optimizeCode",
"when": "editorHasSelection",
"group": "aiAssistant@2"
},
{
"command": "aiAssistant.generateTests",
"when": "editorHasSelection",
"group": "aiAssistant@3"
}
],
"view/title": [
{
"command": "aiAssistant.openChat",
"when": "view == aiAssistantView",
"group": "navigation"
}
]
},
"views": {
"aiAssistant": [
{
"id": "aiAssistantView",
"name": "AI Assistant",
"when": "aiAssistant.enabled"
}
]
},
"viewsContainers": {
"activitybar": [
{
"id": "aiAssistant",
"title": "AI Assistant",
"icon": "$(robot)"
}
]
},
"configuration": {
"title": "AI Assistant",
"properties": {
"aiAssistant.apiKey": {
"type": "string",
"description": "OpenAI API Key"
},
"aiAssistant.model": {
"type": "string",
"enum": ["gpt-4", "gpt-3.5-turbo", "claude-3", "local"],
"default": "gpt-3.5-turbo",
"description": "AI Model to use"
},
"aiAssistant.maxTokens": {
"type": "number",
"default": 2048,
"description": "Maximum tokens for AI responses"
}
}
}
}
}

核心扩展逻辑

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
// src/extension.ts
import * as vscode from 'vscode';
import { AIService } from './services/aiService';
import { CodeAnalyzer } from './services/codeAnalyzer';
import { ChatWebviewProvider } from './webview/chatWebviewProvider';
import { CommandHandler } from './commands/commandHandler';

export async function activate(context: vscode.ExtensionContext) {
console.log('AI Code Assistant is now active!');

// 初始化服务
const aiService = new AIService();
const codeAnalyzer = new CodeAnalyzer();
const chatProvider = new ChatWebviewProvider(context.extensionUri, aiService);
const commandHandler = new CommandHandler(aiService, codeAnalyzer);

// 注册WebView Provider
context.subscriptions.push(
vscode.window.registerWebviewViewProvider(
'aiAssistantView',
chatProvider
)
);

// 注册命令
const commands = [
vscode.commands.registerCommand('aiAssistant.generateCode',
commandHandler.generateCode.bind(commandHandler)
),
vscode.commands.registerCommand('aiAssistant.explainCode',
commandHandler.explainCode.bind(commandHandler)
),
vscode.commands.registerCommand('aiAssistant.optimizeCode',
commandHandler.optimizeCode.bind(commandHandler)
),
vscode.commands.registerCommand('aiAssistant.generateTests',
commandHandler.generateTests.bind(commandHandler)
),
vscode.commands.registerCommand('aiAssistant.generateDocs',
commandHandler.generateDocs.bind(commandHandler)
),
vscode.commands.registerCommand('aiAssistant.openChat',
() => chatProvider.show()
)
];

context.subscriptions.push(...commands);

// 设置上下文变量
vscode.commands.executeCommand('setContext', 'aiAssistant.enabled', true);

// 监听配置变化
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration(event => {
if (event.affectsConfiguration('aiAssistant')) {
aiService.updateConfiguration();
}
})
);
}

AI服务实现

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
166
167
168
169
170
// src/services/aiService.ts
import * as vscode from 'vscode';
import OpenAI from 'openai';

export interface AIRequest {
prompt: string;
context?: string;
language?: string;
temperature?: number;
}

export interface AIResponse {
content: string;
usage?: {
prompt_tokens: number;
completion_tokens: number;
total_tokens: number;
};
}

export class AIService {
private openai: OpenAI | null = null;
private config: vscode.WorkspaceConfiguration;

constructor() {
this.updateConfiguration();
}

updateConfiguration() {
this.config = vscode.workspace.getConfiguration('aiAssistant');
const apiKey = this.config.get<string>('apiKey');

if (apiKey) {
this.openai = new OpenAI({
apiKey: apiKey
});
}
}

async generateCode(request: AIRequest): Promise<AIResponse> {
if (!this.openai) {
throw new Error('AI service not configured. Please set your API key.');
}

const systemPrompt = `You are an expert programmer. Generate clean, efficient, and well-commented code based on the user's requirements.

Language: ${request.language || 'TypeScript'}
Context: ${request.context || 'No additional context'}

Please provide only the code without explanations unless specifically requested.`;

try {
const completion = await this.openai.chat.completions.create({
model: this.config.get<string>('model') || 'gpt-3.5-turbo',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: request.prompt }
],
max_tokens: this.config.get<number>('maxTokens') || 2048,
temperature: request.temperature || 0.3
});

const response = completion.choices[0]?.message?.content || '';

return {
content: response,
usage: completion.usage ? {
prompt_tokens: completion.usage.prompt_tokens,
completion_tokens: completion.usage.completion_tokens,
total_tokens: completion.usage.total_tokens
} : undefined
};
} catch (error) {
console.error('AI Service Error:', error);
throw new Error(`AI request failed: ${error.message}`);
}
}

async explainCode(code: string, language: string): Promise<AIResponse> {
const prompt = `Explain the following ${language} code in detail. Include:
1. What the code does
2. How it works
3. Any patterns or best practices used
4. Potential improvements or concerns

Code:
\`\`\`${language}
${code}
\`\`\``;

return this.generateCode({
prompt,
language,
temperature: 0.2
});
}

async optimizeCode(code: string, language: string): Promise<AIResponse> {
const prompt = `Analyze and optimize the following ${language} code. Provide:
1. Optimized version of the code
2. Explanation of improvements made
3. Performance benefits
4. Any trade-offs to consider

Original code:
\`\`\`${language}
${code}
\`\`\``;

return this.generateCode({
prompt,
language,
temperature: 0.1
});
}

async generateTests(code: string, language: string): Promise<AIResponse> {
const framework = this.getTestFramework(language);

const prompt = `Generate comprehensive unit tests for the following ${language} code using ${framework}. Include:
1. Test cases for normal operation
2. Edge cases and error handling
3. Mock dependencies if needed
4. Clear test descriptions

Code to test:
\`\`\`${language}
${code}
\`\`\``;

return this.generateCode({
prompt,
language,
temperature: 0.2
});
}

async generateDocumentation(code: string, language: string): Promise<AIResponse> {
const prompt = `Generate comprehensive documentation for the following ${language} code. Include:
1. Function/class description
2. Parameter descriptions with types
3. Return value description
4. Usage examples
5. Any side effects or important notes

Code:
\`\`\`${language}
${code}
\`\`\``;

return this.generateCode({
prompt,
language,
temperature: 0.1
});
}

private getTestFramework(language: string): string {
const frameworks: Record<string, string> = {
'typescript': 'Jest',
'javascript': 'Jest',
'python': 'pytest',
'java': 'JUnit',
'csharp': 'MSTest',
'go': 'Go testing package'
};

return frameworks[language] || 'appropriate testing framework';
}
}

代码分析服务

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
// src/services/codeAnalyzer.ts
import * as vscode from 'vscode';
import * as ts from 'typescript';

export interface CodeContext {
selectedText: string;
fileName: string;
language: string;
lineNumber: number;
functionContext?: string;
classContext?: string;
imports?: string[];
}

export class CodeAnalyzer {

async getCodeContext(): Promise<CodeContext | null> {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return null;
}

const document = editor.document;
const selection = editor.selection;
const selectedText = document.getText(selection);

if (!selectedText) {
vscode.window.showWarningMessage('请选择要分析的代码');
return null;
}

const context: CodeContext = {
selectedText,
fileName: document.fileName,
language: document.languageId,
lineNumber: selection.start.line + 1
};

// 为TypeScript/JavaScript提供更详细的上下文
if (document.languageId === 'typescript' || document.languageId === 'javascript') {
this.enhanceTypeScriptContext(context, document, selection);
}

return context;
}

private enhanceTypeScriptContext(
context: CodeContext,
document: vscode.TextDocument,
selection: vscode.Selection
) {
try {
const sourceFile = ts.createSourceFile(
document.fileName,
document.getText(),
ts.ScriptTarget.Latest,
true
);

const position = document.offsetAt(selection.start);

// 查找包含选择文本的函数
const functionNode = this.findContainingFunction(sourceFile, position);
if (functionNode) {
context.functionContext = document.getText(
new vscode.Range(
document.positionAt(functionNode.getStart()),
document.positionAt(functionNode.getEnd())
)
);
}

// 查找包含选择文本的类
const classNode = this.findContainingClass(sourceFile, position);
if (classNode) {
context.classContext = document.getText(
new vscode.Range(
document.positionAt(classNode.getStart()),
document.positionAt(classNode.getEnd())
)
);
}

// 提取导入语句
context.imports = this.extractImports(sourceFile);

} catch (error) {
console.error('TypeScript analysis error:', error);
}
}

private findContainingFunction(node: ts.Node, position: number): ts.FunctionLikeDeclaration | null {
if (ts.isFunctionLike(node) &&
position >= node.getStart() &&
position <= node.getEnd()) {
return node;
}

for (const child of node.getChildren()) {
const result = this.findContainingFunction(child, position);
if (result) {
return result;
}
}

return null;
}

private findContainingClass(node: ts.Node, position: number): ts.ClassDeclaration | null {
if (ts.isClassDeclaration(node) &&
position >= node.getStart() &&
position <= node.getEnd()) {
return node;
}

for (const child of node.getChildren()) {
const result = this.findContainingClass(child, position);
if (result) {
return result;
}
}

return null;
}

private extractImports(sourceFile: ts.SourceFile): string[] {
const imports: string[] = [];

ts.forEachChild(sourceFile, node => {
if (ts.isImportDeclaration(node)) {
imports.push(node.getFullText().trim());
}
});

return imports;
}

detectLanguageFramework(document: vscode.TextDocument): string {
const content = document.getText();
const frameworks: Record<string, RegExp[]> = {
'React': [/import.*react/i, /jsx/i, /useState|useEffect/i],
'Vue': [/import.*vue/i, /<template>/i, /defineComponent/i],
'Angular': [/import.*@angular/i, /@Component/i, /@Injectable/i],
'Express': [/import.*express/i, /app\.get|app\.post/i],
'NestJS': [/import.*@nestjs/i, /@Controller|@Injectable/i]
};

for (const [framework, patterns] of Object.entries(frameworks)) {
if (patterns.some(pattern => pattern.test(content))) {
return framework;
}
}

return 'Unknown';
}
}

命令处理器

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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
// src/commands/commandHandler.ts
import * as vscode from 'vscode';
import { AIService } from '../services/aiService';
import { CodeAnalyzer } from '../services/codeAnalyzer';

export class CommandHandler {
constructor(
private aiService: AIService,
private codeAnalyzer: CodeAnalyzer
) {}

async generateCode() {
const prompt = await vscode.window.showInputBox({
prompt: '描述你想要生成的代码功能',
placeHolder: '例如:创建一个用户注册的API接口',
value: ''
});

if (!prompt) {
return;
}

const editor = vscode.window.activeTextEditor;
const language = editor?.document.languageId || 'typescript';

// 显示进度
await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: "AI正在生成代码...",
cancellable: false
}, async (progress) => {
try {
progress.report({ increment: 30, message: "分析需求中..." });

const response = await this.aiService.generateCode({
prompt,
language,
context: await this.getProjectContext()
});

progress.report({ increment: 70, message: "生成代码中..." });

await this.insertGeneratedCode(response.content);

// 显示token使用情况
if (response.usage) {
vscode.window.showInformationMessage(
`代码已生成!Token使用: ${response.usage.total_tokens}`
);
}

} catch (error) {
vscode.window.showErrorMessage(`代码生成失败: ${error.message}`);
}
});
}

async explainCode() {
const context = await this.codeAnalyzer.getCodeContext();
if (!context) {
return;
}

await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: "AI正在分析代码...",
cancellable: false
}, async () => {
try {
const response = await this.aiService.explainCode(
context.selectedText,
context.language
);

// 在新的编辑器标签页中显示解释
const document = await vscode.workspace.openTextDocument({
content: this.formatExplanation(response.content, context),
language: 'markdown'
});

await vscode.window.showTextDocument(document);

} catch (error) {
vscode.window.showErrorMessage(`代码解释失败: ${error.message}`);
}
});
}

async optimizeCode() {
const context = await this.codeAnalyzer.getCodeContext();
if (!context) {
return;
}

await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: "AI正在优化代码...",
cancellable: false
}, async () => {
try {
const response = await this.aiService.optimizeCode(
context.selectedText,
context.language
);

// 显示优化建议和对比
await this.showOptimizationResult(context, response.content);

} catch (error) {
vscode.window.showErrorMessage(`代码优化失败: ${error.message}`);
}
});
}

async generateTests() {
const context = await this.codeAnalyzer.getCodeContext();
if (!context) {
return;
}

await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: "AI正在生成测试...",
cancellable: false
}, async () => {
try {
const response = await this.aiService.generateTests(
context.selectedText,
context.language
);

// 创建测试文件
await this.createTestFile(context, response.content);

} catch (error) {
vscode.window.showErrorMessage(`测试生成失败: ${error.message}`);
}
});
}

async generateDocs() {
const context = await this.codeAnalyzer.getCodeContext();
if (!context) {
return;
}

await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: "AI正在生成文档...",
cancellable: false
}, async () => {
try {
const response = await this.aiService.generateDocumentation(
context.selectedText,
context.language
);

// 在当前位置插入文档注释
await this.insertDocumentation(context, response.content);

} catch (error) {
vscode.window.showErrorMessage(`文档生成失败: ${error.message}`);
}
});
}

private async getProjectContext(): Promise<string> {
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
if (!workspaceFolder) {
return '';
}

// 读取package.json获取项目信息
try {
const packageJsonUri = vscode.Uri.joinPath(workspaceFolder.uri, 'package.json');
const packageJsonDoc = await vscode.workspace.openTextDocument(packageJsonUri);
const packageJson = JSON.parse(packageJsonDoc.getText());

return `项目: ${packageJson.name}, 描述: ${packageJson.description}, 依赖: ${Object.keys(packageJson.dependencies || {}).join(', ')}`;
} catch {
return `工作区: ${workspaceFolder.name}`;
}
}

private async insertGeneratedCode(code: string) {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}

// 清理生成的代码(移除markdown标记)
const cleanCode = this.cleanGeneratedCode(code);

await editor.edit(editBuilder => {
editBuilder.insert(editor.selection.active, cleanCode);
});

// 自动格式化
await vscode.commands.executeCommand('editor.action.formatDocument');
}

private cleanGeneratedCode(code: string): string {
// 移除markdown代码块标记
return code
.replace(/^```[\w]*\n/, '')
.replace(/\n```$/, '')
.trim();
}

private formatExplanation(explanation: string, context: any): string {
return `# 代码解释

## 分析的代码
\`\`\`${context.language}
${context.selectedText}
\`\`\`

## 文件信息
- **文件**: ${context.fileName}
- **语言**: ${context.language}
- **行号**: ${context.lineNumber}

## 详细解释

${explanation}

---
*由 AI Code Assistant 生成于 ${new Date().toLocaleString()}*
`;
}

private async showOptimizationResult(context: any, optimization: string) {
const document = await vscode.workspace.openTextDocument({
content: `# 代码优化建议

## 原始代码
\`\`\`${context.language}
${context.selectedText}
\`\`\`

## 优化建议

${optimization}

---
*由 AI Code Assistant 生成于 ${new Date().toLocaleString()}*
`,
language: 'markdown'
});

await vscode.window.showTextDocument(document);
}

private async createTestFile(context: any, testCode: string) {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}

// 生成测试文件名
const originalPath = editor.document.uri.fsPath;
const testPath = originalPath.replace(/\.(ts|js)$/, '.test.$1');

try {
const testUri = vscode.Uri.file(testPath);
const testContent = `// 由 AI Code Assistant 自动生成的测试文件
// 生成时间: ${new Date().toLocaleString()}

${this.cleanGeneratedCode(testCode)}
`;

await vscode.workspace.fs.writeFile(testUri, Buffer.from(testContent, 'utf8'));
const testDocument = await vscode.workspace.openTextDocument(testUri);
await vscode.window.showTextDocument(testDocument);

vscode.window.showInformationMessage(`测试文件已创建: ${testPath}`);
} catch (error) {
vscode.window.showErrorMessage(`创建测试文件失败: ${error.message}`);
}
}

private async insertDocumentation(context: any, documentation: string) {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}

// 找到选择文本的开始位置,在其前面插入文档注释
const startLine = editor.selection.start.line;
const insertPosition = new vscode.Position(startLine, 0);

const docComment = this.formatDocComment(documentation, context.language);

await editor.edit(editBuilder => {
editBuilder.insert(insertPosition, docComment + '\n');
});
}

private formatDocComment(documentation: string, language: string): string {
// 清理和格式化文档注释
const cleanDoc = this.cleanGeneratedCode(documentation);

if (language === 'typescript' || language === 'javascript') {
// JSDoc格式
return `/**\n${cleanDoc.split('\n').map(line => ` * ${line}`).join('\n')}\n */`;
} else if (language === 'python') {
// Python docstring格式
return `"""\n${cleanDoc}\n"""`;
} else {
// 通用注释格式
return cleanDoc.split('\n').map(line => `// ${line}`).join('\n');
}
}
}

WebView聊天界面

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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
// src/webview/chatWebviewProvider.ts
import * as vscode from 'vscode';
import { AIService } from '../services/aiService';

export class ChatWebviewProvider implements vscode.WebviewViewProvider {
public static readonly viewType = 'aiAssistantView';

private _view?: vscode.WebviewView;

constructor(
private readonly _extensionUri: vscode.Uri,
private readonly _aiService: AIService
) {}

public resolveWebviewView(
webviewView: vscode.WebviewView,
context: vscode.WebviewViewResolveContext,
_token: vscode.CancellationToken,
) {
this._view = webviewView;

webviewView.webview.options = {
enableScripts: true,
localResourceRoots: [this._extensionUri]
};

webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);

// 处理来自WebView的消息
webviewView.webview.onDidReceiveMessage(async (data) => {
switch (data.type) {
case 'sendMessage':
await this.handleUserMessage(data.message);
break;
case 'clearChat':
this.clearChat();
break;
case 'insertCode':
await this.insertCodeToEditor(data.code);
break;
}
});
}

public show() {
if (this._view) {
this._view.show?.(true);
}
}

private async handleUserMessage(message: string) {
if (!this._view) {
return;
}

// 显示用户消息
this._view.webview.postMessage({
type: 'addMessage',
message: {
role: 'user',
content: message,
timestamp: new Date().toISOString()
}
});

// 显示加载状态
this._view.webview.postMessage({
type: 'setLoading',
loading: true
});

try {
// 获取当前编辑器上下文
const context = await this.getCurrentContext();

const response = await this._aiService.generateCode({
prompt: message,
context: context,
language: this.getCurrentLanguage()
});

// 显示AI回复
this._view.webview.postMessage({
type: 'addMessage',
message: {
role: 'assistant',
content: response.content,
timestamp: new Date().toISOString(),
usage: response.usage
}
});

} catch (error) {
this._view.webview.postMessage({
type: 'addMessage',
message: {
role: 'error',
content: `Error: ${error.message}`,
timestamp: new Date().toISOString()
}
});
} finally {
this._view.webview.postMessage({
type: 'setLoading',
loading: false
});
}
}

private clearChat() {
if (this._view) {
this._view.webview.postMessage({
type: 'clearMessages'
});
}
}

private async insertCodeToEditor(code: string) {
const editor = vscode.window.activeTextEditor;
if (!editor) {
vscode.window.showWarningMessage('请先打开一个文件');
return;
}

await editor.edit(editBuilder => {
editBuilder.insert(editor.selection.active, code);
});

// 自动格式化
await vscode.commands.executeCommand('editor.action.formatDocument');
}

private async getCurrentContext(): Promise<string> {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return '';
}

const document = editor.document;
const selection = editor.selection;

if (!selection.isEmpty) {
return `当前选中的代码:\n${document.getText(selection)}`;
}

// 获取当前函数上下文
const currentLine = document.lineAt(selection.active.line);
const functionContext = this.findFunctionContext(document, selection.active.line);

return functionContext || `当前行: ${currentLine.text}`;
}

private findFunctionContext(document: vscode.TextDocument, lineNumber: number): string {
// 简单的函数查找逻辑
for (let i = lineNumber; i >= 0; i--) {
const line = document.lineAt(i).text;
if (line.match(/function\s+\w+|const\s+\w+\s*=.*=>|class\s+\w+/)) {
return line.trim();
}
}
return '';
}

private getCurrentLanguage(): string {
const editor = vscode.window.activeTextEditor;
return editor?.document.languageId || 'typescript';
}

private _getHtmlForWebview(webview: vscode.Webview) {
const styleResetUri = webview.asWebviewUri(
vscode.Uri.joinPath(this._extensionUri, 'media', 'reset.css')
);
const styleVSCodeUri = webview.asWebviewUri(
vscode.Uri.joinPath(this._extensionUri, 'media', 'vscode.css')
);
const styleMainUri = webview.asWebviewUri(
vscode.Uri.joinPath(this._extensionUri, 'media', 'main.css')
);
const scriptUri = webview.asWebviewUri(
vscode.Uri.joinPath(this._extensionUri, 'media', 'main.js')
);

const nonce = getNonce();

return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource} 'unsafe-inline'; script-src 'nonce-${nonce}'; font-src ${webview.cspSource};">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="${styleResetUri}" rel="stylesheet">
<link href="${styleVSCodeUri}" rel="stylesheet">
<link href="${styleMainUri}" rel="stylesheet">
<title>AI Assistant</title>
</head>
<body>
<div class="container">
<div class="header">
<h2>AI Code Assistant</h2>
<button id="clearBtn" class="btn-secondary">清空对话</button>
</div>

<div id="chatContainer" class="chat-container">
<div class="welcome-message">
<h3>👋 欢迎使用AI代码助手</h3>
<p>我可以帮助你:</p>
<ul>
<li>生成代码片段</li>
<li>解释复杂代码</li>
<li>优化代码性能</li>
<li>生成单元测试</li>
<li>编写技术文档</li>
</ul>
</div>
</div>

<div class="input-container">
<div class="input-wrapper">
<textarea
id="messageInput"
placeholder="输入你的问题或需求..."
rows="3"
></textarea>
<button id="sendBtn" class="btn-primary">发送</button>
</div>
<div id="loadingIndicator" class="loading" style="display: none;">
<div class="spinner"></div>
<span>AI思考中...</span>
</div>
</div>
</div>

<script nonce="${nonce}" src="${scriptUri}"></script>
</body>
</html>`;
}
}

function getNonce() {
let text = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < 32; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}

WebView前端脚本

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
// media/main.js
(function() {
const vscode = acquireVsCodeApi();

const chatContainer = document.getElementById('chatContainer');
const messageInput = document.getElementById('messageInput');
const sendBtn = document.getElementById('sendBtn');
const clearBtn = document.getElementById('clearBtn');
const loadingIndicator = document.getElementById('loadingIndicator');

let messageHistory = [];

// 发送消息
function sendMessage() {
const message = messageInput.value.trim();
if (!message) return;

messageInput.value = '';
vscode.postMessage({
type: 'sendMessage',
message: message
});
}

// 事件监听
sendBtn.addEventListener('click', sendMessage);
clearBtn.addEventListener('click', () => {
vscode.postMessage({ type: 'clearChat' });
});

messageInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});

// 接收来自扩展的消息
window.addEventListener('message', event => {
const message = event.data;

switch (message.type) {
case 'addMessage':
addMessageToChat(message.message);
break;
case 'setLoading':
setLoading(message.loading);
break;
case 'clearMessages':
clearMessages();
break;
}
});

function addMessageToChat(message) {
const messageElement = createMessageElement(message);
chatContainer.appendChild(messageElement);
chatContainer.scrollTop = chatContainer.scrollHeight;

messageHistory.push(message);
}

function createMessageElement(message) {
const messageDiv = document.createElement('div');
messageDiv.className = `message ${message.role}`;

const header = document.createElement('div');
header.className = 'message-header';

const role = document.createElement('span');
role.className = 'role';
role.textContent = message.role === 'user' ? '你' :
message.role === 'assistant' ? 'AI助手' : '错误';

const timestamp = document.createElement('span');
timestamp.className = 'timestamp';
timestamp.textContent = new Date(message.timestamp).toLocaleTimeString();

header.appendChild(role);
header.appendChild(timestamp);

const content = document.createElement('div');
content.className = 'message-content';

if (message.role === 'assistant' && isCodeContent(message.content)) {
content.innerHTML = formatCodeContent(message.content);

// 添加插入代码按钮
const codeBlocks = content.querySelectorAll('pre code');
codeBlocks.forEach(codeBlock => {
const insertBtn = document.createElement('button');
insertBtn.className = 'insert-code-btn';
insertBtn.textContent = '插入代码';
insertBtn.onclick = () => {
vscode.postMessage({
type: 'insertCode',
code: codeBlock.textContent
});
};

const pre = codeBlock.parentElement;
pre.appendChild(insertBtn);
});
} else {
content.innerHTML = formatMarkdown(message.content);
}

messageDiv.appendChild(header);
messageDiv.appendChild(content);

// 添加使用统计
if (message.usage) {
const usageDiv = document.createElement('div');
usageDiv.className = 'usage-info';
usageDiv.textContent = `Token使用: ${message.usage.total_tokens}`;
messageDiv.appendChild(usageDiv);
}

return messageDiv;
}

function isCodeContent(content) {
return content.includes('```') || content.includes('function ') || content.includes('class ');
}

function formatCodeContent(content) {
// 简单的markdown代码块格式化
return content
.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => {
return `<pre><code class="language-${lang || 'text'}">${escapeHtml(code.trim())}</code></pre>`;
})
.replace(/`([^`]+)`/g, '<code>$1</code>')
.replace(/\n/g, '<br>');
}

function formatMarkdown(content) {
return content
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.*?)\*/g, '<em>$1</em>')
.replace(/\n/g, '<br>');
}

function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}

function setLoading(loading) {
loadingIndicator.style.display = loading ? 'flex' : 'none';
sendBtn.disabled = loading;
}

function clearMessages() {
const welcomeMessage = chatContainer.querySelector('.welcome-message');
chatContainer.innerHTML = '';
if (welcomeMessage) {
chatContainer.appendChild(welcomeMessage.cloneNode(true));
}
messageHistory = [];
}
})();

1.3 测试和优化

单元测试

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
// src/test/suite/aiService.test.ts
import * as assert from 'assert';
import { AIService } from '../../services/aiService';

suite('AI Service Tests', () => {
let aiService: AIService;

setup(() => {
aiService = new AIService();
});

test('should handle code generation request', async () => {
const request = {
prompt: 'Create a simple hello world function',
language: 'typescript'
};

// Mock的实现,实际测试中需要使用真实的API或Mock
// const response = await aiService.generateCode(request);
// assert.ok(response.content.length > 0);
});

test('should extract code context correctly', async () => {
// 测试代码上下文提取
assert.ok(true); // 占位符测试
});
});

1.4 发布准备

构建脚本

1
2
3
4
5
6
7
8
9
{
"scripts": {
"vscode:prepublish": "npm run package",
"compile": "webpack",
"watch": "webpack --watch",
"package": "webpack --mode production --devtool hidden-source-map",
"test": "node ./out/test/runTest.js"
}
}

🛠️ 项目二:API Testing Tool(API测试工具)

项目介绍

开发一个类似Postman的API测试工具,但专门为VS Code集成优化。

核心功能

  • HTTP请求构建:支持GET、POST、PUT、DELETE等方法
  • 环境变量管理:多环境配置切换
  • 请求历史:自动保存和管理请求记录
  • 响应分析:JSON格式化、响应时间统计
  • 批量测试:支持测试集合和自动化测试

2.1 技术架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 项目结构
src/
├── extension.ts // 扩展入口
├── providers/
│ ├── apiTreeProvider.ts // API列表树视图
│ ├── requestWebview.ts // 请求构建界面
│ └── historyProvider.ts // 历史记录提供者
├── services/
│ ├── httpClient.ts // HTTP请求服务
│ ├── environmentService.ts // 环境变量服务
│ └── storageService.ts // 数据存储服务
├── models/
│ ├── request.ts // 请求数据模型
│ ├── response.ts // 响应数据模型
│ └── environment.ts // 环境配置模型
└── webview/ // React前端
├── components/
├── services/
└── App.tsx

2.2 核心实现

HTTP客户端服务

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
// src/services/httpClient.ts
import axios, { AxiosResponse, AxiosRequestConfig } from 'axios';
import { ApiRequest, ApiResponse } from '../models/request';

export class HttpClientService {

async sendRequest(request: ApiRequest): Promise<ApiResponse> {
const startTime = Date.now();

try {
const config: AxiosRequestConfig = {
method: request.method,
url: request.url,
headers: request.headers,
data: request.body,
timeout: request.timeout || 30000
};

const response: AxiosResponse = await axios(config);
const endTime = Date.now();

return {
id: this.generateId(),
request: request,
status: response.status,
statusText: response.statusText,
headers: response.headers,
data: response.data,
responseTime: endTime - startTime,
timestamp: new Date().toISOString()
};

} catch (error) {
const endTime = Date.now();

if (axios.isAxiosError(error)) {
return {
id: this.generateId(),
request: request,
status: error.response?.status || 0,
statusText: error.response?.statusText || 'Network Error',
headers: error.response?.headers || {},
data: error.response?.data || error.message,
responseTime: endTime - startTime,
timestamp: new Date().toISOString(),
error: error.message
};
}

throw error;
}
}

private generateId(): string {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
}

📊 项目三:Data Visualizer(数据可视化工具)

项目介绍

创建一个强大的数据可视化扩展,支持多种数据源和图表类型。

核心功能

  • 多数据源支持:JSON、CSV、数据库连接
  • 丰富图表类型:柱状图、线图、饼图、散点图、热力图
  • 实时数据:WebSocket数据流支持
  • 交互式图表:缩放、筛选、钻取
  • 导出功能:PNG、SVG、PDF格式导出

3.1 技术实现

数据处理服务

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
// src/services/dataProcessor.ts
import * as d3 from 'd3';

export interface DataPoint {
x: any;
y: any;
category?: string;
[key: string]: any;
}

export class DataProcessor {

processCSV(csvContent: string): DataPoint[] {
const data = d3.csvParse(csvContent);
return data.map(row => ({
...row,
x: row[Object.keys(row)[0]],
y: +row[Object.keys(row)[1]] || row[Object.keys(row)[1]]
}));
}

processJSON(jsonContent: string): DataPoint[] {
const data = JSON.parse(jsonContent);
if (Array.isArray(data)) {
return data;
}
return [data];
}

aggregateData(data: DataPoint[], groupBy: string, aggregateBy: string, aggregateFunction: 'sum' | 'avg' | 'count' | 'max' | 'min'): DataPoint[] {
const grouped = d3.group(data, d => d[groupBy]);

return Array.from(grouped, ([key, values]) => {
let aggregatedValue: number;

switch (aggregateFunction) {
case 'sum':
aggregatedValue = d3.sum(values, d => +d[aggregateBy]);
break;
case 'avg':
aggregatedValue = d3.mean(values, d => +d[aggregateBy]) || 0;
break;
case 'count':
aggregatedValue = values.length;
break;
case 'max':
aggregatedValue = d3.max(values, d => +d[aggregateBy]) || 0;
break;
case 'min':
aggregatedValue = d3.min(values, d => +d[aggregateBy]) || 0;
break;
}

return {
x: key,
y: aggregatedValue,
category: key,
count: values.length
};
});
}
}

📋 项目四:Project Manager(项目管理工具)

项目介绍

开发一个轻量级的项目管理扩展,帮助开发者组织任务和跟踪进度。

核心功能

  • 任务管理:创建、编辑、删除任务
  • 进度跟踪:可视化进度条和状态
  • 时间跟踪:工作时间记录和统计
  • 团队协作:任务分配和评论
  • 报告生成:项目进度报告

🔍 项目五:Code Quality Monitor(代码质量监控)

项目介绍

构建一个代码质量监控工具,集成多种代码分析工具,提供全面的代码质量洞察。

核心功能

  • 静态代码分析:ESLint、SonarJS集成
  • 代码复杂度:圈复杂度、认知复杂度计算
  • 技术债务:代码异味检测和修复建议
  • 测试覆盖率:覆盖率可视化和趋势分析
  • 质量趋势:历史质量数据跟踪

5.1 Language Server实现

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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// src/server/qualityServer.ts
import {
createConnection,
TextDocuments,
ProposedFeatures,
InitializeParams,
DidChangeConfigurationNotification,
CompletionItem,
CompletionItemKind,
TextDocumentPositionParams,
TextDocumentSyncKind,
InitializeResult,
DocumentDiagnosticReportKind,
type DocumentDiagnosticReport
} from 'vscode-languageserver/node';

import { TextDocument } from 'vscode-languageserver-textdocument';

// 创建连接
const connection = createConnection(ProposedFeatures.all);

// 创建文档管理器
const documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);

let hasConfigurationCapability = false;
let hasWorkspaceFolderCapability = false;
let hasDiagnosticRelatedInformationCapability = false;

connection.onInitialize((params: InitializeParams) => {
const capabilities = params.capabilities;

hasConfigurationCapability = !!(
capabilities.workspace && !!capabilities.workspace.configuration
);
hasWorkspaceFolderCapability = !!(
capabilities.workspace && !!capabilities.workspace.workspaceFolders
);
hasDiagnosticRelatedInformationCapability = !!(
capabilities.textDocument &&
capabilities.textDocument.publishDiagnostics &&
capabilities.textDocument.publishDiagnostics.relatedInformation
);

const result: InitializeResult = {
capabilities: {
textDocumentSync: TextDocumentSyncKind.Incremental,
completionProvider: {
resolveProvider: true
},
diagnosticProvider: {
interFileDependencies: false,
workspaceDiagnostics: false
}
}
};

if (hasWorkspaceFolderCapability) {
result.capabilities.workspace = {
workspaceFolders: {
supported: true
}
};
}

return result;
});

connection.onInitialized(() => {
if (hasConfigurationCapability) {
connection.client.register(DidChangeConfigurationNotification.type, undefined);
}
if (hasWorkspaceFolderCapability) {
connection.workspace.onDidChangeWorkspaceFolders(_event => {
connection.console.log('Workspace folder change event received.');
});
}
});

// 代码质量分析
async function analyzeCodeQuality(document: TextDocument): Promise<DocumentDiagnosticReport> {
const text = document.getText();
const diagnostics = [];

// 简单的复杂度分析
const complexity = calculateCyclomaticComplexity(text);
if (complexity > 10) {
diagnostics.push({
severity: 2, // Warning
range: {
start: { line: 0, character: 0 },
end: { line: 0, character: 10 }
},
message: `High cyclomatic complexity: ${complexity}. Consider refactoring.`,
source: 'code-quality-monitor'
});
}

// 检查长函数
const longFunctions = findLongFunctions(text);
longFunctions.forEach(func => {
diagnostics.push({
severity: 2, // Warning
range: func.range,
message: `Function is too long (${func.lines} lines). Consider breaking it down.`,
source: 'code-quality-monitor'
});
});

return {
kind: DocumentDiagnosticReportKind.Full,
items: diagnostics
};
}

function calculateCyclomaticComplexity(code: string): number {
// 简化的圈复杂度计算
const complexityKeywords = [
'if', 'else if', 'while', 'for', 'case', 'catch', '&&', '||', '?'
];

let complexity = 1; // 基础复杂度

complexityKeywords.forEach(keyword => {
const regex = new RegExp(`\\b${keyword}\\b`, 'g');
const matches = code.match(regex);
if (matches) {
complexity += matches.length;
}
});

return complexity;
}

function findLongFunctions(code: string): Array<{range: any, lines: number}> {
const functions = [];
const lines = code.split('\n');
let inFunction = false;
let functionStart = 0;
let braceCount = 0;

for (let i = 0; i < lines.length; i++) {
const line = lines[i];

if (line.includes('function ') || line.includes('=>') || line.includes('class ')) {
if (!inFunction) {
inFunction = true;
functionStart = i;
braceCount = 0;
}
}

if (inFunction) {
braceCount += (line.match(/{/g) || []).length;
braceCount -= (line.match(/}/g) || []).length;

if (braceCount === 0 && i > functionStart) {
const functionLength = i - functionStart + 1;
if (functionLength > 50) { // 超过50行认为是长函数
functions.push({
range: {
start: { line: functionStart, character: 0 },
end: { line: i, character: line.length }
},
lines: functionLength
});
}
inFunction = false;
}
}
}

return functions;
}

// 文档诊断
connection.languages.onDocumentDiagnostic(async (params) => {
const document = documents.get(params.textDocument.uri);
if (document !== undefined) {
return analyzeCodeQuality(document);
} else {
return {
kind: DocumentDiagnosticReportKind.Full,
items: []
};
}
});

// 监听文档变化
documents.onDidChangeContent(change => {
// 文档内容变化时重新分析
analyzeCodeQuality(change.document);
});

// 启动文档管理器监听
documents.listen(connection);

// 启动连接
connection.listen();

🚀 第四部分:部署和推广

4.1 CI/CD流程

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
# .github/workflows/release.yml
name: Release Extension

on:
push:
tags:
- 'v*'

jobs:
build-and-release:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'

- name: Install dependencies
run: |
npm ci
cd webview && npm ci

- name: Run tests
run: npm test

- name: Build extension
run: npm run package

- name: Package extension
run: npx vsce package

- name: Publish to marketplace
run: npx vsce publish
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}

- name: Create GitHub release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false

4.2 用户反馈和迭代

遥测数据收集

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
// src/telemetry/telemetryService.ts
export class TelemetryService {

trackFeatureUsage(feature: string, properties?: Record<string, any>) {
// 收集功能使用数据
const data = {
feature,
timestamp: new Date().toISOString(),
version: this.getExtensionVersion(),
...properties
};

this.sendTelemetry('feature_usage', data);
}

trackError(error: Error, context?: string) {
// 收集错误信息
const data = {
error: error.message,
stack: error.stack,
context,
timestamp: new Date().toISOString(),
version: this.getExtensionVersion()
};

this.sendTelemetry('error', data);
}

trackPerformance(operation: string, duration: number) {
// 收集性能数据
const data = {
operation,
duration,
timestamp: new Date().toISOString(),
version: this.getExtensionVersion()
};

this.sendTelemetry('performance', data);
}
}

📚 本阶段总结

通过这5个完整的实战项目,你已经掌握了:

AI集成开发:OpenAI API集成、智能代码生成、自然语言处理
全栈开发能力:前后端分离架构、数据库操作、API设计
数据可视化:D3.js图表库、实时数据处理、交互式界面
Language Server:LSP协议实现、代码分析、诊断服务
DevOps实践:自动化测试、持续集成、发布流程
产品思维:用户体验设计、需求分析、市场定位

核心技能回顾

  • 架构设计:微服务架构、事件驱动、插件化设计
  • 技术栈整合:TypeScript + React/Vue + Node.js + AI
  • 性能优化:代码分割、懒加载、缓存策略
  • 用户体验:响应式设计、无障碍访问、国际化
  • 质量保证:单元测试、集成测试、代码质量

🎓 毕业项目挑战

挑战任务

选择以下任一方向,完成一个创新性的VS Code扩展:

  1. AI驱动的重构助手:智能识别代码问题并提供重构建议
  2. 实时协作编程平台:类似Live Share的协作功能增强
  3. 智能项目脚手架:基于项目类型自动生成最佳实践模板
  4. 代码安全扫描器:集成多种安全扫描工具的统一界面
  5. 开发者效率仪表板:个人编程习惯分析和效率提升建议

评估标准

  • 创新性:解决实际问题的独特方案
  • 技术深度:复杂技术的正确应用
  • 用户体验:直观易用的界面设计
  • 代码质量:良好的架构和代码规范
  • 完整性:从需求到发布的完整流程

相关资源

恭喜你完成了VS Code扩展开发的完整学习旅程!现在你已经具备了独立开发专业级扩展的所有技能。继续创新,构建改变开发者工作方式的优秀工具吧!