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