"          __
"  __  __ /\_\    ___ ___   _ __   ___
" /\ \/\ \\/\ \ /' __` __`\/\`'__\/'___\
" \ \ \_/ |\ \ \/\ \/\ \/\ \ \ \//\ \__/
"  \ \___/  \ \_\ \_\ \_\ \_\ \_\\ \____\
"   \/__/    \/_/\/_/\/_/\/_/\/_/ \/____/

" dein.vim settings {{{
" install dir {{{
let s:dein_dir = expand('~/.cache/dein')
let s:dein_repo_dir = s:dein_dir . '/repos/github.com/Shougo/dein.vim'
" }}}

" dein installation check {{{
if &runtimepath !~# '/dein.vim'
	if !isdirectory(s:dein_repo_dir)
		execute '!git clone https://github.com/Shougo/dein.vim' s:dein_repo_dir
	endif
	execute 'set runtimepath^=' . s:dein_repo_dir
endif
" }}}

" begin settings {{{
if dein#load_state(s:dein_dir)
	call dein#begin(s:dein_dir)

	" .toml file
	let s:rc_dir = expand('~/.vim')
	let s:toml = s:rc_dir . '/dein.toml'
	let s:lazy_toml = s:rc_dir . '/dein_lazy.toml'

	" read toml and cache
	call dein#load_toml(s:toml, {'lazy': 0})
	call dein#load_toml(s:lazy_toml, {'lazy': 1})

	" end settings
	call dein#end()
	call dein#save_state()
endif
" }}}

" plugin installation check {{{
if dein#check_install()
	call dein#install()
endif
" }}}

" plugin remove check {{{
let s:removed_plugins = dein#check_clean()
if len(s:removed_plugins) > 0
	call map(s:removed_plugins, "delete(v:val, 'rf')")
	call dein#recache_runtimepath()
endif
" }}}

" }}}

" general settings {{{

" ミュート {{{
set t_vb=
set visualbell
set noerrorbells
" }}}

" 文字コード {{{
set encoding=utf-8
set fileencodings=utf-8,iso-2022-jp,euc-jp,sjis
set fileformats=unix,dos,mac
" }}}

" vim scriptでマルチバイト文字を使用しているため設定 {{{
scriptencoding utf-8
" }}}

" https://vim-jp.org/vimdoc-ja/map.html#mapleader
" Leaderキーをスペースに設定 {{{
let g:mapleader = "\<Space>"
" }}}

" シンタックスを有効にする {{{
syntax enable
" }}}

" カラースキームを使う {{{
colorscheme iceberg
" }}}

" https://vim-jp.org/vimdoc-ja/filetype.html#:filetype-plugin-on
" https://vim-jp.org/vimdoc-ja/filetype.html#:filetype-indent-on
" ファイル形式別プラグインとインデントを有効にする {{{
filetype plugin indent on
" }}}

" バックスペースとCtrl+hで削除を有効にする {{{
set backspace=2
" }}}

" 改行時自動インデント {{{
set smartindent
" }}}

" 行番号を表示 {{{
set number
" }}}

" カーソルから相対的な行数を表示する {{{
set relativenumber
" }}}

" https://vim-jp.org/vimdoc-ja/options.html#'tabstop'
" タブでも常に空白を挿入 {{{
set tabstop=4
set shiftwidth=4
"set expandtab
" }}}

" インクリメントサーチを有効にする {{{
set incsearch
" }}}

" https://vim-jp.org/vimdoc-ja/options.html#'ignorecase'
" 検索時大文字小文字を区別しない {{{
set ignorecase
" }}}

" https://vim-jp.org/vimdoc-ja/options.html#'smartcase'
" 検索時に大文字を入力した場合ignorecaseが無効になる {{{
set smartcase
" }}}

" ハイライトサーチを有効にする {{{
set hlsearch
" }}}

" undoできる最大数 {{{
set undolevels=1000
" }}}

" mac os のクリップボードを共有 {{{
set clipboard+=unnamed
" }}}

" カーソルが常に中央に来るようにする {{{
set scrolloff=100
" }}}

" スワップファイルに書き込まれる時間(ミリ秒単位) {{{
set updatetime=450
" }}}

" マクロで効果発揮 {{{
set lazyredraw
set ttyfast
" }}}

" 一行が長いファイルをsyntaxを制御することで軽くする {{{
set synmaxcol=256
" }}}

