Repository: tyru/open-browser-github.vim
Branch: master
Commit: ac7c034e300f
Files: 12
Total size: 47.4 KB
Directory structure:
gitextract_urosfd0w/
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── autoload/
│ ├── openbrowser/
│ │ └── github.vim
│ ├── vital/
│ │ ├── _open_browser_github/
│ │ │ ├── Data/
│ │ │ │ └── List.vim
│ │ │ └── System/
│ │ │ └── Filepath.vim
│ │ ├── _open_browser_github.vim
│ │ └── open_browser_github.vital
│ └── vital.vim
├── doc/
│ └── openbrowser-github.txt
└── plugin/
└── openbrowser/
└── github.vim
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
doc/tags
================================================
FILE: LICENSE
================================================
BSD 3-Clause License
Copyright (c) 2013, Takuya Fujiwara
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: Makefile
================================================
release:
git archive HEAD plugin autoload doc --output=open-browser-github-$(shell git describe --tags HEAD).zip
.PHONY: release
================================================
FILE: README.md
================================================
# open-browser-github.vim
## About
Opens GitHub URL of current file, etc. from Vim.
Also supports GitHub Enterprise.
## Install
This plugin requires:
* [open-browser.vim](https://github.com/tyru/open-browser.vim)
* `git` command in your PATH
## Usage
There are 4 commands.
### `:OpenGithubFile`
Opens a specific file in github.com repository(it also opens in the current branch by default).
```vimL
" Opens current files URL in github.com
:OpenGithubFile
" Opens current files highlighted place in github.com
:'<,'>OpenGithubFile
" Opens a specific file in github.com
:OpenGithubFile PATH/TO/FILE
```
### `:OpenGithubIssue`
Opens a specific Issue.
```vimL
" Opens current repositories Issue #1
:OpenGithubIssue 1
" Opens a specific repositories Issue #1
:OpenGithubIssue 1 tyru/open-browser.vim
" Opens current repositories Issue List
:OpenGithubIssue
" Opens a specific repositories Issue list
:OpenGithubIssue tyru/open-browser.vim
```
### `:OpenGithubPullReq`
This command opens `/pulls` page when it has no argument. Otherwise, it does entirely the same thing as `:OpenGithubIssue` since GitHub redirects `/issues/1` to `/pull/1` if #1 is a Pull Request.
### `:OpenGithubProject`
Opens a project page.
```vimL
" Opens current opening file's repository.
" ex) https://{hostname}/{user}/{name}
:OpenGithubProject
" Opens current opening file's repository.
" ex) https://{hostname}/tyru/open-browser.vim
:OpenGithubProject tyru/open-browser.vim
```
## GitHub Enterprise setting
### If you have `hub` command
If you have [hub command](https://github.com/github/hub) in your PATH,
`openbrowser-github` executes the following command:
```
hub browse -u -- {path}
```
And it will open the returned (output) URL.
### If you _don't_ have `hub` command
If you don't have `hub` command in your PATH, `openbrowser-github` tries to
get each part of URL from the following gitconfig key:
* hub.host
You can specify GitHub Enterprise repository URL by setting above keys in
gitconfig.
For example, you can set `hub.host` by executing the following command in your
git repository which you want to specify GitHub Enterprise repository URL.
```
git config --local hub.host my.git.org
```
================================================
FILE: autoload/openbrowser/github.vim
================================================
" vim:foldmethod=marker:fen:
scriptencoding utf-8
" Saving 'cpoptions' {{{
let s:save_cpo = &cpo
set cpo&vim
" }}}
let s:V = vital#of('open_browser_github')
let s:Filepath = s:V.import('System.Filepath')
let s:List = s:V.import('Data.List')
unlet s:V
function! openbrowser#github#load() abort
" dummy function to load this script.
endfunction
function! openbrowser#github#file(args, rangegiven, firstlnum, lastlnum) abort
let file = s:resolve(expand(get(a:args, 0, '%')))
let worktree = s:lookup_git_worktree(file)
let lastlnum = max([a:firstlnum, a:lastlnum]) " a:firstlnum could be bigger in NeoVim
call s:call_with_temp_dir(worktree, 's:cmd_file', [a:args, a:rangegiven, a:firstlnum, lastlnum])
endfunction
" Opens a specific file in github.com repository.
function! s:cmd_file(args, rangegiven, firstlnum, lastlnum) abort
let rangegiven = a:rangegiven || get(g:, 'openbrowser_github_select_current_line', 0)
let [path, err] = s:parse_cmd_file_args(a:args, rangegiven, a:firstlnum, a:lastlnum)
if err !=# ''
call s:error(err)
return
endif
if executable('hub')
let url = s:hub('browse', '-u', '--', path)
else
let [url, err] = s:get_url_from_git({'path': path})
if err !=# ''
call s:error(err)
return
endif
endif
if !s:url_exists(url) && input(
\ "Maybe you are opening a URL which is not git-push'ed yet. OK?[y/n]: "
\) !~? '^\%[YES]$'
" TODO: Retry
return
endif
return openbrowser#open(url)
endfunction
" * :OpenGithubFile [{path}]
function! s:parse_cmd_file_args(args, rangegiven, firstlnum, lastlnum) abort
let file = s:resolve(expand(empty(a:args) ? '%' : a:args[0]))
if !filereadable(file)
if a:0 is 0
return ['', 'current buffer is not a file.']
else
return ['', printf('''%s'' is not readable.', file)]
endif
endif
let relpath = s:get_repos_relpath(file)
if g:openbrowser_github_always_used_branch !=# ''
let branch = g:openbrowser_github_always_used_branch
elseif g:openbrowser_github_always_use_commit_hash
let branch = s:git('rev-parse', 'HEAD')
else
" When working tree is detached state,
" branch becomes commit hash.
let head_ref = s:git('symbolic-ref', '--short', '-q', 'HEAD')
let is_detached_state = (head_ref ==# '')
if is_detached_state
let branch = s:git('rev-parse', 'HEAD')
else
let branch = head_ref
endif
endif
if a:rangegiven
let lnum = '#L'.a:firstlnum
\ .(a:firstlnum is a:lastlnum ? '' : '-L'.a:lastlnum)
else
let lnum = ''
endif
" Check input values.
if branch ==# ''
return ['', 'Could not detect current branch name.']
endif
if relpath ==# ''
return ['', 'Could not detect relative path of repository.']
endif
let path = 'blob/' . branch . '/' . relpath . lnum
return [path, '']
endfunction
function! s:get_url_from_git(repoinfo) abort
let host = s:get_github_host()
let user = get(a:repoinfo, 'user', '')
let repos = get(a:repoinfo, 'repos', '')
let path = get(a:repoinfo, 'path', '')
if user ==# '' || repos ==# ''
" May prompt user to choose which repos is used.
try
let github_repos =
\ s:detect_github_repos_from_git_remote(host)
catch /^INVALID INDEX$/
return ['', 'canceled or invalid GitHub URL was selected.']
endtry
let user = get(github_repos, 'user', '')
let repos = get(github_repos, 'repos', '')
endif
if user ==# ''
return ['', 'Could not detect repos user.']
endif
if repos ==# ''
return ['', 'Could not detect current repos name on github.']
endif
let url = 'https://' . host . '/' . user . '/' . repos . '/' . path
return [url, '']
endfunction
function! s:url_exists(url) abort
if g:openbrowser_github_url_exists_check ==# 'ignore'
return 1
endif
if !executable('curl')
call s:warn('You must have ''curl'' command to check whether the opening URL exists.')
call s:warn('You can suppress this check by writing the following '
\ . 'config in your vimrc (:help g:openbrowser_github_url_exists_check).')
call s:warn(' let g:openbrowser_github_url_exists_check = ''ignore''')
call input('Press ENTER to continue...')
return 1
endif
let cmdline = 'curl -k -LI "' . a:url . '"'
let headers = split(system(cmdline), '\n')
if v:shell_error
call s:warn(cmdline)
call s:warn('curl returned error code: ' . v:shell_error)
return 1
endif
let re = '^status:'
let status_line = get(filter(headers, 'v:val =~? re'), 0, '')
if status_line ==# ''
call s:warn(cmdline)
call s:warn('curl received a response without ''Status'' header.')
return 1
endif
return status_line =~? '^status:\s*2'
endfunction
let s:TYPE_ISSUE = 0
function! openbrowser#github#issue(args) abort
let file = expand('%')
let worktree = s:lookup_git_worktree(file)
call s:call_with_temp_dir(worktree, 's:cmd_open_url', [a:args, s:TYPE_ISSUE])
endfunction
let s:TYPE_PULLREQ = 1
function! openbrowser#github#pullreq(args) abort
let file = expand('%')
let worktree = s:lookup_git_worktree(file)
call s:call_with_temp_dir(worktree, 's:cmd_open_url', [a:args, s:TYPE_PULLREQ])
endfunction
let s:TYPE_PROJECT = 2
function! openbrowser#github#project(args) abort
let file = expand('%')
let worktree = s:lookup_git_worktree(file)
call s:call_with_temp_dir(worktree, 's:cmd_open_url', [a:args, s:TYPE_PROJECT])
endfunction
let s:TYPE_COMMIT = 3
function! openbrowser#github#commit(args) abort
let file = expand('%')
let worktree = s:lookup_git_worktree(file)
call s:call_with_temp_dir(worktree, 's:cmd_open_url', [a:args, s:TYPE_COMMIT])
endfunction
" Opens a specific Issue/Pullreq/Project.
function! s:cmd_open_url(...) abort
try
let repoinfo = call('s:parse_cmd_open_url_args', a:000)
catch
call s:error(v:exception)
return
endtry
if executable('hub')
let url = s:hub('browse', '-u', '--', repoinfo.path)
else
let [url, err] = s:get_url_from_git(repoinfo)
if err !=# ''
call s:error(err)
return
endif
endif
return openbrowser#open(url)
endfunction
" * :OpenGithubIssue
" * :OpenGithubIssue [#]{number} [{user}/{repos}]
" * :OpenGithubIssue {user}/{repos}
" * :OpenGithubPullReq
" * :OpenGithubPullReq [#]{number} [{user}/{repos}]
" * :OpenGithubPullReq #{branch} [{user}/{repos}]
" * :OpenGithubPullReq {user}/{repos}
" * :OpenGithubProject [{user}/{repos}]
" * :OpenGithubCommit {commit hash} [{user}/{repos}]
function! s:parse_cmd_open_url_args(args, type) abort
if a:type ==# s:TYPE_ISSUE
" ex) '#1', '1'
let nr = matchstr(get(a:args, 0, ''), '^#\?\zs\d\+\ze$')
if nr !=# ''
let path = 'issues/' . nr
else
let path = 'issues'
endif
" If the argument of repository was given and valid format, get user and repos.
let idx = nr ==# '' ? 0 : 1
let m = matchlist(get(a:args, idx, ''),
\ '^\([^/]\+\)/\([^/]\+\)$')
let [user, repos] = !empty(m) ? m[1:2] : ['', '']
elseif a:type ==# s:TYPE_PULLREQ
" ex) '#1', '1', '#branch_name_of_pull_request'
let nr_or_branch = matchstr(get(a:args, 0, ''), '^\%(#\?\zs\d\+\ze\|#\zs.\+\ze\)$')
if nr_or_branch !=# ''
let path = 'pull/' . nr_or_branch
else
let path = 'pulls'
endif
" If the argument of repository was given and valid format, get user and repos.
let idx = nr_or_branch ==# '' ? 0 : 1
let m = matchlist(get(a:args, idx, ''),
\ '^\([^/]\+\)/\([^/]\+\)$')
let [user, repos] = !empty(m) ? m[1:2] : ['', '']
elseif a:type ==# s:TYPE_PROJECT
let path = ''
let m = matchlist(get(a:args, 0, ''),
\ '^\([^/]\+\)/\([^/]\+\)$')
let [user, repos] = !empty(m) ? m[1:2] : ['', '']
else " if a:type ==# s:TYPE_COMMIT
if len(a:args) > 1
let hash = a:args[0]
else
let hash = s:git('rev-parse', a:args[0])
if v:shell_error
throw "'" . a:args[0] . "' is not valid commit hash"
endif
endif
let path = 'commit/' . hash
let m = matchlist(get(a:args, 1, ''),
\ '^\([^/]\+\)/\([^/]\+\)$')
let [user, repos] = !empty(m) ? m[1:2] : ['', '']
endif
return {
\ 'path': path,
\ 'user': user,
\ 'repos': repos,
\}
endfunction
function! s:call_with_temp_dir(dir, funcname, args) abort
let haslocaldir = haslocaldir()
let cwd = getcwd()
" a:dir could be empty string
" when specifying opening repos.
" e.g.)
" * :OpenGithubFile path/to/file
" * :OpenGithubIssue [#]{number} {user}/{repos}
if a:dir !=# '' && a:dir !=# cwd
execute 'lcd' a:dir
endif
try
return call(a:funcname, a:args)
finally
if a:dir !=# cwd
execute (haslocaldir ? 'lcd' : 'cd') cwd
endif
endtry
endfunction
function! s:parse_github_remote_url(github_host) abort
let host_re = escape(a:github_host, '.')
let gh_host_re = 'github\.com'
" ex) ssh_re_fmt also supports 'ssh://' protocol. (#10)
" - git@github.com:tyru/open-github-browser.vim
" - ssh://git@github.com/tyru/open-github-browser.vim
let ssh_re_fmt = 'git@%s[:/]\([^/]\+\)/\([^/]\+\)\s'
let ssh2_re_fmt = '\s%s[:/]\([^/]\+\)/\([^/]\+\)\s'
let ssh3_re_fmt = 'ssh://%s/\([^/]\+\)/\([^/]\+\)\s'
let git_re_fmt = 'git://%s/\([^/]\+\)/\([^/]\+\)\s'
let https_re_fmt = 'https\?://%s/\([^/]\+\)/\([^/]\+\)\s'
let ssh_re = printf(ssh_re_fmt, host_re)
let ssh2_re = printf(ssh2_re_fmt, host_re)
let ssh3_re = printf(ssh3_re_fmt, host_re)
let git_re = printf(git_re_fmt, host_re)
let https_re = printf(https_re_fmt, host_re)
let gh_ssh_re = printf(ssh_re_fmt, gh_host_re)
let gh_ssh2_re = printf(ssh2_re_fmt, gh_host_re)
let gh_ssh3_re = printf(ssh3_re_fmt, gh_host_re)
let gh_git_re = printf(git_re_fmt, gh_host_re)
let gh_https_re = printf(https_re_fmt, gh_host_re)
let matched = []
for line in s:git_lines('remote', '-v')
" Even if host is not 'github.com',
" parse also 'github.com'.
for re in [ssh_re, ssh2_re, ssh3_re, git_re, https_re] +
\ (a:github_host !=# 'github.com' ?
\ [gh_ssh_re, gh_ssh2_re, gh_ssh3_re, gh_git_re, gh_https_re] : [])
let m = matchlist(line, re)
if !empty(m)
call add(matched, {
\ 'user': m[1],
\ 'repos': substitute(m[2], '\.git$', '', ''),
\})
endif
endfor
endfor
return matched
endfunction
" Detect user name and repos name from 'git remote -v' output.
" * Duplicated candidates of user and repos are removed.
" * Returns empty Dictionary if no valid GitHub repos are found.
" * Returns an Dictionary with 'user' and 'repos'
" if exact 1 repos is found.
" * Prompt a user to choose which repos
" if 2 or more repos are found.
" * Throws "INVALID INDEX" if invalid input was given.
function! s:detect_github_repos_from_git_remote(github_host) abort
let github_urls = s:parse_github_remote_url(a:github_host)
let github_urls = s:List.uniq_by(github_urls, 'v:val.user."/".v:val.repos')
let NONE = {}
if len(github_urls) ==# 0
return NONE
elseif len(github_urls) ==# 1
return github_urls[0]
else
" Prompt which GitHub URL.
let GITHUB_URL_FORMAT = 'https://%s/%s/%s'
let list = ['Which GitHub repository?']
for i in range(len(github_urls))
let url = printf(GITHUB_URL_FORMAT,
\ a:github_host,
\ github_urls[i].user,
\ github_urls[i].repos)
call add(list, (i+1).'. '.url)
endfor
let index = inputlist(list)
if 1 <=# index && index <=# len(github_urls)
return github_urls[index-1]
else
throw 'INVALID INDEX'
endif
endif
endfunction
function! s:get_repos_relpath(file) abort
let relpath = ''
if s:Filepath.is_relative(a:file)
let dir = s:git('rev-parse', '--show-prefix')
let dir = dir !=# '' ? dir.'/' : ''
let relpath = dir.a:file
else
let relpath = s:lookup_git_relpath(a:file)
endif
let relpath = substitute(relpath, '\', '/', 'g')
let relpath = substitute(relpath, '/\{2,}', '/', 'g')
return relpath
endfunction
function! s:lookup_git_relpath(path) abort
return get(s:split_repos_path(a:path), 1, '')
endfunction
function! s:lookup_git_worktree(path) abort
return get(s:split_repos_path(a:path), 0, '')
endfunction
" Returns [git worktree, relative path] when git dir is found.
" Otherwise, returns empty List.
function! s:split_repos_path(path, ...) abort
let parent = s:Filepath.dirname(a:path)
let basename = s:Filepath.basename(a:path)
let removed_path = a:0 ? a:1 : ''
if a:path ==# parent
" a:path is root directory. not found.
return []
elseif s:is_git_worktree(a:path)
return [a:path, removed_path]
else
if removed_path ==# ''
let removed_path = basename
else
let removed_path = s:Filepath.join(basename, removed_path)
endif
return s:split_repos_path(parent, removed_path)
endif
endfunction
function! s:is_git_worktree(path) abort
" .git may be a file when its repository is a submodule.
let gitdir = s:Filepath.join(a:path, '.git')
return isdirectory(gitdir) || filereadable(gitdir)
endfunction
" Enterprise GitHub is supported.
" ('hub' command is using this config key)
function! s:get_github_host() abort
let url = s:git('config', '--get', 'hub.host')
return url !=# '' ? url : 'github.com'
endfunction
" Default value is 1 (use vimproc)
if get(g:, 'openbrowser_use_vimproc', 1)
\ && globpath(&rtp, 'autoload/vimproc.vim') !=# ''
function! s:git(...) abort
return s:trim(vimproc#system(['git'] + a:000))
endfunction
function! s:hub(...) abort
return s:trim(vimproc#system(['hub'] + a:000))
endfunction
else
function! s:git(...) abort
return s:trim(system(join(['git'] + a:000, ' ')))
endfunction
function! s:hub(...) abort
return s:trim(system(join(['hub'] + a:000, ' ')))
endfunction
endif
function! s:git_lines(...) abort
let output = call('s:git', a:000)
let keepempty = 1
return split(output, '\n', keepempty)
endfunction
function! s:trim(str) abort
let str = a:str
let str = substitute(str, '^[ \t\n]\+', '', 'g')
let str = substitute(str, '[ \t\n]\+$', '', 'g')
return str
endfunction
function! s:resolve(path) abort
return exists('*resolve') ? resolve(a:path) : a:path
endfunction
function! s:echomsg(msg, hl) abort
execute 'echohl' a:hl
try
echomsg '[openbrowser-github]' a:msg
finally
echohl None
endtry
endfunction
function! s:warn(msg) abort
call s:echomsg(a:msg, 'WarningMsg')
endfunction
function! s:error(msg) abort
call s:echomsg(a:msg, 'ErrorMsg')
endfunction
" Restore 'cpoptions' {{{
let &cpo = s:save_cpo
" }}}
================================================
FILE: autoload/vital/_open_browser_github/Data/List.vim
================================================
" Utilities for list.
let s:save_cpo = &cpo
set cpo&vim
function! s:pop(list)
return remove(a:list, -1)
endfunction
function! s:push(list, val)
call add(a:list, a:val)
return a:list
endfunction
function! s:shift(list)
return remove(a:list, 0)
endfunction
function! s:unshift(list, val)
return insert(a:list, a:val)
endfunction
function! s:cons(x, xs)
return [a:x] + a:xs
endfunction
function! s:conj(xs, x)
return a:xs + [a:x]
endfunction
" Removes duplicates from a list.
function! s:uniq(list)
return s:uniq_by(a:list, 'v:val')
endfunction
" Removes duplicates from a list.
function! s:uniq_by(list, f)
let list = map(copy(a:list), printf('[v:val, %s]', a:f))
let i = 0
let seen = {}
while i < len(list)
let key = string(list[i][1])
if has_key(seen, key)
call remove(list, i)
else
let seen[key] = 1
let i += 1
endif
endwhile
return map(list, 'v:val[0]')
endfunction
function! s:clear(list)
if !empty(a:list)
unlet! a:list[0 : len(a:list) - 1]
endif
return a:list
endfunction
" Concatenates a list of lists.
" XXX: Should we verify the input?
function! s:concat(list)
let memo = []
for Value in a:list
let memo += Value
endfor
return memo
endfunction
" Take each elements from lists to a new list.
function! s:flatten(list, ...)
let limit = a:0 > 0 ? a:1 : -1
let memo = []
if limit == 0
return a:list
endif
let limit -= 1
for Value in a:list
let memo +=
\ type(Value) == type([]) ?
\ s:flatten(Value, limit) :
\ [Value]
unlet! Value
endfor
return memo
endfunction
" Sorts a list with expression to compare each two values.
" a:a and a:b can be used in {expr}.
function! s:sort(list, expr)
if type(a:expr) == type(function('function'))
return sort(a:list, a:expr)
endif
let s:expr = a:expr
return sort(a:list, 's:_compare')
endfunction
function! s:_compare(a, b)
return eval(s:expr)
endfunction
" Sorts a list using a set of keys generated by mapping the values in the list
" through the given expr.
" v:val is used in {expr}
function! s:sort_by(list, expr)
let pairs = map(a:list, printf('[v:val, %s]', a:expr))
return map(s:sort(pairs,
\ 'a:a[1] ==# a:b[1] ? 0 : a:a[1] ># a:b[1] ? 1 : -1'), 'v:val[0]')
endfunction
" Returns a maximum value in {list} through given {expr}.
" Returns 0 if {list} is empty.
" v:val is used in {expr}
function! s:max_by(list, expr)
if empty(a:list)
return 0
endif
let list = map(copy(a:list), a:expr)
return a:list[index(list, max(list))]
endfunction
" Returns a minimum value in {list} through given {expr}.
" Returns 0 if {list} is empty.
" v:val is used in {expr}
" FIXME: -0x80000000 == 0x80000000
function! s:min_by(list, expr)
return s:max_by(a:list, '-(' . a:expr . ')')
endfunction
" Returns List of character sequence between [a:from, a:to]
" e.g.: s:char_range('a', 'c') returns ['a', 'b', 'c']
function! s:char_range(from, to)
return map(
\ range(char2nr(a:from), char2nr(a:to)),
\ 'nr2char(v:val)'
\)
endfunction
" Returns true if a:list has a:value.
" Returns false otherwise.
function! s:has(list, value)
return index(a:list, a:value) isnot -1
endfunction
" Returns true if a:list[a:index] exists.
" Returns false otherwise.
" NOTE: Returns false when a:index is negative number.
function! s:has_index(list, index)
" Return true when negative index?
" let index = a:index >= 0 ? a:index : len(a:list) + a:index
return 0 <= a:index && a:index < len(a:list)
endfunction
" similar to Haskell's Data.List.span
function! s:span(f, xs)
let border = len(a:xs)
for i in range(len(a:xs))
if !eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g'))
let border = i
break
endif
endfor
return border == 0 ? [[], copy(a:xs)] : [a:xs[: border - 1], a:xs[border :]]
endfunction
" similar to Haskell's Data.List.break
function! s:break(f, xs)
return s:span(printf('!(%s)', a:f), a:xs)
endfunction
" similar to Haskell's Data.List.takeWhile
function! s:take_while(f, xs)
return s:span(a:f, a:xs)[0]
endfunction
" similar to Haskell's Data.List.partition
function! s:partition(f, xs)
return [filter(copy(a:xs), a:f), filter(copy(a:xs), '!(' . a:f . ')')]
endfunction
" similar to Haskell's Prelude.all
function! s:all(f, xs)
return !s:any(printf('!(%s)', a:f), a:xs)
endfunction
" similar to Haskell's Prelude.any
function! s:any(f, xs)
return !empty(filter(map(copy(a:xs), a:f), 'v:val'))
endfunction
" similar to Haskell's Prelude.and
function! s:and(xs)
return s:all('v:val', a:xs)
endfunction
" similar to Haskell's Prelude.or
function! s:or(xs)
return s:any('v:val', a:xs)
endfunction
function! s:map_accum(expr, xs, init)
let memo = []
let init = a:init
for x in a:xs
let expr = substitute(a:expr, 'v:memo', init, 'g')
let expr = substitute(expr, 'v:val', x, 'g')
let [tmp, init] = eval(expr)
call add(memo, tmp)
endfor
return memo
endfunction
" similar to Haskell's Prelude.foldl
function! s:foldl(f, init, xs)
let memo = a:init
for x in a:xs
let expr = substitute(a:f, 'v:val', string(x), 'g')
let expr = substitute(expr, 'v:memo', string(memo), 'g')
unlet memo
let memo = eval(expr)
endfor
return memo
endfunction
" similar to Haskell's Prelude.foldl1
function! s:foldl1(f, xs)
if len(a:xs) == 0
throw 'foldl1'
endif
return s:foldl(a:f, a:xs[0], a:xs[1:])
endfunction
" similar to Haskell's Prelude.foldr
function! s:foldr(f, init, xs)
return s:foldl(a:f, a:init, reverse(copy(a:xs)))
endfunction
" similar to Haskell's Prelude.fold11
function! s:foldr1(f, xs)
if len(a:xs) == 0
throw 'foldr1'
endif
return s:foldr(a:f, a:xs[-1], a:xs[0:-2])
endfunction
" similar to python's zip()
function! s:zip(...)
return map(range(min(map(copy(a:000), 'len(v:val)'))), "map(copy(a:000), 'v:val['.v:val.']')")
endfunction
" similar to zip(), but goes until the longer one.
function! s:zip_fill(xs, ys, filler)
if empty(a:xs) && empty(a:ys)
return []
elseif empty(a:ys)
return s:cons([a:xs[0], a:filler], s:zip_fill(a:xs[1 :], [], a:filler))
elseif empty(a:xs)
return s:cons([a:filler, a:ys[0]], s:zip_fill([], a:ys[1 :], a:filler))
else
return s:cons([a:xs[0], a:ys[0]], s:zip_fill(a:xs[1 :], a:ys[1: ], a:filler))
endif
endfunction
" Inspired by Ruby's with_index method.
function! s:with_index(list, ...)
let base = a:0 > 0 ? a:1 : 0
return s:zip(a:list, range(base, len(a:list)+base-1))
endfunction
" similar to Ruby's detect or Haskell's find.
" TODO spec and doc
function! s:find(list, default, f)
for x in a:list
if eval(substitute(a:f, 'v:val', string(x), 'g'))
return x
endif
endfor
return a:default
endfunction
" Return non-zero if a:list1 and a:list2 have any common item(s).
" Return zero otherwise.
function! s:has_common_items(list1, list2)
return !empty(filter(copy(a:list1), 'index(a:list2, v:val) isnot -1'))
endfunction
" similar to Ruby's group_by.
function! s:group_by(xs, f)
let result = {}
let list = map(copy(a:xs), printf('[v:val, %s]', a:f))
for x in list
let Val = x[0]
let key = type(x[1]) !=# type('') ? string(x[1]) : x[1]
if has_key(result, key)
call add(result[key], Val)
else
let result[key] = [Val]
endif
unlet Val
endfor
return result
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo
" vim:set et ts=2 sts=2 sw=2 tw=0:
================================================
FILE: autoload/vital/_open_browser_github/System/Filepath.vim
================================================
" You should check the following related builtin functions.
" fnamemodify()
" resolve()
" simplify()
let s:save_cpo = &cpo
set cpo&vim
let s:path_sep_pattern = (exists('+shellslash') ? '[\\/]' : '/') . '\+'
let s:is_windows = has('win16') || has('win32') || has('win64') || has('win95')
let s:is_cygwin = has('win32unix')
let s:is_mac = !s:is_windows && !s:is_cygwin
\ && (has('mac') || has('macunix') || has('gui_macvim') ||
\ (!isdirectory('/proc') && executable('sw_vers')))
" Get the directory separator.
function! s:separator()
return fnamemodify('.', ':p')[-1 :]
endfunction
" Get the path separator.
let s:path_separator = s:is_windows ? ';' : ':'
function! s:path_separator()
return s:path_separator
endfunction
" Get the path extensions
function! s:path_extensions()
if !exists('s:path_extensions')
if s:is_windows
if exists('$PATHEXT')
let pathext = $PATHEXT
else
" get default PATHEXT
let pathext = matchstr(system('set pathext'), '^pathext=\zs.*\ze\n', 'i')
endif
let s:path_extensions = map(split(pathext, s:path_separator), 'tolower(v:val)')
elseif s:is_cygwin
" cygwin is not use $PATHEXT
let s:path_extensions = ['', '.exe']
else
let s:path_extensions = ['']
endif
endif
return s:path_extensions
endfunction
" Convert all directory separators to "/".
function! s:unify_separator(path)
return substitute(a:path, s:path_sep_pattern, '/', 'g')
endfunction
" Get the full path of command.
if exists('*exepath')
function! s:which(str)
return exepath(a:str)
endfunction
else
function! s:which(command, ...)
let pathlist = a:command =~# s:path_sep_pattern ? [''] :
\ !a:0 ? split($PATH, s:path_separator) :
\ type(a:1) == type([]) ? copy(a:1) :
\ split(a:1, s:path_separator)
let pathext = s:path_extensions()
if index(pathext, '.' . tolower(fnamemodify(a:command, ':e'))) != -1
let pathext = ['']
endif
let dirsep = s:separator()
for dir in pathlist
let head = dir ==# '' ? '' : dir . dirsep
for ext in pathext
let full = fnamemodify(head . a:command . ext, ':p')
if filereadable(full)
if s:is_case_tolerant()
let full = glob(substitute(
\ toupper(full), '\u:\@!', '[\0\L\0]', 'g'), 1)
endif
if full != ''
return full
endif
endif
endfor
endfor
return ''
endfunction
endif
" Split the path with directory separator.
" Note that this includes the drive letter of MS Windows.
function! s:split(path)
return split(a:path, s:path_sep_pattern)
endfunction
" Join the paths.
" join('foo', 'bar') => 'foo/bar'
" join('foo/', 'bar') => 'foo/bar'
" join('/foo/', ['bar', 'buz/']) => '/foo/bar/buz/'
function! s:join(...)
let sep = s:separator()
let path = ''
for part in a:000
let path .= sep .
\ (type(part) is type([]) ? call('s:join', part) :
\ part)
unlet part
endfor
return substitute(path[1 :], s:path_sep_pattern, sep, 'g')
endfunction
" Check if the path is absolute path.
if s:is_windows
function! s:is_absolute(path)
return a:path =~? '^[a-z]:[/\\]'
endfunction
else
function! s:is_absolute(path)
return a:path[0] ==# '/'
endfunction
endif
function! s:is_relative(path)
return !s:is_absolute(a:path)
endfunction
" Return the parent directory of the path.
" NOTE: fnamemodify(path, ':h') does not return the parent directory
" when path[-1] is the separator.
function! s:dirname(path)
let path = a:path
let orig = a:path
let path = s:remove_last_separator(path)
if path == ''
return orig " root directory
endif
let path = fnamemodify(path, ':h')
return path
endfunction
" Return the basename of the path.
" NOTE: fnamemodify(path, ':h') does not return basename
" when path[-1] is the separator.
function! s:basename(path)
let path = a:path
let orig = a:path
let path = s:remove_last_separator(path)
if path == ''
return orig " root directory
endif
let path = fnamemodify(path, ':t')
return path
endfunction
" Remove the separator at the end of a:path.
function! s:remove_last_separator(path)
let sep = s:separator()
let pat = (sep == '\' ? '\\' : '/') . '\+$'
return substitute(a:path, pat, '', '')
endfunction
" Return true if filesystem ignores alphabetic case of a filename.
" Return false otherwise.
let s:is_case_tolerant = filereadable(expand('<sfile>:r') . '.VIM')
function! s:is_case_tolerant()
return s:is_case_tolerant
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo
" vim:set et ts=2 sts=2 sw=2 tw=0:
================================================
FILE: autoload/vital/_open_browser_github.vim
================================================
let s:self_version = expand('<sfile>:t:r')
" Note: The extra argument to globpath() was added in Patch 7.2.051.
let s:globpath_third_arg = v:version > 702 || v:version == 702 && has('patch51')
let s:loaded = {}
function! s:import(name, ...)
let target = {}
let functions = []
for a in a:000
if type(a) == type({})
let target = a
elseif type(a) == type([])
let functions = a
endif
unlet a
endfor
let module = s:_import(a:name)
if empty(functions)
call extend(target, module, 'keep')
else
for f in functions
if has_key(module, f) && !has_key(target, f)
let target[f] = module[f]
endif
endfor
endif
return target
endfunction
function! s:load(...) dict
for arg in a:000
let [name; as] = type(arg) == type([]) ? arg[: 1] : [arg, arg]
let target = split(join(as, ''), '\W\+')
let dict = self
while 1 <= len(target)
let ns = remove(target, 0)
if !has_key(dict, ns)
let dict[ns] = {}
endif
if type(dict[ns]) == type({})
let dict = dict[ns]
else
unlet dict
break
endif
endwhile
if exists('dict')
call extend(dict, s:_import(name))
endif
unlet arg
endfor
return self
endfunction
function! s:unload()
let s:loaded = {}
endfunction
function! s:exists(name)
return s:_get_module_path(a:name) !=# ''
endfunction
function! s:search(pattern)
let paths = s:_vital_files(a:pattern)
let modules = sort(map(paths, 's:_file2module(v:val)'))
return s:_uniq(modules)
endfunction
function! s:expand_modules(entry, all)
if type(a:entry) == type([])
let candidates = s:_concat(map(copy(a:entry), 's:search(v:val)'))
if empty(candidates)
throw printf('vital: Any of module %s is not found', string(a:entry))
endif
if eval(join(map(copy(candidates), 'has_key(a:all, v:val)'), '+'))
let modules = []
else
let modules = [candidates[0]]
endif
else
let modules = s:search(a:entry)
if empty(modules)
throw printf('vital: Module %s is not found', a:entry)
endif
endif
call filter(modules, '!has_key(a:all, v:val)')
for module in modules
let a:all[module] = 1
endfor
return modules
endfunction
function! s:_import(name)
if type(a:name) == type(0)
return s:_build_module(a:name)
endif
let path = s:_get_module_path(a:name)
if path ==# ''
throw 'vital: module not found: ' . a:name
endif
let sid = s:_get_sid_by_script(path)
if !sid
try
execute 'source' fnameescape(path)
catch /^Vim\%((\a\+)\)\?:E484/
throw 'vital: module not found: ' . a:name
catch /^Vim\%((\a\+)\)\?:E127/
" Ignore.
endtry
let sid = s:_get_sid_by_script(path)
endif
return s:_build_module(sid)
endfunction
function! s:_get_module_path(name)
if s:_is_absolute_path(a:name) && filereadable(a:name)
return a:name
endif
if a:name ==# ''
let paths = [s:self_file]
elseif a:name =~# '\v^\u\w*%(\.\u\w*)*$'
let paths = s:_vital_files(a:name)
else
throw 'vital: Invalid module name: ' . a:name
endif
call filter(paths, 'filereadable(expand(v:val, 1))')
let path = get(paths, 0, '')
return path !=# '' ? path : ''
endfunction
function! s:_get_sid_by_script(path)
let path = s:_unify_path(a:path)
for line in filter(split(s:_redir('scriptnames'), "\n"),
\ 'stridx(v:val, s:self_version) > 0')
let list = matchlist(line, '^\s*\(\d\+\):\s\+\(.\+\)\s*$')
if !empty(list) && s:_unify_path(list[2]) ==# path
return list[1] - 0
endif
endfor
return 0
endfunction
function! s:_file2module(file)
let filename = fnamemodify(a:file, ':p:gs?[\\/]\+?/?')
let tail = matchstr(filename, 'autoload/vital/_\w\+/\zs.*\ze\.vim$')
return join(split(tail, '[\\/]\+'), '.')
endfunction
if filereadable(expand('<sfile>:r') . '.VIM')
" resolve() is slow, so we cache results.
let s:_unify_path_cache = {}
" Note: On windows, vim can't expand path names from 8.3 formats.
" So if getting full path via <sfile> and $HOME was set as 8.3 format,
" vital load duplicated scripts. Below's :~ avoid this issue.
function! s:_unify_path(path)
if has_key(s:_unify_path_cache, a:path)
return s:_unify_path_cache[a:path]
endif
let value = tolower(fnamemodify(resolve(fnamemodify(
\ a:path, ':p')), ':~:gs?[\\/]\+?/?'))
let s:_unify_path_cache[a:path] = value
return value
endfunction
else
function! s:_unify_path(path)
return resolve(fnamemodify(a:path, ':p:gs?[\\/]\+?/?'))
endfunction
endif
if s:globpath_third_arg
function! s:_runtime_files(path)
return split(globpath(&runtimepath, a:path, 1), "\n")
endfunction
else
function! s:_runtime_files(path)
return split(globpath(&runtimepath, a:path), "\n")
endfunction
endif
let s:_vital_files_cache_runtimepath = ''
let s:_vital_files_cache = []
function! s:_vital_files(pattern)
if s:_vital_files_cache_runtimepath !=# &runtimepath
let path = printf('autoload/vital/%s/**/*.vim', s:self_version)
let s:_vital_files_cache = s:_runtime_files(path)
let mod = ':p:gs?[\\/]\+?/?'
call map(s:_vital_files_cache, 'fnamemodify(v:val, mod)')
let s:_vital_files_cache_runtimepath = &runtimepath
endif
let target = substitute(a:pattern, '\.', '/', 'g')
let target = substitute(target, '\*', '[^/]*', 'g')
let regexp = printf('autoload/vital/%s/%s.vim', s:self_version, target)
return filter(copy(s:_vital_files_cache), 'v:val =~# regexp')
endfunction
" Copy from System.Filepath
if has('win16') || has('win32') || has('win64')
function! s:_is_absolute_path(path)
return a:path =~? '^[a-z]:[/\\]'
endfunction
else
function! s:_is_absolute_path(path)
return a:path[0] ==# '/'
endfunction
endif
function! s:_build_module(sid)
if has_key(s:loaded, a:sid)
return copy(s:loaded[a:sid])
endif
let functions = s:_get_functions(a:sid)
let prefix = '<SNR>' . a:sid . '_'
let module = {}
for func in functions
let module[func] = function(prefix . func)
endfor
if has_key(module, '_vital_loaded')
let V = vital#{s:self_version}#new()
if has_key(module, '_vital_depends')
let all = {}
let modules =
\ s:_concat(map(module._vital_depends(),
\ 's:expand_modules(v:val, all)'))
call call(V.load, modules, V)
endif
try
call module._vital_loaded(V)
catch
" FIXME: Show an error message for debug.
endtry
endif
if !get(g:, 'vital_debug', 0)
call filter(module, 'v:key =~# "^\\a"')
endif
let s:loaded[a:sid] = module
return copy(module)
endfunction
if exists('+regexpengine')
function! s:_get_functions(sid)
let funcs = s:_redir(printf("function /\\%%#=2^\<SNR>%d_", a:sid))
let map_pat = '<SNR>' . a:sid . '_\zs\w\+'
return map(split(funcs, "\n"), 'matchstr(v:val, map_pat)')
endfunction
else
function! s:_get_functions(sid)
let prefix = '<SNR>' . a:sid . '_'
let funcs = s:_redir('function')
let filter_pat = '^\s*function ' . prefix
let map_pat = prefix . '\zs\w\+'
return map(filter(split(funcs, "\n"),
\ 'stridx(v:val, prefix) > 0 && v:val =~# filter_pat'),
\ 'matchstr(v:val, map_pat)')
endfunction
endif
if exists('*uniq')
function! s:_uniq(list)
return uniq(a:list)
endfunction
else
function! s:_uniq(list)
let i = len(a:list) - 1
while 0 < i
if a:list[i] ==# a:list[i - 1]
call remove(a:list, i)
let i -= 2
else
let i -= 1
endif
endwhile
return a:list
endfunction
endif
function! s:_concat(lists)
let result_list = []
for list in a:lists
let result_list += list
endfor
return result_list
endfunction
function! s:_redir(cmd)
let [save_verbose, save_verbosefile] = [&verbose, &verbosefile]
set verbose=0 verbosefile=
redir => res
silent! execute a:cmd
redir END
let [&verbose, &verbosefile] = [save_verbose, save_verbosefile]
return res
endfunction
function! vital#{s:self_version}#new()
return s:_import('')
endfunction
let s:self_file = s:_unify_path(expand('<sfile>'))
================================================
FILE: autoload/vital/open_browser_github.vital
================================================
open_browser_github
e8ec38a
System.Filepath
Data.List
================================================
FILE: autoload/vital.vim
================================================
function! vital#of(name)
let files = globpath(&runtimepath, 'autoload/vital/' . a:name . '.vital')
let file = split(files, "\n")
if empty(file)
throw 'vital: version file not found: ' . a:name
endif
let ver = readfile(file[0], 'b')
if empty(ver)
throw 'vital: invalid version file: ' . a:name
endif
return vital#_{substitute(ver[0], '\W', '', 'g')}#new()
endfunction
================================================
FILE: doc/openbrowser-github.txt
================================================
*openbrowser-github* Launch GitHub quickly from Vim
Author:
tyru <tyru.exe@gmail.com>
Version: 1.0.0
License: See LICENSE file in this repository
==============================================================================
CONTENTS *openbrowser-github-contents*
Introduction |openbrowser-github-introduction|
Requirements |openbrowser-github-requirements|
Interface |openbrowser-github-interface|
Variables |openbrowser-github-variables|
Commands |openbrowser-github-commands|
TODO |openbrowser-github-todo|
Changelog |openbrowser-github-changelog|
==============================================================================
INTRODUCTION *openbrowser-github-introduction* {{{
Opens GitHub URL of current file, etc. from Vim.
Also supports GitHub Enterprise(|openbrowser-github-enterprise|).
See |openbrowser-github-commands| for the details of available commands.
}}}
==============================================================================
REQUIREMENTS *openbrowser-github-requirements* {{{
You must install the following plugin/libraries.
* open-browser.vim
https://github.com/tyru/open-browser.vim
* git command in your PATH
}}}
==============================================================================
GITHUB ENTERPRISE SETTING *openbrowser-github-enterprise* {{{
If you have `hub` command
-----------------------
If you have `hub` command(https://github.com/github/hub) in your PATH,
|openbrowser-github| executes the following command: >
hub browse -u -- {path}
And it will open the returned (output) URL.
If you _don't_ have `hub` command
-------------------------------
If you don't have `hub` command in your PATH, |openbrowser-github| tries to
get each part of URL from the following gitconfig key:
* hub.host
You can specify GitHub Enterprise repository URL by setting above keys in
gitconfig.
For example, you can set `hub.host` by executing the following command in your
git repository which you want to specify GitHub Enterprise repository URL.
>
git config --local hub.host my.git.org
<
}}}
==============================================================================
INTERFACE *openbrowser-github-interface* {{{
------------------------------------------------------------------------------
VARIABLES *openbrowser-github-variables* {{{
*g:openbrowser_github_always_used_branch*
g:openbrowser_github_always_used_branch
(Default: "")
If this variable is not empty string,
|openbrowser-github| always opens current file
in the branch.
*g:openbrowser_github_always_use_commit_hash*
g:openbrowser_github_always_use_commit_hash
(Default: 1)
If this variable is non-zero value,
|openbrowser-github| always opens a URL
with a current commit hash.
ex) https://github.com/tyru/open-browser.vim/blob/9f1de0e38a1e378061a8f10df6ed8e22c48aa9ae/autoload/openbrowser.vim
If this variable is zero value,
|openbrowser-github| opens a URL
with a current commit hash
if current working tree is detached state.
Otherwise, |openbrowser-github| opens a URL
with a current branch's latest file.
ex) https://github.com/tyru/open-browser.vim/blob/master/autoload/openbrowser.vim
*g:openbrowser_github_url_exists_check*
g:openbrowser_github_url_exists_check
(Default: "yes")
This determines the behavior when you
push to the non-existent/unauthorized repository.
If this variable is "yes", shows prompt to ask you
if you really open URL.
If this variable is "ignore", any prompts and warning
messages aren't showed.
Otherwise, it is treated as default value ("yes").
*g:openbrowser_github_select_current_line*
g:openbrowser_github_select_current_line
(Default: 0)
|:OpenGithubFile| opens a github page that current line is highlighted on.
If this variable is non-zero, always opens URL with current line fragment
(`...#Lxxx`) even the line is not selected in visual-mode. If this
variable is zero (default), it opens URL with current line fragment only
when the line is selected in visual-mode.
}}}
------------------------------------------------------------------------------
COMMANDS *openbrowser-github-commands* {{{
:OpenGithubFile [{path}] *:OpenGithubFile*
------------------------
Opens a specific file in github.com repository(it also opens in the current branch by default).
>
" Opens current files URL in github.com
" ex) https://{hostname}/{user}/{repos}/blob/{hash or branch}/{relpath}
:OpenGithubFile
" Opens current files highlighted place in github.com
" ex) https://{hostname}/{user}/{repos}/blob/{hash or branch}/{relpath}#{lnum}
:'<,'>OpenGithubFile
" Opens a specific file in github.com
" ex) https://{hostname}/{user}/{repos}/blob/{hash or branch}/PATH/TO/FILE#{lnum}
:OpenGithubFile PATH/TO/FILE
<
Those URLs are influenced by some global variables.
See:
* |g:openbrowser_github_always_used_branch|
* |g:openbrowser_github_always_use_commit_hash|
*:OpenGithubIssue*
:OpenGithubIssue
:OpenGithubIssue [#]{number} [{user}/{repos}]
:OpenGithubIssue {user}/{repos}
-----------------------------------
Opens a specific Issue.
>
" Opens current repositories Issue #1
" ex) https://{hostname}/{user}/{repos}/issues/1
:OpenGithubIssue 1
" Opens a specific repositories Issue #1
" ex) https://{hostname}/tyru/open-browser.vim/issues/1
:OpenGithubIssue 1 tyru/open-browser.vim
" Opens current repositories Issue List
" ex) https://{hostname}/{user}/{repos}/issues
:OpenGithubIssue
" Opens a specific repositories Issue list
" ex) https://{hostname}/tyru/open-browser.vim/issues
:OpenGithubIssue tyru/open-browser.vim
<
*:OpenGithubPullReq*
:OpenGithubPullReq
:OpenGithubPullReq [#]{number} [{user}/{repos}]
:OpenGithubPullReq #{branch} [{user}/{repos}]
:OpenGithubPullReq {user}/{repos}
-------------------------------------
Opens '/pulls' page when it has no argument.
Otherwise, it does entirely the same thing as |:OpenGithubIssue|
since GitHub redirects '/issues/1' to '/pull/1'
if #1 is a Pull Request.
Additionally, this command supports opening a pull request page by branch name.
>
" Opens current repository's pull request of specified branch name
:OpenGithubPullReq #branch_name_of_pull_request
" Opens https://github.com/tyru/open-browser-github.vim/pull/25
:OpenGithubPullReq #OpenGithubPullReq-branch-argument tyru/open-browser-github.vim
<
*:OpenGithubProject*
:OpenGithubProject [{user}/{repos}]
Opens a project page.
>
" Opens current opening file's repository.
" ex) https://{hostname}/{user}/{name}
:OpenGithubProject
>
" Opens current opening file's repository.
" ex) https://{hostname}/tyru/open-browser.vim
:OpenGithubProject tyru/open-browser.vim
<
*:OpenGithubCommit*
:OpenGithubCommit {commit hash} [{user}/{repos}]
Opens a commit page.
>
" Opens current repository's commit.
" ex) https://{hostname}/{user}/{name}/commit/{hash}
:OpenGithubCommit HEAD
>
" Opens speicified repository's commit.
" ex) https://{hostname}/vim/vim/commit/b1c9198afb
:OpenGithubCommit b1c9198afb vim/vim
}}}
}}}
==============================================================================
TODO *openbrowser-github-todo* {{{
Any requests? ;)
https://github.com/tyru/open-browser-github.vim/issues
}}}
==============================================================================
CHANGELOG *openbrowser-github-changelog* {{{
1.0.0:
- Initial upload
}}}
==============================================================================
vim:tw=78:fo=tcq2mM:ts=4:ft=help:norl:noet:fdm=marker:fen
================================================
FILE: plugin/openbrowser/github.vim
================================================
" vim:foldmethod=marker:fen:
scriptencoding utf-8
" Load Once {{{
if get(g:, 'loaded_openbrowser_github', 0) || &cp
finish
endif
let g:loaded_openbrowser_github = 1
" }}}
" Saving 'cpoptions' {{{
let s:save_cpo = &cpo
set cpo&vim
" }}}
function! s:error(msg) abort
echohl ErrorMsg
echomsg a:msg
echohl None
endfunction
if !executable('git')
call s:error('Please install git in your PATH.')
finish
endif
if globpath(&rtp, 'plugin/openbrowser.vim') ==# ''
call s:error('open-browser-github.vim depends on open-browser.vim. Please install open-browser.vim')
finish
endif
if !exists('g:openbrowser_github_always_used_branch')
let g:openbrowser_github_always_used_branch = ''
endif
if !exists('g:openbrowser_github_always_use_commit_hash')
let g:openbrowser_github_always_use_commit_hash = 1
endif
if !exists('g:openbrowser_github_url_exists_check')
let g:openbrowser_github_url_exists_check = 'yes'
endif
if !exists('g:openbrowser_github_select_current_line')
let g:openbrowser_github_select_current_line = 0
endif
command! -range=0 -bar -nargs=* -complete=file
\ OpenGithubFile
\ call openbrowser#github#file([<f-args>], <count>, <line1>, <line2>)
command! -bar -nargs=*
\ OpenGithubIssue
\ call openbrowser#github#issue([<f-args>])
command! -bar -nargs=*
\ OpenGithubPullReq
\ call openbrowser#github#pullreq([<f-args>])
command! -bar -nargs=*
\ OpenGithubProject
\ call openbrowser#github#project([<f-args>])
command! -bar -nargs=+
\ OpenGithubCommit
\ call openbrowser#github#commit([<f-args>])
" Restore 'cpoptions' {{{
let &cpo = s:save_cpo
" }}}
gitextract_urosfd0w/
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── autoload/
│ ├── openbrowser/
│ │ └── github.vim
│ ├── vital/
│ │ ├── _open_browser_github/
│ │ │ ├── Data/
│ │ │ │ └── List.vim
│ │ │ └── System/
│ │ │ └── Filepath.vim
│ │ ├── _open_browser_github.vim
│ │ └── open_browser_github.vital
│ └── vital.vim
├── doc/
│ └── openbrowser-github.txt
└── plugin/
└── openbrowser/
└── github.vim
Condensed preview — 12 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (52K chars).
[
{
"path": ".gitignore",
"chars": 9,
"preview": "doc/tags\n"
},
{
"path": "LICENSE",
"chars": 1515,
"preview": "BSD 3-Clause License\n\nCopyright (c) 2013, Takuya Fujiwara\nAll rights reserved.\n\nRedistribution and use in source and bin"
},
{
"path": "Makefile",
"chars": 132,
"preview": "\nrelease:\n\tgit archive HEAD plugin autoload doc --output=open-browser-github-$(shell git describe --tags HEAD).zip\n\n.PHO"
},
{
"path": "README.md",
"chars": 2207,
"preview": "# open-browser-github.vim\n\n## About\n\nOpens GitHub URL of current file, etc. from Vim.\nAlso supports GitHub Enterprise.\n\n"
},
{
"path": "autoload/openbrowser/github.vim",
"chars": 14700,
"preview": "\" vim:foldmethod=marker:fen:\nscriptencoding utf-8\n\n\" Saving 'cpoptions' {{{\nlet s:save_cpo = &cpo\nset cpo&vim\n\" }}}\n\nlet"
},
{
"path": "autoload/vital/_open_browser_github/Data/List.vim",
"chars": 7437,
"preview": "\" Utilities for list.\n\nlet s:save_cpo = &cpo\nset cpo&vim\n\nfunction! s:pop(list)\n return remove(a:list, -1)\nendfunction\n"
},
{
"path": "autoload/vital/_open_browser_github/System/Filepath.vim",
"chars": 4776,
"preview": "\" You should check the following related builtin functions.\n\" fnamemodify()\n\" resolve()\n\" simplify()\n\nlet s:save_cpo = &"
},
{
"path": "autoload/vital/_open_browser_github.vim",
"chars": 8166,
"preview": "let s:self_version = expand('<sfile>:t:r')\n\n\" Note: The extra argument to globpath() was added in Patch 7.2.051.\nlet s:g"
},
{
"path": "autoload/vital/open_browser_github.vital",
"chars": 55,
"preview": "open_browser_github\ne8ec38a\n\nSystem.Filepath\nData.List\n"
},
{
"path": "autoload/vital.vim",
"chars": 391,
"preview": "function! vital#of(name)\n let files = globpath(&runtimepath, 'autoload/vital/' . a:name . '.vital')\n let file = split("
},
{
"path": "doc/openbrowser-github.txt",
"chars": 7567,
"preview": "*openbrowser-github* Launch GitHub quickly from Vim\n\nAuthor:\n tyru <tyru.exe@gmail.com>\nVersion: 1.0.0\nLicense: See LIC"
},
{
"path": "plugin/openbrowser/github.vim",
"chars": 1606,
"preview": "\" vim:foldmethod=marker:fen:\nscriptencoding utf-8\n\n\" Load Once {{{\nif get(g:, 'loaded_openbrowser_github', 0) || &cp\n f"
}
]
About this extraction
This page contains the full source code of the tyru/open-browser-github.vim GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 12 files (47.4 KB), approximately 14.8k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.