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