ocaml.vim 23 KB

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