vscode插件开发笔记——大模型应用之AI编程助手

系列文章目录

文章目录


前言

最近在开发vscode插件相关的项目,网上很少有关于大模型作为AI 编程助手这方面的教程。因此,借此机会把最近写的几个demo分享记录一下。

一、代码补全

思路:

  1. 读取vscode插件上鼠标光标的上下文信息。
  2. 将提示词和上下文代码作为输入,通过modelfusion调用大模型得到输出。
  3. 将输出内容插入到光标所在位置,通过Declaration在vscode页面进行显示。
  4. 设置快捷键,如果代码补全正确,按下快捷键后自动插入代码。反之光标移动,代码自动消失。

下面直接上代码。

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)

相关推荐
亦枫Leonlew38 分钟前
微积分复习笔记 Calculus Volume 2 - 5.1 Sequences
笔记·数学·微积分
高山我梦口香糖1 小时前
[react 3种方法] 获取ant组件ref用ts如何定义?
typescript·react
爱码小白1 小时前
网络编程(王铭东老师)笔记
服务器·网络·笔记
LuH11242 小时前
【论文阅读笔记】Learning to sample
论文阅读·笔记·图形渲染·点云
一棵开花的树,枝芽无限靠近你3 小时前
【PPTist】组件结构设计、主题切换
前端·笔记·学习·编辑器
prall3 小时前
实战小技巧:下划线转驼峰篇
前端·typescript
犬余4 小时前
设计模式之桥接模式:抽象与实现之间的分离艺术
笔记·学习·设计模式·桥接模式
数据爬坡ing5 小时前
小白考研历程:跌跌撞撞,起起伏伏,五个月备战历程!!!
大数据·笔记·考研·数据分析
咖肥猫5 小时前
【ue5学习笔记2】在场景放入一个物体的蓝图输入事件无效?
笔记·学习·ue5
P7进阶路5 小时前
Ai编程从零开始全栈开发一个后台管理系统之用户登录、权限控制、用户管理-前端部分(十二)
前端·ai编程