Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 1 | " Vim indent file |
Bram Moolenaar | 942db23 | 2021-02-13 18:14:48 +0100 | [diff] [blame] | 2 | " Language: Clojure |
| 3 | " Maintainer: Alex Vear <av@axvr.io> |
| 4 | " Former Maintainers: Sung Pae <self@sungpae.com> |
| 5 | " Meikel Brandmeyer <mb@kotka.de> |
| 6 | " URL: https://github.com/clojure-vim/clojure.vim |
| 7 | " License: Vim (see :h license) |
| 8 | " Last Change: 2021-02-13 |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 9 | |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 10 | if exists("b:did_indent") |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 11 | finish |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 12 | endif |
| 13 | let b:did_indent = 1 |
| 14 | |
| 15 | let s:save_cpo = &cpo |
| 16 | set cpo&vim |
| 17 | |
Bram Moolenaar | a687837 | 2014-03-22 21:02:50 +0100 | [diff] [blame] | 18 | let b:undo_indent = 'setlocal autoindent< smartindent< expandtab< softtabstop< shiftwidth< indentexpr< indentkeys<' |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 19 | |
| 20 | setlocal noautoindent nosmartindent |
| 21 | setlocal softtabstop=2 shiftwidth=2 expandtab |
| 22 | setlocal indentkeys=!,o,O |
| 23 | |
| 24 | if exists("*searchpairpos") |
| 25 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 26 | if !exists('g:clojure_maxlines') |
| 27 | let g:clojure_maxlines = 100 |
| 28 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 29 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 30 | if !exists('g:clojure_fuzzy_indent') |
| 31 | let g:clojure_fuzzy_indent = 1 |
| 32 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 33 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 34 | if !exists('g:clojure_fuzzy_indent_patterns') |
| 35 | let g:clojure_fuzzy_indent_patterns = ['^with', '^def', '^let'] |
| 36 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 37 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 38 | if !exists('g:clojure_fuzzy_indent_blacklist') |
| 39 | let g:clojure_fuzzy_indent_blacklist = ['-fn$', '\v^with-%(meta|out-str|loading-context)$'] |
| 40 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 41 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 42 | if !exists('g:clojure_special_indent_words') |
| 43 | let g:clojure_special_indent_words = 'deftype,defrecord,reify,proxy,extend-type,extend-protocol,letfn' |
| 44 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 45 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 46 | if !exists('g:clojure_align_multiline_strings') |
| 47 | let g:clojure_align_multiline_strings = 0 |
| 48 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 49 | |
Bram Moolenaar | 438f67a | 2014-01-07 06:09:28 +0100 | [diff] [blame] | 50 | if !exists('g:clojure_align_subforms') |
| 51 | let g:clojure_align_subforms = 0 |
| 52 | endif |
| 53 | |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 54 | function! s:syn_id_name() |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 55 | return synIDattr(synID(line("."), col("."), 0), "name") |
| 56 | endfunction |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 57 | |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 58 | function! s:ignored_region() |
| 59 | return s:syn_id_name() =~? '\vstring|regex|comment|character' |
| 60 | endfunction |
| 61 | |
| 62 | function! s:current_char() |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 63 | return getline('.')[col('.')-1] |
| 64 | endfunction |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 65 | |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 66 | function! s:current_word() |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 67 | return getline('.')[col('.')-1 : searchpos('\v>', 'n', line('.'))[1]-2] |
| 68 | endfunction |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 69 | |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 70 | function! s:is_paren() |
| 71 | return s:current_char() =~# '\v[\(\)\[\]\{\}]' && !s:ignored_region() |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 72 | endfunction |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 73 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 74 | " Returns 1 if string matches a pattern in 'patterns', which may be a |
| 75 | " list of patterns, or a comma-delimited string of implicitly anchored |
| 76 | " patterns. |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 77 | function! s:match_one(patterns, string) |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 78 | let list = type(a:patterns) == type([]) |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 79 | \ ? a:patterns |
| 80 | \ : map(split(a:patterns, ','), '"^" . v:val . "$"') |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 81 | for pat in list |
Bram Moolenaar | 76f3b1a | 2014-03-27 22:30:07 +0100 | [diff] [blame] | 82 | if a:string =~# pat | return 1 | endif |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 83 | endfor |
| 84 | endfunction |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 85 | |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 86 | function! s:match_pairs(open, close, stopat) |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 87 | " Stop only on vector and map [ resp. {. Ignore the ones in strings and |
| 88 | " comments. |
Bram Moolenaar | 942db23 | 2021-02-13 18:14:48 +0100 | [diff] [blame] | 89 | if a:stopat == 0 && g:clojure_maxlines > 0 |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 90 | let stopat = max([line(".") - g:clojure_maxlines, 0]) |
| 91 | else |
| 92 | let stopat = a:stopat |
| 93 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 94 | |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 95 | let pos = searchpairpos(a:open, '', a:close, 'bWn', "!s:is_paren()", stopat) |
| 96 | return [pos[0], col(pos)] |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 97 | endfunction |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 98 | |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 99 | function! s:clojure_check_for_string_worker() |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 100 | " Check whether there is the last character of the previous line is |
| 101 | " highlighted as a string. If so, we check whether it's a ". In this |
| 102 | " case we have to check also the previous character. The " might be the |
| 103 | " closing one. In case the we are still in the string, we search for the |
| 104 | " opening ". If this is not found we take the indent of the line. |
| 105 | let nb = prevnonblank(v:lnum - 1) |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 106 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 107 | if nb == 0 |
| 108 | return -1 |
| 109 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 110 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 111 | call cursor(nb, 0) |
| 112 | call cursor(0, col("$") - 1) |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 113 | if s:syn_id_name() !~? "string" |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 114 | return -1 |
| 115 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 116 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 117 | " This will not work for a " in the first column... |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 118 | if s:current_char() == '"' |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 119 | call cursor(0, col("$") - 2) |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 120 | if s:syn_id_name() !~? "string" |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 121 | return -1 |
| 122 | endif |
Bram Moolenaar | 942db23 | 2021-02-13 18:14:48 +0100 | [diff] [blame] | 123 | if s:current_char() != '\' |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 124 | return -1 |
| 125 | endif |
| 126 | call cursor(0, col("$") - 1) |
| 127 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 128 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 129 | let p = searchpos('\(^\|[^\\]\)\zs"', 'bW') |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 130 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 131 | if p != [0, 0] |
| 132 | return p[1] - 1 |
| 133 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 134 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 135 | return indent(".") |
| 136 | endfunction |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 137 | |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 138 | function! s:check_for_string() |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 139 | let pos = getpos('.') |
| 140 | try |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 141 | let val = s:clojure_check_for_string_worker() |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 142 | finally |
| 143 | call setpos('.', pos) |
| 144 | endtry |
| 145 | return val |
| 146 | endfunction |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 147 | |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 148 | function! s:strip_namespace_and_macro_chars(word) |
Bram Moolenaar | 76f3b1a | 2014-03-27 22:30:07 +0100 | [diff] [blame] | 149 | return substitute(a:word, "\\v%(.*/|[#'`~@^,]*)(.*)", '\1', '') |
| 150 | endfunction |
| 151 | |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 152 | function! s:clojure_is_method_special_case_worker(position) |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 153 | " Find the next enclosing form. |
| 154 | call search('\S', 'Wb') |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 155 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 156 | " Special case: we are at a '(('. |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 157 | if s:current_char() == '(' |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 158 | return 0 |
| 159 | endif |
| 160 | call cursor(a:position) |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 161 | |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 162 | let next_paren = s:match_pairs('(', ')', 0) |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 163 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 164 | " Special case: we are now at toplevel. |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 165 | if next_paren == [0, 0] |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 166 | return 0 |
| 167 | endif |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 168 | call cursor(next_paren) |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 169 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 170 | call search('\S', 'W') |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 171 | let w = s:strip_namespace_and_macro_chars(s:current_word()) |
Bram Moolenaar | 942db23 | 2021-02-13 18:14:48 +0100 | [diff] [blame] | 172 | |
Bram Moolenaar | 76f3b1a | 2014-03-27 22:30:07 +0100 | [diff] [blame] | 173 | if g:clojure_special_indent_words =~# '\V\<' . w . '\>' |
Bram Moolenaar | 942db23 | 2021-02-13 18:14:48 +0100 | [diff] [blame] | 174 | |
| 175 | " `letfn` is a special-special-case. |
| 176 | if w ==# 'letfn' |
| 177 | " Earlier code left the cursor at: |
| 178 | " (letfn [...] ...) |
| 179 | " ^ |
| 180 | |
| 181 | " Search and get coordinates of first `[` |
| 182 | " (letfn [...] ...) |
| 183 | " ^ |
| 184 | call search('\[', 'W') |
| 185 | let pos = getcurpos() |
| 186 | let letfn_bracket = [pos[1], pos[2]] |
| 187 | |
| 188 | " Move cursor to start of the form this function was |
| 189 | " initially called on. Grab the coordinates of the |
| 190 | " closest outer `[`. |
| 191 | call cursor(a:position) |
| 192 | let outer_bracket = s:match_pairs('\[', '\]', 0) |
| 193 | |
| 194 | " If the located square brackets are not the same, |
| 195 | " don't use special-case formatting. |
| 196 | if outer_bracket != letfn_bracket |
| 197 | return 0 |
| 198 | endif |
| 199 | endif |
| 200 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 201 | return 1 |
| 202 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 203 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 204 | return 0 |
| 205 | endfunction |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 206 | |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 207 | function! s:is_method_special_case(position) |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 208 | let pos = getpos('.') |
| 209 | try |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 210 | let val = s:clojure_is_method_special_case_worker(a:position) |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 211 | finally |
| 212 | call setpos('.', pos) |
| 213 | endtry |
| 214 | return val |
| 215 | endfunction |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 216 | |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 217 | " Check if form is a reader conditional, that is, it is prefixed by #? |
| 218 | " or @#? |
| 219 | function! s:is_reader_conditional_special_case(position) |
Bram Moolenaar | 942db23 | 2021-02-13 18:14:48 +0100 | [diff] [blame] | 220 | return getline(a:position[0])[a:position[1] - 3 : a:position[1] - 2] == "#?" |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 221 | endfunction |
| 222 | |
| 223 | " Returns 1 for opening brackets, -1 for _anything else_. |
| 224 | function! s:bracket_type(char) |
| 225 | return stridx('([{', a:char) > -1 ? 1 : -1 |
| 226 | endfunction |
| 227 | |
| 228 | " Returns: [opening-bracket-lnum, indent] |
| 229 | function! s:clojure_indent_pos() |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 230 | " Get rid of special case. |
| 231 | if line(".") == 1 |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 232 | return [0, 0] |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 233 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 234 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 235 | " We have to apply some heuristics here to figure out, whether to use |
| 236 | " normal lisp indenting or not. |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 237 | let i = s:check_for_string() |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 238 | if i > -1 |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 239 | return [0, i + !!g:clojure_align_multiline_strings] |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 240 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 241 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 242 | call cursor(0, 1) |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 243 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 244 | " Find the next enclosing [ or {. We can limit the second search |
| 245 | " to the line, where the [ was found. If no [ was there this is |
| 246 | " zero and we search for an enclosing {. |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 247 | let paren = s:match_pairs('(', ')', 0) |
| 248 | let bracket = s:match_pairs('\[', '\]', paren[0]) |
| 249 | let curly = s:match_pairs('{', '}', bracket[0]) |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 250 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 251 | " In case the curly brace is on a line later then the [ or - in |
| 252 | " case they are on the same line - in a higher column, we take the |
| 253 | " curly indent. |
| 254 | if curly[0] > bracket[0] || curly[1] > bracket[1] |
| 255 | if curly[0] > paren[0] || curly[1] > paren[1] |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 256 | return curly |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 257 | endif |
| 258 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 259 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 260 | " If the curly was not chosen, we take the bracket indent - if |
| 261 | " there was one. |
| 262 | if bracket[0] > paren[0] || bracket[1] > paren[1] |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 263 | return bracket |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 264 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 265 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 266 | " There are neither { nor [ nor (, ie. we are at the toplevel. |
| 267 | if paren == [0, 0] |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 268 | return paren |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 269 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 270 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 271 | " Now we have to reimplement lispindent. This is surprisingly easy, as |
| 272 | " soon as one has access to syntax items. |
| 273 | " |
| 274 | " - Check whether we are in a special position after a word in |
| 275 | " g:clojure_special_indent_words. These are special cases. |
| 276 | " - Get the next keyword after the (. |
| 277 | " - If its first character is also a (, we have another sexp and align |
| 278 | " one column to the right of the unmatched (. |
| 279 | " - In case it is in lispwords, we indent the next line to the column of |
| 280 | " the ( + sw. |
| 281 | " - If not, we check whether it is last word in the line. In that case |
| 282 | " we again use ( + sw for indent. |
| 283 | " - In any other case we use the column of the end of the word + 2. |
| 284 | call cursor(paren) |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 285 | |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 286 | if s:is_method_special_case(paren) |
Bram Moolenaar | 942db23 | 2021-02-13 18:14:48 +0100 | [diff] [blame] | 287 | return [paren[0], paren[1] + &shiftwidth - 1] |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 288 | endif |
| 289 | |
| 290 | if s:is_reader_conditional_special_case(paren) |
| 291 | return paren |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 292 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 293 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 294 | " In case we are at the last character, we use the paren position. |
| 295 | if col("$") - 1 == paren[1] |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 296 | return paren |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 297 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 298 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 299 | " In case after the paren is a whitespace, we search for the next word. |
| 300 | call cursor(0, col('.') + 1) |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 301 | if s:current_char() == ' ' |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 302 | call search('\v\S', 'W') |
| 303 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 304 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 305 | " If we moved to another line, there is no word after the (. We |
| 306 | " use the ( position for indent. |
| 307 | if line(".") > paren[0] |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 308 | return paren |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 309 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 310 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 311 | " We still have to check, whether the keyword starts with a (, [ or {. |
| 312 | " In that case we use the ( position for indent. |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 313 | let w = s:current_word() |
| 314 | if s:bracket_type(w[0]) == 1 |
| 315 | return paren |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 316 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 317 | |
Bram Moolenaar | 942db23 | 2021-02-13 18:14:48 +0100 | [diff] [blame] | 318 | " If the keyword begins with #, check if it is an anonymous |
| 319 | " function or set, in which case we indent by the shiftwidth |
| 320 | " (minus one if g:clojure_align_subforms = 1), or if it is |
| 321 | " ignored, in which case we use the ( position for indent. |
| 322 | if w[0] == "#" |
| 323 | " TODO: Handle #=() and other rare reader invocations? |
| 324 | if w[1] == '(' || w[1] == '{' |
| 325 | return [paren[0], paren[1] + (g:clojure_align_subforms ? 0 : &shiftwidth - 1)] |
| 326 | elseif w[1] == '_' |
| 327 | return paren |
| 328 | endif |
| 329 | endif |
| 330 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 331 | " Test words without namespace qualifiers and leading reader macro |
| 332 | " metacharacters. |
| 333 | " |
| 334 | " e.g. clojure.core/defn and #'defn should both indent like defn. |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 335 | let ww = s:strip_namespace_and_macro_chars(w) |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 336 | |
Bram Moolenaar | 76f3b1a | 2014-03-27 22:30:07 +0100 | [diff] [blame] | 337 | if &lispwords =~# '\V\<' . ww . '\>' |
Bram Moolenaar | 942db23 | 2021-02-13 18:14:48 +0100 | [diff] [blame] | 338 | return [paren[0], paren[1] + &shiftwidth - 1] |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 339 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 340 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 341 | if g:clojure_fuzzy_indent |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 342 | \ && !s:match_one(g:clojure_fuzzy_indent_blacklist, ww) |
| 343 | \ && s:match_one(g:clojure_fuzzy_indent_patterns, ww) |
Bram Moolenaar | 942db23 | 2021-02-13 18:14:48 +0100 | [diff] [blame] | 344 | return [paren[0], paren[1] + &shiftwidth - 1] |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 345 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 346 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 347 | call search('\v\_s', 'cW') |
| 348 | call search('\v\S', 'W') |
| 349 | if paren[0] < line(".") |
Bram Moolenaar | 942db23 | 2021-02-13 18:14:48 +0100 | [diff] [blame] | 350 | return [paren[0], paren[1] + (g:clojure_align_subforms ? 0 : &shiftwidth - 1)] |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 351 | endif |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 352 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 353 | call search('\v\S', 'bW') |
Bram Moolenaar | 6f1d9a0 | 2016-07-24 14:12:38 +0200 | [diff] [blame] | 354 | return [line('.'), col('.') + 1] |
| 355 | endfunction |
| 356 | |
| 357 | function! GetClojureIndent() |
| 358 | let lnum = line('.') |
| 359 | let orig_lnum = lnum |
| 360 | let orig_col = col('.') |
| 361 | let [opening_lnum, indent] = s:clojure_indent_pos() |
| 362 | |
| 363 | " Account for multibyte characters |
| 364 | if opening_lnum > 0 |
| 365 | let indent -= indent - virtcol([opening_lnum, indent]) |
| 366 | endif |
| 367 | |
| 368 | " Return if there are no previous lines to inherit from |
| 369 | if opening_lnum < 1 || opening_lnum >= lnum - 1 |
| 370 | call cursor(orig_lnum, orig_col) |
| 371 | return indent |
| 372 | endif |
| 373 | |
| 374 | let bracket_count = 0 |
| 375 | |
| 376 | " Take the indent of the first previous non-white line that is |
| 377 | " at the same sexp level. cf. src/misc1.c:get_lisp_indent() |
| 378 | while 1 |
| 379 | let lnum = prevnonblank(lnum - 1) |
| 380 | let col = 1 |
| 381 | |
| 382 | if lnum <= opening_lnum |
| 383 | break |
| 384 | endif |
| 385 | |
| 386 | call cursor(lnum, col) |
| 387 | |
| 388 | " Handle bracket counting edge case |
| 389 | if s:is_paren() |
| 390 | let bracket_count += s:bracket_type(s:current_char()) |
| 391 | endif |
| 392 | |
| 393 | while 1 |
| 394 | if search('\v[(\[{}\])]', '', lnum) < 1 |
| 395 | break |
| 396 | elseif !s:ignored_region() |
| 397 | let bracket_count += s:bracket_type(s:current_char()) |
| 398 | endif |
| 399 | endwhile |
| 400 | |
| 401 | if bracket_count == 0 |
| 402 | " Check if this is part of a multiline string |
| 403 | call cursor(lnum, 1) |
| 404 | if s:syn_id_name() !~? '\vstring|regex' |
| 405 | call cursor(orig_lnum, orig_col) |
| 406 | return indent(lnum) |
| 407 | endif |
| 408 | endif |
| 409 | endwhile |
| 410 | |
| 411 | call cursor(orig_lnum, orig_col) |
| 412 | return indent |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 413 | endfunction |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 414 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 415 | setlocal indentexpr=GetClojureIndent() |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 416 | |
| 417 | else |
| 418 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 419 | " In case we have searchpairpos not available we fall back to |
| 420 | " normal lisp indenting. |
| 421 | setlocal indentexpr= |
| 422 | setlocal lisp |
| 423 | let b:undo_indent .= '| setlocal lisp<' |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 424 | |
| 425 | endif |
| 426 | |
Bram Moolenaar | fa13eef | 2013-02-06 17:34:04 +0100 | [diff] [blame] | 427 | let &cpo = s:save_cpo |
| 428 | unlet! s:save_cpo |
| 429 | |
Bram Moolenaar | baca7f7 | 2013-09-22 14:42:24 +0200 | [diff] [blame] | 430 | " vim:sts=8:sw=8:ts=8:noet |