xref: /vim-8.2.3635/runtime/ftplugin/ocaml.vim (revision a0122dcd)
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  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
212" Vim support for OCaml .annot files
213"
214" Last Change: 2007 Jul 17
215" Maintainer:  Vincent Aravantinos <[email protected]>
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
225" 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"
231" Typing '<LocalLeader>t' (LocalLeader defaults to '\', see :h LocalLeader)
232" will cause " Ocaml_print_type function to be invoked with the right
233" argument depending on the current mode (visual or not).
234" >>
235"
236" If you find something not matching this behaviour, please signal it.
237"
238" Differences are:
239"   - no need for python support
240"     + plus : more portable
241"     + minus: no more lazy parsing, it looks very fast however
242"
243"   - ocamlbuild support, ie.
244"     + the plugin finds the _build directory and looks for the
245"       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:
253"       the variable 'b:_build_dir' contains the inferred path to the build
254"       directory, even if this one is not named '_build'.
255"
256" 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
262
263  " 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  " >>
293
294
295" 1. Finding the annotation file even if we use ocamlbuild
296
297    " 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
306
307    " After call:
308    "
309    "  Following information have been put in s:annot_file_list, using
310    "  annot_file_name name as key:
311    " - annot_file_path :
312    "                       path to the .annot file corresponding to the
313    "                       source file (dealing with ocamlbuild stuff)
314    " - _build_path:
315    "                       path to the build directory even if this one is
316    "                       not named '_build'
317    " - 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.
320  function! s:Locate_annotation()
321    let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot'
322    if !exists ("s:annot_file_list[annot_file_name]")
323      silent exe 'cd' s:Fnameescape(expand('%:p:h'))
324      " 1st case : the annot file is in the same directory as the buffer (no ocamlbuild)
325      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 = ''
329      else
330        " 2nd case : the buffer and the _build directory are in the same directory
331        "      ..
332        "     /  \
333        "    /    \
334        " _build  .ml
335        "
336        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
342          endif
343        else
344          " 3rd case : the _build directory is in a directory higher in the file hierarchy
345          "            (it can't be deeper by ocamlbuild requirements)
346          "      ..
347          "     /  \
348          "    /    \
349          " _build  ...
350          "           \
351          "            \
352          "           .ml
353          "
354          let _build_path = finddir('_build',';')
355          if _build_path != ''
356            let project_path                = substitute(_build_path,'/_build$','','')
357            let path_relative_to_project    = s:Fnameescape(substitute(expand('%:p:h'),project_path.'/','',''))
358            let annot_file_path           = findfile(annot_file_name,project_path.'/_build/'.path_relative_to_project)
359          else
360            let annot_file_path = findfile(annot_file_name,'**')
361            "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
363            if annot_file_path != ''
364              " 4a. we suppose the renamed _build directory is in the current directory
365              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
369              endif
370            else
371              let annot_file_name = ''
372              "(Pierre Vittet: I have commented 4b because this was chrashing
373              "my vim (it produced infinite loop))
374              "
375              " 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
378              "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.'/[^/]*')
381            endif
382          endif
383        endif
384      endif
385
386      if annot_file_path == ''
387        throw 'E484: no annotation file found'
388      endif
389
390      silent exe 'cd' '-'
391      let s:annot_file_list[annot_file_name]= [annot_file_path, _build_path, 0]
392    endif
393  endfun
394
395  " 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
398  " - 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 = {}
402
403" 2. Finding the type information in the annotation file
404
405  " a. The annotation file is opened in vim as a buffer that
406  " should be (almost) invisible to the user.
407
408      " 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.
411    function! s:Enter_annotation_buffer(annot_file_path)
412      let s:current_pos = getpos('.')
413      let s:current_hidden = &l:hidden
414      set hidden
415      let s:current_buf = bufname('%')
416      if bufloaded(a:annot_file_path)
417        silent exe 'keepj keepalt' 'buffer' s:Fnameescape(a:annot_file_path)
418      else
419        silent exe 'keepj keepalt' 'view' s:Fnameescape(a:annot_file_path)
420      endif
421      call setpos(".", [0, 0 , 0 , 0])
422    endfun
423
424      " After call:
425      "   The original buffer has been restored in the exact same state as before.
426    function! s:Exit_annotation_buffer()
427      silent exe 'keepj keepalt' 'buffer' s:Fnameescape(s:current_buf)
428      let &l:hidden = s:current_hidden
429      call setpos('.',s:current_pos)
430    endfun
431
432      " After call:
433      "   The annot file is loaded and assigned to a buffer.
434      "   This also handles the modification date of the .annot file, eg. after a
435      "   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]
442      endif
443      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)
450        setlocal nobuflisted
451        setlocal bufhidden=hide
452        setlocal noswapfile
453        setlocal buftype=nowrite
454        call s:Exit_annotation_buffer()
455        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
458      endif
459    endfun
460
461  "b. 'search' and 'match' work to find the type information
462
463      "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
471      let path       = '"\(\\"\|[^"]\)\+"'
472      let start_pos  = path.' '.start_num1.' '.start_num2.' '.start_num3
473      let end_num1   = a:lin2
474      let end_num2   = line2byte(a:lin2) - 1
475      let end_num3   = end_num2 + a:col2
476      let end_pos    = path.' '.end_num1.' '.end_num2.' '.end_num3
477      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
482
483      "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
503
504      "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
507    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)
510      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
521
522  "c. link this stuff with what the user wants
523  " ie. get the expression selected/under the cursor
524
525    let s:ocaml_word_char = '\w|[\xc0-\xff]|'''
526
527      "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
566
567      "In:  the current mode (eg. "visual", "normal", etc.)
568      "Out: the type information (calls s:Extract_type_data)
569    function! s:Get_type(mode, annot_file_name)
570      let [lin1,lin2,col1,col2] = s:Match_borders(a:mode)
571      return s:Extract_type_data(s:Block_pattern(lin1,lin2,col1,col2), a:annot_file_name)
572    endfun
573
574      "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
587  "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)
592        let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot'
593        call s:Locate_annotation()
594        call s:Load_annotation(annot_file_name)
595        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
599      endfun
600    endif
601
602    if !exists("*Ocaml_get_type_or_not")
603      function Ocaml_get_type_or_not(mode)
604        let t=reltime()
605        try
606          let res = Ocaml_get_type(a:mode)
607          return res
608        catch
609          return ""
610        endtry
611      endfun
612    endif
613
614    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
633
634" Maps
635  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>`<
637
638let &cpoptions=s:cposet
639unlet s:cposet
640
641" vim:sw=2 fdm=indent
642