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