blob: 793b887afc99fa8ec68f079959b8f2a1e51ec157 [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 Moolenaar7e6a5152021-01-02 16:39:53 +01008" URL: https://github.com/ocaml/vim-ocaml
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +01009" Last Change:
Bram Moolenaar790c18b2019-07-04 17:22:06 +020010" 2013 Oct 27 - Added commentstring (MM)
Bram Moolenaar16ea3672013-07-28 16:02:18 +020011" 2013 Jul 26 - load default compiler settings (MM)
12" 2013 Jul 24 - removed superfluous efm-setting (MM)
13" 2013 Jul 22 - applied fixes supplied by Hirotaka Hamada (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
Bram Moolenaar790c18b2019-07-04 17:22:06 +020040" Comment string
Bram Moolenaar7e6a5152021-01-02 16:39:53 +010041setlocal comments=sr:(*\ ,mb:\ ,ex:*)
42setlocal comments^=sr:(**,mb:\ \ ,ex:*)
Bram Moolenaar790c18b2019-07-04 17:22:06 +020043setlocal commentstring=(*%s*)
44
Bram Moolenaar071d4272004-06-13 20:20:40 +000045" Add mappings, unless the user didn't want this.
46if !exists("no_plugin_maps") && !exists("no_ocaml_maps")
Bram Moolenaara5792f52005-11-23 21:25:05 +000047 " (un)commenting
Bram Moolenaar071d4272004-06-13 20:20:40 +000048 if !hasmapto('<Plug>Comment')
49 nmap <buffer> <LocalLeader>c <Plug>LUncomOn
Bram Moolenaar16ea3672013-07-28 16:02:18 +020050 xmap <buffer> <LocalLeader>c <Plug>BUncomOn
Bram Moolenaar071d4272004-06-13 20:20:40 +000051 nmap <buffer> <LocalLeader>C <Plug>LUncomOff
Bram Moolenaar16ea3672013-07-28 16:02:18 +020052 xmap <buffer> <LocalLeader>C <Plug>BUncomOff
Bram Moolenaar071d4272004-06-13 20:20:40 +000053 endif
54
Bram Moolenaar16ea3672013-07-28 16:02:18 +020055 nnoremap <buffer> <Plug>LUncomOn gI(* <End> *)<ESC>
Bram Moolenaara5792f52005-11-23 21:25:05 +000056 nnoremap <buffer> <Plug>LUncomOff :s/^(\* \(.*\) \*)/\1/<CR>:noh<CR>
Bram Moolenaar16ea3672013-07-28 16:02:18 +020057 xnoremap <buffer> <Plug>BUncomOn <ESC>:'<,'><CR>`<O<ESC>0i(*<ESC>`>o<ESC>0i*)<ESC>`<
58 xnoremap <buffer> <Plug>BUncomOff <ESC>:'<,'><CR>`<dd`>dd`<
Bram Moolenaar071d4272004-06-13 20:20:40 +000059
Bram Moolenaar16ea3672013-07-28 16:02:18 +020060 nmap <buffer> <LocalLeader>s <Plug>OCamlSwitchEdit
61 nmap <buffer> <LocalLeader>S <Plug>OCamlSwitchNewWin
62
63 nmap <buffer> <LocalLeader>t <Plug>OCamlPrintType
64 xmap <buffer> <LocalLeader>t <Plug>OCamlPrintType
Bram Moolenaar071d4272004-06-13 20:20:40 +000065endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000066
67" Let % jump between structure elements (due to Issac Trotts)
Bram Moolenaar790c18b2019-07-04 17:22:06 +020068let b:mw = '\<let\>:\<and\>:\(\<in\>\|;;\)'
Bram Moolenaara5792f52005-11-23 21:25:05 +000069let b:mw = b:mw . ',\<if\>:\<then\>:\<else\>'
Bram Moolenaar790c18b2019-07-04 17:22:06 +020070let b:mw = b:mw . ',\<\(for\|while\)\>:\<do\>:\<done\>'
Bram Moolenaara5792f52005-11-23 21:25:05 +000071let b:mw = b:mw . ',\<\(object\|sig\|struct\|begin\)\>:\<end\>'
72let b:mw = b:mw . ',\<\(match\|try\)\>:\<with\>'
73let b:match_words = b:mw
74
75let b:match_ignorecase=0
Bram Moolenaar5eb86f92004-07-26 12:53:41 +000076
Bram Moolenaar790c18b2019-07-04 17:22:06 +020077function! s:OcpGrep(bang,args) abort
78 let grepprg = &l:grepprg
79 let grepformat = &l:grepformat
80 let shellpipe = &shellpipe
81 try
82 let &l:grepprg = "ocp-grep -c never"
83 setlocal grepformat=%f:%l:%m
84 if &shellpipe ==# '2>&1| tee' || &shellpipe ==# '|& tee'
85 let &shellpipe = "| tee"
86 endif
87 execute 'grep! '.a:args
88 if empty(a:bang) && !empty(getqflist())
89 return 'cfirst'
90 else
91 return ''
92 endif
93 finally
94 let &l:grepprg = grepprg
95 let &l:grepformat = grepformat
96 let &shellpipe = shellpipe
97 endtry
98endfunction
99command! -bar -bang -complete=file -nargs=+ Ocpgrep exe s:OcpGrep(<q-bang>, <q-args>)
100
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000101" switching between interfaces (.mli) and implementations (.ml)
102if !exists("g:did_ocaml_switch")
103 let g:did_ocaml_switch = 1
Bram Moolenaar16ea3672013-07-28 16:02:18 +0200104 nnoremap <Plug>OCamlSwitchEdit :<C-u>call OCaml_switch(0)<CR>
105 nnoremap <Plug>OCamlSwitchNewWin :<C-u>call OCaml_switch(1)<CR>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000106 fun OCaml_switch(newwin)
107 if (match(bufname(""), "\\.mli$") >= 0)
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000108 let fname = s:Fnameescape(substitute(bufname(""), "\\.mli$", ".ml", ""))
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000109 if (a:newwin == 1)
Bram Moolenaara5792f52005-11-23 21:25:05 +0000110 exec "new " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000111 else
Bram Moolenaara5792f52005-11-23 21:25:05 +0000112 exec "arge " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000113 endif
114 elseif (match(bufname(""), "\\.ml$") >= 0)
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000115 let fname = s:Fnameescape(bufname("")) . "i"
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000116 if (a:newwin == 1)
Bram Moolenaara5792f52005-11-23 21:25:05 +0000117 exec "new " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000118 else
Bram Moolenaara5792f52005-11-23 21:25:05 +0000119 exec "arge " . fname
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000120 endif
121 endif
122 endfun
123endif
124
Bram Moolenaara5792f52005-11-23 21:25:05 +0000125" Folding support
126
127" Get the modeline because folding depends on indentation
Bram Moolenaar790c18b2019-07-04 17:22:06 +0200128let lnum = search('^\s*(\*:o\?caml:', 'n')
129let s:modeline = lnum? getline(lnum): ""
Bram Moolenaara5792f52005-11-23 21:25:05 +0000130
131" Get the indentation params
132let s:m = matchstr(s:modeline,'default\s*=\s*\d\+')
133if s:m != ""
134 let s:idef = matchstr(s:m,'\d\+')
135elseif exists("g:omlet_indent")
136 let s:idef = g:omlet_indent
137else
138 let s:idef = 2
139endif
140let s:m = matchstr(s:modeline,'struct\s*=\s*\d\+')
141if s:m != ""
142 let s:i = matchstr(s:m,'\d\+')
143elseif exists("g:omlet_indent_struct")
144 let s:i = g:omlet_indent_struct
145else
146 let s:i = s:idef
147endif
148
149" Set the folding method
150if exists("g:ocaml_folding")
151 setlocal foldmethod=expr
152 setlocal foldexpr=OMLetFoldLevel(v:lnum)
153endif
154
Bram Moolenaar16ea3672013-07-28 16:02:18 +0200155let b:undo_ftplugin = "setlocal efm< foldmethod< foldexpr<"
156 \ . "| unlet! b:mw b:match_words b:match_ignorecase"
157
158
Bram Moolenaara5792f52005-11-23 21:25:05 +0000159" - Only definitions below, executed once -------------------------------------
160
161if exists("*OMLetFoldLevel")
162 finish
163endif
164
165function s:topindent(lnum)
166 let l = a:lnum
167 while l > 0
168 if getline(l) =~ '\s*\%(\<struct\>\|\<sig\>\|\<object\>\)'
169 return indent(l)
170 endif
171 let l = l-1
172 endwhile
173 return -s:i
174endfunction
175
176function OMLetFoldLevel(l)
177
178 " This is for not merging blank lines around folds to them
179 if getline(a:l) !~ '\S'
180 return -1
181 endif
182
183 " We start folds for modules, classes, and every toplevel definition
184 if getline(a:l) =~ '^\s*\%(\<val\>\|\<module\>\|\<class\>\|\<type\>\|\<method\>\|\<initializer\>\|\<inherit\>\|\<exception\>\|\<external\>\)'
185 exe 'return ">' (indent(a:l)/s:i)+1 '"'
186 endif
187
188 " Toplevel let are detected thanks to the indentation
189 if getline(a:l) =~ '^\s*let\>' && indent(a:l) == s:i+s:topindent(a:l)
190 exe 'return ">' (indent(a:l)/s:i)+1 '"'
191 endif
192
193 " We close fold on end which are associated to struct, sig or object.
194 " We use syntax information to do that.
195 if getline(a:l) =~ '^\s*end\>' && synIDattr(synID(a:l, indent(a:l)+1, 0), "name") != "ocamlKeyword"
196 return (indent(a:l)/s:i)+1
197 endif
198
199 " Folds end on ;;
200 if getline(a:l) =~ '^\s*;;'
201 exe 'return "<' (indent(a:l)/s:i)+1 '"'
202 endif
203
204 " Comments around folds aren't merged to them.
205 if synIDattr(synID(a:l, indent(a:l)+1, 0), "name") == "ocamlComment"
206 return -1
207 endif
208
209 return '='
210endfunction
211
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000212" Vim support for OCaml .annot files
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000213"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000214" Last Change: 2007 Jul 17
215" Maintainer: Vincent Aravantinos <vincent.aravantinos@gmail.com>
216" License: public domain
217"
218" Originally inspired by 'ocaml-dtypes.vim' by Stefano Zacchiroli.
219" The source code is quite radically different for we not use python anymore.
220" However this plugin should have the exact same behaviour, that's why the
221" following lines are the quite exact copy of Stefano's original plugin :
222"
223" <<
224" Executing Ocaml_print_type(<mode>) function will display in the Vim bottom
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000225" line(s) the type of an ocaml value getting it from the corresponding .annot
226" file (if any). If Vim is in visual mode, <mode> should be "visual" and the
227" selected ocaml value correspond to the highlighted text, otherwise (<mode>
228" can be anything else) it corresponds to the literal found at the current
229" cursor position.
230"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000231" Typing '<LocalLeader>t' (LocalLeader defaults to '\', see :h LocalLeader)
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100232" will cause " Ocaml_print_type function to be invoked with the right
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000233" argument depending on the current mode (visual or not).
234" >>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000235"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000236" If you find something not matching this behaviour, please signal it.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000237"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000238" Differences are:
239" - no need for python support
240" + plus : more portable
241" + minus: no more lazy parsing, it looks very fast however
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100242"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000243" - ocamlbuild support, ie.
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100244" + the plugin finds the _build directory and looks for the
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000245" corresponding file inside;
246" + if the user decides to change the name of the _build directory thanks
247" to the '-build-dir' option of ocamlbuild, the plugin will manage in
248" most cases to find it out (most cases = if the source file has a unique
249" name among your whole project);
250" + if ocamlbuild is not used, the usual behaviour holds; ie. the .annot
251" file should be in the same directory as the source file;
252" + for vim plugin programmers:
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100253" the variable 'b:_build_dir' contains the inferred path to the build
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000254" directory, even if this one is not named '_build'.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000255"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000256" Bonus :
257" - latin1 accents are handled
258" - lists are handled, even on multiple lines, you don't need the visual mode
259" (the cursor must be on the first bracket)
260" - parenthesized expressions, arrays, and structures (ie. '(...)', '[|...|]',
261" and '{...}') are handled the same way
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000262
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000263 " Copied from Stefano's original plugin :
264 " <<
265 " .annot ocaml file representation
266 "
267 " File format (copied verbatim from caml-types.el)
268 "
269 " file ::= block *
270 " block ::= position <SP> position <LF> annotation *
271 " position ::= filename <SP> num <SP> num <SP> num
272 " annotation ::= keyword open-paren <LF> <SP> <SP> data <LF> close-paren
273 "
274 " <SP> is a space character (ASCII 0x20)
275 " <LF> is a line-feed character (ASCII 0x0A)
276 " num is a sequence of decimal digits
277 " filename is a string with the lexical conventions of O'Caml
278 " open-paren is an open parenthesis (ASCII 0x28)
279 " close-paren is a closed parenthesis (ASCII 0x29)
280 " data is any sequence of characters where <LF> is always followed by
281 " at least two space characters.
282 "
283 " - in each block, the two positions are respectively the start and the
284 " end of the range described by the block.
285 " - in a position, the filename is the name of the file, the first num
286 " is the line number, the second num is the offset of the beginning
287 " of the line, the third num is the offset of the position itself.
288 " - the char number within the line is the difference between the third
289 " and second nums.
290 "
291 " For the moment, the only possible keyword is \"type\"."
292 " >>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000293
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000294
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000295" 1. Finding the annotation file even if we use ocamlbuild
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000296
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000297 " In: two strings representing paths
298 " Out: one string representing the common prefix between the two paths
299 function! s:Find_common_path (p1,p2)
300 let temp = a:p2
301 while matchstr(a:p1,temp) == ''
302 let temp = substitute(temp,'/[^/]*$','','')
303 endwhile
304 return temp
305 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000306
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000307 " After call:
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100308 "
309 " Following information have been put in s:annot_file_list, using
310 " annot_file_name name as key:
311 " - annot_file_path :
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000312 " path to the .annot file corresponding to the
313 " source file (dealing with ocamlbuild stuff)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100314 " - _build_path:
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000315 " path to the build directory even if this one is
316 " not named '_build'
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100317 " - date_of_last annot:
318 " Set to 0 until we load the file. It contains the
319 " date at which the file has been loaded.
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000320 function! s:Locate_annotation()
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100321 let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot'
322 if !exists ("s:annot_file_list[annot_file_name]")
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000323 silent exe 'cd' s:Fnameescape(expand('%:p:h'))
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000324 " 1st case : the annot file is in the same directory as the buffer (no ocamlbuild)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100325 let annot_file_path = findfile(annot_file_name,'.')
326 if annot_file_path != ''
327 let annot_file_path = getcwd().'/'.annot_file_path
328 let _build_path = ''
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000329 else
330 " 2nd case : the buffer and the _build directory are in the same directory
331 " ..
332 " / \
333 " / \
334 " _build .ml
335 "
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100336 let _build_path = finddir('_build','.')
337 if _build_path != ''
338 let _build_path = getcwd().'/'._build_path
339 let annot_file_path = findfile(annot_file_name,'_build')
340 if annot_file_path != ''
341 let annot_file_path = getcwd().'/'.annot_file_path
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000342 endif
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000343 else
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100344 " 3rd case : the _build directory is in a directory higher in the file hierarchy
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000345 " (it can't be deeper by ocamlbuild requirements)
346 " ..
347 " / \
348 " / \
349 " _build ...
350 " \
351 " \
352 " .ml
353 "
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100354 let _build_path = finddir('_build',';')
355 if _build_path != ''
356 let project_path = substitute(_build_path,'/_build$','','')
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000357 let path_relative_to_project = s:Fnameescape(substitute(expand('%:p:h'),project_path.'/','',''))
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100358 let annot_file_path = findfile(annot_file_name,project_path.'/_build/'.path_relative_to_project)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000359 else
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100360 let annot_file_path = findfile(annot_file_name,'**')
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000361 "4th case : what if the user decided to change the name of the _build directory ?
362 " -> we relax the constraints, it should work in most cases
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100363 if annot_file_path != ''
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000364 " 4a. we suppose the renamed _build directory is in the current directory
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100365 let _build_path = matchstr(annot_file_path,'^[^/]*')
366 if annot_file_path != ''
367 let annot_file_path = getcwd().'/'.annot_file_path
368 let _build_path = getcwd().'/'._build_path
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000369 endif
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000370 else
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100371 let annot_file_name = ''
372 "(Pierre Vittet: I have commented 4b because this was chrashing
373 "my vim (it produced infinite loop))
374 "
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000375 " 4b. anarchy : the renamed _build directory may be higher in the hierarchy
376 " this will work if the file for which we are looking annotations has a unique name in the whole project
377 " if this is not the case, it may still work, but no warranty here
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100378 "let annot_file_path = findfile(annot_file_name,'**;')
379 "let project_path = s:Find_common_path(annot_file_path,expand('%:p:h'))
380 "let _build_path = matchstr(annot_file_path,project_path.'/[^/]*')
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000381 endif
382 endif
383 endif
384 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000385
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100386 if annot_file_path == ''
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000387 throw 'E484: no annotation file found'
388 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000389
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000390 silent exe 'cd' '-'
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100391 let s:annot_file_list[annot_file_name]= [annot_file_path, _build_path, 0]
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000392 endif
393 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000394
Bram Moolenaar7e6a5152021-01-02 16:39:53 +0100395 " This variable contains a dictionary of lists. Each element of the dictionary
396 " represents an annotation system. An annotation system is a list with:
397 " - annotation file name as its key
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100398 " - annotation file path as first element of the contained list
399 " - build path as second element of the contained list
400 " - annot_file_last_mod (contain the date of .annot file) as third element
401 let s:annot_file_list = {}
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000402
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000403" 2. Finding the type information in the annotation file
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100404
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000405 " a. The annotation file is opened in vim as a buffer that
406 " should be (almost) invisible to the user.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000407
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000408 " After call:
409 " The current buffer is now the one containing the .annot file.
410 " We manage to keep all this hidden to the user's eye.
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100411 function! s:Enter_annotation_buffer(annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000412 let s:current_pos = getpos('.')
413 let s:current_hidden = &l:hidden
414 set hidden
415 let s:current_buf = bufname('%')
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100416 if bufloaded(a:annot_file_path)
417 silent exe 'keepj keepalt' 'buffer' s:Fnameescape(a:annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000418 else
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100419 silent exe 'keepj keepalt' 'view' s:Fnameescape(a:annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000420 endif
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100421 call setpos(".", [0, 0 , 0 , 0])
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000422 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000423
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000424 " After call:
425 " The original buffer has been restored in the exact same state as before.
426 function! s:Exit_annotation_buffer()
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000427 silent exe 'keepj keepalt' 'buffer' s:Fnameescape(s:current_buf)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000428 let &l:hidden = s:current_hidden
429 call setpos('.',s:current_pos)
430 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000431
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000432 " After call:
433 " The annot file is loaded and assigned to a buffer.
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100434 " This also handles the modification date of the .annot file, eg. after a
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100435 " compilation (return an updated annot_file_list).
436 function! s:Load_annotation(annot_file_name)
437 let annot = s:annot_file_list[a:annot_file_name]
438 let annot_file_path = annot[0]
439 let annot_file_last_mod = 0
440 if exists("annot[2]")
441 let annot_file_last_mod = annot[2]
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000442 endif
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100443 if bufloaded(annot_file_path) && annot_file_last_mod < getftime(annot_file_path)
444 " if there is a more recent file
445 let nr = bufnr(annot_file_path)
446 silent exe 'keepj keepalt' 'bunload' nr
447 endif
448 if !bufloaded(annot_file_path)
449 call s:Enter_annotation_buffer(annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000450 setlocal nobuflisted
451 setlocal bufhidden=hide
452 setlocal noswapfile
453 setlocal buftype=nowrite
454 call s:Exit_annotation_buffer()
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100455 let annot[2] = getftime(annot_file_path)
456 " List updated with the new date
457 let s:annot_file_list[a:annot_file_name] = annot
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000458 endif
459 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100460
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000461 "b. 'search' and 'match' work to find the type information
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100462
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000463 "In: - lin1,col1: postion of expression first char
464 " - lin2,col2: postion of expression last char
465 "Out: - the pattern to be looked for to find the block
466 " Must be called in the source buffer (use of line2byte)
467 function! s:Block_pattern(lin1,lin2,col1,col2)
468 let start_num1 = a:lin1
469 let start_num2 = line2byte(a:lin1) - 1
470 let start_num3 = start_num2 + a:col1
Bram Moolenaar9c754c42010-07-10 15:52:35 +0200471 let path = '"\(\\"\|[^"]\)\+"'
472 let start_pos = path.' '.start_num1.' '.start_num2.' '.start_num3
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000473 let end_num1 = a:lin2
474 let end_num2 = line2byte(a:lin2) - 1
475 let end_num3 = end_num2 + a:col2
Bram Moolenaar9c754c42010-07-10 15:52:35 +0200476 let end_pos = path.' '.end_num1.' '.end_num2.' '.end_num3
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000477 return '^'.start_pos.' '.end_pos."$"
478 " rq: the '^' here is not totally correct regarding the annot file "grammar"
479 " but currently the annotation file respects this, and it's a little bit faster with the '^';
480 " can be removed safely.
481 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000482
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000483 "In: (the cursor position should be at the start of an annotation)
484 "Out: the type information
485 " Must be called in the annotation buffer (use of search)
486 function! s:Match_data()
487 " rq: idem as previously, in the following, the '^' at start of patterns is not necessary
488 keepj while search('^type($','ce',line(".")) == 0
489 keepj if search('^.\{-}($','e') == 0
490 throw "no_annotation"
491 endif
492 keepj if searchpair('(','',')') == 0
493 throw "malformed_annot_file"
494 endif
495 endwhile
496 let begin = line(".") + 1
497 keepj if searchpair('(','',')') == 0
498 throw "malformed_annot_file"
499 endif
500 let end = line(".") - 1
501 return join(getline(begin,end),"\n")
502 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100503
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000504 "In: the pattern to look for in order to match the block
505 "Out: the type information (calls s:Match_data)
506 " Should be called in the annotation buffer
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100507 function! s:Extract_type_data(block_pattern, annot_file_name)
508 let annot_file_path = s:annot_file_list[a:annot_file_name][0]
509 call s:Enter_annotation_buffer(annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000510 try
511 if search(a:block_pattern,'e') == 0
512 throw "no_annotation"
513 endif
514 call cursor(line(".") + 1,1)
515 let annotation = s:Match_data()
516 finally
517 call s:Exit_annotation_buffer()
518 endtry
519 return annotation
520 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100521
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000522 "c. link this stuff with what the user wants
523 " ie. get the expression selected/under the cursor
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100524
Bram Moolenaar7e6a5152021-01-02 16:39:53 +0100525 let s:ocaml_word_char = '\w|[\xc0-\xff]|'''
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000526
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000527 "In: the current mode (eg. "visual", "normal", etc.)
528 "Out: the borders of the expression we are looking for the type
529 function! s:Match_borders(mode)
530 if a:mode == "visual"
531 let cur = getpos(".")
532 normal `<
533 let col1 = col(".")
534 let lin1 = line(".")
535 normal `>
536 let col2 = col(".")
537 let lin2 = line(".")
538 call cursor(cur[1],cur[2])
539 return [lin1,lin2,col1-1,col2]
540 else
541 let cursor_line = line(".")
542 let cursor_col = col(".")
543 let line = getline('.')
544 if line[cursor_col-1:cursor_col] == '[|'
545 let [lin2,col2] = searchpairpos('\[|','','|\]','n')
546 return [cursor_line,lin2,cursor_col-1,col2+1]
547 elseif line[cursor_col-1] == '['
548 let [lin2,col2] = searchpairpos('\[','','\]','n')
549 return [cursor_line,lin2,cursor_col-1,col2]
550 elseif line[cursor_col-1] == '('
551 let [lin2,col2] = searchpairpos('(','',')','n')
552 return [cursor_line,lin2,cursor_col-1,col2]
553 elseif line[cursor_col-1] == '{'
554 let [lin2,col2] = searchpairpos('{','','}','n')
555 return [cursor_line,lin2,cursor_col-1,col2]
556 else
557 let [lin1,col1] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','ncb')
558 let [lin2,col2] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','nce')
559 if col1 == 0 || col2 == 0
560 throw "no_expression"
561 endif
562 return [cursor_line,cursor_line,col1-1,col2]
563 endif
564 endif
565 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000566
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000567 "In: the current mode (eg. "visual", "normal", etc.)
568 "Out: the type information (calls s:Extract_type_data)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100569 function! s:Get_type(mode, annot_file_name)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000570 let [lin1,lin2,col1,col2] = s:Match_borders(a:mode)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100571 return s:Extract_type_data(s:Block_pattern(lin1,lin2,col1,col2), a:annot_file_name)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000572 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100573
Bram Moolenaar16ea3672013-07-28 16:02:18 +0200574 "In: A string destined to be printed in the 'echo buffer'. It has line
575 "break and 2 space at each line beginning.
576 "Out: A string destined to be yanked, without space and double space.
577 function s:unformat_ocaml_type(res)
578 "Remove end of line.
579 let res = substitute (a:res, "\n", "", "g" )
580 "remove double space
581 let res =substitute(res , " ", " ", "g")
582 "remove space at begining of string.
583 let res = substitute(res, "^ *", "", "g")
584 return res
585 endfunction
586
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000587 "d. main
588 "In: the current mode (eg. "visual", "normal", etc.)
589 "After call: the type information is displayed
590 if !exists("*Ocaml_get_type")
591 function Ocaml_get_type(mode)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100592 let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot'
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000593 call s:Locate_annotation()
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100594 call s:Load_annotation(annot_file_name)
Bram Moolenaar16ea3672013-07-28 16:02:18 +0200595 let res = s:Get_type(a:mode, annot_file_name)
596 " Copy result in the unnamed buffer
597 let @" = s:unformat_ocaml_type(res)
598 return res
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000599 endfun
600 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000601
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000602 if !exists("*Ocaml_get_type_or_not")
603 function Ocaml_get_type_or_not(mode)
604 let t=reltime()
605 try
Bram Moolenaar16ea3672013-07-28 16:02:18 +0200606 let res = Ocaml_get_type(a:mode)
607 return res
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000608 catch
609 return ""
610 endtry
611 endfun
612 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000613
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000614 if !exists("*Ocaml_print_type")
615 function Ocaml_print_type(mode)
616 if expand("%:e") == "mli"
617 echohl ErrorMsg | echo "No annotations for interface (.mli) files" | echohl None
618 return
619 endif
620 try
621 echo Ocaml_get_type(a:mode)
622 catch /E484:/
623 echohl ErrorMsg | echo "No type annotations (.annot) file found" | echohl None
624 catch /no_expression/
625 echohl ErrorMsg | echo "No expression found under the cursor" | echohl None
626 catch /no_annotation/
627 echohl ErrorMsg | echo "No type annotation found for the given text" | echohl None
628 catch /malformed_annot_file/
629 echohl ErrorMsg | echo "Malformed .annot file" | echohl None
630 endtry
631 endfun
632 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000633
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000634" Maps
Bram Moolenaar16ea3672013-07-28 16:02:18 +0200635 nnoremap <silent> <Plug>OCamlPrintType :<C-U>call Ocaml_print_type("normal")<CR>
636 xnoremap <silent> <Plug>OCamlPrintType :<C-U>call Ocaml_print_type("visual")<CR>`<
Bram Moolenaara5792f52005-11-23 21:25:05 +0000637
638let &cpoptions=s:cposet
639unlet s:cposet
640
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000641" vim:sw=2 fdm=indent