/
clojure.vim
206 lines (165 loc) · 5.32 KB
/
clojure.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
" Vim indent file
" Language: Clojure
" Maintainer: Meikel Brandmeyer <mb@kotka.de>
" URL: http://kotka.de/projects/clojure/vimclojure.html
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
finish
endif
let b:did_indent = 1
let s:save_cpo = &cpo
set cpo&vim
let b:undo_indent = "setlocal ai< si< lw< et< sts< sw< inde< indk<"
setlocal autoindent expandtab nosmartindent
setlocal softtabstop=2
setlocal shiftwidth=2
setlocal indentkeys=!,o,O
if exists("*searchpairpos")
function! s:MatchPairs(open, close, stopat)
" Stop only on vector and map [ resp. {. Ignore the ones in strings and
" comments.
return searchpairpos(a:open, '', a:close, 'bWn',
\ 'vimclojure#SynIdName() !~ "clojureParen\\d"',
\ a:stopat)
endfunction
function! VimClojureCheckForStringWorker()
" Check whether there is the last character of the previous line is
" highlighted as a string. If so, we check whether it's a ". In this
" case we have to check also the previous character. The " might be the
" closing one. In case the we are still in the string, we search for the
" opening ". If this is not found we take the indent of the line.
let nb = prevnonblank(v:lnum - 1)
if nb == 0
return -1
endif
call cursor(nb, 0)
call cursor(0, col("$") - 1)
if vimclojure#SynIdName() != "clojureString"
return -1
endif
" This will not work for a " in the first column...
if vimclojure#Yank('l', 'normal! "lyl') == '"'
call cursor(0, col("$") - 2)
if vimclojure#SynIdName() != "clojureString"
return -1
endif
if vimclojure#Yank('l', 'normal! "lyl') != '\\'
return -1
endif
call cursor(0, col("$") - 1)
endif
let p = searchpos('\(^\|[^\\]\)\zs"', 'bW')
if p != [0, 0]
return p[1] - 1
endif
return indent(".")
endfunction
function! VimClojureCheckForString()
return vimclojure#WithSavedPosition({'f': function("VimClojureCheckForStringWorker")})
endfunction
function! GetClojureIndent()
" Get rid of special case.
if line(".") == 1
return 0
endif
" We have to apply some heuristics here to figure out, whether to use
" normal lisp indenting or not.
let i = VimClojureCheckForString()
if i > -1
return i
endif
call cursor(0, 1)
" Find the next enclosing [ or {. We can limit the second search
" to the line, where the [ was found. If no [ was there this is
" zero and we search for an enclosing {.
let paren = s:MatchPairs('(', ')', 0)
let bracket = s:MatchPairs('\[', '\]', paren[0])
let curly = s:MatchPairs('{', '}', bracket[0])
" In case the curly brace is on a line later then the [ or - in
" case they are on the same line - in a higher column, we take the
" curly indent.
if curly[0] > bracket[0] || curly[1] > bracket[1]
if curly[0] > paren[0] || curly[1] > paren[1]
return curly[1]
endif
endif
" If the curly was not chosen, we take the bracket indent - if
" there was one.
if bracket[0] > paren[0] || bracket[1] > paren[1]
return bracket[1]
endif
" There are neither { nor [ nor (, ie. we are at the toplevel.
if paren == [0, 0]
return 0
endif
" Now we have to reimplement lispindent. This is surprisingly easy, as
" soon as one has access to syntax items.
"
" - Get the next keyword after the (.
" - If its first character is also a (, we have another sexp and align
" one column to the right of the unmatched (.
" - In case it is in lispwords, we indent the next line to the column of
" the ( + sw.
" - If not, we check whether it is last word in the line. In that case
" we again use ( + sw for indent.
" - In any other case we use the column of the end of the word + 2.
call cursor(paren[0] , paren[1])
" In case we are at the last character, we use the paren position.
if col("$") - 1 == paren[1]
return paren[1]
endif
" In case after the paren is a whitespace, we search for the next word.
normal! l
let reg = getreg("l")
normal! "lyl
if getreg("l") == ' '
normal! w
endif
call setreg("l", reg)
" If we moved to another line, there is no word after the (. We
" use the ( position for indent.
if line(".") > paren[0]
return paren[1]
endif
" We still have to check, whether the keyword starts with a (, [ or {.
" In that case we use the ( position for indent.
let reg = getreg("l")
normal! "lye
let w = getreg("l")
call setreg("l", reg)
if stridx('([{', w[0]) > 0
return paren[1]
endif
if &lispwords =~ '\<' . w . '\>'
return paren[1] + &shiftwidth - 1
endif
normal! w
if paren[0] < line(".")
return paren[1] + &shiftwidth - 1
endif
normal! ge
return col(".") + 1
endfunction
setlocal indentexpr=GetClojureIndent()
else
" In case we have searchpairpos not available we fall back to
" normal lisp indenting.
setlocal indentexpr=
setlocal lisp
let b:undo_indent .= " lisp<"
endif
" Defintions:
setlocal lispwords=def,def-,defn,defn-,defmacro,defmacro-,defmethod,defmulti
setlocal lispwords+=defonce,defvar,defvar-,defunbound,let,fn,letfn,binding,proxy
setlocal lispwords+=defnk
" Conditionals and Loops:
setlocal lispwords+=if,if-not,if-let,when,when-not,when-let,when-first
setlocal lispwords+=cond,condp,loop,dotimes,for,while
" Blocks:
setlocal lispwords+=do,doto,try,catch,locking,with-in-str,with-out-str,with-open
setlocal lispwords+=dosync,with-local-vars,doseq,dorun,doall,->,future
" Namespaces:
setlocal lispwords+=ns,clojure.core/ns
" Java Classes:
setlocal lispwords+=gen-class,gen-interface
let &cpo = s:save_cpo