├── .gitignore ├── doc ├── vim-github-comment.gif └── github-comment.txt ├── LICENSE ├── README.md └── plugin └── github-comment.vim /.gitignore: -------------------------------------------------------------------------------- 1 | /doc/tags 2 | -------------------------------------------------------------------------------- /doc/vim-github-comment.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmozuras/vim-github-comment/HEAD/doc/vim-github-comment.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2015 Mindaugas Mozūras 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vim-github-comment 2 | 3 | Want to comment commits on [GitHub] straight from [Vim]? You can now. After [installation](#installation), from inside vim, just run `:GHComment `. This will create a comment on the commit that changed that line last. 4 | 5 | ![vim-github-comment demo](/doc/vim-github-comment.gif "") 6 | 7 | ## Installation 8 | 9 | I use [pathogen], but you can install [vim-github-comment] using your favorite way too. Short instructions to use with pathogen follow. 10 | 11 | Inside your .vim directory, run: 12 | 13 | git clone git://github.com/mmozuras/vim-github-comment.git bundle/vim-github-comment 14 | 15 | vim-github-comment requires [webapi], so if you don't have it in your bundle yet: 16 | 17 | git clone git://github.com/mattn/webapi-vim.git bundle/webapi 18 | 19 | ## Setup 20 | 21 | If you don't have `github.user` set in your `.gitconfig`, add the following 22 | into your ~/.vimrc 23 | 24 | let g:github_user = '' 25 | 26 | Optionally, browser can be opened with your comment after you post it: 27 | 28 | let g:github_comment_open_browser = 1 29 | 30 | You can always run `help` to get more information about vim-github-comment: 31 | 32 | :help github-comment 33 | 34 | [vim-github-comment]://github.com/mmozuras/vim-github-comment 35 | [webapi]://github.com/mattn/webapi-vim 36 | [pathogen]://github.com/tpope/vim-pathogen 37 | [Vim]:http://www.vim.org 38 | [GitHub]://github.com 39 | -------------------------------------------------------------------------------- /doc/github-comment.txt: -------------------------------------------------------------------------------- 1 | *github-comment* Vimscript for commenting on GitHub from Vim 2 | 3 | Usage |github-comment-usage| 4 | Install |github-comment-install| 5 | Requirements |github-comment-requirements| 6 | Setup |github-comment-setup| 7 | 8 | This is a vimscript to comment commits on GitHub using Vim. 9 | 10 | For the latest version please see 11 | https://github.com/mmozuras/vim-github-comment. 12 | 13 | ============================================================================== 14 | USAGE *:GHComment* *github-comment-usage* 15 | 16 | - Write a comment for the current line. > 17 | 18 | :GHComment 19 | < 20 | ============================================================================== 21 | INSTALL *github-comment-install* 22 | 23 | Copy following files into your plugin directory. 24 | 25 | rtp: 26 | - plugin/github-comment.vim 27 | 28 | You can also use other ways of installation. 29 | 30 | You need to install webapi-vim also: 31 | 32 | https://github.com/mattn/webapi-vim 33 | 34 | ============================================================================== 35 | REQUIREMENTS *github-comment-requirements* 36 | 37 | - git command-line client 38 | - curl command (http://curl.haxx.se/) 39 | - webapi-vim (https://github.com/mattn/webapi-vim) 40 | 41 | ============================================================================== 42 | SETUP *github-comment-setup* 43 | 44 | If you don't have `github.user` set in your `.gitconfig`, add the following 45 | into your ~/.vimrc 46 | > 47 | let g:github_user = '' 48 | < 49 | 50 | Optionally, browser can be opened with your comment after you post it 51 | > 52 | let g:github_comment_open_browser = 1 53 | < 54 | -------------------------------------------------------------------------------- /plugin/github-comment.vim: -------------------------------------------------------------------------------- 1 | " github-comment: Make GitHub comments straight from within Vim 2 | " Author: mmozuras 3 | " HomePage: https://github.com/mmozuras/vim-github-comment 4 | " Readme: https://github.com/mmozuras/vim-github-comment/blob/master/README.md 5 | " Version: 0.0.4 6 | 7 | let s:tokenfile = expand('~/.github-comment') 8 | 9 | if !exists('g:github_user') && !executable('git') 10 | echohl ErrorMsg | echomsg "github-comment requires 'git'" | echohl None 11 | finish 12 | endif 13 | 14 | let s:system = function(get(g:, 'webapi#system_function', 'system')) 15 | 16 | if !exists('g:github_user') 17 | let g:github_user = substitute(s:system('git config --get github.user'), "\n", '', '') 18 | if strlen(g:github_user) == 0 19 | let g:github_user = $GITHUB_USER 20 | end 21 | endif 22 | 23 | if strlen(g:github_user) == 0 24 | echohl ErrorMsg | echomsg "github-comment requires a github account. Set g:github_user or github.user in .gitconfig." | echohl None 25 | finish 26 | endif 27 | 28 | if !executable('curl') 29 | echohl ErrorMsg | echomsg "github-comment requires 'curl'" | echohl None 30 | finish 31 | endif 32 | 33 | " webapi uses autoload to define its functions, so they don't exist until they 34 | " are called. 35 | silent! call webapi#json#decode('{}') 36 | if !exists('*webapi#json#decode') 37 | echohl ErrorMsg | echomsg "github-comment requires webapi (https://github.com/mattn/webapi-vim)" | echohl None 38 | finish 39 | endif 40 | 41 | com! -nargs=+ GHComment call GHComment() 42 | 43 | function! GHComment(body) 44 | let auth = s:GetAuthHeader() 45 | if len(auth) == 0 46 | echohl ErrorMsg | echomsg "github-comment auth failed" | echohl None 47 | return 48 | endif 49 | 50 | let repo = s:GitHubRepository() 51 | let commit_sha = s:CommitShaForCurrentLine() 52 | let path = s:GetRelativePathOfBufferInRepository() 53 | let diff_position = s:GetDiffLineNumber(commit_sha, path) 54 | let comment = a:body 55 | let save_view = winsaveview() 56 | 57 | let response = s:CommentOnGitHub(auth, repo, commit_sha, path, diff_position, comment) 58 | 59 | if response.status == 201 60 | let body = webapi#json#decode(response.content) 61 | let html_url = body['html_url'] 62 | if get(g:, 'github_comment_open_browser', 0) == 1 63 | call s:OpenBrowser(html_url) 64 | endif 65 | 66 | echomsg "Comment created: ".html_url 67 | else 68 | echohl ErrorMsg | echomsg "Could not create comment. You may not have the rights." | echohl None 69 | endif 70 | 71 | call winrestview(save_view) 72 | endfunction 73 | 74 | function! s:CommentOnGitHub(auth, repo, commit_sha, path, diff_position, comment) 75 | let request_uri = 'https://api.github.com/repos/'.a:repo.'/commits/'.a:commit_sha.'/comments' 76 | 77 | let response = webapi#http#post(request_uri, webapi#json#encode({ 78 | \ "sha" : a:commit_sha, 79 | \ "path" : a:path, 80 | \ "position" : a:diff_position, 81 | \ "body" : a:comment 82 | \}), { 83 | \ "Authorization": a:auth, 84 | \ "Content-Type": "application/json", 85 | \}) 86 | 87 | return response 88 | endfunction 89 | 90 | function! s:GitHubRepository() 91 | let cmd = 'git ls-remote --get-url' 92 | let remote = system(cmd) 93 | 94 | let name = split(remote, 'git://github\.com/')[0] 95 | let name = split(name, 'git@github\.com:')[0] 96 | let name = split(name, '\.git')[0] 97 | let name = substitute(name, '\n\+$', '', '') 98 | 99 | return name 100 | endfunction 101 | 102 | function! s:CommitShaForCurrentLine() 103 | let linenumber = line('.') 104 | let path = expand('%:p') 105 | 106 | let cmd = 'git blame HEAD -L'.linenumber.','.linenumber.' --porcelain '.path 107 | let blame_text = system(cmd) 108 | 109 | return matchstr(blame_text, '\w\+') 110 | endfunction 111 | 112 | function! s:GetDiffLineNumber(commit_sha, path) 113 | let line = getline('.') 114 | let cmd = 'git show --oneline '.a:commit_sha.' '.a:path.' | grep -nFx '.shellescape('+'.line) 115 | let diff_text = system(cmd) 116 | let split_line = split(diff_text, ':') 117 | return split_line[0] - 6 118 | endfunction 119 | 120 | function! s:GetAuthHeader() 121 | let token = "" 122 | if filereadable(s:tokenfile) 123 | let token = join(readfile(s:tokenfile), "") 124 | endif 125 | if len(token) > 0 126 | return token 127 | endif 128 | 129 | let password = inputsecret("GitHub password for ".g:github_user.": ") 130 | if len(password) > 0 131 | let authorization = s:Authorize(password) 132 | 133 | if has_key(authorization, 'token') 134 | let token = printf("token %s", authorization.token) 135 | execute s:WriteToken(token) 136 | elseif has_key(authorization, 'message') 137 | redraw 138 | echohl ErrorMsg | echomsg authorization.message | echohl None 139 | endif 140 | endif 141 | 142 | return token 143 | endfunction 144 | 145 | function! s:WriteToken(token) 146 | call writefile([a:token], s:tokenfile) 147 | call system("chmod go= ".s:tokenfile) 148 | echomsg printf(" -> wrote token to %s", s:tokenfile) 149 | endfunction 150 | 151 | function! s:Authorize(password) 152 | let auth = printf("basic %s", webapi#base64#b64encode(g:github_user.":".a:password)) 153 | 154 | let auth_url = 'https://api.github.com/authorizations' 155 | let data = webapi#json#encode({ "scopes" : ["repo"], "note" : "vim-github-comment Authorization" }) 156 | let header = { "Content-Type" : "application/json", "Authorization" : auth } 157 | 158 | let response = webapi#http#post(auth_url, data, header) 159 | 160 | let otp = filter(response.header, 'stridx(v:val, "X-GitHub-OTP:") == 0') 161 | if len(otp) 162 | let otp_token = inputsecret("GitHub authentication token: ") 163 | let header["X-GitHub-OTP"] = otp_token 164 | let response = webapi#http#post(auth_url, data, header) 165 | endif 166 | 167 | return webapi#json#decode(response.content) 168 | endfunction 169 | 170 | function! s:GetRelativePathOfBufferInRepository() 171 | let buffer_path = expand("%:p") 172 | let git_dir = s:GetGitTopDir()."/" 173 | 174 | return substitute(buffer_path, git_dir, "", "") 175 | endfunction 176 | 177 | function! s:GetGitTopDir() 178 | let buffer_path = expand("%:p") 179 | let buf = split(buffer_path, "/") 180 | 181 | while len(buf) > 0 182 | let path = "/".join(buf, "/") 183 | 184 | if empty(finddir(path."/.git")) 185 | call remove(buf, -1) 186 | else 187 | return path 188 | endif 189 | endwhile 190 | 191 | return "" 192 | endfunction 193 | 194 | function! s:OpenBrowser(url) 195 | if has('win32') || has('win64') 196 | let cmd = '!start rundll32 url.dll,FileProtocolHandler '.shellescape(a:url) 197 | silent! exec cmd 198 | elseif has('mac') || has('macunix') || has('gui_macvim') 199 | let cmd = 'open '.shellescape(a:url) 200 | call system(cmd) 201 | elseif executable('xdg-open') 202 | let cmd = 'xdg-open '.shellescape(a:url) 203 | call system(cmd) 204 | elseif executable('firefox') 205 | let cmd = 'firefox '.shellescape(a:url).' &' 206 | call system(cmd) 207 | else 208 | echohl WarningMsg | echomsg "That's weird. It seems that you don't have a web browser." | echohl None 209 | end 210 | endfunction 211 | --------------------------------------------------------------------------------