入坑VS Code
前,我已是一名久經考驗的Emacs
老用戶了,所以開始正式使用VS Code
後,我第一時間啓用了它的Emacs Keymap
。但不久我便發現,這套鍵映射缺乏一個重要的快捷鍵——ctrl-l
。git
在Emacs
中,ctrl-l
對應的命令是recenter-top-bottom
,它用於將光標所在的行輪替地滾動到可視區域(即Emacs
中的window
)的中間、頂部,以及底部(以下圖所示)github
這是我高頻使用的一個功能,尤爲是跳轉到函數的定義的首行後,我習慣於連按兩次,將其滾動到window
的頂部以便在一屏中看到儘可能多的內容。typescript
爲了不重複發明輪子,我先搜索了一番,找到了一個宣稱實現了該功能的擴展Recenter Top Bottom
。惋惜的是,安裝後並不生效。shell
難道只能委屈本身用鼠標當心翼翼地將光標所在行滾到頂部了嗎?固然不是。既然沒有開箱即用的,那便本身寫一個VS Code
的擴展實現這個功能吧。npm
要想入門VS Code
擴展的開發,官方便提供了一份不錯的教程。一個擴展有許多的「八股文」代碼,能夠用yo
和generator-code
來快速生成json
npm install -g yo generator-code yo code
到這裏,便獲得了一個名爲helloworld
的目錄了。用VS Code
打開它,接下來要在其中大展身手。api
VS Code
擴展的核心邏輯定義在文件src/extension.ts
中。在yo
生成的示例代碼中,用registerCommand
註冊了一個名爲helloworld.helloWorld
的命令,其邏輯是簡單地在右下角彈出一句Hello VS Code from HelloWorld!
。這個回調函數,即是業務邏輯的落腳點。編輯器
要想實現將光標所在行滾動到中間的功能,首先要知道VS Code
爲開發者提供了哪些支持。在摸索了一通從VS Code
的API文檔
後,我有了如下的線索:函數
vscode.window.activeTextEditor
能夠取得當前聚焦的編輯器——其值可能爲空(undefined
);TextEditor
實例的屬性.selection.active
能夠取得當前光標的位置;TextEditor
實例有一個方法revealRange
能夠滾動文原本改變展現的範圍,它須要一個vscode.Range
類的實例,以及一個vscode.TextEditorRevealType
類型的枚舉值;vscode.TextEditorRevealType.InCenter
的效果是將所給定的範圍展現在中間,vscode.TextEditorRevealType.AtTop
則是置頂。有了這些知識儲備,實現這樣的一個回調函數即是信手拈來的事情了spa
function recenterTop() { const editor = vscode.window.activeTextEditor; if (!editor) { return; } const cursorPosition = editor.selection.active; editor.revealRange(new vscode.Range(cursorPosition, cursorPosition), vscode.TextEditorRevealType.InCenter); }
因爲暫時沒有配置該命令的快捷鍵,只能用VS Code
的命令面板來調用
接下來我將實現連續調用兩次helloworld.helloWorld
命令,把光標所在行滾動到頂部的效果。在Emacs
中,能夠很輕鬆地知道一個命令是否被連續運行——Emacs
有一個名爲last-command
的變量存儲着上一個命令的名稱,只須要檢查其是否等於recenter-top-bottom
便可。但VS Code
沒有暴露這麼強大的功能,只能另闢蹊徑。
個人策略是,若是調用helloworld.helloWorld
時光標的位置,與上一次調用該命令時的位置相同,就認爲是連續調用。爲此,須要兩個在函數recenterTop
以外定義的變量:
previousPosition
負責記錄上一次調用recenterTop
時光標的位置,它的初始值爲null
;revealType
存儲着上一次調整展現範圍時傳遞給TextEditor
實例的revealRange
方法的第二個參數的值,它的初始值也爲null
。個人目標是儘可能模擬Emacs
中的recenter-top-bottom
所具有的、交替使用居中、置頂效果的特色,所以:
revealType
爲null
,意味着這是第一次調用recenterTop
,那麼效果即是居中。不然;recenterTop
後調用過其它命令,效果依然是居中。不然;revealType
已是居中了,就改成置頂。不然;revealType
改成居中。Talk is cheap. Show me the code.
let previousPosition: null|vscode.Position = null; let revealType: null|vscode.TextEditorRevealType = null; function recenterTop() { const editor = vscode.window.activeTextEditor; if (!editor) { return; } const cursorPosition = editor.selection.active; if (!revealType) { revealType = vscode.TextEditorRevealType.InCenter; } else if (previousPosition && !cursorPosition.isEqual(previousPosition)) { revealType = vscode.TextEditorRevealType.InCenter; } else if (revealType === vscode.TextEditorRevealType.InCenter) { revealType = vscode.TextEditorRevealType.AtTop; } else { revealType = vscode.TextEditorRevealType.InCenter; } previousPosition = cursorPosition; editor.revealRange(new vscode.Range(cursorPosition, cursorPosition), revealType); }
經過命令面板來使用不是個人最終目標,經過快捷鍵纔是。根據VS Code
的文檔能夠知道,只要在package.json
的contributes
對象中,新增名爲keybindings
的屬性,並定義命令及按鍵序列便可。
{ // 此處省略其它沒必要要的屬性 "contributes": { "keybindings":{ // 新增屬性 "command": "helloworld.helloWorld", "key": "ctrl+l" } } }
若是看過我以前的文章《手指疼,寫點代碼緩解一下》的讀者應當會記得,我已經從Emacs Keymap
「叛逃」到了Vim Keymap
了。因此,我並無真正用上上述的VS Code
擴展。相反,目前高頻使用的是Vim Keymap
內置的z-.
以及z-↵
了——前者用於垂直居中,後者用於置頂。
愛護手指,從使用Vim Keymap
作起。