1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-01-23 07:00:04 +08:00

feat(plantuml): add java_command option

close https://github.com/SpaceVim/SpaceVim/issues/4586
This commit is contained in:
Wang Shidong 2022-02-20 12:22:00 +08:00 committed by GitHub
parent dfb8b10556
commit 6975374b3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 16393 additions and 47 deletions

View File

@ -6,11 +6,46 @@
" License: GPLv3
"=============================================================================
""
" @section lang#plantuml, layers-lang-plantuml
" @parentsection layers
" This layer is for plantuml development, disabled by default, to enable this
" layer, add following snippet to your SpaceVim configuration file.
" >
" [[layers]]
" name = 'lang#plantuml'
" <
"
" @subsection layer options
"
" 1. `java_command`: Set the path of java command, by default, it is `java`
" >
" [[layers]]
" name = 'lang#plantuml'
" java_command = 'path/to/java'
" <
" 2. `plantuml_jar_path`: Set the path of `pluatuml.jar`.
" >
" [[layers]]
" name = 'lang#plantuml'
" plantuml_jar_path = 'path/to/plantuml.jar'
" <
"
" @subsection Key bindings
" >
" Mode Key Function
" ---------------------------------------------
" normal SPC l p preview uml file
" normal SPC l c stop preview
" normal SPC l s save uml file
" <
"
function! SpaceVim#layers#lang#plantuml#plugins() abort
let plugins = []
call add(plugins, ['aklt/plantuml-syntax', {'on_ft' : 'plantuml'}])
call add(plugins, ['wsdjeg/vim-slumlord', {'on_ft' : 'plantuml'}])
call add(plugins, ['weirongxu/plantuml-previewer.vim', {'depends': 'open-browser.vim'}])
call add(plugins, [g:_spacevim_root_dir . 'bundle/plantuml-previewer.vim', {'merged':0}])
return plugins
endfunction
@ -28,6 +63,7 @@ let s:plantuml_jar_path = ''
function! SpaceVim#layers#lang#plantuml#set_variable(var) abort
let s:plantuml_jar_path = get(a:var, 'plantuml_jar_path', s:plantuml_jar_path)
let g:plantuml_java_command = get(a:var, 'java_command', 'java')
endfunction

View File

