blob: c379f534054b476275a694a577a36a83406d62aa [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>
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +01005" Pierre Vittet <pierre-vittet@pvittet.com>
Bram Moolenaara5792f52005-11-23 21:25:05 +00006" Stefano Zacchiroli <zack@bononia.it>
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00007" Vincent Aravantinos <firstname.name@imag.fr>
Bram Moolenaara5792f52005-11-23 21:25:05 +00008" URL: http://www.ocaml.info/vim/ftplugin/ocaml.vim
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +01009" Last Change:
10" 2012 Jan 15 - Bugfix :reloading .annot file does not close
11" splitted view (Pierre Vittet)
12" 2011 Nov 28 - Bugfix + support of multiple ml annotation file
13" (Pierre Vittet)
14" 2010 Jul 10 - Bugfix, thanks to Pat Rondon
Bram Moolenaar9c754c42010-07-10 15:52:35 +020015" 2008 Jul 17 - Bugfix related to fnameescape (VA)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +010016
Bram Moolenaar4c3f5362006-04-11 21:38:50 +000017if exists("b:did_ftplugin")
18 finish
19endif
Bram Moolenaara5792f52005-11-23 21:25:05 +000020let b:did_ftplugin=1
Bram Moolenaar071d4272004-06-13 20:20:40 +000021
Bram Moolenaare37d50a2008-08-06 17:06:04 +000022" some macro
23if exists('*fnameescape')
24 function! s:Fnameescape(s)
25 return fnameescape(a:s)
26 endfun
27else
28 function! s:Fnameescape(s)
29 return escape(a:s," \t\n*?[{`$\\%#'\"|!<")
30 endfun
31endif
32
Bram Moolenaara5792f52005-11-23 21:25:05 +000033" Error handling -- helps moving where the compiler wants you to go
34let s:cposet=&cpoptions
Bram Moolenaarf1568ec2011-12-14 21:17:39 +010035set cpo&vim
Bram Moolenaar071d4272004-06-13 20:20:40 +000036setlocal efm=
Bram Moolenaara5792f52005-11-23 21:25:05 +000037 \%EFile\ \"%f\"\\,\ line\ %l\\,\ characters\ %c-%*\\d:,
38 \%EFile\ \"%f\"\\,\ line\ %l\\,\ character\ %c:%m,
39 \%+EReference\ to\ unbound\ regexp\ name\ %m,
40 \%Eocamlyacc:\ e\ -\ line\ %l\ of\ \"%f\"\\,\ %m,
41 \%Wocamlyacc:\ w\ -\ %m,
42 \%-Zmake%.%#,
43 \%C%m,
44 \%D%*\\a[%*\\d]:\ Entering\ directory\ `%f',
45 \%X%*\\a[%*\\d]:\ Leaving\ directory\ `%f',
46 \%D%*\\a:\ Entering\ directory\ `%f',
47 \%X%*\\a:\ Leaving\ directory\ `%f',
48 \%DMaking\ %*\\a\ in\ %f
Bram Moolenaar071d4272004-06-13 20:20:40 +000049
50" Add mappings, unless the user didn't want this.
51if !exists("no_plugin_maps") && !exists("no_ocaml_maps")
Bram Moolenaara5792f52005-11-23 21:25:05 +000052 " (un)commenting
Bram Moolenaar071d4272004-06-13 20:20:40 +000053 if !hasmapto('<Plug>Comment')
54 nmap <buffer> <LocalLeader>c <Plug>LUncomOn
55 vmap <buffer> <LocalLeader>c <Plug>BUncomOn
56 nmap <buffer> <LocalLeader>C <Plug>LUncomOff
57 vmap <buffer> <LocalLeader>C <Plug>BUncomOff
58 endif
59
60 nnoremap <buffer> <Plug>LUncomOn mz0i(* <ESC>$A *)<ESC>`z
Bram Moolenaara5792f52005-11-23 21:25:05 +000061 nnoremap <buffer> <Plug>LUncomOff :s/^(\* \(.*\) \*)/\1/<CR>:noh<CR>
Bram Moolenaar071d4272004-06-13 20:20:40 +000062 vnoremap <buffer> <Plug>BUncomOn <ESC>:'<,'><CR>`<O<ESC>0i(*<ESC>`>o<ESC>0i*)<ESC>`<
63 vnoremap <buffer> <Plug>BUncomOff <ESC>:'<,'><CR>`<dd`>dd`<
64
65 if !hasmapto('<Plug>Abbrev')
Bram Moolenaar4c3f5362006-04-11 21:38:50 +000066 iabbrev <buffer> ASS (assert (0=1) (* XXX *))
Bram Moolenaar071d4272004-06-13 20:20:40 +000067 endif
68endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000069
70" Let % jump between structure elements (due to Issac Trotts)
Bram Moolenaara5792f52005-11-23 21:25:05 +000071let b:mw = ''
72let b:mw = b:mw . ',\<let\>:\<and\>:\(\<in\>\|;;\)'
73let b:mw = b:mw . ',\<if\>:\<then\>:\<else\>'
74let b:mw = b:mw . ',\<\(for\|while\)\>:\<do\>:\<done\>,'
75let b:mw = b:mw . ',\<\(object\|sig\|struct\|begin\)\>:\<end\>'
76let b:mw = b:mw . ',\<\(match\|try\)\>:\<with\>'
77let b:match_words = b:mw
78
79let b:match_ignorecase=0
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000080
81" switching between interfaces (.mli) and implementations (.ml)
82if !exists("g:did_ocaml_switch")
83 let g:did_ocaml_switch = 1
Bram Moolenaara5792f52005-11-23 21:25:05 +000084 map <LocalLeader>s :call OCaml_switch(0)<CR>
85 map <LocalLeader>S :call OCaml_switch(1)<CR>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000086 fun OCaml_switch(newwin)
87 if (match(bufname(""), "\\.mli$") >= 0)
Bram Moolenaare37d50a2008-08-06 17:06:04 +000088 let fname = s:Fnameescape(substitute(bufname(""), "\\.mli$", ".ml", ""))
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000089 if (a:newwin == 1)
Bram Moolenaara5792f52005-11-23 21:25:05 +000090 exec "new " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000091 else
Bram Moolenaara5792f52005-11-23 21:25:05 +000092 exec "arge " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000093 endif
94 elseif (match(bufname(""), "\\.ml$") >= 0)
Bram Moolenaare37d50a2008-08-06 17:06:04 +000095 let fname = s:Fnameescape(bufname("")) . "i"
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000096 if (a:newwin == 1)
Bram Moolenaara5792f52005-11-23 21:25:05 +000097 exec "new " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000098 else
Bram Moolenaara5792f52005-11-23 21:25:05 +000099 exec "arge " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000100 endif
101 endif
102 endfun
103endif
104
Bram Moolenaara5792f52005-11-23 21:25:05 +0000105" Folding support
106
107" Get the modeline because folding depends on indentation
108let s:s = line2byte(line('.'))+col('.')-1
109if search('^\s*(\*:o\?caml:')
110 let s:modeline = getline(".")
111else
112 let s:modeline = ""
113endif
114if s:s > 0
115 exe 'goto' s:s
116endif
117
118" Get the indentation params
119let s:m = matchstr(s:modeline,'default\s*=\s*\d\+')
120if s:m != ""
121 let s:idef = matchstr(s:m,'\d\+')
122elseif exists("g:omlet_indent")
123 let s:idef = g:omlet_indent
124else
125 let s:idef = 2
126endif
127let s:m = matchstr(s:modeline,'struct\s*=\s*\d\+')
128if s:m != ""
129 let s:i = matchstr(s:m,'\d\+')
130elseif exists("g:omlet_indent_struct")
131 let s:i = g:omlet_indent_struct
132else
133 let s:i = s:idef
134endif
135
136" Set the folding method
137if exists("g:ocaml_folding")
138 setlocal foldmethod=expr
139 setlocal foldexpr=OMLetFoldLevel(v:lnum)
140endif
141
142" - Only definitions below, executed once -------------------------------------
143
144if exists("*OMLetFoldLevel")
145 finish
146endif
147
148function s:topindent(lnum)
149 let l = a:lnum
150 while l > 0
151 if getline(l) =~ '\s*\%(\<struct\>\|\<sig\>\|\<object\>\)'
152 return indent(l)
153 endif
154 let l = l-1
155 endwhile
156 return -s:i
157endfunction
158
159function OMLetFoldLevel(l)
160
161 " This is for not merging blank lines around folds to them
162 if getline(a:l) !~ '\S'
163 return -1
164 endif
165
166 " We start folds for modules, classes, and every toplevel definition
167 if getline(a:l) =~ '^\s*\%(\<val\>\|\<module\>\|\<class\>\|\<type\>\|\<method\>\|\<initializer\>\|\<inherit\>\|\<exception\>\|\<external\>\)'
168 exe 'return ">' (indent(a:l)/s:i)+1 '"'
169 endif
170
171 " Toplevel let are detected thanks to the indentation
172 if getline(a:l) =~ '^\s*let\>' && indent(a:l) == s:i+s:topindent(a:l)
173 exe 'return ">' (indent(a:l)/s:i)+1 '"'
174 endif
175
176 " We close fold on end which are associated to struct, sig or object.
177 " We use syntax information to do that.
178 if getline(a:l) =~ '^\s*end\>' && synIDattr(synID(a:l, indent(a:l)+1, 0), "name") != "ocamlKeyword"
179 return (indent(a:l)/s:i)+1
180 endif
181
182 " Folds end on ;;
183 if getline(a:l) =~ '^\s*;;'
184 exe 'return "<' (indent(a:l)/s:i)+1 '"'
185 endif
186
187 " Comments around folds aren't merged to them.
188 if synIDattr(synID(a:l, indent(a:l)+1, 0), "name") == "ocamlComment"
189 return -1
190 endif
191
192 return '='
193endfunction
194
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000195" Vim support for OCaml .annot files
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000196"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000197" Last Change: 2007 Jul 17
198" Maintainer: Vincent Aravantinos <vincent.aravantinos@gmail.com>
199" License: public domain
200"
201" Originally inspired by 'ocaml-dtypes.vim' by Stefano Zacchiroli.
202" The source code is quite radically different for we not use python anymore.
203" However this plugin should have the exact same behaviour, that's why the
204" following lines are the quite exact copy of Stefano's original plugin :
205"
206" <<
207" Executing Ocaml_print_type(<mode>) function will display in the Vim bottom
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000208" line(s) the type of an ocaml value getting it from the corresponding .annot
209" file (if any). If Vim is in visual mode, <mode> should be "visual" and the
210" selected ocaml value correspond to the highlighted text, otherwise (<mode>
211" can be anything else) it corresponds to the literal found at the current
212" cursor position.
213"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000214" Typing '<LocalLeader>t' (LocalLeader defaults to '\', see :h LocalLeader)
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100215" will cause " Ocaml_print_type function to be invoked with the right
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000216" argument depending on the current mode (visual or not).
217" >>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000218"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000219" If you find something not matching this behaviour, please signal it.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000220"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000221" Differences are:
222" - no need for python support
223" + plus : more portable
224" + minus: no more lazy parsing, it looks very fast however
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100225"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000226" - ocamlbuild support, ie.
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100227" + the plugin finds the _build directory and looks for the
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000228" corresponding file inside;
229" + if the user decides to change the name of the _build directory thanks
230" to the '-build-dir' option of ocamlbuild, the plugin will manage in
231" most cases to find it out (most cases = if the source file has a unique
232" name among your whole project);
233" + if ocamlbuild is not used, the usual behaviour holds; ie. the .annot
234" file should be in the same directory as the source file;
235" + for vim plugin programmers:
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100236" the variable 'b:_build_dir' contains the inferred path to the build
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000237" directory, even if this one is not named '_build'.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000238"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000239" Bonus :
240" - latin1 accents are handled
241" - lists are handled, even on multiple lines, you don't need the visual mode
242" (the cursor must be on the first bracket)
243" - parenthesized expressions, arrays, and structures (ie. '(...)', '[|...|]',
244" and '{...}') are handled the same way
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000245
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000246 " Copied from Stefano's original plugin :
247 " <<
248 " .annot ocaml file representation
249 "
250 " File format (copied verbatim from caml-types.el)
251 "
252 " file ::= block *
253 " block ::= position <SP> position <LF> annotation *
254 " position ::= filename <SP> num <SP> num <SP> num
255 " annotation ::= keyword open-paren <LF> <SP> <SP> data <LF> close-paren
256 "
257 " <SP> is a space character (ASCII 0x20)
258 " <LF> is a line-feed character (ASCII 0x0A)
259 " num is a sequence of decimal digits
260 " filename is a string with the lexical conventions of O'Caml
261 " open-paren is an open parenthesis (ASCII 0x28)
262 " close-paren is a closed parenthesis (ASCII 0x29)
263 " data is any sequence of characters where <LF> is always followed by
264 " at least two space characters.
265 "
266 " - in each block, the two positions are respectively the start and the
267 " end of the range described by the block.
268 " - in a position, the filename is the name of the file, the first num
269 " is the line number, the second num is the offset of the beginning
270 " of the line, the third num is the offset of the position itself.
271 " - the char number within the line is the difference between the third
272 " and second nums.
273 "
274 " For the moment, the only possible keyword is \"type\"."
275 " >>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000276
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000277
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000278" 1. Finding the annotation file even if we use ocamlbuild
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000279
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000280 " In: two strings representing paths
281 " Out: one string representing the common prefix between the two paths
282 function! s:Find_common_path (p1,p2)
283 let temp = a:p2
284 while matchstr(a:p1,temp) == ''
285 let temp = substitute(temp,'/[^/]*$','','')
286 endwhile
287 return temp
288 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000289
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000290 " After call:
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100291 "
292 " Following information have been put in s:annot_file_list, using
293 " annot_file_name name as key:
294 " - annot_file_path :
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000295 " path to the .annot file corresponding to the
296 " source file (dealing with ocamlbuild stuff)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100297 " - _build_path:
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000298 " path to the build directory even if this one is
299 " not named '_build'
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100300 " - date_of_last annot:
301 " Set to 0 until we load the file. It contains the
302 " date at which the file has been loaded.
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000303 function! s:Locate_annotation()
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100304 let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot'
305 if !exists ("s:annot_file_list[annot_file_name]")
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000306 silent exe 'cd' s:Fnameescape(expand('%:p:h'))
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000307 " 1st case : the annot file is in the same directory as the buffer (no ocamlbuild)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100308 let annot_file_path = findfile(annot_file_name,'.')
309 if annot_file_path != ''
310 let annot_file_path = getcwd().'/'.annot_file_path
311 let _build_path = ''
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 "
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100319 let _build_path = finddir('_build','.')
320 if _build_path != ''
321 let _build_path = getcwd().'/'._build_path
322 let annot_file_path = findfile(annot_file_name,'_build')
323 if annot_file_path != ''
324 let annot_file_path = getcwd().'/'.annot_file_path
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000325 endif
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000326 else
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100327 " 3rd case : the _build directory is in a directory higher in the file hierarchy
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000328 " (it can't be deeper by ocamlbuild requirements)
329 " ..
330 " / \
331 " / \
332 " _build ...
333 " \
334 " \
335 " .ml
336 "
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100337 let _build_path = finddir('_build',';')
338 if _build_path != ''
339 let project_path = substitute(_build_path,'/_build$','','')
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000340 let path_relative_to_project = s:Fnameescape(substitute(expand('%:p:h'),project_path.'/','',''))
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100341 let annot_file_path = findfile(annot_file_name,project_path.'/_build/'.path_relative_to_project)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000342 else
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100343 let annot_file_path = findfile(annot_file_name,'**')
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000344 "4th case : what if the user decided to change the name of the _build directory ?
345 " -> we relax the constraints, it should work in most cases
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100346 if annot_file_path != ''
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000347 " 4a. we suppose the renamed _build directory is in the current directory
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100348 let _build_path = matchstr(annot_file_path,'^[^/]*')
349 if annot_file_path != ''
350 let annot_file_path = getcwd().'/'.annot_file_path
351 let _build_path = getcwd().'/'._build_path
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000352 endif
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000353 else
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100354 let annot_file_name = ''
355 "(Pierre Vittet: I have commented 4b because this was chrashing
356 "my vim (it produced infinite loop))
357 "
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000358 " 4b. anarchy : the renamed _build directory may be higher in the hierarchy
359 " this will work if the file for which we are looking annotations has a unique name in the whole project
360 " if this is not the case, it may still work, but no warranty here
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100361 "let annot_file_path = findfile(annot_file_name,'**;')
362 "let project_path = s:Find_common_path(annot_file_path,expand('%:p:h'))
363 "let _build_path = matchstr(annot_file_path,project_path.'/[^/]*')
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000364 endif
365 endif
366 endif
367 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000368
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100369 if annot_file_path == ''
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000370 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 Moolenaarb8ff1fb2012-02-04 21:59:01 +0100374 let s:annot_file_list[annot_file_name]= [annot_file_path, _build_path, 0]
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000375 endif
376 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000377
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100378 " This variable contain a dictionnary of list. Each element of the dictionnary
379 " represent an annotation system. An annotation system is a list with :
380 " - annotation file name as it's key
381 " - annotation file path as first element of the contained list
382 " - build path as second element of the contained list
383 " - annot_file_last_mod (contain the date of .annot file) as third element
384 let s:annot_file_list = {}
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000385
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000386" 2. Finding the type information in the annotation file
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100387
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000388 " a. The annotation file is opened in vim as a buffer that
389 " should be (almost) invisible to the user.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000390
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000391 " After call:
392 " The current buffer is now the one containing the .annot file.
393 " We manage to keep all this hidden to the user's eye.
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100394 function! s:Enter_annotation_buffer(annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000395 let s:current_pos = getpos('.')
396 let s:current_hidden = &l:hidden
397 set hidden
398 let s:current_buf = bufname('%')
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100399 if bufloaded(a:annot_file_path)
400 silent exe 'keepj keepalt' 'buffer' s:Fnameescape(a:annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000401 else
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100402 silent exe 'keepj keepalt' 'view' s:Fnameescape(a:annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000403 endif
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100404 call setpos(".", [0, 0 , 0 , 0])
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000405 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000406
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000407 " After call:
408 " The original buffer has been restored in the exact same state as before.
409 function! s:Exit_annotation_buffer()
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000410 silent exe 'keepj keepalt' 'buffer' s:Fnameescape(s:current_buf)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000411 let &l:hidden = s:current_hidden
412 call setpos('.',s:current_pos)
413 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000414
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000415 " After call:
416 " The annot file is loaded and assigned to a buffer.
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100417 " This also handles the modification date of the .annot file, eg. after a
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100418 " compilation (return an updated annot_file_list).
419 function! s:Load_annotation(annot_file_name)
420 let annot = s:annot_file_list[a:annot_file_name]
421 let annot_file_path = annot[0]
422 let annot_file_last_mod = 0
423 if exists("annot[2]")
424 let annot_file_last_mod = annot[2]
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000425 endif
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100426 if bufloaded(annot_file_path) && annot_file_last_mod < getftime(annot_file_path)
427 " if there is a more recent file
428 let nr = bufnr(annot_file_path)
429 silent exe 'keepj keepalt' 'bunload' nr
430 endif
431 if !bufloaded(annot_file_path)
432 call s:Enter_annotation_buffer(annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000433 setlocal nobuflisted
434 setlocal bufhidden=hide
435 setlocal noswapfile
436 setlocal buftype=nowrite
437 call s:Exit_annotation_buffer()
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100438 let annot[2] = getftime(annot_file_path)
439 " List updated with the new date
440 let s:annot_file_list[a:annot_file_name] = annot
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000441 endif
442 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100443
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000444 "b. 'search' and 'match' work to find the type information
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100445
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000446 "In: - lin1,col1: postion of expression first char
447 " - lin2,col2: postion of expression last char
448 "Out: - the pattern to be looked for to find the block
449 " Must be called in the source buffer (use of line2byte)
450 function! s:Block_pattern(lin1,lin2,col1,col2)
451 let start_num1 = a:lin1
452 let start_num2 = line2byte(a:lin1) - 1
453 let start_num3 = start_num2 + a:col1
Bram Moolenaar9c754c42010-07-10 15:52:35 +0200454 let path = '"\(\\"\|[^"]\)\+"'
455 let start_pos = path.' '.start_num1.' '.start_num2.' '.start_num3
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000456 let end_num1 = a:lin2
457 let end_num2 = line2byte(a:lin2) - 1
458 let end_num3 = end_num2 + a:col2
Bram Moolenaar9c754c42010-07-10 15:52:35 +0200459 let end_pos = path.' '.end_num1.' '.end_num2.' '.end_num3
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000460 return '^'.start_pos.' '.end_pos."$"
461 " rq: the '^' here is not totally correct regarding the annot file "grammar"
462 " but currently the annotation file respects this, and it's a little bit faster with the '^';
463 " can be removed safely.
464 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000465
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000466 "In: (the cursor position should be at the start of an annotation)
467 "Out: the type information
468 " Must be called in the annotation buffer (use of search)
469 function! s:Match_data()
470 " rq: idem as previously, in the following, the '^' at start of patterns is not necessary
471 keepj while search('^type($','ce',line(".")) == 0
472 keepj if search('^.\{-}($','e') == 0
473 throw "no_annotation"
474 endif
475 keepj if searchpair('(','',')') == 0
476 throw "malformed_annot_file"
477 endif
478 endwhile
479 let begin = line(".") + 1
480 keepj if searchpair('(','',')') == 0
481 throw "malformed_annot_file"
482 endif
483 let end = line(".") - 1
484 return join(getline(begin,end),"\n")
485 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100486
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000487 "In: the pattern to look for in order to match the block
488 "Out: the type information (calls s:Match_data)
489 " Should be called in the annotation buffer
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100490 function! s:Extract_type_data(block_pattern, annot_file_name)
491 let annot_file_path = s:annot_file_list[a:annot_file_name][0]
492 call s:Enter_annotation_buffer(annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000493 try
494 if search(a:block_pattern,'e') == 0
495 throw "no_annotation"
496 endif
497 call cursor(line(".") + 1,1)
498 let annotation = s:Match_data()
499 finally
500 call s:Exit_annotation_buffer()
501 endtry
502 return annotation
503 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100504
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000505 "c. link this stuff with what the user wants
506 " ie. get the expression selected/under the cursor
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100507
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000508 let s:ocaml_word_char = '\w|[À-ÿ]|'''
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000509
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000510 "In: the current mode (eg. "visual", "normal", etc.)
511 "Out: the borders of the expression we are looking for the type
512 function! s:Match_borders(mode)
513 if a:mode == "visual"
514 let cur = getpos(".")
515 normal `<
516 let col1 = col(".")
517 let lin1 = line(".")
518 normal `>
519 let col2 = col(".")
520 let lin2 = line(".")
521 call cursor(cur[1],cur[2])
522 return [lin1,lin2,col1-1,col2]
523 else
524 let cursor_line = line(".")
525 let cursor_col = col(".")
526 let line = getline('.')
527 if line[cursor_col-1:cursor_col] == '[|'
528 let [lin2,col2] = searchpairpos('\[|','','|\]','n')
529 return [cursor_line,lin2,cursor_col-1,col2+1]
530 elseif line[cursor_col-1] == '['
531 let [lin2,col2] = searchpairpos('\[','','\]','n')
532 return [cursor_line,lin2,cursor_col-1,col2]
533 elseif line[cursor_col-1] == '('
534 let [lin2,col2] = searchpairpos('(','',')','n')
535 return [cursor_line,lin2,cursor_col-1,col2]
536 elseif line[cursor_col-1] == '{'
537 let [lin2,col2] = searchpairpos('{','','}','n')
538 return [cursor_line,lin2,cursor_col-1,col2]
539 else
540 let [lin1,col1] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','ncb')
541 let [lin2,col2] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','nce')
542 if col1 == 0 || col2 == 0
543 throw "no_expression"
544 endif
545 return [cursor_line,cursor_line,col1-1,col2]
546 endif
547 endif
548 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000549
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000550 "In: the current mode (eg. "visual", "normal", etc.)
551 "Out: the type information (calls s:Extract_type_data)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100552 function! s:Get_type(mode, annot_file_name)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000553 let [lin1,lin2,col1,col2] = s:Match_borders(a:mode)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100554 return s:Extract_type_data(s:Block_pattern(lin1,lin2,col1,col2), a:annot_file_name)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000555 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100556
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000557 "d. main
558 "In: the current mode (eg. "visual", "normal", etc.)
559 "After call: the type information is displayed
560 if !exists("*Ocaml_get_type")
561 function Ocaml_get_type(mode)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100562 let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot'
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000563 call s:Locate_annotation()
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100564 call s:Load_annotation(annot_file_name)
565 return s:Get_type(a:mode, annot_file_name)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000566 endfun
567 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000568
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000569 if !exists("*Ocaml_get_type_or_not")
570 function Ocaml_get_type_or_not(mode)
571 let t=reltime()
572 try
573 return Ocaml_get_type(a:mode)
574 catch
575 return ""
576 endtry
577 endfun
578 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000579
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000580 if !exists("*Ocaml_print_type")
581 function Ocaml_print_type(mode)
582 if expand("%:e") == "mli"
583 echohl ErrorMsg | echo "No annotations for interface (.mli) files" | echohl None
584 return
585 endif
586 try
587 echo Ocaml_get_type(a:mode)
588 catch /E484:/
589 echohl ErrorMsg | echo "No type annotations (.annot) file found" | echohl None
590 catch /no_expression/
591 echohl ErrorMsg | echo "No expression found under the cursor" | echohl None
592 catch /no_annotation/
593 echohl ErrorMsg | echo "No type annotation found for the given text" | echohl None
594 catch /malformed_annot_file/
595 echohl ErrorMsg | echo "Malformed .annot file" | echohl None
596 endtry
597 endfun
598 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000599
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000600" Maps
601 map <silent> <LocalLeader>t :call Ocaml_print_type("normal")<CR>
602 vmap <silent> <LocalLeader>t :<C-U>call Ocaml_print_type("visual")<CR>`<
Bram Moolenaara5792f52005-11-23 21:25:05 +0000603
604let &cpoptions=s:cposet
605unlet s:cposet
606
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000607" vim:sw=2 fdm=indent