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 +8/−0
- reflex-ghci.cabal +3/−3
- src-bin/ghci.hs +5/−2
- src/Reflex/Process/GHCi.hs +12/−14
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