@ -0,0 +1,6 @@
/viewer/tmp.svg
/viewer/tmp.png
/viewer/tmp.js
/viewer/tmp.puml
/viewer/node_modules
/tmp

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017 Weirong Xu <weirongxu.raidou@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,71 @@
# Plantuml Previewer Vim
Vim/NeoVim plugin for preview [PlantUML](http://plantuml.com/)
![image](https://user-images.githubusercontent.com/1709861/40650003-dcd75a76-6364-11e8-8cb1-40d710a0cc0a.png)
## Dependencies
* Java
* Graphviz (https://www.graphviz.org/download/)
* brew install graphviz
* apt-get install graphviz
* [open-browser.vim](https://github.com/tyru/open-browser.vim)
* [aklt/plantuml-syntax](https://github.com/aklt/plantuml-syntax) (vim syntax file for plantuml)
## Usage
1. Start editing plantuml file in Vim
2. Run `:PlantumlOpen` to open previewer webpage in browser
3. Saving plantuml file in Vim, then previewer webpage will refresh
### Commands
#### PlantumlOpen
Open previewer webpage in browser, and watch current buffer
#### PlantumlStart
Like `PlantumlOpen`, but won't open in browser
#### PlantumlStop
Stop watch buffer
#### PlantumlSave [filepath] [format]
Export uml diagram to file path
Available formats
> png, svg, eps, pdf, vdx, xmi,
> scxml, html, txt, utxt, latex
Example:
```
:e diagram.puml
:PlantumlSave
:PlantumlSave diagram.png
:PlantumlSave diagram.svg
```
### Variables
#### `g:plantuml_previewer#plantuml_jar_path`
Custom plantuml.jar file path
If plant uml was installed by homebrew, you can add the following code to your `.vimrc` to use the version installed by homebrew:
```vim
au FileType plantuml let g:plantuml_previewer#plantuml_jar_path = get(
\ matchlist(system('cat `which plantuml` | grep plantuml.jar'), '\v.*\s[''"]?(\S+plantuml\.jar).*'),
\ 1,
\ 0
\)
```
#### `g:plantuml_previewer#save_format`
`:PlantumlSave` default format
Default: 'png'
#### `g:plantuml_previewer#viewer_path`
Custom plantuml viewer path
The plugin will copy viewer to here if the directory does not exist
And `tmp.puml` and `tmp.svg` will output to here
## Related
* [vim-slumlord](https://github.com/scrooloose/vim-slumlord)
* [previm](https://github.com/kannokanno/previm)

View File

@ -0,0 +1,210 @@
let s:Process = vital#plantuml_previewer#new().import('System.Process')
let s:Job = vital#plantuml_previewer#new().import('System.Job')
let s:is_win = has('win32') || has('win64') || has('win95')
let s:base_path = fnameescape(expand("<sfile>:p:h")) . '/..'
let s:default_jar_path = s:base_path . '/lib/plantuml.jar'
let s:tmp_path = s:base_path . '/tmp'
let s:save_as_script_path = s:base_path . '/script/save-as' . (s:is_win ? '.cmd' : '.sh')
let s:update_viewer_script_path = s:base_path . '/script/update-viewer' . (s:is_win ? '.cmd' : '.sh')
let s:watched_bufnr = 0
let s:started = v:false
let s:java = get(g:, 'plantuml_java_command', 'java')
function! plantuml_previewer#start() "{{{
if !executable(s:java)
echoerr 'require java command'
return v:false
endif
let viewer_path = s:viewer_path()
if !isdirectory(viewer_path) && !filereadable(viewer_path)
call plantuml_previewer#copy_viewer_directory()
endif
call delete(s:viewer_tmp_puml_path())
call delete(s:viewer_tmp_svg_path())
let s:watched_bufnr = bufnr('%')
call plantuml_previewer#refresh(s:watched_bufnr)
augroup plantuml_previewer
autocmd!
autocmd BufWritePost *.pu,*.uml,*.plantuml,*.puml,*.iuml call plantuml_previewer#refresh(s:watched_bufnr)
augroup END
return v:true
endfunction "}}}
function! plantuml_previewer#open() "{{{
if !exists('*OpenBrowser')
echoerr 'require open-browser.vim'
return v:false
endif
let start_result = plantuml_previewer#start()
if !start_result
return v:false
endif
call OpenBrowser(s:viewer_html_path())
let s:started = v:true
return v:true
endfunction "}}}
function! plantuml_previewer#stop() "{{{
augroup plantuml_previewer
autocmd!
augroup END
let s:started = v:false
endfunction "}}}
function! plantuml_previewer#toggle() abort
if s:started
call plantuml_previewer#stop()
echo 'plantuml-previewer stopped'
else
let open_result = plantuml_previewer#open()
if open_result
echo 'plantuml-previewer opened'
endif
endif
endfunction
function! s:is_zero(val) "{{{
return type(a:val) == type(0) && a:val == 0
endfunction "}}}
function! plantuml_previewer#copy_viewer_directory() "{{{
let viewer_path = s:viewer_path()
let default_viewer_path = plantuml_previewer#default_viewer_path()
if viewer_path != default_viewer_path
if s:is_win
call system('xcopy ' . default_viewer_path . ' ' . g:plantuml_previewer#viewer_path . ' /O /X /E /H /K')
else
call system('cp -r ' . default_viewer_path . ' ' . g:plantuml_previewer#viewer_path)
endif
echom 'copy ' . default_viewer_path . ' -> ' . viewer_path
endif
endfunction "}}}
function! plantuml_previewer#default_viewer_path() "{{{
return s:base_path . '/viewer'
endfunction "}}}
function! s:viewer_path() "{{{
let path = get(g:, 'plantuml_previewer#viewer_path', 0)
return s:is_zero(path) ? plantuml_previewer#default_viewer_path() : fnameescape(path)
endfunction "}}}
function! s:viewer_tmp_puml_path() "{{{
return s:viewer_path() . '/tmp.puml'
endfunction "}}}
function! s:viewer_tmp_svg_path() "{{{
return s:viewer_path() . '/tmp.svg'
endfunction "}}}
function! s:viewer_tmp_js_path() "{{{
return s:viewer_path() . '/tmp.js'
endfunction "}}}
function! s:viewer_html_path() "{{{
return s:viewer_path() . '/index.html'
endfunction "}}}
function! s:jar_path() "{{{
let path = get(g:, 'plantuml_previewer#plantuml_jar_path', 0)
return s:is_zero(path) ? s:default_jar_path : path
endfunction "}}}
function! s:save_format() "{{{
return get(g:, 'plantuml_previewer#save_format', 'png')
endfunction "}}}
function! s:ext_to_fmt(ext) "{{{
return a:ext == 'tex' ? 'latex' : a:ext
endfunction "}}}
function! s:fmt_to_ext(fmt) "{{{
return a:fmt == 'latex' ? 'tex' : a:fmt
endfunction "}}}
function! s:run_in_background(cmd) "{{{
if s:Job.is_available()
call s:Job.start(a:cmd)
else
try
call s:Process.execute(a:cmd, {
\ 'background': 1,
\})
catch
call s:Process.execute(a:cmd)
endtry
endif
endfunction "}}}
function! s:normalize_path(path) "{{{
return simplify(expand(a:path, 1))
endfunction "}}}
function! plantuml_previewer#refresh(bufnr) "{{{
let puml_src_path = fnamemodify(bufname(a:bufnr), ':p')
let puml_filename = fnamemodify(puml_src_path, ':t:r')
let image_type = 'svg'
let image_ext = s:fmt_to_ext(image_type)
let output_dir_path = s:tmp_path
let output_path = output_dir_path . '/' . puml_filename . '.' . image_ext
let finial_path = s:viewer_path() . '/tmp.' . image_ext
let cmd = [
\ s:update_viewer_script_path,
\ s:java,
\ s:jar_path(),
\ puml_src_path,
\ s:normalize_path(output_dir_path),
\ s:normalize_path(output_path),
\ s:normalize_path(finial_path),
\ image_type,
\ localtime(),
\ s:normalize_path(s:viewer_tmp_js_path()),
\ ]
call s:run_in_background(cmd)
endfunction "}}}
function! plantuml_previewer#save_as(...) "{{{
if !executable('java')
echoerr 'require java command'
return
endif
let save_path = get(a:000, 0, 0)
let image_type = get(a:000, 1, 0)
if s:is_zero(save_path)
let source_name = expand('%:t:r')
let save_path = printf("%s.%s", source_name, s:fmt_to_ext(s:save_format()))
else
let save_path = fnamemodify(save_path, ':p')
endif
if s:is_zero(image_type)
let ext = fnamemodify(save_path, ':e')
let image_type = ext == '' ? s:save_format() : s:ext_to_fmt(ext)
endif
let puml_src_path = expand('%:p')
let puml_filename = fnamemodify(puml_src_path, ':t:r')
let image_ext = s:fmt_to_ext(image_type)
let output_dir_path = s:tmp_path
let output_path = output_dir_path . '/' . puml_filename . '.' . image_ext
call mkdir(fnamemodify(save_path, ':p:h'), 'p')
let cmd = [
\ s:save_as_script_path,
\ s:java,
\ s:jar_path(),
\ puml_src_path,
\ s:normalize_path(output_dir_path),
\ s:normalize_path(output_path),
\ s:normalize_path(save_path),
\ image_type,
\ ]
call s:run_in_background(cmd)
endfunction "}}}

View File

@ -0,0 +1,9 @@
let s:_plugin_name = expand('<sfile>:t:r')
function! vital#{s:_plugin_name}#new() abort
return vital#{s:_plugin_name[1:]}#new()
endfunction
function! vital#{s:_plugin_name}#function(funcname) abort
silent! return function(a:funcname)
endfunction

View File

@ -0,0 +1,151 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_plantuml_previewer#Data#Dict#import() abort', printf("return map({'_vital_depends': '', 'clear': '', 'max_by': '', 'foldl': '', 'pick': '', 'from_list': '', 'swap': '', 'omit': '', 'min_by': '', 'foldr': '', 'make_index': '', 'make': '', '_vital_loaded': ''}, \"vital#_plantuml_previewer#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
" Utilities for dictionary.
let s:save_cpo = &cpo
set cpo&vim
function! s:_vital_loaded(V) abort
let s:t = a:V.import('Vim.Type').types
endfunction
function! s:_vital_depends() abort
return ['Vim.Type']
endfunction
function! s:_ensure_key(key) abort
let t = type(a:key)
if t != s:t.string && t != s:t.number
throw 'vital: Data.Dict: Invalid key: ' . string(a:key)
endif
endfunction
function! s:from_list(list) abort
let dict = {}
let i = 0
let len = len(a:list)
while i < len
if type(a:list[i]) == s:t.list
let key_value = a:list[i]
if len(key_value) != 2
throw 'vital: Data.Dict: Invalid key-value pair index at ' . i
endif
call s:_ensure_key(key_value[0])
let dict[key_value[0]] = key_value[1]
let i += 1
else
if len <= i + 1
throw 'vital: Data.Dict: Invalid key-value pair index at ' . i
endif
call s:_ensure_key(a:list[i])
let dict[a:list[i]] = a:list[i + 1]
let i += 2
endif
endwhile
return dict
endfunction
" Makes a dict from keys and values
function! s:make(keys, values, ...) abort
let dict = {}
let fill = a:0 ? a:1 : 0
for i in range(len(a:keys))
let key = type(a:keys[i]) == s:t.string ? a:keys[i] : string(a:keys[i])
if key ==# ''
throw "vital: Data.Dict: Can't use an empty string for key."
endif
let dict[key] = get(a:values, i, fill)
endfor
return dict
endfunction
" Swaps keys and values
function! s:swap(dict) abort
return s:make(values(a:dict), keys(a:dict))
endfunction
" Makes a index dict from a list
function! s:make_index(list, ...) abort
let value = a:0 ? a:1 : 1
return s:make(a:list, [], value)
endfunction
function! s:pick(dict, keys) abort
let new_dict = {}
for key in a:keys
if has_key(a:dict, key)
let new_dict[key] = a:dict[key]
endif
endfor
return new_dict
endfunction
function! s:omit(dict, keys) abort
let new_dict = copy(a:dict)
for key in a:keys
if has_key(a:dict, key)
call remove(new_dict, key)
endif
endfor
return new_dict
endfunction
function! s:clear(dict) abort
for key in keys(a:dict)
call remove(a:dict, key)
endfor
return a:dict
endfunction
function! s:_max_by(dict, expr) abort
let dict = s:swap(map(copy(a:dict), a:expr))
let key = dict[max(keys(dict))]
return [key, a:dict[key]]
endfunction
function! s:max_by(dict, expr) abort
if empty(a:dict)
throw 'vital: Data.Dict: Empty dictionary'
endif
return s:_max_by(a:dict, a:expr)
endfunction
function! s:min_by(dict, expr) abort
if empty(a:dict)
throw 'vital: Data.Dict: Empty dictionary'
endif
return s:_max_by(a:dict, '-(' . a:expr . ')')
endfunction
function! s:_foldl(f, init, xs) abort
let memo = a:init
for [k, v] in a:xs
let expr = substitute(a:f, 'v:key', string(k), 'g')
let expr = substitute(expr, 'v:val', string(v), 'g')
let expr = substitute(expr, 'v:memo', string(memo), 'g')
unlet memo
let memo = eval(expr)
endfor
return memo
endfunction
function! s:foldl(f, init, dict) abort
return s:_foldl(a:f, a:init, items(a:dict))
endfunction
function! s:foldr(f, init, dict) abort
return s:_foldl(a:f, a:init, reverse(items(a:dict)))
endfunction
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,464 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_plantuml_previewer#Data#List#import() abort', printf("return map({'combinations': '', 'and': '', 'sort_by': '', 'foldr1': '', 'sort': '', 'flatten': '', 'has_index': '', 'find_indices': '', 'any': '', 'unshift': '', 'span': '', 'pop': '', 'binary_search': '', 'uniq_by': '', 'or': '', 'all': '', 'zip': '', 'find_last_index': '', 'find': '', 'partition': '', 'shift': '', 'permutations': '', 'break': '', 'max_by': '', 'foldl': '', 'foldr': '', 'find_index': '', 'drop_while': '', 'group_by': '', 'take_while': '', 'conj': '', 'push': '', 'char_range': '', 'cons': '', 'foldl1': '', 'intersect': '', 'concat': '', 'map_accum': '', 'clear': '', 'has_common_items': '', 'product': '', 'zip_fill': '', 'uniq': '', 'has': '', 'min_by': '', 'with_index': ''}, \"vital#_plantuml_previewer#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
" Utilities for list.
let s:save_cpo = &cpo
set cpo&vim
function! s:pop(list) abort
return remove(a:list, -1)
endfunction
function! s:push(list, val) abort
call add(a:list, a:val)
return a:list
endfunction
function! s:shift(list) abort
return remove(a:list, 0)
endfunction
function! s:unshift(list, val) abort
return insert(a:list, a:val)
endfunction
function! s:cons(x, xs) abort
return [a:x] + a:xs
endfunction
function! s:conj(xs, x) abort
return a:xs + [a:x]
endfunction
" Removes duplicates from a list.
function! s:uniq(list) abort
return s:uniq_by(a:list, 'v:val')
endfunction
" Removes duplicates from a list.
function! s:uniq_by(list, f) abort
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) abort
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) abort
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, ...) abort
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) abort
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) abort
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) abort
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) abort
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) abort
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) abort
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) abort
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) abort
" 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) abort
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) abort
return s:span(printf('!(%s)', a:f), a:xs)
endfunction
" similar to Haskell's Data.List.takeWhile
function! s:take_while(f, xs) abort
return s:span(a:f, a:xs)[0]
endfunction
" similar to Haskell's Data.List.dropWhile
function! s:drop_while(f, xs) abort
return s:span(a:f, a:xs)[1]
endfunction
" similar to Haskell's Data.List.partition
function! s:partition(f, xs) abort
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) abort
return !s:any(printf('!(%s)', a:f), a:xs)
endfunction
" similar to Haskell's Prelude.any
function! s:any(f, xs) abort
return !empty(filter(map(copy(a:xs), a:f), 'v:val'))
endfunction
" similar to Haskell's Prelude.and
function! s:and(xs) abort
return s:all('v:val', a:xs)
endfunction
" similar to Haskell's Prelude.or
function! s:or(xs) abort
return s:any('v:val', a:xs)
endfunction
function! s:map_accum(expr, xs, init) abort
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) abort
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) abort
if len(a:xs) == 0
throw 'vital: Data.List: 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) abort
return s:foldl(a:f, a:init, reverse(copy(a:xs)))
endfunction
" similar to Haskell's Prelude.fold11
function! s:foldr1(f, xs) abort
if len(a:xs) == 0
throw 'vital: Data.List: foldr1'
endif
return s:foldr(a:f, a:xs[-1], a:xs[0:-2])
endfunction
" similar to python's zip()
function! s:zip(...) abort
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) abort
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, ...) abort
let base = a:0 > 0 ? a:1 : 0
return map(copy(a:list), '[v:val, v:key + base]')
endfunction
" similar to Ruby's detect or Haskell's find.
function! s:find(list, default, f) abort
let l:Call = type(a:f) is type(function('function'))
\ ? function('call')
\ : function('s:_call_string_expr')
for x in a:list
if l:Call(a:f, [x])
return x
endif
endfor
return a:default
endfunction
function! s:_call_string_expr(expr, args) abort
return map([a:args[0]], a:expr)[0]
endfunction
" Returns the index of the first element which satisfies the given expr.
function! s:find_index(xs, f, ...) abort
let len = len(a:xs)
let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : 0
let default = a:0 > 1 ? a:2 : -1
if start >=# len || start < 0
return default
endif
for i in range(start, len - 1)
if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g'))
return i
endif
endfor
return default
endfunction
" Returns the index of the last element which satisfies the given expr.
function! s:find_last_index(xs, f, ...) abort
let len = len(a:xs)
let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : len - 1
let default = a:0 > 1 ? a:2 : -1
if start >=# len || start < 0
return default
endif
for i in range(start, 0, -1)
if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g'))
return i
endif
endfor
return default
endfunction
" Similar to find_index but returns the list of indices satisfying the given expr.
function! s:find_indices(xs, f, ...) abort
let len = len(a:xs)
let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : 0
let result = []
if start >=# len || start < 0
return result
endif
for i in range(start, len - 1)
if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g'))
call add(result, i)
endif
endfor
return result
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) abort
return !empty(filter(copy(a:list1), 'index(a:list2, v:val) isnot -1'))
endfunction
function! s:intersect(list1, list2) abort
let items = []
" for funcref
for X in a:list1
if index(a:list2, X) != -1 && index(items, X) == -1
let items += [X]
endif
endfor
return items
endfunction
" similar to Ruby's group_by.
function! s:group_by(xs, f) abort
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
function! s:_default_compare(a, b) abort
return a:a <# a:b ? -1 : a:a ># a:b ? 1 : 0
endfunction
function! s:binary_search(list, value, ...) abort
let Predicate = a:0 >= 1 ? a:1 : 's:_default_compare'
let dic = a:0 >= 2 ? a:2 : {}
let start = 0
let end = len(a:list) - 1
while 1
if start > end
return -1
endif
let middle = (start + end) / 2
let compared = call(Predicate, [a:value, a:list[middle]], dic)
if compared < 0
let end = middle - 1
elseif compared > 0
let start = middle + 1
else
return middle
endif
endwhile
endfunction
function! s:product(lists) abort
let result = [[]]
for pool in a:lists
let tmp = []
for x in result
let tmp += map(copy(pool), 'x + [v:val]')
endfor
let result = tmp
endfor
return result
endfunction
function! s:permutations(list, ...) abort
if a:0 > 1
throw 'vital: Data.List: too many arguments'
endif
let r = a:0 == 1 ? a:1 : len(a:list)
if r > len(a:list)
return []
elseif r < 0
throw 'vital: Data.List: {r} must be non-negative integer'
endif
let n = len(a:list)
let result = []
for indices in s:product(map(range(r), 'range(n)'))
if len(s:uniq(indices)) == r
call add(result, map(indices, 'a:list[v:val]'))
endif
endfor
return result
endfunction
function! s:combinations(list, r) abort
if a:r > len(a:list)
return []
elseif a:r < 0
throw 'vital: Data.List: {r} must be non-negative integer'
endif
let n = len(a:list)
let result = []
for indices in s:permutations(range(n), a:r)
if s:sort(copy(indices), 'a:a - a:b') == indices
call add(result, map(indices, 'a:list[v:val]'))
endif
endfor
return result
endfunction
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,628 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_plantuml_previewer#Data#String#import() abort', printf("return map({'starts_with': '', 'split3': '', 'replace_first': '', 'chop': '', 'unescape': '', 'split_posix_text': '', 'replace': '', 'scan': '', 'strwidthpart': '', 'common_head': '', 'reverse': '', 'escape_pattern': '', 'trim_end': '', '_vital_depends': '', 'wrap': '', 'join_posix_lines': '', 'contains_multibyte': '', 'truncate_skipping': '', 'split_leftright': '', 'ends_with': '', 'nsplit': '', 'strwidthpart_reverse': '', 'unescape_pattern': '', 'levenshtein_distance': '', 'trim_start': '', 'justify_equal_spacing': '', 'nr2hex': '', 'iconv': '', 'pad_left': '', 'nr2enc_char': '', 'lines': '', 'repair_posix_text': '', 'nr2byte': '', 'trim': '', 'diffidx': '', 'truncate': '', 'split_by_displaywidth': '', '_vital_created': '', 'padding_by_displaywidth': '', 'hash': '', 'chomp': '', 'pad_between_letters': '', 'dstring': '', 'pad_both_sides': '', 'substitute_last': '', 'pad_right': '', 'remove_ansi_sequences': '', '_vital_loaded': ''}, \"vital#_plantuml_previewer#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
" Utilities for string.
let s:save_cpo = &cpo
set cpo&vim
function! s:_vital_loaded(V) abort
let s:V = a:V
let s:L = s:V.import('Data.List')
endfunction
function! s:_vital_depends() abort
return ['Data.List']
endfunction
function! s:_vital_created(module) abort
" Expose script-local funcref
if exists('s:strchars')
let a:module.strchars = s:strchars
endif
if exists('s:wcswidth')
let a:module.wcswidth = s:wcswidth
endif
endfunction
" Substitute a:from => a:to by string.
" To substitute by pattern, use substitute() instead.
function! s:replace(str, from, to) abort
return s:_replace(a:str, a:from, a:to, 'g')
endfunction
" Substitute a:from => a:to only once.
" cf. s:replace()
function! s:replace_first(str, from, to) abort
return s:_replace(a:str, a:from, a:to, '')
endfunction
" implement of replace() and replace_first()
function! s:_replace(str, from, to, flags) abort
return substitute(a:str, '\V'.escape(a:from, '\'), escape(a:to, '\'), a:flags)
endfunction
function! s:scan(str, pattern) abort
let list = []
call substitute(a:str, a:pattern, '\=add(list, submatch(0)) == [] ? "" : ""', 'g')
return list
endfunction
function! s:reverse(str) abort
return join(reverse(split(a:str, '.\zs')), '')
endfunction
function! s:starts_with(str, prefix) abort
return stridx(a:str, a:prefix) == 0
endfunction
function! s:ends_with(str, suffix) abort
let idx = strridx(a:str, a:suffix)
return 0 <= idx && idx + len(a:suffix) == len(a:str)
endfunction
function! s:common_head(strs) abort
if empty(a:strs)
return ''
endif
let len = len(a:strs)
if len == 1
return a:strs[0]
endif
let strs = len == 2 ? a:strs : sort(copy(a:strs))
let pat = substitute(strs[0], '.', '\="[" . escape(submatch(0), "^\\") . "]"', 'g')
return pat ==# '' ? '' : matchstr(strs[-1], '\C^\%[' . pat . ']')
endfunction
" Split to two elements of List. ([left, right])
" e.g.: s:split3('neocomplcache', 'compl') returns ['neo', 'compl', 'cache']
function! s:split_leftright(expr, pattern) abort
let [left, _, right] = s:split3(a:expr, a:pattern)
return [left, right]
endfunction
function! s:split3(expr, pattern) abort
let ERROR = ['', '', '']
if a:expr ==# '' || a:pattern ==# ''
return ERROR
endif
let begin = match(a:expr, a:pattern)
if begin is -1
return ERROR
endif
let end = matchend(a:expr, a:pattern)
let left = begin <=# 0 ? '' : a:expr[: begin - 1]
let right = a:expr[end :]
return [left, a:expr[begin : end-1], right]
endfunction
" Slices into strings determines the number of substrings.
" e.g.: s:nsplit("neo compl cache", 2, '\s') returns ['neo', 'compl cache']
function! s:nsplit(expr, n, ...) abort
let pattern = get(a:000, 0, '\s')
let keepempty = get(a:000, 1, 1)
let ret = []
let expr = a:expr
if a:n <= 1
return [expr]
endif
while 1
let pos = match(expr, pattern)
if pos == -1
if expr !~ pattern || keepempty
call add(ret, expr)
endif
break
elseif pos >= 0
let left = pos > 0 ? expr[:pos-1] : ''
if pos > 0 || keepempty
call add(ret, left)
endif
let ml = len(matchstr(expr, pattern))
if pos == 0 && ml == 0
let pos = 1
endif
let expr = expr[pos+ml :]
endif
if len(expr) == 0
break
endif
if len(ret) == a:n - 1
call add(ret, expr)
break
endif
endwhile
return ret
endfunction
" Returns the number of character in a:str.
" NOTE: This returns proper value
" even if a:str contains multibyte character(s).
" s:strchars(str) {{{
if exists('*strchars')
let s:strchars = function('strchars')
else
function! s:strchars(str) abort
return strlen(substitute(copy(a:str), '.', 'x', 'g'))
endfunction
endif "}}}
" Returns the bool of contains any multibyte character in s:str
function! s:contains_multibyte(str) abort "{{{
return strlen(a:str) != s:strchars(a:str)
endfunction "}}}
" Remove last character from a:str.
" NOTE: This returns proper value
" even if a:str contains multibyte character(s).
function! s:chop(str) abort "{{{
return substitute(a:str, '.$', '', '')
endfunction "}}}
" Remove last \r,\n,\r\n from a:str.
function! s:chomp(str) abort "{{{
return substitute(a:str, '\%(\r\n\|[\r\n]\)$', '', '')
endfunction "}}}
" wrap() and its internal functions
" * _split_by_wcswidth_once()
" * _split_by_wcswidth()
" * _concat()
" * wrap()
"
" NOTE _concat() is just a copy of Data.List.concat().
" FIXME don't repeat yourself
function! s:_split_by_wcswidth_once(body, x) abort
let fst = s:strwidthpart(a:body, a:x)
let snd = s:strwidthpart_reverse(a:body, s:wcswidth(a:body) - s:wcswidth(fst))
return [fst, snd]
endfunction
function! s:_split_by_wcswidth(body, x) abort
let memo = []
let body = a:body
while s:wcswidth(body) > a:x
let [tmp, body] = s:_split_by_wcswidth_once(body, a:x)
call add(memo, tmp)
endwhile
call add(memo, body)
return memo
endfunction
function! s:trim(str) abort
return substitute(a:str,'\%#=1^[[:space:]]\+\|[[:space:]]\+$', '', 'g')
endfunction
function! s:trim_start(str) abort
return substitute(a:str,'\%#=1^[[:space:]]\+', '', '')
endfunction
function! s:trim_end(str) abort
let i = match(a:str, '\%#=2[[:space:]]*$')
return i is# 0 ? '' : a:str[: i-1]
endfunction
function! s:wrap(str,...) abort
let _columns = a:0 > 0 ? a:1 : &columns
return s:L.concat(
\ map(split(a:str, '\r\n\|[\r\n]'), 's:_split_by_wcswidth(v:val, _columns - 1)'))
endfunction
function! s:nr2byte(nr) abort
if a:nr < 0x80
return nr2char(a:nr)
elseif a:nr < 0x800
return nr2char(a:nr/64+192).nr2char(a:nr%64+128)
else
return nr2char(a:nr/4096%16+224).nr2char(a:nr/64%64+128).nr2char(a:nr%64+128)
endif
endfunction
function! s:nr2enc_char(charcode) abort
if &encoding ==# 'utf-8'
return nr2char(a:charcode)
endif
let char = s:nr2byte(a:charcode)
if strlen(char) > 1
let char = strtrans(iconv(char, 'utf-8', &encoding))
endif
return char
endfunction
function! s:nr2hex(nr) abort
let n = a:nr
let r = ''
while n
let r = '0123456789ABCDEF'[n % 16] . r
let n = n / 16
endwhile
return r
endfunction
" If a ==# b, returns -1.
" If a !=# b, returns first index of different character.
function! s:diffidx(a, b) abort
return a:a ==# a:b ? -1 : strlen(s:common_head([a:a, a:b]))
endfunction
function! s:substitute_last(expr, pat, sub) abort
return substitute(a:expr, printf('.*\zs%s', a:pat), a:sub, '')
endfunction
function! s:dstring(expr) abort
let x = substitute(string(a:expr), "^'\\|'$", '', 'g')
let x = substitute(x, "''", "'", 'g')
return printf('"%s"', escape(x, '"'))
endfunction
function! s:lines(str) abort
return split(a:str, '\r\?\n')
endfunction
function! s:_pad_with_char(str, left, right, char) abort
return repeat(a:char, a:left). a:str. repeat(a:char, a:right)
endfunction
function! s:pad_left(str, width, ...) abort
let char = get(a:, 1, ' ')
if strdisplaywidth(char) != 1
throw "vital: Data.String: Can't use non-half-width characters for padding."
endif
let left = max([0, a:width - strdisplaywidth(a:str)])
return s:_pad_with_char(a:str, left, 0, char)
endfunction
function! s:pad_right(str, width, ...) abort
let char = get(a:, 1, ' ')
if strdisplaywidth(char) != 1
throw "vital: Data.String: Can't use non-half-width characters for padding."
endif
let right = max([0, a:width - strdisplaywidth(a:str)])
return s:_pad_with_char(a:str, 0, right, char)
endfunction
function! s:pad_both_sides(str, width, ...) abort
let char = get(a:, 1, ' ')
if strdisplaywidth(char) != 1
throw "vital: Data.String: Can't use non-half-width characters for padding."
endif
let space = max([0, a:width - strdisplaywidth(a:str)])
let left = space / 2
let right = space - left
return s:_pad_with_char(a:str, left, right, char)
endfunction
function! s:pad_between_letters(str, width, ...) abort
let char = get(a:, 1, ' ')
if strdisplaywidth(char) != 1
throw "vital: Data.String: Can't use non-half-width characters for padding."
endif
let letters = split(a:str, '\zs')
let each_width = a:width / len(letters)
let str = join(map(letters, 's:pad_both_sides(v:val, each_width, char)'), '')
if a:width - strdisplaywidth(str) > 0
return char. s:pad_both_sides(str, a:width - 1, char)
endif
return str
endfunction
function! s:justify_equal_spacing(str, width, ...) abort
let char = get(a:, 1, ' ')
if strdisplaywidth(char) != 1
throw "vital: Data.String: Can't use non-half-width characters for padding."
endif
let letters = split(a:str, '\zs')
let first_letter = letters[0]
" {width w/o the first letter} / {length w/o the first letter}
let each_width = (a:width - strdisplaywidth(first_letter)) / (len(letters) - 1)
let remainder = (a:width - strdisplaywidth(first_letter)) % (len(letters) - 1)
return first_letter. join(s:L.concat([
\ map(letters[1:remainder], 's:pad_left(v:val, each_width + 1, char)'),
\ map(letters[remainder + 1:], 's:pad_left(v:val, each_width, char)')
\ ]), '')
endfunction
function! s:levenshtein_distance(str1, str2) abort
let letters1 = split(a:str1, '\zs')
let letters2 = split(a:str2, '\zs')
let length1 = len(letters1)
let length2 = len(letters2)
let distances = map(range(1, length1 + 1), 'map(range(1, length2 + 1), ''0'')')
for i1 in range(0, length1)
let distances[i1][0] = i1
endfor
for i2 in range(0, length2)
let distances[0][i2] = i2
endfor
for i1 in range(1, length1)
for i2 in range(1, length2)
let cost = (letters1[i1 - 1] ==# letters2[i2 - 1]) ? 0 : 1
let distances[i1][i2] = min([
\ distances[i1 - 1][i2 ] + 1,
\ distances[i1 ][i2 - 1] + 1,
\ distances[i1 - 1][i2 - 1] + cost,
\])
endfor
endfor
return distances[length1][length2]
endfunction
function! s:padding_by_displaywidth(expr, width, float) abort
let padding_char = ' '
let n = a:width - strdisplaywidth(a:expr)
if n <= 0
let n = 0
endif
if a:float < 0
return a:expr . repeat(padding_char, n)
elseif 0 < a:float
return repeat(padding_char, n) . a:expr
else
if n % 2 is 0
return repeat(padding_char, n / 2) . a:expr . repeat(padding_char, n / 2)
else
return repeat(padding_char, (n - 1) / 2) . a:expr . repeat(padding_char, (n - 1) / 2) . padding_char
endif
endif
endfunction
function! s:split_by_displaywidth(expr, width, float, is_wrap) abort
if a:width is 0
return ['']
endif
let lines = []
let cs = split(a:expr, '\zs')
let cs_index = 0
let text = ''
while cs_index < len(cs)
if cs[cs_index] is# "\n"
let text = s:padding_by_displaywidth(text, a:width, a:float)
let lines += [text]
let text = ''
else
let w = strdisplaywidth(text . cs[cs_index])
if w < a:width
let text .= cs[cs_index]
elseif a:width < w
let text = s:padding_by_displaywidth(text, a:width, a:float)
else
let text .= cs[cs_index]
endif
if a:width <= w
let lines += [text]
let text = ''
if a:is_wrap
if a:width < w
if a:width < strdisplaywidth(cs[cs_index])
while get(cs, cs_index, "\n") isnot# "\n"
let cs_index += 1
endwhile
continue
else
let text = cs[cs_index]
endif
endif
else
while get(cs, cs_index, "\n") isnot# "\n"
let cs_index += 1
endwhile
continue
endif
endif
endif
let cs_index += 1
endwhile
if !empty(text)
let lines += [ s:padding_by_displaywidth(text, a:width, a:float) ]
endif
return lines
endfunction
function! s:hash(str) abort
if exists('*sha256')
return sha256(a:str)
else
" This gives up sha256ing but just adds up char with index.
let sum = 0
for i in range(len(a:str))
let sum += char2nr(a:str[i]) * (i + 1)
endfor
return printf('%x', sum)
endif
endfunction
function! s:truncate(str, width) abort
" Original function is from mattn.
" http://github.com/mattn/googlereader-vim/tree/master
if a:str =~# '^[\x00-\x7f]*$'
return len(a:str) < a:width
\ ? printf('%-' . a:width . 's', a:str)
\ : strpart(a:str, 0, a:width)
endif
let ret = a:str
let width = s:wcswidth(a:str)
if width > a:width
let ret = s:strwidthpart(ret, a:width)
let width = s:wcswidth(ret)
endif
if width < a:width
let ret .= repeat(' ', a:width - width)
endif
return ret
endfunction
function! s:truncate_skipping(str, max, footer_width, separator) abort
let width = s:wcswidth(a:str)
if width <= a:max
let ret = a:str
else
let header_width = a:max - s:wcswidth(a:separator) - a:footer_width
let ret = s:strwidthpart(a:str, header_width) . a:separator
\ . s:strwidthpart_reverse(a:str, a:footer_width)
endif
return s:truncate(ret, a:max)
endfunction
function! s:strwidthpart(str, width) abort
let str = tr(a:str, "\t", ' ')
let vcol = a:width + 2
return matchstr(str, '.*\%<' . (vcol < 0 ? 0 : vcol) . 'v')
endfunction
function! s:strwidthpart_reverse(str, width) abort
let str = tr(a:str, "\t", ' ')
let vcol = s:wcswidth(str) - a:width
return matchstr(str, '\%>' . (vcol < 0 ? 0 : vcol) . 'v.*')
endfunction
if v:version >= 703
" Use builtin function.
let s:wcswidth = function('strwidth')
else
function! s:wcswidth(str) abort
if a:str =~# '^[\x00-\x7f]*$'
return strlen(a:str)
endif
let mx_first = '^\(.\)'
let str = a:str
let width = 0
while 1
let ucs = char2nr(substitute(str, mx_first, '\1', ''))
if ucs == 0
break
endif
let width += s:_wcwidth(ucs)
let str = substitute(str, mx_first, '', '')
endwhile
return width
endfunction
" UTF-8 only.
function! s:_wcwidth(ucs) abort
let ucs = a:ucs
if (ucs >= 0x1100
\ && (ucs <= 0x115f
\ || ucs == 0x2329
\ || ucs == 0x232a
\ || (ucs >= 0x2e80 && ucs <= 0xa4cf
\ && ucs != 0x303f)
\ || (ucs >= 0xac00 && ucs <= 0xd7a3)
\ || (ucs >= 0xf900 && ucs <= 0xfaff)
\ || (ucs >= 0xfe30 && ucs <= 0xfe6f)
\ || (ucs >= 0xff00 && ucs <= 0xff60)
\ || (ucs >= 0xffe0 && ucs <= 0xffe6)
\ || (ucs >= 0x20000 && ucs <= 0x2fffd)
\ || (ucs >= 0x30000 && ucs <= 0x3fffd)
\ ))
return 2
endif
return 1
endfunction
endif
function! s:remove_ansi_sequences(text) abort
return substitute(a:text, '\e\[\%(\%(\d\+;\)*\d\+\)\?[mK]', '', 'g')
endfunction
function! s:escape_pattern(str) abort
" escape characters for no-magic
return escape(a:str, '^$~.*[]\')
endfunction
function! s:unescape_pattern(str) abort
" unescape characters for no-magic
return s:unescape(a:str, '^$~.*[]\')
endfunction
function! s:unescape(str, chars) abort
let chars = map(split(a:chars, '\zs'), 'escape(v:val, ''^$~.*[]\'')')
return substitute(a:str, '\\\(' . join(chars, '\|') . '\)', '\1', 'g')
endfunction
function! s:iconv(expr, from, to) abort
if a:from ==# '' || a:to ==# '' || a:from ==? a:to
return a:expr
endif
let result = iconv(a:expr, a:from, a:to)
return empty(result) ? a:expr : result
endfunction
" NOTE:
" A definition of a TEXT file is "A file that contains characters organized
" into one or more lines."
" A definition of a LINE is "A sequence of zero or more non- <newline>s
" plus a terminating <newline>"
" That's why {stdin} always ends with <newline> ideally. However, there are
" some programs which does not follow the POSIX rule and a Vim's way to join
" List into TEXT; join({text}, "\n"); does not add <newline> to the end of
" the last line.
" That's why add a trailing <newline> if it does not exist.
" REF:
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_392
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_205
" :help split()
" NOTE:
" it does nothing if the text is a correct POSIX text
function! s:repair_posix_text(text, ...) abort
let newline = get(a:000, 0, "\n")
return a:text =~# '\n$' ? a:text : a:text . newline
endfunction
" NOTE:
" A definition of a TEXT file is "A file that contains characters organized
" into one or more lines."
" A definition of a LINE is "A sequence of zero or more non- <newline>s
" plus a terminating <newline>"
" REF:
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_392
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_205
function! s:join_posix_lines(lines, ...) abort
let newline = get(a:000, 0, "\n")
return join(a:lines, newline) . newline
endfunction
" NOTE:
" A definition of a TEXT file is "A file that contains characters organized
" into one or more lines."
" A definition of a LINE is "A sequence of zero or more non- <newline>s
" plus a terminating <newline>"
" TEXT into List; split({text}, '\r\?\n', 1); add an extra empty line at the
" end of List because the end of TEXT ends with <newline> and keepempty=1 is
" specified. (btw. keepempty=0 cannot be used because it will remove
" emptylines in the head and the tail).
" That's why removing a trailing <newline> before proceeding to 'split' is required
" REF:
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_392
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_205
function! s:split_posix_text(text, ...) abort
let newline = get(a:000, 0, '\r\?\n')
let text = substitute(a:text, newline . '$', '', '')
return split(text, newline, 1)
endfunction
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,424 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_plantuml_previewer#Prelude#import() abort', printf("return map({'escape_pattern': '', 'is_funcref': '', 'path2directory': '', 'wcswidth': '', 'is_string': '', 'input_helper': '', 'is_number': '', 'is_cygwin': '', 'path2project_directory': '', 'strwidthpart_reverse': '', 'input_safe': '', 'is_list': '', 'truncate_skipping': '', 'glob': '', 'truncate': '', 'is_dict': '', 'set_default': '', 'is_numeric': '', 'getchar_safe': '', 'substitute_path_separator': '', 'is_mac': '', 'strwidthpart': '', 'getchar': '', 'is_unix': '', 'is_windows': '', 'globpath': '', 'escape_file_searching': '', 'is_float': '', 'smart_execute_command': ''}, \"vital#_plantuml_previewer#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
let s:save_cpo = &cpo
set cpo&vim
if v:version > 703 ||
\ (v:version == 703 && has('patch465'))
function! s:glob(expr) abort
return glob(a:expr, 1, 1)
endfunction
else
function! s:glob(expr) abort
return split(glob(a:expr, 1), '\n')
endfunction
endif
if v:version > 704 ||
\ (v:version == 704 && has('patch279'))
function! s:globpath(path, expr) abort
return globpath(a:path, a:expr, 1, 1)
endfunction
else
function! s:globpath(path, expr) abort
return split(globpath(a:path, a:expr, 1), '\n')
endfunction
endif
" Wrapper functions for type().
" NOTE: __TYPE_FLOAT = -1 when -float.
" this doesn't match to anything.
if has('patch-7.4.2071')
let [
\ s:__TYPE_NUMBER,
\ s:__TYPE_STRING,
\ s:__TYPE_FUNCREF,
\ s:__TYPE_LIST,
\ s:__TYPE_DICT,
\ s:__TYPE_FLOAT] = [
\ v:t_number,
\ v:t_string,
\ v:t_func,
\ v:t_list,
\ v:t_dict,
\ v:t_float]
else
let [
\ s:__TYPE_NUMBER,
\ s:__TYPE_STRING,
\ s:__TYPE_FUNCREF,
\ s:__TYPE_LIST,
\ s:__TYPE_DICT,
\ s:__TYPE_FLOAT] = [
\ type(3),
\ type(''),
\ type(function('tr')),
\ type([]),
\ type({}),
\ has('float') ? type(str2float('0')) : -1]
endif
" Number or Float
function! s:is_numeric(Value) abort
let _ = type(a:Value)
return _ ==# s:__TYPE_NUMBER
\ || _ ==# s:__TYPE_FLOAT
endfunction
" Number
function! s:is_number(Value) abort
return type(a:Value) ==# s:__TYPE_NUMBER
endfunction
" String
function! s:is_string(Value) abort
return type(a:Value) ==# s:__TYPE_STRING
endfunction
" Funcref
function! s:is_funcref(Value) abort
return type(a:Value) ==# s:__TYPE_FUNCREF
endfunction
" List
function! s:is_list(Value) abort
return type(a:Value) ==# s:__TYPE_LIST
endfunction
" Dictionary
function! s:is_dict(Value) abort
return type(a:Value) ==# s:__TYPE_DICT
endfunction
" Float
function! s:is_float(Value) abort
return type(a:Value) ==# s:__TYPE_FLOAT
endfunction
function! s:truncate_skipping(str, max, footer_width, separator) abort
call s:_warn_deprecated('truncate_skipping', 'Data.String.truncate_skipping')
let width = s:wcswidth(a:str)
if width <= a:max
let ret = a:str
else
let header_width = a:max - s:wcswidth(a:separator) - a:footer_width
let ret = s:strwidthpart(a:str, header_width) . a:separator
\ . s:strwidthpart_reverse(a:str, a:footer_width)
endif
return s:truncate(ret, a:max)
endfunction
function! s:truncate(str, width) abort
" Original function is from mattn.
" http://github.com/mattn/googlereader-vim/tree/master
call s:_warn_deprecated('truncate', 'Data.String.truncate')
if a:str =~# '^[\x00-\x7f]*$'
return len(a:str) < a:width ?
\ printf('%-'.a:width.'s', a:str) : strpart(a:str, 0, a:width)
endif
let ret = a:str
let width = s:wcswidth(a:str)
if width > a:width
let ret = s:strwidthpart(ret, a:width)
let width = s:wcswidth(ret)
endif
if width < a:width
let ret .= repeat(' ', a:width - width)
endif
return ret
endfunction
function! s:strwidthpart(str, width) abort
call s:_warn_deprecated('strwidthpart', 'Data.String.strwidthpart')
if a:width <= 0
return ''
endif
let ret = a:str
let width = s:wcswidth(a:str)
while width > a:width
let char = matchstr(ret, '.$')
let ret = ret[: -1 - len(char)]
let width -= s:wcswidth(char)
endwhile
return ret
endfunction
function! s:strwidthpart_reverse(str, width) abort
call s:_warn_deprecated('strwidthpart_reverse', 'Data.String.strwidthpart_reverse')
if a:width <= 0
return ''
endif
let ret = a:str
let width = s:wcswidth(a:str)
while width > a:width
let char = matchstr(ret, '^.')
let ret = ret[len(char) :]
let width -= s:wcswidth(char)
endwhile
return ret
endfunction
if v:version >= 703
" Use builtin function.
function! s:wcswidth(str) abort
call s:_warn_deprecated('wcswidth', 'Data.String.wcswidth')
return strwidth(a:str)
endfunction
else
function! s:wcswidth(str) abort
call s:_warn_deprecated('wcswidth', 'Data.String.wcswidth')
if a:str =~# '^[\x00-\x7f]*$'
return strlen(a:str)
end
let mx_first = '^\(.\)'
let str = a:str
let width = 0
while 1
let ucs = char2nr(substitute(str, mx_first, '\1', ''))
if ucs == 0
break
endif
let width += s:_wcwidth(ucs)
let str = substitute(str, mx_first, '', '')
endwhile
return width
endfunction
" UTF-8 only.
function! s:_wcwidth(ucs) abort
let ucs = a:ucs
if (ucs >= 0x1100
\ && (ucs <= 0x115f
\ || ucs == 0x2329
\ || ucs == 0x232a
\ || (ucs >= 0x2e80 && ucs <= 0xa4cf
\ && ucs != 0x303f)
\ || (ucs >= 0xac00 && ucs <= 0xd7a3)
\ || (ucs >= 0xf900 && ucs <= 0xfaff)
\ || (ucs >= 0xfe30 && ucs <= 0xfe6f)
\ || (ucs >= 0xff00 && ucs <= 0xff60)
\ || (ucs >= 0xffe0 && ucs <= 0xffe6)
\ || (ucs >= 0x20000 && ucs <= 0x2fffd)
\ || (ucs >= 0x30000 && ucs <= 0x3fffd)
\ ))
return 2
endif
return 1
endfunction
endif
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')))
let s:is_unix = has('unix')
function! s:is_windows() abort
return s:is_windows
endfunction
function! s:is_cygwin() abort
return s:is_cygwin
endfunction
function! s:is_mac() abort
return s:is_mac
endfunction
function! s:is_unix() abort
return s:is_unix
endfunction
function! s:_warn_deprecated(name, alternative) abort
try
echohl Error
echomsg 'Prelude.' . a:name . ' is deprecated! Please use ' . a:alternative . ' instead.'
finally
echohl None
endtry
endfunction
function! s:smart_execute_command(action, word) abort
execute a:action . ' ' . (a:word ==# '' ? '' : '`=a:word`')
endfunction
function! s:escape_file_searching(buffer_name) abort
return escape(a:buffer_name, '*[]?{}, ')
endfunction
function! s:escape_pattern(str) abort
call s:_warn_deprecated(
\ 'escape_pattern',
\ 'Data.String.escape_pattern',
\)
return escape(a:str, '~"\.^$[]*')
endfunction
function! s:getchar(...) abort
let c = call('getchar', a:000)
return type(c) == type(0) ? nr2char(c) : c
endfunction
function! s:getchar_safe(...) abort
let c = s:input_helper('getchar', a:000)
return type(c) == type('') ? c : nr2char(c)
endfunction
function! s:input_safe(...) abort
return s:input_helper('input', a:000)
endfunction
function! s:input_helper(funcname, args) abort
let success = 0
if inputsave() !=# success
throw 'vital: Prelude: inputsave() failed'
endif
try
return call(a:funcname, a:args)
finally
if inputrestore() !=# success
throw 'vital: Prelude: inputrestore() failed'
endif
endtry
endfunction
function! s:set_default(var, val) abort
if !exists(a:var) || type({a:var}) != type(a:val)
let {a:var} = a:val
endif
endfunction
function! s:substitute_path_separator(path) abort
return s:is_windows ? substitute(a:path, '\\', '/', 'g') : a:path
endfunction
function! s:path2directory(path) abort
return s:substitute_path_separator(isdirectory(a:path) ? a:path : fnamemodify(a:path, ':p:h'))
endfunction
function! s:_path2project_directory_git(path) abort
let parent = a:path
while 1
let path = parent . '/.git'
if isdirectory(path) || filereadable(path)
return parent
endif
let next = fnamemodify(parent, ':h')
if next == parent
return ''
endif
let parent = next
endwhile
endfunction
function! s:_path2project_directory_svn(path) abort
let search_directory = a:path
let directory = ''
let find_directory = s:escape_file_searching(search_directory)
let d = finddir('.svn', find_directory . ';')
if d ==# ''
return ''
endif
let directory = fnamemodify(d, ':p:h:h')
" Search parent directories.
let parent_directory = s:path2directory(
\ fnamemodify(directory, ':h'))
if parent_directory !=# ''
let d = finddir('.svn', parent_directory . ';')
if d !=# ''
let directory = s:_path2project_directory_svn(parent_directory)
endif
endif
return directory
endfunction
function! s:_path2project_directory_others(vcs, path) abort
let vcs = a:vcs
let search_directory = a:path
let find_directory = s:escape_file_searching(search_directory)
let d = finddir(vcs, find_directory . ';')
if d ==# ''
return ''
endif
return fnamemodify(d, ':p:h:h')
endfunction
function! s:path2project_directory(path, ...) abort
let is_allow_empty = get(a:000, 0, 0)
let search_directory = s:path2directory(a:path)
let directory = ''
" Search VCS directory.
for vcs in ['.git', '.bzr', '.hg', '.svn']
if vcs ==# '.git'
let directory = s:_path2project_directory_git(search_directory)
elseif vcs ==# '.svn'
let directory = s:_path2project_directory_svn(search_directory)
else
let directory = s:_path2project_directory_others(vcs, search_directory)
endif
if directory !=# ''
break
endif
endfor
" Search project file.
if directory ==# ''
for d in ['build.xml', 'prj.el', '.project', 'pom.xml', 'package.json',
\ 'Makefile', 'configure', 'Rakefile', 'NAnt.build',
\ 'P4CONFIG', 'tags', 'gtags']
let d = findfile(d, s:escape_file_searching(search_directory) . ';')
if d !=# ''
let directory = fnamemodify(d, ':p:h')
break
endif
endfor
endif
if directory ==# ''
" Search /src/ directory.
let base = s:substitute_path_separator(search_directory)
if base =~# '/src/'
let directory = base[: strridx(base, '/src/') + 3]
endif
endif
if directory ==# '' && !is_allow_empty
" Use original path.
let directory = search_directory
endif
return s:substitute_path_separator(directory)
endfunction
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,61 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_plantuml_previewer#System#Job#import() abort', printf("return map({'_vital_depends': '', '_vital_healthcheck': '', 'is_available': '', 'start': '', '_vital_loaded': ''}, \"vital#_plantuml_previewer#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
let s:t_string = type('')
let s:t_list = type([])
function! s:_vital_loaded(V) abort
if has('nvim')
let s:Job = a:V.import('System.Job.Neovim')
else
let s:Job = a:V.import('System.Job.Vim')
endif
endfunction
function! s:_vital_depends() abort
return [
\ 'System.Job.Vim',
\ 'System.Job.Neovim',
\]
endfunction
function! s:_vital_healthcheck() abort
if has('patch-8.0.0027') || has('nvim-0.2.0')
return
endif
return 'This module requires Vim 8.0.0027 or Neovim 0.2.0'
endfunction
" Note:
" Vim does not raise E902 on Unix system even the prog is not found so use a
" custom exception instead to make the method compatible.
" Note:
" Vim/Neovim treat String a bit differently so prohibit String as well
function! s:_validate_args(args) abort
if type(a:args) != s:t_list
throw 'vital: System.Job: Argument requires to be a List instance.'
endif
if len(a:args) == 0
throw 'vital: System.Job: Argument vector must have at least one item.'
endif
let prog = a:args[0]
if !executable(prog)
throw printf('vital: System.Job: "%s" is not an executable', prog)
endif
endfunction
function! s:is_available() abort
return s:Job.is_available()
endfunction
function! s:start(args, ...) abort
call s:_validate_args(a:args)
return s:Job.start(a:args, a:0 ? a:1 : {})
endfunction

View File

@ -0,0 +1,119 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_plantuml_previewer#System#Job#Neovim#import() abort', printf("return map({'is_available': '', 'start': ''}, \"vital#_plantuml_previewer#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
" http://vim-jp.org/blog/2016/03/23/take-care-of-patch-1577.html
function! s:is_available() abort
return has('nvim') && has('nvim-0.2.0')
endfunction
function! s:start(args, options) abort
let job = extend(copy(s:job), a:options)
let job_options = {}
if has_key(job, 'on_stdout')
let job_options.on_stdout = function('s:_on_stdout', [job])
endif
if has_key(job, 'on_stderr')
let job_options.on_stderr = function('s:_on_stderr', [job])
endif
if has_key(job, 'on_exit')
let job_options.on_exit = function('s:_on_exit', [job])
else
let job_options.on_exit = function('s:_on_exit_raw', [job])
endif
let job.__job = jobstart(a:args, job_options)
let job.__exitval = v:null
let job.args = a:args
return job
endfunction
function! s:_on_stdout(job, job_id, data, event) abort
call a:job.on_stdout(a:data)
endfunction
function! s:_on_stderr(job, job_id, data, event) abort
call a:job.on_stderr(a:data)
endfunction
function! s:_on_exit(job, job_id, exitval, event) abort
let a:job.__exitval = a:exitval
call a:job.on_exit(a:exitval)
endfunction
function! s:_on_exit_raw(job, job_id, exitval, event) abort
let a:job.__exitval = a:exitval
endfunction
" Instance -------------------------------------------------------------------
function! s:_job_id() abort dict
return self.__job
endfunction
function! s:_job_status() abort dict
try
call jobpid(self.__job)
return 'run'
catch /^Vim\%((\a\+)\)\=:E900/
return 'dead'
endtry
endfunction
if exists('*chansend') " Neovim 0.2.3
function! s:_job_send(data) abort dict
return chansend(self.__job, a:data)
endfunction
else
function! s:_job_send(data) abort dict
return jobsend(self.__job, a:data)
endfunction
endif
if exists('*chanclose') " Neovim 0.2.3
function! s:_job_close() abort dict
call chanclose(self.__job, 'stdin')
endfunction
else
function! s:_job_close() abort dict
call jobclose(self.__job, 'stdin')
endfunction
endif
function! s:_job_stop() abort dict
try
call jobstop(self.__job)
catch /^Vim\%((\a\+)\)\=:E900/
" NOTE:
" Vim does not raise exception even the job has already closed so fail
" silently for 'E900: Invalid job id' exception
endtry
endfunction
function! s:_job_wait(...) abort dict
let timeout = a:0 ? a:1 : v:null
let exitval = timeout is# v:null
\ ? jobwait([self.__job])[0]
\ : jobwait([self.__job], timeout)[0]
if exitval != -3
return exitval
endif
" Wait until 'on_exit' callback is called
while self.__exitval is# v:null
sleep 1m
endwhile
return self.__exitval
endfunction
" To make debug easier, use funcref instead.
let s:job = {
\ 'id': function('s:_job_id'),
\ 'status': function('s:_job_status'),
\ 'send': function('s:_job_send'),
\ 'close': function('s:_job_close'),
\ 'stop': function('s:_job_stop'),
\ 'wait': function('s:_job_wait'),
\}

View File

@ -0,0 +1,143 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_plantuml_previewer#System#Job#Vim#import() abort', printf("return map({'is_available': '', 'start': ''}, \"vital#_plantuml_previewer#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
" https://github.com/neovim/neovim/blob/f629f83/src/nvim/event/process.c#L24-L26
let s:KILL_TIMEOUT_MS = 2000
function! s:is_available() abort
return !has('nvim') && has('patch-8.0.0027')
endfunction
function! s:start(args, options) abort
let job = extend(copy(s:job), a:options)
let job_options = {
\ 'mode': 'raw',
\ 'timeout': 0,
\}
if has_key(job, 'on_stdout')
let job_options.out_cb = function('s:_out_cb', [job])
else
let job_options.out_io = 'null'
endif
if has_key(job, 'on_stderr')
let job_options.err_cb = function('s:_err_cb', [job])
else
let job_options.err_io = 'null'
endif
if has_key(job, 'on_stdout') || has_key(job, 'on_stderr')
let job_options.close_cb = function('s:_close_cb', [job])
endif
if has_key(job, 'on_exit')
let job_options.exit_cb = function('s:_exit_cb', [job])
endif
let job.__job = job_start(a:args, job_options)
let job.__closed = v:false
let job.args = a:args
return job
endfunction
function! s:_out_cb(job, channel, msg) abort
call a:job.on_stdout(split(a:msg, "\n", 1))
endfunction
function! s:_err_cb(job, channel, msg) abort
call a:job.on_stderr(split(a:msg, "\n", 1))
endfunction
function! s:_close_cb(job, channel) abort
let a:job.__closed = v:true
if has_key(a:job, 'on_stdout')
let options = {'part': 'out'}
while ch_status(a:channel, options) ==# 'buffered'
call s:_out_cb(a:job, a:channel, ch_readraw(a:channel, options))
endwhile
endif
if has_key(a:job, 'on_stderr')
let options = {'part': 'err'}
while ch_status(a:channel, options) ==# 'buffered'
call s:_err_cb(a:job, a:channel, ch_readraw(a:channel, options))
endwhile
endif
endfunction
function! s:_exit_cb(job, channel, exitval) abort
" Make sure on_stdout/on_stderr are called prior to on_exit.
" This check requires 'close_cb' so perform only when on_stdout/on_stderr
" is defined in a job instance.
if has_key(a:job, 'on_stdout') || has_key(a:job, 'on_stderr')
while !a:job.__closed
sleep 1m
endwhile
endif
call a:job.on_exit(a:exitval)
endfunction
" Instance -------------------------------------------------------------------
function! s:_job_id() abort dict
return job_info(self.__job).process
endfunction
" NOTE:
" On Unix a non-existing command results in "dead" instead
" So returns "dead" instead of "fail" even in non Unix.
function! s:_job_status() abort dict
let status = job_status(self.__job)
return status ==# 'fail' ? 'dead' : status
endfunction
" NOTE:
" A Null character (\0) is used as a terminator of a string in Vim.
" Neovim can send \0 by using \n splitted list but in Vim.
" So replace all \n in \n splitted list to ''
function! s:_job_send(data) abort dict
let data = type(a:data) == v:t_list
\ ? join(map(a:data, 'substitute(v:val, "\n", '''', ''g'')'), "\n")
\ : a:data
return ch_sendraw(self.__job, data)
endfunction
function! s:_job_close() abort dict
call ch_close_in(self.__job)
endfunction
function! s:_job_stop() abort dict
call job_stop(self.__job)
call timer_start(s:KILL_TIMEOUT_MS, { -> job_stop(self.__job, 'kill') })
endfunction
function! s:_job_wait(...) abort dict
let timeout = a:0 ? a:1 : v:null
let timeout = timeout is# v:null ? v:null : timeout / 1000.0
let start_time = reltime()
let job = self.__job
try
while timeout is# v:null || timeout > reltimefloat(reltime(start_time))
let status = job_status(job)
if status !=# 'run'
return status ==# 'dead' ? job_info(job).exitval : -3
endif
sleep 1m
endwhile
catch /^Vim:Interrupt$/
call self.stop()
return -2
endtry
return -1
endfunction
" To make debug easier, use funcref instead.
let s:job = {
\ 'id': function('s:_job_id'),
\ 'status': function('s:_job_status'),
\ 'send': function('s:_job_send'),
\ 'close': function('s:_job_close'),
\ 'stop': function('s:_job_stop'),
\ 'wait': function('s:_job_wait'),
\}

View File

@ -0,0 +1,96 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_plantuml_previewer#System#Process#import() abort', printf("return map({'_vital_depends': '', 'execute': '', 'register': '', '_vital_loaded': ''}, \"vital#_plantuml_previewer#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
let s:save_cpoptions = &cpoptions
set cpoptions&vim
let s:registry = {}
let s:priority = []
function! s:_vital_loaded(V) abort
let s:V = a:V
let s:Prelude = a:V.import('Prelude')
let s:String = a:V.import('Data.String')
call s:register('System.Process.Vimproc')
call s:register('System.Process.System')
endfunction
function! s:_vital_depends() abort
return [
\ 'Prelude',
\ 'Data.String',
\ 'System.Process.System',
\ 'System.Process.Vimproc',
\]
endfunction
function! s:_throw(msg) abort
throw printf('vital: System.Process: %s', a:msg)
endfunction
function! s:register(name) abort
let client = s:V.import(a:name)
if client.is_available()
let s:registry[a:name] = client
call add(s:priority, a:name)
endif
endfunction
function! s:_execute(args, options) abort
for name_or_client in a:options.clients
let client = s:Prelude.is_string(name_or_client)
\ ? s:registry[name_or_client]
\ : name_or_client
if !client.is_supported(a:options)
continue
endif
return client.execute(a:args, a:options)
endfor
call s:_throw(printf(
\ 'None of client support options : %s',
\ string(a:options),
\))
endfunction
" execute({args}[, {options}])
function! s:execute(args, ...) abort
let options = extend({
\ 'clients': s:priority,
\ 'input': 0,
\ 'timeout': 0,
\ 'background': 0,
\ 'encode_input': 1,
\ 'encode_output': 1,
\ 'split_output': 1,
\ 'debug': &verbose,
\}, get(a:000, 0, {}))
if s:Prelude.is_string(options.input) && !empty(options.encode_input)
let encoding = s:Prelude.is_string(options.encode_input)
\ ? options.encode_input
\ : &encoding
let options.input = s:String.iconv(options.input, encoding, 'char')
endif
let result = s:_execute(a:args, options)
if s:Prelude.is_string(result.output) && !empty(options.encode_output)
let encoding = s:Prelude.is_string(options.encode_output)
\ ? options.encode_output
\ : &encoding
let result.output = s:String.iconv(result.output, 'char', encoding)
endif
if options.split_output
let result.content = s:String.split_posix_text(result.output)
endif
let result.success = result.status == 0
let result.args = a:args
let result.options = options
return result
endfunction
let &cpoptions = s:save_cpoptions
unlet s:save_cpoptions

View File

@ -0,0 +1,128 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_plantuml_previewer#System#Process#System#import() abort', printf("return map({'_vital_depends': '', 'shellescape': '', 'execute': '', 'is_supported': '', 'is_available': '', '_vital_loaded': ''}, \"vital#_plantuml_previewer#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
let s:save_cpoptions = &cpoptions
set cpoptions&vim
function! s:_vital_loaded(V) abort
let s:Prelude = a:V.import('Prelude')
let s:String = a:V.import('Data.String')
let s:Guard = a:V.import('Vim.Guard')
endfunction
function! s:_vital_depends() abort
return [
\ 'Prelude',
\ 'Data.String',
\ 'Vim.Guard',
\]
endfunction
function! s:is_available() abort
return 1
endfunction
function! s:is_supported(options) abort
if get(a:options, 'timeout')
return 0
elseif get(a:options, 'background') && s:Prelude.is_windows()
return 0
endif
return 1
endfunction
function! s:shellescape(string) abort
if s:Prelude.is_windows()
" NOTE:
" In windows, a string which does not contain space SHOULD NOT be escaped
return a:string =~# '\s' ? shellescape(a:string) : a:string
else
return shellescape(a:string)
endif
endfunction
function! s:execute(args, options) abort
" NOTE:
" execute() is a command for executing program WITHOUT using shell.
" so mimic that behaviour with shell
let guard = s:Guard.store(filter([
\ '&shell',
\ '&shellcmdflag',
\ '&shellquote',
\ '&shellredir',
\ '&shelltemp',
\ (exists('+shelltype') ? '&shelltype' : ''),
\ (exists('+shellxescape') ? '&shellxescape' : ''),
\ (exists('+shellxquote') ? '&shellxquote' : ''),
\ (exists('+shellslash') ? '&shellslash' : ''),
\], '!empty(v:val)')
\)
try
" Reset shell related options
if s:Prelude.is_windows()
set shell&
if exists('+shellslash')
set shellslash&
endif
else
set shell=sh
endif
set shellcmdflag& shellquote& shellredir& shelltemp&
if exists('+shelltype')
set shelltype&
endif
if exists('+shellxescape')
set shellxescape&
endif
if exists('+shellxquote')
set shellxquote&
endif
let cmdline = join(map(
\ copy(a:args),
\ 's:shellescape(v:val)',
\))
if a:options.background && !s:Prelude.is_windows()
let cmdline = cmdline . ' &'
endif
if a:options.debug > 0
echomsg printf(
\ 'vital: System.Process.System: %s',
\ cmdline
\)
endif
if v:version < 704 || (v:version == 704 && !has('patch122'))
" {cmdline} of system() before Vim 7.4.122 is not converted so convert
" it manually from &encoding to 'char'
let cmdline = s:String.iconv(cmdline, &encoding, 'char')
endif
let args = [cmdline] + (s:Prelude.is_string(a:options.input) ? [a:options.input] : [])
let output = call('system', args)
if s:Prelude.is_windows()
" A builtin system() add a trailing space in Windows.
" It is probably an issue of pipe in Windows so remove it.
let output = substitute(output, '\s\n$', '\n', '')
endif
" NOTE:
" Vim 7.4 always return exit_status:0 for background process so mimic
let status = a:options.background ? 0 : v:shell_error
" NOTE:
" status, output are COMMON information
" cmdline is an EXTRA information
return {
\ 'status': status,
\ 'output': output,
\ 'cmdline': cmdline,
\}
finally
call guard.restore()
endtry
endfunction
let &cpoptions = s:save_cpoptions
unlet s:save_cpoptions

View File

@ -0,0 +1,82 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_plantuml_previewer#System#Process#Vimproc#import() abort', printf("return map({'_vital_depends': '', 'execute': '', 'is_supported': '', 'is_available': '', '_vital_loaded': ''}, \"vital#_plantuml_previewer#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
let s:save_cpoptions = &cpoptions
set cpoptions&vim
function! s:_vital_loaded(V) abort
let s:Prelude = a:V.import('Prelude')
endfunction
function! s:_vital_depends() abort
return [
\ 'Prelude',
\]
endfunction
function! s:is_available() abort
if exists('s:vimproc_available')
return s:vimproc_available
endif
try
call vimproc#version()
let s:vimproc_available = 1
catch
let s:vimproc_available = 0
endtry
return s:vimproc_available
endfunction
function! s:is_supported(options) abort
if get(a:options, 'background') && (
\ s:Prelude.is_string(get(a:options, 'input')) ||
\ get(a:options, 'timeout')
\)
return 0
endif
return 1
endfunction
function! s:execute(args, options) abort
let cmdline = join(map(
\ copy(a:args),
\ 'vimproc#shellescape(v:val)',
\))
if a:options.debug > 0
echomsg printf(
\ 'vital: System.Process.Vimproc: %s',
\ cmdline
\)
endif
if a:options.background
let output = vimproc#system_bg(cmdline)
" NOTE:
" background process via Builtin always return exit_code:0 so mimic
let status = 0
else
let output = vimproc#system(
\ cmdline,
\ s:Prelude.is_string(a:options.input) ? a:options.input : '',
\ a:options.timeout,
\)
let status = vimproc#get_last_status()
endif
" NOTE:
" status, output are COMMON information
" errormsg, cmdline are EXTRA information
return {
\ 'status': status,
\ 'output': output,
\ 'errormsg': vimproc#get_last_errmsg(),
\ 'cmdline': cmdline,
\}
endfunction
let &cpoptions = s:save_cpoptions
unlet s:save_cpoptions

View File

@ -0,0 +1,234 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_plantuml_previewer#Vim#Guard#import() abort', printf("return map({'_vital_depends': '', '_vital_created': '', 'store': '', '_vital_loaded': ''}, \"vital#_plantuml_previewer#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
let s:save_cpo = &cpo
set cpo&vim
" Use a Funcref as a special term _UNDEFINED
function! s:_undefined() abort
return 'undefined'
endfunction
let s:_UNDEFINED = function('s:_undefined')
function! s:_vital_loaded(V) abort
let s:V = a:V
let s:Prelude = s:V.import('Prelude')
let s:List = s:V.import('Data.List')
let s:Dict = s:V.import('Data.Dict')
endfunction
function! s:_vital_depends() abort
return ['Prelude', 'Data.List', 'Data.Dict']
endfunction
function! s:_vital_created(module) abort
" define constant variables
if !exists('s:const')
let s:const = {}
let s:const.is_local_variable_supported =
\ v:version > 703 || (v:version == 703 && has('patch560'))
" NOTE:
" The third argument is available from 7.4.242 but it had bug and that
" bug was fixed from 7.4.513
let s:const.is_third_argument_of_getreg_supported = has('patch-7.4.513')
lockvar s:const
endif
call extend(a:module, s:const)
endfunction
function! s:_throw(msg) abort
throw printf('vital: Vim.Guard: %s', a:msg)
endfunction
let s:option = {}
function! s:_new_option(name) abort
if a:name !~# '^&'
call s:_throw(printf(
\'An option name "%s" requires to be started from "&"', a:name
\))
elseif !exists(a:name)
call s:_throw(printf(
\'An option name "%s" does not exist', a:name
\))
endif
let option = copy(s:option)
let option.name = a:name
let option.value = eval(a:name)
return option
endfunction
function! s:option.restore() abort
execute printf('let %s = %s', self.name, string(self.value))
endfunction
let s:register = {}
function! s:_new_register(name) abort
if len(a:name) != 2
call s:_throw(printf(
\'A register name "%s" requires to be "@" + a single character', a:name
\))
elseif a:name !~# '^@'
call s:_throw(printf(
\'A register name "%s" requires to be started from "@"', a:name
\))
elseif a:name =~# '^@[:.%]$'
call s:_throw(printf(
\'A register name "%s" is read only', a:name
\))
elseif a:name !~# '^@[@0-9a-zA-Z#=*+~_/-]$'
call s:_throw(printf(
\'A register name "%s" does not exist. See ":help let-register"', a:name
\))
endif
let name = a:name ==# '@@' ? '' : a:name[1]
let register = copy(s:register)
let register.name = name
if s:const.is_third_argument_of_getreg_supported
let register.value = getreg(name, 1, 1)
else
let register.value = getreg(name, 1)
endif
let register.type = getregtype(name)
return register
endfunction
function! s:register.restore() abort
" https://github.com/vim/vim/commit/5a50c2255c447838d08d3b4895a3be3a41cd8eda
if has('patch-7.4.243') || self.name !=# '='
call setreg(self.name, self.value, self.type)
else
let @= = self.value
endif
endfunction
let s:environment = {}
function! s:_new_environment(name) abort
if a:name !~# '^\$'
call s:_throw(printf(
\'An environment variable name "%s" requires to be started from "$"', a:name
\))
elseif !exists(a:name)
call s:_throw(printf(
\'An environment variable name "%s" does not exist. While Vim cannot unlet environment variable, it requires to exist', a:name
\))
endif
let environment = copy(s:environment)
let environment.name = a:name
let environment.value = eval(a:name)
return environment
endfunction
function! s:environment.restore() abort
execute printf('let %s = %s', self.name, string(self.value))
endfunction
let s:variable = {}
function! s:_new_variable(name, ...) abort
if a:0 == 0
let m = matchlist(a:name, '^\([bwtg]:\)\(.*\)$')
if empty(m)
call s:_throw(printf(
\ join([
\ 'An variable name "%s" requires to start from b:, w:, t:, or g:',
\ 'while no {namespace} is specified',
\ ]),
\ a:name,
\))
endif
let [prefix, name] = m[1 : 2]
let namespace = eval(prefix)
else
let name = a:name
let namespace = a:1
endif
let variable = copy(s:variable)
let variable.name = name
let variable.value = get(namespace, name, s:_UNDEFINED)
let variable.value =
\ type(variable.value) == type({}) || type(variable.value) == type([])
\ ? deepcopy(variable.value)
\ : variable.value
let variable._namespace = namespace
return variable
endfunction
function! s:variable.restore() abort
" unlet the variable to prevent variable type mis-match in case
silent! unlet! self._namespace[self.name]
if type(self.value) == type(s:_UNDEFINED) && self.value == s:_UNDEFINED
" do nothing, leave the variable as undefined
else
let self._namespace[self.name] = self.value
endif
endfunction
let s:instance = {}
function! s:_new_instance(instance, ...) abort
let shallow = get(a:000, 0, 0)
if !s:Prelude.is_list(a:instance) && !s:Prelude.is_dict(a:instance)
call s:_throw(printf(
\'An instance "%s" requires to be List or Dictionary', string(a:instance)
\))
endif
let instance = copy(s:instance)
let instance.instance = a:instance
let instance.values = shallow ? copy(a:instance) : deepcopy(a:instance)
return instance
endfunction
function! s:instance.restore() abort
if s:Prelude.is_list(self.instance)
call s:List.clear(self.instance)
else
call s:Dict.clear(self.instance)
endif
call extend(self.instance, self.values)
endfunction
let s:guard = {}
function! s:store(targets) abort
let resources = []
for meta in a:targets
if s:Prelude.is_list(meta)
if len(meta) == 1
call add(resources, s:_new_instance(meta[0]))
elseif len(meta) == 2
if s:Prelude.is_string(meta[0])
call add(resources, call('s:_new_variable', meta))
else
call add(resources, call('s:_new_instance', meta))
endif
else
call s:_throw('List assignment requires one or two elements')
endif
elseif type(meta) == type('')
if meta =~# '^[bwtgls]:'
" Note:
" To improve an error message, handle l:XXX or s:XXX as well
call add(resources, s:_new_variable(meta))
elseif meta =~# '^&'
call add(resources, s:_new_option(meta))
elseif meta =~# '^@'
call add(resources, s:_new_register(meta))
elseif meta =~# '^\$'
call add(resources, s:_new_environment(meta))
else
call s:_throw(printf(
\ 'Unknown value "%s" was specified',
\ meta
\))
endif
endif
unlet meta
endfor
let guard = copy(s:guard)
let guard._resources = resources
return guard
endfunction
function! s:guard.restore() abort
for resource in self._resources
call resource.restore()
endfor
endfunction
let &cpo = s:save_cpo
unlet! s:save_cpo
" vim:set et ts=2 sts=2 sw=2 tw=0 fdm=marker:

View File

@ -0,0 +1,92 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_plantuml_previewer#Vim#Type#import() abort', printf("return map({'is_comparable': '', '_vital_created': '', 'is_predicate': '', 'is_numeric': '', 'is_special': ''}, \"vital#_plantuml_previewer#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
let s:types = {
\ 'number': 0,
\ 'string': 1,
\ 'func': 2,
\ 'list': 3,
\ 'dict': 4,
\ 'float': 5,
\ 'bool': 6,
\ 'none': 7,
\ 'job': 8,
\ 'channel': 9,
\ }
lockvar 1 s:types
let s:type_names = {
\ '0': 'number',
\ '1': 'string',
\ '2': 'func',
\ '3': 'list',
\ '4': 'dict',
\ '5': 'float',
\ '6': 'bool',
\ '7': 'none',
\ '8': 'job',
\ '9': 'channel',
\ }
lockvar 1 s:type_names
function! s:_vital_created(module) abort
let a:module.types = s:types
let a:module.type_names = s:type_names
endfunction
function! s:is_numeric(value) abort
let t = type(a:value)
return t == s:types.number || t == s:types.float
endfunction
function! s:is_special(value) abort
let t = type(a:value)
return t == s:types.bool || t == s:types.none
endfunction
function! s:is_predicate(value) abort
let t = type(a:value)
return t == s:types.number || t == s:types.string ||
\ t == s:types.bool || t == s:types.none
endfunction
function! s:is_comparable(value1, value2) abort
if !exists('s:is_comparable_cache')
let s:is_comparable_cache = s:_make_is_comparable_cache()
endif
return s:is_comparable_cache[type(a:value1)][type(a:value2)]
endfunction
function! s:_make_is_comparable_cache() abort
let vals = [
\ 0, '', function('type'), [], {}, 0.0,
\ get(v:, 'false'),
\ get(v:, 'null'),
\ exists('*test_null_job') ? test_null_job() : 0,
\ exists('*test_null_channel') ? test_null_channel() : 0,
\ ]
let result = []
for l:V1 in vals
let result_V1 = []
let result += [result_V1]
for l:V2 in vals
try
let _ = V1 == V2
let result_V1 += [1]
catch
let result_V1 += [0]
endtry
unlet V2
endfor
unlet V1
endfor
return result
endfunction

View File

@ -0,0 +1,328 @@
let s:plugin_name = expand('<sfile>:t:r')
let s:vital_base_dir = expand('<sfile>:h')
let s:project_root = expand('<sfile>:h:h:h')
let s:is_vital_vim = s:plugin_name is# 'vital'
let s:loaded = {}
let s:cache_sid = {}
" function() wrapper
if v:version > 703 || v:version == 703 && has('patch1170')
function! s:_function(fstr) abort
return function(a:fstr)
endfunction
else
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
let s:_s = '<SNR>' . s:_SID() . '_'
function! s:_function(fstr) abort
return function(substitute(a:fstr, 's:', s:_s, 'g'))
endfunction
endif
function! vital#{s:plugin_name}#new() abort
return s:new(s:plugin_name)
endfunction
function! vital#{s:plugin_name}#import(...) abort
if !exists('s:V')
let s:V = s:new(s:plugin_name)
endif
return call(s:V.import, a:000, s:V)
endfunction
let s:Vital = {}
function! s:new(plugin_name) abort
let base = deepcopy(s:Vital)
let base._plugin_name = a:plugin_name
return base
endfunction
function! s:vital_files() abort
if !exists('s:vital_files')
let s:vital_files = map(
\ s:is_vital_vim ? s:_global_vital_files() : s:_self_vital_files(),
\ 'fnamemodify(v:val, ":p:gs?[\\\\/]?/?")')
endif
return copy(s:vital_files)
endfunction
let s:Vital.vital_files = s:_function('s:vital_files')
function! s:import(name, ...) abort dict
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 = self._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
let s:Vital.import = s:_function('s:import')
function! s:load(...) abort 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
let dict_type = type({})
while !empty(target)
let ns = remove(target, 0)
if !has_key(dict, ns)
let dict[ns] = {}
endif
if type(dict[ns]) == dict_type
let dict = dict[ns]
else
unlet dict
break
endif
endwhile
if exists('dict')
call extend(dict, self._import(name))
endif
unlet arg
endfor
return self
endfunction
let s:Vital.load = s:_function('s:load')
function! s:unload() abort dict
let s:loaded = {}
let s:cache_sid = {}
unlet! s:vital_files
endfunction
let s:Vital.unload = s:_function('s:unload')
function! s:exists(name) abort dict
if a:name !~# '\v^\u\w*%(\.\u\w*)*$'
throw 'vital: Invalid module name: ' . a:name
endif
return s:_module_path(a:name) isnot# ''
endfunction
let s:Vital.exists = s:_function('s:exists')
function! s:search(pattern) abort dict
let paths = s:_extract_files(a:pattern, self.vital_files())
let modules = sort(map(paths, 's:_file2module(v:val)'))
return s:_uniq(modules)
endfunction
let s:Vital.search = s:_function('s:search')
function! s:plugin_name() abort dict
return self._plugin_name
endfunction
let s:Vital.plugin_name = s:_function('s:plugin_name')
function! s:_self_vital_files() abort
let builtin = printf('%s/__%s__/', s:vital_base_dir, s:plugin_name)
let installed = printf('%s/_%s/', s:vital_base_dir, s:plugin_name)
let base = builtin . ',' . installed
return split(globpath(base, '**/*.vim', 1), "\n")
endfunction
function! s:_global_vital_files() abort
let pattern = 'autoload/vital/__*__/**/*.vim'
return split(globpath(&runtimepath, pattern, 1), "\n")
endfunction
function! s:_extract_files(pattern, files) abort
let tr = {'.': '/', '*': '[^/]*', '**': '.*'}
let target = substitute(a:pattern, '\.\|\*\*\?', '\=tr[submatch(0)]', 'g')
let regexp = printf('autoload/vital/[^/]\+/%s.vim$', target)
return filter(a:files, 'v:val =~# regexp')
endfunction
function! s:_file2module(file) abort
let filename = fnamemodify(a:file, ':p:gs?[\\/]?/?')
let tail = matchstr(filename, 'autoload/vital/_\w\+/\zs.*\ze\.vim$')
return join(split(tail, '[\\/]\+'), '.')
endfunction
" @param {string} name e.g. Data.List
function! s:_import(name) abort dict
if has_key(s:loaded, a:name)
return copy(s:loaded[a:name])
endif
let module = self._get_module(a:name)
if has_key(module, '_vital_created')
call module._vital_created(module)
endif
let export_module = filter(copy(module), 'v:key =~# "^\\a"')
" Cache module before calling module.vital_loaded() to avoid cyclic
" dependences but remove the cache if module._vital_loaded() fails.
" let s:loaded[a:name] = export_module
let s:loaded[a:name] = export_module
if has_key(module, '_vital_loaded')
try
call module._vital_loaded(vital#{s:plugin_name}#new())
catch
unlet s:loaded[a:name]
throw 'vital: fail to call ._vital_loaded(): ' . v:exception
endtry
endif
return copy(s:loaded[a:name])
endfunction
let s:Vital._import = s:_function('s:_import')
" s:_get_module() returns module object wihch has all script local functions.
function! s:_get_module(name) abort dict
let funcname = s:_import_func_name(self.plugin_name(), a:name)
try
return call(funcname, [])
catch /^Vim\%((\a\+)\)\?:E117/
return s:_get_builtin_module(a:name)
endtry
endfunction
function! s:_get_builtin_module(name) abort
return s:sid2sfuncs(s:_module_sid(a:name))
endfunction
if s:is_vital_vim
" For vital.vim, we can use s:_get_builtin_module directly
let s:Vital._get_module = s:_function('s:_get_builtin_module')
else
let s:Vital._get_module = s:_function('s:_get_module')
endif
function! s:_import_func_name(plugin_name, module_name) abort
return printf('vital#_%s#%s#import', a:plugin_name, s:_dot_to_sharp(a:module_name))
endfunction
function! s:_module_sid(name) abort
let path = s:_module_path(a:name)
if !filereadable(path)
throw 'vital: module not found: ' . a:name
endif
let vital_dir = s:is_vital_vim ? '__\w\+__' : printf('_\{1,2}%s\%%(__\)\?', s:plugin_name)
let base = join([vital_dir, ''], '[/\\]\+')
let p = base . substitute('' . a:name, '\.', '[/\\\\]\\+', 'g')
let sid = s:_sid(path, p)
if !sid
call s:_source(path)
let sid = s:_sid(path, p)
if !sid
throw printf('vital: cannot get <SID> from path: %s', path)
endif
endif
return sid
endfunction
function! s:_module_path(name) abort
return get(s:_extract_files(a:name, s:vital_files()), 0, '')
endfunction
function! s:_module_sid_base_dir() abort
return s:is_vital_vim ? &rtp : s:project_root
endfunction
function! s:_dot_to_sharp(name) abort
return substitute(a:name, '\.', '#', 'g')
endfunction
function! s:_source(path) abort
execute 'source' fnameescape(a:path)
endfunction
" @vimlint(EVL102, 1, l:_)
" @vimlint(EVL102, 1, l:__)
function! s:_sid(path, filter_pattern) abort
let unified_path = s:_unify_path(a:path)
if has_key(s:cache_sid, unified_path)
return s:cache_sid[unified_path]
endif
for line in filter(split(s:_execute(':scriptnames'), "\n"), 'v:val =~# a:filter_pattern')
let [_, sid, path; __] = matchlist(line, '^\s*\(\d\+\):\s\+\(.\+\)\s*$')
if s:_unify_path(path) is# unified_path
let s:cache_sid[unified_path] = sid
return s:cache_sid[unified_path]
endif
endfor
return 0
endfunction
" We want to use a execute() builtin function instead of s:_execute(),
" however there is a bug in execute().
" execute() returns empty string when it is called in
" completion function of user defined ex command.
" https://github.com/vim-jp/issues/issues/1129
function! s:_execute(cmd) abort
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
if filereadable(expand('<sfile>:r') . '.VIM') " is case-insensitive or not
let s:_unify_path_cache = {}
" resolve() is slow, so we cache results.
" 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) abort
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) abort
return resolve(fnamemodify(a:path, ':p:gs?[\\/]?/?'))
endfunction
endif
" copied and modified from Vim.ScriptLocal
let s:SNR = join(map(range(len("\<SNR>")), '"[\\x" . printf("%0x", char2nr("\<SNR>"[v:val])) . "]"'), '')
function! s:sid2sfuncs(sid) abort
let fs = split(s:_execute(printf(':function /^%s%s_', s:SNR, a:sid)), "\n")
let r = {}
let pattern = printf('\m^function\s<SNR>%d_\zs\w\{-}\ze(', a:sid)
for fname in map(fs, 'matchstr(v:val, pattern)')
let r[fname] = function(s:_sfuncname(a:sid, fname))
endfor
return r
endfunction
"" Return funcname of script local functions with SID
function! s:_sfuncname(sid, funcname) abort
return printf('<SNR>%s_%s', a:sid, a:funcname)
endfunction
if exists('*uniq')
function! s:_uniq(list) abort
return uniq(a:list)
endfunction
else
function! s:_uniq(list) abort
let i = len(a:list) - 1
while 0 < i
if a:list[i] ==# a:list[i - 1]
call remove(a:list, i)
endif
let i -= 1
endwhile
return a:list
endfunction
endif

View File

@ -0,0 +1,5 @@
plantuml_previewer
a8773a35b8b122b59c956a23d1e686d595bca3b4
System.Job
System.Process

View File

@ -0,0 +1,20 @@
@startuml
scale 600 width
[*] -> State1
State1 --> State2 : Succeeded
State1 --> [*] : Aborted
State2 --> State3 : Succeeded
State2 --> [*] : Aborted
state State3 {
state "Accumulate Enough Data\nLong State Name" as long1
long1 : Just a test
[*] --> long1
long1 --> long1 : New Data
long1 --> ProcessData : Enough Data
}
State3 --> State3 : Failed
State3 --> [*] : Succeeded / Save Result
State3 --> [*] : Aborted
@enduml

View File

@ -0,0 +1,107 @@
*plantuml-previewer.txt* plugin for preview PlantUML
Version: 1.5.1
Author: Weirong Xu <weirongxu.raidou@gmail.com>
License: MIT License
==============================================================================
CONTENTS *plantuml-previewer-contents*
Introduction |plantuml-previewer-introduction|
Install |plantuml-previewer-install|
Usage |plantuml-previewer-usage|
Interface |plantuml-previewer-interface|
Commands |plantuml-previewer-interface-commands|
Variables |plantuml-previewer-interface-variables|
==============================================================================
INTRODUCTION *plantuml-previewer-introduction*
plantuml-previewer is a plugin to preview PlantUML
==============================================================================
INSTALL *plantuml-previewer-install*
Dependencies
- Java
- Graphviz : https://www.graphviz.org/download/
- Mac >
brew install graphviz
<
- Ubuntu >
apt-get install graphviz
<
- Other
https://www.graphviz.org/download/
- open-browser.vim https://github.com/tyru/open-browser.vim
- aklt/plantuml-syntax https://github.com/aklt/plantuml-syntax (vim syntax file for plantuml)
==============================================================================
USAGE *plantuml-previewer-usage*
1. Start editing plantuml file in Vim
2. Run |:PlantumlOpen| to open previewer webpage in browser
3. Saving plantuml file in Vim, then previewer webpage will refresh
==============================================================================
INTERFACE *plantuml-previewer-interface*
-----------------------------------------------------------------------------
COMMANDS *plantuml-previewer-interface-commands*
*:PlantumlOpen*
:PlantumlOpen
Open previewer webpage in browser, and watch current buffer
*:PlantumlStart*
:PlantumlStart
Like |PlantumlOpen|, but won't open in browser
*:PlantumlStop*
:PlantumlStop
Stop watch buffers
*:PlantumlToggle*
:PlantumlToggle
|PlantumlOpen| or |PlantumlStop|
*:PlantumlSave*
:PlantumlSave [{filepath}] [{format}]
Export uml diagram
If {filepath} is missing, the {filepath} using the same file path as
plant uml, and default extension guessed by |g:plantuml_previewer#save_format|
If {format} is missing, the {format} will be guessed by {filepath}
extension.
Available formats: >
png, svg, eps, pdf, vdx, xmi,
scxml, html, txt, utxt, latex
<
Example: >
:e diagram.puml
:PlantumlSave
:PlantumlSave diagram.png
:PlantumlSave diagram.svg
<
-----------------------------------------------------------------------------
VARIABLES *plantuml-previewer-interface-variables*
*g:plantuml_previewer#plantuml_jar_path*
g:plantuml_previewer#plantuml_jar_path
plantuml.jar path
Default: "lib/plantuml.jar"
*g:plantuml_previewer#save_format*
g:plantuml_previewer#save_format
|:PlantumlSave| default format
Default: "png"
*g:plantuml_previewer#viewer_path*
g:plantuml_previewer#viewer_path
Custom plantuml viewer path
The plugin will copy viewer to here if the directory does not exist
And `tmp.puml` and `tmp.svg` will output to here
==============================================================================
vim:tw=78:ts=8:ft=help:norl:

Binary file not shown.

View File

@ -0,0 +1,5 @@
command! PlantumlToggle call plantuml_previewer#toggle()
command! PlantumlOpen call plantuml_previewer#open()
command! PlantumlStart call plantuml_previewer#start()
command! PlantumlStop call plantuml_previewer#stop()
command! -nargs=* -complete=file PlantumlSave call plantuml_previewer#save_as(<f-args>)

View File

@ -0,0 +1,11 @@
set java_path=%1
set jar_path=%2
set puml_src_path=%3
set output_dir_path=%4
set output_path=%5
set save_path=%6
set image_type=%7
"%java_path%" -Dapple.awt.UIElement=true -jar "%jar_path%" "%puml_src_path%" -t%image_type% -o "%output_dir_path%"
copy "%output_path%" "%save_path%"

View File

@ -0,0 +1,12 @@
#!/usr/bin/env bash
java_path=$1
jar_path=$2
puml_src_path=$3
output_dir_path=$4
output_path=$5
save_path=$6
image_type=$7
"$java_path" -Dapple.awt.UIElement=true -jar "$jar_path" "$puml_src_path" -t$image_type -o "$output_dir_path"
cp "$output_path" "$save_path"

View File

@ -0,0 +1,15 @@
set java_path=%1
set jar_path=%2
set puml_src_path=%3
set output_dir_path=%4
set output_path=%5
set finial_path=%6
set image_type=%7
set timestamp=%8
set update_js_path=%9
"%java_path%" -Dapple.awt.UIElement=true -jar "%jar_path%" "%puml_src_path%" -t%image_type% -o "%output_dir_path%"
echo F | xcopy /S /Q /F /Y "%output_path%" "%finial_path%"
echo window.updateDiagramURL('%timestamp%') > "%update_js_path%"

View File

@ -0,0 +1,17 @@
#!/usr/bin/env bash
java_path=$1
jar_path=$2
puml_src_path=$3
output_dir_path=$4
output_path=$5
finial_path=$6
image_type=$7
timestamp=$8
update_js_path=$9
"$java_path" -Dapple.awt.UIElement=true -jar "$jar_path" "$puml_src_path" -t$image_type -o "$output_dir_path"
cp "$output_path" "$finial_path"
echo "window.updateDiagramURL('$timestamp')" > "$update_js_path"

View File

@ -0,0 +1 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 595.28 841.89"><defs><style>.cls-1{fill:none;stroke:#231815;stroke-miterlimit:10;stroke-width:60px;}.cls-2{fill:#231815;}</style></defs><title>未标题-2</title><circle class="cls-1" cx="237.42" cy="329.29" r="169.09" transform="translate(-163.31 264.32) rotate(-45)"/><rect class="cls-2" x="428.28" y="406.9" width="60" height="286.5" rx="30" ry="30" transform="translate(-254.79 485.18) rotate(-45)"/><path class="cls-2" d="M300.41,299.29h-33v-33a30,30,0,0,0-60,0v33h-33a30,30,0,0,0-30,30h0a30,30,0,0,0,30,30h33v33a30,30,0,1,0,60,0v-33h33a30,30,0,0,0,30-30h0A30,30,0,0,0,300.41,299.29Z"/></svg>

After

Width:  |  Height:  |  Size: 685 B

View File

@ -0,0 +1 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 595.28 841.89"><defs><style>.cls-1{fill:#231815;}.cls-2{fill:none;stroke:#231815;stroke-miterlimit:10;stroke-width:60px;}</style></defs><title>未标题-2</title><rect class="cls-1" x="143.94" y="299.29" width="185.99" height="60" rx="30" ry="30"/><circle class="cls-2" cx="236.93" cy="329.29" r="169.09" transform="translate(-163.45 263.98) rotate(-45)"/><rect class="cls-1" x="427.79" y="406.9" width="60" height="286.5" rx="30" ry="30" transform="translate(-254.93 484.84) rotate(-45)"/></svg>

After

Width:  |  Height:  |  Size: 584 B

View File

@ -0,0 +1 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 595.28 841.89"><defs><style>.cls-1{fill:#231815;}</style></defs><title>未标题-2</title><path class="cls-1" d="M477.38,403.76L328.27,245a44,44,0,0,0-64-.22l-151.36,159a41,41,0,0,0-10.75,27.67V605.09a41,41,0,0,0,41,41H214a24.4,24.4,0,0,0,24.4-24.4V542h113.4v79.68a24.4,24.4,0,0,0,24.4,24.4h70.9a41,41,0,0,0,41-41V431.44A41,41,0,0,0,477.38,403.76Z"/><path class="cls-1" d="M509.59,397.39L323.83,196.74a40,40,0,0,0-58.63-.08L83.09,392.29a40,40,0,0,1-56.53,2h0a40,40,0,0,1-2-56.53L265.36,79.07a40,40,0,0,1,58.63.08L568.3,343a40,40,0,0,1-2.18,56.53h0A40,40,0,0,1,509.59,397.39Z"/></svg>

After

Width:  |  Height:  |  Size: 671 B

View File

@ -0,0 +1 @@
<!DOCTYPE html><html><head><meta http-equiv="Content-type" content="text/html; charset=utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Plantuml Previewer</title><link href="./main.3f72aac9.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="app"></div><script type="text/javascript" src="./vendors~main.16025f89.js"></script><script type="text/javascript" src="./main.e8e44d5b.js"></script></body></html>

View File

@ -0,0 +1,2 @@
.body[data-v-2d92bcce]{width:100%;height:100%;position:relative}.tools[data-v-2d92bcce]{position:absolute;right:10px;top:10px;display:flex;flex-direction:column;z-index:1000}.tools a[data-v-2d92bcce]{cursor:pointer;display:block;width:30px;height:30px}.tools a img[data-v-2d92bcce]{width:30px;height:30px}.wrapper[data-v-2d92bcce]{flex:1;width:100%;height:100%;position:relative;top:0;left:0;overflow:hidden;background:#ccc}.box[data-v-2d92bcce]{position:absolute;cursor:move}.box img[data-v-2d92bcce]{width:100%;height:100%}
body,html{margin:0;width:100%;height:100%}

View File

@ -0,0 +1 @@
!function(t){function e(e){for(var n,a,s=e[0],l=e[1],h=e[2],d=0,u=[];d<s.length;d++)a=s[d],r[a]&&u.push(r[a][0]),r[a]=0;for(n in l)Object.prototype.hasOwnProperty.call(l,n)&&(t[n]=l[n]);for(c&&c(e);u.length;)u.shift()();return o.push.apply(o,h||[]),i()}function i(){for(var t,e=0;e<o.length;e++){for(var i=o[e],n=!0,s=1;s<i.length;s++){var l=i[s];0!==r[l]&&(n=!1)}n&&(o.splice(e--,1),t=a(a.s=i[0]))}return t}var n={},r={1:0},o=[];function a(e){if(n[e])return n[e].exports;var i=n[e]={i:e,l:!1,exports:{}};return t[e].call(i.exports,i,i.exports,a),i.l=!0,i.exports}a.m=t,a.c=n,a.d=function(t,e,i){a.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:i})},a.r=function(t){Object.defineProperty(t,"__esModule",{value:!0})},a.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return a.d(e,"a",e),e},a.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},a.p="./";var s=window.webpackJsonp=window.webpackJsonp||[],l=s.push.bind(s);s.push=e,s=s.slice();for(var h=0;h<s.length;h++)e(s[h]);var c=l;o.push([20,0]),i()}([,,,,,function(t,e,i){"use strict";i.r(e);var n=i(4),r=i(3),o=i(2),a=i.n(o),s=new Image,l={data:function(){return{img:{realWidth:null,realHeight:null,width:null,height:null,whRate:null},relativeCenter:{left:null,top:null},lastTimestamp:null,needReset:!0,diagramUrl:"../tmp.svg",url:null}},methods:{boxReset:function(){var t=this.$refs.wrapper;t.clientWidth/t.clientHeight>this.img.whRate?this.boxResize({height:t.clientHeight}):this.boxResize({width:t.clientWidth}),this.boxCenter()},boxResize:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=t.width,i=void 0===e?null:e,n=t.height,r=void 0===n?null:n;this.$refs.box;i?(this.img.width=i,this.img.height=i/this.img.whRate):r&&(this.img.height=r,this.img.width=r*this.img.whRate)},boxCenter:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=t.top,i=void 0===e?0:e,n=t.left,r=void 0===n?0:n,o=this.$refs.wrapper,a=this.$refs.box;this.relativeCenter.left=r,this.relativeCenter.top=i,r=o.clientWidth/2-r*this.img.width-this.img.width/2,i=o.clientHeight/2-i*this.img.height-this.img.height/2,a.style.left=r+"px",a.style.top=i+"px"},bindEvent:function(){var t=this,e=this.$refs.wrapper,i=this.$refs.box;new a.a(i).on("dragEnd",function(n){var r=i.style,o=r.top,a=r.left;o=parseFloat(o),a=parseFloat(a),t.relativeCenter.left=(e.clientWidth/2-(a+t.img.width/2))/t.img.width,t.relativeCenter.top=(e.clientHeight/2-(o+t.img.height/2))/t.img.height}),Object(r.addWheelListener)(e,function(e){e.preventDefault(),t.zoom(e.deltaY)}),s.addEventListener("error",function(e){console.error(e),t.needReset=!0}),s.addEventListener("load",function(){t.url=s.src,t.img.realWidth=s.width,t.img.realHeight=s.height;var e=s.width/s.height;t.img.whRate!==e&&(t.img.height=t.img.width/e,t.img.whRate=e),t.needReset&&(t.needReset=!1,t.boxReset())})},zoom:function(t){var e=this.img.width-t*this.img.width/1e3;if(e>100){var i=this.relativeCenter,n=i.top,r=i.left;this.boxResize({width:e}),this.boxCenter({top:n,left:r})}},reloadImage:function(){s.src=this.diagramUrl+"?t="+Date.now()},reloadUpdateJs:function(){var t=document.getElementsByTagName("head")[0],e=document.createElement("script"),i=function(){setTimeout(function(){t.removeChild(e)},200)};e.type="text/javascript",e.addEventListener("error",i),e.addEventListener("load",i),e.src="../tmp.js?t="+Date.now(),t.appendChild(e)}},mounted:function(){var t=this;this.bindEvent(),this.reloadImage(),window.updateDiagramURL=function(e){e!==t.lastTimestamp&&(t.lastTimestamp=e,t.reloadImage())},setInterval(function(){t.reloadUpdateJs()},1e3)}},h=i(1);var c=function(t){i(16)},d=Object(h.a)(l,function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"body"},[n("div",{staticClass:"tools"},[n("a",{attrs:{title:"Double click"},on:{click:t.boxReset}},[n("img",{attrs:{src:i(10)}})]),t._v(" "),n("a",{attrs:{title:"Mouse wheel"},on:{click:function(e){t.zoom(-100)}}},[n("img",{attrs:{src:i(9)}})]),t._v(" "),n("a",{attrs:{title:"Mouse wheel"},on:{click:function(e){t.zoom(100)}}},[n("img",{attrs:{src:i(8)}})])]),t._v(" "),n("div",{ref:"wrapper",staticClass:"wrapper",on:{dblclick:t.boxReset}},[n("div",{ref:"box",staticClass:"box",style:{width:t.img.width+"px",height:t.img.height+"px"},attrs:{"data-center":"left: "+t.relativeCenter.left+", top: "+t.relativeCenter.top}},[n("img",{directives:[{name:"show",rawName:"v-show",value:t.url,expression:"url"}],attrs:{src:t.url},on:{load:function(){return t.$emit("loadedImage")}}})])])])},[],!1,c,"data-v-2d92bcce",null).exports;i(7);new n.a({el:"#app",render:function(t){return t(d)}})},,function(t,e,i){},function(t,e,i){t.exports=i.p+"c38c5e8361a61039ba107d5798346ce6.svg"},function(t,e,i){t.exports=i.p+"c027ca96553d780c4370107827292565.svg"},function(t,e,i){t.exports=i.p+"ce21159a71b4ecaf63de7f048eef373b.svg"},,,,,,function(t,e,i){},,,,function(t,e,i){t.exports=i(5)}]);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Plantuml Previewer</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="app"></div>
</body>
</html>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Plantuml Previewer</title>
<meta http-equiv="refresh" content="0; url=./dist/index.html" />
</head>
<body>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
{
"name": "plantuml-previewer",
"version": "1.0.0",
"description": "Plantuml Previewer",
"private": true,
"dependencies": {
"draggabilly": "^2.2.0",
"vue": "^2.5.16",
"wheel": "0.0.5"
},
"devDependencies": {
"file-loader": "^1.1.11",
"node-sass": "^4.13.1",
"poi": "^10.1.9",
"sass-loader": "^7.0.1"
},
"scripts": {
"start": "poi --port 3233 src/index.js",
"build": "poi build src/index.js -d dist"
},
"author": "Weirong Xu <weirongxu.raidou@gmail.com>",
"license": "MIT"
}

View File

@ -0,0 +1,11 @@
module.exports = {
plugins: [
(poi) => {
const isBuild = poi.command === 'build'
if (isBuild) {
poi.options.sourceMap = false
poi.options.publicPath = './'
}
}
],
}

View File

@ -0,0 +1 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 595.28 841.89"><defs><style>.cls-1{fill:#231815;}</style></defs><title>未标题-2</title><path class="cls-1" d="M477.38,403.76L328.27,245a44,44,0,0,0-64-.22l-151.36,159a41,41,0,0,0-10.75,27.67V605.09a41,41,0,0,0,41,41H214a24.4,24.4,0,0,0,24.4-24.4V542h113.4v79.68a24.4,24.4,0,0,0,24.4,24.4h70.9a41,41,0,0,0,41-41V431.44A41,41,0,0,0,477.38,403.76Z"/><path class="cls-1" d="M509.59,397.39L323.83,196.74a40,40,0,0,0-58.63-.08L83.09,392.29a40,40,0,0,1-56.53,2h0a40,40,0,0,1-2-56.53L265.36,79.07a40,40,0,0,1,58.63.08L568.3,343a40,40,0,0,1-2.18,56.53h0A40,40,0,0,1,509.59,397.39Z"/></svg>

After

Width:  |  Height:  |  Size: 671 B

View File

@ -0,0 +1 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 595.28 841.89"><defs><style>.cls-1{fill:none;stroke:#231815;stroke-miterlimit:10;stroke-width:60px;}.cls-2{fill:#231815;}</style></defs><title>未标题-2</title><circle class="cls-1" cx="237.42" cy="329.29" r="169.09" transform="translate(-163.31 264.32) rotate(-45)"/><rect class="cls-2" x="428.28" y="406.9" width="60" height="286.5" rx="30" ry="30" transform="translate(-254.79 485.18) rotate(-45)"/><path class="cls-2" d="M300.41,299.29h-33v-33a30,30,0,0,0-60,0v33h-33a30,30,0,0,0-30,30h0a30,30,0,0,0,30,30h33v33a30,30,0,1,0,60,0v-33h33a30,30,0,0,0,30-30h0A30,30,0,0,0,300.41,299.29Z"/></svg>

After

Width:  |  Height:  |  Size: 685 B

View File

@ -0,0 +1 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 595.28 841.89"><defs><style>.cls-1{fill:#231815;}.cls-2{fill:none;stroke:#231815;stroke-miterlimit:10;stroke-width:60px;}</style></defs><title>未标题-2</title><rect class="cls-1" x="143.94" y="299.29" width="185.99" height="60" rx="30" ry="30"/><circle class="cls-2" cx="236.93" cy="329.29" r="169.09" transform="translate(-163.45 263.98) rotate(-45)"/><rect class="cls-1" x="427.79" y="406.9" width="60" height="286.5" rx="30" ry="30" transform="translate(-254.93 484.84) rotate(-45)"/></svg>

After

Width:  |  Height:  |  Size: 584 B

View File

@ -0,0 +1,8 @@
import Vue from 'vue'
import Preview from './preview.vue'
import './index.scss'
new Vue({
el: '#app',
render: h => h(Preview)
})

View File

@ -0,0 +1,5 @@
html, body {
margin: 0;
width: 100%;
height: 100%;
}

View File

@ -0,0 +1,201 @@
<template>
<div class="body">
<div class="tools">
<a title="Double click" @click="boxReset"><img src="./icons/home.svg"></a>
<a title="Mouse wheel" @click="zoom(-100)"><img src="./icons/zoom-in.svg"></a>
<a title="Mouse wheel" @click="zoom(100)"><img src="./icons/zoom-out.svg"></a>
</div>
<div class="wrapper" ref="wrapper" @dblclick="boxReset">
<div
class="box"
:style="{width: img.width + 'px', height: img.height + 'px'}"
:data-center="`left: ${relativeCenter.left}, top: ${relativeCenter.top}`"
ref="box">
<img
v-show="url"
:src="url"
@load="() => $emit('loadedImage')">
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.body {
width: 100%;
height: 100%;
position: relative;
}
.tools {
position: absolute;
right: 10px;
top: 10px;
display: flex;
flex-direction: column;
z-index: 1000;
a {
cursor: pointer;
display: block;
width: 30px;
height: 30px;
img {
width: 30px;
height: 30px;
}
}
}
.wrapper {
flex: 1;
width: 100%;
height: 100%;
position: relative;
top: 0;
left: 0;
overflow: hidden;
background: #ccc;
}
.box {
position: absolute;
img {
width: 100%;
height: 100%;
}
cursor: move;
}
</style>
<script>
import {addWheelListener, removeWheelListener} from 'wheel'
import Dragger from 'draggabilly'
const updateJsPath = '../tmp.js'
const diagramUrl = '../tmp.svg'
const $tmpImage = new Image()
export default {
data() {
return {
img: {
realWidth: null,
realHeight: null,
width: null,
height: null,
whRate: null,
},
relativeCenter: {
left: null,
top: null,
},
lastTimestamp: null,
needReset: true,
diagramUrl,
url: null,
}
},
methods: {
boxReset() {
const $wrapper = this.$refs.wrapper
if ($wrapper.clientWidth / $wrapper.clientHeight > this.img.whRate) {
this.boxResize({height: $wrapper.clientHeight})
} else {
this.boxResize({width: $wrapper.clientWidth})
}
this.boxCenter()
},
boxResize({width = null, height = null} = {}) {
const $box = this.$refs.box
if (width) {
this.img.width = width
this.img.height = width / this.img.whRate
} else if (height) {
this.img.height = height
this.img.width = height * this.img.whRate
}
},
boxCenter({top = 0, left = 0} = {}) {
const $wrapper = this.$refs.wrapper
const $box = this.$refs.box
this.relativeCenter.left = left
this.relativeCenter.top = top
left = $wrapper.clientWidth/2 - (left * this.img.width) - this.img.width/2
top = $wrapper.clientHeight/2 - (top * this.img.height) - this.img.height/2
$box.style.left = left + 'px'
$box.style.top = top + 'px'
},
bindEvent() {
const $wrapper = this.$refs.wrapper
const $box = this.$refs.box
const dragger = new Dragger($box)
dragger.on('dragEnd', event => {
let {top, left} = $box.style
top = parseFloat(top)
left = parseFloat(left)
this.relativeCenter.left = ($wrapper.clientWidth/2 - (left + this.img.width/2)) / this.img.width
this.relativeCenter.top = ($wrapper.clientHeight/2 - (top + this.img.height/2)) / this.img.height
})
addWheelListener($wrapper, e => {
e.preventDefault()
this.zoom(e.deltaY)
})
$tmpImage.addEventListener('error', (error) => {
console.error(error)
this.needReset = true
})
$tmpImage.addEventListener('load', () => {
this.url = $tmpImage.src
this.img.realWidth = $tmpImage.width
this.img.realHeight = $tmpImage.height
const whRate = $tmpImage.width / $tmpImage.height
if (this.img.whRate !== whRate) {
this.img.height = this.img.width / whRate
this.img.whRate = whRate
}
if (this.needReset) {
this.needReset = false
this.boxReset()
}
})
},
zoom(delta) {
const width = this.img.width - delta * this.img.width / 1000
if (width > 100) {
const {top, left} = this.relativeCenter
this.boxResize({width})
this.boxCenter({top, left})
}
},
reloadImage() {
$tmpImage.src = this.diagramUrl + '?t=' + Date.now()
},
reloadUpdateJs() {
const head = document.getElementsByTagName('head')[0]
const script = document.createElement('script')
const removeScript = () => {
setTimeout(() => {
head.removeChild(script)
}, 200)
}
script.type = 'text/javascript'
script.addEventListener('error', removeScript)
script.addEventListener('load', removeScript)
script.src = updateJsPath + '?t=' + Date.now()
head.appendChild(script)
},
},
mounted() {
this.bindEvent()
this.reloadImage()
window.updateDiagramURL = (timestamp) => {
if (timestamp !== this.lastTimestamp) {
this.lastTimestamp = timestamp
this.reloadImage()
}
}
setInterval(() => {
this.reloadUpdateJs()
}, 1000)
},
}
</script>

View File

@ -164,52 +164,53 @@ CONTENTS *SpaceVim-contents*
77. lang#pascal............................|SpaceVim-layers-lang-pascal|
78. lang#perl................................|SpaceVim-layers-lang-perl|
79. lang#php..................................|SpaceVim-layers-lang-php|
80. lang#pony................................|SpaceVim-layers-lang-pony|
81. lang#processing....................|SpaceVim-layers-lang-processing|
82. lang#prolog............................|SpaceVim-layers-lang-prolog|
83. lang#puppet............................|SpaceVim-layers-lang-puppet|
84. lang#purescript....................|SpaceVim-layers-lang-purescript|
85. lang#python............................|SpaceVim-layers-lang-python|
86. lang#racket............................|SpaceVim-layers-lang-racket|
87. lang#racket...............................|SpaceVim-layers-lang-red|
88. lang#reason............................|SpaceVim-layers-lang-reason|
89. lang#ring...................................|SpaceVim-layers-lang-r|
90. lang#ring................................|SpaceVim-layers-lang-ring|
91. lang#ruby................................|SpaceVim-layers-lang-ruby|
92. lang#rust................................|SpaceVim-layers-lang-rust|
93. lang#scala..............................|SpaceVim-layers-lang-scala|
94. lang#scheme............................|SpaceVim-layers-lang-scheme|
95. lang#sh....................................|SpaceVim-layers-lang-sh|
96. lang#smalltalk......................|SpaceVim-layers-lang-smalltalk|
97. lang#sml..................................|SpaceVim-layers-lang-sml|
98. lang#swig...............................|SpaceVim-layers-lang-swift|
99. lang#swig................................|SpaceVim-layers-lang-swig|
100. lang#tcl.................................|SpaceVim-layers-lang-tcl|
101. lang#toml...............................|SpaceVim-layers-lang-toml|
102. lang#typescript...................|SpaceVim-layers-lang-typescript|
103. lang#v.....................................|SpaceVim-layers-lang-v|
104. lang#vala...............................|SpaceVim-layers-lang-vala|
105. lang#vbnet.............................|SpaceVim-layers-lang-vbnet|
106. lang#vim.................................|SpaceVim-layers-lang-vim|
107. lang#vue.................................|SpaceVim-layers-lang-vue|
108. lang#wolfram.........................|SpaceVim-layers-lang-wolfram|
109. lang#xml.................................|SpaceVim-layers-lang-xml|
110. lang#xquery...........................|SpaceVim-layers-lang-xquery|
111. lang#zig.................................|SpaceVim-layers-lang-zig|
112. language server protocol......................|SpaceVim-layers-lsp|
113. leaderf...................................|SpaceVim-layers-leaderf|
114. mail.........................................|SpaceVim-layers-mail|
115. operator.................................|SpaceVim-layers-operator|
116. shell.......................................|SpaceVim-layers-shell|
117. ssh...........................................|SpaceVim-layers-ssh|
118. test.........................................|SpaceVim-layers-test|
119. tmux.........................................|SpaceVim-layers-tmux|
120. tools#dash.............................|SpaceVim-layers-tools-dash|
121. tools#mpv...............................|SpaceVim-layers-tools-mpv|
122. tools#zeal.............................|SpaceVim-layers-tools-zeal|
123. treesitter.............................|SpaceVim-layers-treesitter|
124. ui.............................................|SpaceVim-layers-ui|
125. unite.......................................|SpaceVim-layers-unite|
80. lang#plantuml........................|SpaceVim-layers-lang-plantuml|
81. lang#pony................................|SpaceVim-layers-lang-pony|
82. lang#processing....................|SpaceVim-layers-lang-processing|
83. lang#prolog............................|SpaceVim-layers-lang-prolog|
84. lang#puppet............................|SpaceVim-layers-lang-puppet|
85. lang#purescript....................|SpaceVim-layers-lang-purescript|
86. lang#python............................|SpaceVim-layers-lang-python|
87. lang#racket............................|SpaceVim-layers-lang-racket|
88. lang#racket...............................|SpaceVim-layers-lang-red|
89. lang#reason............................|SpaceVim-layers-lang-reason|
90. lang#ring...................................|SpaceVim-layers-lang-r|
91. lang#ring................................|SpaceVim-layers-lang-ring|
92. lang#ruby................................|SpaceVim-layers-lang-ruby|
93. lang#rust................................|SpaceVim-layers-lang-rust|
94. lang#scala..............................|SpaceVim-layers-lang-scala|
95. lang#scheme............................|SpaceVim-layers-lang-scheme|
96. lang#sh....................................|SpaceVim-layers-lang-sh|
97. lang#smalltalk......................|SpaceVim-layers-lang-smalltalk|
98. lang#sml..................................|SpaceVim-layers-lang-sml|
99. lang#swig...............................|SpaceVim-layers-lang-swift|
100. lang#swig...............................|SpaceVim-layers-lang-swig|
101. lang#tcl.................................|SpaceVim-layers-lang-tcl|
102. lang#toml...............................|SpaceVim-layers-lang-toml|
103. lang#typescript...................|SpaceVim-layers-lang-typescript|
104. lang#v.....................................|SpaceVim-layers-lang-v|
105. lang#vala...............................|SpaceVim-layers-lang-vala|
106. lang#vbnet.............................|SpaceVim-layers-lang-vbnet|
107. lang#vim.................................|SpaceVim-layers-lang-vim|
108. lang#vue.................................|SpaceVim-layers-lang-vue|
109. lang#wolfram.........................|SpaceVim-layers-lang-wolfram|
110. lang#xml.................................|SpaceVim-layers-lang-xml|
111. lang#xquery...........................|SpaceVim-layers-lang-xquery|
112. lang#zig.................................|SpaceVim-layers-lang-zig|
113. language server protocol......................|SpaceVim-layers-lsp|
114. leaderf...................................|SpaceVim-layers-leaderf|
115. mail.........................................|SpaceVim-layers-mail|
116. operator.................................|SpaceVim-layers-operator|
117. shell.......................................|SpaceVim-layers-shell|
118. ssh...........................................|SpaceVim-layers-ssh|
119. test.........................................|SpaceVim-layers-test|
120. tmux.........................................|SpaceVim-layers-tmux|
121. tools#dash.............................|SpaceVim-layers-tools-dash|
122. tools#mpv...............................|SpaceVim-layers-tools-mpv|
123. tools#zeal.............................|SpaceVim-layers-tools-zeal|
124. treesitter.............................|SpaceVim-layers-treesitter|
125. ui.............................................|SpaceVim-layers-ui|
126. unite.......................................|SpaceVim-layers-unite|
7. Usage....................................................|SpaceVim-usage|
1. buffers-and-files..................|SpaceVim-usage-buffers-and-files|
2. command-line-mode..................|SpaceVim-usage-command-line-mode|
@ -3711,6 +3712,44 @@ This layer also provides REPL support for php, the key bindings are:
<
==============================================================================
LANG#PLANTUML *SpaceVim-layers-lang-plantuml*
This layer is for plantuml development, disabled by default, to enable this
layer, add following snippet to your SpaceVim configuration file.
>
[[layers]]
name = 'lang#plantuml'
<
LAYER OPTIONS
1. `java_command`: Set the path of java command, by default, it is `java`
>
[[layers]]
name = 'lang#plantuml'
java_command = 'path/to/java'
<
2. `plantuml_jar_path`: Set the path of `pluatuml.jar`.
>
[[layers]]
name = 'lang#plantuml'
plantuml_jar_path = 'path/to/plantuml.jar'
<
KEY BINDINGS
>
Mode Key Function
---------------------------------------------
normal SPC l p preview uml file
normal SPC l c stop preview
normal SPC l s save uml file
<
==============================================================================
LANG#PONY *SpaceVim-layers-lang-pony*