blob: 510f7e8f426c464562b0d106f6042ba843e20d7c [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
Bram Moolenaar71b6d332022-09-10 13:13:14 +01006" 2022 Sep 07: b:undo_indent added by Doug Kearns
Bram Moolenaar34401cc2014-08-29 15:12:19 +02007" 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
Bram Moolenaar07d87792014-07-19 14:04:47 +020010
Bram Moolenaar34401cc2014-08-29 15:12:19 +020011" 0. Initialization {{{1
12" =================
13
14" Only load this indent file when no other was loaded.
15if exists("b:did_indent")
16 finish
17endif
18let b:did_indent = 1
19
20setlocal nosmartindent
21
22" Now, set up our indentation expression and keys that trigger it.
Bram Moolenaar1c6737b2020-09-07 22:18:52 +020023setlocal indentexpr=GetJSONIndent(v:lnum)
Bram Moolenaar34401cc2014-08-29 15:12:19 +020024setlocal indentkeys=0{,0},0),0[,0],!^F,o,O,e
25
Bram Moolenaar71b6d332022-09-10 13:13:14 +010026let b:undo_indent = "setl inde< indk< si<"
27
Bram Moolenaar34401cc2014-08-29 15:12:19 +020028" Only define the function once.
29if exists("*GetJSONIndent")
30 finish
Bram Moolenaar07d87792014-07-19 14:04:47 +020031endif
32
Bram Moolenaar34401cc2014-08-29 15:12:19 +020033let s:cpo_save = &cpo
34set cpo&vim
Bram Moolenaar07d87792014-07-19 14:04:47 +020035
Bram Moolenaar34401cc2014-08-29 15:12:19 +020036" 1. Variables {{{1
37" ============
38
39let s:line_term = '\s*\%(\%(\/\/\).*\)\=$'
40" Regex that defines blocks.
41let 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.
47function s:IsInString(lnum, col)
48 return synIDattr(synID(a:lnum, a:col, 1), 'name') == 'jsonString'
49endfunction
50
51" Find line above 'lnum' that isn't empty, or in a string.
52function 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
63endfunction
64
65" Check if line 'lnum' has more opening brackets than closing ones.
66function 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)
82endfunction
83
84function 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
87endfunction
88
89" 3. GetJSONIndent Function {{{1
90" =========================
91
Bram Moolenaar1c6737b2020-09-07 22:18:52 +020092function GetJSONIndent(...)
Bram Moolenaar34401cc2014-08-29 15:12:19 +020093 " 3.1. Setup {{{2
94 " ----------
Bram Moolenaar1c6737b2020-09-07 22:18:52 +020095 " For the current line, use the first argument if given, else v:lnum
96 let clnum = a:0 ? a:1 : v:lnum
Bram Moolenaar34401cc2014-08-29 15:12:19 +020097
Bram Moolenaar1c6737b2020-09-07 22:18:52 +020098 " Set up variables for restoring position in file. Could use clnum here.
Bram Moolenaar34401cc2014-08-29 15:12:19 +020099 let vcol = col('.')
100
101 " 3.2. Work on the current line {{{2
102 " -----------------------------
103
104 " Get the current line.
Bram Moolenaar1c6737b2020-09-07 22:18:52 +0200105 let line = getline(clnum)
Bram Moolenaar34401cc2014-08-29 15:12:19 +0200106 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
Bram Moolenaar1c6737b2020-09-07 22:18:52 +0200112 if col > 0 && !s:IsInString(clnum, col)
113 call cursor(clnum, col)
Bram Moolenaar34401cc2014-08-29 15:12:19 +0200114 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.
Bram Moolenaar1c6737b2020-09-07 22:18:52 +0200130 if s:IsInString(clnum, matchend(line, '^\s*') + 1)
Bram Moolenaar34401cc2014-08-29 15:12:19 +0200131 return indent('.')
132 endif
133
134 " 3.3. Work on the previous line. {{{2
135 " -------------------------------
136
Bram Moolenaar1c6737b2020-09-07 22:18:52 +0200137 let lnum = prevnonblank(clnum - 1)
Bram Moolenaar34401cc2014-08-29 15:12:19 +0200138
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)
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200149 " return indent(lnum) + shiftwidth()
Bram Moolenaar34401cc2014-08-29 15:12:19 +0200150 " 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'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200157 return ind + shiftwidth()
Bram Moolenaar34401cc2014-08-29 15:12:19 +0200158 else
Bram Moolenaar1c6737b2020-09-07 22:18:52 +0200159 call cursor(clnum, vcol)
Bram Moolenaar34401cc2014-08-29 15:12:19 +0200160 end
161 endif
162
163 " }}}2
164
165 return ind
166endfunction
167
168" }}}1
169
170let &cpo = s:cpo_save
171unlet s:cpo_save
172
173" vim:set sw=2 sts=2 ts=8 noet: