command.vim 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. " vim: set fdm=marker et ts=4 sw=4 sts=4:
  2. let s:plugin_root = expand('<sfile>:p:h:h:h')
  3. " Init(): sets up defaults, creates the Pandoc command, requires python support {{{1
  4. function! pandoc#command#Init() abort
  5. " set up defaults {{{2
  6. " use message buffers? {{{3
  7. if !exists('g:pandoc#command#use_message_buffers')
  8. if has('nvim') == 1
  9. let g:pandoc#command#use_message_buffers = 0
  10. else
  11. let g:pandoc#command#use_message_buffers = 1
  12. endif
  13. endif
  14. " LaTeX engine to use to produce PDFs with pandoc (xelatex, pdflatex, lualatex) {{{3
  15. if !exists('g:pandoc#command#latex_engine')
  16. let g:pandoc#command#latex_engine = 'xelatex'
  17. endif
  18. if exists('b:pandoc_yaml_data')
  19. if has_key(b:pandoc_yaml_data, 'latex_engine')
  20. let b:pandoc_command_latex_engine = b:pandoc_yaml_data['latex_engine']
  21. endif
  22. endif
  23. " custom function defining command to open the created files {{{3
  24. if !exists('g:pandoc#command#custom_open')
  25. let g:pandoc#command#custom_open = ''
  26. endif
  27. " open pdf files preferrably? {{{3
  28. if !exists('g:pandoc#command#prefer_pdf')
  29. let g:pandoc#command#prefer_pdf = 0
  30. endif
  31. " file where to save command templates {{{3
  32. if !exists('g:pandoc#command#templates_file')
  33. let g:pandoc#command#templates_file = s:plugin_root . '/templates'
  34. endif
  35. " auto-execute pandoc on writes {{{3
  36. if !exists('g:pandoc#command#autoexec_on_writes')
  37. let g:pandoc#command#autoexec_on_writes = 0
  38. endif
  39. " command to execute on writes {{{2
  40. if !exists('g:pandoc#command#autoexec_command')
  41. let g:pandoc#command#autoexec = ''
  42. endif
  43. " path to pandoc executable
  44. if !exists('g:pandoc#command#path')
  45. let g:pandoc#command#path = 'pandoc'
  46. endif
  47. " custom command to execute instead of pandoc
  48. if !exists('g:pandoc#compiler#command')
  49. let g:pandoc#compiler#command = g:pandoc#command#path
  50. endif
  51. " custom command arguments
  52. if !exists('g:pandoc#compiler#arguments')
  53. let g:pandoc#compiler#arguments = ''
  54. endif
  55. " create :Pandoc {{{2
  56. if has('python3') || has('python3/dyn')
  57. " let's make sure it gets loaded
  58. py3 import vim
  59. command! -buffer -bang -nargs=? -complete=customlist,pandoc#command#PandocComplete
  60. \ Pandoc call pandoc#command#Pandoc('<args>', '<bang>')
  61. else
  62. " simple version for systems without python
  63. command! -buffer -bang -nargs=? Pandoc call pandoc#command#PandocNative('<args>', '<bang>')
  64. endif "}}}2
  65. " create :PandocTemplate {{{2
  66. command! -buffer -nargs=1 -complete=custom,pandoc#command#PandocTemplateComplete
  67. \ PandocTemplate call pandoc#command#PandocTemplate('<args>')
  68. " set up auto-execution
  69. au! VimPandoc BufWritePost <buffer> call pandoc#command#AutoPandoc()
  70. endfunction
  71. " :Pandoc command {{{1
  72. " Pandoc(args, bang): the Pandoc command itself, requires python support {{{2
  73. " args: arguments to pass pandoc
  74. " bang: should we open the created file afterwards?
  75. function! pandoc#command#Pandoc(args, bang) abort
  76. if has('python3') || has('python3/dyn')
  77. py3 from vim_pandoc.command import pandoc
  78. let l:expanded_args = s:ExpandArgs(a:args)
  79. py3 pandoc(vim.eval('l:expanded_args'), vim.eval('a:bang') != '')
  80. endif
  81. endfunction
  82. function! pandoc#command#PandocNative(args, bang) abort
  83. let l:cmd = g:pandoc#compiler#command.' '.g:pandoc#compiler#arguments.' '.s:ExpandArgs(a:args).' '.fnameescape(expand('%'))
  84. if has('job')
  85. if a:bang ==# '!'
  86. call job_start(l:cmd, {'exit_cb':'pandoc#command#ExitHandler'})
  87. else
  88. call job_start(l:cmd)
  89. endif
  90. else
  91. call system(l:cmd)
  92. if a:bang ==# '!'
  93. let l:output_filepath = s:ExtractOutputFilepath(split(s:ExpandArgs(a:args)))
  94. call s:LaunchFile(l:output_filepath)
  95. endif
  96. endif
  97. endfunction
  98. function! s:ExpandArgs(args) abort
  99. let templatized_args = substitute(a:args, '#\(\S\+\)',
  100. \'\=pandoc#command#GetTemplate(submatch(1))', 'g') " expand templates
  101. let expanded_args = substitute(templatized_args, '%\(:[phtre]\)\+',
  102. \'\=expand(submatch(0))', 'g') " expand placeholders
  103. return eval('expanded_args')
  104. endfunction
  105. " PandocComplete(a, c, pos): the Pandoc command argument completion func, requires python support {{{2
  106. function! pandoc#command#PandocComplete(a, c, pos) abort
  107. if has('python3') || has('python3/dyn')
  108. py3 from vim_pandoc.helpparser import PandocInfo
  109. py3 pandoc_info = PandocInfo()
  110. let cmd_args = split(a:c, ' ', 1)[1:]
  111. if len(cmd_args) == 1 && (cmd_args[0] ==# '' || py3eval('vim.eval("cmd_args[0]").startswith(vim.eval("a:a"))'))
  112. return py3eval('list(filter(lambda i: i.startswith(vim.eval("a:a")), sorted(pandoc_info.output_formats + ["pdf"])))')
  113. endif
  114. if len(cmd_args) >= 2
  115. let long_opts = py3eval('["--" + i for i in filter(lambda i: i.startswith(vim.eval("a:a[2:]")), [v for v in pandoc_info.get_options_list() if len(v) > 1])]')
  116. let short_opts = py3eval('["-" + i for i in filter(lambda i: i.startswith(vim.eval("a:a[1:]")), [v for v in pandoc_info.get_options_list() if len(v) == 1])]')
  117. return filter(uniq(extend(sort(short_opts), sort(long_opts))), 'v:val !=# "-:"')
  118. endif
  119. endif
  120. endfunction
  121. function! pandoc#command#AutoPandoc() abort
  122. if g:pandoc#command#autoexec_on_writes == 1
  123. let command = ''
  124. if exists('g:pandoc#command#autoexec_command')
  125. let command = g:pandoc#command#autoexec_command
  126. endif
  127. if exists('b:pandoc_command_autoexec_command')
  128. let command = b:pandoc_command_autoexec_command
  129. endif
  130. exe command
  131. endif
  132. endfunction
  133. " PandocAsyncCallback(should_open, returncode): Callback to execute after pandoc finished {{{2
  134. " should_open: should we open the cretaed file?
  135. " returncode: the returncode value pandoc gave
  136. function! pandoc#command#PandocAsyncCallback(should_open, returncode) abort
  137. py3 from vim_pandoc.command import pandoc
  138. py3 pandoc.on_done(vim.eval('a:should_open') == '1', vim.eval('a:returncode'))
  139. endfunction
  140. " PandocJobHandler(id, data, event): Callback for neovim {{{2
  141. function! pandoc#command#JobHandler(id, data, event) abort dict
  142. if a:event ==# 'stdout'
  143. call writefile(a:data, 'pandoc.out', 'ab')
  144. elseif a:event ==# 'stderr'
  145. call writefile(a:data, 'pandoc.out', 'ab')
  146. else
  147. if a:event ==# 'exit' && a:data == 0
  148. bwipeout! pandoc-execute
  149. endif
  150. py3 from vim_pandoc.command import pandoc
  151. py3 pandoc.on_done(vim.eval('self.should_open') == '1', vim.eval('a:data'))
  152. endif
  153. endfunction
  154. " Functions for launching file without python {{{2
  155. function! s:ExtractOutputFilepath(lst_command) abort " {{{3
  156. let output_parameter_position = index(a:lst_command, '-o')
  157. if (output_parameter_position + 1 < len(a:lst_command))
  158. return a:lst_command[output_parameter_position + 1]
  159. else
  160. return ''
  161. endif
  162. endfunction
  163. function! s:LaunchFile(filepath) abort " {{{3 http://www.dwheeler.com/essays/open-files-urls.html
  164. let open_command = ''
  165. if (has('win32') || has('win64')) && executable('cmd')
  166. let open_command = 'cmd /c start'
  167. elseif (has('macunix') || has('unix')) && executable('open')
  168. let open_command = 'open'
  169. elseif has('unix') && executable('xdg-open')
  170. let open_command = 'xdg-open'
  171. elseif has('win32unix') && executable('cygstart')
  172. let open_command = 'cygstart'
  173. endif
  174. if strlen(open_command) && strlen(a:filepath)
  175. execute '!' . open_command . ' ' . a:filepath
  176. endif
  177. endfunction
  178. function! pandoc#command#ExitHandler(job, status) abort " {{{3
  179. if a:status == 0 "if exit status shows success
  180. " don't run if platform is windows and doesn't have patch 8.1.0464 because
  181. " if there were backslashes in the command (e.g. output to subdirectory),
  182. " the cmd property will be missing them
  183. " https://github.com/vim/vim/commit/1df2fa47b49dae568af6048b1dce1edbf4eee7e7
  184. if !((has('win32')||has('win64')) && !(has('patch-8-1-0464')))
  185. let job_cmd = job_info(a:job).cmd
  186. let output_filepath = s:ExtractOutputFilepath(job_cmd)
  187. call s:LaunchFile(output_filepath)
  188. endif
  189. endif
  190. endfunction
  191. " Command template functions {{{1
  192. function! pandoc#command#PandocTemplate(args) abort "{{{2
  193. let cmd_args = split(a:args, ' ', 1)
  194. if cmd_args[0] ==# 'get'
  195. if len(cmd_args) >= 2
  196. echo pandoc#command#GetTemplate(join(cmd_args[1:], ' '))
  197. else
  198. echohl ErrorMsg
  199. echom "pandoc:command:'PandocTemplate get' needs an argument"
  200. echohl None
  201. endif
  202. elseif cmd_args[0] ==# 'save'
  203. if len(cmd_args) == 2 && cmd_args[1] !=# ''
  204. call pandoc#command#SaveTemplate(cmd_args[1])
  205. elseif len(cmd_args) > 2
  206. call pandoc#command#SaveTemplate(cmd_args[1], join(cmd_args[2:], ' '))
  207. else
  208. echohl ErrorMsg
  209. echom "pandoc:command:missing or invalid arguments for 'PandocTemplate save'"
  210. echohl None
  211. endif
  212. endif
  213. endfunction
  214. function! pandoc#command#PandocTemplateComplete(a, c, pos) abort "{{{2
  215. let cmd_args = split(a:c, ' ', 1)[1:]
  216. if len(cmd_args) == 1
  217. return "save\nget"
  218. elseif len(cmd_args) > 1
  219. if cmd_args[0] ==# 'get'
  220. return join(pandoc#command#GetTemplateNames(), "\n")
  221. endif
  222. endif
  223. return ''
  224. endfunction
  225. function! s:LastCommandAsTemplate() abort "{{{2
  226. let hist_item_idx = histnr('cmd')
  227. while 1
  228. let hist_item = histget('cmd', hist_item_idx)
  229. if match(hist_item, '^Pandoc!\? ') == 0
  230. break
  231. endif
  232. let hist_item_idx = hist_item_idx - 1
  233. endwhile
  234. return join(split(hist_item, ' ')[1:], ' ')
  235. endfunction
  236. function! s:LoadTemplates() abort "{{{2
  237. let templates_list = []
  238. if filereadable(g:pandoc#command#templates_file) == 1
  239. call extend(templates_list, readfile(g:pandoc#command#templates_file))
  240. endif
  241. let templates = {}
  242. if len(templates_list) > 0
  243. for temp in templates_list
  244. let data = split(temp, '|')
  245. let templates[data[0]] = data[1]
  246. endfor
  247. endif
  248. return templates
  249. endfunction
  250. function! pandoc#command#GetTemplateNames() abort "{{{2
  251. return keys(s:LoadTemplates())
  252. endfunction
  253. function! pandoc#command#SaveTemplate(template_name, ...) abort "{{{2
  254. if a:0 > 0
  255. let template = a:1
  256. else
  257. let template = s:LastCommandAsTemplate()
  258. endif
  259. let templates = s:LoadTemplates()
  260. let new_template = {a:template_name : template}
  261. echom string(new_template)
  262. call extend(templates, new_template)
  263. let new_templates_list = []
  264. for key in keys(templates)
  265. call add(new_templates_list, key . '|' . templates[key])
  266. endfor
  267. call writefile(new_templates_list, g:pandoc#command#templates_file)
  268. endfunction
  269. function! pandoc#command#GetTemplate(template_name) abort "{{{2
  270. let templates = s:LoadTemplates()
  271. return templates[a:template_name]
  272. endfunction