" カーソルラインを表示する {{{
set cursorline
" }}}

" https://vim-jp.org/vimdoc-ja/pi_netrw.html#g:netrw_liststyle
" netrwツリー表示を有効にする {{{
let g:netrw_liststyle=3
let g:netrw_banner=0
let g:netrw_sizestyle='H'
let g:netrw_timefmt='%Y/%m/%d(%a) %H:%M:%S'
let g:netrw_preview=1
" }}}

" 拡張子ごとのインデントを指定する {{{
augroup fileTypeIndent
	autocmd!
	au BufRead,BufNewFile *.php setlocal tabstop=4 softtabstop=4 shiftwidth=4
	au BufRead,BufNewFile *.html setlocal tabstop=4 softtabstop=4 shiftwidth=4
	au BufRead,BufNewFile *.js   setlocal tabstop=4 softtabstop=4 shiftwidth=4
	au BufRead,BufNewFile *.vue  setlocal tabstop=4 softtabstop=4 shiftwidth=4
augroup END
" }}}

" https://vim-jp.org/vimdoc-ja/options.html#'wildmode'
" wildmenuを有効にする {{{
set wildmenu
set wildmode=full
" }}}

" grepした結果をquickfixに表示する {{{
augroup grepwindow
	autocmd!
	au QuickFixCmdPost *grep* cwindow
augroup END
" }}}

" カーソルラインの位置を保存する {{{
augroup cursorlineRestore
	autocmd!
	autocmd BufReadPost *
				\ if line("'\"") > 0 && line ("'\"") <= line("$") |
				\   exe "normal! g'\"" |
				\ endif
augroup END
" }}}

" undoの保存先 {{{
if has('persistent_undo')
	set undodir=~/.vim/undo
	set undofile
endif
" }}}

" visualモードのハイライトカラー {{{
hi Visual cterm=reverse ctermbg=NONE
" }}}

" 矩形選択時に文字の無いところまで選択範囲を広げる {{{
set virtualedit=block
" }}}

" ヘルプの言語を日本語優先にする {{{
set helplang=ja
" }}}

" ファイル保存時に整形する {{{
function! Format() abort
	let pos = getcurpos()
	execute 'normal 1G=G'
	call setpos('.', pos)
endfunction

function! JsFormat() abort
	if executable('js-beautify')
		" --type [js|css|html] default is js
		" -t indent with tabs
		let pos = getcurpos()
		exe "%!js-beautify --type js -t"
		call setpos('.', pos)
	else
		echo "js-beautify doesn't install, please refference the https://github.com/beautify-web/js-beautify"
	endif
endfunction

function! JsonFormat() abort
	if executable('jq')
		let pos = getcurpos()
		exe "%!jq --tab"
		call setpos('.', pos)
	else
		echo "jq doesn't install, please refference the https://stedolan.github.io/jq/"
	endif
endfunction

augroup format
	autocmd!
	autocmd BufWritePre *.js call JsFormat()
	autocmd BufWritePre *.vim call Format()
	autocmd BufWritePre *.html call Format()
	autocmd BufWritePre *.java call Format()
	autocmd BufWritePre *.json call JsonFormat()
augroup END
" }}}

" プラグインディレクトリ配下の.vimをすべてsourceする {{{
function! SourceDir(...) abort
	let l:path = getcwd()
	if a:0 > 1
		let l:path = a:1
	endif

	if !isdirectory(l:path)
		return
	endif

	exe 'set rtp^=' . l:path
	if isdirectory(l:path . '/plugin')
		exe 'runtime plugin/*.vim'
	endif

	if isdirectory(l:path . '/autoload')
		exe 'runtime autoload/*.vim'
	endif

	if isdirectory(l:path . '/syntax')
		exe 'runtime syntax/*.vim'
	endif
endfunction
" }}}

" listの設定 {{{
set list
set listchars=tab:»-,trail:-,eol:↲,extends:»,precedes:«,nbsp:%
"hi NonText    ctermbg=None ctermfg=Yellow
"hi SpecialKey ctermbg=None ctermfg=Yellow
" }}}

" {{{ 行末のホワイトスペース削除
autocmd BufWritePre * :silent %s/\s\+$//ge
augroup HighlightTrailingSpaces
	autocmd!
	autocmd VimEnter,WinEnter,ColorScheme * highlight TrailingSpaces term=underline guibg=Red ctermbg=Red
	autocmd VimEnter,WinEnter * match TrailingSpaces /\s\+$/
