blob: a59141182e341c89eb80ba58773a3c4ec394c726 [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>
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00006" Vincent Aravantinos <firstname.name@imag.fr>
Bram Moolenaara5792f52005-11-23 21:25:05 +00007" URL: http://www.ocaml.info/vim/ftplugin/ocaml.vim
Bram Moolenaar9c754c42010-07-10 15:52:35 +02008" Last Change: 2010 Jul 10 - Bugfix, thanks to Pat Rondon
9" 2008 Jul 17 - Bugfix related to fnameescape (VA)
Bram Moolenaare37d50a2008-08-06 17:06:04 +000010" 2007 Sep 09 - Added .annot support for ocamlbuild, python not
Bram Moolenaar3577c6f2008-06-24 21:16:56 +000011" needed anymore (VA)
12" 2006 May 01 - Added .annot support for file.whateverext (SZ)
13" 2006 Apr 11 - Fixed an initialization bug; fixed ASS abbrev (MM)
Bram Moolenaar4c3f5362006-04-11 21:38:50 +000014" 2005 Oct 13 - removed GPL; better matchit support (MM, SZ)
Bram Moolenaara5792f52005-11-23 21:25:05 +000015"
Bram Moolenaar4c3f5362006-04-11 21:38:50 +000016if exists("b:did_ftplugin")
17 finish
18endif
Bram Moolenaara5792f52005-11-23 21:25:05 +000019let b:did_ftplugin=1
Bram Moolenaar071d4272004-06-13 20:20:40 +000020
Bram Moolenaare37d50a2008-08-06 17:06:04 +000021" some macro
22if exists('*fnameescape')
23 function! s:Fnameescape(s)
24 return fnameescape(a:s)
25 endfun
26else
27 function! s:Fnameescape(s)
28 return escape(a:s," \t\n*?[{`$\\%#'\"|!<")
29 endfun
30endif
31
Bram Moolenaara5792f52005-11-23 21:25:05 +000032" Error handling -- helps moving where the compiler wants you to go
33let s:cposet=&cpoptions
Bram Moolenaar071d4272004-06-13 20:20:40 +000034set cpo-=C
Bram Moolenaar071d4272004-06-13 20:20:40 +000035setlocal efm=
Bram Moolenaara5792f52005-11-23 21:25:05 +000036 \%EFile\ \"%f\"\\,\ line\ %l\\,\ characters\ %c-%*\\d:,
37 \%EFile\ \"%f\"\\,\ line\ %l\\,\ character\ %c:%m,
38 \%+EReference\ to\ unbound\ regexp\ name\ %m,
39 \%Eocamlyacc:\ e\ -\ line\ %l\ of\ \"%f\"\\,\ %m,
40 \%Wocamlyacc:\ w\ -\ %m,
41 \%-Zmake%.%#,
42 \%C%m,
43 \%D%*\\a[%*\\d]:\ Entering\ directory\ `%f',
44 \%X%*\\a[%*\\d]:\ Leaving\ directory\ `%f',
45 \%D%*\\a:\ Entering\ directory\ `%f',
46 \%X%*\\a:\ Leaving\ directory\ `%f',
47 \%DMaking\ %*\\a\ in\ %f
Bram Moolenaar071d4272004-06-13 20:20:40 +000048
49" Add mappings, unless the user didn't want this.
50if !exists("no_plugin_maps") && !exists("no_ocaml_maps")
Bram Moolenaara5792f52005-11-23 21:25:05 +000051 " (un)commenting
Bram Moolenaar071d4272004-06-13 20:20:40 +000052 if !hasmapto('<Plug>Comment')
53 nmap <buffer> <LocalLeader>c <Plug>LUncomOn
54 vmap <buffer> <LocalLeader>c <Plug>BUncomOn
55 nmap <buffer> <LocalLeader>C <Plug>LUncomOff
56 vmap <buffer> <LocalLeader>C <Plug>BUncomOff
57 endif
58
59 nnoremap <buffer> <Plug>LUncomOn mz0i(* <ESC>$A *)<ESC>`z
Bram Moolenaara5792f52005-11-23 21:25:05 +000060 nnoremap <buffer> <Plug>LUncomOff :s/^(\* \(.*\) \*)/\1/<CR>:noh<CR>
Bram Moolenaar071d4272004-06-13 20:20:40 +000061 vnoremap <buffer> <Plug>BUncomOn <ESC>:'<,'><CR>`<O<ESC>0i(*<ESC>`>o<ESC>0i*)<ESC>`<
62 vnoremap <buffer> <Plug>BUncomOff <ESC>:'<,'><CR>`<dd`>dd`<
63
64 if !hasmapto('<Plug>Abbrev')
Bram Moolenaar4c3f5362006-04-11 21:38:50 +000065 iabbrev <buffer> ASS (assert (0=1) (* XXX *))
Bram Moolenaar071d4272004-06-13 20:20:40 +000066 endif
67endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000068
69" Let % jump between structure elements (due to Issac Trotts)
Bram Moolenaara5792f52005-11-23 21:25:05 +000070let b:mw = ''
71let b:mw = b:mw . ',\<let\>:\<and\>:\(\<in\>\|;;\)'
72let b:mw = b:mw . ',\<if\>:\<then\>:\<else\>'
73let b:mw = b:mw . ',\<\(for\|while\)\>:\<do\>:\<done\>,'
74let b:mw = b:mw . ',\<\(object\|sig\|struct\|begin\)\>:\<end\>'
75let b:mw = b:mw . ',\<\(match\|try\)\>:\<with\>'
76let b:match_words = b:mw
77
78let b:match_ignorecase=0
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000079
80" switching between interfaces (.mli) and implementations (.ml)
81if !exists("g:did_ocaml_switch")
82 let g:did_ocaml_switch = 1
Bram Moolenaara5792f52005-11-23 21:25:05 +000083 map <LocalLeader>s :call OCaml_switch(0)<CR>
84 map <LocalLeader>S :call OCaml_switch(1)<CR>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000085 fun OCaml_switch(newwin)
86 if (match(bufname(""), "\\.mli$") >= 0)
Bram Moolenaare37d50a2008-08-06 17:06:04 +000087 let fname = s:Fnameescape(substitute(bufname(""), "\\.mli$", ".ml", ""))
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000088 if (a:newwin == 1)
Bram Moolenaara5792f52005-11-23 21:25:05 +000089 exec "new " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000090 else
Bram Moolenaara5792f52005-11-23 21:25:05 +000091 exec "arge " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000092 endif
93 elseif (match(bufname(""), "\\.ml$") >= 0)
Bram Moolenaare37d50a2008-08-06 17:06:04 +000094 let fname = s:Fnameescape(bufname("")) . "i"
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000095 if (a:newwin == 1)
Bram Moolenaara5792f52005-11-23 21:25:05 +000096 exec "new " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000097 else
Bram Moolenaara5792f52005-11-23 21:25:05 +000098 exec "arge " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000099 endif
100 endif
101 endfun
102endif
103
Bram Moolenaara5792f52005-11-23 21:25:05 +0000104" Folding support
105
106" Get the modeline because folding depends on indentation
107let s:s = line2byte(line('.'))+col('.')-1
108if search('^\s*(\*:o\?caml:')
109 let s:modeline = getline(".")
110else
111 let s:modeline = ""
112endif
113if s:s > 0
114 exe 'goto' s:s
115endif
116
117" Get the indentation params
118let s:m = matchstr(s:modeline,'default\s*=\s*\d\+')
119if s:m != ""
120 let s:idef = matchstr(s:m,'\d\+')
121elseif exists("g:omlet_indent")
122 let s:idef = g:omlet_indent
123else
124 let s:idef = 2
125endif
126let s:m = matchstr(s:modeline,'struct\s*=\s*\d\+')
127if s:m != ""
128 let s:i = matchstr(s:m,'\d\+')
129elseif exists("g:omlet_indent_struct")
130 let s:i = g:omlet_indent_struct
131else
132 let s:i = s:idef
133endif
134
135" Set the folding method
136if exists("g:ocaml_folding")
137 setlocal foldmethod=expr
138 setlocal foldexpr=OMLetFoldLevel(v:lnum)
139endif
140
141" - Only definitions below, executed once -------------------------------------
142
143if exists("*OMLetFoldLevel")
144 finish
145endif
146
147function s:topindent(lnum)
148 let l = a:lnum
149 while l > 0
150 if getline(l) =~ '\s*\%(\<struct\>\|\<sig\>\|\<object\>\)'
151 return indent(l)
152 endif
153 let l = l-1
154 endwhile
155 return -s:i
156endfunction
157
158function OMLetFoldLevel(l)
159
160 " This is for not merging blank lines around folds to them
161 if getline(a:l) !~ '\S'
162 return -1
163 endif
164
165 " We start folds for modules, classes, and every toplevel definition
166 if getline(a:l) =~ '^\s*\%(\<val\>\|\<module\>\|\<class\>\|\<type\>\|\<method\>\|\<initializer\>\|\<inherit\>\|\<exception\>\|\<external\>\)'
167 exe 'return ">' (indent(a:l)/s:i)+1 '"'
168 endif
169
170 " Toplevel let are detected thanks to the indentation
171 if getline(a:l) =~ '^\s*let\>' && indent(a:l) == s:i+s:topindent(a:l)
172 exe 'return ">' (indent(a:l)/s:i)+1 '"'
173 endif
174
175 " We close fold on end which are associated to struct, sig or object.
176 " We use syntax information to do that.
177 if getline(a:l) =~ '^\s*end\>' && synIDattr(synID(a:l, indent(a:l)+1, 0), "name") != "ocamlKeyword"
178 return (indent(a:l)/s:i)+1
179 endif
180
181 " Folds end on ;;
182 if getline(a:l) =~ '^\s*;;'
183 exe 'return "<' (indent(a:l)/s:i)+1 '"'
184 endif
185
186 " Comments around folds aren't merged to them.
187 if synIDattr(synID(a:l, indent(a:l)+1, 0), "name") == "ocamlComment"
188 return -1
189 endif
190
191 return '='
192endfunction
193
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000194" Vim support for OCaml .annot files
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000195"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000196" Last Change: 2007 Jul 17
197" Maintainer: Vincent Aravantinos <vincent.aravantinos@gmail.com>
198" License: public domain
199"
200" Originally inspired by 'ocaml-dtypes.vim' by Stefano Zacchiroli.
201" The source code is quite radically different for we not use python anymore.
202" However this plugin should have the exact same behaviour, that's why the
203" following lines are the quite exact copy of Stefano's original plugin :
204"
205" <<
206" Executing Ocaml_print_type(<mode>) function will display in the Vim bottom
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000207" line(s) the type of an ocaml value getting it from the corresponding .annot
208" file (if any). If Vim is in visual mode, <mode> should be "visual" and the
209" selected ocaml value correspond to the highlighted text, otherwise (<mode>
210" can be anything else) it corresponds to the literal found at the current
211" cursor position.
212"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000213" Typing '<LocalLeader>t' (LocalLeader defaults to '\', see :h LocalLeader)
214" will cause " Ocaml_print_type function to be invoked with the right
215" argument depending on the current mode (visual or not).
216" >>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000217"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000218" If you find something not matching this behaviour, please signal it.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000219"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000220" Differences are:
221" - no need for python support
222" + plus : more portable
223" + minus: no more lazy parsing, it looks very fast however
224"
225" - ocamlbuild support, ie.
226" + the plugin finds the _build directory and looks for the
227" corresponding file inside;
228" + if the user decides to change the name of the _build directory thanks
229" to the '-build-dir' option of ocamlbuild, the plugin will manage in
230" most cases to find it out (most cases = if the source file has a unique
231" name among your whole project);
232" + if ocamlbuild is not used, the usual behaviour holds; ie. the .annot
233" file should be in the same directory as the source file;
234" + for vim plugin programmers:
235" the variable 'b:_build_dir' contains the inferred path to the build
236" directory, even if this one is not named '_build'.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000237"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000238" Bonus :
239" - latin1 accents are handled
240" - lists are handled, even on multiple lines, you don't need the visual mode
241" (the cursor must be on the first bracket)
242" - parenthesized expressions, arrays, and structures (ie. '(...)', '[|...|]',
243" and '{...}') are handled the same way
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000244
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000245 " Copied from Stefano's original plugin :
246 " <<
247 " .annot ocaml file representation
248 "
249 " File format (copied verbatim from caml-types.el)
250 "
251 " file ::= block *
252 " block ::= position <SP> position <LF> annotation *
253 " position ::= filename <SP> num <SP> num <SP> num
254 " annotation ::= keyword open-paren <LF> <SP> <SP> data <LF> close-paren
255 "
256 " <SP> is a space character (ASCII 0x20)
257 " <LF> is a line-feed character (ASCII 0x0A)
258 " num is a sequence of decimal digits
259 " filename is a string with the lexical conventions of O'Caml
260 " open-paren is an open parenthesis (ASCII 0x28)
261 " close-paren is a closed parenthesis (ASCII 0x29)
262 " data is any sequence of characters where <LF> is always followed by
263 " at least two space characters.
264 "
265 " - in each block, the two positions are respectively the start and the
266 " end of the range described by the block.
267 " - in a position, the filename is the name of the file, the first num
268 " is the line number, the second num is the offset of the beginning
269 " of the line, the third num is the offset of the position itself.
270 " - the char number within the line is the difference between the third
271 " and second nums.
272 "
273 " For the moment, the only possible keyword is \"type\"."
274 " >>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000275
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000276
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000277" 1. Finding the annotation file even if we use ocamlbuild
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000278
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000279 " In: two strings representing paths
280 " Out: one string representing the common prefix between the two paths
281 function! s:Find_common_path (p1,p2)
282 let temp = a:p2
283 while matchstr(a:p1,temp) == ''
284 let temp = substitute(temp,'/[^/]*$','','')
285 endwhile
286 return temp
287 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000288
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000289 " After call:
290 " - b:annot_file_path :
291 " path to the .annot file corresponding to the
292 " source file (dealing with ocamlbuild stuff)
293 " - b:_build_path:
294 " path to the build directory even if this one is
295 " not named '_build'
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000296 function! s:Locate_annotation()
297 if !b:annotation_file_located
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000298
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000299 silent exe 'cd' s:Fnameescape(expand('%:p:h'))
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000300
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000301 let annot_file_name = s:Fnameescape(expand('%:r')).'.annot'
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000302
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000303 " 1st case : the annot file is in the same directory as the buffer (no ocamlbuild)
304 let b:annot_file_path = findfile(annot_file_name,'.')
305 if b:annot_file_path != ''
306 let b:annot_file_path = getcwd().'/'.b:annot_file_path
307 let b:_build_path = ''
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000308 else
309 " 2nd case : the buffer and the _build directory are in the same directory
310 " ..
311 " / \
312 " / \
313 " _build .ml
314 "
315 let b:_build_path = finddir('_build','.')
316 if b:_build_path != ''
317 let b:_build_path = getcwd().'/'.b:_build_path
318 let b:annot_file_path = findfile(annot_file_name,'_build')
319 if b:annot_file_path != ''
320 let b:annot_file_path = getcwd().'/'.b:annot_file_path
321 endif
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000322 else
323 " 3rd case : the _build directory is in a directory higher in the file hierarchy
324 " (it can't be deeper by ocamlbuild requirements)
325 " ..
326 " / \
327 " / \
328 " _build ...
329 " \
330 " \
331 " .ml
332 "
333 let b:_build_path = finddir('_build',';')
334 if b:_build_path != ''
335 let project_path = substitute(b:_build_path,'/_build$','','')
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000336 let path_relative_to_project = s:Fnameescape(substitute(expand('%:p:h'),project_path.'/','',''))
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000337 let b:annot_file_path = findfile(annot_file_name,project_path.'/_build/'.path_relative_to_project)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000338 else
339 let b:annot_file_path = findfile(annot_file_name,'**')
340 "4th case : what if the user decided to change the name of the _build directory ?
341 " -> we relax the constraints, it should work in most cases
342 if b:annot_file_path != ''
343 " 4a. we suppose the renamed _build directory is in the current directory
344 let b:_build_path = matchstr(b:annot_file_path,'^[^/]*')
345 if b:annot_file_path != ''
346 let b:annot_file_path = getcwd().'/'.b:annot_file_path
347 let b:_build_path = getcwd().'/'.b:_build_path
348 endif
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000349 else
350 " 4b. anarchy : the renamed _build directory may be higher in the hierarchy
351 " this will work if the file for which we are looking annotations has a unique name in the whole project
352 " if this is not the case, it may still work, but no warranty here
353 let b:annot_file_path = findfile(annot_file_name,'**;')
354 let project_path = s:Find_common_path(b:annot_file_path,expand('%:p:h'))
355 let b:_build_path = matchstr(b:annot_file_path,project_path.'/[^/]*')
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000356 endif
357 endif
358 endif
359 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000360
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000361 if b:annot_file_path == ''
362 throw 'E484: no annotation file found'
363 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000364
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000365 silent exe 'cd' '-'
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000366
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000367 let b:annotation_file_located = 1
368 endif
369 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000370
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000371 " This in order to locate the .annot file only once
372 let b:annotation_file_located = 0
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000373
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000374" 2. Finding the type information in the annotation file
375
376 " a. The annotation file is opened in vim as a buffer that
377 " should be (almost) invisible to the user.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000378
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000379 " After call:
380 " The current buffer is now the one containing the .annot file.
381 " We manage to keep all this hidden to the user's eye.
382 function! s:Enter_annotation_buffer()
383 let s:current_pos = getpos('.')
384 let s:current_hidden = &l:hidden
385 set hidden
386 let s:current_buf = bufname('%')
387 if bufloaded(b:annot_file_path)
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000388 silent exe 'keepj keepalt' 'buffer' s:Fnameescape(b:annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000389 else
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000390 silent exe 'keepj keepalt' 'view' s:Fnameescape(b:annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000391 endif
392 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000393
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000394 " After call:
395 " The original buffer has been restored in the exact same state as before.
396 function! s:Exit_annotation_buffer()
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000397 silent exe 'keepj keepalt' 'buffer' s:Fnameescape(s:current_buf)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000398 let &l:hidden = s:current_hidden
399 call setpos('.',s:current_pos)
400 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000401
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000402 " After call:
403 " The annot file is loaded and assigned to a buffer.
404 " This also handles the modification date of the .annot file, eg. after a
405 " compilation.
406 function! s:Load_annotation()
407 if bufloaded(b:annot_file_path) && b:annot_file_last_mod < getftime(b:annot_file_path)
408 call s:Enter_annotation_buffer()
409 silent exe "bunload"
410 call s:Exit_annotation_buffer()
411 endif
412 if !bufloaded(b:annot_file_path)
413 call s:Enter_annotation_buffer()
414 setlocal nobuflisted
415 setlocal bufhidden=hide
416 setlocal noswapfile
417 setlocal buftype=nowrite
418 call s:Exit_annotation_buffer()
419 let b:annot_file_last_mod = getftime(b:annot_file_path)
420 endif
421 endfun
422
423 "b. 'search' and 'match' work to find the type information
424
425 "In: - lin1,col1: postion of expression first char
426 " - lin2,col2: postion of expression last char
427 "Out: - the pattern to be looked for to find the block
428 " Must be called in the source buffer (use of line2byte)
429 function! s:Block_pattern(lin1,lin2,col1,col2)
430 let start_num1 = a:lin1
431 let start_num2 = line2byte(a:lin1) - 1
432 let start_num3 = start_num2 + a:col1
Bram Moolenaar9c754c42010-07-10 15:52:35 +0200433 let path = '"\(\\"\|[^"]\)\+"'
434 let start_pos = path.' '.start_num1.' '.start_num2.' '.start_num3
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000435 let end_num1 = a:lin2
436 let end_num2 = line2byte(a:lin2) - 1
437 let end_num3 = end_num2 + a:col2
Bram Moolenaar9c754c42010-07-10 15:52:35 +0200438 let end_pos = path.' '.end_num1.' '.end_num2.' '.end_num3
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000439 return '^'.start_pos.' '.end_pos."$"
440 " rq: the '^' here is not totally correct regarding the annot file "grammar"
441 " but currently the annotation file respects this, and it's a little bit faster with the '^';
442 " can be removed safely.
443 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000444
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000445 "In: (the cursor position should be at the start of an annotation)
446 "Out: the type information
447 " Must be called in the annotation buffer (use of search)
448 function! s:Match_data()
449 " rq: idem as previously, in the following, the '^' at start of patterns is not necessary
450 keepj while search('^type($','ce',line(".")) == 0
451 keepj if search('^.\{-}($','e') == 0
452 throw "no_annotation"
453 endif
454 keepj if searchpair('(','',')') == 0
455 throw "malformed_annot_file"
456 endif
457 endwhile
458 let begin = line(".") + 1
459 keepj if searchpair('(','',')') == 0
460 throw "malformed_annot_file"
461 endif
462 let end = line(".") - 1
463 return join(getline(begin,end),"\n")
464 endfun
465
466 "In: the pattern to look for in order to match the block
467 "Out: the type information (calls s:Match_data)
468 " Should be called in the annotation buffer
469 function! s:Extract_type_data(block_pattern)
470 call s:Enter_annotation_buffer()
471 try
472 if search(a:block_pattern,'e') == 0
473 throw "no_annotation"
474 endif
475 call cursor(line(".") + 1,1)
476 let annotation = s:Match_data()
477 finally
478 call s:Exit_annotation_buffer()
479 endtry
480 return annotation
481 endfun
482
483 "c. link this stuff with what the user wants
484 " ie. get the expression selected/under the cursor
485
486 let s:ocaml_word_char = '\w|[À-ÿ]|'''
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000487
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000488 "In: the current mode (eg. "visual", "normal", etc.)
489 "Out: the borders of the expression we are looking for the type
490 function! s:Match_borders(mode)
491 if a:mode == "visual"
492 let cur = getpos(".")
493 normal `<
494 let col1 = col(".")
495 let lin1 = line(".")
496 normal `>
497 let col2 = col(".")
498 let lin2 = line(".")
499 call cursor(cur[1],cur[2])
500 return [lin1,lin2,col1-1,col2]
501 else
502 let cursor_line = line(".")
503 let cursor_col = col(".")
504 let line = getline('.')
505 if line[cursor_col-1:cursor_col] == '[|'
506 let [lin2,col2] = searchpairpos('\[|','','|\]','n')
507 return [cursor_line,lin2,cursor_col-1,col2+1]
508 elseif line[cursor_col-1] == '['
509 let [lin2,col2] = searchpairpos('\[','','\]','n')
510 return [cursor_line,lin2,cursor_col-1,col2]
511 elseif line[cursor_col-1] == '('
512 let [lin2,col2] = searchpairpos('(','',')','n')
513 return [cursor_line,lin2,cursor_col-1,col2]
514 elseif line[cursor_col-1] == '{'
515 let [lin2,col2] = searchpairpos('{','','}','n')
516 return [cursor_line,lin2,cursor_col-1,col2]
517 else
518 let [lin1,col1] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','ncb')
519 let [lin2,col2] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','nce')
520 if col1 == 0 || col2 == 0
521 throw "no_expression"
522 endif
523 return [cursor_line,cursor_line,col1-1,col2]
524 endif
525 endif
526 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000527
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000528 "In: the current mode (eg. "visual", "normal", etc.)
529 "Out: the type information (calls s:Extract_type_data)
530 function! s:Get_type(mode)
531 let [lin1,lin2,col1,col2] = s:Match_borders(a:mode)
532 return s:Extract_type_data(s:Block_pattern(lin1,lin2,col1,col2))
533 endfun
534
535 "d. main
536 "In: the current mode (eg. "visual", "normal", etc.)
537 "After call: the type information is displayed
538 if !exists("*Ocaml_get_type")
539 function Ocaml_get_type(mode)
540 call s:Locate_annotation()
541 call s:Load_annotation()
542 return s:Get_type(a:mode)
543 endfun
544 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000545
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000546 if !exists("*Ocaml_get_type_or_not")
547 function Ocaml_get_type_or_not(mode)
548 let t=reltime()
549 try
550 return Ocaml_get_type(a:mode)
551 catch
552 return ""
553 endtry
554 endfun
555 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000556
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000557 if !exists("*Ocaml_print_type")
558 function Ocaml_print_type(mode)
559 if expand("%:e") == "mli"
560 echohl ErrorMsg | echo "No annotations for interface (.mli) files" | echohl None
561 return
562 endif
563 try
564 echo Ocaml_get_type(a:mode)
565 catch /E484:/
566 echohl ErrorMsg | echo "No type annotations (.annot) file found" | echohl None
567 catch /no_expression/
568 echohl ErrorMsg | echo "No expression found under the cursor" | echohl None
569 catch /no_annotation/
570 echohl ErrorMsg | echo "No type annotation found for the given text" | echohl None
571 catch /malformed_annot_file/
572 echohl ErrorMsg | echo "Malformed .annot file" | echohl None
573 endtry
574 endfun
575 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000576
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000577" Maps
578 map <silent> <LocalLeader>t :call Ocaml_print_type("normal")<CR>
579 vmap <silent> <LocalLeader>t :<C-U>call Ocaml_print_type("visual")<CR>`<
Bram Moolenaara5792f52005-11-23 21:25:05 +0000580
581let &cpoptions=s:cposet
582unlet s:cposet
583
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000584" vim:sw=2 fdm=indent