-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathr.vim
1098 lines (955 loc) · 32.2 KB
/
r.vim
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
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Vim indent file for R language
" Language: R
" Maintainer: Grant Farnsworth
" Created: 2013 Apr 5
" Last Change: 2013 Apr 5
"
" At present it makes these choices:
" 1. Comments are indented just like regular code
" 2. Indentation after ( an [ aligns with the next character after the ( or [
" 3. Multiline quotes are indented just line regular code (I would fix this but % doesn't work with ")
" 4. Assignment is by <-. using = for assignment may indent multiline expressions incorrectly in some cases
"
" TODO:
" 1. Write code to handle multiline quotes
" 2. Write code for multiline -> cases (they rarely come up)
" 3. Consider improving indenting for use of = for assignment
" 4. Open if else clauses need some work
" 5. probably should have marker for previous code line begin instead of just physical lines
"
" New Issues:
" * 161 indent should be SW past the if, not past the line
" * 317 indent should be SW past the if, not past the line
" * 84 indent should check whether if associated with else has something before it
" * 126 hard to fix quoting problem
" * 302 this looks fixable
" * 262
"
" IMMEDIATE TODO: all lines before indenting should check whether previous line was open if
" TODO: known bug: unmatched } locks up indent algorithm
"
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" only load when no other indent is loaded
if exists("b:r_indent_gvf")
finish
endif
let b:r_indent_gvf = 1
" tell vim the name of our indent function
setlocal indentexpr=RIndent_GVF(v:lnum)
" if these keys are entered in insert mode, reindent the line
setlocal indentkeys={,},!^F,o,O,e,=else
" don't keep redefining the function
if exists("*RIndent_GVF")
finish
endif
" this variable dictates whether debug messages will be echoed
let s:debug_mode = 0
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" function to return the previous line that's not blank or a comment
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! s:GetPrevNonCommentLineNum( line_num )
" regular expression denoting a comment
let skip_lines = '\m^\s*#'
" find previous non-blank line
let nline = a:line_num
while nline > 0
let nline = prevnonblank(nline-1)
if getline(nline) !~? skip_lines
break
endif
endwhile
return nline
endfunction " s:GetPrevNonCommentLineNum
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" uses vim's % command to find a matching character for what's
" under the cursor
"
" Cursor location input is zero indexed
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! s:FindMatch(in_line,in_col)
" save current cursor location
let mycol = col(".")
let myline = line(".")
" to go starting point
call cursor(a:in_line,a:in_col+1)
" find matching par
normal! %
" save match locations
let matchline = line(".")
let matchcol = col(".")
" temporary debug
if matchline=myline && matchcol=mycol
echom "FindMatch could not find a match on line " . myline
endif
" go back to where we were
call cursor(myline,mycol)
" return -1 if we fail, otherwise match position
if myline==matchline && mycol==matchcol
return [ -1, -1 ]
else
return [ matchline, matchcol ]
endif
endfunction " findmatch
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" takes line and column of a } as its argument
" uses vim's % to find the match for it, then returns the line
" of the beginning of that statement (for use with indent())
"
" Before {} should come one of the following:
" if ()
" function()
" for()
" while()
" repeat
" else
"
" in_col is zero indexed
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! s:FindMatchCurlyStatement(in_line,in_col)
" save current cursor location
let mycol = col(".")
let myline = line(".")
" first find the matching {
let firstcurly = s:FindMatch(a:in_line,a:in_col)
" i is column number and jj is line number
let i = firstcurly[1]-1
let jj = firstcurly[0]
let tmpline = s:ZapQuotesAndComments(getline(jj))
" in case the match was in position 0
if i < 0
let jj -= 1
let tmpline = s:ZapQuotesAndComments(getline(jj))
let i = strlen(tmpline)-1
endif
" loop
while jj > 0
" echom 'row ' . jj . ' col ' . i . ' Char: (' . tmpline[i] . ") Line: " . tmpline
" skip over { and (
if tmpline[i] == '{' || tmpline[i] == '('
if i > 0
let i -=1
else
let jj -= 1
let tmpline = s:ZapQuotesAndComments(getline(jj))
let i = strlen(tmpline) -1
endif
continue
endif
" skip over white space
if tmpline[i] == ' ' || tmpline[i] =="\t"
if i > 0
let i -= 1
else
let jj -= 1
let tmpline = s:ZapQuotesAndComments(getline(jj))
let i = strlen(tmpline) -1
endif
continue
endif
" if we found ) skip to the other side
if tmpline[i] == ')'
let tmpcoords = s:FindMatch(jj,i)
" echom 'Found a Paren from ' . jj . '(' . i . ') to (' . tmpcoords[1] . ') on ' . tmpcoords[0]
if i > 0
let i = tmpcoords[1]-1
let jj = tmpcoords[0]
let tmpline = s:ZapQuotesAndComments(getline(jj))
else
let jj = tmpcoords[0] - 1
let tmpline = s:ZapQuotesAndComments(getline(jj))
let i = strlen(tmpline)-1
endif
continue
" return tmpcoords[0]
endif
" curly could happen if we saw an else
if tmpline[i] == '}'
let tmpcoords = s:FindMatch(jj,i)
"echom "Match for curly" . tmpcoords[0] . ' ' . tmpcoords[1]
if i >= 0
let i = tmpcoords[1]-1
let jj = tmpcoords[0]
let tmpline = s:ZapQuotesAndComments(getline(jj))
let i = strlen(tmpline)-1
else
let jj -= 1
let tmpline = s:ZapQuotesAndComments(getline(jj))
let i = strlen(tmpline)-1
endif
"echom 'Found a Curly'
continue
endif
" did we find one of the key words?
" else, for, if, while, function, repeat
if tmpline[i] =~ '\m[efrnt]'
" check for else
if i > 3 && tmpline[(i-3):i] == 'else'
let i -= 4
if i < 0
let jj -= 1
let tmpline = s:ZapQuotesAndComments(getline(jj))
let i = strlen(tmpline) -1
endif
continue
endif
" check for for
if i > 2 && tmpline[(i-2):i] == 'for'
" echom "Found for "
let tmp = s:IsOpenIndent(jj-1)
if tmp[0]==1
" echom "got in and doing it right"
return tmp[1]
else
return jj
endif
endif
" check for function
if i > 7 && tmpline[(i-7):i] == 'function'
" echom "Found Function"
let tmp = s:IsOpenIndent(jj-1)
if tmp[0]==1
" echom "got in and doing it right"
return tmp[1]
else
return jj
endif
endif
" check for repeat
if i > 5 && tmpline[(i-5):i] == 'repeat'
let tmp = s:IsOpenIndent(jj-1)
if tmp[0]==1
" echom "got in and doing it right"
return tmp[1]
else
return jj
endif
endif
" check for while
if i > 4 && tmpline[(i-4):i] == 'while'
let tmp = s:IsOpenIndent(jj-1)
if tmp[0]==1
" echom "got in and doing it right"
return tmp[1]
else
return jj
endif
endif
" some kind of strange case. I guess just return this line
echom "Interior Strange case at line " in_line
return jj
endif
" if we got here some strange hud happened just return the line
echom "Strange case"
return jj
endwhile
" if we got here there was nothing before that up to the beginning of the file
return -1
endfunction " FindMatchCurlyStatement
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" this function edits a line of text, checks if double and single quotes are
" matched and deletes everything inside of them if there are an even number (of each kind)
"
" if quotes are unmatches, they are ignored.
" Double quotes are done first, then single
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! s:ZapQuotesAndComments( line_code )
" local variable name
let my_code = a:line_code
" outline is temporary storage
let outline = my_code
" Zap anything in double quotes
if my_code =~ '\m"' && ( float2nr(fmod(strlen(substitute(my_code,'[^"]',"","g")),2)) == 0 )
let i = 0
let killing = 0
let outline = ""
let llen = strlen(my_code)
while i < llen
if my_code[i] == '"'
if killing == 0
let killing = 1
else
let killing = 0
let outline = outline . 'S'
let i += 1
continue
endif
endif
if killing == 1
let outline = outline . 'S'
else
let outline = outline . my_code[i]
endif
let i += 1
endwhile
let my_code = outline
endif
"
" Zap anything in single quotes
if my_code =~ '\m''' && float2nr(fmod(strlen(substitute(my_code,'[^'']',"","g")),2)) == 0
let i = 0
let killing = 0
let outline = ""
let llen = strlen(my_code)
while i < llen
if my_code[i] == "'"
if killing == 0
let killing = 1
else
let killing = 0
let outline = outline . 'S'
let i += 1
continue
endif
endif
if killing == 1
let outline = outline . 'S'
else
let outline = outline . my_code[i]
endif
let i += 1
endwhile
let my_code = outline
endif
" Strip out comments
if my_code =~ '\m#'
let my_code = substitute( my_code, '#.*$',"","g")
endif
" return the zapped version
return my_code
endfunction " ZapQuotesAndComments
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" This function determines whether the given line ends a an open indent
" command, which is either a for, if, while, or function without any
" curly brackets following.
"
" It returns a list. The first element is whether or not it is an open
" indent command and the second is the line number at which the command begins,
" which in most cases is equal to the line passed in.
"
" This function recusively calls itself to get to the first such open
"
" The purpose of this function is to allow us to find how far back to unindent
"
" This function still needs some work
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! s:IsOpenIndent(this_line)
if s:debug_mode
echom "Entered IsOpenIndent: " . a:this_line
endif
" strip stuff out as usual
let myline = s:ZapQuotesAndComments(getline(a:this_line))
" check for ending with <-
if myline =~ '\m<-\s*$'
if s:debug_mode
echom "Open Indent found at " . a:this_line
endif
return[1,a:this_line]
endif
" check for else
if myline =~ '\melse\s*$'
" TODO need to follow this through to the if
if s:debug_mode
echom "Open Indent found at " . a:this_line
endif
return [1,a:this_line]
endif
" check for repeat
if myline =~ '\mrepeat\s*$'
let prevopen= s:IsOpenIndent(s:GetPrevNonCommentLineNum(a:this_line))
if prevopen[0]
return prevopen
else
if s:debug_mode
echom "Open Indent found at " . a:this_line
endif
return [1,a:this_line]
endif
endif
" open indents making it this far always end in parens
" echom "MYLINE: (" . a:this_line . ") " . myline
if myline !~ '\m)\s*$'
" echom "Death zone: " . myline
return [0,0]
endif
" it ended in a paren, search back
let endparcolumn = match(myline,'\m)\s*$')
let matchline = s:FindMatch(a:this_line,endparcolumn)
if s:debug_mode
echom "endparcolumn " . endparcolumn
echom "Matchline was originally " . matchline[0] . " and " . matchline[1]
endif
"
" check if previous paren is preceded by function, for, if, or while
"
" extract the part of the line leading up to the ( so we can tell what it is
let secondline = s:ZapQuotesAndComments(getline(matchline[0]))
let firstpart = strpart(secondline,0,matchline[1])
" first see if this is a paren by itself. If so, go back to previous line
" TODO this should check that the first match is also the match from above
if firstpart =~ '\m^\s*('
let matchline = s:GetPrevNonCommentLineNum(matchline)
let firstpart = s:ZapQuotesAndComments(getline(matchline[0]))
endif
if s:debug_mode
echom "got to first part"
echom "matchline: " . matchline[0]
echom "FIrst Part: " . firstpart
endif
" check whether firstpart was indented because of previous line
let doublecheck = s:IsOpenIndent(s:GetPrevNonCommentLineNum(matchline[0]))
if doublecheck[0]==1
if s:debug_mode
echom "Found Previous indent at" . doublecheck[1]
endif
return doublecheck
endif
" now check whether firstpart is an if, for, while, or function line
if firstpart =~ '\m\<for\>\s*(\s*$' || firstpart =~ '\m\<if\>\s*(\s*$' || firstpart =~ '\m\<while\>\s*(\s*$' || firstpart =~ '\m\<function\>\s*(\s*$'
if s:debug_mode
echom "Got in"
echom "Open Indent found at " . matchline[0]
endif
return [ 1, matchline[0] ]
endif
" if we got here, it's not indented
return [ 0, 0]
endfunction " IsOpenIndent
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" count how many net open items there are (can be negative)
" openc EXCLUDES the pattern we want to count '[^(]' for example
" closec EXCLUDES the pattern we want to count '[^)]' for example
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! s:CountOpens( this_line, openc, closec )
" first, strip out any trailing comments
let myline = s:ZapQuotesAndComments(getline(a:this_line))
" calculate number of opens and closes
let lineopens = strlen(substitute(myline,a:openc,"","g"))
let linecloses = strlen(substitute(myline,a:closec,"","g"))
return lineopens - linecloses
endfunction " CountOpens
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" function to find previous line that matches a regular expression
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! s:FindPrev(this_line,reg)
let i = a:this_line
while i >= 0 && (getline(i) !~ a:reg) && getline(i) !~ '\m^\s*$'
let i -= 1
endwhile
return i
endfunction " FindPrev
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" function to find unbalanced character and return its column
" there should be more openc than closec in this_line
"
" GVF: this should return last one, not first
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! s:GetUnbalancedCol( this_line, openc, closec )
" zap quotes
let myline = s:ZapQuotesAndComments(getline(a:this_line))
" remove openc, closec pairs
let llength = strchars(myline)
let i = 0
let opens = [ ]
while i < llength
" add to list of open paren
if myline[i] == a:openc
let opens = opens + [ i ]
endif
" if this is a close paren, remove last open
if myline[i] == a:closec && len(opens) > 0
let opens = opens[ 0 : -2]
endif
" increment
let i += 1
endwhile
" return the last open paren or -1 if we failed
if len(opens) <= 0
return -1
else
return opens[-1]
endif
endfunction " GetUnbalancedCol
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" function to find last unbalanced character and return its column
" there should be more closec than openc in this_line
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! s:GetUnbalancedCloseCol( this_line, openc, closec )
" zap quotes
let myline = s:ZapQuotesAndComments(getline(a:this_line))
" remove openc, closec pairs
let llength = strchars(myline)
let i = llength
let closes = [ ]
while i >= 0
" add to list of closed paren locations
if myline[ i] == a:closec
let closes = closes + [ i ]
endif
" if this is a close paren, remove last open
if myline[i] == a:openc && len(closes) > 0
let closes = closes[ 0 : -2]
endif
" increment
let i -= 1
endwhile
" return the last close paren or -1 if we failed
if len(closes) <= 0
return -1
else
return closes[0] + 1
endif
endfunction " GetUnbalancedCloseCol
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" This function takes as its argument the line and column of the beginning of
" a command (either an open curly or an R command of some time). It searches
" backward to find the immediately previous if, while, repeat, for, or function
" and returns its line and the column at which the word begins.
"
" column counting is 0 indexed
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! s:GetStatementBegin(this_line, this_col)
" start at the line and column given us
let linecounter = a:this_line
let colcounter = a:this_col
let linetext = s:ZapQuotesAndComments(getline(a:this_line))
if s:debug_mode
echom "Started at : " . linecounter . ' ' . colcounter
endif
" loop backward until we find the preceding word
while linecounter > 0
if s:debug_mode
echom ': ' . linecounter . ' ' . colcounter
endif
" check for whitespace
if linetext[colcounter] =~ "\s"
" Move backward
if colcounter == 0
let linecounter -= 1
let linetext = s:ZapQuotesAndComments(getline(linecounter))
let colcounter = len(linetext)
else
let colcounter -= 1
endif
continue
endif
" check for ) or }. Zoom past if we find one
if linetext[colcounter] == ')' || linetext[colcounter] == '}'
if s:debug_mode
echom "Finding match"
endif
let tmpstuff = s:FindMatch(linecounter,colcounter)
if tmpstuff[0] < 0
echom "ERROR: count not find match in GetStatementBegin"
endif
let linecounter = tmpstuff[0]
let colcounter = tmpstuff[1]-1
let linetext = s:ZapQuotesAndComments(getline(linecounter))
continue
endif
" check for else, zoom backward if we find one
if linetext[colcounter] == 'e' && colcounter > 2 && linetext[(colcounter-3):colcounter] == 'else'
let colcounter -= 3
" Move backward
if colcounter <= 0
let linecounter -= 1
let linetext = s:ZapQuotesAndComments(getline(linecounter))
let colcounter = len(linetext)
else
let colcounter -= 1
endif
continue
endif
" check for for, return if we find one
if linetext[colcounter] == 'r' && colcounter > 1 && linetext[(colcounter-2):colcounter] == 'for'
let colcounter -= 2
return [linecounter,colcounter]
endif
" check for if, return if we find it (need to check that it's not an else if
if linetext[colcounter] == 'f' && colcounter > 0 && linetext[(colcounter-1):colcounter] == 'if'
let colcounter -= 1
return [linecounter,colcounter]
endif
" check for function, return if we find one
if linetext[colcounter] == 'n' && colcounter > 6 && linetext[(colcounter-7):colcounter] == 'function'
if s:debug_mode
echom "Finding Function"
endif
let colcounter -= 7
return [linecounter,colcounter]
endif
" check for while, return if we find it
if linetext[colcounter] == 'e' && colcounter > 3 && linetext[(colcounter-4):colcounter] == 'while'
let colcounter -= 4
return [linecounter,colcounter]
endif
" check for repeat, return if we find it
if linetext[colcounter] == 't' && colcounter > 4 && linetext[(colcounter-5):colcounter] == 'repeat'
let colcounter -= 5
return [linecounter,colcounter]
endif
" Move backward
if colcounter <= 0
let linecounter -= 1
let linetext = s:ZapQuotesAndComments(getline(linecounter))
let colcounter = len(linetext)
else
let colcounter -= 1
endif
endwhile
endfunction " s:GetStatementBegin()
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" External function: Calls RIndent_Internal but preserves view
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! RIndent_GVF( line_num )
" remember current window view so we don't unexpectedly scroll
let l:winview = winsaveview()
if s:debug_mode
echom "Saved View"
endif
" call the main code to determine the indent level
let indentlev = s:RIndent_Internal( a:line_num )
" restore view as it was
call winrestview(l:winview)
" return the desired indent level
return indentlev
endfunction " RIndent_GVF
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Main function: returns indent level for current line
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! s:RIndent_Internal( line_num )
" first line in the file is always not indented
if a:line_num == 1
if s:debug_mode
echom "Marker -1"
endif
return 0
endif
"""
" Preparation
"""
" get clean version of this line, previous, and previous previous
let this_code = s:ZapQuotesAndComments(getline(a:line_num))
let prev_codeline_num = s:GetPrevNonCommentLineNum( a:line_num )
let prev_code = s:ZapQuotesAndComments(getline(prev_codeline_num))
let prev_prev_codeline_num = s:GetPrevNonCommentLineNum( prev_codeline_num )
let prev_prev_code = s:ZapQuotesAndComments(getline(prev_prev_codeline_num))
" starting point for indenting
let idt = indent( prev_codeline_num)
" debug
if s:debug_mode
echom "PrevPrev: " . prev_prev_code
echom "Prev: " . prev_code
endif
"""""
" This line begins with closing bracket or paren (indent to its match)
"""""
" Check if this line begins with }
if this_code =~ '\m^\s*}'
let col_loc = match(this_code,'\m}')
let match_loc = s:FindMatchCurlyStatement(a:line_num,col_loc)
let idt = indent(match_loc)
if s:debug_mode
echom "Marker 0 " . match_loc
endif
return idt
endif
" Check if this line begins with )
if this_code =~ '\m^\s*)'
let col_loc = match(this_code,'\m)')
let match_loc = s:FindMatch(a:line_num,col_loc)
let idt = match_loc[1]-1
if s:debug_mode
echom "Marker 2"
endif
return idt
endif
" Check if this line begins with ]
if this_code =~ '\m^\s*]'
let col_loc = match(this_code,'\m]')
let match_loc = s:FindMatch(a:line_num,col_loc)
let idt = match_loc[1]-1
if s:debug_mode
echom "Marker 3"
endif
return idt
endif
"""""
" Quick check for { at beginning of line
" (indent to beginning of prev if/for/while) or to the line if it's a function
"""""
if this_code =~ '\m^\s*{'
let col_loc = match(this_code,'{') -1
let idt = indent(s:GetStatementBegin(a:line_num,col_loc)[0])
if s:debug_mode
echom "Marker 3.5"
endif
return idt
endif
"""""
" Check if previous line implies greater indent for this one
"""""
if prev_code =~ '\m[\[({]'
" faster version of most common case: indent once if previous line ends in {
if prev_code =~ '\m)\s*{\s*$'
let col_loc = match(prev_code,'\m).\{-}$')
let match_loc = s:FindMatch(prev_codeline_num,col_loc)
let idt = indent(match_loc[0]) + &sw
if s:debug_mode
echom "Marker 4"
endif
return idt
elseif prev_code =~ '\melse\s*{\s*$'
let idt += &sw
if s:debug_mode
echom "Marker 4.5"
endif
return idt
else " less common case where it is internal
" Check if there is any unbalanced paren or bracket
let unbalancedcurly = s:CountOpens(prev_codeline_num,'[^{]','[^}]')
let unbalancedsquare = s:CountOpens(prev_codeline_num,'[^[]','[^\]]')
let unbalancedparen = s:CountOpens(prev_codeline_num,'[^(]','[^)]')
" if there are unbalanced things and it doesn't end in {, figure out what to do
if ( unbalancedcurly > 0 || unbalancedsquare > 0 || unbalancedparen > 0 )
" here we store column locations of last bracket/paren
let lastcurlycolumn = -1
let lastparencolumn = -1
let lastsquarecolumn = -1
" find match of last unbalanced, so we can only indent to the last one
if unbalancedcurly
let lastcurlycolumn = s:GetUnbalancedCol(prev_codeline_num,"{","}")
endif
if unbalancedparen
let lastparencolumn = s:GetUnbalancedCol(prev_codeline_num,"(",")")
endif
if unbalancedsquare
let lastsquarecolumn = s:GetUnbalancedCol(prev_codeline_num,"[","]")
endif
" if { is last, indent once
if lastcurlycolumn > max([lastparencolumn, lastsquarecolumn])
let idt += &sw
if s:debug_mode
echom "Marker 5"
endif
return idt
else
let idt = max([lastsquarecolumn,lastparencolumn]) + 1
if s:debug_mode
echom "Marker 6"
endif
return idt
endif
endif
endif
endif
"""""
" Check for completion of indent because of brackets and parens
"""""
if prev_code =~ '\m[)\]}]'
" Check fast check for common case first
if prev_code =~ '\m}\s*$'
let col_loc = match(prev_code,'\m}\s*$')+1
let match_loc = s:FindMatchCurlyStatement(prev_codeline_num,col_loc)
let second_loc = s:IsOpenIndent(match_loc)
if second_loc[0]
let idt = indent(second_loc[1])
if s:debug_mode
echom "Marker 7.1 :" . match_loc
endif
return idt
else
let idt = indent(match_loc)
if s:debug_mode
echom "Marker 7.2 :" . match_loc
endif
return idt
endif
else " less common cases (internal curly, square bracket, or close paren)
" count unbalanced number of close brackets or parens
let unbalancedcurly = -1 * s:CountOpens(prev_codeline_num,'[^{]','[^}]')
let unbalancedsquare = -1 * s:CountOpens(prev_codeline_num,'[^[]','[^\]]')
let unbalancedparen = -1 * s:CountOpens(prev_codeline_num,'[^(]','[^)]')
if s:debug_mode
echom "Unbalanced: " . unbalancedcurly . " " . unbalancedsquare . " " . unbalancedparen
endif
" if there are unbalanced things figure out what to do
if unbalancedcurly > 0 || unbalancedsquare > 0 || unbalancedparen > 0
" here we store column locations of last closing bracket/paren
let lastcurlycolumn = -1
let lastparencolumn = -1
let lastsquarecolumn = -1
" find last unbalanced, so we can only indent to the last one
if unbalancedcurly
let lastcurlycolumn = s:GetUnbalancedCloseCol(prev_codeline_num,"{","}") -1
endif
if unbalancedparen
let lastparencolumn = s:GetUnbalancedCloseCol(prev_codeline_num,"(",")") -1
endif
if unbalancedsquare
let lastsquarecolumn = s:GetUnbalancedCloseCol(prev_codeline_num,"[","]") -1
endif
" if curlys are the last one, get the appropriate line
if lastcurlycolumn > lastparencolumn && lastcurlycolumn > lastsquarecolumn
let matchline = s:FindMatchCurlyStatement(prev_codeline_num,lastcurlycolumn)
let second_loc = s:IsOpenIndent(matchline)
if second_loc[1]
let idt = indent(second_loc)
if s:debug_mode
echom "Marker 8.1 :" . match_loc
endif
return idt
else
let idt = indent(matchline)
if s:debug_mode
echom "marker 8.0"
endif
return idt
endif
endif
" handle paren case
if lastcurlycolumn < lastparencolumn && lastparencolumn > lastsquarecolumn
let thisindent = s:IsOpenIndent(prev_codeline_num)
let matchline = s:FindMatch(prev_codeline_num,lastparencolumn)
if s:debug_mode
echom "prev_codeline_num was" . prev_codeline_num
echom "lastparencolumn" . lastparencolumn
endif
" if it was indented just find immedate match and indent to there
if thisindent[0]
if matchline[0] >= 0
if s:debug_mode
echom "Marker 8.7.1 to line " . matchline[0]
endif
return (indent(matchline[0]))
else
if s:debug_mode
echom "Marker 8.7.2"
endif
return (indent(prev_codeline_num) )
endif
else
if matchline[0] >= 0
if s:debug_mode
echom "matchlineline was" . matchline[0]
echom "matchlinecol was" . matchline[1]
echom "Marker 8.8.1"
endif
return indent(matchline[0])
else
echo "Marker 8.8.2"
return indent(prev_codeline_num)
endif
endif
endif
" handle square case
let matchline = s:FindMatch(prev_codeline_num,(max([lastparencolumn,lastsquarecolumn])-1))
if matchline[0] >= 0
return indent(matchline[0])
if s:debug_mode
echom "Using Default"
endif
else
return indent(prev_codeline_num)
if s:debug_mode
echom "Adjusting"
endif
endif
endif
endif
endif
""""""
" Unbracketed if/else situations. These need some work.
""""""
let prev_is_open = s:IsOpenIndent(prev_codeline_num)
let prev_prev_is_open = s:IsOpenIndent(prev_prev_codeline_num)
" check if previous line opens an indent
if prev_is_open[0]==1
if this_code !~ '\m^\s*{'
if s:debug_mode
echom "Marker 11"