augroup END
" }}}

" swapファイルを作成しない
set noswapfile

" セッションで保存する対象を設定する
if !has('nvim')
	set sessionoptions=blank,buffers,curdir,folds,help,tabpages,winsize,terminal
endif

" ウィンドウサイズ自動調整を無効化
set noequalalways

" タブを常に表示
set showtabline=2

" }}}

" key mappings {{{
" *でカーソルを移動しないようにする
noremap * *N

" ファイル保存と終了 {{{
nnoremap <Leader>w :w<CR>
nnoremap <Leader>q :q!<CR>
" }}}

" 検索
nnoremap <C-G><C-G> :Ggrep <C-R><C-W><CR><CR>

" 置換
nnoremap <Leader>re :%s;\<<C-R><C-W>\>;g<Left><Left>;

" visualで選択したテキストを置換する
vnoremap <Leader>re y:%s;<C-r>=substitute(@", "<C-v><NL>", "\\\\n", "g")<CR>;;g<Left><Left>

" ハイライトを削除する
nnoremap <Esc><Esc> :nohlsearch<CR>

" vimrcを開く
nnoremap <Leader>. :new ~/.vimrc<CR>
nnoremap <Leader>s :source ~/.vimrc<CR>

" テキストオブジェクトキーマッピング {{{
onoremap 8 i(
onoremap 2 i"
onoremap 7 i'
onoremap @ i`
onoremap [ i[
onoremap { i{

onoremap a8 a(
onoremap a2 a"
onoremap a7 a'
onoremap a@ a`
onoremap a[ a[
onoremap a{ a{

" visual
nnoremap v8 vi(
nnoremap v2 vi"
nnoremap v7 vi'
nnoremap v@ vi`
nnoremap v[ vi[
nnoremap v{ vi{

nnoremap va8 va(
nnoremap va2 va"
nnoremap va7 va'
nnoremap va@ va`
nnoremap va[ va[
nnoremap va{ va{
" }}}

" 行先頭と行末
map H ^
map L $

" タブ切り替え
nnoremap <C-s>n gt
nnoremap <C-s>p gT

" visual時に選択行を移動
vnoremap <C-j> :m '>+1<CR>gv
vnoremap <C-k> :m '<-2<CR>gv

" numberとrelativenumberの切り替え
nnoremap <silent> <Leader>n :set relativenumber!<CR>

" 自作アプリケーションランチャー
nnoremap <silent> <Leader>l :bo term ++close gol -f<CR>

" visual paste
vnoremap <silent> <C-p> "0p<CR>

" ターミナル関連 {{{

" ターミナルを開く
" a:1 new or vnew or tabnew(default is new)
" a:2 path (default is current)
" a:3 shell (default is &shell)
function! s:open_terminal(...) abort
	let open_type = 'new'
	let shell = &shell
	let path = getcwd()

	if a:0 > 0 && a:0 !=# ''
		let open_type = a:1
	endif
	if a:0 > 1 && a:2 !=# ''
		let path = a:2
	endif
	if a:0 > 2 && a:3 !=# ''
		let shell = a:3
	endif
	if open_type ==# 'new'
		let open_type = open_type
	endif

	exe printf('%s | lcd %s', open_type, path)
	exe printf('term ++curwin ++close %s', shell)
	exe 'call term_setrestore("%", printf("++close bash -c \"cd %s && bash\"", getcwd()))'
endfunction

command! -nargs=* OpenTerminal call s:open_terminal(<f-args>)

" ターミナルを開く
noremap <silent> <C-s>\ :OpenTerminal vnew<CR>
noremap <silent> <C-s>- :OpenTerminal<CR>
noremap <silent> <C-s>^ :OpenTerminal tabnew<CR>
tnoremap <silent> <C-s>\ <C-w>:OpenTerminal vnew<CR>
tnoremap <silent> <C-s>- <C-w>:OpenTerminal<CR>
tnoremap <silent> <C-s>^ <C-w>:OpenTerminal tabnew<CR>
""tnoremap <silent> <C-w> <C-w>.
""tnoremap <silent> <C-w>. <C-w>

" ターミナルノーマルモード
tnoremap <C-w>n <C-w>N

" ターミナルでウィンドウ移動
tnoremap <C-s>p <C-w>:tabprevious<CR>
tnoremap <C-s>n <C-w>:tabnext<CR>

" ウィンドウリサイズ開始
tnoremap <C-w><C-e> <C-w>:WinResizerStartResize<CR>

" リポジトリ移動
tnoremap <C-w>cd <C-w>:Repo<CR>

" 単語削除
tnoremap <C-g> <C-w>.
" }}}

" 上下の空白に移動
nnoremap <C-j> }
nnoremap <C-k> {

" review
augroup review
	au!
	au FileType review vnoremap <silent> <Leader>hi c@<cursor>{<c-r>"}<esc>
augroup END

" 改行
nnoremap gj o<esc>
nnoremap gk O<esc>

" git status
nnoremap gs :Gstatus<CR>

" バッファ一覧
nnoremap gb :Buffers<CR>

" ヘルプ
augroup helpMapping
	au!
	au FileType help nnoremap <buffer> <silent> q :q<CR>
augroup END

" コマンドラインで単語移動 {{{
cnoremap <c-b> <S-Left>
cnoremap <c-f> <S-Right>
cnoremap <c-a> <Home>
" }}}

" }}}

" vim-go settings {{{
" ファイル保存時go importを実行する
let g:go_fmt_command = 'goimports'

" ファイル保存時、linterを実行する
let g:go_metalinter_autosave = 1

" linter実行時、go vetのみを実行する
let g:go_metalinter_autosave_enabled = ['vet']

" golangci-lintを使う
let g:go_metalinter_command = "golangci-lint"

" vim-lspをしようするので、vim-goの`Ctrl+]`を無効にする
"let g:go_def_mapping_enabled = 0

" GoDocでポップアップウィンドウを使用する
let g:go_doc_popup_window = 1

" GoRunやGoTest時の画面分割方法変更
let g:go_term_mode = 'split'

" テンプレート作成を無効化
let g:go_template_autocreate = 0

" すでに開いているバッファに定義ジャンプする
let g:go_def_reuse_buffer = 1

" goplsで定義ジャンプ
let g:go_def_mode = 'gopls'

let g:go_fold_enable = ['block', 'import', 'varconst', 'package_comment']

" key mapping
augroup goMapping
	autocmd!
	au FileType go nnoremap <silent> <Leader>c <Plug>(go-coverage)
	au FileType go nnoremap <silent> <Leader>r <Plug>(go-run)
	au FileType go nnoremap <silent> <Leader>t <Plug>(go-test)
	au FileType go nnoremap <silent> <Leader>c <Plug>(go-coverage)
	au FileType go nnoremap <silent> <Leader>at :GoAddTags<CR>
	au FileType go nnoremap <silent> <Leader>rt :GoRemoveTags<CR>
	au FileType go nnoremap <silent> <Leader>fs :GoFillStruct<CR>
	au FileType go nnoremap <silent> <Leader>ki :GoKeyify<CR>
	au FileType go nnoremap <silent> <Leader>dd :GoDeclsDir<CR>
	au FileType go nnoremap <silent> <Leader>dl :GoDecls<CR>
	au FileType go nnoremap <silent> <Leader>ip :GoImpl<CR>
	au FileType go nnoremap <silent> <Leader>rn :GoRename<CR>
	au FileType go nnoremap <silent> <F12> :GoDebugStart<CR>
	au FileType go nnoremap <silent> <F12><F12> :GoDebugStop<CR>
	au FileType go set foldmethod=syntax
augroup END
" }}}

" fzf settings {{{
" ファイル一覧を出すときにプレビュー表示
command! -bang -nargs=? -complete=dir Files
			\ call fzf#vim#files(<q-args>, fzf#vim#with_preview(), <bang>0)
nnoremap <C-P> :Files<CR>
" }}}

" lsp settings {{{
"nnoremap <silent> <C-]> :LspDefinition<CR>
nnoremap <silent> <Leader>rf :LspReferences<CR>
nnoremap <silent> <Leader>im :LspImplementation<CR>

augroup LspMapping
	autocmd!
	au FileType php nnoremap <C-]> :LspDefinition<CR>
	au FileType c nnoremap <C-]> :LspDefinition<CR>
augroup END

" 非同期補完を有効にする
"let g:lsp_async_completion = 1

" lsp log
"let g:lsp_log_verbose = 0
"let g:lsp_log_file = expand('~/vim-lsp.log')

" enable signs
"let g:lsp_signs_error = {'text': '✗'}
"let g:lsp_signs_warning = {'text': '‼'}
"let g:lsp_signs_enabled = 1
"let g:lsp_diagnostics_echo_cursor = 1

" Go {{{
"augroup LspGo
"    au!
"    if executable('gopls')
"        au User lsp_setup call lsp#register_server({
"                    \ 'name': 'gopls',
"                    \ 'cmd': {server_info->['gopls', '-mode', 'stdio', '-logfile', '/Users/skanehira/gopls.log']},
"                    \ 'whitelist': ['go'],
"                    \ })
"    endif
"augroup END
" }}}

" PHP {{{
augroup LspPHP
	au!
	au User lsp_setup call lsp#register_server({
				\ 'name': 'php-language-server',
				\ 'cmd': {server_info->['php', expand('~/.cache/dein/repos/github.com/felixfbecker/php-language-server/bin/php-language-server.php')]},
				\ 'whitelist': ['php'],
				\ })
augroup END
" }}}

" JavaScript {{{
augroup LspJavaScript
	au!
	if executable('flow-language-server')
		au User lsp_setup call lsp#register_server({
					\ 'name': 'flow-language-server',
					\ 'cmd': {server_info->[&shell, &shellcmdflag, 'flow-language-server --stdio']},
					\ 'root_uri':{server_info->lsp#utils#path_to_uri(lsp#utils#find_nearest_parent_file_directory(lsp#utils#get_buffer_path(), '.flowconfig'))},
					\ 'whitelist': ['javascript', 'javascript.jsx'],
					\ })
	endif
augroup END
" }}}

" Docker {{{
augroup LspDocker
	au!
	if executable('docker-langserver')
		au User lsp_setup call lsp#register_server({
					\ 'name': 'docker-langserver',
					\ 'cmd': {server_info->[&shell, &shellcmdflag, 'docker-langserver --stdio']},
					\ 'whitelist': ['dockerfile'],
					\ })
	endif
augroup END
" }}}

" CSS {{{
augroup LspCSS
	au!
	if executable('css-languageserver')
		au User lsp_setup call lsp#register_server({
					\ 'name': 'css-languageserver',
					\ 'cmd': {server_info->[&shell, &shellcmdflag, 'css-languageserver --stdio']},
					\ 'whitelist': ['css', 'less', 'sass'],
					\ })
	endif
augroup END
" }}}

" clang {{{
augroup LspClangd
	if executable('clangd')
		au!
		au User lsp_setup call lsp#register_server({
					\ 'name': 'clangd',
					\ 'cmd': {server_info->['clangd', '-background-index']},
					\ 'whitelist': ['c', 'cpp', 'objc', 'objcpp'],
					\ })
	endif
augroup END
" }}}

" }}}

" asyncomplete settings {{{
inoremap <expr> <C-j> pumvisible() ? "\<C-n>" : "\<Tab>"
inoremap <expr> <C-k> pumvisible() ? "\<C-p>" : "\<S-Tab>"
inoremap <expr> <CR> pumvisible() ? "\<C-y>" : "\<cr>"
" }}}

" vim-markdown settings {{{
" mdを開くときの折りたたみを無効にする
let g:vim_markdown_folding_disabled = 1
" }}}

" vim-surround settings {{{
nmap <Leader>7 ysiw'
nmap <Leader>2 ysiw"
nmap <Leader>` ysiw`
nmap <Leader>8 ysiw)
nmap <Leader>[ ysiw]
nmap <Leader>{ ysiw}
" }}}

