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