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