" ultisnips settings {{{
"let g:UltiSnipsExpandTrigger="<C-f>"
"let g:UltiSnipsJumpForwardTrigger="<tab>"
"let g:UltiSnipsJumpBackwardTrigger="<s-tab>"

" ultisnipes自動補完
"if has('python3')
"    call asyncomplete#register_source(asyncomplete#sources#ultisnips#get_source_options({
"                \ 'name': 'ultisnips',
"                \ 'whitelist': ['*'],
"                \ 'completor': function('asyncomplete#sources#ultisnips#completor'),
"                \ }))
"endif
" }}}

" profiling {{{
" vim +'call ProfileCursorMove()' <カーソルを動かすのが重いファイル>
function! ProfileCursorMove() abort
	let profile_file = expand('./vim-profile.log')
	if filereadable(profile_file)
		call delete(profile_file)
	endif

	normal! ggzR

	execute 'profile start ' . profile_file
	profile func *
	profile file *

	augroup ProfileCursorMove
		autocmd!
		autocmd CursorHold <buffer> profile pause | q
	augroup END

	for i in range(1000)
		call feedkeys('j')
	endfor
endfunction
" }}}

" emment {{{
let g:user_emmet_install_global = 0
let g:user_emmet_settings = {
			\ 'variables': {
			\   'lang' : 'ja'
			\ }
			\}

