blob: ae41422497dacdc3c79d9bc7bf963e326995ac35 [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:
Bram Moolenaar16ea3672013-07-28 16:02:18 +020010" 2013 Jul 26 - load default compiler settings (MM)
11" 2013 Jul 24 - removed superfluous efm-setting (MM)
12" 2013 Jul 22 - applied fixes supplied by Hirotaka Hamada (MM)
13" 2013 Mar 15 - Improved error format (MM)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +010014
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 Moolenaar16ea3672013-07-28 16:02:18 +020020" Use standard compiler settings unless user wants otherwise
21if !exists("current_compiler")
22 :compiler ocaml
23endif
24
Bram Moolenaare37d50a2008-08-06 17:06:04 +000025" some macro
26if exists('*fnameescape')
27 function! s:Fnameescape(s)
28 return fnameescape(a:s)
29 endfun
30else
31 function! s:Fnameescape(s)
32 return escape(a:s," \t\n*?[{`$\\%#'\"|!<")
33 endfun
34endif
35
Bram Moolenaara5792f52005-11-23 21:25:05 +000036" Error handling -- helps moving where the compiler wants you to go
37let s:cposet=&cpoptions
Bram Moolenaarf1568ec2011-12-14 21:17:39 +010038set cpo&vim
Bram Moolenaar071d4272004-06-13 20:20:40 +000039
40" Add mappings, unless the user didn't want this.
41if !exists("no_plugin_maps") && !exists("no_ocaml_maps")
Bram Moolenaara5792f52005-11-23 21:25:05 +000042 " (un)commenting
Bram Moolenaar071d4272004-06-13 20:20:40 +000043 if !hasmapto('<Plug>Comment')
44 nmap <buffer> <LocalLeader>c <Plug>LUncomOn
Bram Moolenaar16ea3672013-07-28 16:02:18 +020045 xmap <buffer> <LocalLeader>c <Plug>BUncomOn
Bram Moolenaar071d4272004-06-13 20:20:40 +000046 nmap <buffer> <LocalLeader>C <Plug>LUncomOff
Bram Moolenaar16ea3672013-07-28 16:02:18 +020047 xmap <buffer> <LocalLeader>C <Plug>BUncomOff
Bram Moolenaar071d4272004-06-13 20:20:40 +000048 endif
49
Bram Moolenaar16ea3672013-07-28 16:02:18 +020050 nnoremap <buffer> <Plug>LUncomOn gI(* <End> *)<ESC>
Bram Moolenaara5792f52005-11-23 21:25:05 +000051 nnoremap <buffer> <Plug>LUncomOff :s/^(\* \(.*\) \*)/\1/<CR>:noh<CR>
Bram Moolenaar16ea3672013-07-28 16:02:18 +020052 xnoremap <buffer> <Plug>BUncomOn <ESC>:'<,'><CR>`<O<ESC>0i(*<ESC>`>o<ESC>0i*)<ESC>`<
53 xnoremap <buffer> <Plug>BUncomOff <ESC>:'<,'><CR>`<dd`>dd`<
Bram Moolenaar071d4272004-06-13 20:20:40 +000054
Bram Moolenaar16ea3672013-07-28 16:02:18 +020055 nmap <buffer> <LocalLeader>s <Plug>OCamlSwitchEdit
56 nmap <buffer> <LocalLeader>S <Plug>OCamlSwitchNewWin
57
58 nmap <buffer> <LocalLeader>t <Plug>OCamlPrintType
59 xmap <buffer> <LocalLeader>t <Plug>OCamlPrintType
Bram Moolenaar071d4272004-06-13 20:20:40 +000060endif
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 Moolenaar16ea3672013-07-28 16:02:18 +020076 nnoremap <Plug>OCamlSwitchEdit :<C-u>call OCaml_switch(0)<CR>
77 nnoremap <Plug>OCamlSwitchNewWin :<C-u>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
Bram Moolenaar16ea3672013-07-28 16:02:18 +0200134let b:undo_ftplugin = "setlocal efm< foldmethod< foldexpr<"
135 \ . "| unlet! b:mw b:match_words b:match_ignorecase"
136
137
Bram Moolenaara5792f52005-11-23 21:25:05 +0000138" - Only definitions below, executed once -------------------------------------
139
140if exists("*OMLetFoldLevel")
141 finish
142endif
143
144function s:topindent(lnum)
145 let l = a:lnum
146 while l > 0
147 if getline(l) =~ '\s*\%(\<struct\>\|\<sig\>\|\<object\>\)'
148 return indent(l)
149 endif
150 let l = l-1
151 endwhile
152 return -s:i
153endfunction
154
155function OMLetFoldLevel(l)
156
157 " This is for not merging blank lines around folds to them
158 if getline(a:l) !~ '\S'
159 return -1
160 endif
161
162 " We start folds for modules, classes, and every toplevel definition
163 if getline(a:l) =~ '^\s*\%(\<val\>\|\<module\>\|\<class\>\|\<type\>\|\<method\>\|\<initializer\>\|\<inherit\>\|\<exception\>\|\<external\>\)'
164 exe 'return ">' (indent(a:l)/s:i)+1 '"'
165 endif
166
167 " Toplevel let are detected thanks to the indentation
168 if getline(a:l) =~ '^\s*let\>' && indent(a:l) == s:i+s:topindent(a:l)
169 exe 'return ">' (indent(a:l)/s:i)+1 '"'
170 endif
171
172 " We close fold on end which are associated to struct, sig or object.
173 " We use syntax information to do that.
174 if getline(a:l) =~ '^\s*end\>' && synIDattr(synID(a:l, indent(a:l)+1, 0), "name") != "ocamlKeyword"
175 return (indent(a:l)/s:i)+1
176 endif
177
178 " Folds end on ;;
179 if getline(a:l) =~ '^\s*;;'
180 exe 'return "<' (indent(a:l)/s:i)+1 '"'
181 endif
182
183 " Comments around folds aren't merged to them.
184 if synIDattr(synID(a:l, indent(a:l)+1, 0), "name") == "ocamlComment"
185 return -1
186 endif
187
188 return '='
189endfunction
190
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000191" Vim support for OCaml .annot files
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000192"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000193" Last Change: 2007 Jul 17
194" Maintainer: Vincent Aravantinos <vincent.aravantinos@gmail.com>
195" License: public domain
196"
197" Originally inspired by 'ocaml-dtypes.vim' by Stefano Zacchiroli.
198" The source code is quite radically different for we not use python anymore.
199" However this plugin should have the exact same behaviour, that's why the
200" following lines are the quite exact copy of Stefano's original plugin :
201"
202" <<
203" Executing Ocaml_print_type(<mode>) function will display in the Vim bottom
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000204" line(s) the type of an ocaml value getting it from the corresponding .annot
205" file (if any). If Vim is in visual mode, <mode> should be "visual" and the
206" selected ocaml value correspond to the highlighted text, otherwise (<mode>
207" can be anything else) it corresponds to the literal found at the current
208" cursor position.
209"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000210" Typing '<LocalLeader>t' (LocalLeader defaults to '\', see :h LocalLeader)
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100211" will cause " Ocaml_print_type function to be invoked with the right
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000212" argument depending on the current mode (visual or not).
213" >>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000214"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000215" If you find something not matching this behaviour, please signal it.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000216"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000217" Differences are:
218" - no need for python support
219" + plus : more portable
220" + minus: no more lazy parsing, it looks very fast however
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100221"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000222" - ocamlbuild support, ie.
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100223" + the plugin finds the _build directory and looks for the
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000224" corresponding file inside;
225" + if the user decides to change the name of the _build directory thanks
226" to the '-build-dir' option of ocamlbuild, the plugin will manage in
227" most cases to find it out (most cases = if the source file has a unique
228" name among your whole project);
229" + if ocamlbuild is not used, the usual behaviour holds; ie. the .annot
230" file should be in the same directory as the source file;
231" + for vim plugin programmers:
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100232" the variable 'b:_build_dir' contains the inferred path to the build
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000233" directory, even if this one is not named '_build'.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000234"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000235" Bonus :
236" - latin1 accents are handled
237" - lists are handled, even on multiple lines, you don't need the visual mode
238" (the cursor must be on the first bracket)
239" - parenthesized expressions, arrays, and structures (ie. '(...)', '[|...|]',
240" and '{...}') are handled the same way
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000241
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000242 " Copied from Stefano's original plugin :
243 " <<
244 " .annot ocaml file representation
245 "
246 " File format (copied verbatim from caml-types.el)
247 "
248 " file ::= block *
249 " block ::= position <SP> position <LF> annotation *
250 " position ::= filename <SP> num <SP> num <SP> num
251 " annotation ::= keyword open-paren <LF> <SP> <SP> data <LF> close-paren
252 "
253 " <SP> is a space character (ASCII 0x20)
254 " <LF> is a line-feed character (ASCII 0x0A)
255 " num is a sequence of decimal digits
256 " filename is a string with the lexical conventions of O'Caml
257 " open-paren is an open parenthesis (ASCII 0x28)
258 " close-paren is a closed parenthesis (ASCII 0x29)
259 " data is any sequence of characters where <LF> is always followed by
260 " at least two space characters.
261 "
262 " - in each block, the two positions are respectively the start and the
263 " end of the range described by the block.
264 " - in a position, the filename is the name of the file, the first num
265 " is the line number, the second num is the offset of the beginning
266 " of the line, the third num is the offset of the position itself.
267 " - the char number within the line is the difference between the third
268 " and second nums.
269 "
270 " For the moment, the only possible keyword is \"type\"."
271 " >>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000272
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000273
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000274" 1. Finding the annotation file even if we use ocamlbuild
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000275
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000276 " In: two strings representing paths
277 " Out: one string representing the common prefix between the two paths
278 function! s:Find_common_path (p1,p2)
279 let temp = a:p2
280 while matchstr(a:p1,temp) == ''
281 let temp = substitute(temp,'/[^/]*$','','')
282 endwhile
283 return temp
284 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000285
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000286 " After call:
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100287 "
288 " Following information have been put in s:annot_file_list, using
289 " annot_file_name name as key:
290 " - annot_file_path :
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000291 " path to the .annot file corresponding to the
292 " source file (dealing with ocamlbuild stuff)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100293 " - _build_path:
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000294 " path to the build directory even if this one is
295 " not named '_build'
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100296 " - date_of_last annot:
297 " Set to 0 until we load the file. It contains the
298 " date at which the file has been loaded.
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000299 function! s:Locate_annotation()
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100300 let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot'
301 if !exists ("s:annot_file_list[annot_file_name]")
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000302 silent exe 'cd' s:Fnameescape(expand('%:p:h'))
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000303 " 1st case : the annot file is in the same directory as the buffer (no ocamlbuild)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100304 let annot_file_path = findfile(annot_file_name,'.')
305 if annot_file_path != ''
306 let annot_file_path = getcwd().'/'.annot_file_path
307 let _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 "
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100315 let _build_path = finddir('_build','.')
316 if _build_path != ''
317 let _build_path = getcwd().'/'._build_path
318 let annot_file_path = findfile(annot_file_name,'_build')
319 if annot_file_path != ''
320 let annot_file_path = getcwd().'/'.annot_file_path
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000321 endif
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000322 else
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100323 " 3rd case : the _build directory is in a directory higher in the file hierarchy
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000324 " (it can't be deeper by ocamlbuild requirements)
325 " ..
326 " / \
327 " / \
328 " _build ...
329 " \
330 " \
331 " .ml
332 "
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100333 let _build_path = finddir('_build',';')
334 if _build_path != ''
335 let project_path = substitute(_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 Moolenaarb8ff1fb2012-02-04 21:59:01 +0100337 let annot_file_path = findfile(annot_file_name,project_path.'/_build/'.path_relative_to_project)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000338 else
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100339 let annot_file_path = findfile(annot_file_name,'**')
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000340 "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
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100342 if annot_file_path != ''
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000343 " 4a. we suppose the renamed _build directory is in the current directory
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100344 let _build_path = matchstr(annot_file_path,'^[^/]*')
345 if annot_file_path != ''
346 let annot_file_path = getcwd().'/'.annot_file_path
347 let _build_path = getcwd().'/'._build_path
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000348 endif
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000349 else
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100350 let annot_file_name = ''
351 "(Pierre Vittet: I have commented 4b because this was chrashing
352 "my vim (it produced infinite loop))
353 "
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000354 " 4b. anarchy : the renamed _build directory may be higher in the hierarchy
355 " this will work if the file for which we are looking annotations has a unique name in the whole project
356 " if this is not the case, it may still work, but no warranty here
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100357 "let annot_file_path = findfile(annot_file_name,'**;')
358 "let project_path = s:Find_common_path(annot_file_path,expand('%:p:h'))
359 "let _build_path = matchstr(annot_file_path,project_path.'/[^/]*')
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000360 endif
361 endif
362 endif
363 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000364
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100365 if annot_file_path == ''
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000366 throw 'E484: no annotation file found'
367 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000368
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000369 silent exe 'cd' '-'
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100370 let s:annot_file_list[annot_file_name]= [annot_file_path, _build_path, 0]
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000371 endif
372 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000373
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100374 " This variable contain a dictionnary of list. Each element of the dictionnary
Bram Moolenaara6c27c42019-05-09 19:16:22 +0200375 " represent an annotation system. An annotation system is a list with:
376 " - annotation file name as its key
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100377 " - annotation file path as first element of the contained list
378 " - build path as second element of the contained list
379 " - annot_file_last_mod (contain the date of .annot file) as third element
380 let s:annot_file_list = {}
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
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100383
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000384 " 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.
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100390 function! s:Enter_annotation_buffer(annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000391 let s:current_pos = getpos('.')
392 let s:current_hidden = &l:hidden
393 set hidden
394 let s:current_buf = bufname('%')
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100395 if bufloaded(a:annot_file_path)
396 silent exe 'keepj keepalt' 'buffer' s:Fnameescape(a:annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000397 else
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100398 silent exe 'keepj keepalt' 'view' s:Fnameescape(a:annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000399 endif
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100400 call setpos(".", [0, 0 , 0 , 0])
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000401 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000402
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000403 " After call:
404 " The original buffer has been restored in the exact same state as before.
405 function! s:Exit_annotation_buffer()
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000406 silent exe 'keepj keepalt' 'buffer' s:Fnameescape(s:current_buf)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000407 let &l:hidden = s:current_hidden
408 call setpos('.',s:current_pos)
409 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000410
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000411 " After call:
412 " The annot file is loaded and assigned to a buffer.
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100413 " This also handles the modification date of the .annot file, eg. after a
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100414 " compilation (return an updated annot_file_list).
415 function! s:Load_annotation(annot_file_name)
416 let annot = s:annot_file_list[a:annot_file_name]
417 let annot_file_path = annot[0]
418 let annot_file_last_mod = 0
419 if exists("annot[2]")
420 let annot_file_last_mod = annot[2]
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000421 endif
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100422 if bufloaded(annot_file_path) && annot_file_last_mod < getftime(annot_file_path)
423 " if there is a more recent file
424 let nr = bufnr(annot_file_path)
425 silent exe 'keepj keepalt' 'bunload' nr
426 endif
427 if !bufloaded(annot_file_path)
428 call s:Enter_annotation_buffer(annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000429 setlocal nobuflisted
430 setlocal bufhidden=hide
431 setlocal noswapfile
432 setlocal buftype=nowrite
433 call s:Exit_annotation_buffer()
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100434 let annot[2] = getftime(annot_file_path)
435 " List updated with the new date
436 let s:annot_file_list[a:annot_file_name] = annot
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000437 endif
438 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100439
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000440 "b. 'search' and 'match' work to find the type information
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100441
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000442 "In: - lin1,col1: postion of expression first char
443 " - lin2,col2: postion of expression last char
444 "Out: - the pattern to be looked for to find the block
445 " Must be called in the source buffer (use of line2byte)
446 function! s:Block_pattern(lin1,lin2,col1,col2)
447 let start_num1 = a:lin1
448 let start_num2 = line2byte(a:lin1) - 1
449 let start_num3 = start_num2 + a:col1
Bram Moolenaar9c754c42010-07-10 15:52:35 +0200450 let path = '"\(\\"\|[^"]\)\+"'
451 let start_pos = path.' '.start_num1.' '.start_num2.' '.start_num3
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000452 let end_num1 = a:lin2
453 let end_num2 = line2byte(a:lin2) - 1
454 let end_num3 = end_num2 + a:col2
Bram Moolenaar9c754c42010-07-10 15:52:35 +0200455 let end_pos = path.' '.end_num1.' '.end_num2.' '.end_num3
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000456 return '^'.start_pos.' '.end_pos."$"
457 " rq: the '^' here is not totally correct regarding the annot file "grammar"
458 " but currently the annotation file respects this, and it's a little bit faster with the '^';
459 " can be removed safely.
460 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000461
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000462 "In: (the cursor position should be at the start of an annotation)
463 "Out: the type information
464 " Must be called in the annotation buffer (use of search)
465 function! s:Match_data()
466 " rq: idem as previously, in the following, the '^' at start of patterns is not necessary
467 keepj while search('^type($','ce',line(".")) == 0
468 keepj if search('^.\{-}($','e') == 0
469 throw "no_annotation"
470 endif
471 keepj if searchpair('(','',')') == 0
472 throw "malformed_annot_file"
473 endif
474 endwhile
475 let begin = line(".") + 1
476 keepj if searchpair('(','',')') == 0
477 throw "malformed_annot_file"
478 endif
479 let end = line(".") - 1
480 return join(getline(begin,end),"\n")
481 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100482
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000483 "In: the pattern to look for in order to match the block
484 "Out: the type information (calls s:Match_data)
485 " Should be called in the annotation buffer
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100486 function! s:Extract_type_data(block_pattern, annot_file_name)
487 let annot_file_path = s:annot_file_list[a:annot_file_name][0]
488 call s:Enter_annotation_buffer(annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000489 try
490 if search(a:block_pattern,'e') == 0
491 throw "no_annotation"
492 endif
493 call cursor(line(".") + 1,1)
494 let annotation = s:Match_data()
495 finally
496 call s:Exit_annotation_buffer()
497 endtry
498 return annotation
499 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100500
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000501 "c. link this stuff with what the user wants
502 " ie. get the expression selected/under the cursor
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100503
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000504 let s:ocaml_word_char = '\w|[À-ÿ]|'''
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000505
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000506 "In: the current mode (eg. "visual", "normal", etc.)
507 "Out: the borders of the expression we are looking for the type
508 function! s:Match_borders(mode)
509 if a:mode == "visual"
510 let cur = getpos(".")
511 normal `<
512 let col1 = col(".")
513 let lin1 = line(".")
514 normal `>
515 let col2 = col(".")
516 let lin2 = line(".")
517 call cursor(cur[1],cur[2])
518 return [lin1,lin2,col1-1,col2]
519 else
520 let cursor_line = line(".")
521 let cursor_col = col(".")
522 let line = getline('.')
523 if line[cursor_col-1:cursor_col] == '[|'
524 let [lin2,col2] = searchpairpos('\[|','','|\]','n')
525 return [cursor_line,lin2,cursor_col-1,col2+1]
526 elseif line[cursor_col-1] == '['
527 let [lin2,col2] = searchpairpos('\[','','\]','n')
528 return [cursor_line,lin2,cursor_col-1,col2]
529 elseif line[cursor_col-1] == '('
530 let [lin2,col2] = searchpairpos('(','',')','n')
531 return [cursor_line,lin2,cursor_col-1,col2]
532 elseif line[cursor_col-1] == '{'
533 let [lin2,col2] = searchpairpos('{','','}','n')
534 return [cursor_line,lin2,cursor_col-1,col2]
535 else
536 let [lin1,col1] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','ncb')
537 let [lin2,col2] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','nce')
538 if col1 == 0 || col2 == 0
539 throw "no_expression"
540 endif
541 return [cursor_line,cursor_line,col1-1,col2]
542 endif
543 endif
544 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000545
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000546 "In: the current mode (eg. "visual", "normal", etc.)
547 "Out: the type information (calls s:Extract_type_data)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100548 function! s:Get_type(mode, annot_file_name)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000549 let [lin1,lin2,col1,col2] = s:Match_borders(a:mode)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100550 return s:Extract_type_data(s:Block_pattern(lin1,lin2,col1,col2), a:annot_file_name)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000551 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100552
Bram Moolenaar16ea3672013-07-28 16:02:18 +0200553 "In: A string destined to be printed in the 'echo buffer'. It has line
554 "break and 2 space at each line beginning.
555 "Out: A string destined to be yanked, without space and double space.
556 function s:unformat_ocaml_type(res)
557 "Remove end of line.
558 let res = substitute (a:res, "\n", "", "g" )
559 "remove double space
560 let res =substitute(res , " ", " ", "g")
561 "remove space at begining of string.
562 let res = substitute(res, "^ *", "", "g")
563 return res
564 endfunction
565
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000566 "d. main
567 "In: the current mode (eg. "visual", "normal", etc.)
568 "After call: the type information is displayed
569 if !exists("*Ocaml_get_type")
570 function Ocaml_get_type(mode)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100571 let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot'
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000572 call s:Locate_annotation()
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100573 call s:Load_annotation(annot_file_name)
Bram Moolenaar16ea3672013-07-28 16:02:18 +0200574 let res = s:Get_type(a:mode, annot_file_name)
575 " Copy result in the unnamed buffer
576 let @" = s:unformat_ocaml_type(res)
577 return res
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000578 endfun
579 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000580
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000581 if !exists("*Ocaml_get_type_or_not")
582 function Ocaml_get_type_or_not(mode)
583 let t=reltime()
584 try
Bram Moolenaar16ea3672013-07-28 16:02:18 +0200585 let res = Ocaml_get_type(a:mode)
586 return res
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000587 catch
588 return ""
589 endtry
590 endfun
591 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000592
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000593 if !exists("*Ocaml_print_type")
594 function Ocaml_print_type(mode)
595 if expand("%:e") == "mli"
596 echohl ErrorMsg | echo "No annotations for interface (.mli) files" | echohl None
597 return
598 endif
599 try
600 echo Ocaml_get_type(a:mode)
601 catch /E484:/
602 echohl ErrorMsg | echo "No type annotations (.annot) file found" | echohl None
603 catch /no_expression/
604 echohl ErrorMsg | echo "No expression found under the cursor" | echohl None
605 catch /no_annotation/
606 echohl ErrorMsg | echo "No type annotation found for the given text" | echohl None
607 catch /malformed_annot_file/
608 echohl ErrorMsg | echo "Malformed .annot file" | echohl None
609 endtry
610 endfun
611 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000612
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000613" Maps
Bram Moolenaar16ea3672013-07-28 16:02:18 +0200614 nnoremap <silent> <Plug>OCamlPrintType :<C-U>call Ocaml_print_type("normal")<CR>
615 xnoremap <silent> <Plug>OCamlPrintType :<C-U>call Ocaml_print_type("visual")<CR>`<
Bram Moolenaara5792f52005-11-23 21:25:05 +0000616
617let &cpoptions=s:cposet
618unlet s:cposet
619
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000620" vim:sw=2 fdm=indent