vscode是個超級好用的開發工具,誰用誰知道。javascript
一個項目開發、維護的時間久了以後,總會多多少少碰到一段不是你寫的,而如今你要維護,但你卻看不明白的那是什麼鬼的代碼;固然有時候也多是多人在同一個項目裏協做開發時,對於bug最終責任人的確診問題(找到最終責任人不是要「修理他」。幫助他認識問題,提升自身能力,加深團隊協做意識才是重點)。java
大多數人都用git
,也知道git裏諸如:git log
,git blame
等命令均可以幫咱們作到以上需求,可每次看到一段代碼以後,先從IDE裏切換到Terminal下,而後敲打命令,而且要記好事故代碼的行號,以及準確的文件路徑,敲來敲去仍是挺煩人的。node
因而,能在不來回切換工做環境的狀況下,迅速找到某一段代碼的做者、寫做時間、寫做目的就顯的仍是有點用了。git
爲了更直觀表達這個擴展的設計思路,我用了一個圖:github
後面咱們就根據圖裏描述的思路來開展工做。typescript
要獲得一段指定行號(行區間)代碼的信息,你們都知道用git blame
,展現結果以下:npm
»git blame -L 10,11 js/index.js 297cb0df (Howard.Zuo 2017-03-08 21:58:03 +0800 10) render(h) { 297cb0df (Howard.Zuo 2017-03-08 21:58:03 +0800 11) return h(Game);
少了,"寫做目的",也就是說,這個命令的結果沒法告訴咱們做者提交這段代碼時的commit message
寫了什麼。因而我得翻翻git blame --help
,可喜的是被我找到了--line-porcelain
選項,在她的描述裏看到了這麼一句:but output commit information for each line
。好像有戲,來試試看:json
»git blame -L 10,11 js/index.js --line-porcelain 297cb0df8ab1fe06ee935798d1a2dd4e712a070d 10 10 2 author Howard.Zuo author-mail <leftstick@qq.com> author-time 1488981483 author-tz +0800 committer Howard.Zuo committer-mail <leftstick@qq.com> committer-time 1488981503 committer-tz +0800 summary fix duplicate key issue previous 43966e5cc4c70998c265781fe8acf02946c28f6e js/index.js filename js/index.js render(h) { 297cb0df8ab1fe06ee935798d1a2dd4e712a070d 11 11 author Howard.Zuo author-mail <leftstick@qq.com> author-time 1488981483 author-tz +0800 committer Howard.Zuo committer-mail <leftstick@qq.com> committer-time 1488981503 committer-tz +0800 summary fix duplicate key issue previous 43966e5cc4c70998c265781fe8acf02946c28f6e js/index.js filename js/index.js return h(Game);
不錯,這明顯是個能夠被解析的數據結構。因此獲取信息就靠她了git blame -L <start,end> <filePath> --line-porcelain
。api
由於vscode是一個基於electron開發的IDE,因此node.js
的API對咱們是可用的,因而能夠經過以下代碼執行上面的命令,而且拿到結果數據:數據結構
//vscode api,獲取當前正在操做的編輯頁面 const editor = vscode.window.activeTextEditor; if (!editor) { vscode.window.showWarningMessage('You have to active a file first'); return; } //拿到鼠標選擇的內容 const selection = editor.selection; //合成git命令 const cmd = `git blame -L ${selection.start.line + 1},${selection.end.line + 1} ${editor.document.fileName} --line-porcelain`; //經過child_press執行該命令, child_process.exec(cmd, { cwd: vscode.workspace.rootPath }, (error, stdout, stderr) => { //這裏的stdout就是上面咱們看到的輸出內容了 });
看了上面的輸出內容,咱們須要一個model來描述一個這樣一個條目:
export interface Item { hash: string; shortHash: string; author: string; authorEmail: string; authorTime: number; authorTz: string; committer: string; committerEmail: string; committerTime: number; committerTz: string; commitMessage: string; previousCommit?: string; fileName: string; change: string; }
接下來就是如何解析git
命令的輸出結果了,一個大大的循環來搞定:
export function parse(output: string): Array<Item> { const lines = output.replace(/\r\n/mg, '\n').split('\n'); const commits: Array<Item> = []; let commit; for (let i = 0; i < lines.length - 1; i++) { const line = lines[i]; //一個item的開始標誌就是commit的hash if (/^[a-z0-9]{15,}/.test(line)) { commit = {}; commits.push(commit); commit.hash = line.split(' ')[0]; commit.shortHash = commit.hash.substring(0, 8); } else if (/^author\s/.test(line)) { commit.author = line.split(' ')[1]; } else if (/^author-mail\s/.test(line)) { commit.authorEmail = line.split(' ')[1]; } else if (/^author-time\s/.test(line)) { commit.authorTime = +line.split(' ')[1]; } else if (/^author-tz\s/.test(line)) { commit.authorTz = line.split(' ')[1]; } else if (/^committer\s/.test(line)) { commit.committer = line.split(' ')[1]; } else if (/^committer-mail\s/.test(line)) { commit.committerEmail = line.split(' ')[1]; } else if (/^committer-time\s/.test(line)) { commit.committerTime = +line.split(' ')[1]; } else if (/^committer-tz\s/.test(line)) { commit.committerTz = line.split(' ')[1]; } else if (/^previous\s/.test(line)) { commit.previousCommit = line.split(' ')[1]; } else if (/^filename\s/.test(line)) { commit.fileName = line.split(' ')[1]; } else if (!commit.fileName) { commit.commitMessage = line; } else { commit.change = line; } } return commits; }
有了上面解析完畢的Array<Item>
,簡單轉換一下字符串,對任何人都是沒什麼難度的:
export function pretty(commits: Array<Item>): string { return commits.map(c => { return `${c.shortHash} ${c.author} ${formatDate(c.authorTime * 1000)} "${c.commitMessage}" ${c.change}`; }) .join('\n'); }
解決了全部核心問題,剩下的就是按照vscode-docs,動手補充一個擴展須要的額外因素了。
npm install -g yo generator-code
yo code
在提問中,依次回答全部問題,最後項目骨架生成。
修改package.json
,增長/修改contributes
字段:
"contributes": { "menus": { "editor/context": [{ "command": "extension.gitblame", "group": "sourcecontrol", "when": "config.git.enabled && scmProvider == git && gitState == idle" }] }, "commands": [{ "command": "extension.gitblame", "title": "Git blame", "key": "ctrl+b" }], "keybindings": [{ "command": "extension.gitblame", "key": "alt+b" }] }
因而乎,一個右鍵選項Git blame就會出如今你選擇一段代碼後的右鍵菜單裏
一個擴展根據需求,並不必定要隨vscode
的啓動而啓動,那樣影響vscode
的總體性能,並且沒什麼特別的好處。因此咱們要懶加載。修改package.json
,增長/修改activationEvents
字段:
"activationEvents": [ "onCommand:extension.gitblame" ]
只有當用戶使用
extension.gitblame
這個命令時(也就是在右鍵菜單裏選擇了Git blame時),該擴展才正式激活。
到此爲止,這個擴展就基本完成了。效果以下:
擴展下載地址:vscode-git-blamer
項目源碼地址:vscode-git-blamer