@@ -1,56 +1,37 @@ | |||
URxvt*termName: rxvt-unicode | |||
URxvt.buffered: true | |||
URxvt.scrollBar: false | |||
URxvt.visualBell: true | |||
URxvt.cursorColor: gray | |||
URxvt.underlineColor: gray | |||
URxvt*keysym.Del: \033[3~ | |||
URxvt*keysym.Home: \033[1~ | |||
URxvt*keysym.End: \033[4~ | |||
URxvt*keysym.Prior: \033[5~ | |||
URxvt*keysym.Next: \033[6~ | |||
URxvt.font:xft:Bitstream Vera Sans Mono:pixelsize=12:antialias=true | |||
URxvt.perl-ext-common: default,matcher | |||
URxvt.urlLauncher: /usr/bin/uzbl-browser | |||
URxvt*keysym.End: \033[4~ | |||
URxvt*keysym.Next: \033[6~ | |||
URxvt*keysym.Home: \033[1~ | |||
URxvt*keysym.Prior: \033[5~ | |||
URxvt*keysym.Del: \033[3~ | |||
URxvt.matcher.button: 1 | |||
urxvt*foreground: #f2f2f2 | |||
urxvt*background: #101010 | |||
! Normal | |||
! color0 black | |||
! color1 red | |||
! color2 green | |||
! color3 yellow | |||
! color4 blue | |||
! color5 purple | |||
! color6 cyan | |||
! color7 white | |||
urxvt*color0: #6c6c6c | |||
urxvt*color1: #e9897c | |||
urxvt*color2: #b6e77d | |||
urxvt*color3: #ecebbe | |||
urxvt*color4: #a9cdeb | |||
urxvt*color5: #ea96eb | |||
urxvt*color6: #c9caec | |||
urxvt*color7: #f2f2f2 | |||
! Light | |||
! color8 black | |||
! color9 red | |||
! color10 green | |||
! color11 yellow | |||
! color12 blue | |||
! color13 purple | |||
! color14 cyan | |||
! color15 white | |||
urxvt*color8: #747474 | |||
urxvt*color9: #f99286 | |||
urxvt*color10: #c3f786 | |||
urxvt*color11: #fcfbcc | |||
urxvt*color12: #b6defb | |||
urxvt*color13: #fba1fb | |||
urxvt*color14: #d7d9fc | |||
urxvt*color15: #e2e2e2 | |||
URxvt.scrollBar: false | |||
URxvt.underlineColor: gray | |||
URxvt.visualBell: true | |||
URxvt.perl-ext-common: default,matcher | |||
URxvt.font: xft:Bitstream Vera Sans Mono:pixelsize=12:antialias=true | |||
URxvt*termName: rxvt-unicode | |||
URxvt.urlLauncher: /usr/bin/google-chrome | |||
URxvt.cursorColor: gray | |||
URxvt.buffered: true | |||
urxvt*color9: #f99286 | |||
urxvt*color12: #b6defb | |||
urxvt*color13: #fba1fb | |||
urxvt*background: #101010 | |||
urxvt*color1: #e9897c | |||
urxvt*color3: #ecebbe | |||
urxvt*color5: #ea96eb | |||
urxvt*color7: #f2f2f2 | |||
urxvt*color0: #6c6c6c | |||
urxvt*color11: #fcfbcc | |||
urxvt*color14: #d7d9fc | |||
urxvt*foreground: #f2f2f2 | |||
urxvt*color15: #e2e2e2 | |||
urxvt*color2: #b6e77d | |||
urxvt*color4: #a9cdeb | |||
urxvt*color10: #c3f786 | |||
urxvt*color8: #747474 | |||
urxvt*color6: #c9caec | |||
xrenoise.use_xcursors: 1 | |||
xrenoise.display: :0 | |||
xrenoise.display_resource: 0x00,0x39,0x42,0x7C,0x49,0x98,0x94,0x26 | |||
xrenoise.executable_path: /usr/local/bin/renoise-2.7.2 |
@@ -0,0 +1 @@ | |||
haskellmode-20100622.vba: call delete('/home/james/.vim/compiler/ghc.vim')|call delete('/home/james/.vim/ftplugin/haskell.vim')|call delete('/home/james/.vim/ftplugin/haskell_doc.vim')|call delete('/home/james/.vim/ftplugin/haskell_hpaste.vim')|call delete('/home/james/.vim/autoload/haskellmode.vim')|call delete('/home/james/.vim/doc/haskellmode.txt') |
@@ -0,0 +1,10 @@ | |||
let g:netrw_dirhistmax =10 | |||
let g:netrw_dirhist_cnt =8 | |||
let g:netrw_dirhist_1='/home/james/.config/xfce4/xfconf' | |||
let g:netrw_dirhist_2='/home/james/.config/xfce4' | |||
let g:netrw_dirhist_3='/home/james/.config/xfce4/panel' | |||
let g:netrw_dirhist_4='/home/james/.config/xfce4' | |||
let g:netrw_dirhist_5='/home/james/.config/xfce4/desktop' | |||
let g:netrw_dirhist_6='/home/james/.config/xfce4' | |||
let g:netrw_dirhist_7='/home/james/.xmonad' | |||
let g:netrw_dirhist_8='/etc/apache2/sites-enabled' |
@@ -0,0 +1,191 @@ | |||
" | |||
" utility functions for haskellmode plugins | |||
" | |||
" (Claus Reinke; last modified: 22/06/2010) | |||
" | |||
" part of haskell plugins: http://projects.haskell.org/haskellmode-vim | |||
" please send patches to <claus.reinke@talk21.com> | |||
" find start/extent of name/symbol under cursor; | |||
" return start, symbolic flag, qualifier, unqualified id | |||
" (this is used in both haskell_doc.vim and in GHC.vim) | |||
function! haskellmode#GetNameSymbol(line,col,off) | |||
let name = "[a-zA-Z0-9_']" | |||
let symbol = "[-!#$%&\*\+/<=>\?@\\^|~:.]" | |||
"let [line] = getbufline(a:buf,a:lnum) | |||
let line = a:line | |||
" find the beginning of unqualified id or qualified id component | |||
let start = (a:col - 1) + a:off | |||
if line[start] =~ name | |||
let pattern = name | |||
elseif line[start] =~ symbol | |||
let pattern = symbol | |||
else | |||
return [] | |||
endif | |||
while start > 0 && line[start - 1] =~ pattern | |||
let start -= 1 | |||
endwhile | |||
let id = matchstr(line[start :],pattern.'*') | |||
" call confirm(id) | |||
" expand id to left and right, to get full id | |||
let idPos = id[0] == '.' ? start+2 : start+1 | |||
let posA = match(line,'\<\(\([A-Z]'.name.'*\.\)\+\)\%'.idPos.'c') | |||
let start = posA>-1 ? posA+1 : idPos | |||
let posB = matchend(line,'\%'.idPos.'c\(\([A-Z]'.name.'*\.\)*\)\('.name.'\+\|'.symbol.'\+\)') | |||
let end = posB>-1 ? posB : idPos | |||
" special case: symbolic ids starting with . | |||
if id[0]=='.' && posA==-1 | |||
let start = idPos-1 | |||
let end = posB==-1 ? start : end | |||
endif | |||
" classify full id and split into qualifier and unqualified id | |||
let fullid = line[ (start>1 ? start-1 : 0) : (end-1) ] | |||
let symbolic = fullid[-1:-1] =~ symbol " might also be incomplete qualified id ending in . | |||
let qualPos = matchend(fullid, '\([A-Z]'.name.'*\.\)\+') | |||
let qualifier = qualPos>-1 ? fullid[ 0 : (qualPos-2) ] : '' | |||
let unqualId = qualPos>-1 ? fullid[ qualPos : -1 ] : fullid | |||
" call confirm(start.'/'.end.'['.symbolic.']:'.qualifier.' '.unqualId) | |||
return [start,symbolic,qualifier,unqualId] | |||
endfunction | |||
function! haskellmode#GatherImports() | |||
let imports={0:{},1:{}} | |||
let i=1 | |||
while i<=line('$') | |||
let res = haskellmode#GatherImport(i) | |||
if !empty(res) | |||
let [i,import] = res | |||
let prefixPat = '^import\s*\%({-#\s*SOURCE\s*#-}\)\?\(qualified\)\?\s\+' | |||
let modulePat = '\([A-Z][a-zA-Z0-9_''.]*\)' | |||
let asPat = '\(\s\+as\s\+'.modulePat.'\)\?' | |||
let hidingPat = '\(\s\+hiding\s*\((.*)\)\)\?' | |||
let listPat = '\(\s*\((.*)\)\)\?' | |||
let importPat = prefixPat.modulePat.asPat.hidingPat.listPat ".'\s*$' | |||
let ml = matchlist(import,importPat) | |||
if ml!=[] | |||
let [_,qualified,module,_,as,_,hiding,_,explicit;x] = ml | |||
let what = as=='' ? module : as | |||
let hidings = split(hiding[1:-2],',') | |||
let explicits = split(explicit[1:-2],',') | |||
let empty = {'lines':[],'hiding':hidings,'explicit':[],'modules':[]} | |||
let entry = has_key(imports[1],what) ? imports[1][what] : deepcopy(empty) | |||
let imports[1][what] = haskellmode#MergeImport(deepcopy(entry),i,hidings,explicits,module) | |||
if !(qualified=='qualified') | |||
let imports[0][what] = haskellmode#MergeImport(deepcopy(entry),i,hidings,explicits,module) | |||
endif | |||
else | |||
echoerr "haskellmode#GatherImports doesn't understand: ".import | |||
endif | |||
endif | |||
let i+=1 | |||
endwhile | |||
if !has_key(imports[1],'Prelude') | |||
let imports[0]['Prelude'] = {'lines':[],'hiding':[],'explicit':[],'modules':[]} | |||
let imports[1]['Prelude'] = {'lines':[],'hiding':[],'explicit':[],'modules':[]} | |||
endif | |||
return imports | |||
endfunction | |||
function! haskellmode#ListElem(list,elem) | |||
for e in a:list | if e==a:elem | return 1 | endif | endfor | |||
return 0 | |||
endfunction | |||
function! haskellmode#ListIntersect(list1,list2) | |||
let l = [] | |||
for e in a:list1 | if index(a:list2,e)!=-1 | let l += [e] | endif | endfor | |||
return l | |||
endfunction | |||
function! haskellmode#ListUnion(list1,list2) | |||
let l = [] | |||
for e in a:list2 | if index(a:list1,e)==-1 | let l += [e] | endif | endfor | |||
return a:list1 + l | |||
endfunction | |||
function! haskellmode#ListWithout(list1,list2) | |||
let l = [] | |||
for e in a:list1 | if index(a:list2,e)==-1 | let l += [e] | endif | endfor | |||
return l | |||
endfunction | |||
function! haskellmode#MergeImport(entry,line,hiding,explicit,module) | |||
let lines = a:entry['lines'] + [ a:line ] | |||
let hiding = a:explicit==[] ? haskellmode#ListIntersect(a:entry['hiding'], a:hiding) | |||
\ : haskellmode#ListWithout(a:entry['hiding'],a:explicit) | |||
let explicit = haskellmode#ListUnion(a:entry['explicit'], a:explicit) | |||
let modules = haskellmode#ListUnion(a:entry['modules'], [ a:module ]) | |||
return {'lines':lines,'hiding':hiding,'explicit':explicit,'modules':modules} | |||
endfunction | |||
" collect lines belonging to a single import statement; | |||
" return number of last line and collected import statement | |||
" (assume opening parenthesis, if any, is on the first line) | |||
function! haskellmode#GatherImport(lineno) | |||
let lineno = a:lineno | |||
let import = getline(lineno) | |||
if !(import=~'^import\s') | return [] | endif | |||
let open = strlen(substitute(import,'[^(]','','g')) | |||
let close = strlen(substitute(import,'[^)]','','g')) | |||
while open!=close | |||
let lineno += 1 | |||
let linecont = getline(lineno) | |||
let open += strlen(substitute(linecont,'[^(]','','g')) | |||
let close += strlen(substitute(linecont,'[^)]','','g')) | |||
let import .= linecont | |||
endwhile | |||
return [lineno,import] | |||
endfunction | |||
function! haskellmode#UrlEncode(string) | |||
let pat = '\([^[:alnum:]]\)' | |||
let code = '\=printf("%%%02X",char2nr(submatch(1)))' | |||
let url = substitute(a:string,pat,code,'g') | |||
return url | |||
endfunction | |||
" TODO: we could have buffer-local settings, at the expense of | |||
" reconfiguring for every new buffer.. do we want to? | |||
function! haskellmode#GHC() | |||
if (!exists("g:ghc") || !executable(g:ghc)) | |||
if !executable('ghc') | |||
echoerr s:scriptname.": can't find ghc. please set g:ghc, or extend $PATH" | |||
return 0 | |||
else | |||
let g:ghc = 'ghc' | |||
endif | |||
endif | |||
return 1 | |||
endfunction | |||
function! haskellmode#GHC_Version() | |||
if !exists("g:ghc_version") | |||
let g:ghc_version = substitute(system(g:ghc . ' --numeric-version'),'\n','','') | |||
endif | |||
return g:ghc_version | |||
endfunction | |||
function! haskellmode#GHC_VersionGE(target) | |||
let current = split(haskellmode#GHC_Version(), '\.' ) | |||
let target = a:target | |||
for i in current | |||
if ((target==[]) || (i>target[0])) | |||
return 1 | |||
elseif (i==target[0]) | |||
let target = target[1:] | |||
else | |||
return 0 | |||
endif | |||
endfor | |||
return 1 | |||
endfunction | |||
@@ -0,0 +1,76 @@ | |||
" Vim indent file | |||
" Language: CSS | |||
" Maintainer: Nikolai Weibull <now@bitwi.se> | |||
" Latest Revision: 2010-12-22 | |||
if exists("b:did_indent") | |||
finish | |||
endif | |||
let b:did_indent = 1 | |||
setlocal indentexpr=GetCSSIndent() | |||
setlocal indentkeys=0{,0},!^F,o,O | |||
setlocal nosmartindent | |||
if exists("*GetCSSIndent") | |||
finish | |||
endif | |||
function s:prevnonblanknoncomment(lnum) | |||
let lnum = a:lnum | |||
while lnum > 1 | |||
let lnum = prevnonblank(lnum) | |||
let line = getline(lnum) | |||
if line =~ '\*/' | |||
while lnum > 1 && line !~ '/\*' | |||
let lnum -= 1 | |||
endwhile | |||
if line =~ '^\s*/\*' | |||
let lnum -= 1 | |||
else | |||
break | |||
endif | |||
else | |||
break | |||
endif | |||
endwhile | |||
return lnum | |||
endfunction | |||
function s:count_braces(lnum, count_open) | |||
let n_open = 0 | |||
let n_close = 0 | |||
let line = getline(a:lnum) | |||
let pattern = '[{}]' | |||
let i = match(line, pattern) | |||
while i != -1 | |||
if synIDattr(synID(a:lnum, i + 1, 0), 'name') !~ 'css\%(Comment\|StringQ\{1,2}\)' | |||
if line[i] == '{' | |||
let n_open += 1 | |||
elseif line[i] == '}' | |||
if n_open > 0 | |||
let n_open -= 1 | |||
else | |||
let n_close += 1 | |||
endif | |||
endif | |||
endif | |||
let i = match(line, pattern, i + 1) | |||
endwhile | |||
return a:count_open ? n_open : n_close | |||
endfunction | |||
function GetCSSIndent() | |||
let line = getline(v:lnum) | |||
if line =~ '^\s*\*' | |||
return cindent(v:lnum) | |||
endif | |||
let pnum = s:prevnonblanknoncomment(v:lnum - 1) | |||
if pnum == 0 | |||
return 0 | |||
endif | |||
return indent(pnum) + s:count_braces(pnum, 1) * &sw | |||
\ - s:count_braces(v:lnum, 0) * &sw | |||
endfunction |
@@ -0,0 +1,536 @@ | |||
" Vim Compiler File | |||
" Compiler: GHC | |||
" Maintainer: Claus Reinke <claus.reinke@talk21.com> | |||
" Last Change: 22/06/2010 | |||
" | |||
" part of haskell plugins: http://projects.haskell.org/haskellmode-vim | |||
" ------------------------------ paths & quickfix settings first | |||
" | |||
if exists("current_compiler") && current_compiler == "ghc" | |||
finish | |||
endif | |||
let current_compiler = "ghc" | |||
let s:scriptname = "ghc.vim" | |||
if !haskellmode#GHC() | finish | endif | |||
if (!exists("b:ghc_staticoptions")) | |||
let b:ghc_staticoptions = '' | |||
endif | |||
" set makeprg (for quickfix mode) | |||
execute 'setlocal makeprg=' . g:ghc . '\ ' . escape(b:ghc_staticoptions,' ') .'\ -e\ :q\ %' | |||
"execute 'setlocal makeprg=' . g:ghc .'\ -e\ :q\ %' | |||
"execute 'setlocal makeprg=' . g:ghc .'\ --make\ %' | |||
" quickfix mode: | |||
" fetch file/line-info from error message | |||
" TODO: how to distinguish multiline errors from warnings? | |||
" (both have the same header, and errors have no common id-tag) | |||
" how to get rid of first empty message in result list? | |||
setlocal errorformat= | |||
\%-Z\ %#, | |||
\%W%f:%l:%c:\ Warning:\ %m, | |||
\%E%f:%l:%c:\ %m, | |||
\%E%>%f:%l:%c:, | |||
\%+C\ \ %#%m, | |||
\%W%>%f:%l:%c:, | |||
\%+C\ \ %#%tarning:\ %m, | |||
" oh, wouldn't you guess it - ghc reports (partially) to stderr.. | |||
setlocal shellpipe=2> | |||
" ------------------------- but ghc can do a lot more for us.. | |||
" | |||
" allow map leader override | |||
if !exists("maplocalleader") | |||
let maplocalleader='_' | |||
endif | |||
" initialize map of identifiers to their types | |||
" associate type map updates to changedtick | |||
if !exists("b:ghc_types") | |||
let b:ghc_types = {} | |||
let b:my_changedtick = b:changedtick | |||
endif | |||
if exists("g:haskell_functions") | |||
finish | |||
endif | |||
let g:haskell_functions = "ghc" | |||
" avoid hit-enter prompts | |||
set cmdheight=3 | |||
" edit static GHC options | |||
" TODO: add completion for options/packages? | |||
command! GHCStaticOptions call GHC_StaticOptions() | |||
function! GHC_StaticOptions() | |||
let b:ghc_staticoptions = input('GHC static options: ',b:ghc_staticoptions) | |||
execute 'setlocal makeprg=' . g:ghc . '\ ' . escape(b:ghc_staticoptions,' ') .'\ -e\ :q\ %' | |||
let b:my_changedtick -=1 | |||
endfunction | |||
map <LocalLeader>T :call GHC_ShowType(1)<cr> | |||
map <LocalLeader>t :call GHC_ShowType(0)<cr> | |||
function! GHC_ShowType(addTypeDecl) | |||
let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),0) | |||
if namsym==[] | |||
redraw | |||
echo 'no name/symbol under cursor!' | |||
return 0 | |||
endif | |||
let [_,symb,qual,unqual] = namsym | |||
let name = qual=='' ? unqual : qual.'.'.unqual | |||
let pname = ( symb ? '('.name.')' : name ) | |||
call GHC_HaveTypes() | |||
if !has_key(b:ghc_types,name) | |||
redraw | |||
echo pname "type not known" | |||
else | |||
redraw | |||
for type in split(b:ghc_types[name],' -- ') | |||
echo pname "::" type | |||
if a:addTypeDecl | |||
call append( line(".")-1, pname . " :: " . type ) | |||
endif | |||
endfor | |||
endif | |||
endfunction | |||
" show type of identifier under mouse pointer in balloon | |||
" TODO: it isn't a good idea to tie potentially time-consuming tasks | |||
" (querying GHCi for the types) to cursor movements (#14). Currently, | |||
" we ask the user to call :GHCReload explicitly. Should there be an | |||
" option to reenable the old implicit querying? | |||
if has("balloon_eval") | |||
set ballooneval | |||
set balloondelay=600 | |||
set balloonexpr=GHC_TypeBalloon() | |||
function! GHC_TypeBalloon() | |||
if exists("b:current_compiler") && b:current_compiler=="ghc" | |||
let [line] = getbufline(v:beval_bufnr,v:beval_lnum) | |||
let namsym = haskellmode#GetNameSymbol(line,v:beval_col,0) | |||
if namsym==[] | |||
return '' | |||
endif | |||
let [start,symb,qual,unqual] = namsym | |||
let name = qual=='' ? unqual : qual.'.'.unqual | |||
let pname = name " ( symb ? '('.name.')' : name ) | |||
if b:ghc_types == {} | |||
redraw | |||
echo "no type information (try :GHGReload)" | |||
elseif (b:my_changedtick != b:changedtick) | |||
redraw | |||
echo "type information may be out of date (try :GHGReload)" | |||
endif | |||
" silent call GHC_HaveTypes() | |||
if b:ghc_types!={} | |||
if has("balloon_multiline") | |||
return (has_key(b:ghc_types,pname) ? split(b:ghc_types[pname],' -- ') : '') | |||
else | |||
return (has_key(b:ghc_types,pname) ? b:ghc_types[pname] : '') | |||
endif | |||
else | |||
return '' | |||
endif | |||
else | |||
return '' | |||
endif | |||
endfunction | |||
endif | |||
map <LocalLeader>si :call GHC_ShowInfo()<cr> | |||
function! GHC_ShowInfo() | |||
let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),0) | |||
if namsym==[] | |||
redraw | |||
echo 'no name/symbol under cursor!' | |||
return 0 | |||
endif | |||
let [_,symb,qual,unqual] = namsym | |||
let name = qual=='' ? unqual : (qual.'.'.unqual) | |||
let output = GHC_Info(name) | |||
pclose | new | |||
setlocal previewwindow | |||
setlocal buftype=nofile | |||
setlocal noswapfile | |||
put =output | |||
wincmd w | |||
"redraw | |||
"echo output | |||
endfunction | |||
" fill the type map, unless nothing has changed since the last attempt | |||
function! GHC_HaveTypes() | |||
if b:ghc_types == {} && (b:my_changedtick != b:changedtick) | |||
let b:my_changedtick = b:changedtick | |||
return GHC_BrowseAll() | |||
endif | |||
endfunction | |||
" update b:ghc_types after successful make | |||
au QuickFixCmdPost make if GHC_CountErrors()==0 | silent call GHC_BrowseAll() | endif | |||
" count only error entries in quickfix list, ignoring warnings | |||
function! GHC_CountErrors() | |||
let c=0 | |||
for e in getqflist() | if e.type=='E' && e.text !~ "^[ \n]*Warning:" | let c+=1 | endif | endfor | |||
return c | |||
endfunction | |||
command! GHCReload call GHC_BrowseAll() | |||
function! GHC_BrowseAll() | |||
" let imports = haskellmode#GatherImports() | |||
" let modules = keys(imports[0]) + keys(imports[1]) | |||
let b:my_changedtick = b:changedtick | |||
let imports = {} " no need for them at the moment | |||
let current = GHC_NameCurrent() | |||
let module = current==[] ? 'Main' : current[0] | |||
if haskellmode#GHC_VersionGE([6,8,1]) | |||
return GHC_BrowseBangStar(module) | |||
else | |||
return GHC_BrowseMultiple(imports,['*'.module]) | |||
endif | |||
endfunction | |||
function! GHC_NameCurrent() | |||
let last = line("$") | |||
let l = 1 | |||
while l<last | |||
let ml = matchlist( getline(l), '^module\s*\([^ (]*\)') | |||
if ml != [] | |||
let [_,module;x] = ml | |||
return [module] | |||
endif | |||
let l += 1 | |||
endwhile | |||
redraw | |||
echo "cannot find module header for file " . expand("%") | |||
return [] | |||
endfunction | |||
function! GHC_BrowseBangStar(module) | |||
redraw | |||
echo "browsing module " a:module | |||
let command = ":browse! *" . a:module | |||
let orig_shellredir = &shellredir | |||
let &shellredir = ">" " ignore error/warning messages, only output or lack of it | |||
let output = system(g:ghc . ' ' . b:ghc_staticoptions . ' -v0 --interactive ' . expand("%") , command ) | |||
let &shellredir = orig_shellredir | |||
return GHC_ProcessBang(a:module,output) | |||
endfunction | |||
function! GHC_BrowseMultiple(imports,modules) | |||
redraw | |||
echo "browsing modules " a:modules | |||
let command = ":browse " . join( a:modules, " \n :browse ") | |||
let command = substitute(command,'\(:browse \(\S*\)\)','putStrLn "-- \2" \n \1','g') | |||
let output = system(g:ghc . ' ' . b:ghc_staticoptions . ' -v0 --interactive ' . expand("%") , command ) | |||
return GHC_Process(a:imports,output) | |||
endfunction | |||
function! GHC_Info(what) | |||
" call GHC_HaveTypes() | |||
let output = system(g:ghc . ' ' . b:ghc_staticoptions . ' -v0 --interactive ' . expand("%"), ":info ". a:what) | |||
return output | |||
endfunction | |||
function! GHC_ProcessBang(module,output) | |||
let module = a:module | |||
let b = a:output | |||
let linePat = '^\(.\{-}\)\n\(.*\)' | |||
let contPat = '\s\+\(.\{-}\)\n\(.*\)' | |||
let typePat = '^\(\)\(\S*\)\s*::\(.*\)' | |||
let commentPat = '^-- \(\S*\)' | |||
let definedPat = '^-- defined locally' | |||
let importedPat = '^-- imported via \(.*\)' | |||
if !(b=~commentPat) | |||
echo s:scriptname.": GHCi reports errors (try :make?)" | |||
return 0 | |||
endif | |||
let b:ghc_types = {} | |||
let ml = matchlist( b , linePat ) | |||
while ml != [] | |||
let [_,l,rest;x] = ml | |||
let mlDecl = matchlist( l, typePat ) | |||
if mlDecl != [] | |||
let [_,indent,id,type;x] = mlDecl | |||
let ml2 = matchlist( rest , '^'.indent.contPat ) | |||
while ml2 != [] | |||
let [_,c,rest;x] = ml2 | |||
let type .= c | |||
let ml2 = matchlist( rest , '^'.indent.contPat ) | |||
endwhile | |||
let id = substitute( id, '^(\(.*\))$', '\1', '') | |||
let type = substitute( type, '\s\+', " ", "g" ) | |||
" using :browse! *<current>, we get both unqualified and qualified ids | |||
let qualified = (id =~ '\.') && (id =~ '[A-Z]') | |||
let b:ghc_types[id] = type | |||
if !qualified | |||
for qual in qualifiers | |||
let b:ghc_types[qual.'.'.id] = type | |||
endfor | |||
endif | |||
else | |||
let mlImported = matchlist( l, importedPat ) | |||
let mlDefined = matchlist( l, definedPat ) | |||
if mlImported != [] | |||
let [_,modules;x] = mlImported | |||
let qualifiers = split( modules, ', ' ) | |||
elseif mlDefined != [] | |||
let qualifiers = [module] | |||
endif | |||
endif | |||
let ml = matchlist( rest , linePat ) | |||
endwhile | |||
return 1 | |||
endfunction | |||
function! GHC_Process(imports,output) | |||
let b = a:output | |||
let imports = a:imports | |||
let linePat = '^\(.\{-}\)\n\(.*\)' | |||
let contPat = '\s\+\(.\{-}\)\n\(.*\)' | |||
let typePat = '^\(\s*\)\(\S*\)\s*::\(.*\)' | |||
let modPat = '^-- \(\S*\)' | |||
" add '-- defined locally' and '-- imported via ..' | |||
if !(b=~modPat) | |||
echo s:scriptname.": GHCi reports errors (try :make?)" | |||
return 0 | |||
endif | |||
let b:ghc_types = {} | |||
let ml = matchlist( b , linePat ) | |||
while ml != [] | |||
let [_,l,rest;x] = ml | |||
let mlDecl = matchlist( l, typePat ) | |||
if mlDecl != [] | |||
let [_,indent,id,type;x] = mlDecl | |||
let ml2 = matchlist( rest , '^'.indent.contPat ) | |||
while ml2 != [] | |||
let [_,c,rest;x] = ml2 | |||
let type .= c | |||
let ml2 = matchlist( rest , '^'.indent.contPat ) | |||
endwhile | |||
let id = substitute(id, '^(\(.*\))$', '\1', '') | |||
let type = substitute( type, '\s\+', " ", "g" ) | |||
" using :browse *<current>, we get both unqualified and qualified ids | |||
if current_module " || has_key(imports[0],module) | |||
if has_key(b:ghc_types,id) && !(matchstr(b:ghc_types[id],escape(type,'[].'))==type) | |||
let b:ghc_types[id] .= ' -- '.type | |||
else | |||
let b:ghc_types[id] = type | |||
endif | |||
endif | |||
if 0 " has_key(imports[1],module) | |||
let qualid = module.'.'.id | |||
let b:ghc_types[qualid] = type | |||
endif | |||
else | |||
let mlMod = matchlist( l, modPat ) | |||
if mlMod != [] | |||
let [_,module;x] = mlMod | |||
let current_module = module[0]=='*' | |||
let module = current_module ? module[1:] : module | |||
endif | |||
endif | |||
let ml = matchlist( rest , linePat ) | |||
endwhile | |||
return 1 | |||
endfunction | |||
let s:ghc_templates = ["module _ () where","class _ where","class _ => _ where","instance _ where","instance _ => _ where","type family _","type instance _ = ","data _ = ","newtype _ = ","type _ = "] | |||
" use ghci :browse index for insert mode omnicompletion (CTRL-X CTRL-O) | |||
function! GHC_CompleteImports(findstart, base) | |||
if a:findstart | |||
let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),-1) " insert-mode: we're 1 beyond the text | |||
if namsym==[] | |||
redraw | |||
echo 'no name/symbol under cursor!' | |||
return -1 | |||
endif | |||
let [start,symb,qual,unqual] = namsym | |||
return (start-1) | |||
else " find keys matching with "a:base" | |||
let res = [] | |||
let l = len(a:base)-1 | |||
call GHC_HaveTypes() | |||
for key in keys(b:ghc_types) | |||
if key[0 : l]==a:base | |||
let res += [{"word":key,"menu":":: ".b:ghc_types[key],"dup":1}] | |||
endif | |||
endfor | |||
return res | |||
endif | |||
endfunction | |||
set omnifunc=GHC_CompleteImports | |||
" | |||
" Vim's default completeopt is menu,preview | |||
" you probably want at least menu, or you won't see alternatives listed | |||
" setlocal completeopt+=menu | |||
" menuone is useful, but other haskellmode menus will try to follow your choice here in future | |||
" setlocal completeopt+=menuone | |||
" longest sounds useful, but doesn't seem to do what it says, and interferes with CTRL-E | |||
" setlocal completeopt-=longest | |||
map <LocalLeader>ct :call GHC_CreateTagfile()<cr> | |||
function! GHC_CreateTagfile() | |||
redraw | |||
echo "creating tags file" | |||
let output = system(g:ghc . ' ' . b:ghc_staticoptions . ' -e ":ctags" ' . expand("%")) | |||
" for ghcs older than 6.6, you would need to call another program | |||
" here, such as hasktags | |||
echo output | |||
endfunction | |||
command! -nargs=1 GHCi redraw | echo system(g:ghc. ' ' . b:ghc_staticoptions .' '.expand("%").' -e "'.escape(<f-args>,'"').'"') | |||
" use :make 'not in scope' errors to explicitly list imported ids | |||
" cursor needs to be on import line, in correctly loadable module | |||
map <LocalLeader>ie :call GHC_MkImportsExplicit()<cr> | |||
function! GHC_MkImportsExplicit() | |||
let save_cursor = getpos(".") | |||
let line = getline('.') | |||
let lineno = line('.') | |||
let ml = matchlist(line,'^import\(\s*qualified\)\?\s*\([^( ]\+\)') | |||
if ml!=[] | |||
let [_,q,mod;x] = ml | |||
silent make | |||
if getqflist()==[] | |||
if line=~"import[^(]*Prelude" | |||
call setline(lineno,substitute(line,"(.*","","").'()') | |||
else | |||
call setline(lineno,'-- '.line) | |||
endif | |||
silent write | |||
silent make | |||
let qflist = getqflist() | |||
call setline(lineno,line) | |||
silent write | |||
let ids = {} | |||
for d in qflist | |||
let ml = matchlist(d.text,'Not in scope: \([^`]*\)`\([^'']*\)''') | |||
if ml!=[] | |||
let [_,what,qid;x] = ml | |||
let id = ( qid =~ "^[A-Z]" ? substitute(qid,'.*\.\([^.]*\)$','\1','') : qid ) | |||
let pid = ( id =~ "[a-zA-Z0-9_']\\+" ? id : '('.id.')' ) | |||
if what =~ "data" | |||
call GHC_HaveTypes() | |||
if has_key(b:ghc_types,id) | |||
let pid = substitute(b:ghc_types[id],'^.*->\s*\(\S*\).*$','\1','').'('.pid.')' | |||
else | |||
let pid = '???('.pid.')' | |||
endif | |||
endif | |||
let ids[pid] = 1 | |||
endif | |||
endfor | |||
call setline(lineno,'import'.q.' '.mod.'('.join(keys(ids),',').')') | |||
else | |||
copen | |||
endif | |||
endif | |||
call setpos('.', save_cursor) | |||
endfunction | |||
" no need to ask GHC about its supported languages and | |||
" options with every editing session. cache the info in | |||
" ~/.vim/haskellmode.config | |||
" TODO: should we store more info (see haskell_doc.vim)? | |||
" move to autoload? | |||
" should we keep a history of GHC versions encountered? | |||
function! GHC_SaveConfig() | |||
let vimdir = expand('~').'/'.'.vim' | |||
let config = vimdir.'/haskellmode.config' | |||
if !isdirectory(vimdir) | |||
call mkdir(vimdir) | |||
endif | |||
let entries = ['-- '.g:ghc_version] | |||
for l in s:ghc_supported_languages | |||
let entries += [l] | |||
endfor | |||
let entries += ['--'] | |||
for l in s:opts | |||
let entries += [l] | |||
endfor | |||
call writefile(entries,config) | |||
endfunction | |||
" reuse cached GHC configuration info, if using the same | |||
" GHC version. | |||
function! GHC_LoadConfig() | |||
let vimdir = expand('~').'/'.'.vim' | |||
let config = vimdir.'/haskellmode.config' | |||
if filereadable(config) | |||
let lines = readfile(config) | |||
if lines[0]=='-- '.g:ghc_version | |||
let i=1 | |||
let s:ghc_supported_languages = [] | |||
while i<len(lines) && lines[i]!='--' | |||
let s:ghc_supported_languages += [lines[i]] | |||
let i+=1 | |||
endwhile | |||
let i+=1 | |||
let s:opts = [] | |||
while i<len(lines) | |||
let s:opts += [lines[i]] | |||
let i+=1 | |||
endwhile | |||
return 1 | |||
else | |||
return 0 | |||
endif | |||
else | |||
return 0 | |||
endif | |||
endfunction | |||
let s:GHC_CachedConfig = haskellmode#GHC_VersionGE([6,8]) && GHC_LoadConfig() | |||
if haskellmode#GHC_VersionGE([6,8,2]) | |||
if !s:GHC_CachedConfig | |||
let s:opts = filter(split(substitute(system(g:ghc . ' -v0 --interactive', ':set'), ' ', '','g'), '\n'), 'v:val =~ "-f"') | |||
endif | |||
else | |||
let s:opts = ["-fglasgow-exts","-fallow-undecidable-instances","-fallow-overlapping-instances","-fno-monomorphism-restriction","-fno-mono-pat-binds","-fno-cse","-fbang-patterns","-funbox-strict-fields"] | |||
endif | |||
let s:opts = sort(s:opts) | |||
amenu ]OPTIONS_GHC.- :echo '-'<cr> | |||
aunmenu ]OPTIONS_GHC | |||
for o in s:opts | |||
exe 'amenu ]OPTIONS_GHC.'.o.' :call append(0,"{-# OPTIONS_GHC '.o.' #-}")<cr>' | |||
endfor | |||
if has("gui_running") | |||
map <LocalLeader>opt :popup ]OPTIONS_GHC<cr> | |||
else | |||
map <LocalLeader>opt :emenu ]OPTIONS_GHC. | |||
endif | |||
amenu ]LANGUAGES_GHC.- :echo '-'<cr> | |||
aunmenu ]LANGUAGES_GHC | |||
if haskellmode#GHC_VersionGE([6,8]) | |||
if !s:GHC_CachedConfig | |||
let s:ghc_supported_languages = sort(split(system(g:ghc . ' --supported-languages'),'\n')) | |||
endif | |||
for l in s:ghc_supported_languages | |||
exe 'amenu ]LANGUAGES_GHC.'.l.' :call append(0,"{-# LANGUAGE '.l.' #-}")<cr>' | |||
endfor | |||
if has("gui_running") | |||
map <LocalLeader>lang :popup ]LANGUAGES_GHC<cr> | |||
else | |||
map <LocalLeader>lang :emenu ]LANGUAGES_GHC. | |||
endif | |||
endif | |||
if !s:GHC_CachedConfig | |||
call GHC_SaveConfig() | |||
endif | |||
@@ -0,0 +1,465 @@ | |||
*haskellmode.txt* Haskell Mode Plugins 02/05/2009 | |||
Authors: | |||
Claus Reinke <claus.reinke@talk21.com> ~ | |||
Homepage: | |||
http://projects.haskell.org/haskellmode-vim | |||
CONTENTS *haskellmode* | |||
1. Overview |haskellmode-overview| | |||
1.1 Runtime Requirements |haskellmode-requirements| | |||
1.2 Quick Reference |haskellmode-quickref| | |||
2. Settings |haskellmode-settings| | |||
2.1 GHC and web browser |haskellmode-settings-main| | |||
2.2 Fine tuning - more configuration options |haskellmode-settings-fine| | |||
3. GHC Compiler Integration |haskellmode-compiler| | |||
4. Haddock Integration |haskellmode-haddock| | |||
4.1 Indexing |haskellmode-indexing| | |||
4.2 Lookup |haskellmode-lookup| | |||
4.3 Editing |haskellmode-editing| | |||
5. Hpaste Integration |haskellmode-hpaste| | |||
6. Additional Resources |haskellmode-resources| | |||
============================================================================== | |||
*haskellmode-overview* | |||
1. Overview ~ | |||
The Haskell mode plugins provide advanced support for Haskell development | |||
using GHC/GHCi on Windows and Unix-like systems. The functionality is | |||
based on Haddock-generated library indices, on GHCi's interactive | |||
commands, or on simply activating (some of) Vim's built-in program editing | |||
support in Haskell-relevant fashion. These plugins live side-by-side with | |||
the pre-defined |syntax-highlighting| support for |haskell| sources, and | |||
any other Haskell-related plugins you might want to install (see | |||
|haskellmode-resources|). | |||
The Haskell mode plugins consist of three filetype plugins (haskell.vim, | |||
haskell_doc.vim, haskell_hpaste.vim), which by Vim's |filetype| detection | |||
mechanism will be auto-loaded whenever files with the extension '.hs' are | |||
opened, and one compiler plugin (ghc.vim) which you will need to load from | |||
your vimrc file (see |haskellmode-settings|). | |||
*haskellmode-requirements* | |||
1.1 Runtime Requirements ~ | |||
The plugins require a recent installation of GHC/GHCi. The functionality | |||
derived from Haddock-generated library indices also requires a local | |||
installation of the Haddock documentation for GHC's libraries (if there is | |||
no documentation package for your system, you can download a tar-ball from | |||
haskell.org), as well as an HTML browser (see |haddock_browser|). If you | |||
want to use the experimental hpaste interface, you will also need Wget. | |||
* GHC/GHCi ~ | |||
Provides core functionality. http://www.haskell.org/ghc | |||
* HTML library documentation files and indices generated by Haddock ~ | |||
These usually come with your GHC installation, possibly as a separate | |||
package. If you cannot get them this way, you can download a tar-ball | |||
matching your GHC version from http://www.haskell.org/ghc/docs/ | |||
* HTML browser with basic CSS support ~ | |||
For browsing Haddock docs. | |||
* Wget ~ | |||
For interfacing with http://hpaste.org. | |||
Wget is widely available for modern Unix-like operating systems. Several | |||
ports also exist for Windows, including: | |||
- Official GNU Wget (natively compiled for Win32) | |||
http://www.gnu.org/software/wget/#downloading | |||
- UnxUtils Wget (natively compiled for Win32, bundled with other ported | |||
Unix utilities) | |||
http://sourceforge.net/projects/unxutils/ | |||
- Cygwin Wget (emulated POSIX in Win32, must be run under Cygwin) | |||
http://cygwin.com/packages/wget/ | |||
*haskellmode-quickref* | |||
1.2 Quick Reference ~ | |||
|:make| load into GHCi, show errors (|quickfix| |:copen|) | |||
|_ct| create |tags| file | |||
|_si| show info for id under cursor | |||
|_t| show type for id under cursor | |||
|_T| insert type declaration for id under cursor | |||
|balloon| show type for id under mouse pointer | |||
|_?| browse Haddock entry for id under cursor | |||
|_?1| search Hoogle for id under cursor | |||
|_?2| search Hayoo! for id under cursor | |||
|:IDoc| {identifier} browse Haddock entry for unqualified {identifier} | |||
|:MDoc| {module} browse Haddock entry for {module} | |||
|:FlagReference| {s} browse Users Guide Flag Reference for section {s} | |||
|_.| qualify unqualified id under cursor | |||
|_i| add 'import <module>(<identifier>)' for id under cursor | |||
|_im| add 'import <module>' for id under cursor | |||
|_iq| add 'import qualified <module>(<identifier>)' for id under cursor | |||
|_iqm| add 'import qualified <module>' for id under cursor | |||
|_ie| make imports explit for import statement under cursor | |||
|_opt| add OPTIONS_GHC pragma | |||
|_lang| add LANGUAGE pragma | |||
|i_CTRL-X_CTRL-O| insert-mode completion based on imported ids (|haskellmode-XO|) | |||
|i_CTRL-X_CTRL-U| insert-mode completion based on documented ids (|haskellmode-XU|) | |||
|i_CTRL-N| insert-mode completion based on imported sources | |||
|:GHCi|{command/expr} run GHCi command/expr in current module | |||
|:GHCStaticOptions| edit static GHC options for this buffer | |||
|:DocSettings| show current Haddock-files-related plugin settings | |||
|:DocIndex| populate Haddock index | |||
|:ExportDocIndex| cache current Haddock index to a file | |||
|:HpasteIndex| Read index of most recent entries from hpaste.org | |||
|:HpastePostNew| Submit current buffer as a new hpaste | |||
============================================================================== | |||
*haskellmode-settings* | |||
2. Settings ~ | |||
The plugins try to find their dependencies in standard locations, so if | |||
you're lucky, you will only need to set |compiler| to ghc, and configure | |||
the location of your favourite web browser. You will also want to make | |||
sure that |filetype| detection and |syntax| highlighting are on. Given the | |||
variety of things to guess, however, some dependencies might not be found | |||
correctly, or the defaults might not be to your liking, in which case you | |||
can do some more fine tuning. All of this configuration should happen in | |||
your |vimrc|. | |||
> | |||
" enable syntax highlighting | |||
syntax on | |||
" enable filetype detection and plugin loading | |||
filetype plugin on | |||
< | |||
*haskellmode-settings-main* | |||
2.1 GHC and web browser ~ | |||
*compiler-ghc* *ghc-compiler* | |||
To use the features provided by the GHC |compiler| plugin, use the | |||
following |autocommand| in your vimrc: | |||
> | |||
au BufEnter *.hs compiler ghc | |||
< | |||
*g:ghc* | |||
If the compiler plugin can't locate your GHC binary, or if you have | |||
several versions of GHC installed and have a preference as to which binary | |||
is used, set |g:ghc|: | |||
> | |||
:let g:ghc="/usr/bin/ghc-6.6.1" | |||
< | |||
*g:haddock_browser* | |||
The preferred HTML browser for viewing Haddock documentation can be set as | |||
follows: | |||
> | |||
:let g:haddock_browser="/usr/bin/firefox" | |||
< | |||
*haskellmode-settings-fine* | |||
2.2 Fine tuning - more configuration options ~ | |||
Most of the fine tuning is likely to happen for the haskellmode_doc.vim | |||
plugin, so you can check the current settings for this plugin via the | |||
command |:DocSettings|. If all the settings reported there are to your | |||
liking, you probably won't need to do any fine tuning. | |||
*g:haddock_browser_callformat* | |||
By default, the web browser|g:haddock_browser| will be started | |||
asynchronously (in the background) on Windows or when vim is running in a | |||
GUI, and synchronously (in the foreground) otherwise. These settings seem | |||
to work fine if you are using a console mode browser (eg, when editing in | |||
a remote session), or if you are starting a GUI browser that will launch | |||
itself in the background. But if these settings do not work for you, you | |||
can change the default browser launching behavior. | |||
This is controlled by |g:haddock_browser_callformat|. It specifies a | |||
format string which uses two '%s' parameters, the first representing the | |||
path of the browser to launch, and the second is the documentation URL | |||
(minus the protocol specifier, i.e. file://) passed to it by the Haddock | |||
plugin. For instance, to launch a GUI browser on Unix-like systems and | |||
force it to the background (see also |shellredir|): | |||
> | |||
:let g:haddock_browser_callformat = '%s file://%s '.printf(&shellredir,'/dev/null').' &' | |||
< | |||
*g:haddock_docdir* | |||
Your system's installed Haddock documentation for GHC and its libraries | |||
should be automatically detected. If the plugin can't locate them, you | |||
must point |g:haddock_docdir| to the path containing the master index.html | |||
file for the subdirectories 'libraries', 'Cabal', 'users_guide', etc.: | |||
> | |||
:let g:haddock_docdir="/usr/local/share/doc/ghc/html/" | |||
< | |||
*g:haddock_indexfiledir* | |||
The information gathered from Haddock's index files will be stored in a | |||
file called 'haddock_index.vim' in a directory derived from the Haddock | |||
location, or in $HOME. To configure another directory for the index file, | |||
use: | |||
> | |||
:let g:haddock_indexfiledir="~/.vim/" | |||
< | |||
*g:wget* | |||
If you also want to try the experimental hpaste functionality, you might | |||
you need to set |g:wget| before the |hpaste| plugin is loaded (unless wget | |||
is in your PATH): | |||
> | |||
:let g:wget="C:\Program Files\wget\wget.exe" | |||
< | |||
Finally, the mappings actually use|<LocalLeader>|behind the scenes, so if | |||
you have to, you can redefine|maplocalleader|to something other than '_'. | |||
Just remember that the docs still refer to mappings starting with '_', to | |||
avoid confusing the majority of users!-) | |||
============================================================================== | |||
*haskellmode-compiler* *ghc* | |||
3. GHC Compiler Integration ~ | |||
The GHC |compiler| plugin sets the basic |errorformat| and |makeprg| to | |||
enable |quickfix| mode using GHCi, and provides functionality for show | |||
info (|_si|), show type (|_t| or mouse |balloon|), add type declaration | |||
(|_T|), create tag file (|_ct|), and insert-mode completion | |||
(|i_CTRL-X_CTRL-O|) based on GHCi browsing of the current and imported | |||
modules. | |||
To avoid frequent calls to GHCi, type information is cached in Vim. The | |||
cache will be populated the first time a command depends on it, and will | |||
be refreshed every time a |:make| goes through without generating errors | |||
(if the |:make| does not succeed, the old types will remain available in | |||
Vim). You can also unconditionally force reloading of type info using | |||
|:GHCReload| (if GHCi cannot load your file, the type info will be empty). | |||
In addition to the standard|quickfix| commands, the GHC compiler plugin | |||
provides: | |||
*:GHCReload* | |||
:GHCReload Reload modules and unconditionally refresh cache of | |||
type info. Usually, |:make| is prefered, as that will | |||
refresh the cache only if GHCi reports no errors, and | |||
show the errors otherwise. | |||
*:GHCStaticOptions* | |||
:GHCStaticOptions Edit the static GHC options (more generally, options | |||
that cannot be set by in-file OPTIONS_GHC pragmas) | |||
for the current buffer. Useful for adding hidden | |||
packages (-package ghc), or additional import paths | |||
(-isrc; you will then also want to augment |path|). | |||
If you have static options you want to set as | |||
defaults, you could use b:ghc_staticoptions, eg: | |||
> | |||
au FileType haskell let b:ghc_staticoptions = '-isrc' | |||
au FileType haskell setlocal path += src | |||
< | |||
*:GHCi* | |||
:GHCi {command/expr} Run GHCi commands/expressions in the current module. | |||
*_ct* | |||
_ct Create |tags| file for the current Haskell source | |||
file. This uses GHCi's :ctags command, so it will work | |||
recursively, but will only list tags for exported | |||
entities. | |||
*_opt* | |||
_opt Shows a menu of frequently used GHC compiler options | |||
(selecting an entry adds the option as a pragma to the | |||
start of the file). Uses popup menu (GUI) or :emenu | |||
and command-line completion (CLI). | |||
*_lang* | |||
_lang Shows a menu of the LANGUAGE options supported by GHC | |||
(selecting an entry adds the language as a pragma to | |||
the start of the file). Uses popup menu (GUI) or | |||
:emenu and command-line completion (CLI). | |||
*_si* | |||
_si Show extended information for the name under the | |||
cursor. Uses GHCi's :info command. Output appears in | |||
|preview-window| (when done, close with |:pclose|). | |||
*_t* | |||
_t Show type for the name under the cursor. Uses cached | |||
info from GHCi's :browse command. | |||
*_T* | |||
_T Insert type declaration for the name under the cursor. | |||
Uses cached info from GHCi's :browse command. | |||
*haskellmode-XO* *haskellmode-omni-completion* | |||
CTRL-X CTRL-O Standard insert-mode omni-completion based on the | |||
cached type info from GHCi browsing current and | |||
imported modules. Only names from the current and from | |||
imported modules are included (the completion menu | |||
also show the type of each identifier). | |||
============================================================================== | |||
*haskellmode-haddock* *haddock* | |||
4. Haddock Integration ~ | |||
Haskell mode integrates with Haddock-generated HTML documentation, | |||
providing features such as navigating to the Haddock entry for the | |||
identifier under the cursor (|_?|), completion for the identifier under | |||
the cursor (|i_CTRL-X_CTRL-U|), and adding import statements (|_i| |_im| | |||
|_iq| |_iqm|) or module qualifier (|_.|) for the identifier under the | |||
cursor. | |||
These commands operate on an internal Haddock index built from the | |||
platform's installed Haddock documentation for GHC's libraries. Since | |||
populating this index takes several seconds, it should be stored as a | |||
file called 'haddock_index.vim' in the directory specified by | |||
|g:haddock_indexfiledir|. | |||
Some commands present a different interface (popup menu or command-line | |||
completion) according to whether the current Vim instance is graphical or | |||
console-based (actually: whether or not the GUI is running). Such | |||
differences are marked below with the annotations (GUI) and (CLI), | |||
respectively. | |||
|:DocSettings| shows the settings for this plugin. If you are happy with | |||
them, you can call |:ExportDocIndex| to populate and write out the | |||
documentation index (should be called once for every new version of GHC). | |||
*:DocSettings* | |||
:DocSettings Show current Haddock-files-related plugin settings. | |||
*haskellmode-indexing* | |||
4.1 Indexing ~ | |||
*:DocIndex* | |||
:DocIndex Populate the Haddock index from the GHC library | |||
documentation. | |||
*:ExportDocIndex* | |||
:ExportDocIndex Cache the current Haddock index to a file (populate | |||
index first, if empty). | |||
*haskellmode-lookup* | |||
4.2 Lookup ~ | |||
*_?* | |||
_? Open the Haddock entry (in |haddock_browser|) for an | |||
identifier under the cursor, selecting full | |||
qualifications from a popup menu (GUI) or via | |||
command-line completion (CLI), if the identifier is | |||
not qualified. | |||
*_?1* | |||
_?1 Search Hoogle (using |haddock_browser|) for an | |||
identifier under the cursor. | |||
*_?2* | |||
_?2 Search Hayoo! (using |haddock_browser|) for an | |||
identifier under the cursor. | |||
*:IDoc* | |||
:IDoc {identifier} Open the Haddock entry for the unqualified | |||
{identifier} in |haddock_browser|, suggesting possible | |||
full qualifications. | |||
*:MDoc* | |||
:MDoc {module} Open the Haddock entry for {module} in | |||
|haddock_browser| (with command-line completion for | |||
the fully qualified module name). | |||
*:FlagReference* | |||
:FlagReference {s} Browse Users Guide Flag Reference for section {s} | |||
(with command-line completion for section headers). | |||
*haskellmode-editing* | |||
4.3 Editing ~ | |||
*_.* | |||
_. Fully qualify the unqualified name under the cursor | |||
selecting full qualifications from a popup menu (GUI) | |||
or via command-line completion (CLI). | |||
*_iq* *_i* | |||
_i _iq Add 'import [qualified] <module>(<identifier>)' | |||
statement for the identifier under the cursor, | |||
selecting fully qualified modules from a popup menu | |||
(GUI) or via command-line completion (CLI), if the | |||
identifier is not qualified. This currently adds one | |||
import statement per call instead of merging into | |||
existing import statements. | |||
*_iqm* *_im* | |||
_im Add 'import [qualified] <module>' statement for the | |||
identifier under the cursor, selecting fully qualified | |||
modules from a popup menu (GUI) or via command-line | |||
completion (CLI), if the identifier is not qualified. | |||
This currently adds one import statement per call | |||
instead of merging into existing import statements. | |||
*_ie* | |||
_ie On an 'import <module>' line, in a correctly loadable | |||
module, temporarily comment out import and use :make | |||
'not in scope' errors to explicitly list imported | |||
identifiers. | |||
*haskellmode-XU* *haskellmode-user-completion* | |||
CTRL-X CTRL-U User-defined insert mode name completion based on all | |||
names known to the Haddock index, including package | |||
names. Completions are presented in a popup menu which | |||
also displays the fully qualified module from which | |||
each entry may be imported. | |||
CamelCode shortcuts are supported, meaning that | |||
lower-case letters can be elided, using only | |||
upper-case letters and module qualifier separators (.) | |||
for disambiguation: | |||
pSL -> putStrLn | |||
C.E.t -> Control.Exception.t | |||
C.M.MP -> Control.Monad.MonadPlus | |||
To reduce unwanted matches, the first letter of such | |||
shortcuts and the first letter after each '.' have to | |||
match directly. | |||
============================================================================== | |||
*haskellmode-hpaste* *hpaste* | |||
5. Hpaste Integration ~ | |||
This experimental feature allows browsing and posting to | |||
http://hpaste.org, a Web-based pastebin tailored for Haskell code. | |||
*:HpasteIndex* | |||
:HpasteIndex Read the most recent entries from hpaste.org. Show an | |||
index of the entries in a new buffer, where ',r' will | |||
open the current highlighted entry [and ',p' will | |||
annotate it with the current buffer]. | |||
*:HpastePostNew* | |||
:HpastePostNew Submit current buffer as a new hpaste entry. | |||
[This, and ',p' above, are temporarily disabled, | |||
needs update to new hpaste.org layout] | |||
============================================================================== | |||
*haskellmode-resources* | |||
6. Additional Resources ~ | |||
An quick screencast tour through of these plugins is available at: | |||
http://projects.haskell.org/haskellmode-vim/screencasts.html | |||
Other Haskell-related Vim plugins can be found here: | |||
http://www.haskell.org/haskellwiki/Libraries_and_tools/Program_development#Vim | |||
Make sure to read about Vim's other program-editing features in its online | |||
|user-manual|. Also have a look at Vim tips and plugins at www.vim.org - | |||
two other plugins I tend to use when editing Haskell are AlignPlugin.vim | |||
(to line up regexps for definitions, keywords, comments, etc. in | |||
consecutive lines) and surround.vim (to surround text with quotes, | |||
brackets, parentheses, comments, etc.). | |||
============================================================================== | |||
vim:tw=78:ts=8:ft=help: |
@@ -0,0 +1,56 @@ | |||
:DocIndex haskellmode.txt /*:DocIndex* | |||
:DocSettings haskellmode.txt /*:DocSettings* | |||
:ExportDocIndex haskellmode.txt /*:ExportDocIndex* | |||
:FlagReference haskellmode.txt /*:FlagReference* | |||
:GHCReload haskellmode.txt /*:GHCReload* | |||
:GHCStaticOptions haskellmode.txt /*:GHCStaticOptions* | |||
:GHCi haskellmode.txt /*:GHCi* | |||
:HpasteIndex haskellmode.txt /*:HpasteIndex* | |||
:HpastePostNew haskellmode.txt /*:HpastePostNew* | |||
:IDoc haskellmode.txt /*:IDoc* | |||
:MDoc haskellmode.txt /*:MDoc* | |||
_. haskellmode.txt /*_.* | |||
_? haskellmode.txt /*_?* | |||
_?1 haskellmode.txt /*_?1* | |||
_?2 haskellmode.txt /*_?2* | |||
_T haskellmode.txt /*_T* | |||
_ct haskellmode.txt /*_ct* | |||
_i haskellmode.txt /*_i* | |||
_ie haskellmode.txt /*_ie* | |||
_im haskellmode.txt /*_im* | |||
_iq haskellmode.txt /*_iq* | |||
_iqm haskellmode.txt /*_iqm* | |||
_lang haskellmode.txt /*_lang* | |||
_opt haskellmode.txt /*_opt* | |||
_si haskellmode.txt /*_si* | |||
_t haskellmode.txt /*_t* | |||
compiler-ghc haskellmode.txt /*compiler-ghc* | |||
g:ghc haskellmode.txt /*g:ghc* | |||
g:haddock_browser haskellmode.txt /*g:haddock_browser* | |||
g:haddock_browser_callformat haskellmode.txt /*g:haddock_browser_callformat* | |||
g:haddock_docdir haskellmode.txt /*g:haddock_docdir* | |||
g:haddock_indexfiledir haskellmode.txt /*g:haddock_indexfiledir* | |||
g:wget haskellmode.txt /*g:wget* | |||
ghc haskellmode.txt /*ghc* | |||
ghc-compiler haskellmode.txt /*ghc-compiler* | |||
haddock haskellmode.txt /*haddock* | |||
haskellmode haskellmode.txt /*haskellmode* | |||
haskellmode-XO haskellmode.txt /*haskellmode-XO* | |||
haskellmode-XU haskellmode.txt /*haskellmode-XU* | |||
haskellmode-compiler haskellmode.txt /*haskellmode-compiler* | |||
haskellmode-editing haskellmode.txt /*haskellmode-editing* | |||
haskellmode-haddock haskellmode.txt /*haskellmode-haddock* | |||
haskellmode-hpaste haskellmode.txt /*haskellmode-hpaste* | |||
haskellmode-indexing haskellmode.txt /*haskellmode-indexing* | |||
haskellmode-lookup haskellmode.txt /*haskellmode-lookup* | |||
haskellmode-omni-completion haskellmode.txt /*haskellmode-omni-completion* | |||
haskellmode-overview haskellmode.txt /*haskellmode-overview* | |||
haskellmode-quickref haskellmode.txt /*haskellmode-quickref* | |||
haskellmode-requirements haskellmode.txt /*haskellmode-requirements* | |||
haskellmode-resources haskellmode.txt /*haskellmode-resources* | |||
haskellmode-settings haskellmode.txt /*haskellmode-settings* | |||
haskellmode-settings-fine haskellmode.txt /*haskellmode-settings-fine* | |||
haskellmode-settings-main haskellmode.txt /*haskellmode-settings-main* | |||
haskellmode-user-completion haskellmode.txt /*haskellmode-user-completion* | |||
haskellmode.txt haskellmode.txt /*haskellmode.txt* | |||
hpaste haskellmode.txt /*hpaste* |
@@ -0,0 +1,2 @@ | |||
au BufRead,BufNewFile .config/uzbl/* set filetype=uzbl | |||
au BufRead,BufNewFile */uzbl/config set filetype=uzbl |
@@ -0,0 +1,14 @@ | |||
" | |||
" general Haskell source settings | |||
" (shared functions are in autoload/haskellmode.vim) | |||
" | |||
" (Claus Reinke, last modified: 28/04/2009) | |||
" | |||
" part of haskell plugins: http://projects.haskell.org/haskellmode-vim | |||
" please send patches to <claus.reinke@talk21.com> | |||
" try gf on import line, or ctrl-x ctrl-i, or [I, [i, .. | |||
setlocal include=^import\\s*\\(qualified\\)\\?\\s* | |||
setlocal includeexpr=substitute(v:fname,'\\.','/','g').'.' | |||
setlocal suffixesadd=hs,lhs,hsc | |||
@@ -0,0 +1,816 @@ | |||
" | |||
" use haddock docs and index files | |||
" show documentation, complete & qualify identifiers | |||
" | |||
" (Claus Reinke; last modified: 17/06/2009) | |||
" | |||
" part of haskell plugins: http://projects.haskell.org/haskellmode-vim | |||
" please send patches to <claus.reinke@talk21.com> | |||
" :Doc <name> and :IDoc <name> open haddocks for <name> in opera | |||
" | |||
" :Doc needs qualified name (default Prelude) and package (default base) | |||
" :IDoc needs unqualified name, looks up possible links in g:haddock_index | |||
" | |||
" :DocIndex populates g:haddock_index from haddock's index files | |||
" :ExportDocIndex saves g:haddock_index to cache file | |||
" :ImportDocIndex reloads g:haddock_index from cache file | |||
" | |||
" all the following use the haddock index (g:haddock_index) | |||
" | |||
" _? opens haddocks for unqualified name under cursor, | |||
" suggesting alternative full qualifications in popup menu | |||
" | |||
" _. fully qualifies unqualified name under cursor, | |||
" suggesting alternative full qualifications in popup menu | |||
" | |||
" _i add import <module>(<name>) statement for unqualified <name> under cursor, | |||
" _im add import <module> statement for unqualified <name> under cursor, | |||
" suggesting alternative full qualifications in popup menu | |||
" (this currently adds one statement per call, instead of | |||
" merging into existing import statements, but it's a start;-) | |||
" | |||
" CTRL-X CTRL-U (user-defined insert mode completion) | |||
" suggests completions of unqualified names in popup menu | |||
let s:scriptname = "haskell_doc.vim" | |||
" script parameters | |||
" g:haddock_browser *mandatory* which browser to call | |||
" g:haddock_browser_callformat [optional] how to call browser | |||
" g:haddock_indexfiledir [optional] where to put 'haddock_index.vim' | |||
" g:haddock_docdir [optional] where to find html docs | |||
" g:ghc [optional] which ghc to call | |||
" g:ghc_pkg [optional] which ghc_pkg to call | |||
" been here before? | |||
if exists("g:haddock_index") | |||
finish | |||
endif | |||
" initialise nested dictionary, to be populated | |||
" - from haddock index files via :DocIndex | |||
" - from previous cached version via :ImportDocIndex | |||
let g:haddock_index = {} | |||
" initialise dictionary, mapping modules with haddocks to their packages, | |||
" populated via MkHaddockModuleIndex() or HaveModuleIndex() | |||
let g:haddock_moduleindex = {} | |||
" program to open urls, please set this in your vimrc | |||
"examples (for windows): | |||
"let g:haddock_browser = "C:/Program Files/Opera/Opera.exe" | |||
"let g:haddock_browser = "C:/Program Files/Mozilla Firefox/firefox.exe" | |||
"let g:haddock_browser = "C:/Program Files/Internet Explorer/IEXPLORE.exe" | |||
if !exists("g:haddock_browser") | |||
echoerr s:scriptname." WARNING: please set g:haddock_browser!" | |||
endif | |||
if !haskellmode#GHC() | finish | endif | |||
if (!exists("g:ghc_pkg") || !executable(g:ghc_pkg)) | |||
let g:ghc_pkg = substitute(g:ghc,'\(.*\)ghc','\1ghc-pkg','') | |||
endif | |||
if exists("g:haddock_docdir") && isdirectory(g:haddock_docdir) | |||
let s:docdir = g:haddock_docdir | |||
elseif executable(g:ghc_pkg) | |||
" try to figure out location of html docs | |||
" first choice: where the base docs are (from the first base listed) | |||
let [field;x] = split(system(g:ghc_pkg . ' field base haddock-html'),'\n') | |||
" path changes in ghc-6.12.* | |||
" let field = substitute(field,'haddock-html: \(.*\)libraries.base','\1','') | |||
let field = substitute(field,'haddock-html: \(.*\)lib\(raries\)\?.base.*$','\1','') | |||
let field = substitute(field,'\\','/','g') | |||
" let alternate = substitute(field,'html','doc/html','') | |||
" changes for ghc-6.12.*: check for doc/html/ first | |||
let alternate = field.'doc/html/' | |||
if isdirectory(alternate) | |||
let s:docdir = alternate | |||
elseif isdirectory(field) | |||
let s:docdir = field | |||
endif | |||
else | |||
echoerr s:scriptname." can't find ghc-pkg (set g:ghc_pkg ?)." | |||
endif | |||
" second choice: try some known suspects for windows/unix | |||
if !exists('s:docdir') || !isdirectory(s:docdir) | |||
let s:ghc_libdir = substitute(system(g:ghc . ' --print-libdir'),'\n','','') | |||
let location1a = s:ghc_libdir . '/doc/html/' | |||
let location1b = s:ghc_libdir . '/doc/' | |||
let location2 = '/usr/share/doc/ghc-' . haskellmode#GHC_Version() . '/html/' | |||
if isdirectory(location1a) | |||
let s:docdir = location1a | |||
elseif isdirectory(location1b) | |||
let s:docdir = location1b | |||
elseif isdirectory(location2) | |||
let s:docdir = location2 | |||
else " give up | |||
echoerr s:scriptname." can't find locaton of html documentation (set g:haddock_docdir)." | |||
finish | |||
endif | |||
endif | |||
" todo: can we turn s:docdir into a list of paths, and | |||
" include docs for third-party libs as well? | |||
let s:libraries = s:docdir . 'libraries/' | |||
let s:guide = s:docdir . 'users_guide/' | |||
let s:index = 'index.html' | |||
if exists("g:haddock_indexfiledir") && filewritable(g:haddock_indexfiledir) | |||
let s:haddock_indexfiledir = g:haddock_indexfiledir | |||
elseif filewritable(s:libraries) | |||
let s:haddock_indexfiledir = s:libraries | |||
elseif filewritable($HOME) | |||
let s:haddock_indexfiledir = $HOME.'/' | |||
else "give up | |||
echoerr s:scriptname." can't locate index file. please set g:haddock_indexfiledir" | |||
finish | |||
endif | |||
let s:haddock_indexfile = s:haddock_indexfiledir . 'haddock_index.vim' | |||
" different browser setups require different call formats; | |||
" you might want to call the browser synchronously or | |||
" asynchronously, and the latter is os-dependent; | |||
" | |||
" by default, the browser is started in the background when on | |||
" windows or if running in a gui, and in the foreground otherwise | |||
" (eg, console-mode for remote sessions, with text-mode browsers). | |||
" | |||
" you can override these defaults in your vimrc, via a format | |||
" string including 2 %s parameters (the first being the browser | |||
" to call, the second being the url). | |||
if !exists("g:haddock_browser_callformat") | |||
if has("win32") || has("win64") | |||
let g:haddock_browser_callformat = 'start %s "%s"' | |||
else | |||
if has("gui_running") | |||
let g:haddock_browser_callformat = '%s %s '.printf(&shellredir,'/dev/null').' &' | |||
else | |||
let g:haddock_browser_callformat = '%s %s' | |||
endif | |||
endif | |||
endif | |||
" allow map leader override | |||
if !exists("maplocalleader") | |||
let maplocalleader='_' | |||
endif | |||
command! DocSettings call DocSettings() | |||
function! DocSettings() | |||
for v in ["g:haddock_browser","g:haddock_browser_callformat","g:haddock_docdir","g:haddock_indexfiledir","s:ghc_libdir","g:ghc_version","s:docdir","s:libraries","s:guide","s:haddock_indexfile"] | |||
if exists(v) | |||
echo v '=' eval(v) | |||
else | |||
echo v '=' | |||
endif | |||
endfor | |||
endfunction | |||
function! DocBrowser(url) | |||
"echomsg "DocBrowser(".url.")" | |||
if (!exists("g:haddock_browser") || !executable(g:haddock_browser)) | |||
echoerr s:scriptname." can't find documentation browser. please set g:haddock_browser" | |||
return | |||
endif | |||
" start browser to open url, according to specified format | |||
let url = a:url=~'^\(file://\|http://\)' ? a:url : 'file://'.a:url | |||
silent exe '!'.printf(g:haddock_browser_callformat,g:haddock_browser,escape(url,'#%')) | |||
endfunction | |||
"Doc/Doct are an old interface for documentation lookup | |||
"(that is the reason they are not documented!-) | |||
" | |||
"These uses are still fine at the moment, and are the reason | |||
"that this command still exists at all | |||
" | |||
" :Doc -top | |||
" :Doc -libs | |||
" :Doc -guide | |||
" | |||
"These uses may or may not work, and shouldn't be relied on anymore | |||
"(usually, you want _?/_?1/_?2 or :MDoc; there is also :IDoc) | |||
" | |||
" :Doc length | |||
" :Doc Control.Monad.when | |||
" :Doc Data.List. | |||
" :Doc Control.Monad.State.runState mtl | |||
command! -nargs=+ Doc call Doc('v',<f-args>) | |||
command! -nargs=+ Doct call Doc('t',<f-args>) | |||
function! Doc(kind,qualname,...) | |||
let suffix = '.html' | |||
let relative = '#'.a:kind.'%3A' | |||
if a:qualname=="-top" | |||
call DocBrowser(s:docdir . s:index) | |||
return | |||
elseif a:qualname=="-libs" | |||
call DocBrowser(s:libraries . s:index) | |||
return | |||
elseif a:qualname=="-guide" | |||
call DocBrowser(s:guide . s:index) | |||
return | |||
endif | |||
if a:0==0 " no package specified | |||
let package = 'base/' | |||
else | |||
let package = a:1 . '/' | |||
endif | |||
if match(a:qualname,'\.')==-1 " unqualified name | |||
let [qual,name] = [['Prelude'],a:qualname] | |||
let file = join(qual,'-') . suffix . relative . name | |||
elseif a:qualname[-1:]=='.' " module qualifier only | |||
let parts = split(a:qualname,'\.') | |||
let quallen = len(parts)-1 | |||
let [qual,name] = [parts[0:quallen],parts[-1]] | |||
let file = join(qual,'-') . suffix | |||
else " qualified name | |||
let parts = split(a:qualname,'\.') | |||
let quallen = len(parts)-2 | |||
let [qual,name] = [parts[0:quallen],parts[-1]] | |||
let file = join(qual,'-') . suffix . relative . name | |||
endif | |||
let path = s:libraries . package . file | |||
call DocBrowser(path) | |||
endfunction | |||
" TODO: add commandline completion for :IDoc | |||
" switch to :emenu instead of inputlist? | |||
" indexed variant of Doc, looking up links in g:haddock_index | |||
" usage: | |||
" 1. :IDoc length | |||
" 2. click on one of the choices, or select by number (starting from 0) | |||
command! -nargs=+ IDoc call IDoc(<f-args>) | |||
function! IDoc(name,...) | |||
let choices = HaddockIndexLookup(a:name) | |||
if choices=={} | return | endif | |||
if a:0==0 | |||
let keylist = map(deepcopy(keys(choices)),'substitute(v:val,"\\[.\\]","","")') | |||
let choice = inputlist(keylist) | |||
else | |||
let choice = a:1 | |||
endif | |||
let path = values(choices)[choice] " assumes same order for keys/values.. | |||
call DocBrowser(path) | |||
endfunction | |||
let s:flagref = s:guide . 'flag-reference.html' | |||
if filereadable(s:flagref) | |||
" extract the generated fragment ids for the | |||
" flag reference sections | |||
let s:headerPat = '.\{-}<h3 class="title"><a name="\([^"]*\)"><\/a>\([^<]*\)<\/h3>\(.*\)' | |||
let s:flagheaders = [] | |||
let s:flagheaderids = {} | |||
let s:contents = join(readfile(s:flagref)) | |||
let s:ml = matchlist(s:contents,s:headerPat) | |||
while s:ml!=[] | |||
let [_,s:id,s:title,s:r;s:x] = s:ml | |||
let s:flagheaders = add(s:flagheaders, s:title) | |||
let s:flagheaderids[s:title] = s:id | |||
let s:ml = matchlist(s:r,s:headerPat) | |||
endwhile | |||
command! -nargs=1 -complete=customlist,CompleteFlagHeaders FlagReference call FlagReference(<f-args>) | |||
function! FlagReference(section) | |||
let relativeUrl = a:section==""||!exists("s:flagheaderids['".a:section."']") ? | |||
\ "" : "#".s:flagheaderids[a:section] | |||
call DocBrowser(s:flagref.relativeUrl) | |||
endfunction | |||
function! CompleteFlagHeaders(al,cl,cp) | |||
let s:choices = s:flagheaders | |||
return CompleteAux(a:al,a:cl,a:cp) | |||
endfunction | |||
endif | |||
command! -nargs=1 -complete=customlist,CompleteHaddockModules MDoc call MDoc(<f-args>) | |||
function! MDoc(module) | |||
let suffix = '.html' | |||
call HaveModuleIndex() | |||
if !has_key(g:haddock_moduleindex,a:module) | |||
echoerr a:module 'not found in haddock module index' | |||
return | |||
endif | |||
let package = g:haddock_moduleindex[a:module]['package'] | |||
let file = substitute(a:module,'\.','-','g') . suffix | |||
" let path = s:libraries . package . '/' . file | |||
let path = g:haddock_moduleindex[a:module]['html'] | |||
call DocBrowser(path) | |||
endfunction | |||
function! CompleteHaddockModules(al,cl,cp) | |||
call HaveModuleIndex() | |||
let s:choices = keys(g:haddock_moduleindex) | |||
return CompleteAux(a:al,a:cl,a:cp) | |||
endfunction | |||
" create a dictionary g:haddock_index, containing the haddoc index | |||
command! DocIndex call DocIndex() | |||
function! DocIndex() | |||
let files = split(globpath(s:libraries,'doc-index*.html'),'\n') | |||
let g:haddock_index = {} | |||
call ProcessHaddockIndexes2(s:libraries,files) | |||
if haskellmode#GHC_VersionGE([6,8,2]) | |||
if &shell =~ 'sh' " unix-type shell | |||
let s:addon_libraries = split(system(g:ghc_pkg . ' field \* haddock-html'),'\n') | |||
else " windows cmd.exe and the like | |||
let s:addon_libraries = split(system(g:ghc_pkg . ' field * haddock-html'),'\n') | |||
endif | |||
for addon in s:addon_libraries | |||
let ml = matchlist(addon,'haddock-html: \("\)\?\(file:///\)\?\([^"]*\)\("\)\?') | |||
if ml!=[] | |||
let [_,quote,file,addon_path;x] = ml | |||
let addon_path = substitute(addon_path,'\(\\\\\|\\\)','/','g') | |||
let addon_files = split(globpath(addon_path,'doc-index*.html'),'\n') | |||
call ProcessHaddockIndexes2(addon_path,addon_files) | |||
endif | |||
endfor | |||
endif | |||
return 1 | |||
endfunction | |||
function! ProcessHaddockIndexes(location,files) | |||
let entryPat= '.\{-}"indexentry"[^>]*>\([^<]*\)<\(\%([^=]\{-}TD CLASS="\%(indexentry\)\@!.\{-}</TD\)*\)[^=]\{-}\(\%(="indexentry\|TABLE\).*\)' | |||
let linkPat = '.\{-}HREF="\([^"]*\)".>\([^<]*\)<\(.*\)' | |||
redraw | |||
echo 'populating g:haddock_index from haddock index files in ' a:location | |||
for f in a:files | |||
echo f[len(a:location):] | |||
let contents = join(readfile(f)) | |||
let ml = matchlist(contents,entryPat) | |||
while ml!=[] | |||
let [_,entry,links,r;x] = ml | |||
"echo entry links | |||
let ml2 = matchlist(links,linkPat) | |||
let link = {} | |||
while ml2!=[] | |||
let [_,l,m,links;x] = ml2 | |||
"echo l m | |||
let link[m] = a:location . '/' . l | |||
let ml2 = matchlist(links,linkPat) | |||
endwhile | |||
let g:haddock_index[DeHTML(entry)] = deepcopy(link) | |||
"echo entry g:haddock_index[entry] | |||
let ml = matchlist(r,entryPat) | |||
endwhile | |||
endfor | |||
endfunction | |||
" concatenating all lines is too slow for a big file, process lines directly | |||
function! ProcessHaddockIndexes2(location,files) | |||
let entryPat= '^>\([^<]*\)</' | |||
let linkPat = '.\{-}A HREF="\([^"]*\)"' | |||
let kindPat = '#\(.\)' | |||
" redraw | |||
echo 'populating g:haddock_index from haddock index files in ' a:location | |||
for f in a:files | |||
echo f[len(a:location):] | |||
let isEntry = 0 | |||
let isLink = '' | |||
let link = {} | |||
let entry = '' | |||
for line in readfile(f) | |||
if line=~'CLASS="indexentry' | |||
if (link!={}) && (entry!='') | |||
if has_key(g:haddock_index,DeHTML(entry)) | |||
let dict = extend(g:haddock_index[DeHTML(entry)],deepcopy(link)) | |||
else | |||
let dict = deepcopy(link) | |||
endif | |||
let g:haddock_index[DeHTML(entry)] = dict | |||
let link = {} | |||
let entry = '' | |||
endif | |||
let isEntry=1 | |||
continue | |||
endif | |||
if isEntry==1 | |||
let ml = matchlist(line,entryPat) | |||
if ml!=[] | let [_,entry;x] = ml | let isEntry=0 | continue | endif | |||
endif | |||
if entry!='' | |||
let ml = matchlist(line,linkPat) | |||
if ml!=[] | let [_,isLink;x]=ml | continue | endif | |||
endif | |||
if isLink!='' | |||
let ml = matchlist(line,entryPat) | |||
if ml!=[] | |||
let [_,module;x] = ml | |||
let [_,kind;x] = matchlist(isLink,kindPat) | |||
let last = a:location[strlen(a:location)-1] | |||
let link[module."[".kind."]"] = a:location . (last=='/'?'':'/') . isLink | |||
let isLink='' | |||
continue | |||
endif | |||
endif | |||
endfor | |||
if link!={} | |||
if has_key(g:haddock_index,DeHTML(entry)) | |||
let dict = extend(g:haddock_index[DeHTML(entry)],deepcopy(link)) | |||
else | |||
let dict = deepcopy(link) | |||
endif | |||
let g:haddock_index[DeHTML(entry)] = dict | |||
endif | |||
endfor | |||
endfunction | |||
command! ExportDocIndex call ExportDocIndex() | |||
function! ExportDocIndex() | |||
call HaveIndex() | |||
let entries = [] | |||
for key in keys(g:haddock_index) | |||
let entries += [key,string(g:haddock_index[key])] | |||
endfor | |||
call writefile(entries,s:haddock_indexfile) | |||
redir end | |||
endfunction | |||
command! ImportDocIndex call ImportDocIndex() | |||
function! ImportDocIndex() | |||
if filereadable(s:haddock_indexfile) | |||
let lines = readfile(s:haddock_indexfile) | |||
let i=0 | |||
while i<len(lines) | |||
let [key,dict] = [lines[i],lines[i+1]] | |||
sandbox let g:haddock_index[key] = eval(dict) | |||
let i+=2 | |||
endwhile | |||
return 1 | |||
else | |||
return 0 | |||
endif | |||
endfunction | |||
function! HaveIndex() | |||
return (g:haddock_index!={} || ImportDocIndex() || DocIndex() ) | |||
endfunction | |||
function! MkHaddockModuleIndex() | |||
let g:haddock_moduleindex = {} | |||
call HaveIndex() | |||
for key in keys(g:haddock_index) | |||
let dict = g:haddock_index[key] | |||
for module in keys(dict) | |||
let html = dict[module] | |||
let html = substitute(html ,'#.*$','','') | |||
let module = substitute(module,'\[.\]','','') | |||
let ml = matchlist(html,'libraries/\([^\/]*\)[\/]') | |||
if ml!=[] | |||
let [_,package;x] = ml | |||
let g:haddock_moduleindex[module] = {'package':package,'html':html} | |||
endif | |||
let ml = matchlist(html,'/\([^\/]*\)\/html/[A-Z]') | |||
if ml!=[] | |||
let [_,package;x] = ml | |||
let g:haddock_moduleindex[module] = {'package':package,'html':html} | |||
endif | |||
endfor | |||
endfor | |||
endfunction | |||
function! HaveModuleIndex() | |||
return (g:haddock_moduleindex!={} || MkHaddockModuleIndex() ) | |||
endfunction | |||
" decode HTML symbol encodings (are these all we need?) | |||
function! DeHTML(entry) | |||
let res = a:entry | |||
let decode = { '<': '<', '>': '>', '&': '\\&' } | |||
for enc in keys(decode) | |||
exe 'let res = substitute(res,"'.enc.'","'.decode[enc].'","g")' | |||
endfor | |||
return res | |||
endfunction | |||
" find haddocks for word under cursor | |||
" also lists possible definition sites | |||
" - needs to work for both qualified and unqualified items | |||
" - for 'import qualified M as A', consider M.item as source of A.item | |||
" - offer sources from both type [t] and value [v] namespaces | |||
" - for unqualified items, list all possible sites | |||
" - for qualified items, list imported sites only | |||
" keep track of keys with and without namespace tags: | |||
" the former are needed for lookup, the latter for matching against source | |||
map <LocalLeader>? :call Haddock()<cr> | |||
function! Haddock() | |||
amenu ]Popup.- :echo '-'<cr> | |||
aunmenu ]Popup | |||
let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),0) | |||
if namsym==[] | |||
redraw | |||
echo 'no name/symbol under cursor!' | |||
return 0 | |||
endif | |||
let [start,symb,qual,unqual] = namsym | |||
let imports = haskellmode#GatherImports() | |||
let asm = has_key(imports[1],qual) ? imports[1][qual]['modules'] : [] | |||
let name = unqual | |||
let dict = HaddockIndexLookup(name) | |||
if dict=={} | return | endif | |||
" for qualified items, narrow results to possible imports that provide qualifier | |||
let filteredKeys = filter(copy(keys(dict)) | |||
\ ,'match(asm,substitute(v:val,''\[.\]'','''',''''))!=-1') | |||
let keys = (qual!='') ? filteredKeys : keys(dict) | |||
if (keys==[]) && (qual!='') | |||
echoerr qual.'.'.unqual.' not found in imports' | |||
return 0 | |||
endif | |||
" use 'setlocal completeopt+=menuone' if you always want to see menus before | |||
" anything happens (I do, but many users don't..) | |||
if len(keys)==1 && (&completeopt!~'menuone') | |||
call DocBrowser(dict[keys[0]]) | |||
elseif has("gui_running") | |||
for key in keys | |||
exe 'amenu ]Popup.'.escape(key,'\.').' :call DocBrowser('''.dict[key].''')<cr>' | |||
endfor | |||
popup ]Popup | |||
else | |||
let s:choices = keys | |||
let key = input('browse docs for '.name.' in: ','','customlist,CompleteAux') | |||
if key!='' | |||
call DocBrowser(dict[key]) | |||
endif | |||
endif | |||
endfunction | |||
if !exists("g:haskell_search_engines") | |||
let g:haskell_search_engines = | |||
\ {'hoogle':'http://www.haskell.org/hoogle/?hoogle=%s' | |||
\ ,'hayoo!':'http://holumbus.fh-wedel.de/hayoo/hayoo.html?query=%s' | |||
\ } | |||
endif | |||
map <LocalLeader>?? :let es=g:haskell_search_engines | |||
\ \|echo "g:haskell_search_engines" | |||
\ \|for e in keys(es) | |||
\ \|echo e.' : '.es[e] | |||
\ \|endfor<cr> | |||
map <LocalLeader>?1 :call HaskellSearchEngine('hoogle')<cr> | |||
map <LocalLeader>?2 :call HaskellSearchEngine('hayoo!')<cr> | |||
" query one of the Haskell search engines for the thing under cursor | |||
" - unqualified symbols need to be url-escaped | |||
" - qualified ids need to be fed as separate qualifier and id for | |||
" both hoogle (doesn't handle qualified symbols) and hayoo! (no qualified | |||
" ids at all) | |||
" - qualified ids referring to import-qualified-as qualifiers need to be | |||
" translated to the multi-module searches over the list of original modules | |||
function! HaskellSearchEngine(engine) | |||
amenu ]Popup.- :echo '-'<cr> | |||
aunmenu ]Popup | |||
let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),0) | |||
if namsym==[] | |||
redraw | |||
echo 'no name/symbol under cursor!' | |||
return 0 | |||
endif | |||
let [start,symb,qual,unqual] = namsym | |||
let imports = haskellmode#GatherImports() | |||
let asm = has_key(imports[1],qual) ? imports[1][qual]['modules'] : [] | |||
let unqual = haskellmode#UrlEncode(unqual) | |||
if a:engine=='hoogle' | |||
let name = asm!=[] ? unqual.'+'.join(map(copy(asm),'"%2B".v:val'),'+') | |||
\ : qual!='' ? unqual.'+'.haskellmode#UrlEncode('+').qual | |||
\ : unqual | |||
elseif a:engine=='hayoo!' | |||
let name = asm!=[] ? unqual.'+module:('.join(copy(asm),' OR ').')' | |||
\ : qual!='' ? unqual.'+module:'.qual | |||
\ : unqual | |||
else | |||
let name = qual=="" ? unqual : qual.".".unqual | |||
endif | |||
if has_key(g:haskell_search_engines,a:engine) | |||
call DocBrowser(printf(g:haskell_search_engines[a:engine],name)) | |||
else | |||
echoerr "unknown search engine: ".a:engine | |||
endif | |||
endfunction | |||
" used to pass on choices to CompleteAux | |||
let s:choices=[] | |||
" if there's no gui, use commandline completion instead of :popup | |||
" completion function CompleteAux suggests completions for a:al, wrt to s:choices | |||
function! CompleteAux(al,cl,cp) | |||
"echomsg '|'.a:al.'|'.a:cl.'|'.a:cp.'|' | |||
let res = [] | |||
let l = len(a:al)-1 | |||
for r in s:choices | |||
if l==-1 || r[0 : l]==a:al | |||
let res += [r] | |||
endif | |||
endfor | |||
return res | |||
endfunction | |||
" CamelCase shorthand matching: | |||
" favour upper-case letters and module qualifier separators (.) for disambiguation | |||
function! CamelCase(shorthand,string) | |||
let s1 = a:shorthand | |||
let s2 = a:string | |||
let notFirst = 0 " don't elide before first pattern letter | |||
while ((s1!="")&&(s2!="")) | |||
let head1 = s1[0] | |||
let head2 = s2[0] | |||
let elide = notFirst && ( ((head1=~'[A-Z]') && (head2!~'[A-Z.]')) | |||
\ ||((head1=='.') && (head2!='.')) ) | |||
if elide | |||
let s2=s2[1:] | |||
elseif (head1==head2) | |||
let s1=s1[1:] | |||
let s2=s2[1:] | |||
else | |||
return 0 | |||
endif | |||
let notFirst = (head1!='.')||(head2!='.') " treat separators as new beginnings | |||
endwhile | |||
return (s1=="") | |||
endfunction | |||
" use haddock name index for insert mode completion (CTRL-X CTRL-U) | |||
function! CompleteHaddock(findstart, base) | |||
if a:findstart | |||
let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),-1) " insert-mode: we're 1 beyond the text | |||
if namsym==[] | |||
redraw | |||
echo 'no name/symbol under cursor!' | |||
return -1 | |||
endif | |||
let [start,symb,qual,unqual] = namsym | |||
return (start-1) | |||
else " find keys matching with "a:base" | |||
let res = [] | |||
let l = len(a:base)-1 | |||
let qual = a:base =~ '^[A-Z][a-zA-Z0-9_'']*\(\.[A-Z][a-zA-Z0-9_'']*\)*\(\.[a-zA-Z0-9_'']*\)\?$' | |||
call HaveIndex() | |||
for key in keys(g:haddock_index) | |||
let keylist = map(deepcopy(keys(g:haddock_index[key])),'substitute(v:val,"\\[.\\]","","")') | |||
if (key[0 : l]==a:base) | |||
for m in keylist | |||
let res += [{"word":key,"menu":m,"dup":1}] | |||
endfor | |||
elseif qual " this tends to be slower | |||
for m in keylist | |||
let word = m . '.' . key | |||
if word[0 : l]==a:base | |||
let res += [{"word":word,"menu":m,"dup":1}] | |||
endif | |||
endfor | |||
endif | |||
endfor | |||
if res==[] " no prefix matches, try CamelCase shortcuts | |||
for key in keys(g:haddock_index) | |||
let keylist = map(deepcopy(keys(g:haddock_index[key])),'substi |