blob: 0b9819a9b6e0ceaa47229d7ec4ec5ab8ce352461 [file] [log] [blame]
Bram Moolenaara5792f52005-11-23 21:25:05 +00001" Language: OCaml
2" Maintainer: David Baelde <firstname.name@ens-lyon.org>
3" Mike Leary <leary@nwlink.com>
4" Markus Mottl <markus.mottl@gmail.com>
5" Stefano Zacchiroli <zack@bononia.it>
6" URL: http://www.ocaml.info/vim/ftplugin/ocaml.vim
Bram Moolenaar4c3f5362006-04-11 21:38:50 +00007" Last Change: 2006 Apr 11 - Fixed an initialization bug; fixed ASS abbrev (MM)
8" 2005 Oct 13 - removed GPL; better matchit support (MM, SZ)
Bram Moolenaara5792f52005-11-23 21:25:05 +00009"
Bram Moolenaar4c3f5362006-04-11 21:38:50 +000010if exists("b:did_ftplugin")
11 finish
12endif
Bram Moolenaara5792f52005-11-23 21:25:05 +000013let b:did_ftplugin=1
Bram Moolenaar071d4272004-06-13 20:20:40 +000014
Bram Moolenaara5792f52005-11-23 21:25:05 +000015" Error handling -- helps moving where the compiler wants you to go
16let s:cposet=&cpoptions
Bram Moolenaar071d4272004-06-13 20:20:40 +000017set cpo-=C
Bram Moolenaar071d4272004-06-13 20:20:40 +000018setlocal efm=
Bram Moolenaara5792f52005-11-23 21:25:05 +000019 \%EFile\ \"%f\"\\,\ line\ %l\\,\ characters\ %c-%*\\d:,
20 \%EFile\ \"%f\"\\,\ line\ %l\\,\ character\ %c:%m,
21 \%+EReference\ to\ unbound\ regexp\ name\ %m,
22 \%Eocamlyacc:\ e\ -\ line\ %l\ of\ \"%f\"\\,\ %m,
23 \%Wocamlyacc:\ w\ -\ %m,
24 \%-Zmake%.%#,
25 \%C%m,
26 \%D%*\\a[%*\\d]:\ Entering\ directory\ `%f',
27 \%X%*\\a[%*\\d]:\ Leaving\ directory\ `%f',
28 \%D%*\\a:\ Entering\ directory\ `%f',
29 \%X%*\\a:\ Leaving\ directory\ `%f',
30 \%DMaking\ %*\\a\ in\ %f
Bram Moolenaar071d4272004-06-13 20:20:40 +000031
32" Add mappings, unless the user didn't want this.
33if !exists("no_plugin_maps") && !exists("no_ocaml_maps")
Bram Moolenaara5792f52005-11-23 21:25:05 +000034 " (un)commenting
Bram Moolenaar071d4272004-06-13 20:20:40 +000035 if !hasmapto('<Plug>Comment')
36 nmap <buffer> <LocalLeader>c <Plug>LUncomOn
37 vmap <buffer> <LocalLeader>c <Plug>BUncomOn
38 nmap <buffer> <LocalLeader>C <Plug>LUncomOff
39 vmap <buffer> <LocalLeader>C <Plug>BUncomOff
40 endif
41
42 nnoremap <buffer> <Plug>LUncomOn mz0i(* <ESC>$A *)<ESC>`z
Bram Moolenaara5792f52005-11-23 21:25:05 +000043 nnoremap <buffer> <Plug>LUncomOff :s/^(\* \(.*\) \*)/\1/<CR>:noh<CR>
Bram Moolenaar071d4272004-06-13 20:20:40 +000044 vnoremap <buffer> <Plug>BUncomOn <ESC>:'<,'><CR>`<O<ESC>0i(*<ESC>`>o<ESC>0i*)<ESC>`<
45 vnoremap <buffer> <Plug>BUncomOff <ESC>:'<,'><CR>`<dd`>dd`<
46
47 if !hasmapto('<Plug>Abbrev')
Bram Moolenaar4c3f5362006-04-11 21:38:50 +000048 iabbrev <buffer> ASS (assert (0=1) (* XXX *))
Bram Moolenaar071d4272004-06-13 20:20:40 +000049 endif
50endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000051
52" Let % jump between structure elements (due to Issac Trotts)
Bram Moolenaara5792f52005-11-23 21:25:05 +000053let b:mw = ''
54let b:mw = b:mw . ',\<let\>:\<and\>:\(\<in\>\|;;\)'
55let b:mw = b:mw . ',\<if\>:\<then\>:\<else\>'
56let b:mw = b:mw . ',\<\(for\|while\)\>:\<do\>:\<done\>,'
57let b:mw = b:mw . ',\<\(object\|sig\|struct\|begin\)\>:\<end\>'
58let b:mw = b:mw . ',\<\(match\|try\)\>:\<with\>'
59let b:match_words = b:mw
60
61let b:match_ignorecase=0
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000062
63" switching between interfaces (.mli) and implementations (.ml)
64if !exists("g:did_ocaml_switch")
65 let g:did_ocaml_switch = 1
Bram Moolenaara5792f52005-11-23 21:25:05 +000066 map <LocalLeader>s :call OCaml_switch(0)<CR>
67 map <LocalLeader>S :call OCaml_switch(1)<CR>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000068 fun OCaml_switch(newwin)
69 if (match(bufname(""), "\\.mli$") >= 0)
70 let fname = substitute(bufname(""), "\\.mli$", ".ml", "")
71 if (a:newwin == 1)
Bram Moolenaara5792f52005-11-23 21:25:05 +000072 exec "new " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000073 else
Bram Moolenaara5792f52005-11-23 21:25:05 +000074 exec "arge " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000075 endif
76 elseif (match(bufname(""), "\\.ml$") >= 0)
77 let fname = bufname("") . "i"
78 if (a:newwin == 1)
Bram Moolenaara5792f52005-11-23 21:25:05 +000079 exec "new " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000080 else
Bram Moolenaara5792f52005-11-23 21:25:05 +000081 exec "arge " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000082 endif
83 endif
84 endfun
85endif
86
Bram Moolenaara5792f52005-11-23 21:25:05 +000087" Folding support
88
89" Get the modeline because folding depends on indentation
90let s:s = line2byte(line('.'))+col('.')-1
91if search('^\s*(\*:o\?caml:')
92 let s:modeline = getline(".")
93else
94 let s:modeline = ""
95endif
96if s:s > 0
97 exe 'goto' s:s
98endif
99
100" Get the indentation params
101let s:m = matchstr(s:modeline,'default\s*=\s*\d\+')
102if s:m != ""
103 let s:idef = matchstr(s:m,'\d\+')
104elseif exists("g:omlet_indent")
105 let s:idef = g:omlet_indent
106else
107 let s:idef = 2
108endif
109let s:m = matchstr(s:modeline,'struct\s*=\s*\d\+')
110if s:m != ""
111 let s:i = matchstr(s:m,'\d\+')
112elseif exists("g:omlet_indent_struct")
113 let s:i = g:omlet_indent_struct
114else
115 let s:i = s:idef
116endif
117
118" Set the folding method
119if exists("g:ocaml_folding")
120 setlocal foldmethod=expr
121 setlocal foldexpr=OMLetFoldLevel(v:lnum)
122endif
123
124" - Only definitions below, executed once -------------------------------------
125
126if exists("*OMLetFoldLevel")
127 finish
128endif
129
130function s:topindent(lnum)
131 let l = a:lnum
132 while l > 0
133 if getline(l) =~ '\s*\%(\<struct\>\|\<sig\>\|\<object\>\)'
134 return indent(l)
135 endif
136 let l = l-1
137 endwhile
138 return -s:i
139endfunction
140
141function OMLetFoldLevel(l)
142
143 " This is for not merging blank lines around folds to them
144 if getline(a:l) !~ '\S'
145 return -1
146 endif
147
148 " We start folds for modules, classes, and every toplevel definition
149 if getline(a:l) =~ '^\s*\%(\<val\>\|\<module\>\|\<class\>\|\<type\>\|\<method\>\|\<initializer\>\|\<inherit\>\|\<exception\>\|\<external\>\)'
150 exe 'return ">' (indent(a:l)/s:i)+1 '"'
151 endif
152
153 " Toplevel let are detected thanks to the indentation
154 if getline(a:l) =~ '^\s*let\>' && indent(a:l) == s:i+s:topindent(a:l)
155 exe 'return ">' (indent(a:l)/s:i)+1 '"'
156 endif
157
158 " We close fold on end which are associated to struct, sig or object.
159 " We use syntax information to do that.
160 if getline(a:l) =~ '^\s*end\>' && synIDattr(synID(a:l, indent(a:l)+1, 0), "name") != "ocamlKeyword"
161 return (indent(a:l)/s:i)+1
162 endif
163
164 " Folds end on ;;
165 if getline(a:l) =~ '^\s*;;'
166 exe 'return "<' (indent(a:l)/s:i)+1 '"'
167 endif
168
169 " Comments around folds aren't merged to them.
170 if synIDattr(synID(a:l, indent(a:l)+1, 0), "name") == "ocamlComment"
171 return -1
172 endif
173
174 return '='
175endfunction
176
177" Vim support for OCaml .annot files (requires Vim with python support)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000178"
179" Executing OCamlPrintType(<mode>) function will display in the Vim bottom
180" line(s) the type of an ocaml value getting it from the corresponding .annot
181" file (if any). If Vim is in visual mode, <mode> should be "visual" and the
182" selected ocaml value correspond to the highlighted text, otherwise (<mode>
183" can be anything else) it corresponds to the literal found at the current
184" cursor position.
185"
186" .annot files are parsed lazily the first time OCamlPrintType is invoked; is
187" also possible to force the parsing using the OCamlParseAnnot() function.
188"
Bram Moolenaara5792f52005-11-23 21:25:05 +0000189" Typing ',3' will cause OCamlPrintType function to be invoked with
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000190" the right argument depending on the current mode (visual or not).
191"
Bram Moolenaara5792f52005-11-23 21:25:05 +0000192" Copyright (C) <2003-2004> Stefano Zacchiroli <zack@bononia.it>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000193"
194" Created: Wed, 01 Oct 2003 18:16:22 +0200 zack
Bram Moolenaara5792f52005-11-23 21:25:05 +0000195" LastModified: Wed, 25 Aug 2004 18:28:39 +0200 zack
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000196
197if !has("python")
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000198 finish
199endif
200
201python << EOF
202
203import re
204import os
205import string
206import time
207import vim
208
209debug = False
210
211class AnnExc(Exception):
212 def __init__(self, reason):
213 self.reason = reason
214
215no_annotations = AnnExc("No type annotations (.annot) file found")
216annotation_not_found = AnnExc("No type annotation found for the given text")
217def malformed_annotations(lineno):
218 return AnnExc("Malformed .annot file (line = %d)" % lineno)
219
220class Annotations:
221 """
222 .annot ocaml file representation
223
224 File format (copied verbatim from caml-types.el)
225
226 file ::= block *
227 block ::= position <SP> position <LF> annotation *
228 position ::= filename <SP> num <SP> num <SP> num
229 annotation ::= keyword open-paren <LF> <SP> <SP> data <LF> close-paren
230
231 <SP> is a space character (ASCII 0x20)
232 <LF> is a line-feed character (ASCII 0x0A)
233 num is a sequence of decimal digits
234 filename is a string with the lexical conventions of O'Caml
235 open-paren is an open parenthesis (ASCII 0x28)
236 close-paren is a closed parenthesis (ASCII 0x29)
237 data is any sequence of characters where <LF> is always followed by
238 at least two space characters.
239
240 - in each block, the two positions are respectively the start and the
Bram Moolenaara5792f52005-11-23 21:25:05 +0000241 end of the range described by the block.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000242 - in a position, the filename is the name of the file, the first num
243 is the line number, the second num is the offset of the beginning
244 of the line, the third num is the offset of the position itself.
245 - the char number within the line is the difference between the third
246 and second nums.
247
248 For the moment, the only possible keyword is \"type\"."
249 """
250
251 def __init__(self):
252 self.__filename = None # last .annot parsed file
253 self.__ml_filename = None # as above but s/.annot/.ml/
254 self.__timestamp = None # last parse action timestamp
255 self.__annot = {}
256 self.__re = re.compile(
Bram Moolenaara5792f52005-11-23 21:25:05 +0000257 '^"[^"]*"\s+(\d+)\s+(\d+)\s+(\d+)\s+"[^"]*"\s+(\d+)\s+(\d+)\s+(\d+)$')
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000258
259 def __parse(self, fname):
260 try:
261 f = open(fname)
262 line = f.readline() # position line
263 lineno = 1
264 while (line != ""):
265 m = self.__re.search(line)
266 if (not m):
267 raise malformed_annotations(lineno)
268 line1 = int(m.group(1))
269 col1 = int(m.group(3)) - int(m.group(2))
270 line2 = int(m.group(4))
271 col2 = int(m.group(6)) - int(m.group(5))
272 line = f.readline() # "type(" string
273 lineno += 1
274 if (line == ""): raise malformed_annotations(lineno)
275 type = []
276 line = f.readline() # type description
277 lineno += 1
278 if (line == ""): raise malformed_annotations(lineno)
279 while line != ")\n":
280 type.append(string.strip(line))
281 line = f.readline()
282 lineno += 1
283 if (line == ""): raise malformed_annotations(lineno)
284 type = string.join(type, "\n")
Bram Moolenaara5792f52005-11-23 21:25:05 +0000285 key = ((line1, col1), (line2, col2))
286 if not self.__annot.has_key(key):
287 self.__annot[key] = type
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000288 line = f.readline() # position line
289 f.close()
290 self.__filename = fname
291 self.__ml_filename = re.sub("\.annot$", ".ml", fname)
292 self.__timestamp = int(time.time())
293 except IOError:
294 raise no_annotations
295
296 def parse(self):
297 annot_file = re.sub("\.ml$", ".annot", vim.current.buffer.name)
298 self.__parse(annot_file)
299
300 def get_type(self, (line1, col1), (line2, col2)):
301 if debug:
302 print line1, col1, line2, col2
303 if vim.current.buffer.name == None:
304 raise no_annotations
305 if vim.current.buffer.name != self.__ml_filename or \
306 os.stat(self.__filename).st_mtime > self.__timestamp:
307 self.parse()
308 try:
309 return self.__annot[(line1, col1), (line2, col2)]
310 except KeyError:
311 raise annotation_not_found
312
313word_char_RE = re.compile("^[\w.]$")
314
315 # TODO this function should recognize ocaml literals, actually it's just an
316 # hack that recognize continuous sequences of word_char_RE above
317def findBoundaries(line, col):
318 """ given a cursor position (as returned by vim.current.window.cursor)
319 return two integers identify the beggining and end column of the word at
320 cursor position, if any. If no word is at the cursor position return the
321 column cursor position twice """
322 left, right = col, col
323 line = line - 1 # mismatch vim/python line indexes
324 (begin_col, end_col) = (0, len(vim.current.buffer[line]) - 1)
325 try:
326 while word_char_RE.search(vim.current.buffer[line][left - 1]):
327 left = left - 1
328 except IndexError:
329 pass
330 try:
331 while word_char_RE.search(vim.current.buffer[line][right + 1]):
332 right = right + 1
333 except IndexError:
334 pass
335 return (left, right)
336
337annot = Annotations() # global annotation object
338
339def printOCamlType(mode):
340 try:
341 if mode == "visual": # visual mode: lookup highlighted text
342 (line1, col1) = vim.current.buffer.mark("<")
343 (line2, col2) = vim.current.buffer.mark(">")
344 else: # any other mode: lookup word at cursor position
345 (line, col) = vim.current.window.cursor
346 (col1, col2) = findBoundaries(line, col)
347 (line1, line2) = (line, line)
348 begin_mark = (line1, col1)
349 end_mark = (line2, col2 + 1)
350 print annot.get_type(begin_mark, end_mark)
351 except AnnExc, exc:
352 print exc.reason
353
354def parseOCamlAnnot():
355 try:
356 annot.parse()
357 except AnnExc, exc:
358 print exc.reason
359
360EOF
361
Bram Moolenaara5792f52005-11-23 21:25:05 +0000362fun! OCamlPrintType(current_mode)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000363 if (a:current_mode == "visual")
364 python printOCamlType("visual")
365 else
366 python printOCamlType("normal")
367 endif
368endfun
369
Bram Moolenaara5792f52005-11-23 21:25:05 +0000370fun! OCamlParseAnnot()
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000371 python parseOCamlAnnot()
372endfun
373
Bram Moolenaara5792f52005-11-23 21:25:05 +0000374map <LocalLeader>t :call OCamlPrintType("normal")<RETURN>
375vmap <LocalLeader>t :call OCamlPrintType("visual")<RETURN>
376
377let &cpoptions=s:cposet
378unlet s:cposet
379
380" vim:sw=2