Full Code of tyru/open-browser-github.vim for AI

master ac7c034e300f cached
12 files
47.4 KB
14.8k tokens
1 requests
Download .txt
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
" }}}
Download .txt
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.

Copied to clipboard!