mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-02-03 07:10:05 +08:00
273 lines
7.7 KiB
VimL
273 lines
7.7 KiB
VimL
" PlantUML Live Preview for ascii/unicode art
|
|
" @Author: Martin Grenfell <martin.grenfell@gmail.com>
|
|
" @Date: 2018-12-07 13:00:22
|
|
" @Last Modified by: Tsuyoshi CHO <Tsuyoshi.CHO@Gmail.com>
|
|
" @Last Modified time: 2018-12-08 00:02:38
|
|
" @License: WTFPL
|
|
" PlantUML preview plugin core
|
|
|
|
" Intro {{{1
|
|
if exists("g:autoloaded_slumlord")
|
|
finish
|
|
endif
|
|
let g:autoloaded_slumlord = 1
|
|
|
|
let s:save_cpo = &cpo
|
|
set cpo&vim
|
|
|
|
" variable {{{1
|
|
let g:slumlord_plantuml_jar_path = get(g:, 'slumlord_plantuml_jar_path', expand("<sfile>:p:h") . "/../plantuml.jar")
|
|
let g:slumlord_plantuml_include_path = get(g:, 'slumlord_plantuml_include_path', expand("~/.config/plantuml/include/"))
|
|
let g:slumlord_asciiart_utf = get(g:, 'slumlord_asciiart_utf', 1)
|
|
|
|
" function {{{1
|
|
function! slumlord#updatePreview(args) abort
|
|
if !s:shouldInsertPreview()
|
|
return
|
|
end
|
|
|
|
let charset = 'UTF-8'
|
|
|
|
let type = 'utxt'
|
|
let ext = 'utxt'
|
|
if !g:slumlord_asciiart_utf
|
|
let type = 'txt'
|
|
let ext = 'atxt'
|
|
endif
|
|
|
|
let tmpfname = tempname()
|
|
call s:mungeDiagramInTmpFile(tmpfname)
|
|
let b:slumlord_preview_fname = fnamemodify(tmpfname, ':r') . '.' . ext
|
|
|
|
let cmd = "java -Dapple.awt.UIElement=true -Dplantuml.include.path=\"". g:slumlord_plantuml_include_path ."\" -splash: -jar ". g:slumlord_plantuml_jar_path ." -charset ". charset ." -t" . type ." ". tmpfname
|
|
|
|
let write = has_key(a:args, 'write') && a:args["write"] == 1
|
|
if exists("*jobstart")
|
|
call jobstart(cmd, { "on_exit": function("s:asyncHandlerAdapter"), "write": write, "bufnr": bufnr("") })
|
|
elseif exists("*job_start")
|
|
call job_start(cmd, { "exit_cb": {job,st->call('s:asyncHandlerAdapter',[job,st,0],{"bufnr": bufnr(""),"write": write})}, "out_io": "buffer", "out_buf": bufnr("") })
|
|
else
|
|
call system(cmd)
|
|
if v:shell_error == 0
|
|
call s:updater.update(a:args)
|
|
endif
|
|
endif
|
|
endfunction
|
|
|
|
function! s:shouldInsertPreview() abort
|
|
"check for 'no-preview flag
|
|
if search('^\s*''no-preview', 'wn') > 0
|
|
return
|
|
endif
|
|
|
|
"check for state diagram
|
|
if search('^\s*\[\*\]', 'wn') > 0
|
|
return
|
|
endif
|
|
|
|
"check for use cases
|
|
if search('^\s*\%((.*)\|:.*:\)', 'wn') > 0
|
|
return
|
|
endif
|
|
|
|
"check for class diagrams
|
|
if search('^\s*class\>', 'wn') > 0
|
|
return
|
|
endif
|
|
|
|
"check for activity diagrams
|
|
if search('^\s*:.*;', 'wn') > 0
|
|
return
|
|
endif
|
|
|
|
return 1
|
|
endfunction
|
|
|
|
function! s:asyncHandlerAdapter(job_id, data, event) abort dict
|
|
if a:data != 0
|
|
return 0
|
|
endif
|
|
|
|
if bufnr("") != self.bufnr
|
|
return 0
|
|
endif
|
|
|
|
call s:updater.update(self)
|
|
endfunction
|
|
|
|
function! s:readWithoutStoringAsAltFile(fname) abort
|
|
let oldcpoptions = &cpoptions
|
|
set cpoptions-=a
|
|
exec 'read' a:fname
|
|
let &cpoptions = oldcpoptions
|
|
endfunction
|
|
|
|
function! s:mungeDiagramInTmpFile(fname) abort
|
|
call writefile(getline(1, '$'), a:fname)
|
|
call s:convertNonAsciiSupportedSyntax(a:fname)
|
|
endfunction
|
|
|
|
function! s:convertNonAsciiSupportedSyntax(fname) abort
|
|
exec 'sp' a:fname
|
|
|
|
/@startuml/,/@enduml/s/^\s*\(boundary\|database\|entity\|control\)/participant/e
|
|
/@startuml/,/@enduml/s/^\s*\(end \)\?\zsref\>/note/e
|
|
/@startuml/,/@enduml/s/^\s*ref\>/note/e
|
|
/@startuml/,/@enduml/s/|||/||4||/e
|
|
/@startuml/,/@enduml/s/\.\.\.\([^.]*\)\.\.\./==\1==/e
|
|
write
|
|
|
|
bwipe!
|
|
endfunction
|
|
|
|
function! s:removeLeadingWhitespace(...) abort
|
|
let opts = a:0 ? a:1 : {}
|
|
|
|
let diagramEnd = get(opts, 'diagramEnd', line('$'))
|
|
|
|
let smallestLead = 100
|
|
|
|
for i in range(1, diagramEnd-1)
|
|
let lead = match(getline(i), '\S')
|
|
if lead >= 0 && lead < smallestLead
|
|
let smallestLead = lead
|
|
endif
|
|
endfor
|
|
|
|
exec '1,' . diagramEnd . 's/^ \{'.smallestLead.'}//e'
|
|
endfunction
|
|
|
|
function! s:addTitle() abort
|
|
let lnum = search('^title ', 'n')
|
|
if !lnum
|
|
return
|
|
endif
|
|
|
|
let title = substitute(getline(lnum), '^title \(.*\)', '\1', '')
|
|
|
|
call append(0, "")
|
|
call append(0, repeat("^", strdisplaywidth(title)+6))
|
|
call append(0, " " . title)
|
|
endfunction
|
|
|
|
" InPlaceUpdater object {{{1
|
|
let s:InPlaceUpdater = {}
|
|
let s:InPlaceUpdater.divider = "@startuml"
|
|
|
|
function! s:InPlaceUpdater.update(args) abort
|
|
let startLine = line(".")
|
|
let lastLine = line("$")
|
|
let startCol = col(".")
|
|
|
|
call self.__deletePreviousDiagram()
|
|
call self.__insertDiagram(b:slumlord_preview_fname)
|
|
call s:addTitle()
|
|
|
|
call cursor(line("$") - (lastLine - startLine), startCol)
|
|
|
|
if a:args['write']
|
|
noautocmd write
|
|
endif
|
|
endfunction
|
|
|
|
function! s:InPlaceUpdater.__deletePreviousDiagram() abort
|
|
if self.__dividerLnum() > 1
|
|
exec '0,' . (self.__dividerLnum() - 1) . 'delete _'
|
|
endif
|
|
endfunction
|
|
|
|
function! s:InPlaceUpdater.__insertDiagram(fname) abort
|
|
call append(0, "")
|
|
call append(0, "")
|
|
0
|
|
|
|
call s:readWithoutStoringAsAltFile(a:fname)
|
|
|
|
"fix trailing whitespace
|
|
exec '1,' . self.__dividerLnum() . 's/\s\+$//e'
|
|
|
|
call s:removeLeadingWhitespace()
|
|
endfunction
|
|
|
|
function! s:InPlaceUpdater.__dividerLnum() abort
|
|
return search(self.divider, 'wn')
|
|
endfunction
|
|
|
|
" WinUpdater object {{{1
|
|
let s:WinUpdater = {}
|
|
function! s:WinUpdater.update(args) abort
|
|
let fname = b:slumlord_preview_fname
|
|
call self.__moveToWin()
|
|
%d
|
|
|
|
call append(0, "")
|
|
call append(0, "")
|
|
0
|
|
|
|
call s:readWithoutStoringAsAltFile(fname)
|
|
|
|
"fix trailing whitespace
|
|
%s/\s\+$//e
|
|
|
|
call s:removeLeadingWhitespace()
|
|
call s:addTitle()
|
|
wincmd p
|
|
endfunction
|
|
|
|
function s:WinUpdater.__moveToWin() abort
|
|
if exists("b:slumlord_bnum") && bufexists(b:slumlord_bnum)
|
|
if bufwinnr(b:slumlord_bnum) != -1
|
|
exec bufwinnr(b:slumlord_bnum) . "wincmd w"
|
|
else
|
|
exec b:slumlord_bnum . "sb"
|
|
endif
|
|
else
|
|
let prev_bnum = bufnr("")
|
|
new
|
|
setlocal buftype=nofile
|
|
setlocal bufhidden=wipe
|
|
setlocal noswapfile
|
|
setlocal textwidth=0 " avoid automatic line break
|
|
call setbufvar(prev_bnum, "slumlord_bnum", bufnr(""))
|
|
call self.__setupWinOpts()
|
|
endif
|
|
endfunction
|
|
|
|
function s:WinUpdater.__setupWinOpts() abort
|
|
setl nowrap
|
|
setl buftype=nofile
|
|
syn match plantumlPreviewBoxParts #[┌┐└┘┬─│┴<>╚═╪╝╔═╤╪╗║╧╟╠╣]#
|
|
syn match plantumlPreviewCtrlFlow #\(LOOP\|ALT\|OPT\)[^│]*│\s*[a-zA-Z0-9?! ]*#
|
|
syn match plantumlPreviewCtrlFlow #║ \[[^]]*\]#hs=s+3,he=e-1
|
|
syn match plantumlPreviewEntity #│\w*│#hs=s+1,he=e-1
|
|
syn match plantumlPreviewTitleUnderline #\^\+#
|
|
syn match plantumlPreviewNoteText #║[^┌┐└┘┬─│┴<>╚═╪╝╔═╤╪╗║╧╟╠╣]*[░ ]║#hs=s+1,he=e-2
|
|
syn match plantumlPreviewDividerText #╣[^┌┐└┘┬─│┴<>╚═╪╝╔═╤╪╗║╧╟╣]*╠#hs=s+1,he=e-1
|
|
syn match plantumlPreviewMethodCall #\(\(│\|^\)\s*\)\@<=[a-zA-Z_]*([[:alnum:],_ ]*)#
|
|
syn match plantumlPreviewMethodCallParen #[()]# containedin=plantumlPreviewMethodCall contained
|
|
|
|
hi def link plantumlPreviewBoxParts normal
|
|
hi def link plantumlPreviewCtrlFlow Keyword
|
|
hi def link plantumlPreviewLoopName Statement
|
|
hi def link plantumlPreviewEntity Statement
|
|
hi def link plantumlPreviewTitleUnderline Statement
|
|
hi def link plantumlPreviewNoteText Constant
|
|
hi def link plantumlPreviewDividerText Constant
|
|
hi def link plantumlPreviewMethodCall plantumlText
|
|
hi def link plantumlPreviewMethodCallParen plantumlColonLine
|
|
endfunction
|
|
|
|
|
|
" other shit {{{1
|
|
if exists("g:slumlord_separate_win") && g:slumlord_separate_win
|
|
let s:updater = s:WinUpdater
|
|
else
|
|
let s:updater = s:InPlaceUpdater
|
|
endif
|
|
|
|
" Outro {{{1
|
|
let &cpo = s:save_cpo
|
|
unlet s:save_cpo
|
|
|
|
" vim:set fdm=marker:
|