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