augroup emmet
	autocmd!
	au FileType vue,html,js EmmetInstall
	au FileType vue,html,js imap <C-f> <C-y>,
augroup END
" }}}

" lightline {{{
set laststatus=2
if !has('gui_running')
	set t_Co=256
endif
" }}}

" vim-vue {{{
" https://github.com/posva/vim-vue#my-syntax-highlighting-stops-working-randomly
" vueファイルのシンタックスが効かなくなる問題対応
augroup VueSyntax
	autocmd!
	au FileType vue syntax sync fromstart
augroup END
" }}}

" gitgutter {{{
" ハイライトを無効にする
"highlight default link GitGutterAdd          NONE
"highlight default link GitGutterChange       NONE
"highlight default link GitGutterDelete       NONE
"highlight default link GitGutterChangeDelete NONE
" }}}

" nerd commenter {{{
" コメントの後にスペースを装入
let g:NERDSpaceDelims = 1
" }}}

" calendar.vim {{{
" Googleカレンダー同期
let g:calendar_google_calendar = 1
" }}}

" sonictemplate.vim {{{
let g:sonictemplate_author = 'skanehira'
let g:sonictemplate_license = 'MIT'
" }}}

" vimhelpgenarator {{{
let g:vimhelpgenerator_version = 'Version : 0.0.1'
let g:vimhelpgenerator_author = 'Author  : skanehira <sho19921005@gmai.com>'
let g:vimhelpgenerator_uri = 'https://github.com/skanehira/'
let g:vimhelpgenerator_defaultlanguage = 'en'
" }}}

