yaml.vim 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. " Vim indent file
  2. " Language: YAML
  3. " Maintainer: Nikolai Pavlov <zyx.vim@gmail.com>
  4. " Last Updates: Lukas Reineke, "lacygoill"
  5. " Last Change: 2022 Jun 17
  6. " 2024 Feb 29 by Vim project: disable mulitline indent by default
  7. " 2024 Aug 14 by Vim project: fix re-indenting when commenting out lines
  8. " Only load this indent file when no other was loaded.
  9. if exists('b:did_indent')
  10. finish
  11. endif
  12. let b:did_indent = 1
  13. setlocal indentexpr=GetYAMLIndent(v:lnum)
  14. setlocal indentkeys=!^F,o,O,0},0],<:>,0-
  15. setlocal nosmartindent
  16. let b:undo_indent = 'setlocal indentexpr< indentkeys< smartindent<'
  17. " Only define the function once.
  18. if exists('*GetYAMLIndent')
  19. finish
  20. endif
  21. let s:save_cpo = &cpo
  22. set cpo&vim
  23. function s:FindPrevLessIndentedLine(lnum, ...)
  24. let prevlnum = prevnonblank(a:lnum-1)
  25. let curindent = a:0 ? a:1 : indent(a:lnum)
  26. while prevlnum
  27. \ && indent(prevlnum) >= curindent
  28. \ && getline(prevlnum) !~# '^\s*#'
  29. let prevlnum = prevnonblank(prevlnum-1)
  30. endwhile
  31. return prevlnum
  32. endfunction
  33. function s:FindPrevLEIndentedLineMatchingRegex(lnum, regex)
  34. let plilnum = s:FindPrevLessIndentedLine(a:lnum, indent(a:lnum)+1)
  35. while plilnum && getline(plilnum) !~# a:regex
  36. let plilnum = s:FindPrevLessIndentedLine(plilnum)
  37. endwhile
  38. return plilnum
  39. endfunction
  40. let s:mapkeyregex = '\v^\s*\#@!\S@=%(\''%([^'']|\''\'')*\''' ..
  41. \ '|\"%([^"\\]|\\.)*\"' ..
  42. \ '|%(%(\:\ )@!.)*)\:%(\ |$)'
  43. let s:liststartregex = '\v^\s*%(\-%(\ |$))'
  44. let s:c_ns_anchor_char = '\v%([\n\r\uFEFF \t,[\]{}]@!\p)'
  45. let s:c_ns_anchor_name = s:c_ns_anchor_char .. '+'
  46. let s:c_ns_anchor_property = '\v\&' .. s:c_ns_anchor_name
  47. let s:ns_word_char = '\v[[:alnum:]_\-]'
  48. let s:ns_tag_char = '\v%(\x\x|' .. s:ns_word_char .. '|[#/;?:@&=+$.~*''()])'
  49. let s:c_named_tag_handle = '\v\!' .. s:ns_word_char .. '+\!'
  50. let s:c_secondary_tag_handle = '\v\!\!'
  51. let s:c_primary_tag_handle = '\v\!'
  52. let s:c_tag_handle = '\v%(' .. s:c_named_tag_handle.
  53. \ '|' .. s:c_secondary_tag_handle.
  54. \ '|' .. s:c_primary_tag_handle .. ')'
  55. let s:c_ns_shorthand_tag = '\v' .. s:c_tag_handle .. s:ns_tag_char .. '+'
  56. let s:c_non_specific_tag = '\v\!'
  57. let s:ns_uri_char = '\v%(\x\x|' .. s:ns_word_char .. '\v|[#/;?:@&=+$,.!~*''()[\]])'
  58. let s:c_verbatim_tag = '\v\!\<' .. s:ns_uri_char.. '+\>'
  59. let s:c_ns_tag_property = '\v' .. s:c_verbatim_tag.
  60. \ '\v|' .. s:c_ns_shorthand_tag.
  61. \ '\v|' .. s:c_non_specific_tag
  62. let s:block_scalar_header = '\v[|>]%([+-]?[1-9]|[1-9]?[+-])?'
  63. function GetYAMLIndent(lnum)
  64. if a:lnum == 1 || !prevnonblank(a:lnum-1)
  65. return 0
  66. endif
  67. let prevlnum = prevnonblank(a:lnum-1)
  68. let previndent = indent(prevlnum)
  69. let line = getline(a:lnum)
  70. if line =~# '^\s*#' && getline(a:lnum-1) =~# '^\s*#'
  71. " Comment blocks should have identical indent
  72. return previndent
  73. elseif line =~# '^\s*[\]}]'
  74. " Lines containing only closing braces should have previous indent
  75. return indent(s:FindPrevLessIndentedLine(a:lnum))
  76. endif
  77. " Ignore comment lines when calculating indent
  78. while getline(prevlnum) =~# '^\s*#'
  79. let prevlnum = prevnonblank(prevlnum-1)
  80. if !prevlnum
  81. return previndent
  82. endif
  83. endwhile
  84. let prevline = getline(prevlnum)
  85. let previndent = indent(prevlnum)
  86. " Any examples below assume that shiftwidth=2
  87. if prevline =~# '\v[{[:]$|[:-]\ [|>][+\-]?%(\s+\#.*|\s*)$'
  88. " Mapping key:
  89. " nested mapping: ...
  90. "
  91. " - {
  92. " key: [
  93. " list value
  94. " ]
  95. " }
  96. "
  97. " - |-
  98. " Block scalar without indentation indicator
  99. return previndent+shiftwidth()
  100. elseif prevline =~# '\v[:-]\ [|>]%(\d+[+\-]?|[+\-]?\d+)%(\#.*|\s*)$'
  101. " - |+2
  102. " block scalar with indentation indicator
  103. "#^^ indent+2, not indent+shiftwidth
  104. return previndent + str2nr(matchstr(prevline,
  105. \'\v([:-]\ [|>])@<=[+\-]?\d+%([+\-]?%(\s+\#.*|\s*)$)@='))
  106. elseif prevline =~# '\v\"%([^"\\]|\\.)*\\$'
  107. " "Multiline string \
  108. " with escaped end"
  109. let qidx = match(prevline, '\v\"%([^"\\]|\\.)*\\')
  110. return virtcol([prevlnum, qidx+1])
  111. elseif line =~# s:liststartregex
  112. " List line should have indent equal to previous list line unless it was
  113. " caught by one of the previous rules
  114. return indent(s:FindPrevLEIndentedLineMatchingRegex(a:lnum,
  115. \ s:liststartregex))
  116. elseif line =~# s:mapkeyregex
  117. " Same for line containing mapping key
  118. let prevmapline = s:FindPrevLEIndentedLineMatchingRegex(a:lnum,
  119. \ s:mapkeyregex)
  120. if getline(prevmapline) =~# '^\s*- '
  121. return indent(prevmapline) + 2
  122. else
  123. return indent(prevmapline)
  124. endif
  125. elseif get(g:, 'yaml_indent_multiline_scalar', 0) &&
  126. \ prevline =~# '^\s*- '
  127. " - List with
  128. " multiline scalar
  129. return previndent+2
  130. elseif get(g:, 'yaml_indent_multiline_scalar', 0) &&
  131. \ prevline =~# s:mapkeyregex .. '\v\s*%(%(' .. s:c_ns_tag_property ..
  132. \ '\v|' .. s:c_ns_anchor_property ..
  133. \ '\v|' .. s:block_scalar_header ..
  134. \ '\v)%(\s+|\s*%(\#.*)?$))*'
  135. " Mapping with: value
  136. " that is multiline scalar
  137. return previndent+shiftwidth()
  138. endif
  139. return previndent
  140. endfunction
  141. let &cpo = s:save_cpo