clojure.vim (5448B)
1 " Vim indent file 2 " Language: Clojure 3 " Maintainer: Meikel Brandmeyer <mb@kotka.de> 4 " URL: http://kotka.de/projects/clojure/vimclojure.html 5 6 " Only load this indent file when no other was loaded. 7 if exists("b:did_indent") 8 finish 9 endif 10 let b:did_indent = 1 11 12 let s:save_cpo = &cpo 13 set cpo&vim 14 15 let b:undo_indent = "setlocal ai< si< lw< et< sts< sw< inde< indk<" 16 17 setlocal autoindent expandtab nosmartindent 18 19 setlocal softtabstop=2 20 setlocal shiftwidth=2 21 22 setlocal indentkeys=!,o,O 23 24 if exists("*searchpairpos") 25 26 function! s:MatchPairs(open, close, stopat) 27 " Stop only on vector and map [ resp. {. Ignore the ones in strings and 28 " comments. 29 return searchpairpos(a:open, '', a:close, 'bWn', 30 \ 'vimclojure#SynIdName() !~ "clojureParen\\d"', 31 \ a:stopat) 32 endfunction 33 34 function! VimClojureCheckForStringWorker() 35 " Check whether there is the last character of the previous line is 36 " highlighted as a string. If so, we check whether it's a ". In this 37 " case we have to check also the previous character. The " might be the 38 " closing one. In case the we are still in the string, we search for the 39 " opening ". If this is not found we take the indent of the line. 40 let nb = prevnonblank(v:lnum - 1) 41 42 if nb == 0 43 return -1 44 endif 45 46 call cursor(nb, 0) 47 call cursor(0, col("$") - 1) 48 if vimclojure#SynIdName() != "clojureString" 49 return -1 50 endif 51 52 " This will not work for a " in the first column... 53 if vimclojure#Yank('l', 'normal! "lyl') == '"' 54 call cursor(0, col("$") - 2) 55 if vimclojure#SynIdName() != "clojureString" 56 return -1 57 endif 58 if vimclojure#Yank('l', 'normal! "lyl') != '\\' 59 return -1 60 endif 61 call cursor(0, col("$") - 1) 62 endif 63 64 let p = searchpos('\(^\|[^\\]\)\zs"', 'bW') 65 66 if p != [0, 0] 67 return p[1] - 1 68 endif 69 70 return indent(".") 71 endfunction 72 73 function! VimClojureCheckForString() 74 return vimclojure#WithSavedPosition({'f': function("VimClojureCheckForStringWorker")}) 75 endfunction 76 77 function! GetClojureIndent() 78 " Get rid of special case. 79 if line(".") == 1 80 return 0 81 endif 82 83 " We have to apply some heuristics here to figure out, whether to use 84 " normal lisp indenting or not. 85 let i = VimClojureCheckForString() 86 if i > -1 87 return i 88 endif 89 90 call cursor(0, 1) 91 92 " Find the next enclosing [ or {. We can limit the second search 93 " to the line, where the [ was found. If no [ was there this is 94 " zero and we search for an enclosing {. 95 let paren = s:MatchPairs('(', ')', 0) 96 let bracket = s:MatchPairs('\[', '\]', paren[0]) 97 let curly = s:MatchPairs('{', '}', bracket[0]) 98 99 " In case the curly brace is on a line later then the [ or - in 100 " case they are on the same line - in a higher column, we take the 101 " curly indent. 102 if curly[0] > bracket[0] || curly[1] > bracket[1] 103 if curly[0] > paren[0] || curly[1] > paren[1] 104 return curly[1] 105 endif 106 endif 107 108 " If the curly was not chosen, we take the bracket indent - if 109 " there was one. 110 if bracket[0] > paren[0] || bracket[1] > paren[1] 111 return bracket[1] 112 endif 113 114 " There are neither { nor [ nor (, ie. we are at the toplevel. 115 if paren == [0, 0] 116 return 0 117 endif 118 119 " Now we have to reimplement lispindent. This is surprisingly easy, as 120 " soon as one has access to syntax items. 121 " 122 " - Get the next keyword after the (. 123 " - If its first character is also a (, we have another sexp and align 124 " one column to the right of the unmatched (. 125 " - In case it is in lispwords, we indent the next line to the column of 126 " the ( + sw. 127 " - If not, we check whether it is last word in the line. In that case 128 " we again use ( + sw for indent. 129 " - In any other case we use the column of the end of the word + 2. 130 call cursor(paren[0] , paren[1]) 131 132 " In case we are at the last character, we use the paren position. 133 if col("$") - 1 == paren[1] 134 return paren[1] 135 endif 136 137 " In case after the paren is a whitespace, we search for the next word. 138 normal! l 139 let reg = getreg("l") 140 normal! "lyl 141 if getreg("l") == ' ' 142 normal! w 143 endif 144 call setreg("l", reg) 145 146 " If we moved to another line, there is no word after the (. We 147 " use the ( position for indent. 148 if line(".") > paren[0] 149 return paren[1] 150 endif 151 152 " We still have to check, whether the keyword starts with a (, [ or {. 153 " In that case we use the ( position for indent. 154 let reg = getreg("l") 155 normal! "lye 156 let w = getreg("l") 157 call setreg("l", reg) 158 if stridx('([{', w[0]) > 0 159 return paren[1] 160 endif 161 162 if &lispwords =~ '\<' . w . '\>' 163 return paren[1] + &shiftwidth - 1 164 endif 165 166 normal! w 167 if paren[0] < line(".") 168 return paren[1] + &shiftwidth - 1 169 endif 170 171 normal! ge 172 return col(".") + 1 173 endfunction 174 175 setlocal indentexpr=GetClojureIndent() 176 177 else 178 179 " In case we have searchpairpos not available we fall back to 180 " normal lisp indenting. 181 setlocal indentexpr= 182 setlocal lisp 183 let b:undo_indent .= " lisp<" 184 185 endif 186 187 " Defintions: 188 setlocal lispwords=def,def-,defn,defn-,defmacro,defmacro-,defmethod,defmulti 189 setlocal lispwords+=defonce,defvar,defvar-,defunbound,let,fn,letfn,binding,proxy 190 setlocal lispwords+=defnk 191 192 " Conditionals and Loops: 193 setlocal lispwords+=if,if-not,if-let,when,when-not,when-let,when-first 194 setlocal lispwords+=cond,condp,loop,dotimes,for,while 195 196 " Blocks: 197 setlocal lispwords+=do,doto,try,catch,locking,with-in-str,with-out-str,with-open 198 setlocal lispwords+=dosync,with-local-vars,doseq,dorun,doall,->,future 199 200 " Namespaces: 201 setlocal lispwords+=ns,clojure.core/ns 202 203 " Java Classes: 204 setlocal lispwords+=gen-class,gen-interface 205 206 let &cpo = s:save_cpo