reflex-ghci 0.1.4.2 → 0.1.5.0
raw patch · 7 files changed
+190/−9 lines, 7 files
Files
- ChangeLog.md +4/−0
- reflex-ghci.cabal +1/−1
- src/Reflex/Process/GHCi.hs +27/−8
- tests/exe-pkg/dist-newstyle/build/x86_64-linux/ghc-8.6.5/tests-0.1.0.0/x/tests/build/tests/autogen/Paths_tests.hs +50/−0
- tests/lib-pkg-err/dist-newstyle/build/x86_64-linux/ghc-8.6.5/test-pkg2-0.1.0.0/build/autogen/Paths_test_pkg2.hs +50/−0
- tests/lib-pkg/dist-newstyle/build/x86_64-linux/ghc-8.6.5/test-pkg2-0.1.0.0/build/autogen/Paths_test_pkg2.hs +50/−0
- tests/test.hs +8/−0
ChangeLog.md view
@@ -1,5 +1,9 @@ # Revision history for reflex-ghci +## 0.1.5.0++* Fix various circumstances under which reflex-ghci would hang due to failure to properly parse GHCi output or failure to detect filesystem changes (the latter particularly on macOS)+ ## 0.1.4.2 * Tests: Ensure proper shutdown.
reflex-ghci.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: reflex-ghci-version: 0.1.4.2+version: 0.1.5.0 synopsis: A GHCi widget library for use in reflex applications description: Run GHCi from within a Reflex FRP (<https://reflex-frp.org>) application and interact with it using a functional reactive interface.
src/Reflex/Process/GHCi.hs view
@@ -31,10 +31,14 @@ import System.Directory (getCurrentDirectory) import qualified System.FSNotify as FS import System.FilePath.Posix (takeExtension)+import qualified System.Info as Sys import System.Posix.Signals (sigINT) import qualified System.Process as P import qualified Text.Regex.TDFA as Regex ((=~)) +msg :: (IsString a, Semigroup a) => a -> a+msg = (<>) "<reflex-ghci>: "+ -- | Runs a GHCi process and reloads it whenever the provided event fires ghci :: ( TriggerEvent t m@@ -54,22 +58,33 @@ -> m (Ghci t) ghci cmd mexpr reloadReq = do -- Run the process and feed it some input:+ let msgInit = msg "performing setup..."+ msgExprStarted = msg "evaluating expression..."+ msgExprFinished = msg "expression evaluation ended."+ putMsgLn :: ByteString -> ByteString+ putMsgLn m = "Prelude.putStrLn \"" <> m <> "\"\n" rec proc <- createProcess cmd $ ProcessConfig { _processConfig_stdin = SendPipe_Message . (<> "\n") <$> leftmost [ reload -- Execute some expression if GHCi is ready to receive it , fforMaybe (updated status) $ \case- Status_LoadSucceeded -> mexpr+ Status_LoadSucceeded -> ffor mexpr $ \expr ->+ C8.intercalate "\n"+ [ putMsgLn msgExprStarted+ , putMsgLn expr+ , expr+ , putMsgLn msgExprFinished+ ] _ -> Nothing -- On first load, set the prompt , let f old new = if old == Status_Initializing && new == Status_Loading then Just $ C8.intercalate "\n"- [ "Prelude.putStrLn \"Initialized. Setting up reflex-ghci...\""+ [ putMsgLn msgInit , ":set prompt ..." , ":set -fno-break-on-exception" , ":set -fno-break-on-error" , ":set prompt \"\""- , "Prelude.putStrLn \"\""+ , putMsgLn "" , ":set prompt " <> prompt , ":r" ]@@ -106,7 +121,6 @@ -- a proxy for GHCi's readiness to be interrupted ghciVersionMessage = "GHCi, version.*: https?://www.haskell.org/ghc/" :: ByteString - -- Inspect the output and determine what state GHCi is in status :: Dynamic t Status <- holdUniqDyn <=< foldDyn ($) Status_Initializing $ leftmost [ fforMaybe (updated errors) $ \err -> if err Regex.=~ exceptionMessage || err Regex.=~ interactiveErrorMessage@@ -117,9 +131,8 @@ lastLine:expectedMessage:_ | lastLine == prompt && expectedMessage Regex.=~ okModulesLoaded -> const Status_LoadSucceeded | lastLine == prompt && expectedMessage Regex.=~ failedNoModulesLoaded -> const Status_LoadFailed- | lastLine == prompt -> \case- Status_Executing -> Status_ExecutionSucceeded- s -> s+ | lastLine == prompt && expectedMessage == msgExprStarted -> const Status_Executing+ | lastLine Regex.=~ (prompt :: String) && expectedMessage Regex.=~ msgExprFinished -> const Status_ExecutionSucceeded | lastLine Regex.=~ ghciVersionMessage -> const Status_Loading | otherwise -> \case Status_LoadSucceeded -> case mexpr of@@ -184,7 +197,13 @@ -- We could use ":show modules" to see which hs files are loaded and determine what to do based -- on that, but we'll need to parse that output. - fsEvents <- watchDirectoryTree (noDebounce FS.defaultConfig) (dir <$ pb) $ \e ->+ -- On macOS, use the polling backend due to https://github.com/luite/hfsevents/issues/13+ -- TODO check if this is an issue with nixpkgs+ let fsConfig = noDebounce $ FS.defaultConfig+ { FS.confUsePolling = Sys.os == "darwin"+ , FS.confPollInterval = 250000+ }+ fsEvents <- watchDirectoryTree fsConfig (dir <$ pb) $ \e -> takeExtension (FS.eventPath e) `elem` [".hs", ".lhs"] -- Events are batched because otherwise we'd get several updates corresponding to one
+ tests/exe-pkg/dist-newstyle/build/x86_64-linux/ghc-8.6.5/tests-0.1.0.0/x/tests/build/tests/autogen/Paths_tests.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE NoRebindableSyntax #-}+{-# OPTIONS_GHC -fno-warn-missing-import-lists #-}+module Paths_tests (+ version,+ getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,+ getDataFileName, getSysconfDir+ ) where++import qualified Control.Exception as Exception+import Data.Version (Version(..))+import System.Environment (getEnv)+import Prelude++#if defined(VERSION_base)++#if MIN_VERSION_base(4,0,0)+catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a+#else+catchIO :: IO a -> (Exception.Exception -> IO a) -> IO a+#endif++#else+catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a+#endif+catchIO = Exception.catch++version :: Version+version = Version [0,1,0,0] []+bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath++bindir = "/home/ali/.cabal/bin"+libdir = "/home/ali/.cabal/lib/x86_64-linux-ghc-8.6.5/tests-0.1.0.0-inplace-tests"+dynlibdir = "/home/ali/.cabal/lib/x86_64-linux-ghc-8.6.5"+datadir = "/home/ali/.cabal/share/x86_64-linux-ghc-8.6.5/tests-0.1.0.0"+libexecdir = "/home/ali/.cabal/libexec/x86_64-linux-ghc-8.6.5/tests-0.1.0.0"+sysconfdir = "/home/ali/.cabal/etc"++getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir, getSysconfDir :: IO FilePath+getBinDir = catchIO (getEnv "tests_bindir") (\_ -> return bindir)+getLibDir = catchIO (getEnv "tests_libdir") (\_ -> return libdir)+getDynLibDir = catchIO (getEnv "tests_dynlibdir") (\_ -> return dynlibdir)+getDataDir = catchIO (getEnv "tests_datadir") (\_ -> return datadir)+getLibexecDir = catchIO (getEnv "tests_libexecdir") (\_ -> return libexecdir)+getSysconfDir = catchIO (getEnv "tests_sysconfdir") (\_ -> return sysconfdir)++getDataFileName :: FilePath -> IO FilePath+getDataFileName name = do+ dir <- getDataDir+ return (dir ++ "/" ++ name)
+ tests/lib-pkg-err/dist-newstyle/build/x86_64-linux/ghc-8.6.5/test-pkg2-0.1.0.0/build/autogen/Paths_test_pkg2.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE NoRebindableSyntax #-}+{-# OPTIONS_GHC -fno-warn-missing-import-lists #-}+module Paths_test_pkg2 (+ version,+ getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,+ getDataFileName, getSysconfDir+ ) where++import qualified Control.Exception as Exception+import Data.Version (Version(..))+import System.Environment (getEnv)+import Prelude++#if defined(VERSION_base)++#if MIN_VERSION_base(4,0,0)+catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a+#else+catchIO :: IO a -> (Exception.Exception -> IO a) -> IO a+#endif++#else+catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a+#endif+catchIO = Exception.catch++version :: Version+version = Version [0,1,0,0] []+bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath++bindir = "/home/ali/.cabal/bin"+libdir = "/home/ali/.cabal/lib/x86_64-linux-ghc-8.6.5/test-pkg2-0.1.0.0-inplace"+dynlibdir = "/home/ali/.cabal/lib/x86_64-linux-ghc-8.6.5"+datadir = "/home/ali/.cabal/share/x86_64-linux-ghc-8.6.5/test-pkg2-0.1.0.0"+libexecdir = "/home/ali/.cabal/libexec/x86_64-linux-ghc-8.6.5/test-pkg2-0.1.0.0"+sysconfdir = "/home/ali/.cabal/etc"++getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir, getSysconfDir :: IO FilePath+getBinDir = catchIO (getEnv "test_pkg2_bindir") (\_ -> return bindir)+getLibDir = catchIO (getEnv "test_pkg2_libdir") (\_ -> return libdir)+getDynLibDir = catchIO (getEnv "test_pkg2_dynlibdir") (\_ -> return dynlibdir)+getDataDir = catchIO (getEnv "test_pkg2_datadir") (\_ -> return datadir)+getLibexecDir = catchIO (getEnv "test_pkg2_libexecdir") (\_ -> return libexecdir)+getSysconfDir = catchIO (getEnv "test_pkg2_sysconfdir") (\_ -> return sysconfdir)++getDataFileName :: FilePath -> IO FilePath+getDataFileName name = do+ dir <- getDataDir+ return (dir ++ "/" ++ name)
+ tests/lib-pkg/dist-newstyle/build/x86_64-linux/ghc-8.6.5/test-pkg2-0.1.0.0/build/autogen/Paths_test_pkg2.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE NoRebindableSyntax #-}+{-# OPTIONS_GHC -fno-warn-missing-import-lists #-}+module Paths_test_pkg2 (+ version,+ getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,+ getDataFileName, getSysconfDir+ ) where++import qualified Control.Exception as Exception+import Data.Version (Version(..))+import System.Environment (getEnv)+import Prelude++#if defined(VERSION_base)++#if MIN_VERSION_base(4,0,0)+catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a+#else+catchIO :: IO a -> (Exception.Exception -> IO a) -> IO a+#endif++#else+catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a+#endif+catchIO = Exception.catch++version :: Version+version = Version [0,1,0,0] []+bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath++bindir = "/home/ali/.cabal/bin"+libdir = "/home/ali/.cabal/lib/x86_64-linux-ghc-8.6.5/test-pkg2-0.1.0.0-inplace"+dynlibdir = "/home/ali/.cabal/lib/x86_64-linux-ghc-8.6.5"+datadir = "/home/ali/.cabal/share/x86_64-linux-ghc-8.6.5/test-pkg2-0.1.0.0"+libexecdir = "/home/ali/.cabal/libexec/x86_64-linux-ghc-8.6.5/test-pkg2-0.1.0.0"+sysconfdir = "/home/ali/.cabal/etc"++getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir, getSysconfDir :: IO FilePath+getBinDir = catchIO (getEnv "test_pkg2_bindir") (\_ -> return bindir)+getLibDir = catchIO (getEnv "test_pkg2_libdir") (\_ -> return libdir)+getDynLibDir = catchIO (getEnv "test_pkg2_dynlibdir") (\_ -> return dynlibdir)+getDataDir = catchIO (getEnv "test_pkg2_datadir") (\_ -> return datadir)+getLibexecDir = catchIO (getEnv "test_pkg2_libexecdir") (\_ -> return libexecdir)+getSysconfDir = catchIO (getEnv "test_pkg2_sysconfdir") (\_ -> return sysconfdir)++getDataFileName :: FilePath -> IO FilePath+getDataFileName name = do+ dir <- getDataDir+ return (dir ++ "/" ++ name)
tests/test.hs view
@@ -233,3 +233,11 @@ eValue <- hold Nothing $ Just <$> e after <- shutdown $ g <$ e pure $ fmapMaybe id $ tag eValue after++debug :: (Reflex t, PerformEvent t m, MonadIO (Performable m)) => Ghci t -> m ()+debug g = do+ performEvent_ $ ffor (updated $ _ghci_status g) $ liftIO . print+ performEvent_ $ ffor (_ghci_moduleOut g) $ liftIO . print+ performEvent_ $ ffor (_ghci_moduleErr g) $ liftIO . print+ performEvent_ $ ffor (_ghci_execOut g) $ liftIO . print+ performEvent_ $ ffor (_ghci_execErr g) $ liftIO . print