説在前面
最近突然想錄制一些視頻來分享一些代碼,想在視頻裏展示代碼編寫的過程,但是又不想重頭開始再敲一遍代碼,所以便想着能不能寫一個插件來幫我們“敲代碼”🙂
效果展示
如上圖,看着是和手打一樣逐字輸入的效果,但其實是通過vscode插件來自動輸入的,可以用於視頻錄製時重現代碼實現過程。
插件完整演示視頻可以看這裏:
https://www.bilibili.com/video/BV1MqVnzdECu/?vd_source=a9f51d5fb9dff1a8879cb3a9794b86e9
插件使用
插件安裝
插件目前已經發布到了vscode插件應用商城,可以直接在vscode插件中搜索 code-editing-simulation ,如下圖:
安裝完插件之後,在文件編輯區域右鍵打開菜單欄,可以看到模擬代碼輸入這一個選項,鼠標懸浮可以看到子菜單郵有兩個功能。
功能介紹
修改配置
選擇子菜單欄中的修改配置選項,可以修改插件的相關配置,具體配置如下:
- 保存標識
在自動輸入的過程中,識別到保存標識的時候會觸發保存事件,將文件保存,默認保存標識為:@保存@。 比如使用canvas繪製圖像的時候,我們可以在畫完一筆的時候保存一下,這時候可以直觀的看到每一筆的繪製效果。
- 打印速度(毫秒/字符)
字符輸入的速度,默認是50,即每50毫秒輸入一個字符。
模擬代碼輸入
插件核心功能,模擬代碼手打逐字輸入的效果,使用方式主要有以下兩種:
1、在當前光標所在位置全量插入代碼
- (1)首先需要新建一個txt文件,在文件中準備好需要輸入的代碼,如下圖,我們有一個code.txt文件,裏面有幾行代碼。
- (2)在需要插入代碼的地方右鍵選擇模擬代碼輸入,也可以直接使用快捷鍵(Ctrl+Alt+C)
- (3)在彈出的文件選擇框中選擇需要插入的代碼文件,這裏選擇我們前面準備的code.txt
- (4)選擇好文件後,插件就會將文件中的代碼插入到當前光標的位置中去
2、根據標識符在對應位置插入代碼
- (1)同樣需要新建一個txt文件,在文件中準備好需要輸入的代碼,這裏的代碼格式需要進行調整,具體格式如下,每一段代碼都有一箇中括號標籤包着:
- (2)中括號裏的標識需要與原代碼中的標識一致,對應標籤中的代碼會被插到對應標識之後,如下圖:
- (3)在需要插入代碼的文件中右鍵選擇模擬代碼輸入,也可以直接使用快捷鍵(Ctrl+Alt+C)
- (4)在彈出的文件選擇框中選擇需要插入的代碼文件,這裏選擇我們前面準備的index.txt
- (5)選擇好文件後,插件就會將文件中每個標識標籤中的代碼插入到對應標識所在的位置中去
插件實現
在光標所在位置全量插入代碼
輸入參數
- code:要輸入的代碼字符串。
- saveFlag:保存標記,當遇到該標記時保存文件。
- printSpeed:輸入每個字符的時間間隔(毫秒)。
- editor:VS Code 的文本編輯器對象。
- position:開始輸入的位置,默認為當前光標位置。
檢測保存標記
判斷當前遍歷的字符是不是保存標記,是保存標記的話就執行保存操作,並跳過標記內容。
const flagLen = saveFlag.length;
if(i <= code.length - flagLen && code.slice(i, i + flagLen) === saveFlag){
i += flagLen - 1;
await editor.document.save();
continue; // 不插入標記內容
}
保持編輯區域可視
當在模擬輸入代碼的過程中遇到換行符時,需要將光標移動到下一行的行首,並且當行數超過一定數量時,自動滾動編輯器視圖,讓新的行能夠顯示在合適的位置。
const newLine = Math.min(position.line + 1, maxLine);
position = new vscode.Position(newLine, 0);
if(newLine > 20){
const scrollPosition = new vscode.Range(
Math.max(0, newLine - 20),
position.character,
newLine,
position.character
);
editor.revealRange(
scrollPosition,
vscode.TextEditorRevealType.AtTop
);
}
1. 計算新的行號並移動光標到下一行行首
const newLine = Math.min(position.line + 1, maxLine);
position = new vscode.Position(newLine, 0);
position.line代表當前光標的行號。position.line + 1表示下一行的行號。maxLine是編輯器文檔的最大行號(editor.document.lineCount - 1)。使用Math.min(position.line + 1, maxLine)確保新的行號不會超過文檔的最大行號,避免出現越界錯誤。new vscode.Position(newLine, 0)創建了一個新的Position對象,該對象代表新的光標位置,其中newLine是行號,0表示列號(即行首)。最後將新的位置賦值給position變量,從而移動光標到下一行的行首。
2. 判斷是否需要滾動編輯器視圖
if(newLine > 20) {
// ...
}
當新的行號 newLine 大於 20 時,説明當前行已經比較靠下,可能需要滾動編輯器視圖,以便用户能夠看到新輸入的內容。
3. 計算滾動範圍
const scrollPosition = new vscode.Range(
Math.max(0, newLine - 20), // 向上滾動一行
position.character,
newLine,
position.character
);
vscode.Range用於表示編輯器中的一個範圍,它由起始位置和結束位置組成。Math.max(0, newLine - 20)計算滾動範圍的起始行號。確保起始行號不會小於 0,避免出現負數行號的錯誤。這裏newLine - 20表示從當前行向上數 20 行的位置,作為滾動範圍的起始行。position.character表示列號,由於我們只關注行的滾動,所以起始列號和結束列號都使用當前光標的列號。newLine是滾動範圍的結束行號,即當前行。
4. 滾動編輯器視圖
editor.revealRange(
scrollPosition,
vscode.TextEditorRevealType.AtTop
);
editor.revealRange是 VS Code 編輯器提供的一個方法,用於將指定的範圍滾動到可見區域。scrollPosition是要滾動到可見區域的範圍。vscode.TextEditorRevealType.AtTop是一個枚舉值,表示將指定範圍滾動到編輯器視圖的頂部,這樣可以確保新輸入的行顯示在視圖的頂部,方便用户查看。
在模擬輸入代碼遇到換行符時,移動光標到下一行行首,並在行數較多時自動滾動編輯器視圖,保證能夠清晰看到正在輸入的內容。
逐字輸入代碼
const currentLine = editor.document.lineAt(Math.min(position.line,maxLine));
await editor.edit(editBuilder => {
// 清空光標右側內容
const deleteRange = new vscode.Range(
position,
currentLine.range.end
);
editBuilder.delete(deleteRange);
// 插入新字符
editBuilder.insert(position, code[i]);
});
// 更新光標位置
position = position.translate(0, 1);
// 控制輸入速度
await new Promise(resolve =>
setTimeout(resolve, printSpeed));
1. 獲取當前行信息
const currentLine = editor.document.lineAt(Math.min(position.line, maxLine));
editor.document代表當前在編輯器中打開的文檔。lineAt是Document對象的一個方法,用於獲取指定行號的TextLine對象。position.line是當前光標的行號。maxLine是文檔的最大行號(editor.document.lineCount - 1)。使用Math.min(position.line, maxLine)確保傳入lineAt方法的行號不會超出文檔的最大行號,避免出現越界錯誤。- 最終
currentLine存儲的是當前光標所在行的TextLine對象,該對象包含了當前行的文本內容、範圍等信息。
2. 執行編輯操作
await editor.edit(editBuilder => {
// ...
});
editor.edit是 VS Code 編輯器提供的一個方法,用於執行一系列的編輯操作。它接受一個回調函數作為參數,在回調函數中可以使用editBuilder對象來定義具體的編輯操作。await關鍵字用於等待編輯操作完成,確保後續代碼在編輯操作執行完畢後再繼續執行。
3. 清空光標右側內容
const deleteRange = new vscode.Range(
position,
currentLine.range.end
);
editBuilder.delete(deleteRange);
vscode.Range用於表示編輯器中的一個範圍,由起始位置和結束位置組成。position是當前光標的位置,作為刪除範圍的起始位置。currentLine.range.end是當前行的結束位置,作為刪除範圍的結束位置。editBuilder.delete(deleteRange)用於刪除指定範圍內的文本內容,即清空從當前光標位置到當前行末尾的所有內容。
4. 插入新字符
editBuilder.insert(position, code[i]);
editBuilder.insert用於在指定位置插入文本內容。position是插入的位置,即當前光標的位置。code[i]是要插入的字符,code是要輸入的代碼字符串,i是當前遍歷到的字符索引。
5. 更新光標位置
position = position.translate(0, 1);
position.translate是Position對象的一個方法,用於創建一個新的Position對象,該對象相對於當前位置在指定的行和列上進行偏移。0表示在行方向上不進行偏移。1表示在列方向上向右偏移 1 個位置,即更新光標位置到新插入字符的右側。
6. 控制輸入速度
await new Promise(resolve =>
setTimeout(resolve, printSpeed));
new Promise用於創建一個 Promise 對象,resolve是 Promise 的解決函數。setTimeout是 JavaScript 的一個全局函數,用於在指定的時間後執行一個回調函數。printSpeed是輸入每個字符的時間間隔(毫秒)。await關鍵字用於等待setTimeout的回調函數執行完畢,從而實現控制輸入速度的效果,即每隔printSpeed毫秒插入一個字符。
根據標識符在對應位置插入代碼
標識標籤設計
這裏我們使用兩層中括號將一段字符串包起來,作為一個標籤,兩個一樣的標籤中間包含的代碼就是我們要插入的代碼。
將帶標識標籤的文本轉為json格式
function stringToObject(inputString: string) {
const regex = /\[\[([^\]]+)\]\]([\s\S]*?)\[\[\1\]\]/g;
const result: Record<string, string> = {};
let match;
while ((match = regex.exec(inputString))!== null) {
const key = match[1].trim();
const value = match[2];
//去除value開頭和結尾的換行符,保留空格
if(value.startsWith('\n')){
result[key] = value.slice(1);
}else{
result[key] = value;
}
if(value.endsWith('\n')){
result[key] = result[key].slice(0,-1);
}
}
return result;
}
找到標識所在位置並插入代碼
async function typingCodeByKey(key:string,code:string,saveFlag:string,printSpeed:number,editor:vscode.TextEditor){
//獲取key字符串所在的行數
const lineNum = editor.document.getText().split('\n').findIndex((line) => line.trim() === key);
if(lineNum === -1){return;}
let position = new vscode.Position(lineNum, editor.document.lineAt(lineNum).text.length);
await editor.edit(editBuilder => {
editBuilder.insert(position, '\n');
});
await typingCode(code,saveFlag,printSpeed,editor,position);
}
源碼地址
gitee
https://gitee.com/zheng_yongtao/code-editing-simulation.git
github
https://github.com/yongtaozheng/code-editing-simulation.git
- 🌟 覺得有幫助的可以點個 star~
- 🖊 有什麼問題或錯誤可以指出,歡迎 pr~
- 📬 有什麼想要實現的功能或想法可以聯繫我~
公眾號
關注公眾號『 前端也能這麼有趣 』,獲取更多有趣內容。
發送 加羣 還可以加入羣聊,一起來學習(摸魚)吧~
説在後面
🎉 這裏是 JYeontu,現在是一名前端工程師,有空會刷刷算法題,平時喜歡打羽毛球 🏸 ,平時也喜歡寫些東西,既為自己記錄 📋,也希望可以對大家有那麼一丟丟的幫助,寫的不好望多多諒解 🙇,寫錯的地方望指出,定會認真改進 😊,偶爾也會在自己的公眾號『前端也能這麼有趣』發一些比較有趣的文章,有興趣的也可以關注下。在此謝謝大家的支持,我們下文再見 🙌。