ocaml.vim (23890B)
1 " Language: OCaml 2 " Maintainer: David Baelde <firstname.name@ens-lyon.org> 3 " Mike Leary <leary@nwlink.com> 4 " Markus Mottl <markus.mottl@gmail.com> 5 " Pierre Vittet <pierre-vittet@pvittet.com> 6 " Stefano Zacchiroli <zack@bononia.it> 7 " Vincent Aravantinos <firstname.name@imag.fr> 8 " Riley Bruins <ribru17@gmail.com> ('commentstring') 9 " URL: https://github.com/ocaml/vim-ocaml 10 " Last Change: 11 " 2013 Oct 27 - Added commentstring (MM) 12 " 2013 Jul 26 - load default compiler settings (MM) 13 " 2013 Jul 24 - removed superfluous efm-setting (MM) 14 " 2013 Jul 22 - applied fixes supplied by Hirotaka Hamada (MM) 15 " 2024 May 23 - added space in commentstring (RB) 16 17 if exists("b:did_ftplugin") 18 finish 19 endif 20 let b:did_ftplugin=1 21 22 " Use standard compiler settings unless user wants otherwise 23 if !exists("current_compiler") 24 :compiler ocaml 25 endif 26 27 " some macro 28 if exists('*fnameescape') 29 function! s:Fnameescape(s) 30 return fnameescape(a:s) 31 endfun 32 else 33 function! s:Fnameescape(s) 34 return escape(a:s," \t\n*?[{`$\\%#'\"|!<") 35 endfun 36 endif 37 38 " Error handling -- helps moving where the compiler wants you to go 39 let s:cposet=&cpoptions 40 set cpo&vim 41 42 " Comment string 43 setlocal comments=sr:(*\ ,mb:\ ,ex:*) 44 setlocal comments^=sr:(**,mb:\ \ ,ex:*) 45 setlocal commentstring=(*\ %s\ *) 46 47 " Add mappings, unless the user didn't want this. 48 if !exists("no_plugin_maps") && !exists("no_ocaml_maps") 49 " (un)commenting 50 if !hasmapto('<Plug>Comment') 51 nmap <buffer> <LocalLeader>c <Plug>LUncomOn 52 xmap <buffer> <LocalLeader>c <Plug>BUncomOn 53 nmap <buffer> <LocalLeader>C <Plug>LUncomOff 54 xmap <buffer> <LocalLeader>C <Plug>BUncomOff 55 endif 56 57 nnoremap <buffer> <Plug>LUncomOn gI(* <End> *)<ESC> 58 nnoremap <buffer> <Plug>LUncomOff :s/^(\* \(.*\) \*)/\1/<CR>:noh<CR> 59 xnoremap <buffer> <Plug>BUncomOn <ESC>:'<,'><CR>`<O<ESC>0i(*<ESC>`>o<ESC>0i*)<ESC>`< 60 xnoremap <buffer> <Plug>BUncomOff <ESC>:'<,'><CR>`<dd`>dd`< 61 62 nmap <buffer> <LocalLeader>s <Plug>OCamlSwitchEdit 63 nmap <buffer> <LocalLeader>S <Plug>OCamlSwitchNewWin 64 65 nmap <buffer> <LocalLeader>t <Plug>OCamlPrintType 66 xmap <buffer> <LocalLeader>t <Plug>OCamlPrintType 67 endif 68 69 " Let % jump between structure elements (due to Issac Trotts) 70 let b:mw = '\<let\>:\<and\>:\(\<in\>\|;;\)' 71 let b:mw = b:mw . ',\<if\>:\<then\>:\<else\>' 72 let b:mw = b:mw . ',\<\(for\|while\)\>:\<do\>:\<done\>' 73 let b:mw = b:mw . ',\<\(object\|sig\|struct\|begin\)\>:\<end\>' 74 let b:mw = b:mw . ',\<\(match\|try\)\>:\<with\>' 75 let b:match_words = b:mw 76 77 let b:match_ignorecase=0 78 79 function! s:OcpGrep(bang,args) abort 80 let grepprg = &l:grepprg 81 let grepformat = &l:grepformat 82 let shellpipe = &shellpipe 83 try 84 let &l:grepprg = "ocp-grep -c never" 85 setlocal grepformat=%f:%l:%m 86 if &shellpipe ==# '2>&1| tee' || &shellpipe ==# '|& tee' 87 let &shellpipe = "| tee" 88 endif 89 execute 'grep! '.a:args 90 if empty(a:bang) && !empty(getqflist()) 91 return 'cfirst' 92 else 93 return '' 94 endif 95 finally 96 let &l:grepprg = grepprg 97 let &l:grepformat = grepformat 98 let &shellpipe = shellpipe 99 endtry 100 endfunction 101 command! -bar -bang -complete=file -nargs=+ Ocpgrep exe s:OcpGrep(<q-bang>, <q-args>) 102 103 " switching between interfaces (.mli) and implementations (.ml) 104 if !exists("g:did_ocaml_switch") 105 let g:did_ocaml_switch = 1 106 nnoremap <Plug>OCamlSwitchEdit :<C-u>call OCaml_switch(0)<CR> 107 nnoremap <Plug>OCamlSwitchNewWin :<C-u>call OCaml_switch(1)<CR> 108 fun OCaml_switch(newwin) 109 if (match(bufname(""), "\\.mli$") >= 0) 110 let fname = s:Fnameescape(substitute(bufname(""), "\\.mli$", ".ml", "")) 111 if (a:newwin == 1) 112 exec "new " . fname 113 else 114 exec "arge " . fname 115 endif 116 elseif (match(bufname(""), "\\.ml$") >= 0) 117 let fname = s:Fnameescape(bufname("")) . "i" 118 if (a:newwin == 1) 119 exec "new " . fname 120 else 121 exec "arge " . fname 122 endif 123 endif 124 endfun 125 endif 126 127 " Folding support 128 129 " Get the modeline because folding depends on indentation 130 let lnum = search('^\s*(\*:o\?caml:', 'n') 131 let s:modeline = lnum? getline(lnum): "" 132 133 " Get the indentation params 134 let s:m = matchstr(s:modeline,'default\s*=\s*\d\+') 135 if s:m != "" 136 let s:idef = matchstr(s:m,'\d\+') 137 elseif exists("g:omlet_indent") 138 let s:idef = g:omlet_indent 139 else 140 let s:idef = 2 141 endif 142 let s:m = matchstr(s:modeline,'struct\s*=\s*\d\+') 143 if s:m != "" 144 let s:i = matchstr(s:m,'\d\+') 145 elseif exists("g:omlet_indent_struct") 146 let s:i = g:omlet_indent_struct 147 else 148 let s:i = s:idef 149 endif 150 151 " Set the folding method 152 if exists("g:ocaml_folding") 153 setlocal foldmethod=expr 154 setlocal foldexpr=OMLetFoldLevel(v:lnum) 155 endif 156 157 let b:undo_ftplugin = "setlocal efm< foldmethod< foldexpr<" 158 \ . "| unlet! b:mw b:match_words b:match_ignorecase" 159 160 161 " - Only definitions below, executed once ------------------------------------- 162 163 if exists("*OMLetFoldLevel") 164 let &cpoptions = s:cposet 165 unlet s:cposet 166 finish 167 endif 168 169 function s:topindent(lnum) 170 let l = a:lnum 171 while l > 0 172 if getline(l) =~ '\s*\%(\<struct\>\|\<sig\>\|\<object\>\)' 173 return indent(l) 174 endif 175 let l = l-1 176 endwhile 177 return -s:i 178 endfunction 179 180 function OMLetFoldLevel(l) 181 182 " This is for not merging blank lines around folds to them 183 if getline(a:l) !~ '\S' 184 return -1 185 endif 186 187 " We start folds for modules, classes, and every toplevel definition 188 if getline(a:l) =~ '^\s*\%(\<val\>\|\<module\>\|\<class\>\|\<type\>\|\<method\>\|\<initializer\>\|\<inherit\>\|\<exception\>\|\<external\>\)' 189 exe 'return ">' (indent(a:l)/s:i)+1 '"' 190 endif 191 192 " Toplevel let are detected thanks to the indentation 193 if getline(a:l) =~ '^\s*let\>' && indent(a:l) == s:i+s:topindent(a:l) 194 exe 'return ">' (indent(a:l)/s:i)+1 '"' 195 endif 196 197 " We close fold on end which are associated to struct, sig or object. 198 " We use syntax information to do that. 199 if getline(a:l) =~ '^\s*end\>' && synIDattr(synID(a:l, indent(a:l)+1, 0), "name") != "ocamlKeyword" 200 return (indent(a:l)/s:i)+1 201 endif 202 203 " Folds end on ;; 204 if getline(a:l) =~ '^\s*;;' 205 exe 'return "<' (indent(a:l)/s:i)+1 '"' 206 endif 207 208 " Comments around folds aren't merged to them. 209 if synIDattr(synID(a:l, indent(a:l)+1, 0), "name") == "ocamlComment" 210 return -1 211 endif 212 213 return '=' 214 endfunction 215 216 " Vim support for OCaml .annot files 217 " 218 " Last Change: 2007 Jul 17 219 " Maintainer: Vincent Aravantinos <vincent.aravantinos@gmail.com> 220 " License: public domain 221 " 222 " Originally inspired by 'ocaml-dtypes.vim' by Stefano Zacchiroli. 223 " The source code is quite radically different for we not use python anymore. 224 " However this plugin should have the exact same behaviour, that's why the 225 " following lines are the quite exact copy of Stefano's original plugin : 226 " 227 " << 228 " Executing Ocaml_print_type(<mode>) function will display in the Vim bottom 229 " line(s) the type of an ocaml value getting it from the corresponding .annot 230 " file (if any). If Vim is in visual mode, <mode> should be "visual" and the 231 " selected ocaml value correspond to the highlighted text, otherwise (<mode> 232 " can be anything else) it corresponds to the literal found at the current 233 " cursor position. 234 " 235 " Typing '<LocalLeader>t' (LocalLeader defaults to '\', see :h LocalLeader) 236 " will cause " Ocaml_print_type function to be invoked with the right 237 " argument depending on the current mode (visual or not). 238 " >> 239 " 240 " If you find something not matching this behaviour, please signal it. 241 " 242 " Differences are: 243 " - no need for python support 244 " + plus : more portable 245 " + minus: no more lazy parsing, it looks very fast however 246 " 247 " - ocamlbuild support, ie. 248 " + the plugin finds the _build directory and looks for the 249 " corresponding file inside; 250 " + if the user decides to change the name of the _build directory thanks 251 " to the '-build-dir' option of ocamlbuild, the plugin will manage in 252 " most cases to find it out (most cases = if the source file has a unique 253 " name among your whole project); 254 " + if ocamlbuild is not used, the usual behaviour holds; ie. the .annot 255 " file should be in the same directory as the source file; 256 " + for vim plugin programmers: 257 " the variable 'b:_build_dir' contains the inferred path to the build 258 " directory, even if this one is not named '_build'. 259 " 260 " Bonus : 261 " - latin1 accents are handled 262 " - lists are handled, even on multiple lines, you don't need the visual mode 263 " (the cursor must be on the first bracket) 264 " - parenthesized expressions, arrays, and structures (ie. '(...)', '[|...|]', 265 " and '{...}') are handled the same way 266 267 " Copied from Stefano's original plugin : 268 " << 269 " .annot ocaml file representation 270 " 271 " File format (copied verbatim from caml-types.el) 272 " 273 " file ::= block * 274 " block ::= position <SP> position <LF> annotation * 275 " position ::= filename <SP> num <SP> num <SP> num 276 " annotation ::= keyword open-paren <LF> <SP> <SP> data <LF> close-paren 277 " 278 " <SP> is a space character (ASCII 0x20) 279 " <LF> is a line-feed character (ASCII 0x0A) 280 " num is a sequence of decimal digits 281 " filename is a string with the lexical conventions of O'Caml 282 " open-paren is an open parenthesis (ASCII 0x28) 283 " close-paren is a closed parenthesis (ASCII 0x29) 284 " data is any sequence of characters where <LF> is always followed by 285 " at least two space characters. 286 " 287 " - in each block, the two positions are respectively the start and the 288 " end of the range described by the block. 289 " - in a position, the filename is the name of the file, the first num 290 " is the line number, the second num is the offset of the beginning 291 " of the line, the third num is the offset of the position itself. 292 " - the char number within the line is the difference between the third 293 " and second nums. 294 " 295 " For the moment, the only possible keyword is \"type\"." 296 " >> 297 298 299 " 1. Finding the annotation file even if we use ocamlbuild 300 301 " In: two strings representing paths 302 " Out: one string representing the common prefix between the two paths 303 function! s:Find_common_path (p1,p2) 304 let temp = a:p2 305 while matchstr(a:p1,temp) == '' 306 let temp = substitute(temp,'/[^/]*$','','') 307 endwhile 308 return temp 309 endfun 310 311 " After call: 312 " 313 " Following information have been put in s:annot_file_list, using 314 " annot_file_name name as key: 315 " - annot_file_path : 316 " path to the .annot file corresponding to the 317 " source file (dealing with ocamlbuild stuff) 318 " - _build_path: 319 " path to the build directory even if this one is 320 " not named '_build' 321 " - date_of_last annot: 322 " Set to 0 until we load the file. It contains the 323 " date at which the file has been loaded. 324 function! s:Locate_annotation() 325 let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot' 326 if !exists ("s:annot_file_list[annot_file_name]") 327 silent exe 'cd' s:Fnameescape(expand('%:p:h')) 328 " 1st case : the annot file is in the same directory as the buffer (no ocamlbuild) 329 let annot_file_path = findfile(annot_file_name,'.') 330 if annot_file_path != '' 331 let annot_file_path = getcwd().'/'.annot_file_path 332 let _build_path = '' 333 else 334 " 2nd case : the buffer and the _build directory are in the same directory 335 " .. 336 " / \ 337 " / \ 338 " _build .ml 339 " 340 let _build_path = finddir('_build','.') 341 if _build_path != '' 342 let _build_path = getcwd().'/'._build_path 343 let annot_file_path = findfile(annot_file_name,'_build') 344 if annot_file_path != '' 345 let annot_file_path = getcwd().'/'.annot_file_path 346 endif 347 else 348 " 3rd case : the _build directory is in a directory higher in the file hierarchy 349 " (it can't be deeper by ocamlbuild requirements) 350 " .. 351 " / \ 352 " / \ 353 " _build ... 354 " \ 355 " \ 356 " .ml 357 " 358 let _build_path = finddir('_build',';') 359 if _build_path != '' 360 let project_path = substitute(_build_path,'/_build$','','') 361 let path_relative_to_project = s:Fnameescape(substitute(expand('%:p:h'),project_path.'/','','')) 362 let annot_file_path = findfile(annot_file_name,project_path.'/_build/'.path_relative_to_project) 363 else 364 let annot_file_path = findfile(annot_file_name,'**') 365 "4th case : what if the user decided to change the name of the _build directory ? 366 " -> we relax the constraints, it should work in most cases 367 if annot_file_path != '' 368 " 4a. we suppose the renamed _build directory is in the current directory 369 let _build_path = matchstr(annot_file_path,'^[^/]*') 370 if annot_file_path != '' 371 let annot_file_path = getcwd().'/'.annot_file_path 372 let _build_path = getcwd().'/'._build_path 373 endif 374 else 375 let annot_file_name = '' 376 "(Pierre Vittet: I have commented 4b because this was crashing 377 "my vim (it produced infinite loop)) 378 " 379 " 4b. anarchy : the renamed _build directory may be higher in the hierarchy 380 " this will work if the file for which we are looking annotations has a unique name in the whole project 381 " if this is not the case, it may still work, but no warranty here 382 "let annot_file_path = findfile(annot_file_name,'**;') 383 "let project_path = s:Find_common_path(annot_file_path,expand('%:p:h')) 384 "let _build_path = matchstr(annot_file_path,project_path.'/[^/]*') 385 endif 386 endif 387 endif 388 endif 389 390 if annot_file_path == '' 391 throw 'E484: no annotation file found' 392 endif 393 394 silent exe 'cd' '-' 395 let s:annot_file_list[annot_file_name]= [annot_file_path, _build_path, 0] 396 endif 397 endfun 398 399 " This variable contains a dictionary of lists. Each element of the dictionary 400 " represents an annotation system. An annotation system is a list with: 401 " - annotation file name as its key 402 " - annotation file path as first element of the contained list 403 " - build path as second element of the contained list 404 " - annot_file_last_mod (contain the date of .annot file) as third element 405 let s:annot_file_list = {} 406 407 " 2. Finding the type information in the annotation file 408 409 " a. The annotation file is opened in vim as a buffer that 410 " should be (almost) invisible to the user. 411 412 " After call: 413 " The current buffer is now the one containing the .annot file. 414 " We manage to keep all this hidden to the user's eye. 415 function! s:Enter_annotation_buffer(annot_file_path) 416 let s:current_pos = getpos('.') 417 let s:current_hidden = &l:hidden 418 set hidden 419 let s:current_buf = bufname('%') 420 if bufloaded(a:annot_file_path) 421 silent exe 'keepj keepalt' 'buffer' s:Fnameescape(a:annot_file_path) 422 else 423 silent exe 'keepj keepalt' 'view' s:Fnameescape(a:annot_file_path) 424 endif 425 call setpos(".", [0, 0 , 0 , 0]) 426 endfun 427 428 " After call: 429 " The original buffer has been restored in the exact same state as before. 430 function! s:Exit_annotation_buffer() 431 silent exe 'keepj keepalt' 'buffer' s:Fnameescape(s:current_buf) 432 let &l:hidden = s:current_hidden 433 call setpos('.',s:current_pos) 434 endfun 435 436 " After call: 437 " The annot file is loaded and assigned to a buffer. 438 " This also handles the modification date of the .annot file, eg. after a 439 " compilation (return an updated annot_file_list). 440 function! s:Load_annotation(annot_file_name) 441 let annot = s:annot_file_list[a:annot_file_name] 442 let annot_file_path = annot[0] 443 let annot_file_last_mod = 0 444 if exists("annot[2]") 445 let annot_file_last_mod = annot[2] 446 endif 447 if bufloaded(annot_file_path) && annot_file_last_mod < getftime(annot_file_path) 448 " if there is a more recent file 449 let nr = bufnr(annot_file_path) 450 silent exe 'keepj keepalt' 'bunload' nr 451 endif 452 if !bufloaded(annot_file_path) 453 call s:Enter_annotation_buffer(annot_file_path) 454 setlocal nobuflisted 455 setlocal bufhidden=hide 456 setlocal noswapfile 457 setlocal buftype=nowrite 458 call s:Exit_annotation_buffer() 459 let annot[2] = getftime(annot_file_path) 460 " List updated with the new date 461 let s:annot_file_list[a:annot_file_name] = annot 462 endif 463 endfun 464 465 "b. 'search' and 'match' work to find the type information 466 467 "In: - lin1,col1: position of expression first char 468 " - lin2,col2: position of expression last char 469 "Out: - the pattern to be looked for to find the block 470 " Must be called in the source buffer (use of line2byte) 471 function! s:Block_pattern(lin1,lin2,col1,col2) 472 let start_num1 = a:lin1 473 let start_num2 = line2byte(a:lin1) - 1 474 let start_num3 = start_num2 + a:col1 475 let path = '"\(\\"\|[^"]\)\+"' 476 let start_pos = path.' '.start_num1.' '.start_num2.' '.start_num3 477 let end_num1 = a:lin2 478 let end_num2 = line2byte(a:lin2) - 1 479 let end_num3 = end_num2 + a:col2 480 let end_pos = path.' '.end_num1.' '.end_num2.' '.end_num3 481 return '^'.start_pos.' '.end_pos."$" 482 " rq: the '^' here is not totally correct regarding the annot file "grammar" 483 " but currently the annotation file respects this, and it's a little bit faster with the '^'; 484 " can be removed safely. 485 endfun 486 487 "In: (the cursor position should be at the start of an annotation) 488 "Out: the type information 489 " Must be called in the annotation buffer (use of search) 490 function! s:Match_data() 491 " rq: idem as previously, in the following, the '^' at start of patterns is not necessary 492 keepj while search('^type($','ce',line(".")) == 0 493 keepj if search('^.\{-}($','e') == 0 494 throw "no_annotation" 495 endif 496 keepj if searchpair('(','',')') == 0 497 throw "malformed_annot_file" 498 endif 499 endwhile 500 let begin = line(".") + 1 501 keepj if searchpair('(','',')') == 0 502 throw "malformed_annot_file" 503 endif 504 let end = line(".") - 1 505 return join(getline(begin,end),"\n") 506 endfun 507 508 "In: the pattern to look for in order to match the block 509 "Out: the type information (calls s:Match_data) 510 " Should be called in the annotation buffer 511 function! s:Extract_type_data(block_pattern, annot_file_name) 512 let annot_file_path = s:annot_file_list[a:annot_file_name][0] 513 call s:Enter_annotation_buffer(annot_file_path) 514 try 515 if search(a:block_pattern,'e') == 0 516 throw "no_annotation" 517 endif 518 call cursor(line(".") + 1,1) 519 let annotation = s:Match_data() 520 finally 521 call s:Exit_annotation_buffer() 522 endtry 523 return annotation 524 endfun 525 526 "c. link this stuff with what the user wants 527 " ie. get the expression selected/under the cursor 528 529 let s:ocaml_word_char = '\w|[\xc0-\xff]|''' 530 531 "In: the current mode (eg. "visual", "normal", etc.) 532 "Out: the borders of the expression we are looking for the type 533 function! s:Match_borders(mode) 534 if a:mode == "visual" 535 let cur = getpos(".") 536 normal `< 537 let col1 = col(".") 538 let lin1 = line(".") 539 normal `> 540 let col2 = col(".") 541 let lin2 = line(".") 542 call cursor(cur[1],cur[2]) 543 return [lin1,lin2,col1-1,col2] 544 else 545 let cursor_line = line(".") 546 let cursor_col = col(".") 547 let line = getline('.') 548 if line[cursor_col-1:cursor_col] == '[|' 549 let [lin2,col2] = searchpairpos('\[|','','|\]','n') 550 return [cursor_line,lin2,cursor_col-1,col2+1] 551 elseif line[cursor_col-1] == '[' 552 let [lin2,col2] = searchpairpos('\[','','\]','n') 553 return [cursor_line,lin2,cursor_col-1,col2] 554 elseif line[cursor_col-1] == '(' 555 let [lin2,col2] = searchpairpos('(','',')','n') 556 return [cursor_line,lin2,cursor_col-1,col2] 557 elseif line[cursor_col-1] == '{' 558 let [lin2,col2] = searchpairpos('{','','}','n') 559 return [cursor_line,lin2,cursor_col-1,col2] 560 else 561 let [lin1,col1] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','ncb') 562 let [lin2,col2] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','nce') 563 if col1 == 0 || col2 == 0 564 throw "no_expression" 565 endif 566 return [cursor_line,cursor_line,col1-1,col2] 567 endif 568 endif 569 endfun 570 571 "In: the current mode (eg. "visual", "normal", etc.) 572 "Out: the type information (calls s:Extract_type_data) 573 function! s:Get_type(mode, annot_file_name) 574 let [lin1,lin2,col1,col2] = s:Match_borders(a:mode) 575 return s:Extract_type_data(s:Block_pattern(lin1,lin2,col1,col2), a:annot_file_name) 576 endfun 577 578 "In: A string destined to be printed in the 'echo buffer'. It has line 579 "break and 2 space at each line beginning. 580 "Out: A string destined to be yanked, without space and double space. 581 function s:unformat_ocaml_type(res) 582 "Remove end of line. 583 let res = substitute (a:res, "\n", "", "g" ) 584 "remove double space 585 let res =substitute(res , " ", " ", "g") 586 "remove space at beginning of string. 587 let res = substitute(res, "^ *", "", "g") 588 return res 589 endfunction 590 591 "d. main 592 "In: the current mode (eg. "visual", "normal", etc.) 593 "After call: the type information is displayed 594 if !exists("*Ocaml_get_type") 595 function Ocaml_get_type(mode) 596 let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot' 597 call s:Locate_annotation() 598 call s:Load_annotation(annot_file_name) 599 let res = s:Get_type(a:mode, annot_file_name) 600 " Copy result in the unnamed buffer 601 let @" = s:unformat_ocaml_type(res) 602 return res 603 endfun 604 endif 605 606 if !exists("*Ocaml_get_type_or_not") 607 function Ocaml_get_type_or_not(mode) 608 let t=reltime() 609 try 610 let res = Ocaml_get_type(a:mode) 611 return res 612 catch 613 return "" 614 endtry 615 endfun 616 endif 617 618 if !exists("*Ocaml_print_type") 619 function Ocaml_print_type(mode) 620 if expand("%:e") == "mli" 621 echohl ErrorMsg | echo "No annotations for interface (.mli) files" | echohl None 622 return 623 endif 624 try 625 echo Ocaml_get_type(a:mode) 626 catch /E484:/ 627 echohl ErrorMsg | echo "No type annotations (.annot) file found" | echohl None 628 catch /no_expression/ 629 echohl ErrorMsg | echo "No expression found under the cursor" | echohl None 630 catch /no_annotation/ 631 echohl ErrorMsg | echo "No type annotation found for the given text" | echohl None 632 catch /malformed_annot_file/ 633 echohl ErrorMsg | echo "Malformed .annot file" | echohl None 634 endtry 635 endfun 636 endif 637 638 " Maps 639 nnoremap <silent> <Plug>OCamlPrintType :<C-U>call Ocaml_print_type("normal")<CR> 640 xnoremap <silent> <Plug>OCamlPrintType :<C-U>call Ocaml_print_type("visual")<CR>`< 641 642 let &cpoptions = s:cposet 643 unlet s:cposet 644 645 " vim:sw=2 fdm=indent