1" Vim settings file 2" Language: OCaml 3" Maintainers: Mike Leary <[email protected]> 4" Markus Mottl <[email protected]> 5" Stefano Zacchiroli <[email protected]> 6" URL: http://www.oefai.at/~markus/vim/ftplugin/ocaml.vim 7" Last Change: 2004 Apr 12 - better .ml/.mli-switching without Python (SZ) 8" 2003 Nov 21 - match_words-patterns and .ml/.mli-switching (MM) 9" 2003 Oct 16 - re-entered variable 'did_ocaml_dtypes' (MM) 10" 2003 Oct 15 - added Stefano Zacchirolis (SZ) Python-code for 11" displaying type annotations (MM) 12 13" Only do these settings when not done yet for this buffer 14if exists("b:did_ftplugin") 15 finish 16endif 17 18" Don't do other file type settings for this buffer 19let b:did_ftplugin = 1 20 21set cpo-=C 22 23" Error formats 24setlocal efm= 25 \%EFile\ \"%f\"\\,\ line\ %l\\,\ characters\ %c-%*\\d:, 26 \%EFile\ \"%f\"\\,\ line\ %l\\,\ character\ %c:%m, 27 \%+EReference\ to\ unbound\ regexp\ name\ %m, 28 \%Eocamlyacc:\ e\ -\ line\ %l\ of\ \"%f\"\\,\ %m, 29 \%Wocamlyacc:\ w\ -\ %m, 30 \%-Zmake%.%#, 31 \%C%m 32 33" Add mappings, unless the user didn't want this. 34if !exists("no_plugin_maps") && !exists("no_ocaml_maps") 35 " Uncommenting 36 if !hasmapto('<Plug>Comment') 37 nmap <buffer> <LocalLeader>c <Plug>LUncomOn 38 vmap <buffer> <LocalLeader>c <Plug>BUncomOn 39 nmap <buffer> <LocalLeader>C <Plug>LUncomOff 40 vmap <buffer> <LocalLeader>C <Plug>BUncomOff 41 endif 42 43 nnoremap <buffer> <Plug>LUncomOn mz0i(* <ESC>$A *)<ESC>`z 44 nnoremap <buffer> <Plug>LUncomOff <ESC>:s/^(\* \(.*\) \*)/\1/<CR> 45 vnoremap <buffer> <Plug>BUncomOn <ESC>:'<,'><CR>`<O<ESC>0i(*<ESC>`>o<ESC>0i*)<ESC>`< 46 vnoremap <buffer> <Plug>BUncomOff <ESC>:'<,'><CR>`<dd`>dd`< 47 48 if !hasmapto('<Plug>Abbrev') 49 iabbrev <buffer> ASS (assert false) 50 endif 51endif 52 53" Let % jump between structure elements (due to Issac Trotts) 54let b:mw='\<let\>:\<and\>:\(\<in\>\|;;\),' 55let b:mw=b:mw . '\<if\>:\<then\>:\<else\>,\<do\>:\<done\>,' 56let b:mw=b:mw . '\<\(object\|sig\|struct\|begin\)\>:\<end\>' 57let b:match_words=b:mw 58 59" switching between interfaces (.mli) and implementations (.ml) 60if !exists("g:did_ocaml_switch") 61 let g:did_ocaml_switch = 1 62 map ,s :call OCaml_switch(0)<CR> 63 map ,S :call OCaml_switch(1)<CR> 64 fun OCaml_switch(newwin) 65 if (match(bufname(""), "\\.mli$") >= 0) 66 let fname = substitute(bufname(""), "\\.mli$", ".ml", "") 67 if (a:newwin == 1) 68 exec "new " . fname 69 else 70 exec "arge " . fname 71 endif 72 elseif (match(bufname(""), "\\.ml$") >= 0) 73 let fname = bufname("") . "i" 74 if (a:newwin == 1) 75 exec "new " . fname 76 else 77 exec "arge " . fname 78 endif 79 endif 80 endfun 81endif 82 83" Vim support for OCaml 3.07 .annot files (requires Vim with python support) 84" 85" Executing OCamlPrintType(<mode>) function will display in the Vim bottom 86" line(s) the type of an ocaml value getting it from the corresponding .annot 87" file (if any). If Vim is in visual mode, <mode> should be "visual" and the 88" selected ocaml value correspond to the highlighted text, otherwise (<mode> 89" can be anything else) it corresponds to the literal found at the current 90" cursor position. 91" 92" .annot files are parsed lazily the first time OCamlPrintType is invoked; is 93" also possible to force the parsing using the OCamlParseAnnot() function. 94" 95" Hitting the <F3> key will cause OCamlPrintType function to be invoked with 96" the right argument depending on the current mode (visual or not). 97" 98" Copyright (C) <2003> Stefano Zacchiroli <[email protected]> 99" 100" Created: Wed, 01 Oct 2003 18:16:22 +0200 zack 101" LastModified: Mon, 06 Oct 2003 11:05:39 +0200 zack 102" 103" This program is free software; you can redistribute it and/or modify 104" it under the terms of the GNU General Public License as published by 105" the Free Software Foundation; either version 2 of the License, or 106" (at your option) any later version. 107" 108" This program is distributed in the hope that it will be useful, 109" but WITHOUT ANY WARRANTY; without even the implied warranty of 110" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 111" GNU General Public License for more details. 112" 113" You should have received a copy of the GNU General Public License 114" along with this program; if not, write to the Free Software 115" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 116" 117 118if !has("python") 119 echo "Python support not found: OCaml .annot support disabled" 120 finish 121endif 122 123if !exists("g:did_ocaml_dtypes") 124 let g:did_ocaml_dtypes = 1 125else 126 finish 127endif 128 129python << EOF 130 131import re 132import os 133import string 134import time 135import vim 136 137debug = False 138 139class AnnExc(Exception): 140 def __init__(self, reason): 141 self.reason = reason 142 143no_annotations = AnnExc("No type annotations (.annot) file found") 144annotation_not_found = AnnExc("No type annotation found for the given text") 145def malformed_annotations(lineno): 146 return AnnExc("Malformed .annot file (line = %d)" % lineno) 147 148class Annotations: 149 """ 150 .annot ocaml file representation 151 152 File format (copied verbatim from caml-types.el) 153 154 file ::= block * 155 block ::= position <SP> position <LF> annotation * 156 position ::= filename <SP> num <SP> num <SP> num 157 annotation ::= keyword open-paren <LF> <SP> <SP> data <LF> close-paren 158 159 <SP> is a space character (ASCII 0x20) 160 <LF> is a line-feed character (ASCII 0x0A) 161 num is a sequence of decimal digits 162 filename is a string with the lexical conventions of O'Caml 163 open-paren is an open parenthesis (ASCII 0x28) 164 close-paren is a closed parenthesis (ASCII 0x29) 165 data is any sequence of characters where <LF> is always followed by 166 at least two space characters. 167 168 - in each block, the two positions are respectively the start and the 169 - end of the range described by the block. 170 - in a position, the filename is the name of the file, the first num 171 is the line number, the second num is the offset of the beginning 172 of the line, the third num is the offset of the position itself. 173 - the char number within the line is the difference between the third 174 and second nums. 175 176 For the moment, the only possible keyword is \"type\"." 177 """ 178 179 def __init__(self): 180 self.__filename = None # last .annot parsed file 181 self.__ml_filename = None # as above but s/.annot/.ml/ 182 self.__timestamp = None # last parse action timestamp 183 self.__annot = {} 184 self.__re = re.compile( 185 '^"[^"]+"\s+(\d+)\s+(\d+)\s+(\d+)\s+"[^"]+"\s+(\d+)\s+(\d+)\s+(\d+)$') 186 187 def __parse(self, fname): 188 try: 189 f = open(fname) 190 line = f.readline() # position line 191 lineno = 1 192 while (line != ""): 193 m = self.__re.search(line) 194 if (not m): 195 raise malformed_annotations(lineno) 196 line1 = int(m.group(1)) 197 col1 = int(m.group(3)) - int(m.group(2)) 198 line2 = int(m.group(4)) 199 col2 = int(m.group(6)) - int(m.group(5)) 200 line = f.readline() # "type(" string 201 lineno += 1 202 if (line == ""): raise malformed_annotations(lineno) 203 type = [] 204 line = f.readline() # type description 205 lineno += 1 206 if (line == ""): raise malformed_annotations(lineno) 207 while line != ")\n": 208 type.append(string.strip(line)) 209 line = f.readline() 210 lineno += 1 211 if (line == ""): raise malformed_annotations(lineno) 212 type = string.join(type, "\n") 213 self.__annot[(line1, col1), (line2, col2)] = type 214 line = f.readline() # position line 215 f.close() 216 self.__filename = fname 217 self.__ml_filename = re.sub("\.annot$", ".ml", fname) 218 self.__timestamp = int(time.time()) 219 except IOError: 220 raise no_annotations 221 222 def parse(self): 223 annot_file = re.sub("\.ml$", ".annot", vim.current.buffer.name) 224 self.__parse(annot_file) 225 226 def get_type(self, (line1, col1), (line2, col2)): 227 if debug: 228 print line1, col1, line2, col2 229 if vim.current.buffer.name == None: 230 raise no_annotations 231 if vim.current.buffer.name != self.__ml_filename or \ 232 os.stat(self.__filename).st_mtime > self.__timestamp: 233 self.parse() 234 try: 235 return self.__annot[(line1, col1), (line2, col2)] 236 except KeyError: 237 raise annotation_not_found 238 239word_char_RE = re.compile("^[\w.]$") 240 241 # TODO this function should recognize ocaml literals, actually it's just an 242 # hack that recognize continuous sequences of word_char_RE above 243def findBoundaries(line, col): 244 """ given a cursor position (as returned by vim.current.window.cursor) 245 return two integers identify the beggining and end column of the word at 246 cursor position, if any. If no word is at the cursor position return the 247 column cursor position twice """ 248 left, right = col, col 249 line = line - 1 # mismatch vim/python line indexes 250 (begin_col, end_col) = (0, len(vim.current.buffer[line]) - 1) 251 try: 252 while word_char_RE.search(vim.current.buffer[line][left - 1]): 253 left = left - 1 254 except IndexError: 255 pass 256 try: 257 while word_char_RE.search(vim.current.buffer[line][right + 1]): 258 right = right + 1 259 except IndexError: 260 pass 261 return (left, right) 262 263annot = Annotations() # global annotation object 264 265def printOCamlType(mode): 266 try: 267 if mode == "visual": # visual mode: lookup highlighted text 268 (line1, col1) = vim.current.buffer.mark("<") 269 (line2, col2) = vim.current.buffer.mark(">") 270 else: # any other mode: lookup word at cursor position 271 (line, col) = vim.current.window.cursor 272 (col1, col2) = findBoundaries(line, col) 273 (line1, line2) = (line, line) 274 begin_mark = (line1, col1) 275 end_mark = (line2, col2 + 1) 276 print annot.get_type(begin_mark, end_mark) 277 except AnnExc, exc: 278 print exc.reason 279 280def parseOCamlAnnot(): 281 try: 282 annot.parse() 283 except AnnExc, exc: 284 print exc.reason 285 286EOF 287 288fun OCamlPrintType(current_mode) 289 if (a:current_mode == "visual") 290 python printOCamlType("visual") 291 else 292 python printOCamlType("normal") 293 endif 294endfun 295 296fun OCamlParseAnnot() 297 python parseOCamlAnnot() 298endfun 299 300map <F3> :call OCamlPrintType("normal")<RETURN> 301vmap <F3> :call OCamlPrintType("visual")<RETURN> 302