yaml.vim 5.4 KB

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