" {{{ MemoList
let g:memolist_fzf = 1
" }}}

" silicon {{{
if executable('silicon')
	let g:silicon = {
				\ 'theme':             'DarkNeon',
				\ 'font':                  'Hack',
				\ 'background':         '#aaaaff',
				\ 'shadow-color':       '#555555',
				\ 'line-pad':                   2,
				\ 'pad-horiz':                 80,
				\ 'pad-vert':                 100,
				\ 'shadow-blur-radius':         0,
				\ 'shadow-offset-x':            0,
				\ 'shadow-offset-y':            0,
				\ 'line-number':           v:true,
				\ 'round-corner':          v:true,
				\ 'window-controls':       v:true,
				\ }
endif
" }}}

" vsession {{{
let g:vsession_use_fzf = 1
" }}}

" {{{ 自作関数

" {{{ リポジトリに移動
function! s:cd_repo(shell, repo) abort
	exe 'lcd' trim(system('ghq root')) .. '/' .. a:repo
	pwd
endfunction

function! s:repo(multi, cb) abort
	if executable('ghq') && exists('*fzf#run()') && executable('fzf')
		call fzf#run({
					\ 'source': systemlist('ghq list'),
					\ 'sink': a:cb,
					\ 'options': a:multi,
					\ 'down': '40%'},
					\ )
	else
		echo "doesn't installed ghq or fzf.vim(require fzf)"
	endif
endfunction

command! Repo call s:repo('+m', function('s:cd_repo', [&shell]))
" }}}

" 新しいタブを開く {{{
function! s:open_tabs(shell, repo) abort
	exe printf('tabnew | lcd %s/%s', trim(system('ghq root')), a:repo)
endfunction

" fzf.vimのcallbackでは&shellがshになってしまうので、現在実行しているshellを渡す
command! NewTab call s:repo('-m', function('s:open_tabs', [&shell]))

" }}}

" {{{ ウィンドウ一覧
function! s:get_win_info() abort
	let l:win_info_list = []
	for win in getwininfo()
		if !has_key(win.variables, "netrw_prvfile")
			continue
		endif
		let l:tmp_file = split(win.variables.netrw_prvfile, '/')
		if empty(tmp_file)
			let l:file = '[No Name]'
		else
			let l:file = l:tmp_file[len(tmp_file)-1:][0]
		endif
		call add(l:win_info_list, {'winid':win.winid, 'file': l:file})
	endfor
	return l:win_info_list
