packages feed

reflex-ghci 0.1.2.0 → 0.1.3.0

raw patch · 4 files changed

+28/−19 lines, 4 filesdep ~reflex-fsnotify

Dependency ranges changed: reflex-fsnotify

Files

ChangeLog.md view
@@ -1,5 +1,13 @@ # Revision history for reflex-ghci +## 0.1.3.0++* Library: Fix parsing of "Failed, n modules loaded." message+* Library: Properly initialize when GHCi version message is the first output line+* Executable: Terminate ghci process when ctrl-c is received+* Library: Don't use a separate process group. Send termination signal to process, not group+* Library: Use `watchDirectoryTree` from `Reflex.FSNotify` to avoid symlink cycles when enumerating directories+ ## 0.1.2.0  * Extract console GHCi widgets from the executable into a library module
reflex-ghci.cabal view
@@ -1,6 +1,6 @@ cabal-version: >=1.10 name: reflex-ghci-version: 0.1.2.0+version: 0.1.3.0 synopsis: A GHCi widget library for use in reflex applications description:   Run GHCi from within a reflex application and interact with it using a functional-reactive interface.@@ -32,7 +32,7 @@     , fsnotify >= 0.3 && < 0.4     , process >= 1.6 && < 1.7     , reflex >= 0.6.3 && < 0.7-    , reflex-fsnotify >= 0.1 && < 0.2+    , reflex-fsnotify >= 0.2 && < 0.3     , reflex-process >= 0.1 && < 0.2     , regex-tdfa >= 1.2.3 && < 1.3     , reflex-vty >= 0.1.3 && < 0.2@@ -55,7 +55,7 @@     , reflex-process >= 0.1 && < 0.2     , text >= 1.2 && < 1.3     , vty >= 5.25 && < 5.26-  ghc-options: -threaded+  ghc-options: -threaded -rtsopts   default-language: Haskell2010  source-repository head
src-bin/ghci.hs view
@@ -1,12 +1,14 @@+import Reflex.Process import Reflex.Process.GHCi import Reflex.Vty import Reflex.Vty.GHCi  import Control.Concurrent (threadDelay)+import Control.Monad.IO.Class (liftIO) import qualified Graphics.Vty.Input as V import qualified Data.Text as T import qualified Data.Text.Encoding as T-import System.Process (shell)+import System.Process (shell, terminateProcess)  import Options.Applicative @@ -46,7 +48,8 @@     case expr of       Nothing -> ghciModuleStatus g       Just _ -> ghciPanes g-    return $ () <$ exit+    readyToExit <- performEvent $ ffor exit $ \_ -> liftIO $ terminateProcess $ _process_handle $ _ghci_process g+    return $ () <$ readyToExit  -- Some rudimentary test expressions -- Run these to test different scenarios like so:
src/Reflex/Process/GHCi.hs view
@@ -19,7 +19,7 @@   ) where  import Reflex-import Reflex.FSNotify (watchDirectory)+import Reflex.FSNotify (watchDirectoryTree) import Reflex.Process (ProcessConfig(..), Process(..), createProcess)  import Control.Monad ((<=<))@@ -31,6 +31,7 @@ import System.Directory (getCurrentDirectory) import qualified System.FSNotify as FS import System.FilePath.Posix (takeExtension)+import System.Posix.Signals (sigINT) import qualified System.Process as P import qualified Text.Regex.TDFA as Regex ((=~)) @@ -51,10 +52,7 @@   -> Event t ()   -- ^ Ask GHCi to reload   -> m (Ghci t)-ghci p mexpr reloadReq = do--  let cmd = p { P.create_group = True } -- Need to set this so that group interrupts don't impact parent-+ghci cmd mexpr reloadReq = do   -- Run the process and feed it some input:   rec proc <- createProcess cmd $ ProcessConfig         { _processConfig_stdin = leftmost@@ -78,7 +76,7 @@                     else Nothing               in attachWithMaybe f (current status) (updated status)             ]-        , _processConfig_signal = never+        , _processConfig_signal = sigINT <$ requestInterrupt         }        -- Reload@@ -94,12 +92,10 @@      -- Only interrupt when there's a file change and we're ready and not in an idle state       let interruptible s = s `elem` [Status_Loading, Status_Executing]           requestInterrupt = gate (interruptible <$> current status) $ (() <$ reloadReq)-      performEvent_ $ ffor requestInterrupt $-        const $ liftIO $ P.interruptProcessGroupOf $ _process_handle proc        -- Define some Regex patterns to use to determine GHCi's state based on output       let okModulesLoaded = "Ok.*module.*loaded." :: ByteString-          failedNoModulesLoaded = "Failed, no modules loaded." :: ByteString+          failedNoModulesLoaded = "Failed,.*modules loaded." :: ByteString           -- TODO: Is there a way to distinguish GHCi's actual exception output           -- from someone printing "*** Exception:" to stderr?           -- TODO: Are there other exception patterns to watch out for?@@ -130,6 +126,9 @@                     Nothing -> Status_LoadSucceeded                     Just _ -> Status_Executing                   s -> s++            (lastLine:_)+              | lastLine Regex.=~ ghciVersionMessage -> const Status_Loading             _ -> id         ] @@ -180,19 +179,18 @@   -- TODO: Separate the filesystem event logic into its own function   -- Watch the project directory for changes   pb <- getPostBuild-  fsEvents <- watchDirectory (noDebounce FS.defaultConfig) (dir <$ pb)-   -- TODO Handle changes to "src" and ".cabal" differently. ":r" is only really appropriate   -- when there are changes to loaded modules.   -- 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.-  let filteredFsEvents = flip ffilter fsEvents $ \e -> -        takeExtension (FS.eventPath e) `elem` [".hs", ".lhs"] +  fsEvents <- watchDirectoryTree (noDebounce FS.defaultConfig) (dir <$ pb) $ \e ->+    takeExtension (FS.eventPath e) `elem` [".hs", ".lhs"]+   -- Events are batched because otherwise we'd get several updates corresponding to one   -- user-level change. For example, saving a file in vim results in an event claiming   -- the file was removed followed almost immediately by an event adding the file-  batchedFsEvents <- batchOccurrences 0.1 filteredFsEvents+  batchedFsEvents <- batchOccurrences 0.1 fsEvents    -- Call GHCi and request a reload every time the files we're watching change   ghci p mexec $ () <$ batchedFsEvents