blob: 95213daf0c593135850b0b6c3b307935bc67e73f [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 Moolenaar14b69452013-06-29 23:05:20 +020016" 2013 Jul - moving errorformat into compiler/ocaml.vim Marc Weber
17"
18" Marc Weber's comment: This file may contain a lot of (very custom) stuff
19" which eventually should be moved somewhere else ..
20"
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +010021
Bram Moolenaar4c3f5362006-04-11 21:38:50 +000022if exists("b:did_ftplugin")
23 finish
24endif
Bram Moolenaara5792f52005-11-23 21:25:05 +000025let b:did_ftplugin=1
Bram Moolenaar071d4272004-06-13 20:20:40 +000026
Bram Moolenaare37d50a2008-08-06 17:06:04 +000027" some macro
28if exists('*fnameescape')
29 function! s:Fnameescape(s)
30 return fnameescape(a:s)
31 endfun
32else
33 function! s:Fnameescape(s)
34 return escape(a:s," \t\n*?[{`$\\%#'\"|!<")
35 endfun
36endif
37
Bram Moolenaara5792f52005-11-23 21:25:05 +000038" Error handling -- helps moving where the compiler wants you to go
39let s:cposet=&cpoptions
Bram Moolenaarf1568ec2011-12-14 21:17:39 +010040set cpo&vim
Bram Moolenaar071d4272004-06-13 20:20:40 +000041
42" Add mappings, unless the user didn't want this.
43if !exists("no_plugin_maps") && !exists("no_ocaml_maps")
Bram Moolenaara5792f52005-11-23 21:25:05 +000044 " (un)commenting
Bram Moolenaar071d4272004-06-13 20:20:40 +000045 if !hasmapto('<Plug>Comment')
46 nmap <buffer> <LocalLeader>c <Plug>LUncomOn
47 vmap <buffer> <LocalLeader>c <Plug>BUncomOn
48 nmap <buffer> <LocalLeader>C <Plug>LUncomOff
49 vmap <buffer> <LocalLeader>C <Plug>BUncomOff
50 endif
51
52 nnoremap <buffer> <Plug>LUncomOn mz0i(* <ESC>$A *)<ESC>`z
Bram Moolenaara5792f52005-11-23 21:25:05 +000053 nnoremap <buffer> <Plug>LUncomOff :s/^(\* \(.*\) \*)/\1/<CR>:noh<CR>
Bram Moolenaar071d4272004-06-13 20:20:40 +000054 vnoremap <buffer> <Plug>BUncomOn <ESC>:'<,'><CR>`<O<ESC>0i(*<ESC>`>o<ESC>0i*)<ESC>`<
55 vnoremap <buffer> <Plug>BUncomOff <ESC>:'<,'><CR>`<dd`>dd`<
56
57 if !hasmapto('<Plug>Abbrev')
Bram Moolenaar4c3f5362006-04-11 21:38:50 +000058 iabbrev <buffer> ASS (assert (0=1) (* XXX *))
Bram Moolenaar071d4272004-06-13 20:20:40 +000059 endif
60endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000061
62" Let % jump between structure elements (due to Issac Trotts)
Bram Moolenaara5792f52005-11-23 21:25:05 +000063let b:mw = ''
64let b:mw = b:mw . ',\<let\>:\<and\>:\(\<in\>\|;;\)'
65let b:mw = b:mw . ',\<if\>:\<then\>:\<else\>'
66let b:mw = b:mw . ',\<\(for\|while\)\>:\<do\>:\<done\>,'
67let b:mw = b:mw . ',\<\(object\|sig\|struct\|begin\)\>:\<end\>'
68let b:mw = b:mw . ',\<\(match\|try\)\>:\<with\>'
69let b:match_words = b:mw
70
71let b:match_ignorecase=0
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000072
73" switching between interfaces (.mli) and implementations (.ml)
74if !exists("g:did_ocaml_switch")
75 let g:did_ocaml_switch = 1
Bram Moolenaara5792f52005-11-23 21:25:05 +000076 map <LocalLeader>s :call OCaml_switch(0)<CR>
77 map <LocalLeader>S :call OCaml_switch(1)<CR>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000078 fun OCaml_switch(newwin)
79 if (match(bufname(""), "\\.mli$") >= 0)
Bram Moolenaare37d50a2008-08-06 17:06:04 +000080 let fname = s:Fnameescape(substitute(bufname(""), "\\.mli$", ".ml", ""))
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000081 if (a:newwin == 1)
Bram Moolenaara5792f52005-11-23 21:25:05 +000082 exec "new " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000083 else
Bram Moolenaara5792f52005-11-23 21:25:05 +000084 exec "arge " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000085 endif
86 elseif (match(bufname(""), "\\.ml$") >= 0)
Bram Moolenaare37d50a2008-08-06 17:06:04 +000087 let fname = s:Fnameescape(bufname("")) . "i"
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 endif
94 endfun
95endif
96
Bram Moolenaara5792f52005-11-23 21:25:05 +000097" Folding support
98
99" Get the modeline because folding depends on indentation
100let s:s = line2byte(line('.'))+col('.')-1
101if search('^\s*(\*:o\?caml:')
102 let s:modeline = getline(".")
103else
104 let s:modeline = ""
105endif
106if s:s > 0
107 exe 'goto' s:s
108endif
109
110" Get the indentation params
111let s:m = matchstr(s:modeline,'default\s*=\s*\d\+')
112if s:m != ""
113 let s:idef = matchstr(s:m,'\d\+')
114elseif exists("g:omlet_indent")
115 let s:idef = g:omlet_indent
116else
117 let s:idef = 2
118endif
119let s:m = matchstr(s:modeline,'struct\s*=\s*\d\+')
120if s:m != ""
121 let s:i = matchstr(s:m,'\d\+')
122elseif exists("g:omlet_indent_struct")
123 let s:i = g:omlet_indent_struct
124else
125 let s:i = s:idef
126endif
127
128" Set the folding method
129if exists("g:ocaml_folding")
130 setlocal foldmethod=expr
131 setlocal foldexpr=OMLetFoldLevel(v:lnum)
132endif
133
134" - Only definitions below, executed once -------------------------------------
135
136if exists("*OMLetFoldLevel")
137 finish
138endif
139
140function s:topindent(lnum)
141 let l = a:lnum
142 while l > 0
143 if getline(l) =~ '\s*\%(\<struct\>\|\<sig\>\|\<object\>\)'
144 return indent(l)
145 endif
146 let l = l-1
147 endwhile
148 return -s:i
149endfunction
150
151function OMLetFoldLevel(l)
152
153 " This is for not merging blank lines around folds to them
154 if getline(a:l) !~ '\S'
155 return -1
156 endif
157
158 " We start folds for modules, classes, and every toplevel definition
159 if getline(a:l) =~ '^\s*\%(\<val\>\|\<module\>\|\<class\>\|\<type\>\|\<method\>\|\<initializer\>\|\<inherit\>\|\<exception\>\|\<external\>\)'
160 exe 'return ">' (indent(a:l)/s:i)+1 '"'
161 endif
162
163 " Toplevel let are detected thanks to the indentation
164 if getline(a:l) =~ '^\s*let\>' && indent(a:l) == s:i+s:topindent(a:l)
165 exe 'return ">' (indent(a:l)/s:i)+1 '"'
166 endif
167
168 " We close fold on end which are associated to struct, sig or object.
169 " We use syntax information to do that.
170 if getline(a:l) =~ '^\s*end\>' && synIDattr(synID(a:l, indent(a:l)+1, 0), "name") != "ocamlKeyword"
171 return (indent(a:l)/s:i)+1
172 endif
173
174 " Folds end on ;;
175 if getline(a:l) =~ '^\s*;;'
176 exe 'return "<' (indent(a:l)/s:i)+1 '"'
177 endif
178
179 " Comments around folds aren't merged to them.
180 if synIDattr(synID(a:l, indent(a:l)+1, 0), "name") == "ocamlComment"
181 return -1
182 endif
183
184 return '='
185endfunction
186
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000187" Vim support for OCaml .annot files
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000188"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000189" Last Change: 2007 Jul 17
190" Maintainer: Vincent Aravantinos <vincent.aravantinos@gmail.com>
191" License: public domain
192"
193" Originally inspired by 'ocaml-dtypes.vim' by Stefano Zacchiroli.
194" The source code is quite radically different for we not use python anymore.
195" However this plugin should have the exact same behaviour, that's why the
196" following lines are the quite exact copy of Stefano's original plugin :
197"
198" <<
199" Executing Ocaml_print_type(<mode>) function will display in the Vim bottom
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000200" line(s) the type of an ocaml value getting it from the corresponding .annot
201" file (if any). If Vim is in visual mode, <mode> should be "visual" and the
202" selected ocaml value correspond to the highlighted text, otherwise (<mode>
203" can be anything else) it corresponds to the literal found at the current
204" cursor position.
205"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000206" Typing '<LocalLeader>t' (LocalLeader defaults to '\', see :h LocalLeader)
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100207" will cause " Ocaml_print_type function to be invoked with the right
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000208" argument depending on the current mode (visual or not).
209" >>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000210"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000211" If you find something not matching this behaviour, please signal it.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000212"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000213" Differences are:
214" - no need for python support
215" + plus : more portable
216" + minus: no more lazy parsing, it looks very fast however
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100217"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000218" - ocamlbuild support, ie.
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100219" + the plugin finds the _build directory and looks for the
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000220" corresponding file inside;
221" + if the user decides to change the name of the _build directory thanks
222" to the '-build-dir' option of ocamlbuild, the plugin will manage in
223" most cases to find it out (most cases = if the source file has a unique
224" name among your whole project);
225" + if ocamlbuild is not used, the usual behaviour holds; ie. the .annot
226" file should be in the same directory as the source file;
227" + for vim plugin programmers:
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100228" the variable 'b:_build_dir' contains the inferred path to the build
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000229" directory, even if this one is not named '_build'.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000230"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000231" Bonus :
232" - latin1 accents are handled
233" - lists are handled, even on multiple lines, you don't need the visual mode
234" (the cursor must be on the first bracket)
235" - parenthesized expressions, arrays, and structures (ie. '(...)', '[|...|]',
236" and '{...}') are handled the same way
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000237
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000238 " Copied from Stefano's original plugin :
239 " <<
240 " .annot ocaml file representation
241 "
242 " File format (copied verbatim from caml-types.el)
243 "
244 " file ::= block *
245 " block ::= position <SP> position <LF> annotation *
246 " position ::= filename <SP> num <SP> num <SP> num
247 " annotation ::= keyword open-paren <LF> <SP> <SP> data <LF> close-paren
248 "
249 " <SP> is a space character (ASCII 0x20)
250 " <LF> is a line-feed character (ASCII 0x0A)
251 " num is a sequence of decimal digits
252 " filename is a string with the lexical conventions of O'Caml
253 " open-paren is an open parenthesis (ASCII 0x28)
254 " close-paren is a closed parenthesis (ASCII 0x29)
255 " data is any sequence of characters where <LF> is always followed by
256 " at least two space characters.
257 "
258 " - in each block, the two positions are respectively the start and the
259 " end of the range described by the block.
260 " - in a position, the filename is the name of the file, the first num
261 " is the line number, the second num is the offset of the beginning
262 " of the line, the third num is the offset of the position itself.
263 " - the char number within the line is the difference between the third
264 " and second nums.
265 "
266 " For the moment, the only possible keyword is \"type\"."
267 " >>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000268
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000269
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000270" 1. Finding the annotation file even if we use ocamlbuild
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000271
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000272 " In: two strings representing paths
273 " Out: one string representing the common prefix between the two paths
274 function! s:Find_common_path (p1,p2)
275 let temp = a:p2
276 while matchstr(a:p1,temp) == ''
277 let temp = substitute(temp,'/[^/]*$','','')
278 endwhile
279 return temp
280 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000281
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000282 " After call:
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100283 "
284 " Following information have been put in s:annot_file_list, using
285 " annot_file_name name as key:
286 " - annot_file_path :
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000287 " path to the .annot file corresponding to the
288 " source file (dealing with ocamlbuild stuff)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100289 " - _build_path:
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000290 " path to the build directory even if this one is
291 " not named '_build'
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100292 " - date_of_last annot:
293 " Set to 0 until we load the file. It contains the
294 " date at which the file has been loaded.
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000295 function! s:Locate_annotation()
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100296 let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot'
297 if !exists ("s:annot_file_list[annot_file_name]")
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000298 silent exe 'cd' s:Fnameescape(expand('%:p:h'))
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000299 " 1st case : the annot file is in the same directory as the buffer (no ocamlbuild)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100300 let annot_file_path = findfile(annot_file_name,'.')
301 if annot_file_path != ''
302 let annot_file_path = getcwd().'/'.annot_file_path
303 let _build_path = ''
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000304 else
305 " 2nd case : the buffer and the _build directory are in the same directory
306 " ..
307 " / \
308 " / \
309 " _build .ml
310 "
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100311 let _build_path = finddir('_build','.')
312 if _build_path != ''
313 let _build_path = getcwd().'/'._build_path
314 let annot_file_path = findfile(annot_file_name,'_build')
315 if annot_file_path != ''
316 let annot_file_path = getcwd().'/'.annot_file_path
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000317 endif
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000318 else
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100319 " 3rd case : the _build directory is in a directory higher in the file hierarchy
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000320 " (it can't be deeper by ocamlbuild requirements)
321 " ..
322 " / \
323 " / \
324 " _build ...
325 " \
326 " \
327 " .ml
328 "
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100329 let _build_path = finddir('_build',';')
330 if _build_path != ''
331 let project_path = substitute(_build_path,'/_build$','','')
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000332 let path_relative_to_project = s:Fnameescape(substitute(expand('%:p:h'),project_path.'/','',''))
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100333 let annot_file_path = findfile(annot_file_name,project_path.'/_build/'.path_relative_to_project)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000334 else
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100335 let annot_file_path = findfile(annot_file_name,'**')
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000336 "4th case : what if the user decided to change the name of the _build directory ?
337 " -> we relax the constraints, it should work in most cases
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100338 if annot_file_path != ''
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000339 " 4a. we suppose the renamed _build directory is in the current directory
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100340 let _build_path = matchstr(annot_file_path,'^[^/]*')
341 if annot_file_path != ''
342 let annot_file_path = getcwd().'/'.annot_file_path
343 let _build_path = getcwd().'/'._build_path
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000344 endif
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000345 else
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100346 let annot_file_name = ''
347 "(Pierre Vittet: I have commented 4b because this was chrashing
348 "my vim (it produced infinite loop))
349 "
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000350 " 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
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100353 "let annot_file_path = findfile(annot_file_name,'**;')
354 "let project_path = s:Find_common_path(annot_file_path,expand('%:p:h'))
355 "let _build_path = matchstr(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 Moolenaarb8ff1fb2012-02-04 21:59:01 +0100361 if annot_file_path == ''
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000362 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 Moolenaarb8ff1fb2012-02-04 21:59:01 +0100366 let s:annot_file_list[annot_file_name]= [annot_file_path, _build_path, 0]
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000367 endif
368 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000369
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100370 " This variable contain a dictionnary of list. Each element of the dictionnary
371 " represent an annotation system. An annotation system is a list with :
372 " - annotation file name as it's key
373 " - annotation file path as first element of the contained list
374 " - build path as second element of the contained list
375 " - annot_file_last_mod (contain the date of .annot file) as third element
376 let s:annot_file_list = {}
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000377
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000378" 2. Finding the type information in the annotation file
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100379
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000380 " a. The annotation file is opened in vim as a buffer that
381 " should be (almost) invisible to the user.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000382
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000383 " After call:
384 " The current buffer is now the one containing the .annot file.
385 " We manage to keep all this hidden to the user's eye.
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100386 function! s:Enter_annotation_buffer(annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000387 let s:current_pos = getpos('.')
388 let s:current_hidden = &l:hidden
389 set hidden
390 let s:current_buf = bufname('%')
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100391 if bufloaded(a:annot_file_path)
392 silent exe 'keepj keepalt' 'buffer' s:Fnameescape(a:annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000393 else
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100394 silent exe 'keepj keepalt' 'view' s:Fnameescape(a:annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000395 endif
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100396 call setpos(".", [0, 0 , 0 , 0])
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000397 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000398
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000399 " After call:
400 " The original buffer has been restored in the exact same state as before.
401 function! s:Exit_annotation_buffer()
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000402 silent exe 'keepj keepalt' 'buffer' s:Fnameescape(s:current_buf)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000403 let &l:hidden = s:current_hidden
404 call setpos('.',s:current_pos)
405 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000406
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000407 " After call:
408 " The annot file is loaded and assigned to a buffer.
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100409 " This also handles the modification date of the .annot file, eg. after a
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100410 " compilation (return an updated annot_file_list).
411 function! s:Load_annotation(annot_file_name)
412 let annot = s:annot_file_list[a:annot_file_name]
413 let annot_file_path = annot[0]
414 let annot_file_last_mod = 0
415 if exists("annot[2]")
416 let annot_file_last_mod = annot[2]
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000417 endif
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100418 if bufloaded(annot_file_path) && annot_file_last_mod < getftime(annot_file_path)
419 " if there is a more recent file
420 let nr = bufnr(annot_file_path)
421 silent exe 'keepj keepalt' 'bunload' nr
422 endif
423 if !bufloaded(annot_file_path)
424 call s:Enter_annotation_buffer(annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000425 setlocal nobuflisted
426 setlocal bufhidden=hide
427 setlocal noswapfile
428 setlocal buftype=nowrite
429 call s:Exit_annotation_buffer()
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100430 let annot[2] = getftime(annot_file_path)
431 " List updated with the new date
432 let s:annot_file_list[a:annot_file_name] = annot
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000433 endif
434 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100435
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000436 "b. 'search' and 'match' work to find the type information
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100437
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000438 "In: - lin1,col1: postion of expression first char
439 " - lin2,col2: postion of expression last char
440 "Out: - the pattern to be looked for to find the block
441 " Must be called in the source buffer (use of line2byte)
442 function! s:Block_pattern(lin1,lin2,col1,col2)
443 let start_num1 = a:lin1
444 let start_num2 = line2byte(a:lin1) - 1
445 let start_num3 = start_num2 + a:col1
Bram Moolenaar9c754c42010-07-10 15:52:35 +0200446 let path = '"\(\\"\|[^"]\)\+"'
447 let start_pos = path.' '.start_num1.' '.start_num2.' '.start_num3
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000448 let end_num1 = a:lin2
449 let end_num2 = line2byte(a:lin2) - 1
450 let end_num3 = end_num2 + a:col2
Bram Moolenaar9c754c42010-07-10 15:52:35 +0200451 let end_pos = path.' '.end_num1.' '.end_num2.' '.end_num3
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000452 return '^'.start_pos.' '.end_pos."$"
453 " rq: the '^' here is not totally correct regarding the annot file "grammar"
454 " but currently the annotation file respects this, and it's a little bit faster with the '^';
455 " can be removed safely.
456 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000457
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000458 "In: (the cursor position should be at the start of an annotation)
459 "Out: the type information
460 " Must be called in the annotation buffer (use of search)
461 function! s:Match_data()
462 " rq: idem as previously, in the following, the '^' at start of patterns is not necessary
463 keepj while search('^type($','ce',line(".")) == 0
464 keepj if search('^.\{-}($','e') == 0
465 throw "no_annotation"
466 endif
467 keepj if searchpair('(','',')') == 0
468 throw "malformed_annot_file"
469 endif
470 endwhile
471 let begin = line(".") + 1
472 keepj if searchpair('(','',')') == 0
473 throw "malformed_annot_file"
474 endif
475 let end = line(".") - 1
476 return join(getline(begin,end),"\n")
477 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100478
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000479 "In: the pattern to look for in order to match the block
480 "Out: the type information (calls s:Match_data)
481 " Should be called in the annotation buffer
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100482 function! s:Extract_type_data(block_pattern, annot_file_name)
483 let annot_file_path = s:annot_file_list[a:annot_file_name][0]
484 call s:Enter_annotation_buffer(annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000485 try
486 if search(a:block_pattern,'e') == 0
487 throw "no_annotation"
488 endif
489 call cursor(line(".") + 1,1)
490 let annotation = s:Match_data()
491 finally
492 call s:Exit_annotation_buffer()
493 endtry
494 return annotation
495 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100496
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000497 "c. link this stuff with what the user wants
498 " ie. get the expression selected/under the cursor
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100499
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000500 let s:ocaml_word_char = '\w|[À-ÿ]|'''
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000501
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000502 "In: the current mode (eg. "visual", "normal", etc.)
503 "Out: the borders of the expression we are looking for the type
504 function! s:Match_borders(mode)
505 if a:mode == "visual"
506 let cur = getpos(".")
507 normal `<
508 let col1 = col(".")
509 let lin1 = line(".")
510 normal `>
511 let col2 = col(".")
512 let lin2 = line(".")
513 call cursor(cur[1],cur[2])
514 return [lin1,lin2,col1-1,col2]
515 else
516 let cursor_line = line(".")
517 let cursor_col = col(".")
518 let line = getline('.')
519 if line[cursor_col-1:cursor_col] == '[|'
520 let [lin2,col2] = searchpairpos('\[|','','|\]','n')
521 return [cursor_line,lin2,cursor_col-1,col2+1]
522 elseif line[cursor_col-1] == '['
523 let [lin2,col2] = searchpairpos('\[','','\]','n')
524 return [cursor_line,lin2,cursor_col-1,col2]
525 elseif line[cursor_col-1] == '('
526 let [lin2,col2] = searchpairpos('(','',')','n')
527 return [cursor_line,lin2,cursor_col-1,col2]
528 elseif line[cursor_col-1] == '{'
529 let [lin2,col2] = searchpairpos('{','','}','n')
530 return [cursor_line,lin2,cursor_col-1,col2]
531 else
532 let [lin1,col1] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','ncb')
533 let [lin2,col2] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','nce')
534 if col1 == 0 || col2 == 0
535 throw "no_expression"
536 endif
537 return [cursor_line,cursor_line,col1-1,col2]
538 endif
539 endif
540 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000541
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000542 "In: the current mode (eg. "visual", "normal", etc.)
543 "Out: the type information (calls s:Extract_type_data)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100544 function! s:Get_type(mode, annot_file_name)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000545 let [lin1,lin2,col1,col2] = s:Match_borders(a:mode)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100546 return s:Extract_type_data(s:Block_pattern(lin1,lin2,col1,col2), a:annot_file_name)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000547 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100548
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000549 "d. main
550 "In: the current mode (eg. "visual", "normal", etc.)
551 "After call: the type information is displayed
552 if !exists("*Ocaml_get_type")
553 function Ocaml_get_type(mode)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100554 let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot'
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000555 call s:Locate_annotation()
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100556 call s:Load_annotation(annot_file_name)
557 return s:Get_type(a:mode, annot_file_name)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000558 endfun
559 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000560
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000561 if !exists("*Ocaml_get_type_or_not")
562 function Ocaml_get_type_or_not(mode)
563 let t=reltime()
564 try
565 return Ocaml_get_type(a:mode)
566 catch
567 return ""
568 endtry
569 endfun
570 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000571
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000572 if !exists("*Ocaml_print_type")
573 function Ocaml_print_type(mode)
574 if expand("%:e") == "mli"
575 echohl ErrorMsg | echo "No annotations for interface (.mli) files" | echohl None
576 return
577 endif
578 try
579 echo Ocaml_get_type(a:mode)
580 catch /E484:/
581 echohl ErrorMsg | echo "No type annotations (.annot) file found" | echohl None
582 catch /no_expression/
583 echohl ErrorMsg | echo "No expression found under the cursor" | echohl None
584 catch /no_annotation/
585 echohl ErrorMsg | echo "No type annotation found for the given text" | echohl None
586 catch /malformed_annot_file/
587 echohl ErrorMsg | echo "Malformed .annot file" | echohl None
588 endtry
589 endfun
590 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000591
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000592" Maps
593 map <silent> <LocalLeader>t :call Ocaml_print_type("normal")<CR>
594 vmap <silent> <LocalLeader>t :<C-U>call Ocaml_print_type("visual")<CR>`<
Bram Moolenaara5792f52005-11-23 21:25:05 +0000595
596let &cpoptions=s:cposet
597unlet s:cposet
598
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000599" vim:sw=2 fdm=indent