forked from pangloss/vim-javascript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjavascript.vim
More file actions
382 lines (327 loc) · 12.1 KB
/
Copy pathjavascript.vim
File metadata and controls
382 lines (327 loc) · 12.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
" Vim indent file
" Language: Javascript
" Maintainer: Darrick Wiebe <darrick at innatesoftware.com>
" URL: http://github.com/pangloss/vim-javascript
" Version: 1.0.0
" Last Change: August 31, 2009
" Acknowledgement: Based off of vim-ruby maintained by Nikolai Weibull http://vim-ruby.rubyforge.org
" 0. Initialization {{{1
" =================
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
finish
endif
let b:did_indent = 1
setlocal nosmartindent
" Now, set up our indentation expression and keys that trigger it.
setlocal indentexpr=GetJavascriptIndent()
setlocal indentkeys=0{,0},0),0],!^F,o,O,e
" Only define the function once.
if exists("*GetJavascriptIndent")
finish
endif
let s:cpo_save = &cpo
set cpo&vim
" 1. Variables {{{1
" ============
" Regex of syntax group names that are or delimit string or are comments.
" TODO: these need to be changed to the values used by the syntax highligter
let s:syng_strcom = '\<javascript\%(String\|StringEscape\|ASCIICode\|Interpolation\|NoInterpolation\|Comment\|Documentation\)\>'
" Regex of syntax group names that are strings.
let s:syng_string =
\ '\<javascript\%(String\|Interpolation\|NoInterpolation\|StringEscape\)\>'
" Regex of syntax group names that are strings or documentation.
let s:syng_stringdoc =
\'\<javascript\%(String\|Interpolation\|NoInterpolation\|StringEscape\|Documentation\)\>'
" Expression used to check whether we should skip a match with searchpair().
let s:skip_expr = "synIDattr(synID(line('.'),col('.'),1),'name') =~ '".s:syng_strcom."'"
let s:line_term = '\s*\%(\%(\/\/\).*\)\=$'
" Regex that defines continuation lines, not including (, {, or [.
let s:continuation_regex = '\%([\\*+/.:]\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\)' . s:line_term
" Regex that defines continuation lines.
" TODO: this needs to deal with if ...: and so on
let s:msl_regex = '\%([\\*+/.:([]\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\)' . s:line_term
let s:one_line_scope_regex = '\<\%(if\|else\|for\|while\)\>[^{]*' . s:line_term
" Regex that defines blocks.
let s:block_regex = '\%({\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s:line_term
" 2. Auxiliary Functions {{{1
" ======================
" Check if the character at lnum:col is inside a string, comment, or is ascii.
function s:IsInStringOrComment(lnum, col)
return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_strcom
endfunction
" Check if the character at lnum:col is inside a string.
function s:IsInString(lnum, col)
return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_string
endfunction
" Check if the character at lnum:col is inside a string or documentation.
function s:IsInStringOrDocumentation(lnum, col)
return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_stringdoc
endfunction
" Find line above 'lnum' that isn't empty, in a comment, or in a string.
function s:PrevNonBlankNonString(lnum)
let in_block = 0
let lnum = prevnonblank(a:lnum)
while lnum > 0
" Go in and out of blocks comments as necessary.
" If the line isn't empty (with opt. comment) or in a string, end search.
let line = getline(lnum)
if line =~ '/\*'
if in_block
let in_block = 0
else
break
endif
elseif !in_block && line =~ '\*/'
let in_block = 1
elseif !in_block && line !~ '^\s*\%(//\).*$' && !(s:IsInStringOrComment(lnum, 1) && s:IsInStringOrComment(lnum, strlen(line)))
break
endif
let lnum = prevnonblank(lnum - 1)
endwhile
return lnum
endfunction
" Find line above 'lnum' that started the continuation 'lnum' may be part of.
function s:GetMSL(lnum, in_one_line_scope)
" Start on the line we're at and use its indent.
let msl = a:lnum
let lnum = s:PrevNonBlankNonString(a:lnum - 1)
while lnum > 0
" If we have a continuation line, or we're in a string, use line as MSL.
" Otherwise, terminate search as we have found our MSL already.
let line = getline(lnum)
let col = match(line, s:msl_regex) + 1
"echom 'back to' lnum 'from' msl ':' getline(lnum)
if (col > 0 && !s:IsInStringOrComment(lnum, col)) || s:IsInString(lnum, strlen(line))
"echom ' yes, msl is' lnum
let msl = lnum
else
" Don't use lines that are part of a one line scope as msl unless the
" flag in_one_line_scope is set to 1
"
if a:in_one_line_scope
"echom ' **** don''t check for one line scope'
break
end
let msl_one_line = s:Match(lnum, s:one_line_scope_regex)
"echom ' **** one line scope regex' msl_one_line
if msl_one_line == 0
"echom ' **** NOT one line scope'
break
else
"echom ' **** skip one line scoped msl'
endif
endif
let lnum = s:PrevNonBlankNonString(lnum - 1)
endwhile
return msl
endfunction
" Check if line 'lnum' has more opening brackets than closing ones.
function s:LineHasOpeningBrackets(lnum)
let open_0 = 0
let open_2 = 0
let open_4 = 0
let line = getline(a:lnum)
let pos = match(line, '[][(){}]', 0)
while pos != -1
if !s:IsInStringOrComment(a:lnum, pos + 1)
let idx = stridx('(){}[]', line[pos])
if idx % 2 == 0
let open_{idx} = open_{idx} + 1
else
let open_{idx - 1} = open_{idx - 1} - 1
endif
endif
let pos = match(line, '[][(){}]', pos + 1)
endwhile
"echom ' line has opening brackets' (open_0 > 0) . (open_2 > 0) . (open_4 > 0)
return (open_0 > 0) . (open_2 > 0) . (open_4 > 0)
endfunction
function s:Match(lnum, regex)
let col = match(getline(a:lnum), a:regex) + 1
return col > 0 && !s:IsInStringOrComment(a:lnum, col) ? col : 0
endfunction
function s:IndentWithContinuation(lnum, ind, width)
" Set up variables to use and search for MSL to the previous line.
"echom ' in indent with continuation with' a:lnum a:ind a:width
let p_lnum = a:lnum
let lnum = s:GetMSL(a:lnum, 1)
let line = getline(line)
" If the previous line wasn't a MSL and is continuation return its indent.
" TODO: the || s:IsInString() thing worries me a bit.
if p_lnum != lnum
if s:Match(p_lnum,s:continuation_regex)||s:IsInString(p_lnum,strlen(line))
"echom ' no prev MSL but is continuation' a:ind + a:width
return a:ind + a:width
endif
endif
" Set up more variables now that we know we aren't continuation bound.
let msl_ind = indent(lnum)
"echom getline(lnum)
" If the previous line ended with [*+/.-=], start a continuation that
" indents an extra level.
if s:Match(lnum, s:continuation_regex)
"echom ' matched.'
if lnum == p_lnum
"echom ' extra level a' msl_ind + a:width
return msl_ind + a:width
else
"echom ' no extra level b' msl_ind
return msl_ind
endif
endif
"echom ' returning ind unchanged' a:ind
return a:ind
endfunction
function s:InOneLineScope(lnum)
echom ' #### get in ols'
let msl = s:GetMSL(a:lnum, 1)
echom ' #### get ols for' a:lnum 'at' msl
echom ' ' getline(msl)
if msl > 0 && s:Match(msl, s:one_line_scope_regex)
echom 'the current line is in a one line scope'
return msl
endif
echom ' #### not in ols'
return 0
endfunction
function s:ExitingOneLineScope(lnum)
echom ' #### get exiting ols'
let msl = s:GetMSL(a:lnum, 1)
echom ' #### get ols for' a:lnum 'at' msl
echom ' ' getline(msl)
if msl > 0
" if the current line is in a one line scope ..
if s:Match(msl, s:one_line_scope_regex)
echom 'the current line is in a one line scope'
return 0
else
let prev_msl = s:GetMSL(msl - 1, 1)
echom ' #### get ols start for' a:lnum 'at' prev_msl getline(prev_msl)
if s:Match(prev_msl, s:one_line_scope_regex)
echom ' #### found ols start' prev_msl
return prev_msl
endif
endif
endif
echom ' #### no ols'
return 0
endfunction
" 3. GetJavascriptIndent Function {{{1
" =========================
function GetJavascriptIndent()
echom v:lnum '*********************************************************'
" 3.1. Setup {{{2
" ----------
" Set up variables for restoring position in file. Could use v:lnum here.
let vcol = col('.')
" 3.2. Work on the current line {{{2
" -----------------------------
" Get the current line.
let line = getline(v:lnum)
let ind = -1
echom line
" If we got a closing bracket on an empty line, find its match and indent
" according to it. For parentheses we indent to its column - 1, for the
" others we indent to the containing line's MSL's level. Return -1 if fail.
let col = matchend(line, '^\s*[]})]')
if col > 0 && !s:IsInStringOrComment(v:lnum, col)
call cursor(v:lnum, col)
let bs = strpart('(){}[]', stridx(')}]', line[col - 1]) * 2, 2)
if searchpair(escape(bs[0], '\['), '', bs[1], 'bW', s:skip_expr) > 0
if line[col-1]==')' && col('.') != col('$') - 1
"echom ' closing bracket a'
let ind = virtcol('.')-1
else
"echom ' closing bracket b'
let ind = indent(s:GetMSL(line('.'), 0))
endif
endif
"echom 'result' ind
return ind
endif
" If we have a /* or */ set indent to first column.
if match(line, '^\s*\%(/\*\|\*/\)$') != -1
"echom ' in /* or */ '
"echom 'result 0'
return 0
endif
" If we are in a multi-line string or line-comment, don't do anything to it.
if s:IsInStringOrDocumentation(v:lnum, matchend(line, '^\s*') + 1)
"echom ' in string or documentation'
return indent('.')
endif
" 3.3. Work on the previous line. {{{2
" -------------------------------
" Find a non-blank, non-multi-line string line above the current line.
let lnum = s:PrevNonBlankNonString(v:lnum - 1)
"echom ' prev non blank string:' lnum
" If the line is empty and inside a string, use the previous line.
if line =~ '^\s*$' && lnum != prevnonblank(v:lnum - 1)
return indent(prevnonblank(v:lnum))
endif
" At the start of the file use zero indent.
if lnum == 0
return 0
endif
" Set up variables for current line.
let line = getline(lnum)
let ind = indent(lnum)
" If the previous line ended with a block opening, add a level of indent.
if s:Match(lnum, s:block_regex)
"echom ' previous line had block opening'
return indent(s:GetMSL(lnum, 0)) + &sw
endif
" If the previous line contained an opening bracket, and we are still in it,
" add indent depending on the bracket type.
if line =~ '[[({]'
let counts = s:LineHasOpeningBrackets(lnum)
if counts[0] == '1' && searchpair('(', '', ')', 'bW', s:skip_expr) > 0
if col('.') + 1 == col('$')
"echom ' previous line had opening bracket a'
"echom 'result' ind + &sw
return ind + &sw
else
"echom ' previous line had opening bracket b'
"echom 'result' virtcol('.')
return virtcol('.')
endif
elseif counts[1] == '1' || counts[2] == '1'
"echom ' previous line had opening bracket c'
"echom 'result' ind + &sw
return ind + &sw
else
"echom ' previous line had balanced brackets (call cursor method)'
call cursor(v:lnum, vcol)
end
endif
" 3.4. Work on the MSL line. {{{2
" --------------------------
"echom " ind before continuation" ind
let ind_con = ind
let ind = s:IndentWithContinuation(lnum, ind_con, &sw)
"echom " ind after continuation" ind
" }}}2
"
"
echom 'at ols'
let ols = s:InOneLineScope(lnum)
if ols > 0
let ind = ind + &sw
echom 'ols + 1' ind
else
let ols = s:ExitingOneLineScope(lnum)
while ols > 0 && ind > 0
let ind = ind - &sw
echom 'ols - 1' ind
let ols = s:InOneLineScope(ols - 1)
echom 'new ols' ols
endwhile
endif
echom 'result at end' ind
return ind
endfunction
" }}}1
let &cpo = s:cpo_save
unlet s:cpo_save
" vim:set sw=2 sts=2 ts=8 noet: