Skip to content

Commit c06750a

Browse files
committed
Inherit the indent of the previous element at the same sexp level
The builtin 'lisp' indenter does this. See src/misc1.c:get_lisp_indent() Includes test case! Addresses #47
1 parent 82a5781 commit c06750a

File tree

4 files changed

+84
-15
lines changed

4 files changed

+84
-15
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
(foo bar [✖])
2+
3+
;; vim:ft=clojure:
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
(foo bar [
2+
a
3+
b
4+
5+
c])
6+
7+
;; vim:ft=clojure:
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
(ns vim-clojure-static.indent-test
2+
(:require [clojure.test :refer [deftest]]
3+
[vim-clojure-static.test :refer [test-indent]]))
4+
5+
(deftest test-indentation
6+
(test-indent "is inherited from previous element"
7+
:in "test-inherit-indent.in"
8+
:out "test-inherit-indent.out"
9+
:keys "/✖\\<Esc>s\\<CR>\\<C-H>\\<C-H>\\<C-H>\\<C-H>a\\<CR>b\\<CR>\\<CR>c\\<Esc>"))

indent/clojure.vim

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -192,17 +192,23 @@ if exists("*searchpairpos")
192192
return val
193193
endfunction
194194

195-
function! GetClojureIndent()
195+
" Returns 1 for opening brackets, -1 for _anything else_.
196+
function! s:bracket_type(char)
197+
return stridx('([{', a:char) > -1 ? 1 : -1
198+
endfunction
199+
200+
" Returns: [opening-bracket-lnum, indent]
201+
function! s:clojure_indent_pos()
196202
" Get rid of special case.
197203
if line(".") == 1
198-
return 0
204+
return [0, 0]
199205
endif
200206

201207
" We have to apply some heuristics here to figure out, whether to use
202208
" normal lisp indenting or not.
203209
let i = s:check_for_string()
204210
if i > -1
205-
return i + !!g:clojure_align_multiline_strings
211+
return [0, i + !!g:clojure_align_multiline_strings]
206212
endif
207213

208214
call cursor(0, 1)
@@ -219,19 +225,19 @@ if exists("*searchpairpos")
219225
" curly indent.
220226
if curly[0] > bracket[0] || curly[1] > bracket[1]
221227
if curly[0] > paren[0] || curly[1] > paren[1]
222-
return curly[1]
228+
return curly
223229
endif
224230
endif
225231

226232
" If the curly was not chosen, we take the bracket indent - if
227233
" there was one.
228234
if bracket[0] > paren[0] || bracket[1] > paren[1]
229-
return bracket[1]
235+
return bracket
230236
endif
231237

232238
" There are neither { nor [ nor (, ie. we are at the toplevel.
233239
if paren == [0, 0]
234-
return 0
240+
return paren
235241
endif
236242

237243
" Now we have to reimplement lispindent. This is surprisingly easy, as
@@ -250,12 +256,12 @@ if exists("*searchpairpos")
250256
call cursor(paren)
251257

252258
if s:is_method_special_case(paren)
253-
return paren[1] + &shiftwidth - 1
259+
return [paren[0], paren[1] + &shiftwidth - 1]
254260
endif
255261

256262
" In case we are at the last character, we use the paren position.
257263
if col("$") - 1 == paren[1]
258-
return paren[1]
264+
return paren
259265
endif
260266

261267
" In case after the paren is a whitespace, we search for the next word.
@@ -267,14 +273,14 @@ if exists("*searchpairpos")
267273
" If we moved to another line, there is no word after the (. We
268274
" use the ( position for indent.
269275
if line(".") > paren[0]
270-
return paren[1]
276+
return paren
271277
endif
272278

273279
" We still have to check, whether the keyword starts with a (, [ or {.
274280
" In that case we use the ( position for indent.
275281
let w = s:current_word()
276-
if stridx('([{', w[0]) > -1
277-
return paren[1]
282+
if s:bracket_type(w[0]) == 1
283+
return paren
278284
endif
279285

280286
" Test words without namespace qualifiers and leading reader macro
@@ -284,23 +290,67 @@ if exists("*searchpairpos")
284290
let ww = s:strip_namespace_and_macro_chars(w)
285291

286292
if &lispwords =~# '\V\<' . ww . '\>'
287-
return paren[1] + &shiftwidth - 1
293+
return [paren[0], paren[1] + &shiftwidth - 1]
288294
endif
289295

290296
if g:clojure_fuzzy_indent
291297
\ && !s:match_one(g:clojure_fuzzy_indent_blacklist, ww)
292298
\ && s:match_one(g:clojure_fuzzy_indent_patterns, ww)
293-
return paren[1] + &shiftwidth - 1
299+
return [paren[0], paren[1] + &shiftwidth - 1]
294300
endif
295301

296302
call search('\v\_s', 'cW')
297303
call search('\v\S', 'W')
298304
if paren[0] < line(".")
299-
return paren[1] + (g:clojure_align_subforms ? 0 : &shiftwidth - 1)
305+
return [paren[0], paren[1] + (g:clojure_align_subforms ? 0 : &shiftwidth - 1)]
300306
endif
301307

302308
call search('\v\S', 'bW')
303-
return virtcol(".") + 1
309+
return [line('.'), virtcol('.') + 1]
310+
endfunction
311+
312+
function! GetClojureIndent()
313+
let lnum = line('.')
314+
let [opening_lnum, indent] = s:clojure_indent_pos()
315+
316+
" Return if there are no previous lines to inherit from
317+
if opening_lnum < 1 || opening_lnum >= lnum - 1
318+
return indent
319+
endif
320+
321+
let bracket_count = 0
322+
323+
" Take the indent of the first previous non-white line that is
324+
" at the same sexp level. cf. src/misc1.c:get_lisp_indent()
325+
while 1
326+
let lnum = prevnonblank(lnum - 1)
327+
let col = 1
328+
329+
if lnum <= opening_lnum
330+
break
331+
endif
332+
333+
call cursor(lnum, col)
334+
335+
" Handle bracket counting edge case
336+
if s:is_paren()
337+
let bracket_count += s:bracket_type(s:current_char())
338+
endif
339+
340+
while 1
341+
if search('\v[(\[{}\])]', '', lnum) < 1
342+
break
343+
elseif !s:ignored_region()
344+
let bracket_count += s:bracket_type(s:current_char())
345+
endif
346+
endwhile
347+
348+
if bracket_count == 0
349+
return indent(lnum)
350+
endif
351+
endwhile
352+
353+
return indent
304354
endfunction
305355

306356
setlocal indentexpr=GetClojureIndent()

0 commit comments

Comments
 (0)