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