/vim/haskellmode-20090430.vba

https://bitbucket.org/vertespain/config · Visual Basic · 2035 lines · 1829 code · 205 blank · 1 comment · 339 complexity · 43f70636eecf1e84667405319ffe8e58 MD5 · raw file

Large files are truncated click here to view the full file

  1. " Vimball Archiver by Charles E. Campbell, Jr., Ph.D.
  2. UseVimball
  3. finish
  4. compiler/ghc.vim [[[1
  5. 479
  6. " Vim Compiler File
  7. " Compiler: GHC
  8. " Maintainer: Claus Reinke <claus.reinke@talk21.com>
  9. " Last Change: 30/04/2009
  10. "
  11. " part of haskell plugins: http://projects.haskell.org/haskellmode-vim
  12. " ------------------------------ paths & quickfix settings first
  13. "
  14. if exists("current_compiler") && current_compiler == "ghc"
  15. finish
  16. endif
  17. let current_compiler = "ghc"
  18. let s:scriptname = "ghc.vim"
  19. if (!exists("g:ghc") || !executable(g:ghc))
  20. if !executable('ghc')
  21. echoerr s:scriptname.": can't find ghc. please set g:ghc, or extend $PATH"
  22. finish
  23. else
  24. let g:ghc = 'ghc'
  25. endif
  26. endif
  27. let ghc_version = substitute(system(g:ghc . ' --numeric-version'),'\n','','')
  28. if (!exists("b:ghc_staticoptions"))
  29. let b:ghc_staticoptions = ''
  30. endif
  31. " set makeprg (for quickfix mode)
  32. execute 'setlocal makeprg=' . g:ghc . '\ ' . escape(b:ghc_staticoptions,' ') .'\ -e\ :q\ %'
  33. "execute 'setlocal makeprg=' . g:ghc .'\ -e\ :q\ %'
  34. "execute 'setlocal makeprg=' . g:ghc .'\ --make\ %'
  35. " quickfix mode:
  36. " fetch file/line-info from error message
  37. " TODO: how to distinguish multiline errors from warnings?
  38. " (both have the same header, and errors have no common id-tag)
  39. " how to get rid of first empty message in result list?
  40. setlocal errorformat=
  41. \%-Z\ %#,
  42. \%W%f:%l:%c:\ Warning:\ %m,
  43. \%E%f:%l:%c:\ %m,
  44. \%E%>%f:%l:%c:,
  45. \%+C\ \ %#%m,
  46. \%W%>%f:%l:%c:,
  47. \%+C\ \ %#%tarning:\ %m,
  48. " oh, wouldn't you guess it - ghc reports (partially) to stderr..
  49. setlocal shellpipe=2>
  50. " ------------------------- but ghc can do a lot more for us..
  51. "
  52. " allow map leader override
  53. if !exists("maplocalleader")
  54. let maplocalleader='_'
  55. endif
  56. " initialize map of identifiers to their types
  57. " associate type map updates to changedtick
  58. if !exists("b:ghc_types")
  59. let b:ghc_types = {}
  60. let b:my_changedtick = b:changedtick
  61. endif
  62. if exists("g:haskell_functions")
  63. finish
  64. endif
  65. let g:haskell_functions = "ghc"
  66. " avoid hit-enter prompts
  67. set cmdheight=3
  68. " edit static GHC options
  69. " TODO: add completion for options/packages?
  70. command! GHCStaticOptions call GHC_StaticOptions()
  71. function! GHC_StaticOptions()
  72. let b:ghc_staticoptions = input('GHC static options: ',b:ghc_staticoptions)
  73. execute 'setlocal makeprg=' . g:ghc . '\ ' . escape(b:ghc_staticoptions,' ') .'\ -e\ :q\ %'
  74. let b:my_changedtick -=1
  75. endfunction
  76. map <LocalLeader>T :call GHC_ShowType(1)<cr>
  77. map <LocalLeader>t :call GHC_ShowType(0)<cr>
  78. function! GHC_ShowType(addTypeDecl)
  79. let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),0)
  80. if namsym==[]
  81. redraw
  82. echo 'no name/symbol under cursor!'
  83. return 0
  84. endif
  85. let [_,symb,qual,unqual] = namsym
  86. let name = qual=='' ? unqual : qual.'.'.unqual
  87. let pname = ( symb ? '('.name.')' : name )
  88. call GHC_HaveTypes()
  89. if !has_key(b:ghc_types,name)
  90. redraw
  91. echo pname "type not known"
  92. else
  93. redraw
  94. for type in split(b:ghc_types[name],' -- ')
  95. echo pname "::" type
  96. if a:addTypeDecl
  97. call append( line(".")-1, pname . " :: " . type )
  98. endif
  99. endfor
  100. endif
  101. endfunction
  102. " show type of identifier under mouse pointer in balloon
  103. if has("balloon_eval")
  104. set ballooneval
  105. set balloondelay=600
  106. set balloonexpr=GHC_TypeBalloon()
  107. function! GHC_TypeBalloon()
  108. if exists("b:current_compiler") && b:current_compiler=="ghc"
  109. let [line] = getbufline(v:beval_bufnr,v:beval_lnum)
  110. let namsym = haskellmode#GetNameSymbol(line,v:beval_col,0)
  111. if namsym==[]
  112. return ''
  113. endif
  114. let [start,symb,qual,unqual] = namsym
  115. let name = qual=='' ? unqual : qual.'.'.unqual
  116. let pname = name " ( symb ? '('.name.')' : name )
  117. silent call GHC_HaveTypes()
  118. if has("balloon_multiline")
  119. return (has_key(b:ghc_types,pname) ? split(b:ghc_types[pname],' -- ') : '')
  120. else
  121. return (has_key(b:ghc_types,pname) ? b:ghc_types[pname] : '')
  122. endif
  123. else
  124. return ''
  125. endif
  126. endfunction
  127. endif
  128. map <LocalLeader>si :call GHC_ShowInfo()<cr>
  129. function! GHC_ShowInfo()
  130. let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),0)
  131. if namsym==[]
  132. redraw
  133. echo 'no name/symbol under cursor!'
  134. return 0
  135. endif
  136. let [_,symb,qual,unqual] = namsym
  137. let name = qual=='' ? unqual : (qual.'.'.unqual)
  138. let output = GHC_Info(name)
  139. pclose | new
  140. setlocal previewwindow
  141. setlocal buftype=nofile
  142. setlocal noswapfile
  143. put =output
  144. wincmd w
  145. "redraw
  146. "echo output
  147. endfunction
  148. " fill the type map, unless nothing has changed since the last attempt
  149. function! GHC_HaveTypes()
  150. if b:ghc_types == {} && (b:my_changedtick != b:changedtick)
  151. let b:my_changedtick = b:changedtick
  152. return GHC_BrowseAll()
  153. endif
  154. endfunction
  155. " update b:ghc_types after successful make
  156. au QuickFixCmdPost make if GHC_CountErrors()==0 | silent call GHC_BrowseAll() | endif
  157. " count only error entries in quickfix list, ignoring warnings
  158. function! GHC_CountErrors()
  159. let c=0
  160. for e in getqflist() | if e.type=='E' && e.text !~ "^[ \n]*Warning:" | let c+=1 | endif | endfor
  161. return c
  162. endfunction
  163. command! GHCReload call GHC_BrowseAll()
  164. function! GHC_BrowseAll()
  165. " let imports = haskellmode#GatherImports()
  166. " let modules = keys(imports[0]) + keys(imports[1])
  167. let imports = {} " no need for them at the moment
  168. let current = GHC_NameCurrent()
  169. let module = current==[] ? 'Main' : current[0]
  170. if GHC_VersionGE([6,8,1])
  171. return GHC_BrowseBangStar(module)
  172. else
  173. return GHC_BrowseMultiple(imports,['*'.module])
  174. endif
  175. endfunction
  176. function! GHC_VersionGE(target)
  177. let current = split(g:ghc_version, '\.' )
  178. let target = a:target
  179. for i in current
  180. if ((target==[]) || (i>target[0]))
  181. return 1
  182. elseif (i==target[0])
  183. let target = target[1:]
  184. else
  185. return 0
  186. endif
  187. endfor
  188. return 1
  189. endfunction
  190. function! GHC_NameCurrent()
  191. let last = line("$")
  192. let l = 1
  193. while l<last
  194. let ml = matchlist( getline(l), '^module\s*\([^ (]*\)')
  195. if ml != []
  196. let [_,module;x] = ml
  197. return [module]
  198. endif
  199. let l += 1
  200. endwhile
  201. redraw
  202. echo "cannot find module header for file " . expand("%")
  203. return []
  204. endfunction
  205. function! GHC_BrowseBangStar(module)
  206. redraw
  207. echo "browsing module " a:module
  208. let command = ":browse! *" . a:module
  209. let orig_shellredir = &shellredir
  210. let &shellredir = ">" " ignore error/warning messages, only output or lack of it
  211. let output = system(g:ghc . ' ' . b:ghc_staticoptions . ' -v0 --interactive ' . expand("%") , command )
  212. let &shellredir = orig_shellredir
  213. return GHC_ProcessBang(a:module,output)
  214. endfunction
  215. function! GHC_BrowseMultiple(imports,modules)
  216. redraw
  217. echo "browsing modules " a:modules
  218. let command = ":browse " . join( a:modules, " \n :browse ")
  219. let command = substitute(command,'\(:browse \(\S*\)\)','putStrLn "-- \2" \n \1','g')
  220. let output = system(g:ghc . ' ' . b:ghc_staticoptions . ' -v0 --interactive ' . expand("%") , command )
  221. return GHC_Process(a:imports,output)
  222. endfunction
  223. function! GHC_Info(what)
  224. " call GHC_HaveTypes()
  225. let output = system(g:ghc . ' ' . b:ghc_staticoptions . ' -v0 --interactive ' . expand("%"), ":i ". a:what)
  226. return output
  227. endfunction
  228. function! GHC_ProcessBang(module,output)
  229. let module = a:module
  230. let b = a:output
  231. let linePat = '^\(.\{-}\)\n\(.*\)'
  232. let contPat = '\s\+\(.\{-}\)\n\(.*\)'
  233. let typePat = '^\(\)\(\S*\)\s*::\(.*\)'
  234. let commentPat = '^-- \(\S*\)'
  235. let definedPat = '^-- defined locally'
  236. let importedPat = '^-- imported via \(.*\)'
  237. if !(b=~commentPat)
  238. echo s:scriptname.": GHCi reports errors (try :make?)"
  239. return 0
  240. endif
  241. let b:ghc_types = {}
  242. let ml = matchlist( b , linePat )
  243. while ml != []
  244. let [_,l,rest;x] = ml
  245. let mlDecl = matchlist( l, typePat )
  246. if mlDecl != []
  247. let [_,indent,id,type;x] = mlDecl
  248. let ml2 = matchlist( rest , '^'.indent.contPat )
  249. while ml2 != []
  250. let [_,c,rest;x] = ml2
  251. let type .= c
  252. let ml2 = matchlist( rest , '^'.indent.contPat )
  253. endwhile
  254. let id = substitute( id, '^(\(.*\))$', '\1', '')
  255. let type = substitute( type, '\s\+', " ", "g" )
  256. " using :browse! *<current>, we get both unqualified and qualified ids
  257. let qualified = (id =~ '\.') && (id =~ '[A-Z]')
  258. let b:ghc_types[id] = type
  259. if !qualified
  260. for qual in qualifiers
  261. let b:ghc_types[qual.'.'.id] = type
  262. endfor
  263. endif
  264. else
  265. let mlImported = matchlist( l, importedPat )
  266. let mlDefined = matchlist( l, definedPat )
  267. if mlImported != []
  268. let [_,modules;x] = mlImported
  269. let qualifiers = split( modules, ', ' )
  270. elseif mlDefined != []
  271. let qualifiers = [module]
  272. endif
  273. endif
  274. let ml = matchlist( rest , linePat )
  275. endwhile
  276. return 1
  277. endfunction
  278. function! GHC_Process(imports,output)
  279. let b = a:output
  280. let imports = a:imports
  281. let linePat = '^\(.\{-}\)\n\(.*\)'
  282. let contPat = '\s\+\(.\{-}\)\n\(.*\)'
  283. let typePat = '^\(\s*\)\(\S*\)\s*::\(.*\)'
  284. let modPat = '^-- \(\S*\)'
  285. " add '-- defined locally' and '-- imported via ..'
  286. if !(b=~modPat)
  287. echo s:scriptname.": GHCi reports errors (try :make?)"
  288. return 0
  289. endif
  290. let b:ghc_types = {}
  291. let ml = matchlist( b , linePat )
  292. while ml != []
  293. let [_,l,rest;x] = ml
  294. let mlDecl = matchlist( l, typePat )
  295. if mlDecl != []
  296. let [_,indent,id,type;x] = mlDecl
  297. let ml2 = matchlist( rest , '^'.indent.contPat )
  298. while ml2 != []
  299. let [_,c,rest;x] = ml2
  300. let type .= c
  301. let ml2 = matchlist( rest , '^'.indent.contPat )
  302. endwhile
  303. let id = substitute(id, '^(\(.*\))$', '\1', '')
  304. let type = substitute( type, '\s\+', " ", "g" )
  305. " using :browse *<current>, we get both unqualified and qualified ids
  306. if current_module " || has_key(imports[0],module)
  307. if has_key(b:ghc_types,id) && !(matchstr(b:ghc_types[id],escape(type,'[].'))==type)
  308. let b:ghc_types[id] .= ' -- '.type
  309. else
  310. let b:ghc_types[id] = type
  311. endif
  312. endif
  313. if 0 " has_key(imports[1],module)
  314. let qualid = module.'.'.id
  315. let b:ghc_types[qualid] = type
  316. endif
  317. else
  318. let mlMod = matchlist( l, modPat )
  319. if mlMod != []
  320. let [_,module;x] = mlMod
  321. let current_module = module[0]=='*'
  322. let module = current_module ? module[1:] : module
  323. endif
  324. endif
  325. let ml = matchlist( rest , linePat )
  326. endwhile
  327. return 1
  328. endfunction
  329. let s:ghc_templates = ["module _ () where","class _ where","class _ => _ where","instance _ where","instance _ => _ where","type family _","type instance _ = ","data _ = ","newtype _ = ","type _ = "]
  330. " use ghci :browse index for insert mode omnicompletion (CTRL-X CTRL-O)
  331. function! GHC_CompleteImports(findstart, base)
  332. if a:findstart
  333. let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),-1) " insert-mode: we're 1 beyond the text
  334. if namsym==[]
  335. redraw
  336. echo 'no name/symbol under cursor!'
  337. return -1
  338. endif
  339. let [start,symb,qual,unqual] = namsym
  340. return (start-1)
  341. else " find keys matching with "a:base"
  342. let res = []
  343. let l = len(a:base)-1
  344. call GHC_HaveTypes()
  345. for key in keys(b:ghc_types)
  346. if key[0 : l]==a:base
  347. let res += [{"word":key,"menu":":: ".b:ghc_types[key],"dup":1}]
  348. endif
  349. endfor
  350. return res
  351. endif
  352. endfunction
  353. set omnifunc=GHC_CompleteImports
  354. "
  355. " Vim's default completeopt is menu,preview
  356. " you probably want at least menu, or you won't see alternatives listed
  357. " setlocal completeopt+=menu
  358. " menuone is useful, but other haskellmode menus will try to follow your choice here in future
  359. " setlocal completeopt+=menuone
  360. " longest sounds useful, but doesn't seem to do what it says, and interferes with CTRL-E
  361. " setlocal completeopt-=longest
  362. map <LocalLeader>ct :call GHC_CreateTagfile()<cr>
  363. function! GHC_CreateTagfile()
  364. redraw
  365. echo "creating tags file"
  366. let output = system(g:ghc . ' ' . b:ghc_staticoptions . ' -e ":ctags" ' . expand("%"))
  367. " for ghcs older than 6.6, you would need to call another program
  368. " here, such as hasktags
  369. echo output
  370. endfunction
  371. command! -nargs=1 GHCi redraw | echo system(g:ghc. ' ' . b:ghc_staticoptions .' '.expand("%").' -e "'.escape(<f-args>,'"').'"')
  372. " use :make 'not in scope' errors to explicitly list imported ids
  373. " cursor needs to be on import line, in correctly loadable module
  374. map <LocalLeader>ie :call GHC_MkImportsExplicit()<cr>
  375. function! GHC_MkImportsExplicit()
  376. let save_cursor = getpos(".")
  377. let line = getline('.')
  378. let lineno = line('.')
  379. let ml = matchlist(line,'^import\(\s*qualified\)\?\s*\([^( ]\+\)')
  380. if ml!=[]
  381. let [_,q,mod;x] = ml
  382. silent make
  383. if getqflist()==[]
  384. if line=~"import[^(]*Prelude"
  385. call setline(lineno,substitute(line,"(.*","","").'()')
  386. else
  387. call setline(lineno,'-- '.line)
  388. endif
  389. silent write
  390. silent make
  391. let qflist = getqflist()
  392. call setline(lineno,line)
  393. silent write
  394. let ids = {}
  395. for d in qflist
  396. let ml = matchlist(d.text,'Not in scope: \([^`]*\)`\([^'']*\)''')
  397. if ml!=[]
  398. let [_,what,qid;x] = ml
  399. let id = ( qid =~ "^[A-Z]" ? substitute(qid,'.*\.\([^.]*\)$','\1','') : qid )
  400. let pid = ( id =~ "[a-zA-Z0-9_']\\+" ? id : '('.id.')' )
  401. if what =~ "data"
  402. call GHC_HaveTypes()
  403. if has_key(b:ghc_types,id)
  404. let pid = substitute(b:ghc_types[id],'^.*->\s*\(\S*\).*$','\1','').'('.pid.')'
  405. else
  406. let pid = '???('.pid.')'
  407. endif
  408. endif
  409. let ids[pid] = 1
  410. endif
  411. endfor
  412. call setline(lineno,'import'.q.' '.mod.'('.join(keys(ids),',').')')
  413. else
  414. copen
  415. endif
  416. endif
  417. call setpos('.', save_cursor)
  418. endfunction
  419. if GHC_VersionGE([6,8,2])
  420. let opts = filter(split(substitute(system(g:ghc . ' -v0 --interactive', ':set'), ' ', '','g'), '\n'), 'v:val =~ "-f"')
  421. else
  422. let opts = ["-fglasgow-exts","-fallow-undecidable-instances","-fallow-overlapping-instances","-fno-monomorphism-restriction","-fno-mono-pat-binds","-fno-cse","-fbang-patterns","-funbox-strict-fields"]
  423. endif
  424. amenu ]OPTIONS_GHC.- :echo '-'<cr>
  425. aunmenu ]OPTIONS_GHC
  426. for o in opts
  427. exe 'amenu ]OPTIONS_GHC.'.o.' :call append(0,"{-# OPTIONS_GHC '.o.' #-}")<cr>'
  428. endfor
  429. if has("gui_running")
  430. map <LocalLeader>opt :popup ]OPTIONS_GHC<cr>
  431. else
  432. map <LocalLeader>opt :emenu ]OPTIONS_GHC.
  433. endif
  434. amenu ]LANGUAGES_GHC.- :echo '-'<cr>
  435. aunmenu ]LANGUAGES_GHC
  436. if GHC_VersionGE([6,8])
  437. let ghc_supported_languages = split(system(g:ghc . ' --supported-languages'),'\n')
  438. for l in ghc_supported_languages
  439. exe 'amenu ]LANGUAGES_GHC.'.l.' :call append(0,"{-# LANGUAGE '.l.' #-}")<cr>'
  440. endfor
  441. if has("gui_running")
  442. map <LocalLeader>lang :popup ]LANGUAGES_GHC<cr>
  443. else
  444. map <LocalLeader>lang :emenu ]LANGUAGES_GHC.
  445. endif
  446. endif
  447. ftplugin/haskell.vim [[[1
  448. 14
  449. "
  450. " general Haskell source settings
  451. " (shared functions are in autoload/haskellmode.vim)
  452. "
  453. " (Claus Reinke, last modified: 28/04/2009)
  454. "
  455. " part of haskell plugins: http://projects.haskell.org/haskellmode-vim
  456. " please send patches to <claus.reinke@talk21.com>
  457. " try gf on import line, or ctrl-x ctrl-i, or [I, [i, ..
  458. setlocal include=^import\\s*\\(qualified\\)\\?\\s*
  459. setlocal includeexpr=substitute(v:fname,'\\.','/','g').'.'
  460. setlocal suffixesadd=hs,lhs,hsc
  461. ftplugin/haskell_doc.vim [[[1
  462. 837
  463. "
  464. " use haddock docs and index files
  465. " show documentation, complete & qualify identifiers
  466. "
  467. " (Claus Reinke; last modified: 30/04/2009)
  468. "
  469. " part of haskell plugins: http://projects.haskell.org/haskellmode-vim
  470. " please send patches to <claus.reinke@talk21.com>
  471. " :Doc <name> and :IDoc <name> open haddocks for <name> in opera
  472. "
  473. " :Doc needs qualified name (default Prelude) and package (default base)
  474. " :IDoc needs unqualified name, looks up possible links in g:haddock_index
  475. "
  476. " :DocIndex populates g:haddock_index from haddock's index files
  477. " :ExportDocIndex saves g:haddock_index to cache file
  478. " :ImportDocIndex reloads g:haddock_index from cache file
  479. "
  480. " all the following use the haddock index (g:haddock_index)
  481. "
  482. " _? opens haddocks for unqualified name under cursor,
  483. " suggesting alternative full qualifications in popup menu
  484. "
  485. " _. fully qualifies unqualified name under cursor,
  486. " suggesting alternative full qualifications in popup menu
  487. "
  488. " _i add import <module>(<name>) statement for unqualified <name> under cursor,
  489. " _im add import <module> statement for unqualified <name> under cursor,
  490. " suggesting alternative full qualifications in popup menu
  491. " (this currently adds one statement per call, instead of
  492. " merging into existing import statements, but it's a start;-)
  493. "
  494. " CTRL-X CTRL-U (user-defined insert mode completion)
  495. " suggests completions of unqualified names in popup menu
  496. let s:scriptname = "haskell_doc.vim"
  497. " script parameters
  498. " g:haddock_browser *mandatory* which browser to call
  499. " g:haddock_browser_callformat [optional] how to call browser
  500. " g:haddock_indexfiledir [optional] where to put 'haddock_index.vim'
  501. " g:haddock_docdir [optional] where to find html docs
  502. " g:ghc [optional] which ghc to call
  503. " g:ghc_pkg [optional] which ghc_pkg to call
  504. " been here before?
  505. if exists("g:haddock_index")
  506. finish
  507. endif
  508. " initialise nested dictionary, to be populated
  509. " - from haddock index files via :DocIndex
  510. " - from previous cached version via :ImportDocIndex
  511. let g:haddock_index = {}
  512. " initialise dictionary, mapping modules with haddocks to their packages,
  513. " populated via MkHaddockModuleIndex() or HaveModuleIndex()
  514. let g:haddock_moduleindex = {}
  515. " program to open urls, please set this in your vimrc
  516. "examples (for windows):
  517. "let g:haddock_browser = "C:/Program Files/Opera/Opera.exe"
  518. "let g:haddock_browser = "C:/Program Files/Mozilla Firefox/firefox.exe"
  519. "let g:haddock_browser = "C:/Program Files/Internet Explorer/IEXPLORE.exe"
  520. if !exists("g:haddock_browser")
  521. echoerr s:scriptname." WARNING: please set g:haddock_browser!"
  522. endif
  523. if (!exists("g:ghc") || !executable(g:ghc))
  524. if !executable('ghc')
  525. echoerr s:scriptname." can't find ghc. please set g:ghc, or extend $PATH"
  526. finish
  527. else
  528. let g:ghc = 'ghc'
  529. endif
  530. endif
  531. if (!exists("g:ghc_pkg") || !executable(g:ghc_pkg))
  532. let g:ghc_pkg = substitute(g:ghc,'\(.*\)ghc','\1ghc-pkg','')
  533. endif
  534. if exists("g:haddock_docdir") && isdirectory(g:haddock_docdir)
  535. let s:docdir = g:haddock_docdir
  536. elseif executable(g:ghc_pkg)
  537. " try to figure out location of html docs
  538. " first choice: where the base docs are (from the first base listed)
  539. let [field;x] = split(system(g:ghc_pkg . ' field base haddock-html'),'\n')
  540. let field = substitute(field,'haddock-html: \(.*\)libraries.base','\1','')
  541. let field = substitute(field,'\\','/','g')
  542. let alternate = substitute(field,'html','doc/html','')
  543. if isdirectory(field)
  544. let s:docdir = field
  545. elseif isdirectory(alternate)
  546. let s:docdir = alternate
  547. endif
  548. else
  549. echoerr s:scriptname." can't find ghc-pkg (set g:ghc_pkg ?)."
  550. endif
  551. " second choice: try some known suspects for windows/unix
  552. if !exists('s:docdir') || !isdirectory(s:docdir)
  553. let s:ghc_libdir = substitute(system(g:ghc . ' --print-libdir'),'\n','','')
  554. let location1a = s:ghc_libdir . '/doc/html/'
  555. let location1b = s:ghc_libdir . '/doc/'
  556. let s:ghc_version = substitute(system(g:ghc . ' --numeric-version'),'\n','','')
  557. let location2 = '/usr/share/doc/ghc-' . s:ghc_version . '/html/'
  558. if isdirectory(location1a)
  559. let s:docdir = location1a
  560. elseif isdirectory(location1b)
  561. let s:docdir = location1b
  562. elseif isdirectory(location2)
  563. let s:docdir = location2
  564. else " give up
  565. echoerr s:scriptname." can't find locaton of html documentation (set g:haddock_docdir)."
  566. finish
  567. endif
  568. endif
  569. " todo: can we turn s:docdir into a list of paths, and
  570. " include docs for third-party libs as well?
  571. let s:libraries = s:docdir . 'libraries/'
  572. let s:guide = s:docdir . 'users_guide/'
  573. let s:index = 'index.html'
  574. if exists("g:haddock_indexfiledir") && filewritable(g:haddock_indexfiledir)
  575. let s:haddock_indexfiledir = g:haddock_indexfiledir
  576. elseif filewritable(s:libraries)
  577. let s:haddock_indexfiledir = s:libraries
  578. elseif filewritable($HOME)
  579. let s:haddock_indexfiledir = $HOME.'/'
  580. else "give up
  581. echoerr s:scriptname." can't locate index file. please set g:haddock_indexfiledir"
  582. finish
  583. endif
  584. let s:haddock_indexfile = s:haddock_indexfiledir . 'haddock_index.vim'
  585. " different browser setups require different call formats;
  586. " you might want to call the browser synchronously or
  587. " asynchronously, and the latter is os-dependent;
  588. "
  589. " by default, the browser is started in the background when on
  590. " windows or if running in a gui, and in the foreground otherwise
  591. " (eg, console-mode for remote sessions, with text-mode browsers).
  592. "
  593. " you can override these defaults in your vimrc, via a format
  594. " string including 2 %s parameters (the first being the browser
  595. " to call, the second being the url).
  596. if !exists("g:haddock_browser_callformat")
  597. if has("win32") || has("win64")
  598. let g:haddock_browser_callformat = 'start %s "%s"'
  599. else
  600. if has("gui_running")
  601. let g:haddock_browser_callformat = '%s %s '.printf(&shellredir,'/dev/null').' &'
  602. else
  603. let g:haddock_browser_callformat = '%s %s'
  604. endif
  605. endif
  606. endif
  607. " allow map leader override
  608. if !exists("maplocalleader")
  609. let maplocalleader='_'
  610. endif
  611. command! DocSettings call DocSettings()
  612. function! DocSettings()
  613. for v in ["g:haddock_browser","g:haddock_browser_callformat","g:haddock_docdir","g:haddock_indexfiledir","s:ghc_libdir","s:ghc_version","s:docdir","s:libraries","s:guide","s:haddock_indexfile"]
  614. if exists(v)
  615. echo v '=' eval(v)
  616. else
  617. echo v '='
  618. endif
  619. endfor
  620. endfunction
  621. function! DocBrowser(url)
  622. "echomsg "DocBrowser(".url.")"
  623. if (!exists("g:haddock_browser") || !executable(g:haddock_browser))
  624. echoerr s:scriptname." can't find documentation browser. please set g:haddock_browser"
  625. return
  626. endif
  627. " start browser to open url, according to specified format
  628. let url = a:url=~'^\(file://\|http://\)' ? a:url : 'file://'.a:url
  629. silent exe '!'.printf(g:haddock_browser_callformat,g:haddock_browser,escape(url,'#%'))
  630. endfunction
  631. "Doc/Doct are an old interface for documentation lookup
  632. "(that is the reason they are not documented!-)
  633. "
  634. "These uses are still fine at the moment, and are the reason
  635. "that this command still exists at all
  636. "
  637. " :Doc -top
  638. " :Doc -libs
  639. " :Doc -guide
  640. "
  641. "These uses may or may not work, and shouldn't be relied on anymore
  642. "(usually, you want _?/_?1/_?2 or :MDoc; there is also :IDoc)
  643. "
  644. " :Doc length
  645. " :Doc Control.Monad.when
  646. " :Doc Data.List.
  647. " :Doc Control.Monad.State.runState mtl
  648. command! -nargs=+ Doc call Doc('v',<f-args>)
  649. command! -nargs=+ Doct call Doc('t',<f-args>)
  650. function! Doc(kind,qualname,...)
  651. let suffix = '.html'
  652. let relative = '#'.a:kind.'%3A'
  653. if a:qualname=="-top"
  654. call DocBrowser(s:docdir . s:index)
  655. return
  656. elseif a:qualname=="-libs"
  657. call DocBrowser(s:libraries . s:index)
  658. return
  659. elseif a:qualname=="-guide"
  660. call DocBrowser(s:guide . s:index)
  661. return
  662. endif
  663. if a:0==0 " no package specified
  664. let package = 'base/'
  665. else
  666. let package = a:1 . '/'
  667. endif
  668. if match(a:qualname,'\.')==-1 " unqualified name
  669. let [qual,name] = [['Prelude'],a:qualname]
  670. let file = join(qual,'-') . suffix . relative . name
  671. elseif a:qualname[-1:]=='.' " module qualifier only
  672. let parts = split(a:qualname,'\.')
  673. let quallen = len(parts)-1
  674. let [qual,name] = [parts[0:quallen],parts[-1]]
  675. let file = join(qual,'-') . suffix
  676. else " qualified name
  677. let parts = split(a:qualname,'\.')
  678. let quallen = len(parts)-2
  679. let [qual,name] = [parts[0:quallen],parts[-1]]
  680. let file = join(qual,'-') . suffix . relative . name
  681. endif
  682. let path = s:libraries . package . file
  683. call DocBrowser(path)
  684. endfunction
  685. " TODO: add commandline completion for :IDoc
  686. " switch to :emenu instead of inputlist?
  687. " indexed variant of Doc, looking up links in g:haddock_index
  688. " usage:
  689. " 1. :IDoc length
  690. " 2. click on one of the choices, or select by number (starting from 0)
  691. command! -nargs=+ IDoc call IDoc(<f-args>)
  692. function! IDoc(name,...)
  693. let choices = HaddockIndexLookup(a:name)
  694. if choices=={} | return | endif
  695. if a:0==0
  696. let keylist = map(deepcopy(keys(choices)),'substitute(v:val,"\\[.\\]","","")')
  697. let choice = inputlist(keylist)
  698. else
  699. let choice = a:1
  700. endif
  701. let path = values(choices)[choice] " assumes same order for keys/values..
  702. call DocBrowser(path)
  703. endfunction
  704. let s:flagref = s:guide . 'flag-reference.html'
  705. if filereadable(s:flagref)
  706. " extract the generated fragment ids for the
  707. " flag reference sections
  708. let s:headerPat = '.\{-}<h3 class="title"><a name="\([^"]*\)"><\/a>\([^<]*\)<\/h3>\(.*\)'
  709. let s:flagheaders = []
  710. let s:flagheaderids = {}
  711. let s:contents = join(readfile(s:flagref))
  712. let s:ml = matchlist(s:contents,s:headerPat)
  713. while s:ml!=[]
  714. let [_,s:id,s:title,s:r;s:x] = s:ml
  715. let s:flagheaders = add(s:flagheaders, s:title)
  716. let s:flagheaderids[s:title] = s:id
  717. let s:ml = matchlist(s:r,s:headerPat)
  718. endwhile
  719. command! -nargs=1 -complete=customlist,CompleteFlagHeaders FlagReference call FlagReference(<f-args>)
  720. function! FlagReference(section)
  721. let relativeUrl = a:section==""||!exists("s:flagheaderids['".a:section."']") ?
  722. \ "" : "#".s:flagheaderids[a:section]
  723. call DocBrowser(s:flagref.relativeUrl)
  724. endfunction
  725. function! CompleteFlagHeaders(al,cl,cp)
  726. let s:choices = s:flagheaders
  727. return CompleteAux(a:al,a:cl,a:cp)
  728. endfunction
  729. endif
  730. command! -nargs=1 -complete=customlist,CompleteHaddockModules MDoc call MDoc(<f-args>)
  731. function! MDoc(module)
  732. let suffix = '.html'
  733. call HaveModuleIndex()
  734. if !has_key(g:haddock_moduleindex,a:module)
  735. echoerr a:module 'not found in haddock module index'
  736. return
  737. endif
  738. let package = g:haddock_moduleindex[a:module]['package']
  739. let file = substitute(a:module,'\.','-','g') . suffix
  740. " let path = s:libraries . package . '/' . file
  741. let path = g:haddock_moduleindex[a:module]['html']
  742. call DocBrowser(path)
  743. endfunction
  744. function! CompleteHaddockModules(al,cl,cp)
  745. call HaveModuleIndex()
  746. let s:choices = keys(g:haddock_moduleindex)
  747. return CompleteAux(a:al,a:cl,a:cp)
  748. endfunction
  749. " create a dictionary g:haddock_index, containing the haddoc index
  750. command! DocIndex call DocIndex()
  751. function! DocIndex()
  752. let files = split(globpath(s:libraries,'doc-index*.html'),'\n')
  753. let g:haddock_index = {}
  754. call ProcessHaddockIndexes2(s:libraries,files)
  755. if GHC_VersionGE([6,8,2])
  756. if &shell =~ 'sh' " unix-type shell
  757. let s:addon_libraries = split(system(g:ghc_pkg . ' field \* haddock-html'),'\n')
  758. else " windows cmd.exe and the like
  759. let s:addon_libraries = split(system(g:ghc_pkg . ' field * haddock-html'),'\n')
  760. endif
  761. for addon in s:addon_libraries
  762. let ml = matchlist(addon,'haddock-html: \("\)\?\(file:///\)\?\([^"]*\)\("\)\?')
  763. if ml!=[]
  764. let [_,quote,file,addon_path;x] = ml
  765. let addon_path = substitute(addon_path,'\(\\\\\|\\\)','/','g')
  766. let addon_files = split(globpath(addon_path,'doc-index*.html'),'\n')
  767. call ProcessHaddockIndexes2(addon_path,addon_files)
  768. endif
  769. endfor
  770. endif
  771. return 1
  772. endfunction
  773. function! ProcessHaddockIndexes(location,files)
  774. let entryPat= '.\{-}"indexentry"[^>]*>\([^<]*\)<\(\%([^=]\{-}TD CLASS="\%(indexentry\)\@!.\{-}</TD\)*\)[^=]\{-}\(\%(="indexentry\|TABLE\).*\)'
  775. let linkPat = '.\{-}HREF="\([^"]*\)".>\([^<]*\)<\(.*\)'
  776. redraw
  777. echo 'populating g:haddock_index from haddock index files in ' a:location
  778. for f in a:files
  779. echo f[len(a:location):]
  780. let contents = join(readfile(f))
  781. let ml = matchlist(contents,entryPat)
  782. while ml!=[]
  783. let [_,entry,links,r;x] = ml
  784. "echo entry links
  785. let ml2 = matchlist(links,linkPat)
  786. let link = {}
  787. while ml2!=[]
  788. let [_,l,m,links;x] = ml2
  789. "echo l m
  790. let link[m] = a:location . '/' . l
  791. let ml2 = matchlist(links,linkPat)
  792. endwhile
  793. let g:haddock_index[DeHTML(entry)] = deepcopy(link)
  794. "echo entry g:haddock_index[entry]
  795. let ml = matchlist(r,entryPat)
  796. endwhile
  797. endfor
  798. endfunction
  799. " concatenating all lines is too slow for a big file, process lines directly
  800. function! ProcessHaddockIndexes2(location,files)
  801. let entryPat= '^>\([^<]*\)</'
  802. let linkPat = '.\{-}A HREF="\([^"]*\)"'
  803. let kindPat = '#\(.\)'
  804. " redraw
  805. echo 'populating g:haddock_index from haddock index files in ' a:location
  806. for f in a:files
  807. echo f[len(a:location):]
  808. let isEntry = 0
  809. let isLink = ''
  810. let link = {}
  811. let entry = ''
  812. for line in readfile(f)
  813. if line=~'CLASS="indexentry'
  814. if (link!={}) && (entry!='')
  815. if has_key(g:haddock_index,DeHTML(entry))
  816. let dict = extend(g:haddock_index[DeHTML(entry)],deepcopy(link))
  817. else
  818. let dict = deepcopy(link)
  819. endif
  820. let g:haddock_index[DeHTML(entry)] = dict
  821. let link = {}
  822. let entry = ''
  823. endif
  824. let isEntry=1
  825. continue
  826. endif
  827. if isEntry==1
  828. let ml = matchlist(line,entryPat)
  829. if ml!=[] | let [_,entry;x] = ml | let isEntry=0 | continue | endif
  830. endif
  831. if entry!=''
  832. let ml = matchlist(line,linkPat)
  833. if ml!=[] | let [_,isLink;x]=ml | continue | endif
  834. endif
  835. if isLink!=''
  836. let ml = matchlist(line,entryPat)
  837. if ml!=[]
  838. let [_,module;x] = ml
  839. let [_,kind;x] = matchlist(isLink,kindPat)
  840. let last = a:location[strlen(a:location)-1]
  841. let link[module."[".kind."]"] = a:location . (last=='/'?'':'/') . isLink
  842. let isLink=''
  843. continue
  844. endif
  845. endif
  846. endfor
  847. if link!={}
  848. if has_key(g:haddock_index,DeHTML(entry))
  849. let dict = extend(g:haddock_index[DeHTML(entry)],deepcopy(link))
  850. else
  851. let dict = deepcopy(link)
  852. endif
  853. let g:haddock_index[DeHTML(entry)] = dict
  854. endif
  855. endfor
  856. endfunction
  857. command! ExportDocIndex call ExportDocIndex()
  858. function! ExportDocIndex()
  859. call HaveIndex()
  860. let entries = []
  861. for key in keys(g:haddock_index)
  862. let entries += [key,string(g:haddock_index[key])]
  863. endfor
  864. call writefile(entries,s:haddock_indexfile)
  865. redir end
  866. endfunction
  867. command! ImportDocIndex call ImportDocIndex()
  868. function! ImportDocIndex()
  869. if filereadable(s:haddock_indexfile)
  870. let lines = readfile(s:haddock_indexfile)
  871. let i=0
  872. while i<len(lines)
  873. let [key,dict] = [lines[i],lines[i+1]]
  874. sandbox let g:haddock_index[key] = eval(dict)
  875. let i+=2
  876. endwhile
  877. return 1
  878. else
  879. return 0
  880. endif
  881. endfunction
  882. function! HaveIndex()
  883. return (g:haddock_index!={} || ImportDocIndex() || DocIndex() )
  884. endfunction
  885. function! MkHaddockModuleIndex()
  886. let g:haddock_moduleindex = {}
  887. call HaveIndex()
  888. for key in keys(g:haddock_index)
  889. let dict = g:haddock_index[key]
  890. for module in keys(dict)
  891. let html = dict[module]
  892. let html = substitute(html ,'#.*$','','')
  893. let module = substitute(module,'\[.\]','','')
  894. let ml = matchlist(html,'libraries/\([^\/]*\)\/')
  895. if ml!=[]
  896. let [_,package;x] = ml
  897. let g:haddock_moduleindex[module] = {'package':package,'html':html}
  898. endif
  899. let ml = matchlist(html,'/\([^\/]*\)\/html/[A-Z]')
  900. if ml!=[]
  901. let [_,package;x] = ml
  902. let g:haddock_moduleindex[module] = {'package':package,'html':html}
  903. endif
  904. endfor
  905. endfor
  906. endfunction
  907. function! HaveModuleIndex()
  908. return (g:haddock_moduleindex!={} || MkHaddockModuleIndex() )
  909. endfunction
  910. " decode HTML symbol encodings (are these all we need?)
  911. function! DeHTML(entry)
  912. let res = a:entry
  913. let decode = { '&lt;': '<', '&gt;': '>', '&amp;': '\\&' }
  914. for enc in keys(decode)
  915. exe 'let res = substitute(res,"'.enc.'","'.decode[enc].'","g")'
  916. endfor
  917. return res
  918. endfunction
  919. " find haddocks for word under cursor
  920. " also lists possible definition sites
  921. " - needs to work for both qualified and unqualified items
  922. " - for 'import qualified M as A', consider M.item as source of A.item
  923. " - offer sources from both type [t] and value [v] namespaces
  924. " - for unqualified items, list all possible sites
  925. " - for qualified items, list imported sites only
  926. " keep track of keys with and without namespace tags:
  927. " the former are needed for lookup, the latter for matching against source
  928. map <LocalLeader>? :call Haddock()<cr>
  929. function! Haddock()
  930. amenu ]Popup.- :echo '-'<cr>
  931. aunmenu ]Popup
  932. let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),0)
  933. if namsym==[]
  934. redraw
  935. echo 'no name/symbol under cursor!'
  936. return 0
  937. endif
  938. let [start,symb,qual,unqual] = namsym
  939. let imports = haskellmode#GatherImports()
  940. let asm = has_key(imports[1],qual) ? imports[1][qual]['modules'] : []
  941. let name = unqual
  942. let dict = HaddockIndexLookup(name)
  943. if dict=={} | return | endif
  944. " for qualified items, narrow results to possible imports that provide qualifier
  945. let filteredKeys = filter(copy(keys(dict))
  946. \ ,'match(asm,substitute(v:val,''\[.\]'','''',''''))!=-1')
  947. let keys = (qual!='') ? filteredKeys : keys(dict)
  948. if (keys==[]) && (qual!='')
  949. echoerr qual.'.'.unqual.' not found in imports'
  950. return 0
  951. endif
  952. " use 'setlocal completeopt+=menuone' if you always want to see menus before
  953. " anything happens (I do, but many users don't..)
  954. if len(keys)==1 && (&completeopt!~'menuone')
  955. call DocBrowser(dict[keys[0]])
  956. elseif has("gui_running")
  957. for key in keys
  958. exe 'amenu ]Popup.'.escape(key,'\.').' :call DocBrowser('''.dict[key].''')<cr>'
  959. endfor
  960. popup ]Popup
  961. else
  962. let s:choices = keys
  963. let key = input('browse docs for '.name.' in: ','','customlist,CompleteAux')
  964. if key!=''
  965. call DocBrowser(dict[key])
  966. endif
  967. endif
  968. endfunction
  969. if !exists("g:haskell_search_engines")
  970. let g:haskell_search_engines =
  971. \ {'hoogle':'http://www.haskell.org/hoogle/?hoogle=%s'
  972. \ ,'hayoo!':'http://holumbus.fh-wedel.de/hayoo/hayoo.html?query=%s'
  973. \ }
  974. endif
  975. map <LocalLeader>?? :let es=g:haskell_search_engines
  976. \ \|echo "g:haskell_search_engines"
  977. \ \|for e in keys(es)
  978. \ \|echo e.' : '.es[e]
  979. \ \|endfor<cr>
  980. map <LocalLeader>?1 :call HaskellSearchEngine('hoogle')<cr>
  981. map <LocalLeader>?2 :call HaskellSearchEngine('hayoo!')<cr>
  982. " query one of the Haskell search engines for the thing under cursor
  983. " - unqualified symbols need to be url-escaped
  984. " - qualified ids need to be fed as separate qualifier and id for
  985. " both hoogle (doesn't handle qualified symbols) and hayoo! (no qualified
  986. " ids at all)
  987. " - qualified ids referring to import-qualified-as qualifiers need to be
  988. " translated to the multi-module searches over the list of original modules
  989. function! HaskellSearchEngine(engine)
  990. amenu ]Popup.- :echo '-'<cr>
  991. aunmenu ]Popup
  992. let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),0)
  993. if namsym==[]
  994. redraw
  995. echo 'no name/symbol under cursor!'
  996. return 0
  997. endif
  998. let [start,symb,qual,unqual] = namsym
  999. let imports = haskellmode#GatherImports()
  1000. let asm = has_key(imports[1],qual) ? imports[1][qual]['modules'] : []
  1001. let unqual = haskellmode#UrlEncode(unqual)
  1002. if a:engine=='hoogle'
  1003. let name = asm!=[] ? unqual.'+'.join(map(copy(asm),'"%2B".v:val'),'+')
  1004. \ : qual!='' ? unqual.'+'.haskellmode#UrlEncode('+').qual
  1005. \ : unqual
  1006. elseif a:engine=='hayoo!'
  1007. let name = asm!=[] ? unqual.'+module:('.join(copy(asm),' OR ').')'
  1008. \ : qual!='' ? unqual.'+module:'.qual
  1009. \ : unqual
  1010. else
  1011. let name = qual=="" ? unqual : qual.".".unqual
  1012. endif
  1013. if has_key(g:haskell_search_engines,a:engine)
  1014. call DocBrowser(printf(g:haskell_search_engines[a:engine],name))
  1015. else
  1016. echoerr "unknown search engine: ".a:engine
  1017. endif
  1018. endfunction
  1019. " used to pass on choices to CompleteAux
  1020. let s:choices=[]
  1021. " if there's no gui, use commandline completion instead of :popup
  1022. " completion function CompleteAux suggests completions for a:al, wrt to s:choices
  1023. function! CompleteAux(al,cl,cp)
  1024. "echomsg '|'.a:al.'|'.a:cl.'|'.a:cp.'|'
  1025. let res = []
  1026. let l = len(a:al)-1
  1027. for r in s:choices
  1028. if l==-1 || r[0 : l]==a:al
  1029. let res += [r]
  1030. endif
  1031. endfor
  1032. return res
  1033. endfunction
  1034. " CamelCase shorthand matching:
  1035. " favour upper-case letters and module qualifier separators (.) for disambiguation
  1036. function! CamelCase(shorthand,string)
  1037. let s1 = a:shorthand
  1038. let s2 = a:string
  1039. let notFirst = 0 " don't elide before first pattern letter
  1040. while ((s1!="")&&(s2!=""))
  1041. let head1 = s1[0]
  1042. let head2 = s2[0]
  1043. let elide = notFirst && ( ((head1=~'[A-Z]') && (head2!~'[A-Z.]'))
  1044. \ ||((head1=='.') && (head2!='.')) )
  1045. if elide
  1046. let s2=s2[1:]
  1047. elseif (head1==head2)
  1048. let s1=s1[1:]
  1049. let s2=s2[1:]
  1050. else
  1051. return 0
  1052. endif
  1053. let notFirst = (head1!='.')||(head2!='.') " treat separators as new beginnings
  1054. endwhile
  1055. return (s1=="")
  1056. endfunction
  1057. " use haddock name index for insert mode completion (CTRL-X CTRL-U)
  1058. function! CompleteHaddock(findstart, base)
  1059. if a:findstart
  1060. let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),-1) " insert-mode: we're 1 beyond the text
  1061. if namsym==[]
  1062. redraw
  1063. echo 'no name/symbol under cursor!'
  1064. return -1
  1065. endif
  1066. let [start,symb,qual,unqual] = namsym
  1067. return (start-1)
  1068. else " find keys matching with "a:base"
  1069. let res = []
  1070. let l = len(a:base)-1
  1071. let qual = a:base =~ '^[A-Z][a-zA-Z0-9_'']*\(\.[A-Z][a-zA-Z0-9_'']*\)*\(\.[a-zA-Z0-9_'']*\)\?$'
  1072. call HaveIndex()
  1073. for key in keys(g:haddock_index)
  1074. let keylist = map(deepcopy(keys(g:haddock_index[key])),'substitute(v:val,"\\[.\\]","","")')
  1075. if (key[0 : l]==a:base)
  1076. for m in keylist
  1077. let res += [{"word":key,"menu":m,"dup":1}]
  1078. endfor
  1079. elseif qual " this tends to be slower
  1080. for m in keylist
  1081. let word = m . '.' . key
  1082. if word[0 : l]==a:base
  1083. let res += [{"word":word,"menu":m,"dup":1}]
  1084. endif
  1085. endfor
  1086. endif
  1087. endfor
  1088. if res==[] " no prefix matches, try CamelCase shortcuts
  1089. for key in keys(g:haddock_index)
  1090. let keylist = map(deepcopy(keys(g:haddock_index[key])),'substitute(v:val,"\\[.\\]","","")')
  1091. if CamelCase(a:base,key)
  1092. for m in keylist
  1093. let res += [{"word":key,"menu":m,"dup":1}]
  1094. endfor
  1095. elseif qual " this tends to be slower
  1096. for m in keylist
  1097. let word = m . '.' . key
  1098. if CamelCase(a:base,word)
  1099. let res += [{"word":word,"menu":m,"dup":1}]
  1100. endif
  1101. endfor
  1102. endif
  1103. endfor
  1104. endif
  1105. return res
  1106. endif
  1107. endfunction
  1108. setlocal completefunc=CompleteHaddock
  1109. "
  1110. " Vim's default completeopt is menu,preview
  1111. " you probably want at least menu, or you won't see alternatives listed
  1112. " setlocal completeopt+=menu
  1113. " menuone is useful, but other haskellmode menus will try to follow your choice here in future
  1114. " setlocal completeopt+=menuone
  1115. " longest sounds useful, but doesn't seem to do what it says, and interferes with CTRL-E
  1116. " setlocal completeopt-=longest
  1117. " fully qualify an unqualified name
  1118. " TODO: - standardise commandline versions of menus
  1119. map <LocalLeader>. :call Qualify()<cr>
  1120. function! Qualify()
  1121. amenu ]Popup.- :echo '-'<cr>
  1122. aunmenu ]Popup
  1123. let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),0)
  1124. if namsym==[]
  1125. redraw
  1126. echo 'no name/symbol under cursor!'
  1127. return 0
  1128. endif
  1129. let [start,symb,qual,unqual] = namsym
  1130. if qual!='' " TODO: should we support re-qualification?
  1131. redraw
  1132. echo 'already qualified'
  1133. return 0
  1134. endif
  1135. let name = unqual
  1136. let line = line('.')
  1137. let prefix = (start<=1 ? '' : getline(line)[0:start-2] )
  1138. let dict = HaddockIndexLookup(name)
  1139. if dict=={} | return | endif
  1140. let keylist = map(deepcopy(keys(dict)),'substitute(v:val,"\\[.\\]","","")')
  1141. let imports = haskellmode#GatherImports()
  1142. let qualifiedImports = []
  1143. for qualifiedImport in keys(imports[1])
  1144. let c=0
  1145. for module in imports[1][qualifiedImport]['modules']
  1146. if haskellmode#ListElem(keylist,module) | let c+=1 | endif
  1147. endfor
  1148. if c>0 | let qualifiedImports=[qualifiedImport]+qualifiedImports | endif
  1149. endfor
  1150. "let asm = has_key(imports[1],qual) ? imports[1][qual]['modules'] : []
  1151. let keylist = filter(copy(keylist),'index(qualifiedImports,v:val)==-1')
  1152. if has("gui_running")
  1153. " amenu ]Popup.-imported- :
  1154. for key in qualifiedImports
  1155. let lhs=escape(prefix.name,'/.|\')
  1156. let rhs=escape(prefix.key.'.'.name,'/&|\')
  1157. exe 'amenu ]Popup.'.escape(key,'\.').' :'.line.'s/'.lhs.'/'.rhs.'/<cr>:noh<cr>'
  1158. endfor
  1159. amenu ]Popup.-not\ imported- :
  1160. for key in keylist
  1161. let lhs=escape(prefix.name,'/.|\')
  1162. let rhs=escape(prefix.key.'.'.name,'/&|\')
  1163. exe 'amenu ]Popup.'.escape(key,'\.').' :'.line.'s/'.lhs.'/'.rhs.'/<cr>:noh<cr>'
  1164. endfor
  1165. popup ]Popup
  1166. else
  1167. let s:choices = qualifiedImports+keylist
  1168. let key = input('qualify '.name.' with: ','','customlist,CompleteAux')
  1169. if key!=''
  1170. let lhs=escape(prefix.name,'/.\')
  1171. let rhs=escape(prefix.key.'.'.name,'/&\')
  1172. exe line.'s/'.lhs.'/'.rhs.'/'
  1173. noh
  1174. endif
  1175. endif
  1176. endfunction
  1177. " create (qualified) import for a (qualified) name
  1178. " TODO: refine search patterns, to avoid misinterpretation of
  1179. " oddities like import'Neither or not'module
  1180. map <LocalLeader>i :call Import(0,0)<cr>
  1181. map <LocalLeader>im :call Import(1,0)<cr>
  1182. map <LocalLeader>iq :call Import(0,1)<cr>
  1183. map <LocalLeader>iqm :call Import(1,1)<cr>
  1184. function! Import(module,qualified)
  1185. amenu ]Popup.- :echo '-'<cr>
  1186. aunmenu ]Popup
  1187. let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),0)
  1188. if namsym==[]
  1189. redraw
  1190. echo 'no name/symbol under cursor!'
  1191. return 0
  1192. endif
  1193. let [start,symb,qual,unqual] = namsym
  1194. let name = unqual
  1195. let pname = ( symb ? '('.name.')' : name )
  1196. let importlist = a:module ? '' : '('.pname.')'
  1197. let qualified = a:qualified ? 'qualified ' : ''
  1198. if qual!=''
  1199. exe 'call append(search(''\%1c\(\<import\>\|\<module\>\|{-# OPTIONS\|{-# LANGUAGE\)'',''nb''),''import '.qualified.qual.importlist.''')'
  1200. return
  1201. endif
  1202. let line = line('.')
  1203. let prefix = getline(line)[0:start-1]
  1204. let dict = HaddockIndexLookup(name)
  1205. if dict=={} | return | endif
  1206. let keylist = map(deepcopy(keys(dict)),'substitute(v:val,"\\[.\\]","","")')
  1207. if has("gui_running")
  1208. for key in keylist
  1209. " exe 'amenu ]Popup.'.escape(key,'\.').' :call append(search("\\%1c\\(import\\\\|module\\\\|{-# OPTIONS\\)","nb"),"import '.key.importlist.'")<cr>'
  1210. exe 'amenu ]Popup.'.escape(key,'\.').' :call append(search(''\%1c\(\<import\>\\|\<module\>\\|{-# OPTIONS\\|{-# LANGUAGE\)'',''nb''),''import '.qualified.key.escape(importlist,'|').''')<cr>'
  1211. endfor
  1212. popup ]Popup
  1213. else
  1214. let s:choices = keylist
  1215. let key = input('import '.name.' from: ','','customlist,CompleteAux')
  1216. if key!=''
  1217. exe 'call append(search(''\%1c\(\<import\>\|\<module\>\|{-# OPTIONS\|{-# LANGUAGE\)'',''nb''),''import '.qualified.key.importlist.''')'
  1218. endif
  1219. endif
  1220. endfunction
  1221. function! HaddockIndexLookup(name)
  1222. call HaveIndex()
  1223. if !has_key(g:haddock_index,a:name)
  1224. echoerr a:name 'not found in haddock index'
  1225. return {}
  1226. endif
  1227. return g:haddock_index[a:name]
  1228. endfunction
  1229. " copied from ghc.vim :-( should we move everything to using autoload instead?
  1230. " we query the ghc version here, as we don't otherwise need it..
  1231. function! GHC_VersionGE(target)
  1232. let s:ghc_version = substitute(system(g:ghc . ' --numeric-version'),'\n','','')
  1233. let current = split(g:ghc_version, '\.' )
  1234. let target = a:target
  1235. for i in current
  1236. if ((target==[]) || (i>target[0]))
  1237. return 1
  1238. elseif (i==target[0])
  1239. let target = target[1:]
  1240. else
  1241. return 0
  1242. endif
  1243. endfor
  1244. return 1
  1245. endfunction
  1246. ftplugin/haskell_hpaste.vim [[[1
  1247. 79
  1248. " rudimentary hpaste support for vim
  1249. " (using netrw for reading, wget for posting/annotating)
  1250. "
  1251. " claus reinke, last modified: 07/04/2009
  1252. "
  1253. " part of haskell plugins: http://projects.haskell.org/haskellmode-vim
  1254. " unless wget is in your PATH, you need to set g:wget
  1255. " before loading this script. windows users are out of
  1256. " luck, unless they have wget installed (such as the
  1257. " cygwin one looked for here), or adapt this script to
  1258. " whatever alternative they have at hand (perhaps using
  1259. " vim's perl/python bindings?)
  1260. if !exists("g:wget")
  1261. if executable("wget")
  1262. let g:wget = "!wget -q"
  1263. else
  1264. let g:wget = "!c:\\cygwin\\bin\\wget -q"
  1265. endif
  1266. endif
  1267. " read (recent) hpaste files
  1268. " show index in new buffer, where ,r will open current entry
  1269. " and ,p will annotate current entry with current buffer
  1270. command! HpasteIndex call HpasteIndex()
  1271. function! HpasteIndex()
  1272. new
  1273. read http://hpaste.org
  1274. %s/\_$\_.//g
  1275. %s/<tr[^>]*>//g
  1276. %s/<\/tr>/ /g
  1277. g/<\/table>/d
  1278. g/DOCTYPE/d
  1279. %s/<td>\([^<]*\)<\/td><td><a href="\/fastcgi\/hpaste\.fcgi\/view?id=\([0-9]*\)">\([^<]*\)<\/a><\/td><td>\([^<]*\)<\/td><td>\([^<]*\)<\/td><td>\([^<]*\)<\/td>/\2 [\1] "\3" \4 \5 \6/
  1280. map <buffer> ,r 0yE:noh<cr>:call HpasteEditEntry('"')<cr>
  1281. endfunction
  1282. " load an existing entry for editing
  1283. command! -nargs=1 HpasteEditEntry call HpasteEditEntry(<f-args>)
  1284. function! HpasteEditEntry(entry)
  1285. new
  1286. exe 'Nread http://hpaste.org/fastcgi/hpaste.fcgi/raw?id='.a:entry
  1287. "exe 'map <buffer> ,p :call HpasteAnnotate('''.a:entry.''')<cr>'
  1288. endfunction
  1289. " " posting temporarily disabled -- needs someone to look into new
  1290. " " hpaste.org structure
  1291. " " annotate existing entry (only to be called via ,p in HpasteIndex)
  1292. " function! HpasteAnnotate(entry)
  1293. " let nick = input("nick? ")
  1294. " let title = input("title? ")
  1295. " if nick=='' || title==''
  1296. " echo "nick or title missing. aborting annotation"
  1297. " return
  1298. " endif
  1299. " call HpastePost('annotate/'.a:entry,nick,title)
  1300. " endfunction
  1301. "
  1302. " " post new hpaste entry
  1303. " " using 'wget --post-data' and url-encoded content
  1304. " command! HpastePostNew call HpastePost('new',<args>)
  1305. " function! HpastePost(mode,nick,title,...)
  1306. " let lines = getbufline("%",1,"$")
  1307. " let pat = '\([^[:alnum:]]\)'
  1308. " let code = '\=printf("%%%02X",char2nr(submatch(1)))'
  1309. " let lines = map(lines,'substitute(v:val."\r\n",'''.pat.''','''.code.''',''g'')')
  1310. "
  1311. " let url = 'http://hpaste.org/' . a:mode
  1312. " let nick = substitute(a:nick,pat,code,'g')
  1313. " let title = substitute(a:title,pat,code,'g')
  1314. " if a:0==0
  1315. " let announce = 'false'
  1316. " else
  1317. " let announce = a:1
  1318. " endif
  1319. " let cmd = g:wget.' --post-data="content='.join(lines,'').'&nick='.nick.'&title='.title.'&announce='.announce.'" '.url
  1320. " exe escape(cmd,'%')
  1321. " endfunction
  1322. autoload/haskellmode.vim [[[1
  1323. 155
  1324. "
  1325. " utility functions for haskellmode plugins
  1326. "
  1327. " (Claus Reinke; last modified: 23/04/2009)
  1328. "
  1329. " part of haskell plugins: http://projects.haskell.org/haskellmode-vim
  1330. " please send patches to <claus.reinke@talk21.com>
  1331. " find start/extent of name/symbol under cursor;
  1332. " return start, symbolic flag, qualifier, unqualified id
  1333. " (this is used in both haskell_doc.vim and in GHC.vim)
  1334. function! haskellmode#GetNameSymbol(line,col,off)
  1335. let name = "[a-zA-Z0-9_']"
  1336. let symbol = "[-!#$%&\*\+/<=>\?@\\^|~:.]"
  1337. "let [line] = getbufline(a:buf,a:lnum)
  1338. let line = a:line
  1339. " find the beginning of unqualified id or qualified id component
  1340. let start = (a:col - 1) + a:off
  1341. if line[start] =~ name
  1342. let pattern = name
  1343. elseif line[start] =~ symbol
  1344. let pattern = symbol
  1345. else
  1346. return []
  1347. endif
  1348. while start > 0 && line[start - 1] =~ pattern
  1349. let start -= 1
  1350. endwhile
  1351. let id = matchstr(line[start :],pattern.'*')
  1352. " call confirm(id)
  1353. " expand id to left and right, to get full id
  1354. let idPos = id[0] == '.' ? start+2 : start+1
  1355. let posA = match(line,'\<\(\([A-Z]'.name.'*\.\)\+\)\%'.idPos.'c')
  1356. let start = posA>-1 ? posA+1 : idPos
  1357. let posB = matchend(line,'\%'.idPos.'c\(\([A-Z]'.name.'*\.\)*\)\('.name.'\+\|'.symbol.'\+\)')
  1358. let end = posB>-1 ? posB : idPos
  1359. " special case: symbolic ids starting with .
  1360. if id[0]=='.' && posA==-1
  1361. let start = idPos-1
  1362. let end = posB==-1 ? start : end
  1363. endif
  1364. " classify full id and split into qualifier and unqualified id
  1365. let fullid = line[ (start>1 ? start-1 : 0) : (end-1) ]
  1366. let symbolic = fullid[-1:-1] =~ symbol " might also be incomplete qualified id ending in .
  1367. let qualPos = matchend(fullid, '\([A-Z]'.name.'*\.\)\+')
  1368. let qualifier = qualPos>-1 ? fullid[ 0 : (qualPos-2) ] : ''
  1369. let unqualId = qualPos>-1 ? fullid[ qualPos : -1 ] : fullid
  1370. " call confirm(start.'/'.end.'['.symbolic.']:'.qualifier.' '.unqualId)
  1371. return [start,symbolic,qualifier,unqualId]
  1372. endfunction
  1373. function! haskellmode#GatherImports()
  1374. let imports={0:{},1:{}}
  1375. let i=1
  1376. while i<=line('$')
  1377. let res = haskellmode#GatherImport(i)
  1378. if !empty(res)
  1379. let [i,import] = res
  1380. let prefixPat = '^import\s*\(qualified\)\?\s\+'
  1381. let modulePat = '\([A-Z][a-zA-Z0-9_''.]*\)'
  1382. let asPat = '\(\s\+as\s\+'.modulePat.'\)\?'
  1383. let hidingPat = '\(\s\+hiding\s*\((.*)\)\)\?'
  1384. let listPat = '\(\s*\((.*)\)\)\?'
  1385. let importPat = prefixPat.modulePat.asPat.hidingPat.listPat ".'\s*$'
  1386. let ml = matchlist(import,importPat)
  1387. if ml!=[]
  1388. let [_,qualified,module,_,as,_,hiding,_,explicit;x] = ml
  1389. let what