json.vim (4644B)
1 " Vim indent file 2 " Language: JSON 3 " Maintainer: Eli Parra <eli@elzr.com> https://github.com/elzr/vim-json 4 " Last Change: 2020 Aug 30 5 " https://github.com/jakar/vim-json/commit/20b650e22aa750c4ab6a66aa646bdd95d7cd548a#diff-e81fc111b2052e306d126bd9989f7b7c 6 " 2022 Sep 07: b:undo_indent added by Doug Kearns 7 " Original Author: Rogerz Zhang <rogerz.zhang at gmail.com> http://github.com/rogerz/vim-json 8 " Acknowledgement: Based off of vim-javascript maintained by Darrick Wiebe 9 " http://www.vim.org/scripts/script.php?script_id=2765 10 11 " 0. Initialization {{{1 12 " ================= 13 14 " Only load this indent file when no other was loaded. 15 if exists("b:did_indent") 16 finish 17 endif 18 let b:did_indent = 1 19 20 setlocal nosmartindent 21 22 " Now, set up our indentation expression and keys that trigger it. 23 setlocal indentexpr=GetJSONIndent(v:lnum) 24 setlocal indentkeys=0{,0},0),0[,0],!^F,o,O,e 25 26 let b:undo_indent = "setl inde< indk< si<" 27 28 " Only define the function once. 29 if exists("*GetJSONIndent") 30 finish 31 endif 32 33 let s:cpo_save = &cpo 34 set cpo&vim 35 36 " 1. Variables {{{1 37 " ============ 38 39 let s:line_term = '\s*\%(\%(\/\/\).*\)\=$' 40 " Regex that defines blocks. 41 let s:block_regex = '\%({\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s:line_term 42 43 " 2. Auxiliary Functions {{{1 44 " ====================== 45 46 " Check if the character at lnum:col is inside a string. 47 function s:IsInString(lnum, col) 48 return synIDattr(synID(a:lnum, a:col, 1), 'name') == 'jsonString' 49 endfunction 50 51 " Find line above 'lnum' that isn't empty, or in a string. 52 function s:PrevNonBlankNonString(lnum) 53 let lnum = prevnonblank(a:lnum) 54 while lnum > 0 55 " If the line isn't empty or in a string, end search. 56 let line = getline(lnum) 57 if !(s:IsInString(lnum, 1) && s:IsInString(lnum, strlen(line))) 58 break 59 endif 60 let lnum = prevnonblank(lnum - 1) 61 endwhile 62 return lnum 63 endfunction 64 65 " Check if line 'lnum' has more opening brackets than closing ones. 66 function s:LineHasOpeningBrackets(lnum) 67 let open_0 = 0 68 let open_2 = 0 69 let open_4 = 0 70 let line = getline(a:lnum) 71 let pos = match(line, '[][(){}]', 0) 72 while pos != -1 73 let idx = stridx('(){}[]', line[pos]) 74 if idx % 2 == 0 75 let open_{idx} = open_{idx} + 1 76 else 77 let open_{idx - 1} = open_{idx - 1} - 1 78 endif 79 let pos = match(line, '[][(){}]', pos + 1) 80 endwhile 81 return (open_0 > 0) . (open_2 > 0) . (open_4 > 0) 82 endfunction 83 84 function s:Match(lnum, regex) 85 let col = match(getline(a:lnum), a:regex) + 1 86 return col > 0 && !s:IsInString(a:lnum, col) ? col : 0 87 endfunction 88 89 " 3. GetJSONIndent Function {{{1 90 " ========================= 91 92 function GetJSONIndent(...) 93 " 3.1. Setup {{{2 94 " ---------- 95 " For the current line, use the first argument if given, else v:lnum 96 let clnum = a:0 ? a:1 : v:lnum 97 98 " Set up variables for restoring position in file. Could use clnum here. 99 let vcol = col('.') 100 101 " 3.2. Work on the current line {{{2 102 " ----------------------------- 103 104 " Get the current line. 105 let line = getline(clnum) 106 let ind = -1 107 108 " If we got a closing bracket on an empty line, find its match and indent 109 " according to it. 110 let col = matchend(line, '^\s*[]}]') 111 112 if col > 0 && !s:IsInString(clnum, col) 113 call cursor(clnum, col) 114 let bs = strpart('{}[]', stridx('}]', line[col - 1]) * 2, 2) 115 116 let pairstart = escape(bs[0], '[') 117 let pairend = escape(bs[1], ']') 118 let pairline = searchpair(pairstart, '', pairend, 'bW') 119 120 if pairline > 0 121 let ind = indent(pairline) 122 else 123 let ind = virtcol('.') - 1 124 endif 125 126 return ind 127 endif 128 129 " If we are in a multi-line string, don't do anything to it. 130 if s:IsInString(clnum, matchend(line, '^\s*') + 1) 131 return indent('.') 132 endif 133 134 " 3.3. Work on the previous line. {{{2 135 " ------------------------------- 136 137 let lnum = prevnonblank(clnum - 1) 138 139 if lnum == 0 140 return 0 141 endif 142 143 " Set up variables for current line. 144 let line = getline(lnum) 145 let ind = indent(lnum) 146 147 " If the previous line ended with a block opening, add a level of indent. 148 " if s:Match(lnum, s:block_regex) 149 " return indent(lnum) + shiftwidth() 150 " endif 151 152 " If the previous line contained an opening bracket, and we are still in it, 153 " add indent depending on the bracket type. 154 if line =~ '[[({]' 155 let counts = s:LineHasOpeningBrackets(lnum) 156 if counts[0] == '1' || counts[1] == '1' || counts[2] == '1' 157 return ind + shiftwidth() 158 else 159 call cursor(clnum, vcol) 160 end 161 endif 162 163 " }}}2 164 165 return ind 166 endfunction 167 168 " }}}1 169 170 let &cpo = s:cpo_save 171 unlet s:cpo_save 172 173 " vim:set sw=2 sts=2 ts=8 noet: