4 " Maintainer: Claus Reinke <claus.reinke@talk21.com>
5 " Last Change: 22/06/2010
7 " part of haskell plugins: http://projects.haskell.org/haskellmode-vim
9 " ------------------------------ paths & quickfix settings first
12 if exists("current_compiler") && current_compiler == "ghc"
15 let current_compiler = "ghc"
17 let s:scriptname = "ghc.vim"
19 if !haskellmode#GHC() | finish | endif
20 if (!exists("b:ghc_staticoptions"))
21 let b:ghc_staticoptions = ''
24 " set makeprg (for quickfix mode)
25 execute 'setlocal makeprg=' . g:ghc . '\ ' . escape(b:ghc_staticoptions,' ') .'\ -e\ :q\ %'
26 "execute 'setlocal makeprg=' . g:ghc .'\ -e\ :q\ %'
27 "execute 'setlocal makeprg=' . g:ghc .'\ --make\ %'
30 " fetch file/line-info from error message
31 " TODO: how to distinguish multiline errors from warnings?
32 " (both have the same header, and errors have no common id-tag)
33 " how to get rid of first empty message in result list?
36 \%W%f:%l:%c:\ Warning:\ %m,
41 \%+C\ \ %#%tarning:\ %m,
43 " oh, wouldn't you guess it - ghc reports (partially) to stderr..
46 " ------------------------- but ghc can do a lot more for us..
49 " allow map leader override
50 if !exists("maplocalleader")
51 let maplocalleader='_'
54 " initialize map of identifiers to their types
55 " associate type map updates to changedtick
56 if !exists("b:ghc_types")
58 let b:my_changedtick = b:changedtick
61 if exists("g:haskell_functions")
64 let g:haskell_functions = "ghc"
66 " avoid hit-enter prompts
69 " edit static GHC options
70 " TODO: add completion for options/packages?
71 command! GHCStaticOptions call GHC_StaticOptions()
72 function! GHC_StaticOptions()
73 let b:ghc_staticoptions = input('GHC static options: ',b:ghc_staticoptions)
74 execute 'setlocal makeprg=' . g:ghc . '\ ' . escape(b:ghc_staticoptions,' ') .'\ -e\ :q\ %'
75 let b:my_changedtick -=1
78 map <LocalLeader>T :call GHC_ShowType(1)<cr>
79 map <LocalLeader>t :call GHC_ShowType(0)<cr>
80 function! GHC_ShowType(addTypeDecl)
81 let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),0)
84 echo 'no name/symbol under cursor!'
87 let [_,symb,qual,unqual] = namsym
88 let name = qual=='' ? unqual : qual.'.'.unqual
89 let pname = ( symb ? '('.name.')' : name )
91 if !has_key(b:ghc_types,name)
93 echo pname "type not known"
96 for type in split(b:ghc_types[name],' -- ')
99 call append( line(".")-1, pname . " :: " . type )
105 " show type of identifier under mouse pointer in balloon
106 " TODO: it isn't a good idea to tie potentially time-consuming tasks
107 " (querying GHCi for the types) to cursor movements (#14). Currently,
108 " we ask the user to call :GHCReload explicitly. Should there be an
109 " option to reenable the old implicit querying?
110 if has("balloon_eval")
112 set balloonexpr=GHC_TypeBalloon()
113 function! GHC_TypeBalloon()
114 if exists("b:current_compiler") && b:current_compiler=="ghc"
115 let [line] = getbufline(v:beval_bufnr,v:beval_lnum)
116 let namsym = haskellmode#GetNameSymbol(line,v:beval_col,0)
120 let [start,symb,qual,unqual] = namsym
121 let name = qual=='' ? unqual : qual.'.'.unqual
122 let pname = name " ( symb ? '('.name.')' : name )
125 echo "no type information (try :GHGReload)"
126 elseif (b:my_changedtick != b:changedtick)
128 echo "type information may be out of date (try :GHGReload)"
130 " silent call GHC_HaveTypes()
132 if has("balloon_multiline")
133 return (has_key(b:ghc_types,pname) ? split(b:ghc_types[pname],' -- ') : '')
135 return (has_key(b:ghc_types,pname) ? b:ghc_types[pname] : '')
146 map <LocalLeader>si :call GHC_ShowInfo()<cr>
147 function! GHC_ShowInfo()
148 let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),0)
151 echo 'no name/symbol under cursor!'
154 let [_,symb,qual,unqual] = namsym
155 let name = qual=='' ? unqual : (qual.'.'.unqual)
156 let output = GHC_Info(name)
158 setlocal previewwindow
159 setlocal buftype=nofile
167 " fill the type map, unless nothing has changed since the last attempt
168 function! GHC_HaveTypes()
169 if b:ghc_types == {} && (b:my_changedtick != b:changedtick)
170 let b:my_changedtick = b:changedtick
171 return GHC_BrowseAll()
175 " update b:ghc_types after successful make
176 au QuickFixCmdPost make if GHC_CountErrors()==0 | silent call GHC_BrowseAll() | endif
178 " count only error entries in quickfix list, ignoring warnings
179 function! GHC_CountErrors()
181 for e in getqflist() | if e.type=='E' && e.text !~ "^[ \n]*Warning:" | let c+=1 | endif | endfor
185 command! GHCReload call GHC_BrowseAll()
186 function! GHC_BrowseAll()
187 " let imports = haskellmode#GatherImports()
188 " let modules = keys(imports[0]) + keys(imports[1])
189 let b:my_changedtick = b:changedtick
190 let imports = {} " no need for them at the moment
191 let current = GHC_NameCurrent()
192 let module = current==[] ? 'Main' : current[0]
193 if haskellmode#GHC_VersionGE([6,8,1])
194 return GHC_BrowseBangStar(module)
196 return GHC_BrowseMultiple(imports,['*'.module])
200 function! GHC_NameCurrent()
204 let ml = matchlist( getline(l), '^module\s*\([^ (]*\)')
206 let [_,module;x] = ml
212 echo "cannot find module header for file " . expand("%")
216 function! GHC_BrowseBangStar(module)
218 echo "browsing module " a:module
219 let command = ":browse! *" . a:module
220 let orig_shellredir = &shellredir
221 let &shellredir = ">" " ignore error/warning messages, only output or lack of it
222 let output = system(g:ghc . ' ' . b:ghc_staticoptions . ' -v0 --interactive ' . expand("%") , command )
223 let &shellredir = orig_shellredir
224 return GHC_ProcessBang(a:module,output)
227 function! GHC_BrowseMultiple(imports,modules)
229 echo "browsing modules " a:modules
230 let command = ":browse " . join( a:modules, " \n :browse ")
231 let command = substitute(command,'\(:browse \(\S*\)\)','putStrLn "-- \2" \n \1','g')
232 let output = system(g:ghc . ' ' . b:ghc_staticoptions . ' -v0 --interactive ' . expand("%") , command )
233 return GHC_Process(a:imports,output)
236 function! GHC_Info(what)
237 " call GHC_HaveTypes()
238 let output = system(g:ghc . ' ' . b:ghc_staticoptions . ' -v0 --interactive ' . expand("%"), ":info ". a:what)
242 function! GHC_ProcessBang(module,output)
243 let module = a:module
245 let linePat = '^\(.\{-}\)\n\(.*\)'
246 let contPat = '\s\+\(.\{-}\)\n\(.*\)'
247 let typePat = '^\(\)\(\S*\)\s*::\(.*\)'
248 let commentPat = '^-- \(\S*\)'
249 let definedPat = '^-- defined locally'
250 let importedPat = '^-- imported via \(.*\)'
252 echo s:scriptname.": GHCi reports errors (try :make?)"
256 let ml = matchlist( b , linePat )
258 let [_,l,rest;x] = ml
259 let mlDecl = matchlist( l, typePat )
261 let [_,indent,id,type;x] = mlDecl
262 let ml2 = matchlist( rest , '^'.indent.contPat )
264 let [_,c,rest;x] = ml2
266 let ml2 = matchlist( rest , '^'.indent.contPat )
268 let id = substitute( id, '^(\(.*\))$', '\1', '')
269 let type = substitute( type, '\s\+', " ", "g" )
270 " using :browse! *<current>, we get both unqualified and qualified ids
271 let qualified = (id =~ '\.') && (id =~ '[A-Z]')
272 let b:ghc_types[id] = type
274 for qual in qualifiers
275 let b:ghc_types[qual.'.'.id] = type
279 let mlImported = matchlist( l, importedPat )
280 let mlDefined = matchlist( l, definedPat )
282 let [_,modules;x] = mlImported
283 let qualifiers = split( modules, ', ' )
284 elseif mlDefined != []
285 let qualifiers = [module]
288 let ml = matchlist( rest , linePat )
293 function! GHC_Process(imports,output)
295 let imports = a:imports
296 let linePat = '^\(.\{-}\)\n\(.*\)'
297 let contPat = '\s\+\(.\{-}\)\n\(.*\)'
298 let typePat = '^\(\s*\)\(\S*\)\s*::\(.*\)'
299 let modPat = '^-- \(\S*\)'
300 " add '-- defined locally' and '-- imported via ..'
302 echo s:scriptname.": GHCi reports errors (try :make?)"
306 let ml = matchlist( b , linePat )
308 let [_,l,rest;x] = ml
309 let mlDecl = matchlist( l, typePat )
311 let [_,indent,id,type;x] = mlDecl
312 let ml2 = matchlist( rest , '^'.indent.contPat )
314 let [_,c,rest;x] = ml2
316 let ml2 = matchlist( rest , '^'.indent.contPat )
318 let id = substitute(id, '^(\(.*\))$', '\1', '')
319 let type = substitute( type, '\s\+', " ", "g" )
320 " using :browse *<current>, we get both unqualified and qualified ids
321 if current_module " || has_key(imports[0],module)
322 if has_key(b:ghc_types,id) && !(matchstr(b:ghc_types[id],escape(type,'[].'))==type)
323 let b:ghc_types[id] .= ' -- '.type
325 let b:ghc_types[id] = type
328 if 0 " has_key(imports[1],module)
329 let qualid = module.'.'.id
330 let b:ghc_types[qualid] = type
333 let mlMod = matchlist( l, modPat )
335 let [_,module;x] = mlMod
336 let current_module = module[0]=='*'
337 let module = current_module ? module[1:] : module
340 let ml = matchlist( rest , linePat )
345 let s:ghc_templates = ["module _ () where","class _ where","class _ => _ where","instance _ where","instance _ => _ where","type family _","type instance _ = ","data _ = ","newtype _ = ","type _ = "]
347 " use ghci :browse index for insert mode omnicompletion (CTRL-X CTRL-O)
348 function! GHC_CompleteImports(findstart, base)
350 let namsym = haskellmode#GetNameSymbol(getline('.'),col('.'),-1) " insert-mode: we're 1 beyond the text
353 echo 'no name/symbol under cursor!'
356 let [start,symb,qual,unqual] = namsym
358 else " find keys matching with "a:base"
360 let l = len(a:base)-1
362 for key in keys(b:ghc_types)
363 if key[0 : l]==a:base
364 let res += [{"word":key,"menu":":: ".b:ghc_types[key],"dup":1}]
370 set omnifunc=GHC_CompleteImports
372 " Vim's default completeopt is menu,preview
373 " you probably want at least menu, or you won't see alternatives listed
374 " setlocal completeopt+=menu
376 " menuone is useful, but other haskellmode menus will try to follow your choice here in future
377 " setlocal completeopt+=menuone
379 " longest sounds useful, but doesn't seem to do what it says, and interferes with CTRL-E
380 " setlocal completeopt-=longest
382 map <LocalLeader>ct :call GHC_CreateTagfile()<cr>
383 function! GHC_CreateTagfile()
385 echo "creating tags file"
386 let output = system(g:ghc . ' ' . b:ghc_staticoptions . ' -e ":ctags" ' . expand("%"))
387 " for ghcs older than 6.6, you would need to call another program
388 " here, such as hasktags
392 command! -nargs=1 GHCi redraw | echo system(g:ghc. ' ' . b:ghc_staticoptions .' '.expand("%").' -e "'.escape(<f-args>,'"').'"')
394 " use :make 'not in scope' errors to explicitly list imported ids
395 " cursor needs to be on import line, in correctly loadable module
396 map <LocalLeader>ie :call GHC_MkImportsExplicit()<cr>
397 function! GHC_MkImportsExplicit()
398 let save_cursor = getpos(".")
399 let line = getline('.')
400 let lineno = line('.')
401 let ml = matchlist(line,'^import\(\s*qualified\)\?\s*\([^( ]\+\)')
406 if line=~"import[^(]*Prelude"
407 call setline(lineno,substitute(line,"(.*","","").'()')
409 call setline(lineno,'-- '.line)
413 let qflist = getqflist()
414 call setline(lineno,line)
418 let ml = matchlist(d.text,'Not in scope: \([^`]*\)`\([^'']*\)''')
420 let [_,what,qid;x] = ml
421 let id = ( qid =~ "^[A-Z]" ? substitute(qid,'.*\.\([^.]*\)$','\1','') : qid )
422 let pid = ( id =~ "[a-zA-Z0-9_']\\+" ? id : '('.id.')' )
425 if has_key(b:ghc_types,id)
426 let pid = substitute(b:ghc_types[id],'^.*->\s*\(\S*\).*$','\1','').'('.pid.')'
428 let pid = '???('.pid.')'
434 call setline(lineno,'import'.q.' '.mod.'('.join(keys(ids),',').')')
439 call setpos('.', save_cursor)
442 " no need to ask GHC about its supported languages and
443 " options with every editing session. cache the info in
444 " ~/.vim/haskellmode.config
445 " TODO: should we store more info (see haskell_doc.vim)?
447 " should we keep a history of GHC versions encountered?
448 function! GHC_SaveConfig()
449 let vimdir = expand('~').'/'.'.vim'
450 let config = vimdir.'/haskellmode.config'
451 if !isdirectory(vimdir)
454 let entries = ['-- '.g:ghc_version]
455 for l in s:ghc_supported_languages
458 let entries += ['--']
462 call writefile(entries,config)
465 " reuse cached GHC configuration info, if using the same
467 function! GHC_LoadConfig()
468 let vimdir = expand('~').'/'.'.vim'
469 let config = vimdir.'/haskellmode.config'
470 if filereadable(config)
471 let lines = readfile(config)
472 if lines[0]=='-- '.g:ghc_version
474 let s:ghc_supported_languages = []
475 while i<len(lines) && lines[i]!='--'
476 let s:ghc_supported_languages += [lines[i]]
482 let s:opts += [lines[i]]
494 let s:GHC_CachedConfig = haskellmode#GHC_VersionGE([6,8]) && GHC_LoadConfig()
496 if haskellmode#GHC_VersionGE([6,8,2])
497 if !s:GHC_CachedConfig
498 let s:opts = filter(split(substitute(system(g:ghc . ' -v0 --interactive', ':set'), ' ', '','g'), '\n'), 'v:val =~ "-f"')
501 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"]
503 let s:opts = sort(s:opts)
505 amenu ]OPTIONS_GHC.- :echo '-'<cr>
508 exe 'amenu ]OPTIONS_GHC.'.o.' :call append(0,"{-# OPTIONS_GHC '.o.' #-}")<cr>'
510 if has("gui_running")
511 map <LocalLeader>opt :popup ]OPTIONS_GHC<cr>
513 map <LocalLeader>opt :emenu ]OPTIONS_GHC.
516 amenu ]LANGUAGES_GHC.- :echo '-'<cr>
517 aunmenu ]LANGUAGES_GHC
518 if haskellmode#GHC_VersionGE([6,8])
519 if !s:GHC_CachedConfig
520 let s:ghc_supported_languages = sort(split(system(g:ghc . ' --supported-languages'),'\n'))
522 for l in s:ghc_supported_languages
523 exe 'amenu ]LANGUAGES_GHC.'.l.' :call append(0,"{-# LANGUAGE '.l.' #-}")<cr>'
525 if has("gui_running")
526 map <LocalLeader>lang :popup ]LANGUAGES_GHC<cr>
528 map <LocalLeader>lang :emenu ]LANGUAGES_GHC.
532 if !s:GHC_CachedConfig
533 call GHC_SaveConfig()