blob: 8a628604fabd56f8987a4233e44ddb6a6db4b51a [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")
Bram Moolenaare0e39172021-01-25 21:14:57 +0100162 let &cpoptions = s:cposet
163 unlet s:cposet
Bram Moolenaara5792f52005-11-23 21:25:05 +0000164 finish
165endif
166
167function s:topindent(lnum)
168 let l = a:lnum
169 while l > 0
170 if getline(l) =~ '\s*\%(\<struct\>\|\<sig\>\|\<object\>\)'
171 return indent(l)
172 endif
173 let l = l-1
174 endwhile
175 return -s:i
176endfunction
177
178function OMLetFoldLevel(l)
179
180 " This is for not merging blank lines around folds to them
181 if getline(a:l) !~ '\S'
182 return -1
183 endif
184
185 " We start folds for modules, classes, and every toplevel definition
186 if getline(a:l) =~ '^\s*\%(\<val\>\|\<module\>\|\<class\>\|\<type\>\|\<method\>\|\<initializer\>\|\<inherit\>\|\<exception\>\|\<external\>\)'
187 exe 'return ">' (indent(a:l)/s:i)+1 '"'
188 endif
189
190 " Toplevel let are detected thanks to the indentation
191 if getline(a:l) =~ '^\s*let\>' && indent(a:l) == s:i+s:topindent(a:l)
192 exe 'return ">' (indent(a:l)/s:i)+1 '"'
193 endif
194
195 " We close fold on end which are associated to struct, sig or object.
196 " We use syntax information to do that.
197 if getline(a:l) =~ '^\s*end\>' && synIDattr(synID(a:l, indent(a:l)+1, 0), "name") != "ocamlKeyword"
198 return (indent(a:l)/s:i)+1
199 endif
200
201 " Folds end on ;;
202 if getline(a:l) =~ '^\s*;;'
203 exe 'return "<' (indent(a:l)/s:i)+1 '"'
204 endif
205
206 " Comments around folds aren't merged to them.
207 if synIDattr(synID(a:l, indent(a:l)+1, 0), "name") == "ocamlComment"
208 return -1
209 endif
210
211 return '='
212endfunction
213
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000214" Vim support for OCaml .annot files
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000215"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000216" Last Change: 2007 Jul 17
217" Maintainer: Vincent Aravantinos <vincent.aravantinos@gmail.com>
218" License: public domain
219"
220" Originally inspired by 'ocaml-dtypes.vim' by Stefano Zacchiroli.
221" The source code is quite radically different for we not use python anymore.
222" However this plugin should have the exact same behaviour, that's why the
223" following lines are the quite exact copy of Stefano's original plugin :
224"
225" <<
226" Executing Ocaml_print_type(<mode>) function will display in the Vim bottom
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000227" line(s) the type of an ocaml value getting it from the corresponding .annot
228" file (if any). If Vim is in visual mode, <mode> should be "visual" and the
229" selected ocaml value correspond to the highlighted text, otherwise (<mode>
230" can be anything else) it corresponds to the literal found at the current
231" cursor position.
232"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000233" Typing '<LocalLeader>t' (LocalLeader defaults to '\', see :h LocalLeader)
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100234" will cause " Ocaml_print_type function to be invoked with the right
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000235" argument depending on the current mode (visual or not).
236" >>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000237"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000238" If you find something not matching this behaviour, please signal it.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000239"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000240" Differences are:
241" - no need for python support
242" + plus : more portable
243" + minus: no more lazy parsing, it looks very fast however
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100244"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000245" - ocamlbuild support, ie.
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100246" + the plugin finds the _build directory and looks for the
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000247" corresponding file inside;
248" + if the user decides to change the name of the _build directory thanks
249" to the '-build-dir' option of ocamlbuild, the plugin will manage in
250" most cases to find it out (most cases = if the source file has a unique
251" name among your whole project);
252" + if ocamlbuild is not used, the usual behaviour holds; ie. the .annot
253" file should be in the same directory as the source file;
254" + for vim plugin programmers:
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100255" the variable 'b:_build_dir' contains the inferred path to the build
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000256" directory, even if this one is not named '_build'.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000257"
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000258" Bonus :
259" - latin1 accents are handled
260" - lists are handled, even on multiple lines, you don't need the visual mode
261" (the cursor must be on the first bracket)
262" - parenthesized expressions, arrays, and structures (ie. '(...)', '[|...|]',
263" and '{...}') are handled the same way
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000264
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000265 " Copied from Stefano's original plugin :
266 " <<
267 " .annot ocaml file representation
268 "
269 " File format (copied verbatim from caml-types.el)
270 "
271 " file ::= block *
272 " block ::= position <SP> position <LF> annotation *
273 " position ::= filename <SP> num <SP> num <SP> num
274 " annotation ::= keyword open-paren <LF> <SP> <SP> data <LF> close-paren
275 "
276 " <SP> is a space character (ASCII 0x20)
277 " <LF> is a line-feed character (ASCII 0x0A)
278 " num is a sequence of decimal digits
279 " filename is a string with the lexical conventions of O'Caml
280 " open-paren is an open parenthesis (ASCII 0x28)
281 " close-paren is a closed parenthesis (ASCII 0x29)
282 " data is any sequence of characters where <LF> is always followed by
283 " at least two space characters.
284 "
285 " - in each block, the two positions are respectively the start and the
286 " end of the range described by the block.
287 " - in a position, the filename is the name of the file, the first num
288 " is the line number, the second num is the offset of the beginning
289 " of the line, the third num is the offset of the position itself.
290 " - the char number within the line is the difference between the third
291 " and second nums.
292 "
293 " For the moment, the only possible keyword is \"type\"."
294 " >>
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000295
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000296
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000297" 1. Finding the annotation file even if we use ocamlbuild
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000298
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000299 " In: two strings representing paths
300 " Out: one string representing the common prefix between the two paths
301 function! s:Find_common_path (p1,p2)
302 let temp = a:p2
303 while matchstr(a:p1,temp) == ''
304 let temp = substitute(temp,'/[^/]*$','','')
305 endwhile
306 return temp
307 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000308
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000309 " After call:
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100310 "
311 " Following information have been put in s:annot_file_list, using
312 " annot_file_name name as key:
313 " - annot_file_path :
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000314 " path to the .annot file corresponding to the
315 " source file (dealing with ocamlbuild stuff)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100316 " - _build_path:
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000317 " path to the build directory even if this one is
318 " not named '_build'
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100319 " - date_of_last annot:
320 " Set to 0 until we load the file. It contains the
321 " date at which the file has been loaded.
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000322 function! s:Locate_annotation()
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100323 let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot'
324 if !exists ("s:annot_file_list[annot_file_name]")
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000325 silent exe 'cd' s:Fnameescape(expand('%:p:h'))
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000326 " 1st case : the annot file is in the same directory as the buffer (no ocamlbuild)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100327 let annot_file_path = findfile(annot_file_name,'.')
328 if annot_file_path != ''
329 let annot_file_path = getcwd().'/'.annot_file_path
330 let _build_path = ''
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000331 else
332 " 2nd case : the buffer and the _build directory are in the same directory
333 " ..
334 " / \
335 " / \
336 " _build .ml
337 "
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100338 let _build_path = finddir('_build','.')
339 if _build_path != ''
340 let _build_path = getcwd().'/'._build_path
341 let annot_file_path = findfile(annot_file_name,'_build')
342 if annot_file_path != ''
343 let annot_file_path = getcwd().'/'.annot_file_path
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000344 endif
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000345 else
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100346 " 3rd case : the _build directory is in a directory higher in the file hierarchy
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000347 " (it can't be deeper by ocamlbuild requirements)
348 " ..
349 " / \
350 " / \
351 " _build ...
352 " \
353 " \
354 " .ml
355 "
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100356 let _build_path = finddir('_build',';')
357 if _build_path != ''
358 let project_path = substitute(_build_path,'/_build$','','')
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000359 let path_relative_to_project = s:Fnameescape(substitute(expand('%:p:h'),project_path.'/','',''))
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100360 let annot_file_path = findfile(annot_file_name,project_path.'/_build/'.path_relative_to_project)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000361 else
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100362 let annot_file_path = findfile(annot_file_name,'**')
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000363 "4th case : what if the user decided to change the name of the _build directory ?
364 " -> we relax the constraints, it should work in most cases
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100365 if annot_file_path != ''
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000366 " 4a. we suppose the renamed _build directory is in the current directory
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100367 let _build_path = matchstr(annot_file_path,'^[^/]*')
368 if annot_file_path != ''
369 let annot_file_path = getcwd().'/'.annot_file_path
370 let _build_path = getcwd().'/'._build_path
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000371 endif
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000372 else
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100373 let annot_file_name = ''
374 "(Pierre Vittet: I have commented 4b because this was chrashing
375 "my vim (it produced infinite loop))
376 "
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000377 " 4b. anarchy : the renamed _build directory may be higher in the hierarchy
378 " this will work if the file for which we are looking annotations has a unique name in the whole project
379 " if this is not the case, it may still work, but no warranty here
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100380 "let annot_file_path = findfile(annot_file_name,'**;')
381 "let project_path = s:Find_common_path(annot_file_path,expand('%:p:h'))
382 "let _build_path = matchstr(annot_file_path,project_path.'/[^/]*')
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000383 endif
384 endif
385 endif
386 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000387
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100388 if annot_file_path == ''
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000389 throw 'E484: no annotation file found'
390 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000391
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000392 silent exe 'cd' '-'
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100393 let s:annot_file_list[annot_file_name]= [annot_file_path, _build_path, 0]
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000394 endif
395 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000396
Bram Moolenaar7e6a5152021-01-02 16:39:53 +0100397 " This variable contains a dictionary of lists. Each element of the dictionary
398 " represents an annotation system. An annotation system is a list with:
399 " - annotation file name as its key
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100400 " - annotation file path as first element of the contained list
401 " - build path as second element of the contained list
402 " - annot_file_last_mod (contain the date of .annot file) as third element
403 let s:annot_file_list = {}
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000404
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000405" 2. Finding the type information in the annotation file
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100406
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000407 " a. The annotation file is opened in vim as a buffer that
408 " should be (almost) invisible to the user.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000409
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000410 " After call:
411 " The current buffer is now the one containing the .annot file.
412 " We manage to keep all this hidden to the user's eye.
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100413 function! s:Enter_annotation_buffer(annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000414 let s:current_pos = getpos('.')
415 let s:current_hidden = &l:hidden
416 set hidden
417 let s:current_buf = bufname('%')
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100418 if bufloaded(a:annot_file_path)
419 silent exe 'keepj keepalt' 'buffer' s:Fnameescape(a:annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000420 else
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100421 silent exe 'keepj keepalt' 'view' s:Fnameescape(a:annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000422 endif
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100423 call setpos(".", [0, 0 , 0 , 0])
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000424 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000425
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000426 " After call:
427 " The original buffer has been restored in the exact same state as before.
428 function! s:Exit_annotation_buffer()
Bram Moolenaare37d50a2008-08-06 17:06:04 +0000429 silent exe 'keepj keepalt' 'buffer' s:Fnameescape(s:current_buf)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000430 let &l:hidden = s:current_hidden
431 call setpos('.',s:current_pos)
432 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000433
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000434 " After call:
435 " The annot file is loaded and assigned to a buffer.
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100436 " This also handles the modification date of the .annot file, eg. after a
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100437 " compilation (return an updated annot_file_list).
438 function! s:Load_annotation(annot_file_name)
439 let annot = s:annot_file_list[a:annot_file_name]
440 let annot_file_path = annot[0]
441 let annot_file_last_mod = 0
442 if exists("annot[2]")
443 let annot_file_last_mod = annot[2]
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000444 endif
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100445 if bufloaded(annot_file_path) && annot_file_last_mod < getftime(annot_file_path)
446 " if there is a more recent file
447 let nr = bufnr(annot_file_path)
448 silent exe 'keepj keepalt' 'bunload' nr
449 endif
450 if !bufloaded(annot_file_path)
451 call s:Enter_annotation_buffer(annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000452 setlocal nobuflisted
453 setlocal bufhidden=hide
454 setlocal noswapfile
455 setlocal buftype=nowrite
456 call s:Exit_annotation_buffer()
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100457 let annot[2] = getftime(annot_file_path)
458 " List updated with the new date
459 let s:annot_file_list[a:annot_file_name] = annot
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000460 endif
461 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100462
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000463 "b. 'search' and 'match' work to find the type information
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100464
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000465 "In: - lin1,col1: postion of expression first char
466 " - lin2,col2: postion of expression last char
467 "Out: - the pattern to be looked for to find the block
468 " Must be called in the source buffer (use of line2byte)
469 function! s:Block_pattern(lin1,lin2,col1,col2)
470 let start_num1 = a:lin1
471 let start_num2 = line2byte(a:lin1) - 1
472 let start_num3 = start_num2 + a:col1
Bram Moolenaar9c754c42010-07-10 15:52:35 +0200473 let path = '"\(\\"\|[^"]\)\+"'
474 let start_pos = path.' '.start_num1.' '.start_num2.' '.start_num3
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000475 let end_num1 = a:lin2
476 let end_num2 = line2byte(a:lin2) - 1
477 let end_num3 = end_num2 + a:col2
Bram Moolenaar9c754c42010-07-10 15:52:35 +0200478 let end_pos = path.' '.end_num1.' '.end_num2.' '.end_num3
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000479 return '^'.start_pos.' '.end_pos."$"
480 " rq: the '^' here is not totally correct regarding the annot file "grammar"
481 " but currently the annotation file respects this, and it's a little bit faster with the '^';
482 " can be removed safely.
483 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000484
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000485 "In: (the cursor position should be at the start of an annotation)
486 "Out: the type information
487 " Must be called in the annotation buffer (use of search)
488 function! s:Match_data()
489 " rq: idem as previously, in the following, the '^' at start of patterns is not necessary
490 keepj while search('^type($','ce',line(".")) == 0
491 keepj if search('^.\{-}($','e') == 0
492 throw "no_annotation"
493 endif
494 keepj if searchpair('(','',')') == 0
495 throw "malformed_annot_file"
496 endif
497 endwhile
498 let begin = line(".") + 1
499 keepj if searchpair('(','',')') == 0
500 throw "malformed_annot_file"
501 endif
502 let end = line(".") - 1
503 return join(getline(begin,end),"\n")
504 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100505
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000506 "In: the pattern to look for in order to match the block
507 "Out: the type information (calls s:Match_data)
508 " Should be called in the annotation buffer
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100509 function! s:Extract_type_data(block_pattern, annot_file_name)
510 let annot_file_path = s:annot_file_list[a:annot_file_name][0]
511 call s:Enter_annotation_buffer(annot_file_path)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000512 try
513 if search(a:block_pattern,'e') == 0
514 throw "no_annotation"
515 endif
516 call cursor(line(".") + 1,1)
517 let annotation = s:Match_data()
518 finally
519 call s:Exit_annotation_buffer()
520 endtry
521 return annotation
522 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100523
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000524 "c. link this stuff with what the user wants
525 " ie. get the expression selected/under the cursor
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100526
Bram Moolenaar7e6a5152021-01-02 16:39:53 +0100527 let s:ocaml_word_char = '\w|[\xc0-\xff]|'''
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000528
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000529 "In: the current mode (eg. "visual", "normal", etc.)
530 "Out: the borders of the expression we are looking for the type
531 function! s:Match_borders(mode)
532 if a:mode == "visual"
533 let cur = getpos(".")
534 normal `<
535 let col1 = col(".")
536 let lin1 = line(".")
537 normal `>
538 let col2 = col(".")
539 let lin2 = line(".")
540 call cursor(cur[1],cur[2])
541 return [lin1,lin2,col1-1,col2]
542 else
543 let cursor_line = line(".")
544 let cursor_col = col(".")
545 let line = getline('.')
546 if line[cursor_col-1:cursor_col] == '[|'
547 let [lin2,col2] = searchpairpos('\[|','','|\]','n')
548 return [cursor_line,lin2,cursor_col-1,col2+1]
549 elseif line[cursor_col-1] == '['
550 let [lin2,col2] = searchpairpos('\[','','\]','n')
551 return [cursor_line,lin2,cursor_col-1,col2]
552 elseif line[cursor_col-1] == '('
553 let [lin2,col2] = searchpairpos('(','',')','n')
554 return [cursor_line,lin2,cursor_col-1,col2]
555 elseif line[cursor_col-1] == '{'
556 let [lin2,col2] = searchpairpos('{','','}','n')
557 return [cursor_line,lin2,cursor_col-1,col2]
558 else
559 let [lin1,col1] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','ncb')
560 let [lin2,col2] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','nce')
561 if col1 == 0 || col2 == 0
562 throw "no_expression"
563 endif
564 return [cursor_line,cursor_line,col1-1,col2]
565 endif
566 endif
567 endfun
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000568
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000569 "In: the current mode (eg. "visual", "normal", etc.)
570 "Out: the type information (calls s:Extract_type_data)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100571 function! s:Get_type(mode, annot_file_name)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000572 let [lin1,lin2,col1,col2] = s:Match_borders(a:mode)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100573 return s:Extract_type_data(s:Block_pattern(lin1,lin2,col1,col2), a:annot_file_name)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000574 endfun
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100575
Bram Moolenaar16ea3672013-07-28 16:02:18 +0200576 "In: A string destined to be printed in the 'echo buffer'. It has line
577 "break and 2 space at each line beginning.
578 "Out: A string destined to be yanked, without space and double space.
579 function s:unformat_ocaml_type(res)
580 "Remove end of line.
581 let res = substitute (a:res, "\n", "", "g" )
582 "remove double space
583 let res =substitute(res , " ", " ", "g")
584 "remove space at begining of string.
585 let res = substitute(res, "^ *", "", "g")
586 return res
587 endfunction
588
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000589 "d. main
590 "In: the current mode (eg. "visual", "normal", etc.)
591 "After call: the type information is displayed
592 if !exists("*Ocaml_get_type")
593 function Ocaml_get_type(mode)
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100594 let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot'
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000595 call s:Locate_annotation()
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +0100596 call s:Load_annotation(annot_file_name)
Bram Moolenaar16ea3672013-07-28 16:02:18 +0200597 let res = s:Get_type(a:mode, annot_file_name)
598 " Copy result in the unnamed buffer
599 let @" = s:unformat_ocaml_type(res)
600 return res
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000601 endfun
602 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000603
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000604 if !exists("*Ocaml_get_type_or_not")
605 function Ocaml_get_type_or_not(mode)
606 let t=reltime()
607 try
Bram Moolenaar16ea3672013-07-28 16:02:18 +0200608 let res = Ocaml_get_type(a:mode)
609 return res
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000610 catch
611 return ""
612 endtry
613 endfun
614 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000615
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000616 if !exists("*Ocaml_print_type")
617 function Ocaml_print_type(mode)
618 if expand("%:e") == "mli"
619 echohl ErrorMsg | echo "No annotations for interface (.mli) files" | echohl None
620 return
621 endif
622 try
623 echo Ocaml_get_type(a:mode)
624 catch /E484:/
625 echohl ErrorMsg | echo "No type annotations (.annot) file found" | echohl None
626 catch /no_expression/
627 echohl ErrorMsg | echo "No expression found under the cursor" | echohl None
628 catch /no_annotation/
629 echohl ErrorMsg | echo "No type annotation found for the given text" | echohl None
630 catch /malformed_annot_file/
631 echohl ErrorMsg | echo "Malformed .annot file" | echohl None
632 endtry
633 endfun
634 endif
Bram Moolenaar5eb86f92004-07-26 12:53:41 +0000635
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000636" Maps
Bram Moolenaar16ea3672013-07-28 16:02:18 +0200637 nnoremap <silent> <Plug>OCamlPrintType :<C-U>call Ocaml_print_type("normal")<CR>
638 xnoremap <silent> <Plug>OCamlPrintType :<C-U>call Ocaml_print_type("visual")<CR>`<
Bram Moolenaara5792f52005-11-23 21:25:05 +0000639
Bram Moolenaare0e39172021-01-25 21:14:57 +0100640let &cpoptions = s:cposet
Bram Moolenaara5792f52005-11-23 21:25:05 +0000641unlet s:cposet
642
Bram Moolenaar3577c6f2008-06-24 21:16:56 +0000643" vim:sw=2 fdm=indent