formatting.vim 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. " vim: set fdm=marker et ts=4 sw=4 sts=4:
  2. function! pandoc#formatting#Init() abort "{{{1
  3. " set up defaults {{{2
  4. " Formatting mode {{{3
  5. " s: use soft wraps
  6. " h: use hard wraps
  7. " a: auto format (only used if h is set)
  8. " A: smart auto format
  9. if !exists('g:pandoc#formatting#mode')
  10. let g:pandoc#formatting#mode = 's'
  11. endif
  12. "}}}3
  13. " Auto-format {{{3
  14. " Autoformat blacklist {{{4
  15. if !exists('g:pandoc#formatting#smart_autoformat_blacklist')
  16. let g:pandoc#formatting#smart_autoformat_blacklist = [
  17. \ 'pandoc.+header',
  18. \ 'pandoc\S{-}(code|title|line|math)block(title)?',
  19. \ 'pdc(delimited|latex)?codeblock',
  20. \ 'pandoc.+table',
  21. \ 'pandoctable',
  22. \ 'pandoc.+latex',
  23. \ 'pandocreferencedefinition',
  24. \ 'pandocreferencelabel',
  25. \ 'tex.*',
  26. \ 'pdclatex*',
  27. \ 'yaml.*',
  28. \ 'pdcyaml',
  29. \ 'delimiter'
  30. \]
  31. endif
  32. " }}}4
  33. " Autoformat on CursorMovedI {{{4
  34. if !exists('g:pandoc#formatting#smart_autoformat_on_cursormoved')
  35. let g:pandoc#formatting#smart_autoformat_on_cursormoved = 0
  36. endif
  37. "}}}4
  38. "}}}3
  39. " Text width {{{3
  40. if !exists('g:pandoc#formatting#textwidth')
  41. let g:pandoc#formatting#textwidth = 79
  42. endif
  43. " }}}3
  44. " equalprg {{{3
  45. if !exists('g:pandoc#formatting#equalprg')
  46. if executable('pandoc')
  47. let g:pandoc#formatting#equalprg = 'pandoc -t markdown'
  48. if g:pandoc#formatting#mode =~# 'h'
  49. let g:pandoc#formatting#equalprg.= ' --columns '.g:pandoc#formatting#textwidth
  50. else
  51. let g:pandoc#formatting#equalprg.= ' --wrap=none'
  52. endif
  53. else
  54. let g:pandoc#formatting#equalprg = ''
  55. endif
  56. endif
  57. " extend the value of equalprg if needed
  58. if !exists('g:pandoc#formatting#extra_equalprg')
  59. let g:pandoc#formatting#extra_equalprg = '--reference-links'
  60. endif
  61. " }}}3
  62. " formatprg {{{3
  63. if !exists('g:pandoc#formatting#formatprg#use_pandoc')
  64. let g:pandoc#formatting#formatprg#use_pandoc = 0
  65. endif
  66. if !exists('g:pandoc#formatting#formatprg')
  67. if g:pandoc#formatting#formatprg#use_pandoc == 1
  68. if executable('pandoc')
  69. let g:pandoc#formatting#formatprg = 'pandoc -t markdown'
  70. if g:pandoc#formatting#mode =~# 'h'
  71. let g:pandoc#formatting#formatprg.= ' --columns '.g:pandoc#formatting#textwidth
  72. else
  73. let g:pandoc#formatting#formatprg.= ' --wrap=none'
  74. endif
  75. endif
  76. else
  77. let g:pandoc#formatting#formatprg = ''
  78. endif
  79. endif
  80. " }}}3
  81. " Use a custom indentexpr? {{{3
  82. if !exists('g:pandoc#formatting#set_indentexpr')
  83. let g:pandoc#formatting#set_indentexpr = 0
  84. endif
  85. " }}}3
  86. " set up soft or hard wrapping modes "{{{2
  87. if stridx(g:pandoc#formatting#mode, 'h') >= 0 && stridx(g:pandoc#formatting#mode, 's') < 0
  88. call pandoc#formatting#UseHardWraps()
  89. elseif stridx(g:pandoc#formatting#mode, 's') >= 0 && stridx(g:pandoc#formatting#mode, 'h') < 0
  90. call pandoc#formatting#UseSoftWraps()
  91. else
  92. echoerr 'pandoc: The value of g:pandoc#formatting#mode is inconsistent. Using default.'
  93. call pandoc#formatting#UseSoftWraps()
  94. endif
  95. " equalprog {{{2
  96. "
  97. " Use pandoc to tidy up text?
  98. "
  99. " NOTE: If you use this on your entire file, it will wipe out title blocks.
  100. "
  101. if g:pandoc#formatting#equalprg !=? ''
  102. let &l:equalprg=g:pandoc#formatting#equalprg.' '.g:pandoc#formatting#extra_equalprg
  103. endif
  104. " formatprg {{{2
  105. if g:pandoc#formatting#formatprg !=? ''
  106. let &l:formatprg=g:pandoc#formatting#formatprg
  107. endif
  108. " common settings {{{2
  109. " indent using a custom indentexpr
  110. if g:pandoc#formatting#set_indentexpr == 1
  111. setlocal indentexpr=pandoc#formatting#IndentExpr()
  112. endif
  113. setlocal autoindent " copy indent level from previous line.
  114. " Don't add two spaces at the end of punctuation when joining lines
  115. setlocal nojoinspaces
  116. " Always use linebreak.
  117. setlocal linebreak
  118. setlocal breakat-=*
  119. if exists('+breakindent')
  120. setlocal breakindent
  121. endif
  122. if has('smartindent')
  123. setlocal nosmartindent
  124. endif
  125. " Textile uses .. for comments
  126. if &filetype ==? 'textile'
  127. setlocal commentstring=..%s
  128. setlocal comments=f:..
  129. else " Other markup formats use HTML-style comments
  130. setlocal commentstring=<!--%s-->
  131. setlocal comments=s:<!--,m:\ \ \ \ ,e:-->,:\|,n:>
  132. endif
  133. let s:last_autoformat_lnum = 0
  134. "}}}2
  135. "
  136. " Global settings we must override {{{2
  137. let s:original_breakat = &breakat
  138. augroup pandoc_formatting
  139. au! BufEnter <buffer> set breakat-=@
  140. au! BufLeave <buffer> exe 'set breakat='.substitute(s:original_breakat, '\s*', '\\ ', 1)
  141. augroup END
  142. set breakat-=@
  143. "}}}2
  144. endfunction
  145. " Autoformat switches {{{1
  146. function! pandoc#formatting#isAutoformatEnabled() abort
  147. if exists('b:pandoc_autoformat_enabled')
  148. return b:pandoc_autoformat_enabled
  149. else
  150. return 1
  151. endif
  152. endfunction
  153. function! pandoc#formatting#EnableAutoformat() abort
  154. let b:pandoc_autoformat_enabled = 1
  155. endfunction
  156. function! pandoc#formatting#DisableAutoformat() abort
  157. let b:pandoc_autoformat_enabled = 0
  158. endfunction
  159. function! pandoc#formatting#ToggleAutoformat() abort
  160. if get(b:, 'pandoc_autoformat_enabled', 1) == 1
  161. let b:pandoc_autoformat_enabled = 0
  162. else
  163. let b:pandoc_autoformat_enabled = 1
  164. endif
  165. endfunction
  166. function! pandoc#formatting#AutoFormat(force) abort "{{{1
  167. if !exists('b:pandoc_autoformat_enabled') || b:pandoc_autoformat_enabled == 1
  168. let l:line = line('.')
  169. if a:force == 1 || l:line != s:last_autoformat_lnum || (l:line == s:last_autoformat_lnum && col('.') == 1)
  170. let s:last_autoformat_lnum = l:line
  171. let l:stack = []
  172. let l:should_enable = 1
  173. let l:context_prevents = 0
  174. let l:blacklist_re = '\c\v('.join(g:pandoc#formatting#smart_autoformat_blacklist, '|').')'
  175. let l:stack = synstack(l:line, col('.'))
  176. if len(l:stack) == 0
  177. " let's try with the first column in this line
  178. let l:stack = synstack(l:line, 1)
  179. endif
  180. if len(l:stack) > 0
  181. let l:synName = synIDattr(l:stack[0], 'name')
  182. " we check on the base syntax id, so we don't have to pollute the
  183. " blacklist with stuff like pandocAtxMark, which is contained
  184. if match(l:synName, l:blacklist_re) >= 0
  185. let l:should_enable = 0
  186. endif
  187. if match(l:synName, 'pandocdefinitionblock') >= 0
  188. let context_prevents = 1
  189. endif
  190. try
  191. let l:p_synName = synIDattr(synstack(l:line-1, col('$'))[0], 'name')
  192. catch /E684/
  193. let l:p_synName = ''
  194. endtry
  195. if match(l:synName.l:p_synName, '\c\v(pandoc|pdc)[uo]?list') >= 0
  196. let l:context_prevents = 1
  197. endif
  198. else
  199. let l:p_synName = synIDattr(synID(l:line-1, col('$'), 0), 'name')
  200. if l:p_synName =~? '\c\vpandoc(u?list|referencedef)'
  201. let l:context_prevents = 1
  202. elseif l:p_synName =~? '\c\vpandochrule'
  203. let l:context_prevents = 1
  204. elseif l:p_synName =~? '\c\v(pandoc|pdc)codeblock' && indent('.')%4 == 0
  205. let l:context_prevents = 1
  206. elseif getline(l:line -1) =~? '^\w\+:'
  207. let l:context_prevents = 1
  208. endif
  209. endif
  210. if l:should_enable == 1
  211. if l:context_prevents != 1
  212. setlocal formatoptions+=a
  213. else
  214. setlocal formatoptions-=a " in case it is set
  215. endif
  216. setlocal formatoptions+=t
  217. " block quotes are formatted like text comments (hackish, i know),
  218. " so we want to make them break at textwidth
  219. if l:stack != [] && l:synName ==? '\c\v(pandoc|pdc)blockquote'
  220. setlocal formatoptions+=c
  221. endif
  222. elseif l:should_enable == 0
  223. setlocal formatoptions-=a
  224. setlocal formatoptions-=t
  225. setlocal formatoptions-=c "just in case we have added it for a block quote
  226. endif
  227. endif
  228. elseif &formatoptions !=# 'tn'
  229. setlocal formatoptions=tnroq
  230. endif
  231. endfunction
  232. function! pandoc#formatting#UseHardWraps() abort "{{{1
  233. " reset settings that might have changed by UseSoftWraps
  234. setlocal formatoptions&
  235. setlocal display&
  236. setlocal wrap&
  237. " textwidth
  238. exec 'setlocal textwidth='.g:pandoc#formatting#textwidth
  239. " t: wrap on &textwidth
  240. " n: keep inner indent for list items.
  241. setlocal formatoptions=tnroq
  242. " will detect numbers, letters, *, +, and - as list headers, according to
  243. " pandoc syntax.
  244. " TODO: add support for roman numerals
  245. setlocal formatlistpat=^\\s*\\([*+-]\\\|\\((*\\d\\+[.)]\\+\\)\\\|\\((*\\l[.)]\\+\\)\\)\\s\\+
  246. if stridx(g:pandoc#formatting#mode, 'a') >= 0
  247. " a: auto-format
  248. " w: lines with trailing spaces mark continuing
  249. " paragraphs, and lines ending on non-spaces end paragraphs.
  250. " we add `w` as a workaround to `a` joining compact lists.
  251. setlocal formatoptions+=aw
  252. elseif stridx(g:pandoc#formatting#mode, 'A') >= 0
  253. augroup pandoc_autoformat
  254. au InsertEnter <buffer> call pandoc#formatting#AutoFormat(1)
  255. au InsertLeave <buffer> setlocal formatoptions=tnroq
  256. if g:pandoc#formatting#smart_autoformat_on_cursormoved == 1
  257. au CursorMovedI <buffer> call pandoc#formatting#AutoFormat(0)
  258. endif
  259. augroup END
  260. endif
  261. endfunction
  262. function! pandoc#formatting#UseSoftWraps() abort "{{{1
  263. " reset settings that might have been changed by UseHardWraps
  264. setlocal textwidth&
  265. setlocal formatoptions&
  266. setlocal formatlistpat&
  267. " soft wrapping
  268. setlocal wrap
  269. setlocal formatoptions=1n
  270. " Show partial wrapped lines
  271. setlocal display=lastline
  272. endfunction
  273. function! pandoc#formatting#IndentExpr() abort "{{{1
  274. let l:cline = getline(v:lnum)
  275. let l:pline = getline(v:lnum - 1)
  276. let l:cline_li = matchstr(l:cline, '^\s*[*-:]\s*')
  277. if l:cline_li !=? ''
  278. return len(matchstr(l:cline_li, '^\s*'))
  279. endif
  280. let l:pline_li = matchstr(l:pline, '^\s*[*-:]\s\+')
  281. if l:pline_li !=? ''
  282. return len(l:pline_li)
  283. endif
  284. if l:pline ==? ''
  285. return indent(v:lnum)
  286. else
  287. return indent(v:lnum - 1)
  288. endif
  289. endfunction