ocaml.vim 23 KB

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