自己平时使用了一些插件,有些地方感觉不顺手,索性按自己需求写一个VS Code插件,同时练练手。 大体功能就是快速打印Console,Region任意折叠代码块。
快速开始
- 安装开发脚手架
shell
npm install -g yo generator-code
- 运行创建
yo code
shell
# ? What type of extension do you want to create? New Extension (TypeScript)
# ? What's the name of your extension? HelloWorld
### Press <Enter> to choose default for all options below ###
# ? What's the identifier of your extension? helloworld
# ? What's the description of your extension? LEAVE BLANK
# ? Initialize a git repository? Yes
# ? Bundle the source code with webpack? No
# ? Which package manager to use? npm
# ? Do you want to open the new folder with Visual Studio Code? Open with `code`
Console快速打印
获取选中的文本
- 原始代码,运行右下角弹出
Hello World from loghuu!
typescript
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
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 "loghuu" is now active!');
// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
let disposable = vscode.commands.registerCommand('loghuu.helloWorld', () => {
// The code you place here will be executed every time your command is executed
// Display a message box to the user
vscode.window.showInformationMessage('Hello World from loghuu!');
});
context.subscriptions.push(disposable);
}
// This method is called when your extension is deactivated
export function deactivate() { }
- 修改代码,获取选中文本
typescript
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
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 "loghuu" is now active!');
let disposable = vscode.commands.registerCommand('loghuu.helloWorld', () => {
// 获取当前活动的编辑器
const editor = vscode.window.activeTextEditor;
if (editor) {
// 获取当前选中的文本
const selectedText = editor.document.getText(editor.selection);
vscode.window.showInformationMessage(`Selected text: ${selectedText}`);
} else {
vscode.window.showInformationMessage('No active editor.');
}
});
context.subscriptions.push(disposable);
}
// This method is called when your extension is deactivated
export function deactivate() { }
- 效果
将获取到的文本插入到下一行
将刚才的代码改为:
typescript
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
console.log('Congratulations, your extension "loghuu" is now active!');
let disposable = vscode.commands.registerCommand('loghuu.helloWorld', () => {
// 获取当前活动的编辑器
const editor = vscode.window.activeTextEditor;
if (editor) {
// 获取当前选中的文本
const selectedText = editor.document.getText(editor.selection);
if (selectedText) {
// 获取选中文本的结束位置
const selectionEnd = editor.selection.end;
// 计算下一行的位置
const nextLine = new vscode.Position(selectionEnd.line + 1, 0);
// 插入新行并输出文本
editor.edit(editBuilder => {
editBuilder.insert(nextLine, selectedText + '\n');
});
vscode.window.showInformationMessage('Inserted text below selected text.');
} else {
vscode.window.showInformationMessage('No text selected.');
}
} else {
vscode.window.showInformationMessage('No active editor.');
}
});
context.subscriptions.push(disposable);
}
export function deactivate() { }
绑定快捷键
- 完善修改代码
typescript
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
console.log('Congratulations, your extension "loghuu" is now active!');
let disposable = vscode.commands.registerCommand('loghuu.consoleLog', () => {
// 获取当前活动的编辑器
const editor = vscode.window.activeTextEditor;
if (editor) {
// 获取当前选中的文本
const selectedText = editor.document.getText(editor.selection);
// 获取当前行的行号
const currentLineNumber = editor.selection.active.line;
// 获取当前行的文本
const currentLineText = editor.document.lineAt(currentLineNumber).text;
// 构造要插入的 console.log 语句
const consoleStatement = selectedText ? `console.log(${selectedText});\n` : `console.log('${currentLineText}');\n`;
// 获取当前光标位置
const currentPosition = editor.selection.active;
// 插入 console.log 语句
editor.edit(editBuilder => {
if (selectedText) {
// 如果有选中文本,则插入到下一行
editBuilder.insert(new vscode.Position(currentLineNumber + 1, 0), consoleStatement);
} else {
// 如果没有选中文本,则插入到当前行
editBuilder.insert(currentPosition, consoleStatement);
}
});
vscode.window.showInformationMessage('Inserted console.log statement.');
}
});
context.subscriptions.push(disposable);
}
export function deactivate() { }
- 打开package.json,绑定快捷键
CTRL + L
json
"contributes": {
"commands": [
{
"command": "loghuu.consoleLog",
"title": "Loghuu"
}
],
"keybindings": [
{
"command": "loghuu.consoleLog",
"key": "ctrl+l",
"mac": "cmd+l",
"when": "editorTextFocus"
}
]
}
光标定位
输出新的一行之后,把光标定位过去,方便后续操作
typescript
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
console.log('Congratulations, your extension "loghuu" is now active!');
let disposable = vscode.commands.registerCommand('loghuu.consoleLog', async () => {
// 获取当前活动的编辑器
const editor = vscode.window.activeTextEditor;
if (editor) {
// 获取当前选中的文本
const selectedText = editor.document.getText(editor.selection);
// 获取当前行的行号
const currentLineNumber = editor.selection.active.line;
// 获取当前行的文本
const currentLineText = editor.document.lineAt(currentLineNumber).text;
// 构造要插入的 console.log 语句
const consoleStatement = selectedText ? `\nconsole.log(${selectedText});` : `console.log('${currentLineText}');`;
// 获取当前光标位置
const currentPosition = editor.selection.active;
// 插入 console.log 语句
await editor.edit(async editBuilder => {
if (selectedText) {
// 如果有选中文本,则插入到下一行
await editBuilder.insert(new vscode.Position(currentLineNumber + 1, 0), consoleStatement);
// 获取括号内的位置
const openBracketPosition = new vscode.Position(currentLineNumber + 1, consoleStatement.indexOf('('));
const closeBracketPosition = new vscode.Position(currentLineNumber + 1, consoleStatement.indexOf(')') - 1);
// 将光标定位到括号内并选中括号内的内容
editor.selection = new vscode.Selection(openBracketPosition, closeBracketPosition);
} else {
// 如果没有选中文本,则插入到当前行
await editBuilder.insert(currentPosition, consoleStatement);
// 获取括号内的位置
const openBracketPosition = new vscode.Position(currentPosition.line, consoleStatement.indexOf('(') + 2);
const closeBracketPosition = new vscode.Position(currentPosition.line, consoleStatement.indexOf(')') - 1);
// 将光标定位到括号内并选中括号内的内容
editor.selection = new vscode.Selection(openBracketPosition, closeBracketPosition);
}
});
vscode.window.showInformationMessage('Inserted console.log statement.');
}
});
context.subscriptions.push(disposable);
}
export function deactivate() { }
快速console的核心功能到这里就都实现了,下面优化一下使用体验
优化体验,随机文字颜色
console是支持修改打印样式的
生成随机颜色
typescript
/**
* 生成随机颜色
*
* @return {*} {string}
*/
function getRandomColor(): string {
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
由于控制台可能是亮色或者暗色的,导致文字不可见或者模糊,需要针对这种情况作出处理:
生成随机颜色,亮度处理
typescript
/**
* 生成亮色随机颜色
*
* @param {number} [maxAttempts=10]
* @return {*} {string}
*/
function getRandomColor(maxAttempts: number = 10): string {
const isLightColor = (color: string): boolean => {
// 解析颜色
const rgb = parseInt(color.substring(1), 16);
const r = (rgb >> 16) & 0xff;
const g = (rgb >> 8) & 0xff;
const b = (rgb >> 0) & 0xff;
// 计算亮度
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
// 判断亮度,可调整阈值
return brightness > 128;
};
let color: string;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
// 生成随机颜色
color = '#' + Math.floor(Math.random() * 16777215).toString(16);
// 如果是亮色,返回颜色
if (isLightColor(color)) {
return color;
}
}
// 如果尝试次数超过限制,返回默认颜色
return '#5662f6';
}
调整光标位置
typescript
// 插入 console.log 语句
await editor.edit(async editBuilder => {
if (selectedText) {
// 如果有选中文本,则插入到下一行
await editBuilder.insert(new vscode.Position(currentLineNumber + 1, 0), consoleStatement);
const secondCommaIndex = consoleStatement.indexOf(',', consoleStatement.indexOf(',') + 1);
const openBracketPosition = new vscode.Position(currentLineNumber + 1, secondCommaIndex + 1);
const closeBracketPosition = new vscode.Position(currentLineNumber + 1, consoleStatement.indexOf(')') - 1);
editor.selection = new vscode.Selection(openBracketPosition, closeBracketPosition);
} else {
// 如果没有选中文本,则插入到当前行
await editBuilder.insert(currentPosition, consoleStatement);
const firstPosition = new vscode.Position(currentPosition.line, consoleStatement.indexOf('[') + 1);
const secondCommaIndex = consoleStatement.indexOf(',', consoleStatement.indexOf(',') + 1);
const openBracketPosition = new vscode.Position(currentPosition.line, secondCommaIndex + 1);
editor.selections = [
new vscode.Selection(openBracketPosition, openBracketPosition),
new vscode.Selection(firstPosition, firstPosition),
];
}
});
继续升级,根据选中文字自动打印错误
写代码的时候经常需要trycatch错误并打印输出展示,但是打包的时候我们想清除所有console.log单保留错误的输出,这时候可以对错误使用console.error:
typescript
// 构造要插入的 console 语句
let consoleStatement: string;
if (selectedText && (selectedText.toLowerCase() === 'error' || selectedText.toLowerCase() === 'err')) {
consoleStatement = `\nconsole.error('%c[${selectedText}]-${currentLineNumber}:', 'color: ${randomColor}', ${selectedText});`;
} else {
consoleStatement = selectedText
? `\nconsole.log('%c[${selectedText}]-${currentLineNumber}:', 'color: ${randomColor}', ${selectedText});`
: `console.log(' %c[]-${currentLineNumber}:', 'color: ${randomColor}',);`;
}
拆分代码,优化结构
由于后面会添加别的功能,对代码进行拆分,优化结构,方便后面的升级优化
typescript
// commands/loghuu.consoleLog.ts
import * as vscode from 'vscode';
import { getRandomColor } from '../utils/getRandomColor';
export async function insertConsoleStatement(editor: vscode.TextEditor) {
if (editor) {
// 获取当前选中的文本
const selectedText = editor.document.getText(editor.selection);
// 获取当前行的行号
const currentLineNumber = editor.selection.active.line;
// 生成随机颜色,最多尝试10次
const randomColor = getRandomColor(10);
// 构造要插入的 console 语句
let consoleStatement: string;
if (selectedText && (selectedText.toLowerCase() === 'error' || selectedText.toLowerCase() === 'err')) {
consoleStatement = `\nconsole.error('%c[${selectedText}]-${currentLineNumber}:', 'color: ${randomColor}', ${selectedText});`;
} else {
consoleStatement = selectedText
? `\nconsole.log('%c[${selectedText}]-${currentLineNumber}:', 'color: ${randomColor}', ${selectedText});`
: `console.log(' %c[]-${currentLineNumber}:', 'color: ${randomColor}',);`;
}
// 获取当前光标位置
const currentPosition = editor.selection.active;
// 插入 console 语句
await editor.edit(async editBuilder => {
if (selectedText) {
// 如果有选中文本,则插入到下一行
await editBuilder.insert(new vscode.Position(currentLineNumber + 1, 0), consoleStatement);
const secondCommaIndex = consoleStatement.indexOf(',', consoleStatement.indexOf(',') + 1);
const openBracketPosition = new vscode.Position(currentLineNumber + 1, secondCommaIndex + 1);
const closeBracketPosition = new vscode.Position(currentLineNumber + 1, consoleStatement.indexOf(')') - 1);
editor.selection = new vscode.Selection(openBracketPosition, closeBracketPosition);
} else {
// 如果没有选中文本,则插入到当前行
await editBuilder.insert(currentPosition, consoleStatement);
const firstPosition = new vscode.Position(currentPosition.line, consoleStatement.indexOf('[') + 1);
const secondCommaIndex = consoleStatement.indexOf(',', consoleStatement.indexOf(',') + 1);
const openBracketPosition = new vscode.Position(currentPosition.line, secondCommaIndex + 1);
editor.selections = [
new vscode.Selection(openBracketPosition, openBracketPosition),
new vscode.Selection(firstPosition, firstPosition),
];
}
});
}
}
typescript
// extension.ts
import * as vscode from 'vscode';
import { insertConsoleStatement } from './commands/consoleLog';
export function activate(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand('loghuu.consoleLog', async () => {
const editor = vscode.window.activeTextEditor;
editor && insertConsoleStatement(editor);
});
context.subscriptions.push(disposable);
}
export function deactivate() { }
Region快速折叠代码
vs code支持region折叠代码,写个指令快速在代码块前后自动加入region
typescript
import * as vscode from 'vscode';
export function addRegionToSelection(): vscode.Disposable {
const disposable = vscode.commands.registerCommand('loghuu.addRegionToSelection', () => {
const editor = vscode.window.activeTextEditor;
if (editor && editor.selections.length > 0) {
editor.edit(editBuilder => {
const [firstSelection] = editor.selections;
const lastSelection = editor.selections.at(-1);
// 在第一个选中文本的前面和最后一个选中文本的后面插入 regionText
editBuilder.insert(firstSelection.start, '// #region\n');
editBuilder.insert(lastSelection!.end, '\n// #endregion');
});
}
});
return disposable;
}
typescript
import * as vscode from 'vscode';
import { insertConsoleStatement } from './commands/consoleLog';
import { addRegionToSelection } from './commands/addRegionToSelection';
export function activate(context: vscode.ExtensionContext) {
const addRegion = addRegionToSelection();
const insertConsole = insertConsoleStatement();
context.subscriptions.push(addRegion);
context.subscriptions.push(insertConsole);
}
export function deactivate() { }
插件: https://marketplace.visualstudio.com/items?itemName=sharebravery.loghuu
- ctrl + l 快速打印
- ctrl + shift + r 快速Region
我后续迭代了不少版本,文章里没有说到,代码也迁移用了antfu大佬的模板:
此插件完整代码在: https://github.com/sharebravery/vscode-loghuu
antfu插件模板:https://github.com/antfu/starter-vscode?tab=readme-ov-file
END