1
0
mirror of https://github.com/amix/vimrc synced 2025-07-10 03:25:00 +08:00

add Previm and window management

This commit is contained in:
Max Alcala
2016-02-23 14:35:43 -06:00
parent f3143286d3
commit 14bf4d01c6
45 changed files with 3967 additions and 77 deletions

View File

@ -0,0 +1,240 @@
" AUTHOR: kanno <akapanna@gmail.com>
" License: This file is placed in the public domain.
let s:save_cpo = &cpo
set cpo&vim
let s:V = vital#of('previm')
let s:File = s:V.import('System.File')
let s:newline_character = "\n"
function! previm#open(preview_html_file)
call previm#refresh()
if exists('g:previm_open_cmd') && !empty(g:previm_open_cmd)
call s:system(g:previm_open_cmd . ' ''' . a:preview_html_file . '''')
elseif s:exists_openbrowser()
" fix temporary(the cause unknown)
call s:apply_openbrowser('file:///' . a:preview_html_file)
else
call s:echo_err('Command for the open can not be found. show detail :h previm#open')
endif
endfunction
function! s:exists_openbrowser()
try
call openbrowser#load()
return 1
catch /E117.*/
return 0
endtry
endfunction
function! s:apply_openbrowser(path)
let saved_in_vim = g:openbrowser_open_filepath_in_vim
try
let g:openbrowser_open_filepath_in_vim = 0
call openbrowser#open(a:path)
finally
let g:openbrowser_open_filepath_in_vim = saved_in_vim
endtry
endfunction
function! previm#refresh()
call previm#refresh_css()
call previm#refresh_js()
endfunction
function! previm#refresh_css()
let css = []
if get(g:, 'previm_disable_default_css', 0) !=# 1
call extend(css, ["@import url('origin.css');", "@import url('lib/github.css');"])
endif
if exists('g:previm_custom_css_path')
let css_path = expand(g:previm_custom_css_path)
if filereadable(css_path)
call s:File.copy(css_path, previm#make_preview_file_path('css/user_custom.css'))
call add(css, "@import url('user_custom.css');")
else
call s:echo_err('[Previm]failed load custom css. ' . css_path)
endif
endif
call writefile(css, previm#make_preview_file_path('css/previm.css'))
endfunction
" TODO: test(refresh_cssと同じように)
function! previm#refresh_js()
let encoded_lines = split(iconv(s:function_template(), &encoding, 'utf-8'), s:newline_character)
call writefile(encoded_lines, previm#make_preview_file_path('js/previm-function.js'))
endfunction
let s:base_dir = expand('<sfile>:p:h')
function! previm#make_preview_file_path(path)
return s:base_dir . '/../preview/' . a:path
endfunction
" NOTE: getFileType()の必要性について。
" js側でファイル名の拡張子から取得すればこの関数は不要だが、
" その場合「.txtだが内部的なファイルタイプがmarkdown」といった場合に動かなくなる。
" そのためVim側できちんとファイルタイプを返すようにしている。
function! s:function_template()
let current_file = expand('%:p')
return join([
\ 'function isShowHeader() {',
\ printf('return %s;', get(g:, 'previm_show_header', 1)),
\ '}',
\ '',
\ 'function getFileName() {',
\ printf('return "%s";', s:escape_backslash(current_file)),
\ '}',
\ '',
\ 'function getFileType() {',
\ printf('return "%s";', &filetype),
\ '}',
\ '',
\ 'function getLastModified() {',
\ printf('return "%s";', s:get_last_modified_time()),
\ '}',
\ '',
\ 'function getContent() {',
\ printf('return "%s";', previm#convert_to_content(getline(1, '$'))),
\ '}',
\], s:newline_character)
endfunction
function! s:get_last_modified_time()
if exists('*strftime')
return strftime("%Y/%m/%d (%a) %H:%M:%S")
endif
return '(strftime cannot be performed.)'
endfunction
function! s:escape_backslash(text)
return escape(a:text, '\')
endfunction
function! s:system(cmd)
if get(g:, 'previm_disable_vimproc', 0)
return system(a:cmd)
endif
try
" NOTE: WindowsでDOS窓を開かず実行してくれるらしいのでvimprocを使う
let result = vimproc#system(a:cmd)
return result
catch /E117.*/
return system(a:cmd)
endtry
endfunction
function! s:do_external_parse(lines)
if &filetype !=# "rst"
return a:lines
endif
" NOTE: 本来は外部コマンドに頼りたくない
" いずれjsパーサーが出てきたときに移行するが、
" その時に混乱を招かないように設定でrst2htmlへのパスを持つことはしない
let cmd = ''
if executable("rst2html.py") ==# 1
let cmd = "rst2html.py"
elseif executable("rst2html") ==# 1
let cmd = "rst2html"
endif
if empty(cmd)
call s:echo_err("rst2html.py or rst2html has not been installed, you can not run")
return a:lines
endif
let temp = tempname()
call writefile(a:lines, temp)
return split(s:system(cmd . ' ' . s:escape_backslash(temp)), "\n")
endfunction
function! previm#convert_to_content(lines)
let mkd_dir = s:escape_backslash(expand('%:p:h'))
if has("win32unix")
" convert cygwin path to windows path
let mkd_dir = s:escape_backslash(substitute(system('cygpath -wa ' . mkd_dir), "\n$", '', ''))
endif
let converted_lines = []
for line in s:do_external_parse(a:lines)
" TODO エスケープの理由と順番の依存度が複雑
let escaped = substitute(line, '\', '\\\\', 'g')
let escaped = previm#relative_to_absolute_imgpath(escaped, mkd_dir)
let escaped = substitute(escaped, '"', '\\"', 'g')
call add(converted_lines, escaped)
endfor
return join(converted_lines, "\\n")
endfunction
" convert example
" if unix:
" ![alt](file://localhost/Users/kanno/Pictures/img.png "title")
" if win:
" ![alt](file://localhost/C:\Documents%20and%20Settings\folder/pictures\img.png "title")
function! previm#relative_to_absolute_imgpath(text, mkd_dir)
let elem = previm#fetch_imgpath_elements(a:text)
if empty(elem.path)
return a:text
endif
for protocol in ['http://', 'https://', 'file://']
if s:start_with(elem.path, protocol)
" is absolute path
return a:text
endif
endfor
" escape backslash for substitute (see pull/#34)
let dir = substitute(a:mkd_dir, '\\', '\\\\', 'g')
let elem.path = substitute(elem.path, '\\', '\\\\', 'g')
" マルチバイトの解釈はブラウザに任せるのでURLエンコードしない
" 半角空白だけはエラーの原因になるのでURLエンコード対象とする
let pre_slash = s:start_with(dir, '/') ? '' : '/'
let local_path = substitute(dir.'/'.elem.path, ' ', '%20', 'g')
let prev_imgpath = ''
let new_imgpath = ''
if empty(elem.title)
let prev_imgpath = printf('!\[%s\](%s)', elem.alt, elem.path)
let new_imgpath = printf('![%s](file://localhost%s%s)', elem.alt, pre_slash, local_path)
else
let prev_imgpath = printf('!\[%s\](%s "%s")', elem.alt, elem.path, elem.title)
let new_imgpath = printf('![%s](file://localhost%s%s "%s")', elem.alt, pre_slash, local_path, elem.title)
endif
" unify quote
let text = substitute(a:text, "'", '"', 'g')
return substitute(text, prev_imgpath, new_imgpath, '')
endfunction
function! previm#fetch_imgpath_elements(text)
let elem = {'alt': '', 'path': '', 'title': ''}
let matched = matchlist(a:text, '!\[\(.*\)\](\(.*\))')
if empty(matched)
return elem
endif
let elem.alt = matched[1]
return extend(elem, s:fetch_path_and_title(matched[2]))
endfunction
function! s:fetch_path_and_title(path)
let matched = matchlist(a:path, '\(.*\)\s\+["'']\(.*\)["'']')
if empty(matched)
return {'path': a:path}
endif
let trimmed_path = matchstr(matched[1],'^\s*\zs.\{-}\ze\s*$')
return {'path': trimmed_path, 'title': matched[2]}
endfunction
function! s:start_with(haystock, needle)
return stridx(a:haystock, a:needle) ==# 0
endfunction
function! s:echo_err(msg)
echohl WarningMsg
echomsg a:msg
echohl None
endfunction
let &cpo = s:save_cpo
unlet! s:save_cpo

View File

@ -0,0 +1,12 @@
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

View File

@ -0,0 +1,304 @@
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>'))

View File

@ -0,0 +1,237 @@
" Utilities for file copy/move/mkdir/etc.
let s:save_cpo = &cpo
set cpo&vim
let s:is_unix = has('unix')
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')))
" As of 7.4.122, the system()'s 1st argument is converted internally by Vim.
" Note that Patch 7.4.122 does not convert system()'s 2nd argument and
" return-value. We must convert them manually.
let s:need_trans = v:version < 704 || (v:version == 704 && !has('patch122'))
" Open a file.
function! s:open(filename) "{{{
let filename = fnamemodify(a:filename, ':p')
" Detect desktop environment.
if s:is_windows
" For URI only.
if s:need_trans
let filename = iconv(filename, &encoding, 'char')
endif
silent execute '!start rundll32 url.dll,FileProtocolHandler' filename
elseif s:is_cygwin
" Cygwin.
call system(printf('%s %s', 'cygstart',
\ shellescape(filename)))
elseif executable('xdg-open')
" Linux.
call system(printf('%s %s &', 'xdg-open',
\ shellescape(filename)))
elseif exists('$KDE_FULL_SESSION') && $KDE_FULL_SESSION ==# 'true'
" KDE.
call system(printf('%s %s &', 'kioclient exec',
\ shellescape(filename)))
elseif exists('$GNOME_DESKTOP_SESSION_ID')
" GNOME.
call system(printf('%s %s &', 'gnome-open',
\ shellescape(filename)))
elseif executable('exo-open')
" Xfce.
call system(printf('%s %s &', 'exo-open',
\ shellescape(filename)))
elseif s:is_mac && executable('open')
" Mac OS.
call system(printf('%s %s &', 'open',
\ shellescape(filename)))
else
" Give up.
throw 'Not supported.'
endif
endfunction "}}}
" Move a file.
" Dispatch s:move_exe() or s:move_vim().
" FIXME: Currently s:move_vim() does not support
" moving a directory.
function! s:move(src, dest) "{{{
if s:_has_move_exe() || isdirectory(a:src)
return s:move_exe(a:src, a:dest)
else
return s:move_vim(a:src, a:dest)
endif
endfunction "}}}
if s:is_unix
function! s:_has_move_exe()
return executable('mv')
endfunction
elseif s:is_windows
function! s:_has_move_exe()
return 1
endfunction
else
function! s:_has_move_exe()
throw 'vital: System.File._has_move_exe(): your platform is not supported'
endfunction
endif
" Move a file.
" Implemented by external program.
if s:is_unix
function! s:move_exe(src, dest)
if !s:_has_move_exe() | return 0 | endif
let [src, dest] = [a:src, a:dest]
call system('mv ' . shellescape(src) . ' ' . shellescape(dest))
return !v:shell_error
endfunction
elseif s:is_windows
function! s:move_exe(src, dest)
if !s:_has_move_exe() | return 0 | endif
let [src, dest] = [a:src, a:dest]
" Normalize successive slashes to one slash.
let src = substitute(src, '[/\\]\+', '\', 'g')
let dest = substitute(dest, '[/\\]\+', '\', 'g')
" src must not have trailing '\'.
let src = substitute(src, '\\$', '', 'g')
" All characters must be encoded to system encoding.
if s:need_trans
let src = iconv(src, &encoding, 'char')
let dest = iconv(dest, &encoding, 'char')
endif
let cmd_exe = (&shell =~? 'cmd\.exe$' ? '' : 'cmd /c ')
call system(cmd_exe . 'move /y ' . src . ' ' . dest)
return !v:shell_error
endfunction
else
function! s:move_exe()
throw 'vital: System.File.move_exe(): your platform is not supported'
endfunction
endif
" Move a file.
" Implemented by pure Vim script.
function! s:move_vim(src, dest) "{{{
return !rename(a:src, a:dest)
endfunction "}}}
" Copy a file.
" Dispatch s:copy_exe() or s:copy_vim().
function! s:copy(src, dest) "{{{
if s:_has_copy_exe()
return s:copy_exe(a:src, a:dest)
else
return s:copy_vim(a:src, a:dest)
endif
endfunction "}}}
if s:is_unix
function! s:_has_copy_exe()
return executable('cp')
endfunction
elseif s:is_windows
function! s:_has_copy_exe()
return 1
endfunction
else
function! s:_has_copy_exe()
throw 'vital: System.File._has_copy_exe(): your platform is not supported'
endfunction
endif
" Copy a file.
" Implemented by external program.
if s:is_unix
function! s:copy_exe(src, dest)
if !s:_has_copy_exe() | return 0 | endif
let [src, dest] = [a:src, a:dest]
call system('cp ' . shellescape(src) . ' ' . shellescape(dest))
return !v:shell_error
endfunction
elseif s:is_windows
function! s:copy_exe(src, dest)
if !s:_has_copy_exe() | return 0 | endif
let [src, dest] = [a:src, a:dest]
let src = substitute(src, '/', '\', 'g')
let dest = substitute(dest, '/', '\', 'g')
let cmd_exe = (&shell =~? 'cmd\.exe$' ? '' : 'cmd /c ')
call system(cmd_exe . 'copy /y ' . src . ' ' . dest)
return !v:shell_error
endfunction
else
function! s:copy_exe()
throw 'vital: System.File.copy_exe(): your platform is not supported'
endfunction
endif
" Copy a file.
" Implemented by pure Vim script.
function! s:copy_vim(src, dest) "{{{
let ret = writefile(readfile(a:src, "b"), a:dest, "b")
if ret == -1
return 0
endif
return 1
endfunction "}}}
" mkdir() but does not throw an exception.
" Returns true if success.
" Returns false if failure.
function! s:mkdir_nothrow(...) "{{{
try
return call('mkdir', a:000)
catch
return 0
endtry
endfunction "}}}
" Delete a file/directory.
if s:is_unix
function! s:rmdir(path, ...)
let flags = a:0 ? a:1 : ''
let cmd = flags =~# 'r' ? 'rm -r' : 'rmdir'
let cmd .= flags =~# 'f' && cmd ==# 'rm -r' ? ' -f' : ''
let ret = system(cmd . ' ' . shellescape(a:path))
if v:shell_error
let ret = iconv(ret, 'char', &encoding)
throw substitute(ret, '\n', '', 'g')
endif
endfunction
elseif s:is_windows
function! s:rmdir(path, ...)
let flags = a:0 ? a:1 : ''
if &shell =~? "sh$"
let cmd = flags =~# 'r' ? 'rm -r' : 'rmdir'
let cmd .= flags =~# 'f' && cmd ==# 'rm -r' ? ' -f' : ''
let ret = system(cmd . ' ' . shellescape(a:path))
else
" 'f' flag does not make sense.
let cmd = 'rmdir /Q'
let cmd .= flags =~# 'r' ? ' /S' : ''
let ret = system(cmd . ' "' . a:path . '"')
endif
if v:shell_error
let ret = iconv(ret, 'char', &encoding)
throw substitute(ret, '\n', '', 'g')
endif
endfunction
else
function! s:rmdir(...)
throw 'vital: System.File.rmdir(): your platform is not supported'
endfunction
endif
let &cpo = s:save_cpo
unlet s:save_cpo
" vim:set et ts=2 sts=2 sw=2 tw=0:

View File

@ -0,0 +1,4 @@
previm
e8ec38a
System.File