1" Vim indent file 2" Language: R 3" Author: Jakson Alves de Aquino <[email protected]> 4" Last Change: Thu Mar 26, 2015 05:36PM 5 6 7" Only load this indent file when no other was loaded. 8if exists("b:did_indent") 9 finish 10endif 11let b:did_indent = 1 12 13setlocal indentkeys=0{,0},:,!^F,o,O,e 14setlocal indentexpr=GetRIndent() 15 16" Only define the function once. 17if exists("*GetRIndent") 18 finish 19endif 20 21" Options to make the indentation more similar to Emacs/ESS: 22if !exists("g:r_indent_align_args") 23 let g:r_indent_align_args = 1 24endif 25if !exists("g:r_indent_ess_comments") 26 let g:r_indent_ess_comments = 0 27endif 28if !exists("g:r_indent_comment_column") 29 let g:r_indent_comment_column = 40 30endif 31if ! exists("g:r_indent_ess_compatible") 32 let g:r_indent_ess_compatible = 0 33endif 34if ! exists("g:r_indent_op_pattern") 35 let g:r_indent_op_pattern = '\(+\|-\|\*\|/\|=\|\~\|%\)$' 36endif 37 38function s:RDelete_quotes(line) 39 let i = 0 40 let j = 0 41 let line1 = "" 42 let llen = strlen(a:line) 43 while i < llen 44 if a:line[i] == '"' 45 let i += 1 46 let line1 = line1 . 's' 47 while !(a:line[i] == '"' && ((i > 1 && a:line[i-1] == '\' && a:line[i-2] == '\') || a:line[i-1] != '\')) && i < llen 48 let i += 1 49 endwhile 50 if a:line[i] == '"' 51 let i += 1 52 endif 53 else 54 if a:line[i] == "'" 55 let i += 1 56 let line1 = line1 . 's' 57 while !(a:line[i] == "'" && ((i > 1 && a:line[i-1] == '\' && a:line[i-2] == '\') || a:line[i-1] != '\')) && i < llen 58 let i += 1 59 endwhile 60 if a:line[i] == "'" 61 let i += 1 62 endif 63 else 64 if a:line[i] == "`" 65 let i += 1 66 let line1 = line1 . 's' 67 while a:line[i] != "`" && i < llen 68 let i += 1 69 endwhile 70 if a:line[i] == "`" 71 let i += 1 72 endif 73 endif 74 endif 75 endif 76 if i == llen 77 break 78 endif 79 let line1 = line1 . a:line[i] 80 let j += 1 81 let i += 1 82 endwhile 83 return line1 84endfunction 85 86" Convert foo(bar()) int foo() 87function s:RDelete_parens(line) 88 if s:Get_paren_balance(a:line, "(", ")") != 0 89 return a:line 90 endif 91 let i = 0 92 let j = 0 93 let line1 = "" 94 let llen = strlen(a:line) 95 while i < llen 96 let line1 = line1 . a:line[i] 97 if a:line[i] == '(' 98 let nop = 1 99 while nop > 0 && i < llen 100 let i += 1 101 if a:line[i] == ')' 102 let nop -= 1 103 else 104 if a:line[i] == '(' 105 let nop += 1 106 endif 107 endif 108 endwhile 109 let line1 = line1 . a:line[i] 110 endif 111 let i += 1 112 endwhile 113 return line1 114endfunction 115 116function! s:Get_paren_balance(line, o, c) 117 let line2 = substitute(a:line, a:o, "", "g") 118 let openp = strlen(a:line) - strlen(line2) 119 let line3 = substitute(line2, a:c, "", "g") 120 let closep = strlen(line2) - strlen(line3) 121 return openp - closep 122endfunction 123 124function! s:Get_matching_brace(linenr, o, c, delbrace) 125 let line = SanitizeRLine(getline(a:linenr)) 126 if a:delbrace == 1 127 let line = substitute(line, '{$', "", "") 128 endif 129 let pb = s:Get_paren_balance(line, a:o, a:c) 130 let i = a:linenr 131 while pb != 0 && i > 1 132 let i -= 1 133 let pb += s:Get_paren_balance(SanitizeRLine(getline(i)), a:o, a:c) 134 endwhile 135 return i 136endfunction 137 138" This function is buggy because there 'if's without 'else' 139" It must be rewritten relying more on indentation 140function! s:Get_matching_if(linenr, delif) 141 let line = SanitizeRLine(getline(a:linenr)) 142 if a:delif 143 let line = substitute(line, "if", "", "g") 144 endif 145 let elsenr = 0 146 let i = a:linenr 147 let ifhere = 0 148 while i > 0 149 let line2 = substitute(line, '\<else\>', "xxx", "g") 150 let elsenr += strlen(line) - strlen(line2) 151 if line =~ '.*\s*if\s*()' || line =~ '.*\s*if\s*()' 152 let elsenr -= 1 153 if elsenr == 0 154 let ifhere = i 155 break 156 endif 157 endif 158 let i -= 1 159 let line = SanitizeRLine(getline(i)) 160 endwhile 161 if ifhere 162 return ifhere 163 else 164 return a:linenr 165 endif 166endfunction 167 168function! s:Get_last_paren_idx(line, o, c, pb) 169 let blc = a:pb 170 let line = substitute(a:line, '\t', s:curtabstop, "g") 171 let theidx = -1 172 let llen = strlen(line) 173 let idx = 0 174 while idx < llen 175 if line[idx] == a:o 176 let blc -= 1 177 if blc == 0 178 let theidx = idx 179 endif 180 else 181 if line[idx] == a:c 182 let blc += 1 183 endif 184 endif 185 let idx += 1 186 endwhile 187 return theidx + 1 188endfunction 189 190" Get previous relevant line. Search back until getting a line that isn't 191" comment or blank 192function s:Get_prev_line(lineno) 193 let lnum = a:lineno - 1 194 let data = getline( lnum ) 195 while lnum > 0 && (data =~ '^\s*#' || data =~ '^\s*$') 196 let lnum = lnum - 1 197 let data = getline( lnum ) 198 endwhile 199 return lnum 200endfunction 201 202" This function is also used by r-plugin/common_global.vim 203" Delete from '#' to the end of the line, unless the '#' is inside a string. 204function SanitizeRLine(line) 205 let newline = s:RDelete_quotes(a:line) 206 let newline = s:RDelete_parens(newline) 207 let newline = substitute(newline, '#.*', "", "") 208 let newline = substitute(newline, '\s*$', "", "") 209 if &filetype == "rhelp" && newline =~ '^\\method{.*}{.*}(.*' 210 let newline = substitute(newline, '^\\method{\(.*\)}{.*}', '\1', "") 211 endif 212 return newline 213endfunction 214 215function GetRIndent() 216 217 let clnum = line(".") " current line 218 219 let cline = getline(clnum) 220 if cline =~ '^\s*#' 221 if g:r_indent_ess_comments == 1 222 if cline =~ '^\s*###' 223 return 0 224 endif 225 if cline !~ '^\s*##' 226 return g:r_indent_comment_column 227 endif 228 endif 229 endif 230 231 let cline = SanitizeRLine(cline) 232 233 if cline =~ '^\s*}' || cline =~ '^\s*}\s*)$' 234 let indline = s:Get_matching_brace(clnum, '{', '}', 1) 235 if indline > 0 && indline != clnum 236 let iline = SanitizeRLine(getline(indline)) 237 if s:Get_paren_balance(iline, "(", ")") == 0 || iline =~ '(\s*{$' 238 return indent(indline) 239 else 240 let indline = s:Get_matching_brace(indline, '(', ')', 1) 241 return indent(indline) 242 endif 243 endif 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*{' 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) == &sw 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 =~ '<-$' 287 return indent(lnum) + &sw 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) + &sw 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) + &sw 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 =~ '}$' 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 363 if oline =~ g:r_indent_op_pattern 364 return indent(lnum) 365 else 366 return indent(lnum) + &sw 367 endif 368 else 369 if oline =~ g:r_indent_op_pattern 370 return indent(lnum) - &sw 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) + &sw 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) + &sw 396 endif 397 endif 398 endif 399 400 if cline =~ '^\s*else' 401 if line =~ '<-\s*if\s*()' 402 return indent(lnum) + &sw 403 else 404 if line =~ '\<if\s*()' 405 return indent(lnum) 406 else 407 return indent(lnum) - &sw 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 let pind = indent(plnum) 475 476 if g:r_indent_align_args == 0 && pb != 0 477 let ind += pb * &sw 478 return ind 479 endif 480 481 if g:r_indent_align_args == 0 && bb != 0 482 let ind += bb * &sw 483 return ind 484 endif 485 486 if ind == pind || (ind == (pind + &sw) && pline =~ '{$' && ppost_else == 0) 487 return ind 488 endif 489 490 let pline = getline(plnum) 491 let pbb = s:Get_paren_balance(pline, '[', ']') 492 493 while pind < ind && plnum > 0 && ppb == 0 && pbb == 0 494 let ind = pind 495 let plnum = s:Get_prev_line(plnum) 496 let pline = getline(plnum) 497 let ppb = s:Get_paren_balance(pline, '(', ')') 498 let pbb = s:Get_paren_balance(pline, '[', ']') 499 while pline =~ '^\s*else' 500 let plnum = s:Get_matching_if(plnum, 1) 501 let pline = getline(plnum) 502 let ppb = s:Get_paren_balance(pline, '(', ')') 503 let pbb = s:Get_paren_balance(pline, '[', ']') 504 endwhile 505 let pind = indent(plnum) 506 if ind == (pind + &sw) && pline =~ '{$' 507 return ind 508 endif 509 endwhile 510 511 return ind 512 513endfunction 514 515" vim: sw=2 516