-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrust_fold.vim
140 lines (111 loc) · 3.73 KB
/
rust_fold.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
" TODO(nils): improve this
" TODO(nils): does not work well with nested functions / macros
autocmd FileType rust setlocal foldmethod=expr
autocmd FileType rust setlocal foldexpr=Nilsfold(v:lnum)
autocmd FileType rust setlocal foldtext=Nilstext()
function! NextNonBlankLine(lnum)
let numlines = line('$')
let current = a:lnum + 1
while current <= numlines
if getline(current) =~? '\v\S'
return current
endif
let current += 1
endwhile
return -2
endfunction
function! IsImplStart(lnum)
return getline(a:lnum) =~? 'impl \S* {'
endfunction
function! IsImplFor(lnum)
return getline(a:lnum) =~? 'impl \S* for \S* {'
endfunction
function! IndentLevel(lnum)
return indent(a:lnum) / &shiftwidth
endfunction
function! IsAnnotation(lnum)
return getline(a:lnum) =~? '#\[.*\]'
endfunction
function! IsDoc(lnum)
return getline(a:lnum) =~? '///'
endfunction
function! IsWhereClause(lnum)
return getline(a:lnum) =~? '\s*where'
endfunction
function! IsStandaloneFunctionStart(lnum)
return getline(a:lnum) =~? '\s*fn\ .*{'
endfunction
function! IsFunctionStart(lnum)
return getline(a:lnum) =~? '^\s*fn'
endfunction
function! Nilsfold(lnum)
" split at empty lines
if getline(a:lnum) =~? '\v^\s*$'
return '-1'
endif
" block closer
if getline(a:lnum) =~? '}'
return '='
endif
if IsImplStart(a:lnum)
return 0
endif
let previous_indent = IndentLevel(a:lnum -1) > 0
let this_indent = IndentLevel(a:lnum) > 0
let next_indent = IndentLevel(NextNonBlankLine(a:lnum)) > 0
let value = 0
if IsDoc(a:lnum) || IsAnnotation(a:lnum)
if IsDoc(a:lnum -1) || IsAnnotation(a:lnum -1)
return '1' " this is inside a fold with level 1
else
return '>1' " this starts a fold with level 1
endif
endif
" where is always a part of a fold
if IsWhereClause(a:lnum)
return 1
" a function start followed by a where clause starts a fold
elseif IsFunctionStart(a:lnum)
let indent = IsWhereClause(a:lnum + 1) ? (this_indent + 1) : next_indent
if IsAnnotation(a:lnum - 1) || IsDoc(a:lnum - 1) || IsImplFor(a:lnum -1)
return indent
else
return '>' . indent
endif
" a free standing { is part of the previous foldlevel
elseif getline(a:lnum) =~? '^\s*{\s*$'
return previous_indent
" same indentation should be part of the same fold
elseif next_indent == this_indent
return this_indent
" end
elseif next_indent < this_indent
return this_indent
" starts a new fold
elseif (next_indent > this_indent)
if IsAnnotation(a:lnum - 1) || IsDoc(a:lnum - 1)
return next_indent
else
return '>' . next_indent
endif
endif
endfunction
function! Nilstext()
let title_line = v:foldstart
let loop_guard = 5000
while (loop_guard > 0 && (IsDoc(title_line) || IsAnnotation(title_line)))
let title_line = (title_line + 1)
let loop_guard = loop_guard - 1
endwhile
let title = getline(title_line)
" multiline function header
let title = substitute(title, "->.*$", "", '') " remove return type
let title = substitute(title, "(.*)", "(...)", '') " change arguments to '...'
let title = substitute(title, "[{\ ]*$", "", '') " remove function block opener and extra spaces
let title = substitute(title, "\ \ \ ", "---", "g") " change leading spaces to fold character
let fold_size = (v:foldend - v:foldstart)
let linecount = '[' . fold_size . '] '
let prefix = '+-- '
let title = prefix . title . ' ' . linecount
return title
endfunction