1071d4279SBram Moolenaar" Language: OCaml 2a5792f58SBram Moolenaar" Maintainer: David Baelde <[email protected]> 3a5792f58SBram Moolenaar" Mike Leary <[email protected]> 4a5792f58SBram Moolenaar" Markus Mottl <[email protected]> 5b8ff1fb5SBram Moolenaar" Pierre Vittet <[email protected]> 65eb86f91SBram Moolenaar" Stefano Zacchiroli <[email protected]> 73577c6faSBram Moolenaar" Vincent Aravantinos <[email protected]> 87e6a515eSBram Moolenaar" URL: https://github.com/ocaml/vim-ocaml 9b8ff1fb5SBram Moolenaar" Last Change: 10790c18bfSBram Moolenaar" 2013 Oct 27 - Added commentstring (MM) 1116ea3676SBram Moolenaar" 2013 Jul 26 - load default compiler settings (MM) 1216ea3676SBram Moolenaar" 2013 Jul 24 - removed superfluous efm-setting (MM) 1316ea3676SBram Moolenaar" 2013 Jul 22 - applied fixes supplied by Hirotaka Hamada (MM) 14b8ff1fb5SBram Moolenaar 154c3f536fSBram Moolenaarif exists("b:did_ftplugin") 164c3f536fSBram Moolenaar finish 174c3f536fSBram Moolenaarendif 18071d4279SBram Moolenaarlet b:did_ftplugin=1 19071d4279SBram Moolenaar 2016ea3676SBram Moolenaar" Use standard compiler settings unless user wants otherwise 2116ea3676SBram Moolenaarif !exists("current_compiler") 2216ea3676SBram Moolenaar :compiler ocaml 2316ea3676SBram Moolenaarendif 2416ea3676SBram Moolenaar 25e37d50a5SBram Moolenaar" some macro 26e37d50a5SBram Moolenaarif exists('*fnameescape') 27e37d50a5SBram Moolenaar function! s:Fnameescape(s) 28e37d50a5SBram Moolenaar return fnameescape(a:s) 29e37d50a5SBram Moolenaar endfun 30e37d50a5SBram Moolenaarelse 31e37d50a5SBram Moolenaar function! s:Fnameescape(s) 32e37d50a5SBram Moolenaar return escape(a:s," \t\n*?[{`$\\%#'\"|!<") 33e37d50a5SBram Moolenaar endfun 34e37d50a5SBram Moolenaarendif 35e37d50a5SBram Moolenaar 36a5792f58SBram Moolenaar" Error handling -- helps moving where the compiler wants you to go 37a5792f58SBram Moolenaarlet s:cposet=&cpoptions 38f1568ecaSBram Moolenaarset cpo&vim 39071d4279SBram Moolenaar 40790c18bfSBram Moolenaar" Comment string 417e6a515eSBram Moolenaarsetlocal comments=sr:(*\ ,mb:\ ,ex:*) 427e6a515eSBram Moolenaarsetlocal comments^=sr:(**,mb:\ \ ,ex:*) 43790c18bfSBram Moolenaarsetlocal commentstring=(*%s*) 44790c18bfSBram Moolenaar 45071d4279SBram Moolenaar" Add mappings, unless the user didn't want this. 46071d4279SBram Moolenaarif !exists("no_plugin_maps") && !exists("no_ocaml_maps") 47a5792f58SBram Moolenaar " (un)commenting 48071d4279SBram Moolenaar if !hasmapto('<Plug>Comment') 49071d4279SBram Moolenaar nmap <buffer> <LocalLeader>c <Plug>LUncomOn 5016ea3676SBram Moolenaar xmap <buffer> <LocalLeader>c <Plug>BUncomOn 51071d4279SBram Moolenaar nmap <buffer> <LocalLeader>C <Plug>LUncomOff 5216ea3676SBram Moolenaar xmap <buffer> <LocalLeader>C <Plug>BUncomOff 53071d4279SBram Moolenaar endif 54071d4279SBram Moolenaar 5516ea3676SBram Moolenaar nnoremap <buffer> <Plug>LUncomOn gI(* <End> *)<ESC> 56a5792f58SBram Moolenaar nnoremap <buffer> <Plug>LUncomOff :s/^(\* \(.*\) \*)/\1/<CR>:noh<CR> 5716ea3676SBram Moolenaar xnoremap <buffer> <Plug>BUncomOn <ESC>:'<,'><CR>`<O<ESC>0i(*<ESC>`>o<ESC>0i*)<ESC>`< 5816ea3676SBram Moolenaar xnoremap <buffer> <Plug>BUncomOff <ESC>:'<,'><CR>`<dd`>dd`< 59071d4279SBram Moolenaar 6016ea3676SBram Moolenaar nmap <buffer> <LocalLeader>s <Plug>OCamlSwitchEdit 6116ea3676SBram Moolenaar nmap <buffer> <LocalLeader>S <Plug>OCamlSwitchNewWin 6216ea3676SBram Moolenaar 6316ea3676SBram Moolenaar nmap <buffer> <LocalLeader>t <Plug>OCamlPrintType 6416ea3676SBram Moolenaar xmap <buffer> <LocalLeader>t <Plug>OCamlPrintType 65071d4279SBram Moolenaarendif 665eb86f91SBram Moolenaar 675eb86f91SBram Moolenaar" Let % jump between structure elements (due to Issac Trotts) 68790c18bfSBram Moolenaarlet b:mw = '\<let\>:\<and\>:\(\<in\>\|;;\)' 69a5792f58SBram Moolenaarlet b:mw = b:mw . ',\<if\>:\<then\>:\<else\>' 70790c18bfSBram Moolenaarlet b:mw = b:mw . ',\<\(for\|while\)\>:\<do\>:\<done\>' 71a5792f58SBram Moolenaarlet b:mw = b:mw . ',\<\(object\|sig\|struct\|begin\)\>:\<end\>' 72a5792f58SBram Moolenaarlet b:mw = b:mw . ',\<\(match\|try\)\>:\<with\>' 735eb86f91SBram Moolenaarlet b:match_words = b:mw 745eb86f91SBram Moolenaar 75a5792f58SBram Moolenaarlet b:match_ignorecase=0 76a5792f58SBram Moolenaar 77790c18bfSBram Moolenaarfunction! s:OcpGrep(bang,args) abort 78790c18bfSBram Moolenaar let grepprg = &l:grepprg 79790c18bfSBram Moolenaar let grepformat = &l:grepformat 80790c18bfSBram Moolenaar let shellpipe = &shellpipe 81790c18bfSBram Moolenaar try 82790c18bfSBram Moolenaar let &l:grepprg = "ocp-grep -c never" 83790c18bfSBram Moolenaar setlocal grepformat=%f:%l:%m 84790c18bfSBram Moolenaar if &shellpipe ==# '2>&1| tee' || &shellpipe ==# '|& tee' 85790c18bfSBram Moolenaar let &shellpipe = "| tee" 86790c18bfSBram Moolenaar endif 87790c18bfSBram Moolenaar execute 'grep! '.a:args 88790c18bfSBram Moolenaar if empty(a:bang) && !empty(getqflist()) 89790c18bfSBram Moolenaar return 'cfirst' 90790c18bfSBram Moolenaar else 91790c18bfSBram Moolenaar return '' 92790c18bfSBram Moolenaar endif 93790c18bfSBram Moolenaar finally 94790c18bfSBram Moolenaar let &l:grepprg = grepprg 95790c18bfSBram Moolenaar let &l:grepformat = grepformat 96790c18bfSBram Moolenaar let &shellpipe = shellpipe 97790c18bfSBram Moolenaar endtry 98790c18bfSBram Moolenaarendfunction 99790c18bfSBram Moolenaarcommand! -bar -bang -complete=file -nargs=+ Ocpgrep exe s:OcpGrep(<q-bang>, <q-args>) 100790c18bfSBram Moolenaar 1015eb86f91SBram Moolenaar" switching between interfaces (.mli) and implementations (.ml) 1025eb86f91SBram Moolenaarif !exists("g:did_ocaml_switch") 1035eb86f91SBram Moolenaar let g:did_ocaml_switch = 1 10416ea3676SBram Moolenaar nnoremap <Plug>OCamlSwitchEdit :<C-u>call OCaml_switch(0)<CR> 10516ea3676SBram Moolenaar nnoremap <Plug>OCamlSwitchNewWin :<C-u>call OCaml_switch(1)<CR> 1065eb86f91SBram Moolenaar fun OCaml_switch(newwin) 1075eb86f91SBram Moolenaar if (match(bufname(""), "\\.mli$") >= 0) 108e37d50a5SBram Moolenaar let fname = s:Fnameescape(substitute(bufname(""), "\\.mli$", ".ml", "")) 1095eb86f91SBram Moolenaar if (a:newwin == 1) 1105eb86f91SBram Moolenaar exec "new " . fname 1115eb86f91SBram Moolenaar else 1125eb86f91SBram Moolenaar exec "arge " . fname 1135eb86f91SBram Moolenaar endif 1145eb86f91SBram Moolenaar elseif (match(bufname(""), "\\.ml$") >= 0) 115e37d50a5SBram Moolenaar let fname = s:Fnameescape(bufname("")) . "i" 1165eb86f91SBram Moolenaar if (a:newwin == 1) 1175eb86f91SBram Moolenaar exec "new " . fname 1185eb86f91SBram Moolenaar else 1195eb86f91SBram Moolenaar exec "arge " . fname 1205eb86f91SBram Moolenaar endif 1215eb86f91SBram Moolenaar endif 1225eb86f91SBram Moolenaar endfun 1235eb86f91SBram Moolenaarendif 1245eb86f91SBram Moolenaar 125a5792f58SBram Moolenaar" Folding support 126a5792f58SBram Moolenaar 127a5792f58SBram Moolenaar" Get the modeline because folding depends on indentation 128790c18bfSBram Moolenaarlet lnum = search('^\s*(\*:o\?caml:', 'n') 129790c18bfSBram Moolenaarlet s:modeline = lnum? getline(lnum): "" 130a5792f58SBram Moolenaar 131a5792f58SBram Moolenaar" Get the indentation params 132a5792f58SBram Moolenaarlet s:m = matchstr(s:modeline,'default\s*=\s*\d\+') 133a5792f58SBram Moolenaarif s:m != "" 134a5792f58SBram Moolenaar let s:idef = matchstr(s:m,'\d\+') 135a5792f58SBram Moolenaarelseif exists("g:omlet_indent") 136a5792f58SBram Moolenaar let s:idef = g:omlet_indent 137a5792f58SBram Moolenaarelse 138a5792f58SBram Moolenaar let s:idef = 2 139a5792f58SBram Moolenaarendif 140a5792f58SBram Moolenaarlet s:m = matchstr(s:modeline,'struct\s*=\s*\d\+') 141a5792f58SBram Moolenaarif s:m != "" 142a5792f58SBram Moolenaar let s:i = matchstr(s:m,'\d\+') 143a5792f58SBram Moolenaarelseif exists("g:omlet_indent_struct") 144a5792f58SBram Moolenaar let s:i = g:omlet_indent_struct 145a5792f58SBram Moolenaarelse 146a5792f58SBram Moolenaar let s:i = s:idef 147a5792f58SBram Moolenaarendif 148a5792f58SBram Moolenaar 149a5792f58SBram Moolenaar" Set the folding method 150a5792f58SBram Moolenaarif exists("g:ocaml_folding") 151a5792f58SBram Moolenaar setlocal foldmethod=expr 152a5792f58SBram Moolenaar setlocal foldexpr=OMLetFoldLevel(v:lnum) 153a5792f58SBram Moolenaarendif 154a5792f58SBram Moolenaar 15516ea3676SBram Moolenaarlet b:undo_ftplugin = "setlocal efm< foldmethod< foldexpr<" 15616ea3676SBram Moolenaar \ . "| unlet! b:mw b:match_words b:match_ignorecase" 15716ea3676SBram Moolenaar 15816ea3676SBram Moolenaar 159a5792f58SBram Moolenaar" - Only definitions below, executed once ------------------------------------- 160a5792f58SBram Moolenaar 161a5792f58SBram Moolenaarif exists("*OMLetFoldLevel") 162e0e39175SBram Moolenaar let &cpoptions = s:cposet 163e0e39175SBram Moolenaar unlet s:cposet 164a5792f58SBram Moolenaar finish 165a5792f58SBram Moolenaarendif 166a5792f58SBram Moolenaar 167a5792f58SBram Moolenaarfunction s:topindent(lnum) 168a5792f58SBram Moolenaar let l = a:lnum 169a5792f58SBram Moolenaar while l > 0 170a5792f58SBram Moolenaar if getline(l) =~ '\s*\%(\<struct\>\|\<sig\>\|\<object\>\)' 171a5792f58SBram Moolenaar return indent(l) 172a5792f58SBram Moolenaar endif 173a5792f58SBram Moolenaar let l = l-1 174a5792f58SBram Moolenaar endwhile 175a5792f58SBram Moolenaar return -s:i 176a5792f58SBram Moolenaarendfunction 177a5792f58SBram Moolenaar 178a5792f58SBram Moolenaarfunction OMLetFoldLevel(l) 179a5792f58SBram Moolenaar 180a5792f58SBram Moolenaar " This is for not merging blank lines around folds to them 181a5792f58SBram Moolenaar if getline(a:l) !~ '\S' 182a5792f58SBram Moolenaar return -1 183a5792f58SBram Moolenaar endif 184a5792f58SBram Moolenaar 185a5792f58SBram Moolenaar " We start folds for modules, classes, and every toplevel definition 186a5792f58SBram Moolenaar if getline(a:l) =~ '^\s*\%(\<val\>\|\<module\>\|\<class\>\|\<type\>\|\<method\>\|\<initializer\>\|\<inherit\>\|\<exception\>\|\<external\>\)' 187a5792f58SBram Moolenaar exe 'return ">' (indent(a:l)/s:i)+1 '"' 188a5792f58SBram Moolenaar endif 189a5792f58SBram Moolenaar 190a5792f58SBram Moolenaar " Toplevel let are detected thanks to the indentation 191a5792f58SBram Moolenaar if getline(a:l) =~ '^\s*let\>' && indent(a:l) == s:i+s:topindent(a:l) 192a5792f58SBram Moolenaar exe 'return ">' (indent(a:l)/s:i)+1 '"' 193a5792f58SBram Moolenaar endif 194a5792f58SBram Moolenaar 195a5792f58SBram Moolenaar " We close fold on end which are associated to struct, sig or object. 196a5792f58SBram Moolenaar " We use syntax information to do that. 197a5792f58SBram Moolenaar if getline(a:l) =~ '^\s*end\>' && synIDattr(synID(a:l, indent(a:l)+1, 0), "name") != "ocamlKeyword" 198a5792f58SBram Moolenaar return (indent(a:l)/s:i)+1 199a5792f58SBram Moolenaar endif 200a5792f58SBram Moolenaar 201a5792f58SBram Moolenaar " Folds end on ;; 202a5792f58SBram Moolenaar if getline(a:l) =~ '^\s*;;' 203a5792f58SBram Moolenaar exe 'return "<' (indent(a:l)/s:i)+1 '"' 204a5792f58SBram Moolenaar endif 205a5792f58SBram Moolenaar 206a5792f58SBram Moolenaar " Comments around folds aren't merged to them. 207a5792f58SBram Moolenaar if synIDattr(synID(a:l, indent(a:l)+1, 0), "name") == "ocamlComment" 208a5792f58SBram Moolenaar return -1 209a5792f58SBram Moolenaar endif 210a5792f58SBram Moolenaar 211a5792f58SBram Moolenaar return '=' 212a5792f58SBram Moolenaarendfunction 213a5792f58SBram Moolenaar 2143577c6faSBram Moolenaar" Vim support for OCaml .annot files 2155eb86f91SBram Moolenaar" 2163577c6faSBram Moolenaar" Last Change: 2007 Jul 17 2173577c6faSBram Moolenaar" Maintainer: Vincent Aravantinos <[email protected]> 2183577c6faSBram Moolenaar" License: public domain 2193577c6faSBram Moolenaar" 2203577c6faSBram Moolenaar" Originally inspired by 'ocaml-dtypes.vim' by Stefano Zacchiroli. 2213577c6faSBram Moolenaar" The source code is quite radically different for we not use python anymore. 2223577c6faSBram Moolenaar" However this plugin should have the exact same behaviour, that's why the 2233577c6faSBram Moolenaar" following lines are the quite exact copy of Stefano's original plugin : 2243577c6faSBram Moolenaar" 2253577c6faSBram Moolenaar" << 2263577c6faSBram Moolenaar" Executing Ocaml_print_type(<mode>) function will display in the Vim bottom 2275eb86f91SBram Moolenaar" line(s) the type of an ocaml value getting it from the corresponding .annot 2285eb86f91SBram Moolenaar" file (if any). If Vim is in visual mode, <mode> should be "visual" and the 2295eb86f91SBram Moolenaar" selected ocaml value correspond to the highlighted text, otherwise (<mode> 2305eb86f91SBram Moolenaar" can be anything else) it corresponds to the literal found at the current 2315eb86f91SBram Moolenaar" cursor position. 2325eb86f91SBram Moolenaar" 2333577c6faSBram Moolenaar" Typing '<LocalLeader>t' (LocalLeader defaults to '\', see :h LocalLeader) 2343577c6faSBram Moolenaar" will cause " Ocaml_print_type function to be invoked with the right 2353577c6faSBram Moolenaar" argument depending on the current mode (visual or not). 2363577c6faSBram Moolenaar" >> 2375eb86f91SBram Moolenaar" 2383577c6faSBram Moolenaar" If you find something not matching this behaviour, please signal it. 2395eb86f91SBram Moolenaar" 2403577c6faSBram Moolenaar" Differences are: 2413577c6faSBram Moolenaar" - no need for python support 2423577c6faSBram Moolenaar" + plus : more portable 2433577c6faSBram Moolenaar" + minus: no more lazy parsing, it looks very fast however 2445eb86f91SBram Moolenaar" 2453577c6faSBram Moolenaar" - ocamlbuild support, ie. 2463577c6faSBram Moolenaar" + the plugin finds the _build directory and looks for the 2473577c6faSBram Moolenaar" corresponding file inside; 2483577c6faSBram Moolenaar" + if the user decides to change the name of the _build directory thanks 2493577c6faSBram Moolenaar" to the '-build-dir' option of ocamlbuild, the plugin will manage in 2503577c6faSBram Moolenaar" most cases to find it out (most cases = if the source file has a unique 2513577c6faSBram Moolenaar" name among your whole project); 2523577c6faSBram Moolenaar" + if ocamlbuild is not used, the usual behaviour holds; ie. the .annot 2533577c6faSBram Moolenaar" file should be in the same directory as the source file; 2543577c6faSBram Moolenaar" + for vim plugin programmers: 2553577c6faSBram Moolenaar" the variable 'b:_build_dir' contains the inferred path to the build 2563577c6faSBram Moolenaar" directory, even if this one is not named '_build'. 2573577c6faSBram Moolenaar" 2583577c6faSBram Moolenaar" Bonus : 2593577c6faSBram Moolenaar" - latin1 accents are handled 2603577c6faSBram Moolenaar" - lists are handled, even on multiple lines, you don't need the visual mode 2613577c6faSBram Moolenaar" (the cursor must be on the first bracket) 2623577c6faSBram Moolenaar" - parenthesized expressions, arrays, and structures (ie. '(...)', '[|...|]', 2633577c6faSBram Moolenaar" and '{...}') are handled the same way 2645eb86f91SBram Moolenaar 2653577c6faSBram Moolenaar " Copied from Stefano's original plugin : 2663577c6faSBram Moolenaar " << 2673577c6faSBram Moolenaar " .annot ocaml file representation 2683577c6faSBram Moolenaar " 2693577c6faSBram Moolenaar " File format (copied verbatim from caml-types.el) 2703577c6faSBram Moolenaar " 2713577c6faSBram Moolenaar " file ::= block * 2723577c6faSBram Moolenaar " block ::= position <SP> position <LF> annotation * 2733577c6faSBram Moolenaar " position ::= filename <SP> num <SP> num <SP> num 2743577c6faSBram Moolenaar " annotation ::= keyword open-paren <LF> <SP> <SP> data <LF> close-paren 2753577c6faSBram Moolenaar " 2763577c6faSBram Moolenaar " <SP> is a space character (ASCII 0x20) 2773577c6faSBram Moolenaar " <LF> is a line-feed character (ASCII 0x0A) 2783577c6faSBram Moolenaar " num is a sequence of decimal digits 2793577c6faSBram Moolenaar " filename is a string with the lexical conventions of O'Caml 2803577c6faSBram Moolenaar " open-paren is an open parenthesis (ASCII 0x28) 2813577c6faSBram Moolenaar " close-paren is a closed parenthesis (ASCII 0x29) 2823577c6faSBram Moolenaar " data is any sequence of characters where <LF> is always followed by 2833577c6faSBram Moolenaar " at least two space characters. 2843577c6faSBram Moolenaar " 2853577c6faSBram Moolenaar " - in each block, the two positions are respectively the start and the 2863577c6faSBram Moolenaar " end of the range described by the block. 2873577c6faSBram Moolenaar " - in a position, the filename is the name of the file, the first num 2883577c6faSBram Moolenaar " is the line number, the second num is the offset of the beginning 2893577c6faSBram Moolenaar " of the line, the third num is the offset of the position itself. 2903577c6faSBram Moolenaar " - the char number within the line is the difference between the third 2913577c6faSBram Moolenaar " and second nums. 2923577c6faSBram Moolenaar " 2933577c6faSBram Moolenaar " For the moment, the only possible keyword is \"type\"." 2943577c6faSBram Moolenaar " >> 2955eb86f91SBram Moolenaar 296e37d50a5SBram Moolenaar 2973577c6faSBram Moolenaar" 1. Finding the annotation file even if we use ocamlbuild 2985eb86f91SBram Moolenaar 2993577c6faSBram Moolenaar " In: two strings representing paths 3003577c6faSBram Moolenaar " Out: one string representing the common prefix between the two paths 3013577c6faSBram Moolenaar function! s:Find_common_path (p1,p2) 3023577c6faSBram Moolenaar let temp = a:p2 3033577c6faSBram Moolenaar while matchstr(a:p1,temp) == '' 3043577c6faSBram Moolenaar let temp = substitute(temp,'/[^/]*$','','') 3053577c6faSBram Moolenaar endwhile 3063577c6faSBram Moolenaar return temp 3073577c6faSBram Moolenaar endfun 3085eb86f91SBram Moolenaar 3093577c6faSBram Moolenaar " After call: 310b8ff1fb5SBram Moolenaar " 311b8ff1fb5SBram Moolenaar " Following information have been put in s:annot_file_list, using 312b8ff1fb5SBram Moolenaar " annot_file_name name as key: 313b8ff1fb5SBram Moolenaar " - annot_file_path : 3143577c6faSBram Moolenaar " path to the .annot file corresponding to the 3153577c6faSBram Moolenaar " source file (dealing with ocamlbuild stuff) 316b8ff1fb5SBram Moolenaar " - _build_path: 3173577c6faSBram Moolenaar " path to the build directory even if this one is 3183577c6faSBram Moolenaar " not named '_build' 319b8ff1fb5SBram Moolenaar " - date_of_last annot: 320b8ff1fb5SBram Moolenaar " Set to 0 until we load the file. It contains the 321b8ff1fb5SBram Moolenaar " date at which the file has been loaded. 3223577c6faSBram Moolenaar function! s:Locate_annotation() 323b8ff1fb5SBram Moolenaar let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot' 324b8ff1fb5SBram Moolenaar if !exists ("s:annot_file_list[annot_file_name]") 325e37d50a5SBram Moolenaar silent exe 'cd' s:Fnameescape(expand('%:p:h')) 3263577c6faSBram Moolenaar " 1st case : the annot file is in the same directory as the buffer (no ocamlbuild) 327b8ff1fb5SBram Moolenaar let annot_file_path = findfile(annot_file_name,'.') 328b8ff1fb5SBram Moolenaar if annot_file_path != '' 329b8ff1fb5SBram Moolenaar let annot_file_path = getcwd().'/'.annot_file_path 330b8ff1fb5SBram Moolenaar let _build_path = '' 3315eb86f91SBram Moolenaar else 3323577c6faSBram Moolenaar " 2nd case : the buffer and the _build directory are in the same directory 3333577c6faSBram Moolenaar " .. 3343577c6faSBram Moolenaar " / \ 3353577c6faSBram Moolenaar " / \ 3363577c6faSBram Moolenaar " _build .ml 3373577c6faSBram Moolenaar " 338b8ff1fb5SBram Moolenaar let _build_path = finddir('_build','.') 339b8ff1fb5SBram Moolenaar if _build_path != '' 340b8ff1fb5SBram Moolenaar let _build_path = getcwd().'/'._build_path 341b8ff1fb5SBram Moolenaar let annot_file_path = findfile(annot_file_name,'_build') 342b8ff1fb5SBram Moolenaar if annot_file_path != '' 343b8ff1fb5SBram Moolenaar let annot_file_path = getcwd().'/'.annot_file_path 3443577c6faSBram Moolenaar endif 3453577c6faSBram Moolenaar else 3463577c6faSBram Moolenaar " 3rd case : the _build directory is in a directory higher in the file hierarchy 3473577c6faSBram Moolenaar " (it can't be deeper by ocamlbuild requirements) 3483577c6faSBram Moolenaar " .. 3493577c6faSBram Moolenaar " / \ 3503577c6faSBram Moolenaar " / \ 3513577c6faSBram Moolenaar " _build ... 3523577c6faSBram Moolenaar " \ 3533577c6faSBram Moolenaar " \ 3543577c6faSBram Moolenaar " .ml 3553577c6faSBram Moolenaar " 356b8ff1fb5SBram Moolenaar let _build_path = finddir('_build',';') 357b8ff1fb5SBram Moolenaar if _build_path != '' 358b8ff1fb5SBram Moolenaar let project_path = substitute(_build_path,'/_build$','','') 359e37d50a5SBram Moolenaar let path_relative_to_project = s:Fnameescape(substitute(expand('%:p:h'),project_path.'/','','')) 360b8ff1fb5SBram Moolenaar let annot_file_path = findfile(annot_file_name,project_path.'/_build/'.path_relative_to_project) 3613577c6faSBram Moolenaar else 362b8ff1fb5SBram Moolenaar let annot_file_path = findfile(annot_file_name,'**') 3633577c6faSBram Moolenaar "4th case : what if the user decided to change the name of the _build directory ? 3643577c6faSBram Moolenaar " -> we relax the constraints, it should work in most cases 365b8ff1fb5SBram Moolenaar if annot_file_path != '' 3663577c6faSBram Moolenaar " 4a. we suppose the renamed _build directory is in the current directory 367b8ff1fb5SBram Moolenaar let _build_path = matchstr(annot_file_path,'^[^/]*') 368b8ff1fb5SBram Moolenaar if annot_file_path != '' 369b8ff1fb5SBram Moolenaar let annot_file_path = getcwd().'/'.annot_file_path 370b8ff1fb5SBram Moolenaar let _build_path = getcwd().'/'._build_path 3713577c6faSBram Moolenaar endif 3723577c6faSBram Moolenaar else 373b8ff1fb5SBram Moolenaar let annot_file_name = '' 374*6c391a74SBram Moolenaar "(Pierre Vittet: I have commented 4b because this was crashing 375b8ff1fb5SBram Moolenaar "my vim (it produced infinite loop)) 376b8ff1fb5SBram Moolenaar " 3773577c6faSBram Moolenaar " 4b. anarchy : the renamed _build directory may be higher in the hierarchy 3783577c6faSBram Moolenaar " this will work if the file for which we are looking annotations has a unique name in the whole project 3793577c6faSBram Moolenaar " if this is not the case, it may still work, but no warranty here 380b8ff1fb5SBram Moolenaar "let annot_file_path = findfile(annot_file_name,'**;') 381b8ff1fb5SBram Moolenaar "let project_path = s:Find_common_path(annot_file_path,expand('%:p:h')) 382b8ff1fb5SBram Moolenaar "let _build_path = matchstr(annot_file_path,project_path.'/[^/]*') 3833577c6faSBram Moolenaar endif 3843577c6faSBram Moolenaar endif 3853577c6faSBram Moolenaar endif 3863577c6faSBram Moolenaar endif 3873577c6faSBram Moolenaar 388b8ff1fb5SBram Moolenaar if annot_file_path == '' 3893577c6faSBram Moolenaar throw 'E484: no annotation file found' 3903577c6faSBram Moolenaar endif 3913577c6faSBram Moolenaar 3923577c6faSBram Moolenaar silent exe 'cd' '-' 393b8ff1fb5SBram Moolenaar let s:annot_file_list[annot_file_name]= [annot_file_path, _build_path, 0] 3945eb86f91SBram Moolenaar endif 3955eb86f91SBram Moolenaar endfun 3965eb86f91SBram Moolenaar 3977e6a515eSBram Moolenaar " This variable contains a dictionary of lists. Each element of the dictionary 3987e6a515eSBram Moolenaar " represents an annotation system. An annotation system is a list with: 3997e6a515eSBram Moolenaar " - annotation file name as its key 400b8ff1fb5SBram Moolenaar " - annotation file path as first element of the contained list 401b8ff1fb5SBram Moolenaar " - build path as second element of the contained list 402b8ff1fb5SBram Moolenaar " - annot_file_last_mod (contain the date of .annot file) as third element 403b8ff1fb5SBram Moolenaar let s:annot_file_list = {} 4043577c6faSBram Moolenaar 4053577c6faSBram Moolenaar" 2. Finding the type information in the annotation file 4063577c6faSBram Moolenaar 4073577c6faSBram Moolenaar " a. The annotation file is opened in vim as a buffer that 4083577c6faSBram Moolenaar " should be (almost) invisible to the user. 4093577c6faSBram Moolenaar 4103577c6faSBram Moolenaar " After call: 4113577c6faSBram Moolenaar " The current buffer is now the one containing the .annot file. 4123577c6faSBram Moolenaar " We manage to keep all this hidden to the user's eye. 413b8ff1fb5SBram Moolenaar function! s:Enter_annotation_buffer(annot_file_path) 4143577c6faSBram Moolenaar let s:current_pos = getpos('.') 4153577c6faSBram Moolenaar let s:current_hidden = &l:hidden 4163577c6faSBram Moolenaar set hidden 4173577c6faSBram Moolenaar let s:current_buf = bufname('%') 418b8ff1fb5SBram Moolenaar if bufloaded(a:annot_file_path) 419b8ff1fb5SBram Moolenaar silent exe 'keepj keepalt' 'buffer' s:Fnameescape(a:annot_file_path) 4203577c6faSBram Moolenaar else 421b8ff1fb5SBram Moolenaar silent exe 'keepj keepalt' 'view' s:Fnameescape(a:annot_file_path) 4223577c6faSBram Moolenaar endif 423b8ff1fb5SBram Moolenaar call setpos(".", [0, 0 , 0 , 0]) 4245eb86f91SBram Moolenaar endfun 4255eb86f91SBram Moolenaar 4263577c6faSBram Moolenaar " After call: 4273577c6faSBram Moolenaar " The original buffer has been restored in the exact same state as before. 4283577c6faSBram Moolenaar function! s:Exit_annotation_buffer() 429e37d50a5SBram Moolenaar silent exe 'keepj keepalt' 'buffer' s:Fnameescape(s:current_buf) 4303577c6faSBram Moolenaar let &l:hidden = s:current_hidden 4313577c6faSBram Moolenaar call setpos('.',s:current_pos) 4323577c6faSBram Moolenaar endfun 4333577c6faSBram Moolenaar 4343577c6faSBram Moolenaar " After call: 4353577c6faSBram Moolenaar " The annot file is loaded and assigned to a buffer. 4363577c6faSBram Moolenaar " This also handles the modification date of the .annot file, eg. after a 437b8ff1fb5SBram Moolenaar " compilation (return an updated annot_file_list). 438b8ff1fb5SBram Moolenaar function! s:Load_annotation(annot_file_name) 439b8ff1fb5SBram Moolenaar let annot = s:annot_file_list[a:annot_file_name] 440b8ff1fb5SBram Moolenaar let annot_file_path = annot[0] 441b8ff1fb5SBram Moolenaar let annot_file_last_mod = 0 442b8ff1fb5SBram Moolenaar if exists("annot[2]") 443b8ff1fb5SBram Moolenaar let annot_file_last_mod = annot[2] 4443577c6faSBram Moolenaar endif 445b8ff1fb5SBram Moolenaar if bufloaded(annot_file_path) && annot_file_last_mod < getftime(annot_file_path) 446b8ff1fb5SBram Moolenaar " if there is a more recent file 447b8ff1fb5SBram Moolenaar let nr = bufnr(annot_file_path) 448b8ff1fb5SBram Moolenaar silent exe 'keepj keepalt' 'bunload' nr 449b8ff1fb5SBram Moolenaar endif 450b8ff1fb5SBram Moolenaar if !bufloaded(annot_file_path) 451b8ff1fb5SBram Moolenaar call s:Enter_annotation_buffer(annot_file_path) 4523577c6faSBram Moolenaar setlocal nobuflisted 4533577c6faSBram Moolenaar setlocal bufhidden=hide 4543577c6faSBram Moolenaar setlocal noswapfile 4553577c6faSBram Moolenaar setlocal buftype=nowrite 4563577c6faSBram Moolenaar call s:Exit_annotation_buffer() 457b8ff1fb5SBram Moolenaar let annot[2] = getftime(annot_file_path) 458b8ff1fb5SBram Moolenaar " List updated with the new date 459b8ff1fb5SBram Moolenaar let s:annot_file_list[a:annot_file_name] = annot 4603577c6faSBram Moolenaar endif 4613577c6faSBram Moolenaar endfun 4623577c6faSBram Moolenaar 4633577c6faSBram Moolenaar "b. 'search' and 'match' work to find the type information 4643577c6faSBram Moolenaar 465*6c391a74SBram Moolenaar "In: - lin1,col1: position of expression first char 466*6c391a74SBram Moolenaar " - lin2,col2: position of expression last char 4673577c6faSBram Moolenaar "Out: - the pattern to be looked for to find the block 4683577c6faSBram Moolenaar " Must be called in the source buffer (use of line2byte) 4693577c6faSBram Moolenaar function! s:Block_pattern(lin1,lin2,col1,col2) 4703577c6faSBram Moolenaar let start_num1 = a:lin1 4713577c6faSBram Moolenaar let start_num2 = line2byte(a:lin1) - 1 4723577c6faSBram Moolenaar let start_num3 = start_num2 + a:col1 4739c754c45SBram Moolenaar let path = '"\(\\"\|[^"]\)\+"' 4749c754c45SBram Moolenaar let start_pos = path.' '.start_num1.' '.start_num2.' '.start_num3 4753577c6faSBram Moolenaar let end_num1 = a:lin2 4763577c6faSBram Moolenaar let end_num2 = line2byte(a:lin2) - 1 4773577c6faSBram Moolenaar let end_num3 = end_num2 + a:col2 4789c754c45SBram Moolenaar let end_pos = path.' '.end_num1.' '.end_num2.' '.end_num3 4793577c6faSBram Moolenaar return '^'.start_pos.' '.end_pos."$" 4803577c6faSBram Moolenaar " rq: the '^' here is not totally correct regarding the annot file "grammar" 4813577c6faSBram Moolenaar " but currently the annotation file respects this, and it's a little bit faster with the '^'; 4823577c6faSBram Moolenaar " can be removed safely. 4833577c6faSBram Moolenaar endfun 4843577c6faSBram Moolenaar 4853577c6faSBram Moolenaar "In: (the cursor position should be at the start of an annotation) 4863577c6faSBram Moolenaar "Out: the type information 4873577c6faSBram Moolenaar " Must be called in the annotation buffer (use of search) 4883577c6faSBram Moolenaar function! s:Match_data() 4893577c6faSBram Moolenaar " rq: idem as previously, in the following, the '^' at start of patterns is not necessary 4903577c6faSBram Moolenaar keepj while search('^type($','ce',line(".")) == 0 4913577c6faSBram Moolenaar keepj if search('^.\{-}($','e') == 0 4923577c6faSBram Moolenaar throw "no_annotation" 4933577c6faSBram Moolenaar endif 4943577c6faSBram Moolenaar keepj if searchpair('(','',')') == 0 4953577c6faSBram Moolenaar throw "malformed_annot_file" 4963577c6faSBram Moolenaar endif 4973577c6faSBram Moolenaar endwhile 4983577c6faSBram Moolenaar let begin = line(".") + 1 4993577c6faSBram Moolenaar keepj if searchpair('(','',')') == 0 5003577c6faSBram Moolenaar throw "malformed_annot_file" 5013577c6faSBram Moolenaar endif 5023577c6faSBram Moolenaar let end = line(".") - 1 5033577c6faSBram Moolenaar return join(getline(begin,end),"\n") 5043577c6faSBram Moolenaar endfun 5053577c6faSBram Moolenaar 5063577c6faSBram Moolenaar "In: the pattern to look for in order to match the block 5073577c6faSBram Moolenaar "Out: the type information (calls s:Match_data) 5083577c6faSBram Moolenaar " Should be called in the annotation buffer 509b8ff1fb5SBram Moolenaar function! s:Extract_type_data(block_pattern, annot_file_name) 510b8ff1fb5SBram Moolenaar let annot_file_path = s:annot_file_list[a:annot_file_name][0] 511b8ff1fb5SBram Moolenaar call s:Enter_annotation_buffer(annot_file_path) 5123577c6faSBram Moolenaar try 5133577c6faSBram Moolenaar if search(a:block_pattern,'e') == 0 5143577c6faSBram Moolenaar throw "no_annotation" 5153577c6faSBram Moolenaar endif 5163577c6faSBram Moolenaar call cursor(line(".") + 1,1) 5173577c6faSBram Moolenaar let annotation = s:Match_data() 5183577c6faSBram Moolenaar finally 5193577c6faSBram Moolenaar call s:Exit_annotation_buffer() 5203577c6faSBram Moolenaar endtry 5213577c6faSBram Moolenaar return annotation 5223577c6faSBram Moolenaar endfun 5233577c6faSBram Moolenaar 5243577c6faSBram Moolenaar "c. link this stuff with what the user wants 5253577c6faSBram Moolenaar " ie. get the expression selected/under the cursor 5263577c6faSBram Moolenaar 5277e6a515eSBram Moolenaar let s:ocaml_word_char = '\w|[\xc0-\xff]|''' 5283577c6faSBram Moolenaar 5293577c6faSBram Moolenaar "In: the current mode (eg. "visual", "normal", etc.) 5303577c6faSBram Moolenaar "Out: the borders of the expression we are looking for the type 5313577c6faSBram Moolenaar function! s:Match_borders(mode) 5323577c6faSBram Moolenaar if a:mode == "visual" 5333577c6faSBram Moolenaar let cur = getpos(".") 5343577c6faSBram Moolenaar normal `< 5353577c6faSBram Moolenaar let col1 = col(".") 5363577c6faSBram Moolenaar let lin1 = line(".") 5373577c6faSBram Moolenaar normal `> 5383577c6faSBram Moolenaar let col2 = col(".") 5393577c6faSBram Moolenaar let lin2 = line(".") 5403577c6faSBram Moolenaar call cursor(cur[1],cur[2]) 5413577c6faSBram Moolenaar return [lin1,lin2,col1-1,col2] 5423577c6faSBram Moolenaar else 5433577c6faSBram Moolenaar let cursor_line = line(".") 5443577c6faSBram Moolenaar let cursor_col = col(".") 5453577c6faSBram Moolenaar let line = getline('.') 5463577c6faSBram Moolenaar if line[cursor_col-1:cursor_col] == '[|' 5473577c6faSBram Moolenaar let [lin2,col2] = searchpairpos('\[|','','|\]','n') 5483577c6faSBram Moolenaar return [cursor_line,lin2,cursor_col-1,col2+1] 5493577c6faSBram Moolenaar elseif line[cursor_col-1] == '[' 5503577c6faSBram Moolenaar let [lin2,col2] = searchpairpos('\[','','\]','n') 5513577c6faSBram Moolenaar return [cursor_line,lin2,cursor_col-1,col2] 5523577c6faSBram Moolenaar elseif line[cursor_col-1] == '(' 5533577c6faSBram Moolenaar let [lin2,col2] = searchpairpos('(','',')','n') 5543577c6faSBram Moolenaar return [cursor_line,lin2,cursor_col-1,col2] 5553577c6faSBram Moolenaar elseif line[cursor_col-1] == '{' 5563577c6faSBram Moolenaar let [lin2,col2] = searchpairpos('{','','}','n') 5573577c6faSBram Moolenaar return [cursor_line,lin2,cursor_col-1,col2] 5583577c6faSBram Moolenaar else 5593577c6faSBram Moolenaar let [lin1,col1] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','ncb') 5603577c6faSBram Moolenaar let [lin2,col2] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','nce') 5613577c6faSBram Moolenaar if col1 == 0 || col2 == 0 5623577c6faSBram Moolenaar throw "no_expression" 5633577c6faSBram Moolenaar endif 5643577c6faSBram Moolenaar return [cursor_line,cursor_line,col1-1,col2] 5653577c6faSBram Moolenaar endif 5663577c6faSBram Moolenaar endif 5673577c6faSBram Moolenaar endfun 5683577c6faSBram Moolenaar 5693577c6faSBram Moolenaar "In: the current mode (eg. "visual", "normal", etc.) 5703577c6faSBram Moolenaar "Out: the type information (calls s:Extract_type_data) 571b8ff1fb5SBram Moolenaar function! s:Get_type(mode, annot_file_name) 5723577c6faSBram Moolenaar let [lin1,lin2,col1,col2] = s:Match_borders(a:mode) 573b8ff1fb5SBram Moolenaar return s:Extract_type_data(s:Block_pattern(lin1,lin2,col1,col2), a:annot_file_name) 5743577c6faSBram Moolenaar endfun 5753577c6faSBram Moolenaar 57616ea3676SBram Moolenaar "In: A string destined to be printed in the 'echo buffer'. It has line 57716ea3676SBram Moolenaar "break and 2 space at each line beginning. 57816ea3676SBram Moolenaar "Out: A string destined to be yanked, without space and double space. 57916ea3676SBram Moolenaar function s:unformat_ocaml_type(res) 58016ea3676SBram Moolenaar "Remove end of line. 58116ea3676SBram Moolenaar let res = substitute (a:res, "\n", "", "g" ) 58216ea3676SBram Moolenaar "remove double space 58316ea3676SBram Moolenaar let res =substitute(res , " ", " ", "g") 584*6c391a74SBram Moolenaar "remove space at beginning of string. 58516ea3676SBram Moolenaar let res = substitute(res, "^ *", "", "g") 58616ea3676SBram Moolenaar return res 58716ea3676SBram Moolenaar endfunction 58816ea3676SBram Moolenaar 5893577c6faSBram Moolenaar "d. main 5903577c6faSBram Moolenaar "In: the current mode (eg. "visual", "normal", etc.) 5913577c6faSBram Moolenaar "After call: the type information is displayed 5923577c6faSBram Moolenaar if !exists("*Ocaml_get_type") 5933577c6faSBram Moolenaar function Ocaml_get_type(mode) 594b8ff1fb5SBram Moolenaar let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot' 5953577c6faSBram Moolenaar call s:Locate_annotation() 596b8ff1fb5SBram Moolenaar call s:Load_annotation(annot_file_name) 59716ea3676SBram Moolenaar let res = s:Get_type(a:mode, annot_file_name) 59816ea3676SBram Moolenaar " Copy result in the unnamed buffer 59916ea3676SBram Moolenaar let @" = s:unformat_ocaml_type(res) 60016ea3676SBram Moolenaar return res 6013577c6faSBram Moolenaar endfun 6023577c6faSBram Moolenaar endif 6033577c6faSBram Moolenaar 6043577c6faSBram Moolenaar if !exists("*Ocaml_get_type_or_not") 6053577c6faSBram Moolenaar function Ocaml_get_type_or_not(mode) 6063577c6faSBram Moolenaar let t=reltime() 6073577c6faSBram Moolenaar try 60816ea3676SBram Moolenaar let res = Ocaml_get_type(a:mode) 60916ea3676SBram Moolenaar return res 6103577c6faSBram Moolenaar catch 6113577c6faSBram Moolenaar return "" 6123577c6faSBram Moolenaar endtry 6133577c6faSBram Moolenaar endfun 6143577c6faSBram Moolenaar endif 6153577c6faSBram Moolenaar 6163577c6faSBram Moolenaar if !exists("*Ocaml_print_type") 6173577c6faSBram Moolenaar function Ocaml_print_type(mode) 6183577c6faSBram Moolenaar if expand("%:e") == "mli" 6193577c6faSBram Moolenaar echohl ErrorMsg | echo "No annotations for interface (.mli) files" | echohl None 6203577c6faSBram Moolenaar return 6213577c6faSBram Moolenaar endif 6223577c6faSBram Moolenaar try 6233577c6faSBram Moolenaar echo Ocaml_get_type(a:mode) 6243577c6faSBram Moolenaar catch /E484:/ 6253577c6faSBram Moolenaar echohl ErrorMsg | echo "No type annotations (.annot) file found" | echohl None 6263577c6faSBram Moolenaar catch /no_expression/ 6273577c6faSBram Moolenaar echohl ErrorMsg | echo "No expression found under the cursor" | echohl None 6283577c6faSBram Moolenaar catch /no_annotation/ 6293577c6faSBram Moolenaar echohl ErrorMsg | echo "No type annotation found for the given text" | echohl None 6303577c6faSBram Moolenaar catch /malformed_annot_file/ 6313577c6faSBram Moolenaar echohl ErrorMsg | echo "Malformed .annot file" | echohl None 6323577c6faSBram Moolenaar endtry 6333577c6faSBram Moolenaar endfun 6343577c6faSBram Moolenaar endif 6353577c6faSBram Moolenaar 6363577c6faSBram Moolenaar" Maps 63716ea3676SBram Moolenaar nnoremap <silent> <Plug>OCamlPrintType :<C-U>call Ocaml_print_type("normal")<CR> 63816ea3676SBram Moolenaar xnoremap <silent> <Plug>OCamlPrintType :<C-U>call Ocaml_print_type("visual")<CR>`< 639a5792f58SBram Moolenaar 640a5792f58SBram Moolenaarlet &cpoptions = s:cposet 641a5792f58SBram Moolenaarunlet s:cposet 642a5792f58SBram Moolenaar 6433577c6faSBram Moolenaar" vim:sw=2 fdm=indent 644