endfunction

function! s:goto_window(ctx, id, idx) abort
	if a:idx ==# -1
		return
	endif
	call win_gotoid(a:ctx[a:idx-1].winid)
endfunction

function! s:win_list() abort
	let l:win_info = s:get_win_info()
	if empty(l:win_info)
		call s:echo_err('cannot find windows')
		return
	endif

	let l:view_content = []
	let l:min_width = 40

	for win in l:win_info
		call add(l:view_content, win.winid . "\t" . win.file)
		if len(win.file) > l:min_width
			let l:min_width = len(win.file)
		endif
	endfor
	call popup_menu(l:view_content, {
				\ 'border': [],
				\ 'borderchars': ['-','|','-','|','+','+','+','+'],
				\ 'padding': [0,0,0,0],
				\ 'filter': 'popup_filter_menu',
				\ 'callback': function('s:goto_window', [l:win_info]),
				\ 'minwidth': l:min_width,
				\ })
endfunction

command! Windows call s:win_list()
" }}}

" {{{ エラーメッセージ出力
function! s:echo_err(message) abort
	echohl ErrorMsg
	redraw
	echo a:message
	echohl None
endfunction
" }}}

" {{{ ファイル検索(ポップアップウィンドウ)
if !exists('g:loaded_select')
	let g:loaded_select = 1
	call prop_type_add('file_select', {'highlight': 'PmenuSel'})
endif

function! s:get_files(path) abort
	let l:entries = []

	for l:entry in readdir(a:path)
		if l:entry[0] ==# '.'
			continue
		endif
		let l:path_entry = a:path . '/' . l:entry
		if isdirectory(l:path_entry)
			let l:entries += s:get_files(l:path_entry)
		else
			if l:path_entry[0] ==# '.'
				let l:path_entry = l:path_entry[2:]
			endif
			cal add(l:entries, l:path_entry)
		endif
	endfor
	return l:entries
endfunction

function! s:highlight(ctx) abort
	let l:buf = winbufnr(a:ctx.id)
	let l:length = len(a:ctx.files[a:ctx.select])
	let l:lnum_end = len(a:ctx.files)
	let l:lnum = a:ctx.select - a:ctx.offset + 1
	if a:ctx.search_mode ==# 1 && !empty(a:ctx.filter_files)
		let l:length = len(a:ctx.filter_files[a:ctx.select])
		let l:lnum_end = len(a:ctx.filter_files)
	endif

	call prop_clear(1, l:lnum_end, {
				\ 'bufnr': l:buf,
				\ })

	call prop_add(l:lnum, 1, {
				\ 'bufnr': l:buf,
				\ 'type': 'file_select',
				\ 'length': l:length,
				\ })

	call win_execute(a:ctx.id, 'redraw')
endfunction

function! s:update_files(ctx) abort
	let l:files = copy(a:ctx.files)
	if a:ctx.search_mode
		let l:tmp = []
		for file in l:files
			if file =~? a:ctx.word[1:]
				call add(l:tmp, file)
			endif
		endfor
		let l:files = l:tmp
		let a:ctx.filter_files = l:tmp
	endif
	echo a:ctx.word
	call popup_settext(a:ctx.id, l:files[a:ctx.offset:])
	call s:highlight(a:ctx)
	redraw
endfunction

