blob: 09c7d7a85a6aab9c49e992e4c00a03d9eac7356f [file] [log] [blame]
Bram Moolenaar07d87792014-07-19 14:04:47 +02001" Vim indent file
2" Language: JSON
Bram Moolenaar6c391a72021-09-09 21:55:11 +02003" Maintainer: Eli Parra <eli@elzr.com> https://github.com/elzr/vim-json
Bram Moolenaar1c6737b2020-09-07 22:18:52 +02004" Last Change: 2020 Aug 30
Bram Moolenaar34401cc2014-08-29 15:12:19 +02005" 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
Bram Moolenaar07d87792014-07-19 14:04:47 +02009
Bram Moolenaar34401cc2014-08-29 15:12:19 +020010" 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.
Bram Moolenaar1c6737b2020-09-07 22:18:52 +020022setlocal indentexpr=GetJSONIndent(v:lnum)
Bram Moolenaar34401cc2014-08-29 15:12:19 +020023setlocal indentkeys=0{,0},0),0[,0],!^F,o,O,e
24
25" Only define the function once.
26if exists("*GetJSONIndent")
27 finish
Bram Moolenaar07d87792014-07-19 14:04:47 +020028endif
29
Bram Moolenaar34401cc2014-08-29 15:12:19 +020030let s:cpo_save = &cpo
31set cpo&vim
Bram Moolenaar07d87792014-07-19 14:04:47 +020032
Bram Moolenaar34401cc2014-08-29 15:12:19 +020033" 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
Bram Moolenaar1c6737b2020-09-07 22:18:52 +020089function GetJSONIndent(...)
Bram Moolenaar34401cc2014-08-29 15:12:19 +020090 " 3.1. Setup {{{2
91 " ----------
Bram Moolenaar1c6737b2020-09-07 22:18:52 +020092 " For the current line, use the first argument if given, else v:lnum
93 let clnum = a:0 ? a:1 : v:lnum
Bram Moolenaar34401cc2014-08-29 15:12:19 +020094
Bram Moolenaar1c6737b2020-09-07 22:18:52 +020095 " Set up variables for restoring position in file. Could use clnum here.
Bram Moolenaar34401cc2014-08-29 15:12:19 +020096 let vcol = col('.')
97
98 " 3.2. Work on the current line {{{2
99 " -----------------------------
100
101 " Get the current line.
Bram Moolenaar1c6737b2020-09-07 22:18:52 +0200102 let line = getline(clnum)
Bram Moolenaar34401cc2014-08-29 15:12:19 +0200103 let ind = -1
104
105 " If we got a closing bracket on an empty line, find its match and indent
106 " according to it.
107 let col = matchend(line, '^\s*[]}]')
108
Bram Moolenaar1c6737b2020-09-07 22:18:52 +0200109 if col > 0 && !s:IsInString(clnum, col)
110 call cursor(clnum, col)
Bram Moolenaar34401cc2014-08-29 15:12:19 +0200111 let bs = strpart('{}[]', stridx('}]', line[col - 1]) * 2, 2)
112
113 let pairstart = escape(bs[0], '[')
114 let pairend = escape(bs[1], ']')
115 let pairline = searchpair(pairstart, '', pairend, 'bW')
116
117 if pairline > 0
118 let ind = indent(pairline)
119 else
120 let ind = virtcol('.') - 1
121 endif
122
123 return ind
124 endif
125
126 " If we are in a multi-line string, don't do anything to it.
Bram Moolenaar1c6737b2020-09-07 22:18:52 +0200127 if s:IsInString(clnum, matchend(line, '^\s*') + 1)
Bram Moolenaar34401cc2014-08-29 15:12:19 +0200128 return indent('.')
129 endif
130
131 " 3.3. Work on the previous line. {{{2
132 " -------------------------------
133
Bram Moolenaar1c6737b2020-09-07 22:18:52 +0200134 let lnum = prevnonblank(clnum - 1)
Bram Moolenaar34401cc2014-08-29 15:12:19 +0200135
136 if lnum == 0
137 return 0
138 endif
139
140 " Set up variables for current line.
141 let line = getline(lnum)
142 let ind = indent(lnum)
143
144 " If the previous line ended with a block opening, add a level of indent.
145 " if s:Match(lnum, s:block_regex)
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200146 " return indent(lnum) + shiftwidth()
Bram Moolenaar34401cc2014-08-29 15:12:19 +0200147 " endif
148
149 " If the previous line contained an opening bracket, and we are still in it,
150 " add indent depending on the bracket type.
151 if line =~ '[[({]'
152 let counts = s:LineHasOpeningBrackets(lnum)
153 if counts[0] == '1' || counts[1] == '1' || counts[2] == '1'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200154 return ind + shiftwidth()
Bram Moolenaar34401cc2014-08-29 15:12:19 +0200155 else
Bram Moolenaar1c6737b2020-09-07 22:18:52 +0200156 call cursor(clnum, vcol)
Bram Moolenaar34401cc2014-08-29 15:12:19 +0200157 end
158 endif
159
160 " }}}2
161
162 return ind
163endfunction
164
165" }}}1
166
167let &cpo = s:cpo_save
168unlet s:cpo_save
169
170" vim:set sw=2 sts=2 ts=8 noet: