xref: /vim-8.2.3635/runtime/ftplugin/ocaml.vim (revision e30d1025)
1" Language:    OCaml
2" Maintainer:  David Baelde        <[email protected]>
3"              Mike Leary          <[email protected]>
4"              Markus Mottl        <[email protected]>
5"              Pierre Vittet       <[email protected]>
6"              Stefano Zacchiroli  <[email protected]>
7"              Vincent Aravantinos <[email protected]>
8" URL:         https://github.com/ocaml/vim-ocaml
9" Last Change:
10"              2013 Oct 27 - Added commentstring (MM)
11"              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)
14
15if exists("b:did_ftplugin")
16  finish
17endif
18let b:did_ftplugin=1
19
20" Use standard compiler settings unless user wants otherwise
21if !exists("current_compiler")
22  :compiler ocaml
23endif
24
25" 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
36" Error handling -- helps moving where the compiler wants you to go
37let s:cposet=&cpoptions
38set cpo&vim
39
40" Comment string
41setlocal comments=sr:(*\ ,mb:\ ,ex:*)
42setlocal comments^=sr:(**,mb:\ \ ,ex:*)
43setlocal commentstring=(*%s*)
44
45" Add mappings, unless the user didn't want this.
46if !exists("no_plugin_maps") && !exists("no_ocaml_maps")
47  " (un)commenting
48  if !hasmapto('<Plug>Comment')
49    nmap <buffer> <LocalLeader>c <Plug>LUncomOn
50    xmap <buffer> <LocalLeader>c <Plug>BUncomOn
51    nmap <buffer> <LocalLeader>C <Plug>LUncomOff
52    xmap <buffer> <LocalLeader>C <Plug>BUncomOff
53  endif
54
55  nnoremap <buffer> <Plug>LUncomOn gI(* <End> *)<ESC>
56  nnoremap <buffer> <Plug>LUncomOff :s/^(\* \(.*\) \*)/\1/<CR>:noh<CR>
57  xnoremap <buffer> <Plug>BUncomOn <ESC>:'<,'><CR>`<O<ESC>0i(*<ESC>`>o<ESC>0i*)<ESC>`<
58  xnoremap <buffer> <Plug>BUncomOff <ESC>:'<,'><CR>`<dd`>dd`<
59
60  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
65endif
66
67" Let % jump between structure elements (due to Issac Trotts)
68let b:mw =         '\<let\>:\<and\>:\(\<in\>\|;;\)'
69let b:mw = b:mw . ',\<if\>:\<then\>:\<else\>'
70let b:mw = b:mw . ',\<\(for\|while\)\>:\<do\>:\<done\>'
71let 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
76
77function! 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
101" switching between interfaces (.mli) and implementations (.ml)
102if !exists("g:did_ocaml_switch")
103  let g:did_ocaml_switch = 1
104  nnoremap <Plug>OCamlSwitchEdit :<C-u>call OCaml_switch(0)<CR>
105  nnoremap <Plug>OCamlSwitchNewWin :<C-u>call OCaml_switch(1)<CR>
106  fun OCaml_switch(newwin)
107    if (match(bufname(""), "\\.mli$") >= 0)
108      let fname = s:Fnameescape(substitute(bufname(""), "\\.mli$", ".ml", ""))
109      if (a:newwin == 1)
110        exec "new " . fname
111      else
112        exec "arge " . fname
113      endif
114    elseif (match(bufname(""), "\\.ml$") >= 0)
115      let fname = s:Fnameescape(bufname("")) . "i"
116      if (a:newwin == 1)
117        exec "new " . fname
118      else
119        exec "arge " . fname
120      endif
121    endif
122  endfun
123endif
124
125" Folding support
126
127" Get the modeline because folding depends on indentation
128let lnum = search('^\s*(\*:o\?caml:', 'n')
129let s:modeline = lnum? getline(lnum): ""
130
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
155let b:undo_ftplugin = "setlocal efm< foldmethod< foldexpr<"
156	\ . "| unlet! b:mw b:match_words b:match_ignorecase"
157
158
159" - Only definitions below, executed once -------------------------------------
160
161if exists("*OMLetFoldLevel")
162  let &cpoptions = s:cposet
163  unlet s:cposet
164  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
214" Vim support for OCaml .annot files
215"
216" Last Change: 2007 Jul 17
217" Maintainer:  Vincent Aravantinos <[email protected]>
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
227" 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"
233" Typing '<LocalLeader>t' (LocalLeader defaults to '\', see :h LocalLeader)
234" will cause " Ocaml_print_type function to be invoked with the right
235" argument depending on the current mode (visual or not).
236" >>
237"
238" If you find something not matching this behaviour, please signal it.
239"
240" Differences are:
241"   - no need for python support
242"     + plus : more portable
243"     + minus: no more lazy parsing, it looks very fast however
244"
245"   - ocamlbuild support, ie.
246"     + the plugin finds the _build directory and looks for the
247"       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:
255"       the variable 'b:_build_dir' contains the inferred path to the build
256"       directory, even if this one is not named '_build'.
257"
258" 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
264
265  " 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  " >>
295
296
297" 1. Finding the annotation file even if we use ocamlbuild
298
299    " 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
308
309    " After call:
310    "
311    "  Following information have been put in s:annot_file_list, using
312    "  annot_file_name name as key:
313    " - annot_file_path :
314    "                       path to the .annot file corresponding to the
315    "                       source file (dealing with ocamlbuild stuff)
316    " - _build_path:
317    "                       path to the build directory even if this one is
318    "                       not named '_build'
319    " - 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.
322  function! s:Locate_annotation()
323    let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot'
324    if !exists ("s:annot_file_list[annot_file_name]")
325      silent exe 'cd' s:Fnameescape(expand('%:p:h'))
326      " 1st case : the annot file is in the same directory as the buffer (no ocamlbuild)
327      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 = ''
331      else
332        " 2nd case : the buffer and the _build directory are in the same directory
333        "      ..
334        "     /  \
335        "    /    \
336        " _build  .ml
337        "
338        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
344          endif
345        else
346          " 3rd case : the _build directory is in a directory higher in the file hierarchy
347          "            (it can't be deeper by ocamlbuild requirements)
348          "      ..
349          "     /  \
350          "    /    \
351          " _build  ...
352          "           \
353          "            \
354          "           .ml
355          "
356          let _build_path = finddir('_build',';')
357          if _build_path != ''
358            let project_path                = substitute(_build_path,'/_build$','','')
359            let path_relative_to_project    = s:Fnameescape(substitute(expand('%:p:h'),project_path.'/','',''))
360            let annot_file_path           = findfile(annot_file_name,project_path.'/_build/'.path_relative_to_project)
361          else
362            let annot_file_path = findfile(annot_file_name,'**')
363            "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
365            if annot_file_path != ''
366              " 4a. we suppose the renamed _build directory is in the current directory
367              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
371              endif
372            else
373              let annot_file_name = ''
374              "(Pierre Vittet: I have commented 4b because this was crashing
375              "my vim (it produced infinite loop))
376              "
377              " 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
380              "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.'/[^/]*')
383            endif
384          endif
385        endif
386      endif
387
388      if annot_file_path == ''
389        throw 'E484: no annotation file found'
390      endif
391
392      silent exe 'cd' '-'
393      let s:annot_file_list[annot_file_name]= [annot_file_path, _build_path, 0]
394    endif
395  endfun
396
397  " 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
400  " - 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 = {}
404
405" 2. Finding the type information in the annotation file
406
407  " a. The annotation file is opened in vim as a buffer that
408  " should be (almost) invisible to the user.
409
410      " 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.
413    function! s:Enter_annotation_buffer(annot_file_path)
414      let s:current_pos = getpos('.')
415      let s:current_hidden = &l:hidden
416      set hidden
417      let s:current_buf = bufname('%')
418      if bufloaded(a:annot_file_path)
419        silent exe 'keepj keepalt' 'buffer' s:Fnameescape(a:annot_file_path)
420      else
421        silent exe 'keepj keepalt' 'view' s:Fnameescape(a:annot_file_path)
422      endif
423      call setpos(".", [0, 0 , 0 , 0])
424    endfun
425
426      " After call:
427      "   The original buffer has been restored in the exact same state as before.
428    function! s:Exit_annotation_buffer()
429      silent exe 'keepj keepalt' 'buffer' s:Fnameescape(s:current_buf)
430      let &l:hidden = s:current_hidden
431      call setpos('.',s:current_pos)
432    endfun
433
434      " After call:
435      "   The annot file is loaded and assigned to a buffer.
436      "   This also handles the modification date of the .annot file, eg. after a
437      "   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]
444      endif
445      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)
452        setlocal nobuflisted
453        setlocal bufhidden=hide
454        setlocal noswapfile
455        setlocal buftype=nowrite
456        call s:Exit_annotation_buffer()
457        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
460      endif
461    endfun
462
463  "b. 'search' and 'match' work to find the type information
464
465      "In:  - lin1,col1: position of expression first char
466      "     - lin2,col2: position 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
473      let path       = '"\(\\"\|[^"]\)\+"'
474      let start_pos  = path.' '.start_num1.' '.start_num2.' '.start_num3
475      let end_num1   = a:lin2
476      let end_num2   = line2byte(a:lin2) - 1
477      let end_num3   = end_num2 + a:col2
478      let end_pos    = path.' '.end_num1.' '.end_num2.' '.end_num3
479      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
484
485      "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
505
506      "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
509    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)
512      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
523
524  "c. link this stuff with what the user wants
525  " ie. get the expression selected/under the cursor
526
527    let s:ocaml_word_char = '\w|[\xc0-\xff]|'''
528
529      "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
568
569      "In:  the current mode (eg. "visual", "normal", etc.)
570      "Out: the type information (calls s:Extract_type_data)
571    function! s:Get_type(mode, annot_file_name)
572      let [lin1,lin2,col1,col2] = s:Match_borders(a:mode)
573      return s:Extract_type_data(s:Block_pattern(lin1,lin2,col1,col2), a:annot_file_name)
574    endfun
575
576      "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 beginning of string.
585      let res = substitute(res, "^ *", "", "g")
586      return res
587    endfunction
588
589  "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)
594        let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot'
595        call s:Locate_annotation()
596        call s:Load_annotation(annot_file_name)
597        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
601      endfun
602    endif
603
604    if !exists("*Ocaml_get_type_or_not")
605      function Ocaml_get_type_or_not(mode)
606        let t=reltime()
607        try
608          let res = Ocaml_get_type(a:mode)
609          return res
610        catch
611          return ""
612        endtry
613      endfun
614    endif
615
616    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
635
636" Maps
637  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>`<
639
640let &cpoptions = s:cposet
641unlet s:cposet
642
643" vim:sw=2 fdm=indent
644