1" Vim indent file 2" Language: R 3" Author: Jakson Alves de Aquino <[email protected]> 4" Homepage: https://github.com/jalvesaq/R-Vim-runtime 5" Last Change: Sun Aug 19, 2018 09:13PM 6 7 8" Only load this indent file when no other was loaded. 9if exists("b:did_indent") 10 finish 11endif 12let b:did_indent = 1 13 14setlocal indentkeys=0{,0},:,!^F,o,O,e 15setlocal indentexpr=GetRIndent() 16 17" Only define the function once. 18if exists("*GetRIndent") 19 finish 20endif 21 22let s:cpo_save = &cpo 23set cpo&vim 24 25" Options to make the indentation more similar to Emacs/ESS: 26let g:r_indent_align_args = get(g:, 'r_indent_align_args', 1) 27let g:r_indent_ess_comments = get(g:, 'r_indent_ess_comments', 0) 28let g:r_indent_comment_column = get(g:, 'r_indent_comment_column', 40) 29let g:r_indent_ess_compatible = get(g:, 'r_indent_ess_compatible', 0) 30let g:r_indent_op_pattern = get(g:, 'r_indent_op_pattern', 31 \ '\(&\||\|+\|-\|\*\|/\|=\|\~\|%\|->\)\s*$') 32 33function s:RDelete_quotes(line) 34 let i = 0 35 let j = 0 36 let line1 = "" 37 let llen = strlen(a:line) 38 while i < llen 39 if a:line[i] == '"' 40 let i += 1 41 let line1 = line1 . 's' 42 while !(a:line[i] == '"' && ((i > 1 && a:line[i-1] == '\' && a:line[i-2] == '\') || a:line[i-1] != '\')) && i < llen 43 let i += 1 44 endwhile 45 if a:line[i] == '"' 46 let i += 1 47 endif 48 else 49 if a:line[i] == "'" 50 let i += 1 51 let line1 = line1 . 's' 52 while !(a:line[i] == "'" && ((i > 1 && a:line[i-1] == '\' && a:line[i-2] == '\') || a:line[i-1] != '\')) && i < llen 53 let i += 1 54 endwhile 55 if a:line[i] == "'" 56 let i += 1 57 endif 58 else 59 if a:line[i] == "`" 60 let i += 1 61 let line1 = line1 . 's' 62 while a:line[i] != "`" && i < llen 63 let i += 1 64 endwhile 65 if a:line[i] == "`" 66 let i += 1 67 endif 68 endif 69 endif 70 endif 71 if i == llen 72 break 73 endif 74 let line1 = line1 . a:line[i] 75 let j += 1 76 let i += 1 77 endwhile 78 return line1 79endfunction 80 81" Convert foo(bar()) int foo() 82function s:RDelete_parens(line) 83 if s:Get_paren_balance(a:line, "(", ")") != 0 84 return a:line 85 endif 86 let i = 0 87 let j = 0 88 let line1 = "" 89 let llen = strlen(a:line) 90 while i < llen 91 let line1 = line1 . a:line[i] 92 if a:line[i] == '(' 93 let nop = 1 94 while nop > 0 && i < llen 95 let i += 1 96 if a:line[i] == ')' 97 let nop -= 1 98 else 99 if a:line[i] == '(' 100 let nop += 1 101 endif 102 endif 103 endwhile 104 let line1 = line1 . a:line[i] 105 endif 106 let i += 1 107 endwhile 108 return line1 109endfunction 110 111function! s:Get_paren_balance(line, o, c) 112 let line2 = substitute(a:line, a:o, "", "g") 113 let openp = strlen(a:line) - strlen(line2) 114 let line3 = substitute(line2, a:c, "", "g") 115 let closep = strlen(line2) - strlen(line3) 116 return openp - closep 117endfunction 118 119function! s:Get_matching_brace(linenr, o, c, delbrace) 120 let line = SanitizeRLine(getline(a:linenr)) 121 if a:delbrace == 1 122 let line = substitute(line, '{$', "", "") 123 endif 124 let pb = s:Get_paren_balance(line, a:o, a:c) 125 let i = a:linenr 126 while pb != 0 && i > 1 127 let i -= 1 128 let pb += s:Get_paren_balance(SanitizeRLine(getline(i)), a:o, a:c) 129 endwhile 130 return i 131endfunction 132 133" This function is buggy because there 'if's without 'else' 134" It must be rewritten relying more on indentation 135function! s:Get_matching_if(linenr, delif) 136 let line = SanitizeRLine(getline(a:linenr)) 137 if a:delif 138 let line = substitute(line, "if", "", "g") 139 endif 140 let elsenr = 0 141 let i = a:linenr 142 let ifhere = 0 143 while i > 0 144 let line2 = substitute(line, '\<else\>', "xxx", "g") 145 let elsenr += strlen(line) - strlen(line2) 146 if line =~ '.*\s*if\s*()' || line =~ '.*\s*if\s*()' 147 let elsenr -= 1 148 if elsenr == 0 149 let ifhere = i 150 break 151 endif 152 endif 153 let i -= 1 154 let line = SanitizeRLine(getline(i)) 155 endwhile 156 if ifhere 157 return ifhere 158 else 159 return a:linenr 160 endif 161endfunction 162 163function! s:Get_last_paren_idx(line, o, c, pb) 164 let blc = a:pb 165 let line = substitute(a:line, '\t', s:curtabstop, "g") 166 let theidx = -1 167 let llen = strlen(line) 168 let idx = 0 169 while idx < llen 170 if line[idx] == a:o 171 let blc -= 1 172 if blc == 0 173 let theidx = idx 174 endif 175 else 176 if line[idx] == a:c 177 let blc += 1 178 endif 179 endif 180 let idx += 1 181 endwhile 182 return theidx + 1 183endfunction 184 185" Get previous relevant line. Search back until getting a line that isn't 186" comment or blank 187function s:Get_prev_line(lineno) 188 let lnum = a:lineno - 1 189 let data = getline( lnum ) 190 while lnum > 0 && (data =~ '^\s*#' || data =~ '^\s*$') 191 let lnum = lnum - 1 192 let data = getline( lnum ) 193 endwhile 194 return lnum 195endfunction 196 197" This function is also used by r-plugin/common_global.vim 198" Delete from '#' to the end of the line, unless the '#' is inside a string. 199function SanitizeRLine(line) 200 let newline = s:RDelete_quotes(a:line) 201 let newline = s:RDelete_parens(newline) 202 let newline = substitute(newline, '#.*', "", "") 203 let newline = substitute(newline, '\s*$', "", "") 204 if &filetype == "rhelp" && newline =~ '^\\method{.*}{.*}(.*' 205 let newline = substitute(newline, '^\\method{\(.*\)}{.*}', '\1', "") 206 endif 207 return newline 208endfunction 209 210function GetRIndent() 211 212 let clnum = line(".") " current line 213 214 let cline = getline(clnum) 215 if cline =~ '^\s*#' 216 if g:r_indent_ess_comments == 1 217 if cline =~ '^\s*###' 218 return 0 219 endif 220 if cline !~ '^\s*##' 221 return g:r_indent_comment_column 222 endif 223 endif 224 endif 225 226 let cline = SanitizeRLine(cline) 227 228 if cline =~ '^\s*}' 229 let indline = s:Get_matching_brace(clnum, '{', '}', 1) 230 if indline > 0 && indline != clnum 231 let iline = SanitizeRLine(getline(indline)) 232 if s:Get_paren_balance(iline, "(", ")") == 0 || iline =~ '(\s*{$' 233 return indent(indline) 234 else 235 let indline = s:Get_matching_brace(indline, '(', ')', 1) 236 return indent(indline) 237 endif 238 endif 239 endif 240 241 if cline =~ '^\s*)$' 242 let indline = s:Get_matching_brace(clnum, '(', ')', 1) 243 return indent(indline) 244 endif 245 246 " Find the first non blank line above the current line 247 let lnum = s:Get_prev_line(clnum) 248 " Hit the start of the file, use zero indent. 249 if lnum == 0 250 return 0 251 endif 252 253 let line = SanitizeRLine(getline(lnum)) 254 255 if &filetype == "rhelp" 256 if cline =~ '^\\dontshow{' || cline =~ '^\\dontrun{' || cline =~ '^\\donttest{' || cline =~ '^\\testonly{' 257 return 0 258 endif 259 if line =~ '^\\examples{' || line =~ '^\\usage{' || line =~ '^\\dontshow{' || line =~ '^\\dontrun{' || line =~ '^\\donttest{' || line =~ '^\\testonly{' 260 return 0 261 endif 262 endif 263 264 if &filetype == "rnoweb" && line =~ "^<<.*>>=" 265 return 0 266 endif 267 268 if cline =~ '^\s*{' && s:Get_paren_balance(cline, '{', '}') > 0 269 if g:r_indent_ess_compatible && line =~ ')$' 270 let nlnum = lnum 271 let nline = line 272 while s:Get_paren_balance(nline, '(', ')') < 0 273 let nlnum = s:Get_prev_line(nlnum) 274 let nline = SanitizeRLine(getline(nlnum)) . nline 275 endwhile 276 if nline =~ '^\s*function\s*(' && indent(nlnum) == shiftwidth() 277 return 0 278 endif 279 endif 280 if s:Get_paren_balance(line, "(", ")") == 0 281 return indent(lnum) 282 endif 283 endif 284 285 " line is an incomplete command: 286 if line =~ '\<\(if\|while\|for\|function\)\s*()$' || line =~ '\<else$' || line =~ '<-$' || line =~ '->$' 287 return indent(lnum) + shiftwidth() 288 endif 289 290 " Deal with () and [] 291 292 let pb = s:Get_paren_balance(line, '(', ')') 293 294 if line =~ '^\s*{$' || line =~ '(\s*{' || (pb == 0 && (line =~ '{$' || line =~ '(\s*{$')) 295 return indent(lnum) + shiftwidth() 296 endif 297 298 let s:curtabstop = repeat(' ', &tabstop) 299 300 if g:r_indent_align_args == 1 301 if pb > 0 && line =~ '{$' 302 return s:Get_last_paren_idx(line, '(', ')', pb) + shiftwidth() 303 endif 304 305 let bb = s:Get_paren_balance(line, '[', ']') 306 307 if pb > 0 308 if &filetype == "rhelp" 309 let ind = s:Get_last_paren_idx(line, '(', ')', pb) 310 else 311 let ind = s:Get_last_paren_idx(getline(lnum), '(', ')', pb) 312 endif 313 return ind 314 endif 315 316 if pb < 0 && line =~ '.*[,&|\-\*+<>]$' 317 let lnum = s:Get_prev_line(lnum) 318 while pb < 1 && lnum > 0 319 let line = SanitizeRLine(getline(lnum)) 320 let line = substitute(line, '\t', s:curtabstop, "g") 321 let ind = strlen(line) 322 while ind > 0 323 if line[ind] == ')' 324 let pb -= 1 325 else 326 if line[ind] == '(' 327 let pb += 1 328 endif 329 endif 330 if pb == 1 331 return ind + 1 332 endif 333 let ind -= 1 334 endwhile 335 let lnum -= 1 336 endwhile 337 return 0 338 endif 339 340 if bb > 0 341 let ind = s:Get_last_paren_idx(getline(lnum), '[', ']', bb) 342 return ind 343 endif 344 endif 345 346 let post_block = 0 347 if line =~ '}$' && s:Get_paren_balance(line, '{', '}') < 0 348 let lnum = s:Get_matching_brace(lnum, '{', '}', 0) 349 let line = SanitizeRLine(getline(lnum)) 350 if lnum > 0 && line =~ '^\s*{' 351 let lnum = s:Get_prev_line(lnum) 352 let line = SanitizeRLine(getline(lnum)) 353 endif 354 let pb = s:Get_paren_balance(line, '(', ')') 355 let post_block = 1 356 endif 357 358 " Indent after operator pattern 359 let olnum = s:Get_prev_line(lnum) 360 let oline = getline(olnum) 361 if olnum > 0 362 if line =~ g:r_indent_op_pattern && s:Get_paren_balance(line, "(", ")") == 0 363 if oline =~ g:r_indent_op_pattern && s:Get_paren_balance(line, "(", ")") == 0 364 return indent(lnum) 365 else 366 return indent(lnum) + shiftwidth() 367 endif 368 else 369 if oline =~ g:r_indent_op_pattern && s:Get_paren_balance(line, "(", ")") == 0 370 return indent(lnum) - shiftwidth() 371 endif 372 endif 373 endif 374 375 let post_fun = 0 376 if pb < 0 && line !~ ')\s*[,&|\-\*+<>]$' 377 let post_fun = 1 378 while pb < 0 && lnum > 0 379 let lnum -= 1 380 let linepiece = SanitizeRLine(getline(lnum)) 381 let pb += s:Get_paren_balance(linepiece, "(", ")") 382 let line = linepiece . line 383 endwhile 384 if line =~ '{$' && post_block == 0 385 return indent(lnum) + shiftwidth() 386 endif 387 388 " Now we can do some tests again 389 if cline =~ '^\s*{' 390 return indent(lnum) 391 endif 392 if post_block == 0 393 let newl = SanitizeRLine(line) 394 if newl =~ '\<\(if\|while\|for\|function\)\s*()$' || newl =~ '\<else$' || newl =~ '<-$' 395 return indent(lnum) + shiftwidth() 396 endif 397 endif 398 endif 399 400 if cline =~ '^\s*else' 401 if line =~ '<-\s*if\s*()' 402 return indent(lnum) + shiftwidth() 403 else 404 if line =~ '\<if\s*()' 405 return indent(lnum) 406 else 407 return indent(lnum) - shiftwidth() 408 endif 409 endif 410 endif 411 412 let bb = s:Get_paren_balance(line, '[', ']') 413 if bb < 0 && line =~ '.*]' 414 while bb < 0 && lnum > 0 415 let lnum -= 1 416 let linepiece = SanitizeRLine(getline(lnum)) 417 let bb += s:Get_paren_balance(linepiece, "[", "]") 418 let line = linepiece . line 419 endwhile 420 let line = s:RDelete_parens(line) 421 endif 422 423 let plnum = s:Get_prev_line(lnum) 424 let ppost_else = 0 425 if plnum > 0 426 let pline = SanitizeRLine(getline(plnum)) 427 let ppost_block = 0 428 if pline =~ '}$' 429 let ppost_block = 1 430 let plnum = s:Get_matching_brace(plnum, '{', '}', 0) 431 let pline = SanitizeRLine(getline(plnum)) 432 if pline =~ '^\s*{$' && plnum > 0 433 let plnum = s:Get_prev_line(plnum) 434 let pline = SanitizeRLine(getline(plnum)) 435 endif 436 endif 437 438 if pline =~ 'else$' 439 let ppost_else = 1 440 let plnum = s:Get_matching_if(plnum, 0) 441 let pline = SanitizeRLine(getline(plnum)) 442 endif 443 444 if pline =~ '^\s*else\s*if\s*(' 445 let pplnum = s:Get_prev_line(plnum) 446 let ppline = SanitizeRLine(getline(pplnum)) 447 while ppline =~ '^\s*else\s*if\s*(' || ppline =~ '^\s*if\s*()\s*\S$' 448 let plnum = pplnum 449 let pline = ppline 450 let pplnum = s:Get_prev_line(plnum) 451 let ppline = SanitizeRLine(getline(pplnum)) 452 endwhile 453 while ppline =~ '\<\(if\|while\|for\|function\)\s*()$' || ppline =~ '\<else$' || ppline =~ '<-$' 454 let plnum = pplnum 455 let pline = ppline 456 let pplnum = s:Get_prev_line(plnum) 457 let ppline = SanitizeRLine(getline(pplnum)) 458 endwhile 459 endif 460 461 let ppb = s:Get_paren_balance(pline, '(', ')') 462 if ppb < 0 && (pline =~ ')\s*{$' || pline =~ ')$') 463 while ppb < 0 && plnum > 0 464 let plnum -= 1 465 let linepiece = SanitizeRLine(getline(plnum)) 466 let ppb += s:Get_paren_balance(linepiece, "(", ")") 467 let pline = linepiece . pline 468 endwhile 469 let pline = s:RDelete_parens(pline) 470 endif 471 endif 472 473 let ind = indent(lnum) 474 475 if g:r_indent_align_args == 0 && pb != 0 476 let ind += pb * shiftwidth() 477 return ind 478 endif 479 480 if g:r_indent_align_args == 0 && bb != 0 481 let ind += bb * shiftwidth() 482 return ind 483 endif 484 485 if plnum > 0 486 let pind = indent(plnum) 487 else 488 let pind = 0 489 endif 490 491 if ind == pind || (ind == (pind + shiftwidth()) && pline =~ '{$' && ppost_else == 0) 492 return ind 493 endif 494 495 let pline = getline(plnum) 496 let pbb = s:Get_paren_balance(pline, '[', ']') 497 498 while pind < ind && plnum > 0 && ppb == 0 && pbb == 0 499 let ind = pind 500 let plnum = s:Get_prev_line(plnum) 501 let pline = getline(plnum) 502 let ppb = s:Get_paren_balance(pline, '(', ')') 503 let pbb = s:Get_paren_balance(pline, '[', ']') 504 while pline =~ '^\s*else' 505 let plnum = s:Get_matching_if(plnum, 1) 506 let pline = getline(plnum) 507 let ppb = s:Get_paren_balance(pline, '(', ')') 508 let pbb = s:Get_paren_balance(pline, '[', ']') 509 endwhile 510 let pind = indent(plnum) 511 if ind == (pind + shiftwidth()) && pline =~ '{$' 512 return ind 513 endif 514 endwhile 515 516 return ind 517endfunction 518 519let &cpo = s:cpo_save 520unlet s:cpo_save 521 522" vim: sw=2 523