module PfeBrowserF(mainF) where import Prelude hiding (putStr,putStrLn,print,readFile,writeFile,ioError,catch,readIO) import Monad(unless,mplus,when,filterM) import Maybe(fromMaybe,mapMaybe,isJust) import List(find,(\\),nub) import AllFudgets as Fud hiding (getModificationTime) import ContribFudgets hiding (lift) import FudgetIOMonad1 import PfePlumbing import PfeBrowserMonad import IdInfo import PfeBrowserGUI import CertAttrsEditorF import PfeBrowserMenu as M import PfeBrowserCerts import CertServerF import CertCmd import AuxWindowsF import ImageGraphics(gifFile) import FudExtras(longTextPopupF) import HsModule(ModuleName(..),hsModName,{-,HsModuleI-}) import HsTokens as T import PropPosSyntax(getHSName,SrcLoc(..)) import TiDecorate(no_use) import TI(Typing(..),envFrom,topName,HsIdentI(..)) import PNT(PNT(..)) import TypedIds(namespace,isClassOrType) import UniqueNames as UN(OptSrcLoc(N),optOrigModule) import HasBaseName(getBaseName) import PFE0(getModuleInfixes,setBatchMode,checkProject,ppFlags, withProjectDir,epput, updateModuleGraph,findNode,projectDir,getCurrentModuleGraph) import PFE2(getModuleExports) import PFE3(parseSourceFile'') import ScopeModule(checkRefs) import PFE4(exTypeCheck) import Pfe2Cmds(ppExports) import Pfe4Cmds(ppIface,moduleInterface) import PFE_Certs import CertServers(printAttrs) import PPU(pp,ppi,ppu,withPPEnv,($$),(<>),(<+>),vcat) import DrawDoc(drawDoc) import DrawError import DrawLex(origLabel,certIcon) import FudDraw(ulinkD) import MT(lift) import MUtils import AbstractIO hiding (getEnv) import FileUtils(updateFile) import SimpleGraphs(reverseGraph,neighbours) import StatusIndicator(statusF,Status(..)) -- Debuging --import IOExts(trace) --- Plumbing ------------------------------------------------------------------- mainF opts pdir servers = loopOnlyF $ titleShellF' (setSizing Static) myversion pfeBrowserF where pfeBrowserF = pfeF opts >==< guiF -- @ guiF : the graphical user interface fudget of the PFE browser guiF :: F Out In TagF guiF dispatch tags = mapTF vBoxF $ (mapTF hBoxF (pfeMenuBarTF>&&< infoWindowsTF) >&< compTagF (hSplitF' aLeft) graphDispTF moduleDispTF >&< (popupsTF >&< certServerTF) toTitle = Left (_toMenuBar:&:toStatus:&:_toInfoWindows):&:(toGraph:&:toModuleDisp) :&:(toPopups:&:toCertServer)= extend Right tags toMessagePopup:&:toNewCertPopup:&:(toCertAttrsEditors:&:toTextEditor) = toPopups pfeMenuBarTF = tagF fromMenuBar (pfeMenuBarF types) where types = [serverName s|s<-servers, lookup "has_sequent" (serverAttrs s)/=Just "no"] statusTF = tagF (const done) (spacer1F centerS $ {-"Status:" `labLeftOfF`-} statusF) popupsTF = messagePopupTF >&< newCertPopupTF >&< (certAttrsEditorsTF >&< textEditorPopupTF) where messagePopupTF = tagF fromMessagePopup (snd>^=&< compTagF (vSplitF' aBottom) (tagF fromSource sourceDispF) (tagF fromRefs refDispF) infoWindowsTF = tagF fromInfoWindows $ spacer1F (noStretchS False True `compS` rightS) $ auxGfxWindowsF myname fromGraph = newNode fromNodeInfo = either newModuleLbl newFile fromRefs = infoPick Nothing fromMessagePopup _ = done fromInfoWindows = either popupInfoWindow infoPick' where infoPick' (w,infolbl) = infoPick (Just w) infolbl infoPick optw infolbl = case infolbl of CertLbl cert -> clearRefs >> changeHilite [] >> updCertInfo cert DefLbl (HsCon (PNT pn sp _)) -> goto (HsCon pn,sp) DefLbl (HsVar (PNT pn sp _)) -> goto (HsVar pn,sp) SrcLocLbl (path,pos) -> newFile path >> gotoLabel (posLabel pos) where gotoLabel lbl = do pfePut . toSource . mkvis $ lbl changeHilite [lbl] ModuleLbl m -> newModuleLbl (m,Nothing) CertCmd cmd@(ValidateCert qcert) -> do putCertServer cmd vcert <- certDisplay # getStBr when (vcert==Just qcert) $ showQCertInfo qcert CertCmd cmd -> do maybe done close optw putCertServer cmd where close w = popupInfoWindow (w,False) CreateCert ca -> newCertPopup ca CertEdit e -> pfePut (toCertAttrsEditors (Right e)) CertShowText txt -> pfePut . toMessagePopup . map backspace . lines $ txt ShowErrorMsg gfx -> popupErrorMsg gfx newCertPopup ca = do cicons <- certIcons # getStBr clearRefs pfePut (toNewCertPopup (cicons,ca)) putCertServer cmd = do cmds <- certInProgress # getStBr setInProgress (cmds++[cmd]) if null cmds then doCertServer shellcmd else notnow where shellcmd = shellCmd cmd notnow = certCmdMsg "Command added to queue" shellcmd -- pre: no command in progress doCertServer shellcmd = do certCmdMsg "Sent certificate server command" shellcmd pfePut (toCertServer shellcmd) certCmdMsg msg cmd = do cnt <- length . certInProgress # getStBr pfePut $ toRefs $ linesD $ [g $ msg++":", g cmd] ++(if cnt>0 then [g $ "Number of commands in queue: "++show cnt] else []) fromNewCertPopup (ty,(cert,concl)) = if null cert || null concl then popupMsg "Fill in both a certificate name and a conclusion when creating a new certificate!" else do m <- modname # getStBr let qcert = QCertName m cert cmd = CertCmd.NewCert ty qcert concl putCertServer cmd dcert <- certDisplay # getStBr when (dcert==Just qcert) $ showQCertInfo qcert fromCertAttrsEditor (ctype,(cname,userattrs)) = do Just dir <- projectDir m <- modname # getStBr (_,(Just oldattrs,_)) <- certInfo m cname let newattrs = updateAttrs oldattrs userattrs updateFile (certAttrsPath m cname dir) (printAttrs newattrs) infoPick Nothing (CertLbl cname) -- hmm where updateAttrs oldattrs newattrs = map replace oldattrs++addedattrs -- preserve the order! where addedattrs = filter added newattrs added (n,_) = n `notElem` map fst oldattrs replace (name,old) = (name,fromMaybe old (lookup name newattrs)) fromTextEditor ((_,Just src),src') = if src'/=src then do srcpath <- fst . modnode # getStBr -- This assumes that the editor window is modal, so that -- the current node still is the same!! writeFile srcpath src' reloadModule else done fromMenuBar msg = case msg of File fcmd -> fileCmd fcmd View vcmd -> viewCmd vcmd Windows wcmd -> windowCmd wcmd Cert ccmd -> certCmd ccmd _ -> done -- !! where fileCmd Quit = pfeQuit fileCmd ReloadModule = reloadProject fileCmd EditModule = editModule viewCmd vcmd = do updStBr $ \ st->st{viewMode=vcmd} optGetTypes windowCmd (WindowCmd w up) = pfePut (toInfoWindows (w,Left up)) --up is False or True certCmd certcmd = case certcmd of M.NewCert -> whenInModuleM $ newCertPopup ("","") M.NewAll certtype -> whenInModuleM $ newAll certtype M.TodoCert -> putCertServer CertCmd.TodoCert RevalidateAllQuick -> revalidateAll >> reloadModule ValidateAll -> validateAll _ -> done -- !! newAll ty = do m <- modname # getStBr putCertServer $ CertCmd.NewCertAll ty (pp m) validateAll = do m <- modname # getStBr let qcert = QCertName m validateOne = putCertServer . ValidateCert . qcert . fst mapM_ validateOne. certs =<< getStBr editModule = do n <- modnode # getStBr unless (isNoModule n) $ do let srcpath = fst n mname = fst (snd n) prompt = "Edit "++pp mname++" in "++srcpath src <- readFile srcpath pfePut (toTextEditor (Just prompt,Just src)) fromCertServer (ok,output) = do cmds <- certInProgress # getStBr let cmds' = drop 1 cmds setInProgress cmds' if null cmds' then do clearRefs changeHilite [] {-when ok-} reloadModule else doCertServer (shellCmd (head cmds')) unless (null output) (route cmds . map stripEither $ output) where route (ValidateCert qcert:_) msg = certCmdMsg (ppqcert qcert) (last msg) route _ msg = if ok && length msg <= 3 then pfePut . toRefs . linesD . map g $ msg else pfePut $ toMessagePopup msg --- Initialization and main loop ------------------------------------------- --pfeF o = nullF pfeF :: Opts -> PfeF pfeF = runFIOM . runPfeFM pfeFM pfeFM :: PfeFM () pfeFM = do g <- getCurrentModuleGraph icons <- getCertIcons servers sadicon <- getIcon (gifFile (badCertIconPath pdir)) setStBr St{modnode=noModuleNode,viewMode=view0,mrefs=[], hilbls=[], types=Nothing,certs=[],revgraph=reverseGraph (map snd g), certDisplay=Nothing,certInProgress=[], certServers=servers,certIcons=icons,sadIcon=sadicon} pfePut (toGraph g) putInfoWindow (CertTypes,drawCertTypes (zip servers icons)) pfePut (toCertAttrsEditors (Left servers)) setBatchMode False pfeloop where pfeloop :: PfeFM () pfeloop = do (dispatch =<< pfeGet) `catch` stdHandler pfeloop getCertIcons :: CertServers -> PfeFM CertIcons getCertIcons servers = mapM getCertIcon servers getCertIcon certServer = do let dir = serverDir certServer certtype = serverName certServer iconsrc status = gifFile (dir++"/"++status++".gif") valid <- getIcon (iconsrc "valid") invalid <- getIcon (iconsrc "invalid") unknown <- getIcon (iconsrc "unknown") return (certtype,(valid,invalid,unknown)) getIcon = lift . xrequestFIOM . convToPixmapK drawCertTypes = htableD hdrs . concatMap certD where hdrs = ["Icon","Type","Description"] certD (server,(ty,(icon,_,_))) = [g icon,g ty,g descr] where descr = fromMaybe "(Descrption missing)" $ lookup "description" (serverAttrs server) typeErrorHandler err = do --setNoTypes pfePut $ toRefs $ fgD "red" $ g "Type error!" setStatus (Typing,InError) --o <- ppFlags m <- modname # getStBr popupErrorMsg (fontD ppfont (drawTypeError m err)) epput err {- setNoTypes = do updStBr $ \ st->st{viewMode=Hyper} putStrLn "Switching off type info" pfePut (toMenuBar (View Hyper)) -} stdHandler err = do epput msg -- popupMsg msg unless (null msg) $ popupErrorMsg (msgGfx msg) where msg = ioeGetErrorString err popupErrorMsg gfx = do putInfoWindow (ErrorMsg,gfx) popupInfoWindow (ErrorMsg,True) clearErrorMsg = putInfoWindow (ErrorMsg,blankD 10) setStatus = pfePut . toStatus resetStatus = sequence_ [setStatus (l,Unknown)|l<-[minBound..maxBound]] whenInModuleM = unlessM (isNoModule.modnode # getStBr) --- Reactions and auxiliary functions -------------------------------------- reloadProject = -- skip reload if there is no project, or no module is selected whenInModuleM $ reloadGraph >> reloadModule where reloadGraph = do updateModuleGraph -- should also check for added/removed files and update graphDispF updModNode . modname =<< getStBr updModNode m = do g <- getCurrentModuleGraph node <- findNode m updStBr $ \st->st{revgraph=reverseGraph (map snd g),modnode=node} return node reloadModule = whenInModuleM $ do maybe done showQCertInfo . certDisplay =<< getStBr newNode . modnode =<< getStBr {-+ #newNode#: load and display a new module, after the user has entered or clicked on a module name or a file name. -} newNode node@(path,_) = withWaitCursor $ do resetStatus clearErrorMsg setStatus (Syntax,InProgress) dir <- checkProject --pfePut (toSource (Left ("","",[]))) -- empty window to scroll to top r <- try (parseSourceFile'' path) case r of Left err -> badNode dir err Right (ts,((wm,ast),refs)) -> do node@(_,(m,_)) <- updModNode (getBaseName (hsModName ast)) --updates revgraph let --refs =simplifyRefsTypes' rs asserts = assertions ast -- :: [(PNT,AssertionI PNT)] certs_annot = mapMaybe isCertToken ts -- :: [CertName] isCert cert = doesFileExist (certAttrsPath m cert dir) certs_existing <- filterM isCert . fromMaybe [] =<< maybeM (getDirectoryContents (certModuleDir m dir)) let certs_noannot = certs_existing \\ certs_annot certs = certs_annot++certs_noannot certsStatus <- zip certs # quickCertsStatus m certs dir certsInfo <- let replace = zipWith replace1 certsStatus replace1 (_,s) (c,(a,s0)) = (c,(a,s)) in replace # mapM (certInfo m) certs let conccert (cert,(attrs,_)) = flip (,) cert # (lookup "conc" =<< attrs) na = collectByFst . mapMaybe conccert . filter ((`elem` certs_noannot).fst) $ certsInfo updStBr $ \st->st{mrefs=refs,hilbls=[],types=Nothing,certs=certsInfo} e <- getModuleExports m -- To keep the displays consistent with the internal state, -- output operations should come after all operations that can fail! setStatus (Syntax,Good) setStatus (Scoping,InProgress) scope_ok <- showScopeErrors refs setStatus (Scoping,if scope_ok then Good else InError) importedby <- flip neighbours m . revgraph # getStBr pfePut (toNodeInfo (Left (node,importedby))) cicons <- icons # getStBr pfePut (toSource (Left (dir,cicons,node,ts,refs,certsStatus,na))) o <- ppFlags putInfoWindow (Exports,ppo o (ppExports m e)) putInfoWindow (Interface,ppMsgGfx o clickToActivate) putInfoWindow (Pretty,ppo o ast) putInfoWindow (Evidence,evidenceGfx m dir cicons certsInfo asserts) pfePut (toTitle (myname++": "++pp m)) when scope_ok optGetTypes where {-+ Rather than aborting newNode (and potentially leaving the history buttons in an incosistent state) on errors, display an empty window and continue... -} badNode dir err = do o <- ppFlags s <- fromMaybe [] # maybeM (readFile path) updStBr $ \st->st{modnode=node} pfePut (toRefs (ppMsgGfx o (ioeGetErrorString err))) pfePut (toNodeInfo (Left (node,[]))) pfePut (toSource (showsrctext s)) setStatus (Syntax,InError) putInfoWindow (Exports,blankD 10) putInfoWindow (Interface,blankD 10) putInfoWindow (Pretty,blankD 10) putInfoWindow (Evidence,blankD 10) pfePut (toTitle (myname++": "++path)) showScopeErrors refs = do if ok then clearRefs else do o <- ppFlags pfePut (toRefs (gfx o)) return ok where ok = null errs errs = checkRefs refs errcnt = length errs gfx o = labelD (ShowErrorMsg (fontD ppfont (drawScopeErrors errs))) (ulinkD (g msg)) msg = if errcnt==1 then "One scoping error" else show errcnt++" scoping errors" {-+ #newModule#: load and display a new module after the user has entered or clicked on a module name. -} newModule :: ModuleName ->PfeFM () newModule m = newNode =<< findNode m newModuleLbl (m',optlbl) = do n@(_,(m,_)) <- modnode # getStBr when (isNoModule n || m'/=m) $ newModule m' maybe done gotoLabel optlbl where gotoLabel lbl = do pfePut . toSource . mkvis $ lbl showInfo lbl {-+ #newFile#: load and display a new module after the user has entered or clicked on a file name. -} newFile :: FilePath ->PfeFM () newFile path = do (oldpath,_) <- modnode # getStBr when (path/=oldpath) $ do updateModuleGraph g <- getCurrentModuleGraph case [node|node@(path',_)<-g,path'==path] of [node] -> newNode node _ -> fail ("File not part of this project:"++path) {- goto o@(_,r) = case r of Left (s,n) -> do let m' = Module s m <- modname # getStBr unless (m'==m) $ newModule m' gotoLocal o _ -> gotoLocal o -} goto o@(r,_) = do let optm' = optOrigModule r m <- modname # getStBr case optm' of Just m'| m'/=m -> newModule m' _ -> done gotoLocal o gotoLocal (o,sp) = do st <- getStBr case [r|r@((d,_),_,[(o',sp')])<-mrefs st, isDef d,o'==o,namespace sp'==namespace sp] of orig:_ -> do let lbl = origLabel orig pfePut (toSource (mkvis lbl)) rememberLbl lbl showInfo lbl _ -> putStrLn $ "Didn't find def of "++show o rememberLbl lbl = do m <- modname # getStBr pfePut (toNodeInfo (Right (m,lbl))) followLink lbl def = rememberLbl lbl >> goto def changeHilite new = do old <- hilbls # getStBr pfePut (toSource (hilite (old\\new) (new\\old))) updStBr $ \st->st{hilbls=new} getIdInfo lbl@(Lbl token) = do refs <- mrefs # getStBr infixes <- getModuleInfixes path <- fst . modnode # getStBr let p = (path,lblPos lbl) (occurences,optdef,refinfo) = idInfo refs infixes token return ((occurences,optdef,concatMap msglines refinfo),p) fromSource (Just (Button 1),lbl@(Lbl ((ModuleName,(_,mn)),_))) = newModule (PlainModule mn) -- !!! fromSource (Just button,lbl@(Lbl token)) = case button of Button 1 -> case isCertTokenRef token of Just cert -> changeHilite [lbl] >> updCertInfo cert _ -> do info@((_,optdef,_),_) <- getIdInfo lbl case optdef of Nothing -> done Just def -> if isDefLbl lbl then showInfo' lbl info else followLink lbl def Button 2 -> showInfo lbl Button 3 -> do ((occurences,_,refinfo),_) <- getIdInfo lbl let cnt=length occurences msg=if cnt>0 then ["Highlighted all "++show cnt++ " occurences in this module"] else [] changeHilite (map posLabel occurences) pfePut (toRefs (linesD (map g (refinfo++msg)))) _ -> done fromSource (Nothing,lbl) = ifM (isLink lbl) (pfePut (toSource setlinkcursor)) (pfePut (toSource setnormalcursor)) where isLink (Lbl ((ModuleName,(_,mn)),_)) = return True isLink lbl = if isDefLbl lbl then return False else do info@((_,optdef,_),_) <- getIdInfo lbl return (isJust optdef) updCertInfo cert = showCertInfo cert >> updCertIcons cert updCertIcons cert = whenM (statusChange cert) reloadModule -- TODO: efficient icon updates in the source display and evidence window showInfo lbl = showInfo' lbl =<< getIdInfo lbl showInfo' lbl ((_,def,refinfo),p) = do --optGetTypes tinfo <- maybeD (optGetType p) def ainfo <- maybeD getAssertionInfo def pfePut (toRefs (linesD (map g refinfo++ainfo++tinfo))) changeHilite (if isJust def then [lbl] else []) where maybeD = maybe (return []) optGetType p o = maybe (return []) (getType p o) . types =<< getStBr getType p (qn,k) types = do --print (k,qn) m <- modname # getStBr let env = modtypes m types o <- ppFlags --print (let xs:>:_ = unzipTyped mt in xs) return [typeInfo o env k p qn] typeInfo ppopts (mt,env) k p qn = if isClassOrType k then kinfo qn (fst env) else tinfo (snd (no_use env)) mt p qn where kinfo qn = optpp . lookup qn . convTEnv (ppi.fst) optpp = maybe (Fud.g "No type/kind info available") (ppo ppopts) tinfo env mt (path,lexpos) qn = optpp (find' mt `mplus` lookup qn env') where env' = convTEnv (ppi.fst) (env++mt) qn' = getHSName qn find' = fmap pp_use . find (pos.name) name (i:>:_) = getHSName i pos (PNT pn _ (N (Just (SrcLoc f _ lin col)))) = pn==qn' && f==path && col==column lexpos && lin==line lexpos pos i = False pp_use (_:>:(gen,opti)) = case opti of Just inst -> "Generally:"<+>gen<>","$$"Here:"<+>inst _ -> ppi gen pntpos (PNT _ _ (N p)) = p convTEnv f env = [(bn i,f info)|i:>:info<-env] where bn = fmap orig -- . getHSName orig (PNT n _ _) = n modtypes m pfe4info = (snd (envFrom tm),(concat kenvs,concat tenvs)) where Just (_,((_,tm):_,_)) = lookup m (pfe4info::TInfo) (kenvs,tenvs) = unzip [env|(_,(_,(_,(_,env))))<-pfe4info] optGetTypes = do st <- getStBr when (viewMode st==Typed && not (isNoModule (modnode st) || haveTypes st)) (retypeCheck (modname st)) where retypeCheck m = withWaitMsg (g "Type checking...") $ do setStatus (Typing,InProgress) opttypes <- exTypeCheck (Just [m]) case opttypes of Left err -> typeErrorHandler err Right types -> do mIface <- moduleInterface m -- calls typeCheck again ==> slow!! setStatus (Typing,Good) o <- ppFlags putInfoWindow (Interface,ppo o (ppIface mIface)) updStBr $ \st->st{types=Just types} clearRefs = pfePut (toRefs (blankD 10)) popupMsg = pfePut . toMessagePopup . msglines withWaitMsg msg cmd = do pfePut (toRefs msg) tryThen (withWaitCursor cmd) $ clearRefs setInProgress cmds = do updStBr $ \ st -> st{certInProgress=cmds} o <- ppFlags putInfoWindow (PendingCmds,ppo o (vcat cmds)) ppMsgGfx o = msgGfx . ppu o msgGfx = linesD . map g . msglines --msgGfx = vboxlD' 0 . map (atomicD . expandTabs 8) ppo ppopts = ppo' ppopts ppo' (_,ppopts) = fontD ppfont . drawDoc . withPPEnv ppopts . ppi msglines = map (expandTabs 8) . lines clickToActivate = -- "(To activate type checker: click with the middle mouse button on an identifier)" "(To activate type checker: select Type Info from the View menu)" --- {- instance CatchIO e m => CatchIO e (WithState s m) where stm `catch` h = do s <- getSt r <- lift $ try (withStS s stm) case r of Left err -> h err Right (ans,s') -> setSt_ s' >> return ans ioError = lift . ioError -} -- ppfont = argKey "ppfont" refFont