系列文章目录
文章目录
前言
最近在开发vscode插件相关的项目,网上很少有关于大模型作为AI 编程助手这方面的教程。因此,借此机会把最近写的几个demo分享记录一下。
一、代码补全
思路:
- 读取vscode插件上鼠标光标的上下文信息。
- 将提示词和上下文代码作为输入,通过modelfusion调用大模型得到输出。
- 将输出内容插入到光标所在位置,通过Declaration在vscode页面进行显示。
- 设置快捷键,如果代码补全正确,按下快捷键后自动插入代码。反之光标移动,代码自动消失。
下面直接上代码。
extension.ts 文件:
typescript
import * as vscode from 'vscode';
import {
BaseUrlApiConfiguration,
openaicompatible,
streamText,
} from 'modelfusion';
import{ previewInsertion, getBeforeText, getAfterText }from '../src/main'
export function activate(context: vscode.ExtensionContext) {
// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log('Congratulations, your extension "dytest" is now active!');
let globalText:string|undefined ;
let globalindex: vscode.Position | undefined;
// 如何删除字符
const editor = vscode.window.activeTextEditor;
// 代码补全命令
const disposable = vscode.commands.registerCommand('dytest.helloWorld', async () => {
if (!editor) {
return;
}
// 代码所在位置
const code =editor.document.getText();
//光标所在位置
const offset =editor.document.offsetAt(editor.selection.active);
const index = editor.document.positionAt(offset);
const position = editor.selection.active;
const document = editor.document;
const beforeText = getBeforeText(document, position);
const afterText = getAfterText(document, position);
// 调用大模型
const code_prompt='提示词';
const code_instruction='{{{prefix}}}[BLANK]{{{suffix}}}';
const code_instruction_2=code_instruction.replace("{{{prefix}}}", beforeText).replace("{{{suffix}}}",afterText)
console.log(code_instruction_2)
const text2 = await streamText({
model: openaicompatible
.ChatTextGenerator({
// 这里使用的是自己部署的大模型,url和openai的一样。但是模型不是用的openai系列的。如果要改,可以换成openai的。
api: new BaseUrlApiConfiguration({
baseUrl: "模型的url",
headers: {
Authorization: `模型的api`,
},
}),
provider: "openaicompatible-fireworksai", // optional, only needed for logging and custom configs
model: "自己的模型",
})
.withInstructionPrompt(),
prompt: {
system:
code_prompt,
instruction:
code_instruction_2
},
});
let responseUntilNow = "";
for await (const textPart of text2) {
// process.stdout.write(textPart);
responseUntilNow += textPart;
}
console.log(responseUntilNow)
// 进行代码补全的提示
const previewinsertion =previewInsertion(editor,index,responseUntilNow);
globalText=responseUntilNow;
globalindex=index
vscode.window.showInformationMessage('Hello World from dy_test!');
});
context.subscriptions.push(disposable);
const disposable2 = vscode.commands.registerCommand('extension.myCommand', async () => {
editor?.edit((editBuilder => {
if (!globalindex || !globalText){
return;
}
const lines = globalText.split(/\r\n|\n/); // 分割文本
editBuilder.insert(globalindex, lines[0])
globalindex=undefined;
globalText=undefined;
}));
vscode.window.showInformationMessage('Hello World from myextension!');
});
let timeout: NodeJS.Timeout | undefined;
context.subscriptions.push(disposable2);
// 注册事件监听器以在特定条件下调用命令
context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(editor => {
if (editor) {
// 延迟调用命令
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
vscode.commands.executeCommand('dytest.helloWorld');
}, 200); // 延迟
}
}));
context.subscriptions.push(vscode.window.onDidChangeTextEditorSelection(event => {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
vscode.commands.executeCommand('dytest.helloWorld');
}, 200); // 延迟
}));
}
export function deactivate() {}
main.ts文件:
typescript
import {parse}from '@babel/parser'
import traverse from '@babel/traverse'
import * as vscode from 'vscode';
// 代码补全插入
export function previewInsertion(editor: vscode.TextEditor, position: vscode.Position, text: string, ) {
const range = new vscode.Range(position, position.translate({characterDelta: text.length}));
let currentDecoration: vscode.DecorationOptions[] | undefined = undefined;
let decorationType: vscode.TextEditorDecorationType | undefined = undefined;
const lines = text.split(/\r\n|\n/); // 分割文本
let lineCount = 0;
let totalCharacterCount = 0;
// 计算总行数和总字符数
for (const line of lines) {
lineCount++;
totalCharacterCount += line.length;
}
totalCharacterCount--;
console.log(lineCount); // 输出非空行数
if (!decorationType) {
decorationType = vscode.window.createTextEditorDecorationType({
light: { // Light theme settings
after: {
contentText: lines[0],
fontStyle: 'italic',
color: '#7f8c8d', // 灰色
backgroundColor: 'transparent',
// textDecoration: 'none',
margin: '0 0 0 0',
},
},
dark: { // Dark theme settings
after: {
contentText: lines[0],
fontStyle: 'italic',
color: '#95a5a6', // 灰色,适合深色主题
backgroundColor: 'transparent',
// textDecoration: 'none',
margin: '0 0 0 0',
},
}
});
}
const endPosition=position.translate({lineDelta:lineCount-1,characterDelta:totalCharacterCount })
console.log("position:",position)
console.log("endPosition:",endPosition)
// 创建装饰器选项
currentDecoration = [{
range: new vscode.Range(position, position),
hoverMessage: new vscode.MarkdownString('按"tab"键确认'),
}];
// 应用装饰器
editor.setDecorations(decorationType, currentDecoration);
// 监听鼠标移动事件,移除装饰
const mouseMoveDisposable = vscode.window.onDidChangeTextEditorSelection(
(event) => {
if (event.textEditor === editor) {
editor.setDecorations(decorationType, []);
currentDecoration = undefined;
}
},
null,
);
}
// 获取文本上下文信息
export function getBeforeText(document: vscode.TextDocument, position: vscode.Position): string {
const range = new vscode.Range(new vscode.Position(0, 0), position);
return document.getText(range);
}
export function getAfterText(document: vscode.TextDocument, position: vscode.Position): string {
const lastLine = document.lineCount - 1;
const lastLineLength = document.lineAt(lastLine).text.length;
const range = new vscode.Range(position, new vscode.Position(lastLine, lastLineLength));
return document.getText(range);
}
package.json
添加两个命令
typescript
"commands": [
{
"command": "dytest.helloWorld",
"title": "Hello World"
},
{ "command": "extension.myCommand",
"title": "My Command"
}
]
快捷键
typescript
"keybindings": [
{
"command": "extension.myCommand",
"key": "ctrl+shift",
"when": "editorTextFocus"
}
]
实现效果:
代码补全(2)