Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
ocaml.vim646 linesDownload Raw Back to ftplugin
1" Language:    OCaml2" 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-ocaml10" 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 17if exists("b:did_ftplugin")18  finish19endif20let b:did_ftplugin=121 22" Use standard compiler settings unless user wants otherwise23if !exists("current_compiler")24  :compiler ocaml25endif26 27" some macro28if exists('*fnameescape')29  function! s:Fnameescape(s)30    return fnameescape(a:s)31  endfun32else33  function! s:Fnameescape(s)34    return escape(a:s," \t\n*?[{`$\\%#'\"|!<")35  endfun36endif37 38" Error handling -- helps moving where the compiler wants you to go39let s:cposet=&cpoptions40set cpo&vim41 42" Comment string43setlocal comments=sr:(*\ ,mb:\ ,ex:*)44setlocal comments^=sr:(**,mb:\ \ ,ex:*)45setlocal commentstring=(*\ %s\ *)46 47" Add mappings, unless the user didn't want this.48if !exists("no_plugin_maps") && !exists("no_ocaml_maps")49  " (un)commenting50  if !hasmapto('<Plug>Comment')51    nmap <buffer> <LocalLeader>c <Plug>LUncomOn52    xmap <buffer> <LocalLeader>c <Plug>BUncomOn53    nmap <buffer> <LocalLeader>C <Plug>LUncomOff54    xmap <buffer> <LocalLeader>C <Plug>BUncomOff55  endif56 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>OCamlSwitchEdit63  nmap <buffer> <LocalLeader>S <Plug>OCamlSwitchNewWin64 65  nmap <buffer> <LocalLeader>t <Plug>OCamlPrintType66  xmap <buffer> <LocalLeader>t <Plug>OCamlPrintType67endif68 69" Let % jump between structure elements (due to Issac Trotts)70let b:mw =         '\<let\>:\<and\>:\(\<in\>\|;;\)'71let b:mw = b:mw . ',\<if\>:\<then\>:\<else\>'72let b:mw = b:mw . ',\<\(for\|while\)\>:\<do\>:\<done\>'73let b:mw = b:mw . ',\<\(object\|sig\|struct\|begin\)\>:\<end\>'74let b:mw = b:mw . ',\<\(match\|try\)\>:\<with\>'75let b:match_words = b:mw76 77let b:match_ignorecase=078 79function! s:OcpGrep(bang,args) abort80  let grepprg = &l:grepprg81  let grepformat = &l:grepformat82  let shellpipe = &shellpipe83  try84    let &l:grepprg = "ocp-grep -c never"85    setlocal grepformat=%f:%l:%m86    if &shellpipe ==# '2>&1| tee' || &shellpipe ==# '|& tee'87      let &shellpipe = "| tee"88    endif89    execute 'grep! '.a:args90    if empty(a:bang) && !empty(getqflist())91      return 'cfirst'92    else93      return ''94    endif95  finally96    let &l:grepprg = grepprg97    let &l:grepformat = grepformat98    let &shellpipe = shellpipe99  endtry100endfunction101command! -bar -bang -complete=file -nargs=+ Ocpgrep exe s:OcpGrep(<q-bang>, <q-args>)102 103" switching between interfaces (.mli) and implementations (.ml)104if !exists("g:did_ocaml_switch")105  let g:did_ocaml_switch = 1106  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 " . fname113      else114        exec "arge " . fname115      endif116    elseif (match(bufname(""), "\\.ml$") >= 0)117      let fname = s:Fnameescape(bufname("")) . "i"118      if (a:newwin == 1)119        exec "new " . fname120      else121        exec "arge " . fname122      endif123    endif124  endfun125endif126 127" Folding support128 129" Get the modeline because folding depends on indentation130let lnum = search('^\s*(\*:o\?caml:', 'n')131let s:modeline = lnum? getline(lnum): ""132 133" Get the indentation params134let s:m = matchstr(s:modeline,'default\s*=\s*\d\+')135if s:m != ""136  let s:idef = matchstr(s:m,'\d\+')137elseif exists("g:omlet_indent")138  let s:idef = g:omlet_indent139else140  let s:idef = 2141endif142let s:m = matchstr(s:modeline,'struct\s*=\s*\d\+')143if s:m != ""144  let s:i = matchstr(s:m,'\d\+')145elseif exists("g:omlet_indent_struct")146  let s:i = g:omlet_indent_struct147else148  let s:i = s:idef149endif150 151" Set the folding method152if exists("g:ocaml_folding")153  setlocal foldmethod=expr154  setlocal foldexpr=OMLetFoldLevel(v:lnum)155endif156 157let 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 163if exists("*OMLetFoldLevel")164  let &cpoptions = s:cposet165  unlet s:cposet166  finish167endif168 169function s:topindent(lnum)170  let l = a:lnum171  while l > 0172    if getline(l) =~ '\s*\%(\<struct\>\|\<sig\>\|\<object\>\)'173      return indent(l)174    endif175    let l = l-1176  endwhile177  return -s:i178endfunction179 180function OMLetFoldLevel(l)181 182  " This is for not merging blank lines around folds to them183  if getline(a:l) !~ '\S'184    return -1185  endif186 187  " We start folds for modules, classes, and every toplevel definition188  if getline(a:l) =~ '^\s*\%(\<val\>\|\<module\>\|\<class\>\|\<type\>\|\<method\>\|\<initializer\>\|\<inherit\>\|\<exception\>\|\<external\>\)'189    exe 'return ">' (indent(a:l)/s:i)+1 '"'190  endif191 192  " Toplevel let are detected thanks to the indentation193  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  endif196 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)+1201  endif202 203  " Folds end on ;;204  if getline(a:l) =~ '^\s*;;'205    exe 'return "<' (indent(a:l)/s:i)+1 '"'206  endif207 208  " Comments around folds aren't merged to them.209  if synIDattr(synID(a:l, indent(a:l)+1, 0), "name") == "ocamlComment"210    return -1211  endif212 213  return '='214endfunction215 216" Vim support for OCaml .annot files217"218" Last Change: 2007 Jul 17219" Maintainer:  Vincent Aravantinos <vincent.aravantinos@gmail.com>220" License:     public domain221"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 the225" 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 bottom229" line(s) the type of an ocaml value getting it from the corresponding .annot230" file (if any).  If Vim is in visual mode, <mode> should be "visual" and the231" selected ocaml value correspond to the highlighted text, otherwise (<mode>232" can be anything else) it corresponds to the literal found at the current233" 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 right237" 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 support244"     + plus : more portable245"     + minus: no more lazy parsing, it looks very fast however246"247"   - ocamlbuild support, ie.248"     + the plugin finds the _build directory and looks for the249"       corresponding file inside;250"     + if the user decides to change the name of the _build directory thanks251"       to the '-build-dir' option of ocamlbuild, the plugin will manage in252"       most cases to find it out (most cases = if the source file has a unique253"       name among your whole project);254"     + if ocamlbuild is not used, the usual behaviour holds; ie. the .annot255"       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 build258"       directory, even if this one is not named '_build'.259"260" Bonus :261"   - latin1 accents are handled262"   - lists are handled, even on multiple lines, you don't need the visual mode263"     (the cursor must be on the first bracket)264"   - parenthesized expressions, arrays, and structures (ie. '(...)', '[|...|]',265"     and '{...}') are handled the same way266 267  " Copied from Stefano's original plugin :268  " <<269  "      .annot ocaml file representation270  "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> num276  "      annotation ::= keyword open-paren <LF> <SP> <SP> data <LF> close-paren277  "278  "      <SP> is a space character (ASCII 0x20)279  "      <LF> is a line-feed character (ASCII 0x0A)280  "      num is a sequence of decimal digits281  "      filename is a string with the lexical conventions of O'Caml282  "      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 by285  "           at least two space characters.286  "287  "      - in each block, the two positions are respectively the start and the288  "        end of the range described by the block.289  "      - in a position, the filename is the name of the file, the first num290  "        is the line number, the second num is the offset of the beginning291  "        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 third293  "        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 ocamlbuild300 301    " In:  two strings representing paths302    " Out: one string representing the common prefix between the two paths303  function! s:Find_common_path (p1,p2)304    let temp = a:p2305    while matchstr(a:p1,temp) == ''306      let temp = substitute(temp,'/[^/]*$','','')307    endwhile308    return temp309  endfun310 311    " After call:312    "313    "  Following information have been put in s:annot_file_list, using314    "  annot_file_name name as key:315    " - annot_file_path :316    "                       path to the .annot file corresponding to the317    "                       source file (dealing with ocamlbuild stuff)318    " - _build_path:319    "                       path to the build directory even if this one is320    "                       not named '_build'321    " - date_of_last annot:322    "                       Set to 0 until we load the file. It contains the323    "                       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_path332        let _build_path = ''333      else334        " 2nd case : the buffer and the _build directory are in the same directory335        "      ..336        "     /  \337        "    /    \338        " _build  .ml339        "340        let _build_path = finddir('_build','.')341        if _build_path != ''342          let _build_path = getcwd().'/'._build_path343          let annot_file_path           = findfile(annot_file_name,'_build')344          if annot_file_path != ''345            let annot_file_path = getcwd().'/'.annot_file_path346          endif347        else348          " 3rd case : the _build directory is in a directory higher in the file hierarchy349          "            (it can't be deeper by ocamlbuild requirements)350          "      ..351          "     /  \352          "    /    \353          " _build  ...354          "           \355          "            \356          "           .ml357          "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          else364            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 cases367            if annot_file_path != ''368              " 4a. we suppose the renamed _build directory is in the current directory369              let _build_path = matchstr(annot_file_path,'^[^/]*')370              if annot_file_path != ''371                let annot_file_path = getcwd().'/'.annot_file_path372                let _build_path     = getcwd().'/'._build_path373              endif374            else375              let annot_file_name = ''376              "(Pierre Vittet: I have commented 4b because this was crashing377              "my vim (it produced infinite loop))378              "379              " 4b. anarchy : the renamed _build directory may be higher in the hierarchy380              " this will work if the file for which we are looking annotations has a unique name in the whole project381              " if this is not the case, it may still work, but no warranty here382              "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            endif386          endif387        endif388      endif389 390      if annot_file_path == ''391        throw 'E484: no annotation file found'392      endif393 394      silent exe 'cd' '-'395      let s:annot_file_list[annot_file_name]= [annot_file_path, _build_path, 0]396    endif397  endfun398 399  " This variable contains a dictionary of lists. Each element of the dictionary400  " represents an annotation system. An annotation system is a list with:401  " - annotation file name as its key402  " - annotation file path as first element of the contained list403  " - build path as second element of the contained list404  " - annot_file_last_mod (contain the date of .annot file) as third element405  let s:annot_file_list = {}406 407" 2. Finding the type information in the annotation file408 409  " a. The annotation file is opened in vim as a buffer that410  " 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:hidden418      set hidden419      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      else423        silent exe 'keepj keepalt' 'view' s:Fnameescape(a:annot_file_path)424      endif425      call setpos(".", [0, 0 , 0 , 0])426    endfun427 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_hidden433      call setpos('.',s:current_pos)434    endfun435 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 a439      "   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 = 0444      if exists("annot[2]")445        let annot_file_last_mod = annot[2]446      endif447      if bufloaded(annot_file_path) && annot_file_last_mod < getftime(annot_file_path)448        " if there is a more recent file449        let nr = bufnr(annot_file_path)450        silent exe 'keepj keepalt' 'bunload' nr451      endif452      if !bufloaded(annot_file_path)453        call s:Enter_annotation_buffer(annot_file_path)454        setlocal nobuflisted455        setlocal bufhidden=hide456        setlocal noswapfile457        setlocal buftype=nowrite458        call s:Exit_annotation_buffer()459        let annot[2] = getftime(annot_file_path)460        " List updated with the new date461        let s:annot_file_list[a:annot_file_name] = annot462      endif463    endfun464 465  "b. 'search' and 'match' work to find the type information466 467      "In:  - lin1,col1: position of expression first char468      "     - lin2,col2: position of expression last char469      "Out: - the pattern to be looked for to find the block470      " Must be called in the source buffer (use of line2byte)471    function! s:Block_pattern(lin1,lin2,col1,col2)472      let start_num1 = a:lin1473      let start_num2 = line2byte(a:lin1) - 1474      let start_num3 = start_num2 + a:col1475      let path       = '"\(\\"\|[^"]\)\+"'476      let start_pos  = path.' '.start_num1.' '.start_num2.' '.start_num3477      let end_num1   = a:lin2478      let end_num2   = line2byte(a:lin2) - 1479      let end_num3   = end_num2 + a:col2480      let end_pos    = path.' '.end_num1.' '.end_num2.' '.end_num3481      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    endfun486 487      "In: (the cursor position should be at the start of an annotation)488      "Out: the type information489      " 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 necessary492      keepj while search('^type($','ce',line(".")) == 0493        keepj if search('^.\{-}($','e') == 0494          throw "no_annotation"495        endif496        keepj if searchpair('(','',')') == 0497          throw "malformed_annot_file"498        endif499      endwhile500      let begin = line(".") + 1501      keepj if searchpair('(','',')') == 0502        throw "malformed_annot_file"503      endif504      let end = line(".") - 1505      return join(getline(begin,end),"\n")506    endfun507 508      "In:  the pattern to look for in order to match the block509      "Out: the type information (calls s:Match_data)510      " Should be called in the annotation buffer511    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      try515        if search(a:block_pattern,'e') == 0516          throw "no_annotation"517        endif518        call cursor(line(".") + 1,1)519        let annotation = s:Match_data()520      finally521        call s:Exit_annotation_buffer()522      endtry523      return annotation524    endfun525 526  "c. link this stuff with what the user wants527  " ie. get the expression selected/under the cursor528 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 type533    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      else545        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        else561          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 == 0564            throw "no_expression"565          endif566          return [cursor_line,cursor_line,col1-1,col2]567        endif568      endif569    endfun570 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    endfun577 578      "In: A string destined to be printed in the 'echo buffer'. It has line579      "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 space585      let res =substitute(res , "  ", " ", "g")586      "remove space at beginning of string.587      let res = substitute(res, "^ *", "", "g")588      return res589    endfunction590 591  "d. main592      "In:         the current mode (eg. "visual", "normal", etc.)593      "After call: the type information is displayed594    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 buffer601        let @" = s:unformat_ocaml_type(res)602        return res603      endfun604    endif605 606    if !exists("*Ocaml_get_type_or_not")607      function Ocaml_get_type_or_not(mode)608        let t=reltime()609        try610          let res = Ocaml_get_type(a:mode)611          return res612        catch613          return ""614        endtry615      endfun616    endif617 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 None622          return623        endif624        try625          echo Ocaml_get_type(a:mode)626        catch /E484:/627          echohl ErrorMsg | echo "No type annotations (.annot) file found" | echohl None628        catch /no_expression/629          echohl ErrorMsg | echo "No expression found under the cursor" | echohl None630        catch /no_annotation/631          echohl ErrorMsg | echo "No type annotation found for the given text" | echohl None632        catch /malformed_annot_file/633          echohl ErrorMsg | echo "Malformed .annot file" | echohl None634        endtry635      endfun636    endif637 638" Maps639  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 642let &cpoptions = s:cposet643unlet s:cposet644 645" vim:sw=2 fdm=indent646 
codekingpro/portable-devtools · Team Ai