function! s:popup_filter(ctx, id, key) abort
	let l:buf = winbufnr(a:id)
	let l:file = a:ctx.files[a:ctx.select]

	if a:ctx.search_mode ==# 0
		if a:key ==# 'q' || a:key ==# 'x'
			call popup_close(a:id)
			return 1
		elseif a:key ==# '/'
			let a:ctx.search_mode = 1
			let a:ctx.word .= a:key
		elseif a:key ==# "\n" || a:key ==# "\r"
			return s:open_file(a:id, 'e', l:file)
		elseif a:key ==# ""
			return s:open_file(a:id, 'new', l:file)
		elseif a:key ==# ""
			return s:open_file(a:id, 'vnew', l:file)
		elseif a:key ==# ""
			return s:open_file(a:id, 'tabnew' , l:file)
		elseif a:key ==# 'j'
			let a:ctx.select += a:ctx.select ==# len(a:ctx.files)-1 ? 0 : 1
			if a:ctx.select >= a:ctx.offset + a:ctx.maxheight
				let a:ctx.offset = a:ctx.select - (a:ctx.maxheight - 1)
			endif
		elseif a:key ==# 'k'
			let a:ctx.select -= a:ctx.select ==# 0 ? 0 : 1
			if a:ctx.select < a:ctx.offset
				let a:ctx.offset = a:ctx.select
			endif
		elseif a:key ==# '0'
			let a:ctx.select = 0
			let a:ctx.offset = 0
		elseif a:key ==# 'G'
			let a:ctx.select = len(a:ctx.files)-1
			let a:ctx.offset = len(a:ctx.files) - (a:ctx.maxheight)
		endif
	else
		" 検索モードの場合
		if !empty(a:ctx.filter_files)
			let l:file = a:ctx.filter_files[a:ctx.select]
		else
			let l:file = ''
		endif
		if a:key ==# "\<bs>" || a:key ==# "\b"
			if strlen(a:ctx.word) == 1
				let a:ctx.word = ''
				let a:ctx.search_mode = 0
			else
				let a:ctx.word = a:ctx.word[:len(a:ctx.word)-2]
			endif
		elseif a:key ==# '}'
			let a:ctx.select += a:ctx.select ==# len(a:ctx.filter_files)-1 ? 0 : 1
			if a:ctx.select >= a:ctx.offset + a:ctx.maxheight
				let a:ctx.offset = a:ctx.select - (a:ctx.maxheight - 1)
			endif
		elseif a:key ==# '{'
			let a:ctx.select -= a:ctx.select ==# 0 ? 0 : 1
			if a:ctx.select < a:ctx.offset
				let a:ctx.offset = a:ctx.select
			endif
		elseif a:key ==# "\n" || a:key ==# "\r"
			return s:open_file(a:id, 'e', l:file)
		elseif a:key ==# ""
			return s:open_file(a:id, 'new', l:file)
		elseif a:key ==# ""
			return s:open_file(a:id, 'vnew', l:file)
		elseif a:key ==# ""
			return s:open_file(a:id, 'tabnew' , l:file)
		elseif a:key !=# "\<CursorHold>"
			let a:ctx.select = 0
			let a:ctx.offset = 0
			let a:ctx.word .= a:key
		endif
	endif

	if a:key !=# "\<CursorHold>"
		call s:update_files(a:ctx)
	endif
	return 1
endfunction

function! s:open_file(id, key, file) abort
	if empty(a:file)
		return 1
	endif
	call popup_close(a:id)
	exe a:key a:file
	return 1
endfunction

function! s:popup_files(...) abort
	let l:path = '.'
	if a:0 > 0
		let l:path = a:1
	endif

	let l:files = s:get_files(l:path)
	if empty(l:files)
		echo 'not any files'
		return
	endif

	let l:ctx = { 'select': 0,
				\ 'files': l:files,
				\ 'offset': 0,
				\ 'maxheight': 15,
				\ 'word' : '',
				\ 'search_mode': 0,
				\ 'filter_files': [],
				\ }

	let l:maxwidth = 0
	for file in l:ctx.files
		let length = len(file)
		if length > l:maxwidth
			let l:maxwidth = length
		endif
	endfor

	let l:ctx.id = popup_create(l:ctx.files, {
				\ 'filter': function('s:popup_filter', [l:ctx]),
				\ 'borderchars': ['-','|','-','|','+','+','+','+'],
				\ 'border': [],
				\ 'maxheight': l:ctx.maxheight,
				\ 'minwidth': l:maxwidth,
				\ 'padding': [0,0,0,0],
				\ 'mapping': 0,
				\ })

	call s:highlight(l:ctx)
endfunction

command! -nargs=? PopupFiles call s:popup_files(<f-args>)

" }}}

" {{{ ディレクトリ自動生成
augroup vimrc-auto-mkdir
  autocmd!
  autocmd BufWritePre * call s:auto_mkdir(expand('<afile>:p:h'), v:cmdbang)
  function! s:auto_mkdir(dir, force)
    if !isdirectory(a:dir) && (a:force ||
    \    input(printf('"%s" does not exist. Create? [y/N]', a:dir)) =~? '^y\%[es]$')
      call mkdir(iconv(a:dir, &encoding, &termencoding), 'p')
    endif
  endfunction
augroup END
" }}}

" }}}

" vim: tw=78 sw=4 foldmethod=marker