Mercurial > vim
comparison runtime/indent/solidity.vim @ 30547:1e91e26ceebf
Update runtime files
Commit: https://github.com/vim/vim/commit/9fbdbb814f4ad67a14979aba4a6a49800c2f1a99
Author: Bram Moolenaar <Bram@vim.org>
Date: Tue Sep 27 17:30:34 2022 +0100
Update runtime files
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Tue, 27 Sep 2022 18:45:05 +0200 |
parents | |
children | 32c896b7e001 |
comparison
equal
deleted
inserted
replaced
30546:3107cc41afc2 | 30547:1e91e26ceebf |
---|---|
1 " Vim indent file | |
2 " Language: Solidity | |
3 " Acknowledgement: Based off of vim-javascript | |
4 " Maintainer: Cothi (jiungdev@gmail.com) | |
5 " Original Author: tomlion (https://github.com/tomlion/vim-solidity) | |
6 " Last Changed: 2022 Sep 27 | |
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=GetSolidityIndent() | |
21 setlocal indentkeys=0{,0},0),0],0\,,!^F,o,O,e | |
22 | |
23 " Only define the function once. | |
24 if exists("*GetSolidityIndent") | |
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:js_keywords = '^\s*\(break\|case\|catch\|continue\|debugger\|default\|delete\|do\|else\|finally\|for\|function\|if\|in\|instanceof\|new\|return\|switch\|this\|throw\|try\|typeof\|var\|void\|while\|with\)' | |
35 | |
36 " Regex of syntax group names that are or delimit string or are comments. | |
37 let s:syng_strcom = 'string\|regex\|comment\c' | |
38 | |
39 " Regex of syntax group names that are strings. | |
40 let s:syng_string = 'regex\c' | |
41 | |
42 " Regex of syntax group names that are strings or documentation. | |
43 let s:syng_multiline = 'comment\c' | |
44 | |
45 " Regex of syntax group names that are line comment. | |
46 let s:syng_linecom = 'linecomment\c' | |
47 | |
48 " Expression used to check whether we should skip a match with searchpair(). | |
49 let s:skip_expr = "synIDattr(synID(line('.'),col('.'),1),'name') =~ '".s:syng_strcom."'" | |
50 | |
51 let s:line_term = '\s*\%(\%(\/\/\).*\)\=$' | |
52 | |
53 " Regex that defines continuation lines, not including (, {, or [. | |
54 let s:continuation_regex = '\%([\\*+/.:]\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\)' . s:line_term | |
55 | |
56 " Regex that defines continuation lines. | |
57 " TODO: this needs to deal with if ...: and so on | |
58 let s:msl_regex = '\%([\\*+/.:([]\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\)' . s:line_term | |
59 | |
60 let s:one_line_scope_regex = '\<\%(if\|else\|for\|while\)\>[^{;]*' . s:line_term | |
61 | |
62 " Regex that defines blocks. | |
63 let s:block_regex = '\%([{[]\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s:line_term | |
64 | |
65 let s:var_stmt = '^\s*var' | |
66 | |
67 let s:comma_first = '^\s*,' | |
68 let s:comma_last = ',\s*$' | |
69 | |
70 let s:ternary = '^\s\+[?|:]' | |
71 let s:ternary_q = '^\s\+?' | |
72 | |
73 " 2. Auxiliary Functions {{{1 | |
74 " ====================== | |
75 | |
76 " Check if the character at lnum:col is inside a string, comment, or is ascii. | |
77 function s:IsInStringOrComment(lnum, col) | |
78 return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_strcom | |
79 endfunction | |
80 | |
81 " Check if the character at lnum:col is inside a string. | |
82 function s:IsInString(lnum, col) | |
83 return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_string | |
84 endfunction | |
85 | |
86 " Check if the character at lnum:col is inside a multi-line comment. | |
87 function s:IsInMultilineComment(lnum, col) | |
88 return !s:IsLineComment(a:lnum, a:col) && synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_multiline | |
89 endfunction | |
90 | |
91 " Check if the character at lnum:col is a line comment. | |
92 function s:IsLineComment(lnum, col) | |
93 return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_linecom | |
94 endfunction | |
95 | |
96 " Find line above 'lnum' that isn't empty, in a comment, or in a string. | |
97 function s:PrevNonBlankNonString(lnum) | |
98 let in_block = 0 | |
99 let lnum = prevnonblank(a:lnum) | |
100 while lnum > 0 | |
101 " Go in and out of blocks comments as necessary. | |
102 " If the line isn't empty (with opt. comment) or in a string, end search. | |
103 let line = getline(lnum) | |
104 if line =~ '/\*' | |
105 if in_block | |
106 let in_block = 0 | |
107 else | |
108 break | |
109 endif | |
110 elseif !in_block && line =~ '\*/' | |
111 let in_block = 1 | |
112 elseif !in_block && line !~ '^\s*\%(//\).*$' && !(s:IsInStringOrComment(lnum, 1) && s:IsInStringOrComment(lnum, strlen(line))) | |
113 break | |
114 endif | |
115 let lnum = prevnonblank(lnum - 1) | |
116 endwhile | |
117 return lnum | |
118 endfunction | |
119 | |
120 " Find line above 'lnum' that started the continuation 'lnum' may be part of. | |
121 function s:GetMSL(lnum, in_one_line_scope) | |
122 " Start on the line we're at and use its indent. | |
123 let msl = a:lnum | |
124 let lnum = s:PrevNonBlankNonString(a:lnum - 1) | |
125 while lnum > 0 | |
126 " If we have a continuation line, or we're in a string, use line as MSL. | |
127 " Otherwise, terminate search as we have found our MSL already. | |
128 let line = getline(lnum) | |
129 let col = match(line, s:msl_regex) + 1 | |
130 if (col > 0 && !s:IsInStringOrComment(lnum, col)) || s:IsInString(lnum, strlen(line)) | |
131 let msl = lnum | |
132 else | |
133 " Don't use lines that are part of a one line scope as msl unless the | |
134 " flag in_one_line_scope is set to 1 | |
135 " | |
136 if a:in_one_line_scope | |
137 break | |
138 end | |
139 let msl_one_line = s:Match(lnum, s:one_line_scope_regex) | |
140 if msl_one_line == 0 | |
141 break | |
142 endif | |
143 endif | |
144 let lnum = s:PrevNonBlankNonString(lnum - 1) | |
145 endwhile | |
146 return msl | |
147 endfunction | |
148 | |
149 function s:RemoveTrailingComments(content) | |
150 let single = '\/\/\(.*\)\s*$' | |
151 let multi = '\/\*\(.*\)\*\/\s*$' | |
152 return substitute(substitute(a:content, single, '', ''), multi, '', '') | |
153 endfunction | |
154 | |
155 " Find if the string is inside var statement (but not the first string) | |
156 function s:InMultiVarStatement(lnum) | |
157 let lnum = s:PrevNonBlankNonString(a:lnum - 1) | |
158 | |
159 " let type = synIDattr(synID(lnum, indent(lnum) + 1, 0), 'name') | |
160 | |
161 " loop through previous expressions to find a var statement | |
162 while lnum > 0 | |
163 let line = getline(lnum) | |
164 | |
165 " if the line is a js keyword | |
166 if (line =~ s:js_keywords) | |
167 " check if the line is a var stmt | |
168 " if the line has a comma first or comma last then we can assume that we | |
169 " are in a multiple var statement | |
170 if (line =~ s:var_stmt) | |
171 return lnum | |
172 endif | |
173 | |
174 " other js keywords, not a var | |
175 return 0 | |
176 endif | |
177 | |
178 let lnum = s:PrevNonBlankNonString(lnum - 1) | |
179 endwhile | |
180 | |
181 " beginning of program, not a var | |
182 return 0 | |
183 endfunction | |
184 | |
185 " Find line above with beginning of the var statement or returns 0 if it's not | |
186 " this statement | |
187 function s:GetVarIndent(lnum) | |
188 let lvar = s:InMultiVarStatement(a:lnum) | |
189 let prev_lnum = s:PrevNonBlankNonString(a:lnum - 1) | |
190 | |
191 if lvar | |
192 let line = s:RemoveTrailingComments(getline(prev_lnum)) | |
193 | |
194 " if the previous line doesn't end in a comma, return to regular indent | |
195 if (line !~ s:comma_last) | |
196 return indent(prev_lnum) - &sw | |
197 else | |
198 return indent(lvar) + &sw | |
199 endif | |
200 endif | |
201 | |
202 return -1 | |
203 endfunction | |
204 | |
205 | |
206 " Check if line 'lnum' has more opening brackets than closing ones. | |
207 function s:LineHasOpeningBrackets(lnum) | |
208 let open_0 = 0 | |
209 let open_2 = 0 | |
210 let open_4 = 0 | |
211 let line = getline(a:lnum) | |
212 let pos = match(line, '[][(){}]', 0) | |
213 while pos != -1 | |
214 if !s:IsInStringOrComment(a:lnum, pos + 1) | |
215 let idx = stridx('(){}[]', line[pos]) | |
216 if idx % 2 == 0 | |
217 let open_{idx} = open_{idx} + 1 | |
218 else | |
219 let open_{idx - 1} = open_{idx - 1} - 1 | |
220 endif | |
221 endif | |
222 let pos = match(line, '[][(){}]', pos + 1) | |
223 endwhile | |
224 return (open_0 > 0) . (open_2 > 0) . (open_4 > 0) | |
225 endfunction | |
226 | |
227 function s:Match(lnum, regex) | |
228 let col = match(getline(a:lnum), a:regex) + 1 | |
229 return col > 0 && !s:IsInStringOrComment(a:lnum, col) ? col : 0 | |
230 endfunction | |
231 | |
232 function s:IndentWithContinuation(lnum, ind, width) | |
233 " Set up variables to use and search for MSL to the previous line. | |
234 let p_lnum = a:lnum | |
235 let lnum = s:GetMSL(a:lnum, 1) | |
236 let line = getline(lnum) | |
237 | |
238 " If the previous line wasn't a MSL and is continuation return its indent. | |
239 " TODO: the || s:IsInString() thing worries me a bit. | |
240 if p_lnum != lnum | |
241 if s:Match(p_lnum,s:continuation_regex)||s:IsInString(p_lnum,strlen(line)) | |
242 return a:ind | |
243 endif | |
244 endif | |
245 | |
246 " Set up more variables now that we know we aren't continuation bound. | |
247 let msl_ind = indent(lnum) | |
248 | |
249 " If the previous line ended with [*+/.-=], start a continuation that | |
250 " indents an extra level. | |
251 if s:Match(lnum, s:continuation_regex) | |
252 if lnum == p_lnum | |
253 return msl_ind + a:width | |
254 else | |
255 return msl_ind | |
256 endif | |
257 endif | |
258 | |
259 return a:ind | |
260 endfunction | |
261 | |
262 function s:InOneLineScope(lnum) | |
263 let msl = s:GetMSL(a:lnum, 1) | |
264 if msl > 0 && s:Match(msl, s:one_line_scope_regex) | |
265 return msl | |
266 endif | |
267 return 0 | |
268 endfunction | |
269 | |
270 function s:ExitingOneLineScope(lnum) | |
271 let msl = s:GetMSL(a:lnum, 1) | |
272 if msl > 0 | |
273 " if the current line is in a one line scope .. | |
274 if s:Match(msl, s:one_line_scope_regex) | |
275 return 0 | |
276 else | |
277 let prev_msl = s:GetMSL(msl - 1, 1) | |
278 if s:Match(prev_msl, s:one_line_scope_regex) | |
279 return prev_msl | |
280 endif | |
281 endif | |
282 endif | |
283 return 0 | |
284 endfunction | |
285 | |
286 " 3. GetSolidityIndent Function {{{1 | |
287 " ========================= | |
288 | |
289 function GetSolidityIndent() | |
290 " 3.1. Setup {{{2 | |
291 " ---------- | |
292 | |
293 " Set up variables for restoring position in file. Could use v:lnum here. | |
294 let vcol = col('.') | |
295 | |
296 " 3.2. Work on the current line {{{2 | |
297 " ----------------------------- | |
298 | |
299 let ind = -1 | |
300 " Get the current line. | |
301 let line = getline(v:lnum) | |
302 " previous nonblank line number | |
303 let prevline = prevnonblank(v:lnum - 1) | |
304 | |
305 " If we got a closing bracket on an empty line, find its match and indent | |
306 " according to it. For parentheses we indent to its column - 1, for the | |
307 " others we indent to the containing line's MSL's level. Return -1 if fail. | |
308 let col = matchend(line, '^\s*[],})]') | |
309 if col > 0 && !s:IsInStringOrComment(v:lnum, col) | |
310 call cursor(v:lnum, col) | |
311 | |
312 let lvar = s:InMultiVarStatement(v:lnum) | |
313 if lvar | |
314 let prevline_contents = s:RemoveTrailingComments(getline(prevline)) | |
315 | |
316 " check for comma first | |
317 if (line[col - 1] =~ ',') | |
318 " if the previous line ends in comma or semicolon don't indent | |
319 if (prevline_contents =~ '[;,]\s*$') | |
320 return indent(s:GetMSL(line('.'), 0)) | |
321 " get previous line indent, if it's comma first return prevline indent | |
322 elseif (prevline_contents =~ s:comma_first) | |
323 return indent(prevline) | |
324 " otherwise we indent 1 level | |
325 else | |
326 return indent(lvar) + &sw | |
327 endif | |
328 endif | |
329 endif | |
330 | |
331 | |
332 let bs = strpart('(){}[]', stridx(')}]', line[col - 1]) * 2, 2) | |
333 if searchpair(escape(bs[0], '\['), '', bs[1], 'bW', s:skip_expr) > 0 | |
334 if line[col-1]==')' && col('.') != col('$') - 1 | |
335 let ind = virtcol('.')-1 | |
336 else | |
337 let ind = indent(s:GetMSL(line('.'), 0)) | |
338 endif | |
339 endif | |
340 return ind | |
341 endif | |
342 | |
343 " If the line is comma first, dedent 1 level | |
344 if (getline(prevline) =~ s:comma_first) | |
345 return indent(prevline) - &sw | |
346 endif | |
347 | |
348 if (line =~ s:ternary) | |
349 if (getline(prevline) =~ s:ternary_q) | |
350 return indent(prevline) | |
351 else | |
352 return indent(prevline) + &sw | |
353 endif | |
354 endif | |
355 | |
356 " If we are in a multi-line comment, cindent does the right thing. | |
357 if s:IsInMultilineComment(v:lnum, 1) && !s:IsLineComment(v:lnum, 1) | |
358 return cindent(v:lnum) | |
359 endif | |
360 | |
361 " Check for multiple var assignments | |
362 " let var_indent = s:GetVarIndent(v:lnum) | |
363 " if var_indent >= 0 | |
364 " return var_indent | |
365 " endif | |
366 | |
367 " 3.3. Work on the previous line. {{{2 | |
368 " ------------------------------- | |
369 | |
370 " If the line is empty and the previous nonblank line was a multi-line | |
371 " comment, use that comment's indent. Deduct one char to account for the | |
372 " space in ' */'. | |
373 if line =~ '^\s*$' && s:IsInMultilineComment(prevline, 1) | |
374 return indent(prevline) - 1 | |
375 endif | |
376 | |
377 " Find a non-blank, non-multi-line string line above the current line. | |
378 let lnum = s:PrevNonBlankNonString(v:lnum - 1) | |
379 | |
380 " If the line is empty and inside a string, use the previous line. | |
381 if line =~ '^\s*$' && lnum != prevline | |
382 return indent(prevnonblank(v:lnum)) | |
383 endif | |
384 | |
385 " At the start of the file use zero indent. | |
386 if lnum == 0 | |
387 return 0 | |
388 endif | |
389 | |
390 " Set up variables for current line. | |
391 let line = getline(lnum) | |
392 let ind = indent(lnum) | |
393 | |
394 " If the previous line ended with a block opening, add a level of indent. | |
395 if s:Match(lnum, s:block_regex) | |
396 return indent(s:GetMSL(lnum, 0)) + &sw | |
397 endif | |
398 | |
399 " If the previous line contained an opening bracket, and we are still in it, | |
400 " add indent depending on the bracket type. | |
401 if line =~ '[[({]' | |
402 let counts = s:LineHasOpeningBrackets(lnum) | |
403 if counts[0] == '1' && searchpair('(', '', ')', 'bW', s:skip_expr) > 0 | |
404 if col('.') + 1 == col('$') | |
405 return ind + &sw | |
406 else | |
407 return virtcol('.') | |
408 endif | |
409 elseif counts[1] == '1' || counts[2] == '1' | |
410 return ind + &sw | |
411 else | |
412 call cursor(v:lnum, vcol) | |
413 end | |
414 endif | |
415 | |
416 " 3.4. Work on the MSL line. {{{2 | |
417 " -------------------------- | |
418 | |
419 let ind_con = ind | |
420 let ind = s:IndentWithContinuation(lnum, ind_con, &sw) | |
421 | |
422 " }}}2 | |
423 " | |
424 " | |
425 let ols = s:InOneLineScope(lnum) | |
426 if ols > 0 | |
427 let ind = ind + &sw | |
428 else | |
429 let ols = s:ExitingOneLineScope(lnum) | |
430 while ols > 0 && ind > 0 | |
431 let ind = ind - &sw | |
432 let ols = s:InOneLineScope(ols - 1) | |
433 endwhile | |
434 endif | |
435 | |
436 return ind | |
437 endfunction | |
438 | |
439 " }}}1 | |
440 | |
441 let &cpo = s:cpo_save | |
442 unlet s:cpo_save |