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