ihaskell 0.9.1.0 → 0.10.0.0
raw patch · 32 files changed
+741/−1158 lines, 32 filesdep +raw-strings-qqdep +timedep −bin-package-dbdep ~basedep ~ghcdep ~ghc-boot
Dependencies added: raw-strings-qq, time
Dependencies removed: bin-package-db
Dependency ranges changed: base, ghc, ghc-boot
Files
- ihaskell.cabal +41/−36
- main/IHaskellPrelude.hs +0/−151
- main/Main.hs +44/−78
- src/IHaskell/BrokenPackages.hs +4/−9
- src/IHaskell/CSS.hs +5/−4
- src/IHaskell/Convert.hs +1/−6
- src/IHaskell/Convert/Args.hs +2/−7
- src/IHaskell/Convert/IpynbToLhs.hs +4/−7
- src/IHaskell/Convert/LhsToIpynb.hs +6/−8
- src/IHaskell/Display.hs +2/−27
- src/IHaskell/Eval/Completion.hs +25/−70
- src/IHaskell/Eval/Evaluate.hs +123/−225
- src/IHaskell/Eval/Hoogle.hs +70/−59
- src/IHaskell/Eval/Info.hs +0/−5
- src/IHaskell/Eval/Inspect.hs +1/−7
- src/IHaskell/Eval/Lint.hs +32/−44
- src/IHaskell/Eval/ParseShell.hs +10/−11
- src/IHaskell/Eval/Parser.hs +32/−37
- src/IHaskell/Eval/Util.hs +43/−81
- src/IHaskell/Eval/Widgets.hs +17/−20
- src/IHaskell/Flags.hs +19/−23
- src/IHaskell/IPython.hs +14/−83
- src/IHaskell/IPython/Stdin.hs +14/−24
- src/IHaskell/Publish.hs +47/−26
- src/IHaskell/Types.hs +55/−30
- src/IHaskellPrelude.hs +23/−17
- src/StringUtils.hs +1/−0
- src/tests/Hspec.hs +3/−1
- src/tests/IHaskell/Test/Completion.hs +15/−20
- src/tests/IHaskell/Test/Eval.hs +23/−27
- src/tests/IHaskell/Test/Hoogle.hs +63/−0
- src/tests/IHaskell/Test/Parser.hs +2/−15
ihaskell.cabal view
@@ -1,20 +1,20 @@ -- The name of the package. name: ihaskell --- The package version. See the Haskell package versioning policy (PVP) +-- The package version. See the Haskell package versioning policy (PVP) -- for standards guiding when and how versions should be incremented. -- http://www.haskell.org/haskellwiki/Package_versioning_policy--- PVP summary: +-+------- breaking API changes--- | | +----- non-breaking API additions--- | | | +--- code changes with no API change-version: 0.9.1.0+-- PVP summary: +--+------- breaking API changes+-- | | +----- non-breaking API additions+-- | | | +--- code changes with no API change+version: 0.10.0.0 -- A short (one-line) description of the package. synopsis: A Haskell backend kernel for the IPython project. -- A longer description of the package.-description: IHaskell is a Haskell backend kernel for the IPython project. This allows using Haskell via - a console or notebook interface. Additional packages may be installed to provide richer data visualizations. +description: IHaskell is a Haskell backend kernel for the IPython project. This allows using Haskell via+ a console or notebook interface. Additional packages may be installed to provide richer data visualizations. -- URL for the project homepage or repository. homepage: http://github.com/gibiansky/IHaskell@@ -28,12 +28,12 @@ -- The package author(s). author: Andrew Gibiansky --- An email address to which users can send suggestions, bug reports, and +-- An email address to which users can send suggestions, bug reports, and -- patches. maintainer: andrew.gibiansky@gmail.com -- A copyright notice.--- copyright: +-- copyright: category: Development @@ -42,17 +42,18 @@ -- Constraint on the version of Cabal needed to build this package. cabal-version: >=1.16 -data-files: +data-files: html/kernel.js html/logo-64x64.svg -flag binPkgDb- default: False- description: bin-package-db package needed (needed for GHC >= 7.10)- library hs-source-dirs: src default-language: Haskell2010+ ghc-options: -Wall++ if impl (ghc >= 8.4)+ ghc-options: -Wpartial-fields+ build-depends: aeson >=1.0, base >=4.9,@@ -81,18 +82,15 @@ strict >=0.3, system-argv0 -any, text >=0.11,+ time >= 1.6, transformers -any, unix >= 2.6, unordered-containers -any, utf8-string -any, uuid >=1.3, vector -any,- ipython-kernel >=0.9.1- if flag(binPkgDb)- build-depends: bin-package-db-- if impl(ghc >= 8.0)- build-depends: ghc-boot >=8.0 && <8.5+ ipython-kernel >=0.9.1,+ ghc-boot >=8.0 && <8.7 exposed-modules: IHaskell.Display IHaskell.Convert@@ -117,42 +115,50 @@ IHaskell.BrokenPackages IHaskell.CSS Paths_ihaskell- other-modules: IHaskellPrelude+ other-modules: StringUtils executable ihaskell -- .hs or .lhs file containing the Main module. main-is: Main.hs hs-source-dirs: main- other-modules: - IHaskellPrelude+ other-modules: Paths_ihaskell- ghc-options: -threaded -rtsopts- + ghc-options: -threaded -rtsopts -Wall++ if os(darwin)+ ghc-options: -optP-Wno-nonportable-include-path++ if impl (ghc >= 8.4)+ ghc-options: -Wpartial-fields+ -- Other library packages from which modules are imported. default-language: Haskell2010- build-depends: + build-depends: ihaskell -any,- base >=4.6 && < 4.12,+ base >=4.9 && < 4.13, text >=0.11, transformers -any,- ghc >=7.6 || < 7.11,+ ghc >=8.0 && < 8.7, process >=1.1, aeson >=0.7, bytestring >=0.10,+ unordered-containers -any, containers >=0.5, strict >=0.3, unix >= 2.6, directory -any,- ipython-kernel >=0.7-- if flag(binPkgDb)- build-depends: bin-package-db+ ipython-kernel >=0.7,+ unordered-containers -any Test-Suite hspec Type: exitcode-stdio-1.0- Ghc-Options: -threaded+ Ghc-Options: -threaded -Wall++ if impl (ghc >= 8.4)+ ghc-options: -Wpartial-fields+ Main-Is: Hspec.hs hs-source-dirs: src/tests other-modules:@@ -160,6 +166,7 @@ IHaskell.Test.Completion IHaskell.Test.Util IHaskell.Test.Parser+ IHaskell.Test.Hoogle default-language: Haskell2010 build-depends: base,@@ -174,10 +181,8 @@ directory, text, shelly,+ raw-strings-qq, setenv-- if flag(binPkgDb)- build-depends: bin-package-db source-repository head type: git
− main/IHaskellPrelude.hs
@@ -1,151 +0,0 @@-{-# language NoImplicitPrelude, DoAndIfThenElse, OverloadedStrings, ExtendedDefaultRules #-}{-# LANGUAGE CPP #-}-module IHaskellPrelude (- module IHaskellPrelude,- module X,-- -- Select reexports- Data.Typeable.Typeable,- Data.Typeable.cast,--#if MIN_VERSION_ghc(7,8,0)- Data.Typeable.Proxy,-- GHC.Exts.IsString,- GHC.Exts.IsList,-#endif-- System.IO.hPutStrLn,- System.IO.hPutStr,- System.IO.hPutChar,- System.IO.hPrint,- System.IO.stdout,- System.IO.stderr,- System.IO.stdin,- System.IO.getChar,- System.IO.getLine,- System.IO.writeFile,- System.IO.Handle,-- System.IO.Strict.readFile,- System.IO.Strict.getContents,- System.IO.Strict.hGetContents,-- Control.Exception.catch,- Control.Exception.SomeException,-- Control.Applicative.Applicative(..),- Control.Applicative.ZipList(..),- (Control.Applicative.<$>),-- Control.Concurrent.MVar.MVar,- Control.Concurrent.MVar.newMVar,- Control.Concurrent.MVar.newEmptyMVar,- Control.Concurrent.MVar.isEmptyMVar,- Control.Concurrent.MVar.readMVar,- Control.Concurrent.MVar.takeMVar,- Control.Concurrent.MVar.putMVar,- Control.Concurrent.MVar.modifyMVar,- Control.Concurrent.MVar.modifyMVar_,-- Data.IORef.IORef,- Data.IORef.readIORef,- Data.IORef.writeIORef,- Data.IORef.modifyIORef',- Data.IORef.newIORef,-- -- -- Miscellaneous names- Data.Map.Map,- GHC.IO.FilePath,- Data.Text.Text,- Data.ByteString.ByteString,- Text.Printf.printf,- Data.Function.on,- ) where--import Prelude--import Data.Monoid as X-import Data.Tuple as X-import Control.Monad as X-import Data.Maybe as X-import Data.Either as X-import Control.Monad.IO.Class as X-import Data.Ord as X-import GHC.Show as X-import GHC.Enum as X-import GHC.Num as X-import GHC.Real as X-import GHC.Err as X hiding (absentErr)-#if MIN_VERSION_ghc(8,0,0)-import GHC.Base as X hiding (Any, mapM, foldr, sequence, many, (<|>), Module(..))-#elif MIN_VERSION_ghc(7,10,0)-import GHC.Base as X hiding (Any, mapM, foldr, sequence, many, (<|>))-#else-import GHC.Base as X hiding (Any)-#endif-import Data.List as X hiding (head, last, tail, init, transpose, subsequences, permutations,- foldl, foldl1, maximum, minimum, scanl, scanl1, scanr, scanr1,- span, break, mapAccumL, mapAccumR, dropWhileEnd, (!!),- elemIndices, elemIndex, findIndex, findIndices, zip5, zip6,- zip7, zipWith5, zipWith6, zipWith7, unzip5, unzip6, unzip6,- delete, union, lookup, intersect, insert, deleteBy,- deleteFirstBy, unionBy, intersectBy, group, groupBy, insertBy,- maximumBy, minimumBy, genericLength, genericDrop, genericTake,- genericSplitAt, genericIndex, genericReplicate, inits, tails)--import qualified Control.Applicative-import qualified Data.Typeable-import qualified Data.IORef-import qualified Data.Map-import qualified Data.Text-import qualified Data.Text.Lazy-import qualified Data.ByteString-import qualified Data.ByteString.Lazy-import qualified Data.Function-import qualified GHC.Exts-import qualified System.IO-import qualified System.IO.Strict-import qualified GHC.IO-import qualified Text.Printf-import qualified Control.Exception-import qualified Control.Concurrent.MVar--import qualified Data.List-import qualified Prelude as P--type LByteString = Data.ByteString.Lazy.ByteString--type LText = Data.Text.Lazy.Text--(headMay, tailMay, lastMay, initMay, maximumMay, minimumMay) =- (wrapEmpty head, wrapEmpty tail, wrapEmpty last,- wrapEmpty init, wrapEmpty maximum, wrapEmpty minimum)- where- wrapEmpty :: ([a] -> b) -> [a] -> Maybe b- wrapEmpty _ [] = Nothing- wrapEmpty f xs = Just (f xs)--maximumByMay :: (a -> a -> Ordering) -> [a] -> Maybe a-maximumByMay _ [] = Nothing-maximumByMay f xs = Just (Data.List.maximumBy f xs)--minimumByMay :: (a -> a -> Ordering) -> [a] -> Maybe a-minimumByMay _ [] = Nothing-minimumByMay f xs = Just (Data.List.minimumBy f xs)--readMay :: Read a => String -> Maybe a-readMay = fmap fst . headMay . reads--putStrLn :: (MonadIO m) => String -> m ()-putStrLn = liftIO . P.putStrLn--putStr :: (MonadIO m) => String -> m ()-putStr = liftIO . P.putStr--putChar :: MonadIO m => Char -> m ()-putChar = liftIO . P.putChar--print :: (MonadIO m, Show a) => a -> m ()-print = liftIO . P.print
main/Main.hs view
@@ -7,27 +7,20 @@ import IHaskellPrelude import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS -- Standard library imports.-import Control.Concurrent (threadDelay) import Control.Concurrent.Chan import Control.Arrow (second)-import Data.Aeson-import System.Directory+import Data.Aeson hiding (Success) import System.Process (readProcess, readProcessWithExitCode) import System.Exit (exitSuccess, ExitCode(ExitSuccess)) import Control.Exception (try, SomeException) import System.Environment (getArgs)-#if MIN_VERSION_ghc(7,8,0) import System.Environment (setEnv)-#endif import System.Posix.Signals import qualified Data.Map as Map-import qualified Data.Text.Encoding as E+import qualified Data.HashMap.Strict as HashMap import Data.List (break, last) import Data.Version (showVersion) @@ -37,7 +30,6 @@ import IHaskell.Eval.Inspect (inspect) import IHaskell.Eval.Evaluate import IHaskell.Display-import IHaskell.Eval.Info import IHaskell.Eval.Widgets (widgetHandler) import IHaskell.Flags import IHaskell.IPython@@ -51,31 +43,12 @@ -- Cabal imports. import Paths_ihaskell(version) --- GHC API imports.-#if MIN_VERSION_ghc(8,4,0)-import GHC hiding (extensions, language, convert)-#else-import GHC hiding (extensions, language)-#endif---- | Compute the GHC API version number using the dist/build/autogen/cabal_macros.h-ghcVersionInts :: [Int]-ghcVersionInts = map (fromJust . readMay) . words . map dotToSpace $ VERSION_ghc- where- dotToSpace '.' = ' '- dotToSpace x = x--consoleBanner :: Text-consoleBanner =- "Welcome to IHaskell! Run `IHaskell --help` for more information.\n" <>- "Enter `:help` to learn more about IHaskell built-ins."- main :: IO () main = do args <- parseFlags <$> getArgs case args of Left errorMessage -> hPutStrLn stderr errorMessage- Right args -> ihaskell args+ Right xs -> ihaskell xs ihaskell :: Args -> IO () ihaskell (Args (ShowDefault helpStr) args) = showDefault helpStr args@@ -123,16 +96,16 @@ kernelSpecOpts { kernelSpecInstallPrefix = Just prefix } addFlag kernelSpecOpts KernelspecUseStack = kernelSpecOpts { kernelSpecUseStack = True }- addFlag kernelSpecOpts flag = error $ "Unknown flag" ++ show flag+ addFlag _kernelSpecOpts flag = error $ "Unknown flag" ++ show flag -- | Run the IHaskell language kernel. runKernel :: KernelSpecOptions -- ^ Various options from when the kernel was installed. -> String -- ^ File with kernel profile JSON (ports, etc). -> IO ()-runKernel kernelOpts profileSrc = do- let debug = kernelSpecDebug kernelOpts- libdir = kernelSpecGhcLibdir kernelOpts- useStack = kernelSpecUseStack kernelOpts+runKernel kOpts profileSrc = do+ let debug = kernelSpecDebug kOpts+ libdir = kernelSpecGhcLibdir kOpts+ useStack = kernelSpecUseStack kOpts -- Parse the profile file. let profileErr = error $ "ihaskell: "++profileSrc++": Failed to parse profile file"@@ -142,12 +115,11 @@ dir <- getIHaskellDir Stdin.recordKernelProfile dir profile -#if MIN_VERSION_ghc(7,8,0) when useStack $ do -- Detect if we have stack runResult <- try $ readProcessWithExitCode "stack" [] ""- let stack = - case runResult :: Either SomeException (ExitCode, String, String) of + let stack =+ case runResult :: Either SomeException (ExitCode, String, String) of Left _ -> False Right (exitCode, stackStdout, _) -> exitCode == ExitSuccess && "The Haskell Tool Stack" `isInfixOf` stackStdout @@ -160,7 +132,6 @@ in case tailMay val of Nothing -> return () Just val' -> setEnv var val'-#endif -- Serve on all sockets and ports defined in the profile. interface <- serveProfile profile debug@@ -174,21 +145,21 @@ interpret libdir True $ \hasSupportLibraries -> do -- Ignore Ctrl-C the first time. This has to go inside the `interpret`, because GHC API resets the -- signal handlers for some reason (completely unknown to me).- liftIO ignoreCtrlC+ _ <- liftIO ignoreCtrlC liftIO $ modifyMVar_ state $ \kernelState -> return $ kernelState { supportLibrariesAvailable = hasSupportLibraries } -- Initialize the context by evaluating everything we got from the command line flags.- let noPublish _ = return ()+ let noPublish _ _ = return () noWidget s _ = return s evaluator line = void $ do -- Create a new state each time. stateVar <- liftIO initialKernelState- state <- liftIO $ takeMVar stateVar- evaluate state line noPublish noWidget+ st <- liftIO $ takeMVar stateVar+ evaluate st line noPublish noWidget - confFile <- liftIO $ kernelSpecConfFile kernelOpts+ confFile <- liftIO $ kernelSpecConfFile kOpts case confFile of Just filename -> liftIO (readFile filename) >>= evaluator Nothing -> return ()@@ -225,7 +196,7 @@ installHandler keyboardSignal (CatchOnce $ putStrLn "Press Ctrl-C again to quit kernel.") Nothing - isCommMessage req = msgType (header req) `elem` [CommDataMessage, CommCloseMessage]+ isCommMessage req = mhMsgType (header req) `elem` [CommDataMessage, CommCloseMessage] -- Initial kernel state. initialKernelState :: IO (MVar KernelState)@@ -236,19 +207,11 @@ createReplyHeader parent = do -- Generate a new message UUID. newMessageId <- liftIO UUID.random- let repType = fromMaybe err (replyType $ msgType parent)- err = error $ "No reply for message " ++ show (msgType parent)+ let repType = fromMaybe err (replyType $ mhMsgType parent)+ err = error $ "No reply for message " ++ show (mhMsgType parent) - return- MessageHeader- { identifiers = identifiers parent- , parentHeader = Just parent- , metadata = Map.fromList []- , messageId = newMessageId- , sessionId = sessionId parent- , username = username parent- , msgType = repType- }+ return $ MessageHeader (mhIdentifiers parent) (Just parent) mempty+ newMessageId (mhSessionId parent) (mhUsername parent) repType -- | Compute a reply to a message. replyTo :: ZeroMQInterface -> Message -> MessageHeader -> KernelState -> Interpreter (KernelState, Message)@@ -258,7 +221,7 @@ replyTo interface KernelInfoRequest{} replyHeader state = do let send msg = liftIO $ writeChan (iopubChannel interface) msg - -- Notify the frontend that the Kernel is idle + -- Notify the frontend that the Kernel is idle idleHeader <- liftIO $ dupHeader replyHeader StatusMessage send $ PublishStatus idleHeader Idle @@ -289,8 +252,8 @@ -- Reply to a shutdown request by exiting the main thread. Before shutdown, reply to the request to -- let the frontend know shutdown is happening.-replyTo interface ShutdownRequest { restartPending = restartPending } replyHeader _ = liftIO $ do- writeChan (shellReplyChannel interface) $ ShutdownReply replyHeader restartPending+replyTo interface ShutdownRequest { restartPending = pending } replyHeader _ = liftIO $ do+ writeChan (shellReplyChannel interface) $ ShutdownReply replyHeader pending exitSuccess -- Reply to an execution request. The reply itself does not require computation, but this causes@@ -315,7 +278,7 @@ -- re-display with the updated output. displayed <- liftIO $ newMVar [] updateNeeded <- liftIO $ newMVar False- pagerOutput <- liftIO $ newMVar []+ pOut <- liftIO $ newMVar [] let execCount = getExecutionCounter state -- Let all frontends know the execution count and code that's about to run@@ -324,7 +287,7 @@ -- Run code and publish to the frontend as we go. let widgetMessageHandler = widgetHandler send replyHeader- publish = publishResult send replyHeader displayed updateNeeded pagerOutput (usePager state)+ publish = publishResult send replyHeader displayed updateNeeded pOut (usePager state) updatedState <- evaluate state (T.unpack code) publish widgetMessageHandler -- Notify the frontend that we're done computing.@@ -333,7 +296,7 @@ -- Take pager output if we're using the pager. pager <- if usePager state- then liftIO $ readMVar pagerOutput+ then liftIO $ readMVar pOut else return [] return (updatedState, ExecuteReply@@ -366,7 +329,7 @@ let start = pos - length matchedText end = pos- reply = CompleteReply replyHeader (map T.pack completions) start end Map.empty True+ reply = CompleteReply replyHeader (map T.pack completions) start end (Metadata HashMap.empty) True return (state, reply) replyTo _ req@InspectRequest{} replyHeader state = do@@ -401,14 +364,14 @@ -- -- Sending the message only on the shell_reply channel doesn't work, so we send it as a comm message -- on the iopub channel and return the SendNothing message.-replyTo interface open@CommOpen{} replyHeader state = do- let send msg = liftIO $ writeChan (iopubChannel interface) msg+replyTo interface ocomm@CommOpen{} replyHeader state = do+ let send = liftIO . writeChan (iopubChannel interface) - incomingUuid = commUuid open- target = commTargetName open+ incomingUuid = commUuid ocomm+ target = commTargetName ocomm targetMatches = target == "ipython.widget"- valueMatches = commData open == object ["widget_class" .= "ipywidgets.CommInfo"]+ valueMatches = commData ocomm == object ["widget_class" .= ("ipywidgets.CommInfo" :: Text)] commMap = openComms state uuidTargetPairs = map (second targetName) $ Map.toList commMap@@ -417,11 +380,11 @@ currentComms = object $ map pairProcessor $ (incomingUuid, "comm") : uuidTargetPairs - replyValue = object [ "method" .= "custom"+ replyValue = object [ "method" .= ("custom" :: Text) , "content" .= object ["comms" .= currentComms] ] - msg = CommData replyHeader (commUuid open) replyValue+ msg = CommData replyHeader (commUuid ocomm) replyValue -- To the iopub channel you go when (targetMatches && valueMatches) $ send msg@@ -439,7 +402,7 @@ -- MVars to hold intermediate data during publishing displayed <- liftIO $ newMVar [] updateNeeded <- liftIO $ newMVar False- pagerOutput <- liftIO $ newMVar []+ pOut <- liftIO $ newMVar [] let widgets = openComms kernelState uuid = commUuid req@@ -453,7 +416,7 @@ -- a function that executes an IO action and publishes the output to -- the frontend simultaneously. let run = capturedIO publish kernelState- publish = publishResult send replyHeader displayed updateNeeded pagerOutput toUsePager+ publish = publishResult send replyHeader displayed updateNeeded pOut toUsePager -- Notify the frontend that the kernel is busy busyHeader <- liftIO $ dupHeader replyHeader StatusMessage@@ -462,17 +425,20 @@ newState <- case Map.lookup uuid widgets of Nothing -> return kernelState Just (Widget widget) ->- case msgType $ header req of+ case mhMsgType $ header req of CommDataMessage -> do disp <- run $ comm widget dat communicate- pgrOut <- liftIO $ readMVar pagerOutput- liftIO $ publish $ FinalResult disp (if toUsePager then pgrOut else []) []+ pgrOut <- liftIO $ readMVar pOut+ liftIO $ publish (FinalResult disp (if toUsePager then pgrOut else []) []) Success return kernelState CommCloseMessage -> do disp <- run $ close widget dat- pgrOut <- liftIO $ readMVar pagerOutput- liftIO $ publish $ FinalResult disp (if toUsePager then pgrOut else []) []+ pgrOut <- liftIO $ readMVar pOut+ liftIO $ publish (FinalResult disp (if toUsePager then pgrOut else []) []) Success return kernelState { openComms = Map.delete uuid widgets }+ _ ->+ -- Only sensible thing to do.+ return kernelState -- Notify the frontend that the kernel is idle once again idleHeader <- liftIO $ dupHeader replyHeader StatusMessage
src/IHaskell/BrokenPackages.hs view
@@ -4,27 +4,22 @@ import IHaskellPrelude import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS import Text.Parsec import Text.Parsec.String-import Control.Applicative hiding ((<|>), many) import Shelly -data BrokenPackage = BrokenPackage { packageID :: String, brokenDeps :: [String] }+data BrokenPackage = BrokenPackage String [String] instance Show BrokenPackage where- show = packageID+ show (BrokenPackage packageID _) = packageID -- | Get a list of broken packages. This function internally shells out to `ghc-pkg`, and parses the -- output in order to determine what packages are broken. getBrokenPackages :: IO [String] getBrokenPackages = shelly $ do- silently $ errExit False $ run "ghc-pkg" ["check"]+ _ <- silently $ errExit False $ run "ghc-pkg" ["check"] checkOut <- lastStderr -- Get rid of extraneous things@@ -34,7 +29,7 @@ return $ case parse (many check) "ghc-pkg output" ghcPkgOutput of- Left err -> []+ Left _ -> [] Right pkgs -> map show pkgs check :: Parser BrokenPackage
src/IHaskell/CSS.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE NoImplicitPrelude #-} module IHaskell.CSS (ihaskellCSS) where import IHaskellPrelude@@ -5,7 +6,7 @@ ihaskellCSS :: String ihaskellCSS = unlines- [ + [ -- Custom IHaskell CSS "/* Styles used for the Hoogle display in the pager */" , ".hoogle-doc {"@@ -42,7 +43,7 @@ , ".hoogle-class {" , "font-weight: bold;" , "}"- , + , -- Styles used for basic displays ".get-type {" , "color: green;"@@ -75,13 +76,13 @@ , ".err-msg.in.collapse {" , "padding-top: 0.7em;" , "}"- , + , -- Code that will get highlighted before it is highlighted ".highlight-code {" , "white-space: pre;" , "font-family: monospace;" , "}"- , + , -- Hlint styles ".suggestion-warning { " , "font-weight: bold;"
src/IHaskell/Convert.hs view
@@ -1,14 +1,9 @@ {-# LANGUAGE NoImplicitPrelude #-} --- | Description : mostly reversible conversion between ipynb and lhs +-- | Description : mostly reversible conversion between ipynb and lhs module IHaskell.Convert (convert) where import IHaskellPrelude-import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS import Control.Monad.Identity (Identity(Identity), unless, when) import IHaskell.Convert.Args (ConvertSpec(..), fromJustConvertSpec, toConvertSpec)
src/IHaskell/Convert/Args.hs view
@@ -4,18 +4,13 @@ module IHaskell.Convert.Args (ConvertSpec(..), fromJustConvertSpec, toConvertSpec) where import IHaskellPrelude-import qualified Data.Text as T import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS import Control.Applicative ((<$>)) import Control.Monad.Identity (Identity(Identity)) import Data.Char (toLower) import Data.List (partition) import Data.Maybe (fromMaybe)-import qualified Data.Text.Lazy as T (pack, Text) import IHaskell.Flags (Argument(..), LhsStyle, lhsStyleBird, NotebookFormat(..)) import System.FilePath ((<.>), dropExtension, takeExtension) import Text.Printf (printf)@@ -40,11 +35,11 @@ , convertLhsStyle = Identity $ fromMaybe (LT.pack <$> lhsStyleBird) (convertLhsStyle convertSpec) } where- toIpynb = fromMaybe (error "Error: direction for conversion unknown")+ toIpynb = fromMaybe (error "fromJustConvertSpec: direction for conversion unknown") (convertToIpynb convertSpec) (inputFile, outputFile) = case (convertInput convertSpec, convertOutput convertSpec) of- (Nothing, Nothing) -> error "Error: no files specified for conversion"+ (Nothing, Nothing) -> error "fromJustConvertSpec: no files specified for conversion" (Just i, Nothing) | toIpynb -> (i, dropExtension i <.> "ipynb") | otherwise -> (i, dropExtension i <.> "lhs")
src/IHaskell/Convert/IpynbToLhs.hs view
@@ -4,11 +4,8 @@ module IHaskell.Convert.IpynbToLhs (ipynbToLhs) where import IHaskellPrelude-import qualified Data.Text as T import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS import Data.Aeson (decode, Object, Value(Array, Object, String)) import Data.Vector (Vector)@@ -49,12 +46,12 @@ = s convCell sty object | Just (String "code") <- lookup "cell_type" object,- Just (Array i) <- lookup "source" object,+ Just (Array a) <- lookup "source" object, Just (Array o) <- lookup "outputs" object,- ~(Just i) <- concatWithPrefix (lhsCodePrefix sty) i,- o <- fromMaybe mempty (convOutputs sty o)+ ~(Just i) <- concatWithPrefix (lhsCodePrefix sty) a,+ o2 <- fromMaybe mempty (convOutputs sty o) = "\n" <>- lhsBeginCode sty <> i <> lhsEndCode sty <> "\n" <> o <> "\n"+ lhsBeginCode sty <> i <> lhsEndCode sty <> "\n" <> o2 <> "\n" convCell _ _ = "IHaskell.Convert.convCell: unknown cell" convOutputs :: LhsStyle LT.Text
src/IHaskell/Convert/LhsToIpynb.hs view
@@ -6,13 +6,11 @@ import IHaskellPrelude import qualified Data.Text as T import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS import Data.Aeson ((.=), encode, object, Value(Array, Bool, Number, String, Null)) import Data.Char (isSpace)-import qualified Data.Vector as V (fromList, singleton)+import qualified Data.Vector as V import qualified Data.List as List import IHaskell.Flags (LhsStyle(LhsStyle))@@ -97,12 +95,12 @@ groupClassified :: [CellLine LText] -> [Cell [LText]] groupClassified (CodeLine a:x)- | (c, x) <- List.span isCode x,- (_, x) <- List.span isEmptyMD x,- (o, x) <- List.span isOutput x- = Code (a : map untag c) (map untag o) : groupClassified x+ | (c, x1) <- List.span isCode x,+ (_, x2) <- List.span isEmptyMD x1,+ (o, x3) <- List.span isOutput x2+ = Code (a : map untag c) (map untag o) : groupClassified x3 groupClassified (MarkdownLine a:x)- | (m, x) <- List.span isMD x = Markdown (a : map untag m) : groupClassified x+ | (m, x1) <- List.span isMD x = Markdown (a : map untag m) : groupClassified x1 groupClassified (OutputLine a:x) = Markdown [a] : groupClassified x groupClassified [] = []
src/IHaskell/Display.hs view
@@ -42,7 +42,7 @@ -- ** Image and data encoding functions Width, Height,- Base64(..),+ Base64, encode64, base64, @@ -57,14 +57,10 @@ import IHaskellPrelude import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS import qualified Data.ByteString.Char8 as CBS import Data.Serialize as Serialize import qualified Data.ByteString.Base64 as Base64-import Data.Aeson (Value) import System.Directory (getTemporaryDirectory, setCurrentDirectory) import Control.Concurrent.STM (atomically)@@ -80,28 +76,6 @@ type Base64 = Text --- | these instances cause the image, html etc. which look like:------ > Display--- > [Display]--- > IO [Display]--- > IO (IO Display)------ be run the IO and get rendered (if the frontend allows it) in the pretty form.-instance IHaskellDisplay a => IHaskellDisplay (IO a) where- display = (display =<<)--instance IHaskellDisplay Display where- display = return--instance IHaskellDisplay DisplayData where- display disp = return $ Display [disp]--instance IHaskellDisplay a => IHaskellDisplay [a] where- display disps = do- displays <- mapM display disps- return $ ManyDisplay displays- -- | Encode many displays into a single one. All will be output. many :: [Display] -> Display many = ManyDisplay@@ -201,6 +175,7 @@ -- | Convenience function for client libraries. Switch to a temporary directory so that any files we -- create aren't visible. On Unix, this is usually /tmp.+switchToTmpDir :: IO () switchToTmpDir = void (try switchDir :: IO (Either SomeException ())) where switchDir =
src/IHaskell/Eval/Completion.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, DoAndIfThenElse, TypeFamilies, FlexibleContexts #-}+{-# LANGUAGE CPP, NoImplicitPrelude, DoAndIfThenElse, TypeFamilies, FlexibleContexts #-} {- | Description: Generates tab completion options.@@ -13,37 +13,21 @@ module IHaskell.Eval.Completion (complete, completionTarget, completionType, CompletionType(..)) where import IHaskellPrelude-import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS import Control.Applicative ((<$>))-import Data.ByteString.UTF8 hiding (drop, take, lines, length) import Data.Char-import Data.List (nub, init, last, head, elemIndex, concatMap)+import Data.List (nub, init, last, elemIndex, concatMap) import qualified Data.List.Split as Split import qualified Data.List.Split.Internals as Split-import Data.Maybe (fromJust) import System.Environment (getEnv) -import GHC hiding (Qualified)-#if MIN_VERSION_ghc(8,2,0)+import GHC import GHC.PackageDb-#elif MIN_VERSION_ghc(7,10,0)-import GHC.PackageDb (ExposedModule(exposedName))-#endif import DynFlags import GhcMonad-import qualified GhcMonad-import PackageConfig import Outputable (showPpr)-import MonadUtils (MonadIO) - import System.Directory-import System.FilePath import Control.Exception (try) import System.Console.Haskeline.Completion@@ -63,16 +47,15 @@ | KernelOption String | Extension String deriving (Show, Eq)+ #if MIN_VERSION_ghc(8,2,0)+exposedName :: (a, b) -> a exposedName = fst #endif-#if MIN_VERSION_ghc(7,10,0)++extName :: FlagSpec flag -> String extName (FlagSpec { flagSpecName = name }) = name-#else-extName (name, _, _) = name -exposedName = id-#endif complete :: String -> Int -> Interpreter (String, [String]) complete code posOffset = do -- Get the line of code which is being completed and offset within that line@@ -93,11 +76,7 @@ let Just db = pkgDatabase flags getNames = map (moduleNameString . exposedName) . exposedModules-#if MIN_VERSION_ghc(8,0,0) moduleNames = nub $ concatMap getNames $ concatMap snd db-#else- moduleNames = nub $ concatMap getNames db-#endif let target = completionTarget line pos completion = completionType line pos target@@ -106,7 +85,7 @@ case completion of HsFilePath _ match -> match FilePath _ match -> match- otherwise -> intercalate "." target+ _ -> intercalate "." target options <- case completion of Empty -> return []@@ -114,9 +93,8 @@ Identifier candidate -> return $ filter (candidate `isPrefixOf`) unqualNames - Qualified moduleName candidate -> do- trueName <- getTrueModuleName moduleName- let prefix = intercalate "." [moduleName, candidate]+ Qualified mName candidate -> do+ let prefix = intercalate "." [mName, candidate] completions = filter (prefix `isPrefixOf`) qualNames return completions @@ -127,17 +105,11 @@ return $ filter (prefix `isPrefixOf`) moduleNames DynFlag ext -> do- -- Possibly leave out the fLangFlags? The -XUndecidableInstances vs. obsolete- -- -fallow-undecidable-instances.- let kernelOptNames = concatMap getSetName kernelOpts- otherNames = ["-package", "-Wall", "-w"]+ -- Possibly leave out the fLangFlags?+ let otherNames = ["-package", "-Wall", "-w"] fNames = map extName fFlags ++-#if MIN_VERSION_ghc(8,0,0) map extName wWarningFlags ++-#else- map extName fWarningFlags ++-#endif map extName fLangFlags fNoNames = map ("no" ++) fNames fAllNames = map ("-f" ++) (fNames ++ fNoNames)@@ -155,34 +127,17 @@ xNoNames = map ("No" ++) xNames return $ filter (ext `isPrefixOf`) $ xNames ++ xNoNames - HsFilePath lineUpToCursor match -> completePathWithExtensions [".hs", ".lhs"]+ HsFilePath lineUpToCursor _match -> completePathWithExtensions [".hs", ".lhs"] lineUpToCursor - FilePath lineUpToCursor match -> completePath lineUpToCursor+ FilePath lineUpToCursor _match -> completePath lineUpToCursor KernelOption str -> return $ filter (str `isPrefixOf`) (concatMap getOptionName kernelOpts) return (matchedText, options) -getTrueModuleName :: String -> Interpreter String-getTrueModuleName name = do- -- Only use the things that were actually imported- let onlyImportDecl (IIDecl decl) = Just decl- onlyImportDecl _ = Nothing - -- Get all imports that we use.- imports <- catMaybes <$> map onlyImportDecl <$> getContext-- -- Find the ones that have a qualified name attached. If this name isn't one of them, it already is- -- the true name.- flags <- getSessionDynFlags- let qualifiedImports = filter (isJust . ideclAs) imports- hasName imp = name == (showPpr flags . fromJust . ideclAs) imp- case find hasName qualifiedImports of- Nothing -> return name- Just trueImp -> return $ showPpr flags $ unLoc $ ideclName trueImp- -- | Get which type of completion this is from the surrounding context. completionType :: String -- ^ The line on which the completion is being done. -> Int -- ^ Location of the cursor in the line.@@ -241,7 +196,7 @@ else [] Left _ -> Empty - cursorInString str loc = nquotes (take loc str) `mod` 2 /= 0+ cursorInString str lcn = nquotes (take lcn str) `mod` 2 /= (0 :: Int) nquotes ('\\':'"':xs) = nquotes xs nquotes ('"':xs) = 1 + nquotes xs@@ -255,12 +210,12 @@ where go acc rest = case rest of- '"':'\\':rem -> go ('"' : acc) rem- '"':rem -> acc- ' ':'\\':rem -> go (' ' : acc) rem- ' ':rem -> acc- x:rem -> go (x : acc) rem- [] -> acc+ '"':'\\':xs -> go ('"' : acc) xs+ '"':_ -> acc+ ' ':'\\':xs -> go (' ' : acc) xs+ ' ':_ -> acc+ x:xs -> go (x : acc) xs+ [] -> acc -- | Get the word under a given cursor location. completionTarget :: String -> Int -> [String]@@ -269,7 +224,7 @@ pieceToComplete = map fst <$> find (elem cursor . map snd) pieces pieces = splitAlongCursor $ Split.split splitter $ zip code [1 ..] splitter = Split.defaultSplitter- { + { -- Split using only the characters, which are the first elements of the (char, index) tuple Split.delimiter = Split.Delimiter [uncurry isDelim] -- Condense multiple delimiters into one and then drop them.@@ -278,7 +233,7 @@ } isDelim :: Char -> Int -> Bool- isDelim char idx = char `elem` neverIdent || isSymbol char+ isDelim char _idx = char `elem` neverIdent || isSymbol char splitAlongCursor :: [[(Char, Int)]] -> [[(Char, Int)]] splitAlongCursor [] = []@@ -318,8 +273,8 @@ acceptAll = const True completePathWithExtensions :: [String] -> String -> Interpreter [String]-completePathWithExtensions extensions line =- completePathFilter (extensionIsOneOf extensions) acceptAll line ""+completePathWithExtensions extns line =+ completePathFilter (extensionIsOneOf extns) acceptAll line "" where acceptAll = const True extensionIsOneOf exts str = any correctEnding exts
src/IHaskell/Eval/Evaluate.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE NoOverloadedStrings, TypeSynonymInstances, GADTs, CPP #-}+{-# LANGUAGE NoOverloadedStrings, NoImplicitPrelude, TypeSynonymInstances, GADTs, CPP #-} {- | Description : Wrapper around GHC API, exposing a single `evaluate` interface that runs a statement, declaration, import, or directive.@@ -19,64 +19,36 @@ ) where import IHaskellPrelude-import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS import Control.Concurrent (forkIO, threadDelay) import Data.Foldable (foldMap)-import Prelude (putChar, head, tail, last, init, (!!))-import Data.List (findIndex, and, foldl1, nubBy)-import Text.Printf+import Prelude (head, tail, last, init)+import Data.List (nubBy) import Data.Char as Char import Data.Dynamic-import Data.Typeable import qualified Data.Serialize as Serialize import System.Directory #if !MIN_VERSION_base(4,8,0) import System.Posix.IO (createPipe) #endif import System.Posix.IO (fdToHandle)-import System.IO (hGetChar, hSetEncoding, utf8, hFlush)+import System.IO (hGetChar, hSetEncoding, utf8) import System.Random (getStdGen, randomRs)-import Unsafe.Coerce-import Control.Monad (guard) import System.Process import System.Exit-import Data.Maybe (fromJust)-import qualified Control.Monad.IO.Class as MonadIO (MonadIO, liftIO)-import qualified MonadUtils (MonadIO, liftIO)+import Data.Maybe (mapMaybe) import System.Environment (getEnv)-import qualified Data.Map as Map import qualified GHC.Paths-import NameSet-import Name-import PprTyThing import InteractiveEval import DynFlags-import Type import Exception (gtry) import HscTypes-import HscMain-import qualified Linker-import TcType-import Unify-import InstEnv-#if MIN_VERSION_ghc(7, 8, 0)-import GhcMonad (liftIO, withSession)-#else-import GhcMonad (withSession)-#endif+import GhcMonad (liftIO) import GHC hiding (Stmt, TypeSig) import Exception hiding (evaluate) import Outputable hiding ((<>)) import Packages-import Module hiding (Module)-import qualified Pretty-import FastString import Bag import qualified ErrUtils @@ -87,18 +59,18 @@ import IHaskell.Display import qualified IHaskell.Eval.Hoogle as Hoogle import IHaskell.Eval.Util-import IHaskell.Eval.Widgets import IHaskell.BrokenPackages-import qualified IHaskell.IPython.Message.UUID as UUID import StringUtils (replace, split, strip, rstrip) +#if MIN_VERSION_ghc(8,2,0)+import FastString (unpackFS)+#else import Paths_ihaskell (version) import Data.Version (versionBranch)+#endif -data ErrorOccurred = Success- | Failure- deriving (Show, Eq) + -- | Set GHC's verbosity for debugging ghcVerbosity :: Maybe Int ghcVerbosity = Nothing -- Just 5@@ -126,12 +98,7 @@ write state x = when (kernelDebug state) $ liftIO $ hPutStrLn stderr $ "DEBUG: " ++ x type Interpreter = Ghc-#if MIN_VERSION_ghc(7, 8, 0)- -- GHC 7.8 exports a MonadIO instance for Ghc-#else-instance MonadIO.MonadIO Interpreter where- liftIO = MonadUtils.liftIO-#endif+ requiredGlobalImports :: [String] requiredGlobalImports = [ "import qualified Prelude as IHaskellPrelude"@@ -151,12 +118,12 @@ -- | Interpreting function for testing. testInterpret :: Interpreter a -> IO a-testInterpret val = interpret GHC.Paths.libdir False (const val)+testInterpret v = interpret GHC.Paths.libdir False (const v) -- | Evaluation function for testing. testEvaluate :: String -> IO () testEvaluate str = void $ testInterpret $- evaluate defaultKernelState str (const $ return ()) (\state _ -> return state)+ evaluate defaultKernelState str (\_ _ -> return ()) (\state _ -> return state) -- | Run an interpreting action. This is effectively runGhc with initialization and importing. First -- argument indicates whether `stdin` is handled specially, which cannot be done in a testing@@ -194,23 +161,13 @@ Just cfg -> let PackageName name = packageName cfg in unpackFS name-#elif MIN_VERSION_ghc(8,0,0)- fromMaybe "(unknown)" (unitIdPackageIdString dflags $ packageConfigId pkg_cfg)-#elif MIN_VERSION_ghc(7,10,2)- fromMaybe "(unknown)" (packageKeyPackageIdString dflags $ packageConfigId pkg_cfg)-#elif MIN_VERSION_ghc(7,10,0)- packageKeyPackageIdString dflags . packageConfigId #else- packageIdString . packageConfigId+ fromMaybe "(unknown)" (unitIdPackageIdString dflags $ packageConfigId pkg_cfg) #endif getPackageConfigs :: DynFlags -> [PackageConfig] getPackageConfigs dflags =-#if MIN_VERSION_ghc(8,0,0) foldMap snd pkgDb-#else- pkgDb-#endif where Just pkgDb = pkgDatabase dflags @@ -222,9 +179,9 @@ -- version of the ihaskell library. Also verify that the packages we load are not broken. dflags <- getSessionDynFlags broken <- liftIO getBrokenPackages- (dflags, _) <- liftIO $ initPackages dflags- let db = getPackageConfigs dflags- packageNames = map (packageIdString' dflags) db+ (dflgs, _) <- liftIO $ initPackages dflags+ let db = getPackageConfigs dflgs+ packageNames = map (packageIdString' dflgs) db initStr = "ihaskell-" @@ -236,17 +193,6 @@ iHaskellPkgName = initStr ++ intercalate "." (map show (versionBranch version)) #endif -#if !MIN_VERSION_ghc(8,0,0)- unitId = packageId-#endif-- dependsOnRight pkg = not $ null $ do- pkg <- db- depId <- depends pkg- dep <- filter ((== depId) . unitId) db- let idString = packageIdString' dflags dep- guard (iHaskellPkgName `isPrefixOf` idString)- displayPkgs = [ pkgName | pkgName <- packageNames , Just (x:_) <- [stripPrefix initStr pkgName]@@ -257,17 +203,20 @@ -- Generate import statements all Display modules. let capitalize :: String -> String+ capitalize [] = [] capitalize (first:rest) = Char.toUpper first : rest importFmt = "import IHaskell.Display.%s" - dropFirstAndLast :: [a] -> [a]- dropFirstAndLast = reverse . drop 1 . reverse . drop 1 - toImportStmt :: String -> String #if MIN_VERSION_ghc(8,2,0)+ toImportStmt :: String -> String toImportStmt = printf importFmt . concatMap capitalize . drop 1 . split "-" #else+ dropFirstAndLast :: [a] -> [a]+ dropFirstAndLast = reverse . drop 1 . reverse . drop 1++ toImportStmt :: String -> String toImportStmt = printf importFmt . concatMap capitalize . dropFirstAndLast . split "-" #endif @@ -297,8 +246,9 @@ void $ execStmt "let it = ()" execOptions -- | Publisher for IHaskell outputs. The first argument indicates whether this output is final--- (true) or intermediate (false).-type Publisher = (EvaluationResult -> IO ())+-- (true) or intermediate (false). The second argument indicates whether the evaluation+-- completed successfully (Success) or an error occurred (Failure).+type Publisher = (EvaluationResult -> ErrorOccurred -> IO ()) -- | Output of a command evaluation. data EvalOut =@@ -311,11 +261,11 @@ } cleanString :: String -> String-cleanString x = if allBrackets+cleanString istr = if allBrackets then clean- else str+ else istr where- str = strip x+ str = strip istr l = lines str allBrackets = all (fAny [isPrefixOf ">", null]) l fAny fs x = any ($ x) fs@@ -346,14 +296,16 @@ when (getLintStatus kernelState /= LintOff) $ liftIO $ do lintSuggestions <- lint cmds unless (noResults lintSuggestions) $- output $ FinalResult lintSuggestions [] []+ output (FinalResult lintSuggestions [] []) Success runUntilFailure kernelState (map unloc cmds ++ [storeItCommand execCount]) -- Print all parse errors.- errs -> do+ _ -> do forM_ errs $ \err -> do out <- evalCommand output err kernelState- liftIO $ output $ FinalResult (evalResult out) [] []+ liftIO $ output+ (FinalResult (evalResult out) [] [])+ (evalStatus out) return kernelState return updated { getExecutionCounter = execCount + 1 }@@ -385,9 +337,10 @@ Just disps -> evalResult evalOut <> disps -- Output things only if they are non-empty.- let empty = noResults result && null (evalPager evalOut)- unless empty $- liftIO $ output $ FinalResult result (evalPager evalOut) []+ unless (noResults result && null (evalPager evalOut)) $+ liftIO $ output+ (FinalResult result (evalPager evalOut) [])+ (evalStatus evalOut) let tempMsgs = evalMsgs evalOut tempState = evalState evalOut { evalMsgs = [] }@@ -431,7 +384,7 @@ -> [WidgetMsg] -> (KernelState -> [WidgetMsg] -> IO KernelState) -> Interpreter KernelState-flushWidgetMessages state evalMsgs widgetHandler = do+flushWidgetMessages state evalmsgs widgetHandler = do -- Capture all widget messages queued during code execution extracted <- extractValue "IHaskell.Eval.Widgets.relayWidgetMessages" liftIO $@@ -444,16 +397,12 @@ messages <- messagesIO -- Handle all the widget messages- let commMessages = evalMsgs ++ messages+ let commMessages = evalmsgs ++ messages widgetHandler state commMessages getErrMsgDoc :: ErrUtils.ErrMsg -> SDoc-#if MIN_VERSION_ghc(8,0,0) getErrMsgDoc = ErrUtils.pprLocErrMsg-#else-getErrMsgDoc msg = ErrUtils.errMsgShortString msg $$ ErrUtils.errMsgContext msg-#endif safely :: KernelState -> Interpreter EvalOut -> Interpreter EvalOut safely state = ghandle handler . ghandle sourceErrorHandler@@ -534,8 +483,8 @@ -- Return whether this module prevents the loading of the one we're trying to load. If a module B -- exist, we cannot load A.B. All modules must have unique last names (where A.B has last name B). -- However, we *can* just reload a module.- preventsLoading mod =- let pieces = moduleNameOf mod+ preventsLoading md =+ let pieces = moduleNameOf md in last namePieces == last pieces && namePieces /= pieces -- If we've loaded anything with the same last name, we can't use this. Otherwise, GHC tries to load@@ -551,7 +500,7 @@ Nothing -> doLoadModule modName modName -- | Directives set via `:set`.-evalCommand output (Directive SetDynFlag flagsStr) state = safely state $ do+evalCommand _output (Directive SetDynFlag flagsStr) state = safely state $ do write state $ "All Flags: " ++ flagsStr -- Find which flags are IHaskell flags, and which are GHC flags@@ -569,14 +518,14 @@ if null flags then do- flags <- getSessionDynFlags+ flgs <- getSessionDynFlags return EvalOut { evalStatus = Success , evalResult = Display- [ plain $ showSDoc flags $ vcat- [ pprDynFlags False flags- , pprLanguages False flags+ [ plain $ showSDoc flgs $ vcat+ [ pprDynFlags False flgs+ , pprLanguages False flgs ] ] , evalState = state@@ -585,9 +534,9 @@ } else do -- Apply all IHaskell flag updaters to the state to get the new state- let state' = foldl' (.) id (map (fromJust . ihaskellFlagUpdater) ihaskellFlags) state+ let state' = foldl' (.) id (mapMaybe ihaskellFlagUpdater ihaskellFlags) state errs <- setFlags ghcFlags- let display =+ let disp = case errs of [] -> mempty _ -> displayError $ intercalate "\n" errs@@ -604,7 +553,7 @@ return EvalOut { evalStatus = Success- , evalResult = display+ , evalResult = disp , evalState = state' , evalPager = [] , evalMsgs = []@@ -615,7 +564,7 @@ let set = concatMap (" -X" ++) $ words opts evalCommand output (Directive SetDynFlag set) state -evalCommand output (Directive LoadModule mods) state = wrapExecution state $ do+evalCommand _output (Directive LoadModule mods) state = wrapExecution state $ do write state $ "Load Module: " ++ mods let stripped@(firstChar:remainder) = mods (modules, removeModule) =@@ -630,9 +579,9 @@ return mempty -evalCommand a (Directive SetOption opts) state = do+evalCommand _output (Directive SetOption opts) state = do write state $ "Option: " ++ opts- let (existing, nonExisting) = partition optionExists $ words opts+ let nonExisting = filter (not . optionExists) $ words opts if not $ null nonExisting then let err = "No such options: " ++ intercalate ", " nonExisting in return@@ -682,15 +631,16 @@ doLoadModule filename modName return (ManyDisplay displays) -evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $- case words cmd of+evalCommand publish (Directive ShellCmd cmd) state = wrapExecution state $+ -- Assume the first character of 'cmd' is '!'.+ case words $ drop 1 cmd of "cd":dirs -> do -- Get home so we can replace '~` with it. homeEither <- liftIO (try $ getEnv "HOME" :: IO (Either SomeException String)) let home = case homeEither of- Left _ -> "~"- Right val -> val+ Left _ -> "~"+ Right v -> v let directory = replace "~" home $ unwords dirs exists <- liftIO $ doesDirectoryExist directory@@ -701,19 +651,19 @@ liftIO $ setCurrentDirectory directory -- Set the directory for user code.- let cmd = printf "IHaskellDirectory.setCurrentDirectory \"%s\"" $+ let cmd1 = printf "IHaskellDirectory.setCurrentDirectory \"%s\"" $ replace " " "\\ " $ replace "\"" "\\\"" directory- execStmt cmd execOptions+ _ <- execStmt cmd1 execOptions return mempty else return $ displayError $ printf "No such directory: '%s'" directory- cmd -> liftIO $ do- (pipe, handle) <- createPipe'- let initProcSpec = shell $ unwords cmd+ cmd1 -> liftIO $ do+ (pipe, hdl) <- createPipe'+ let initProcSpec = shell $ unwords cmd1 procSpec = initProcSpec { std_in = Inherit- , std_out = UseHandle handle- , std_err = UseHandle handle+ , std_out = UseHandle hdl+ , std_err = UseHandle hdl } (_, _, _, process) <- createProcess procSpec @@ -741,21 +691,17 @@ modifyMVar_ outputAccum (return . (++ nextChunk)) -- Check if we're done.- exitCode <- getProcessExitCode process- let computationDone = isJust exitCode-- when computationDone $ do- nextChunk <- readChars pipe "" maxSize- modifyMVar_ outputAccum (return . (++ nextChunk))-- if not computationDone- then do+ mExitCode <- getProcessExitCode process+ case mExitCode of+ Nothing -> do -- Write to frontend and repeat.- readMVar outputAccum >>= output+ readMVar outputAccum >>= flip output Success loop- else do+ Just exitCode -> do+ next <- readChars pipe "" maxSize+ modifyMVar_ outputAccum (return . (++ next)) out <- readMVar outputAccum- case fromJust exitCode of+ case exitCode of ExitSuccess -> return $ Display [plain out] ExitFailure code -> do let errMsg = "Process exited with error code " ++ show code@@ -819,11 +765,11 @@ strings <- unlines <$> getDescription str -- Make pager work without html by porting to newer architecture- let htmlify str =+ let htmlify str1 = html $ concat [ "<div style='background: rgb(247, 247, 247);'><form><textarea id='code'>"- , str+ , str1 , "</textarea></form></div>" , "<script>CodeMirror.fromTextArea(document.getElementById('code')," , " {mode: 'haskell', readOnly: 'nocursor'});</script>"@@ -888,8 +834,8 @@ -- If it typechecks as a DecsQ, we do not want to display the DecsQ, we just want the -- declaration made. do- write state "Suppressing display for template haskell declaration"- GHC.runDecls expr+ _ <- write state "Suppressing display for template haskell declaration"+ _ <- GHC.runDecls expr return EvalOut { evalStatus = Success@@ -941,7 +887,7 @@ removeSvg (Display disps) = Display $ filter (not . isSvg) disps removeSvg (ManyDisplay disps) = ManyDisplay $ map removeSvg disps - useDisplay displayExpr = do+ useDisplay _displayExpr = do -- If there are instance matches, convert the object into a Display. We also serialize it into a -- bytestring. We get the bytestring IO action as a dynamic and then convert back to a bytestring, -- which we promptly unserialize. Note that attempting to do this without the serialization to@@ -957,8 +903,8 @@ Failure -> return evalOut Success -> wrapExecution state $ do -- Compile the display data into a bytestring.- let compileExpr = "fmap IHaskell.Display.serializeDisplay (IHaskell.Display.display it)"- displayedBytestring <- dynCompileExpr compileExpr+ let cexpr = "fmap IHaskell.Display.serializeDisplay (IHaskell.Display.display it)"+ displayedBytestring <- dynCompileExpr cexpr -- Convert from the bytestring into a display. case fromDynamic displayedBytestring of@@ -967,30 +913,27 @@ bytestring <- liftIO bytestringIO case Serialize.decode bytestring of Left err -> error err- Right display ->+ Right disp -> return $ if useSvg state- then display :: Display- else removeSvg display+ then disp :: Display+ else removeSvg disp #if MIN_VERSION_ghc(8,2,0)- isIO expr = attempt $ exprType TM_Inst $ printf "((\\x -> x) :: IO a -> IO a) (%s)" expr+ isIO exp = attempt $ exprType TM_Inst $ printf "((\\x -> x) :: IO a -> IO a) (%s)" exp #else- isIO expr = attempt $ exprType $ printf "((\\x -> x) :: IO a -> IO a) (%s)" expr+ isIO exp = attempt $ exprType $ printf "((\\x -> x) :: IO a -> IO a) (%s)" exp #endif postprocessShowError :: EvalOut -> EvalOut postprocessShowError evalOut = evalOut { evalResult = Display $ map postprocess disps } where Display disps = evalResult evalOut- text = extractPlain disps+ txt = extractPlain disps - postprocess (DisplayData MimeHtml _) = html $ printf- fmt- unshowableType- (formatErrorWithClass "err-msg collapse"- text)- script+ postprocess (DisplayData MimeHtml _) =+ html $ printf fmt unshowableType+ (formatErrorWithClass "err-msg collapse" txt) script where fmt = "<div class='collapse-group'><span class='btn btn-default' href='#' id='unshowable'>Unshowable:<span class='show-type'>%s</span></span>%s</div><script>%s</script>" script = unlines@@ -1005,7 +948,7 @@ postprocess other = other unshowableType = fromMaybe "" $ do- let pieces = words text+ let pieces = words txt before = takeWhile (/= "arising") pieces after = init $ unwords $ tail $ dropWhile (/= "(Show") before @@ -1052,7 +995,7 @@ , evalMsgs = [] } -evalCommand _ (Pragma (PragmaUnsupported pragmaType) pragmas) state = wrapExecution state $+evalCommand _ (Pragma (PragmaUnsupported pragmaType) _pragmas) state = wrapExecution state $ return $ displayError $ "Pragmas of type " ++ pragmaType ++ "\nare not supported." evalCommand output (Pragma PragmaLanguage pragmas) state = do@@ -1080,14 +1023,10 @@ -- Compile loaded modules. flags <- getSessionDynFlags errRef <- liftIO $ newIORef []- setSessionDynFlags $ flip gopt_set Opt_BuildDynamicToo+ _ <- setSessionDynFlags $ flip gopt_set Opt_BuildDynamicToo flags { hscTarget = objTarget flags-#if MIN_VERSION_ghc(8,0,0)- , log_action = \dflags sev srcspan ppr _style msg -> modifyIORef' errRef (showSDoc flags msg :)-#else- , log_action = \dflags sev srcspan ppr msg -> modifyIORef' errRef (showSDoc flags msg :)-#endif+ , log_action = \_dflags _sev _srcspan _ppr _style msg -> modifyIORef' errRef (showSDoc flags msg :) } -- Load the new target.@@ -1113,7 +1052,7 @@ Succeeded -> IIDecl (simpleImportDecl $ mkModuleName modName) : importedModules -- Switch back to interpreted mode.- setSessionDynFlags flags+ _ <- setSessionDynFlags flags case result of Succeeded -> return mempty@@ -1127,35 +1066,20 @@ print $ show exception -- Explicitly clear targets setTargets []- load LoadAllTargets+ _ <- load LoadAllTargets -- Switch to interpreted mode! flags <- getSessionDynFlags- setSessionDynFlags flags { hscTarget = HscInterpreted }+ _ <- setSessionDynFlags flags { hscTarget = HscInterpreted } -- Return to old context, make sure we have `it`. setContext imported initializeItVariable return $ displayError $ "Failed to load module " ++ modName ++ ": " ++ show exception-#if MIN_VERSION_ghc(7,8,0)-objTarget flags = defaultObjectTarget $ targetPlatform flags-#else-objTarget flags = defaultObjectTarget-#endif-keepingItVariable :: Interpreter a -> Interpreter a-keepingItVariable act = do- -- Generate the it variable temp name- gen <- liftIO getStdGen- let rand = take 20 $ randomRs ('0', '9') gen- var name = name ++ rand- goStmt s = execStmt s execOptions- itVariable = var "it_var_temp_" - goStmt $ printf "let %s = it" itVariable- val <- act- goStmt $ printf "let it = %s" itVariable- act+objTarget :: DynFlags -> HscTarget+objTarget flags = defaultObjectTarget $ targetPlatform flags data Captured a = CapturedStmt String | CapturedIO (IO a)@@ -1208,44 +1132,24 @@ runWithResult (CapturedStmt str) = goStmt str runWithResult (CapturedIO io) = do- status <- gcatch (liftIO io >> return NoException) (return . AnyException)+ stat <- gcatch (liftIO io >> return NoException) (return . AnyException) return $- case status of+ case stat of NoException -> ExecComplete (Right []) 0 AnyException e -> ExecComplete (Left e) 0 -- Initialize evaluation context.- results <- forM initStmts goStmt+ forM_ initStmts goStmt -#if __GLASGOW_HASKELL__ >= 800 -- This works fine on GHC 8.0 and newer dyn <- dynCompileExpr readVariable pipe <- case fromDynamic dyn of Nothing -> fail "Evaluate: Bad pipe" Just fd -> liftIO $ do- handle <- fdToHandle fd- hSetEncoding handle utf8- return handle-#else- -- Get the pipe to read printed output from. This is effectively the source code of dynCompileExpr- -- from GHC API's InteractiveEval. However, instead of using a `Dynamic` as an intermediary, it just- -- directly reads the value. This is incredibly unsafe! However, for some reason the `getContext`- -- and `setContext` required by dynCompileExpr (to import and clear Data.Dynamic) cause issues with- -- data declarations being updated (e.g. it drops newer versions of data declarations for older ones- -- for unknown reasons). First, compile down to an HValue.- let pipeExpr = printf "let %s = %s" (var "pipe_var_") readVariable- Just (_, hValues, _) <- withSession $ liftIO . flip hscStmt pipeExpr- -- Then convert the HValue into an executable bit, and read the value.- pipe <- liftIO $ do- fds <- unsafeCoerce hValues- fd <- case fds of- fd : _ -> return fd- [] -> fail "Failed to evaluate pipes"- _ -> fail $ "Expected one fd, saw "++show (length fds)- handle <- fdToHandle fd- hSetEncoding handle utf8- return handle-#endif+ hdl <- fdToHandle fd+ hSetEncoding hdl utf8+ return hdl+ -- Keep track of whether execution has completed. completed <- liftIO $ newMVar False finishedReading <- liftIO newEmptyMVar@@ -1258,9 +1162,6 @@ ms = 1000 delay = 100 * ms - -- How much to read each time.- chunkSize = 100- -- Maximum size of the output (after which we truncate). maxSize = 100 * 1000 @@ -1286,7 +1187,7 @@ -- We're done reading. putMVar finishedReading True - liftIO $ forkIO loop+ _ <- liftIO $ forkIO loop result <- gfinally (runWithResult stmt) $ do -- Execution is done.@@ -1320,10 +1221,10 @@ case cmd of CapturedStmt stmt -> write state $ "Statement:\n" ++ stmt- CapturedIO io ->+ CapturedIO _ -> write state "Evaluating Action" - (printed, result) <- capturedEval output cmd+ (printed, result) <- capturedEval (flip output Success) cmd case result of ExecComplete (Right names) _ -> do dflags <- getSessionDynFlags@@ -1333,14 +1234,14 @@ name == "it" || name == "it" ++ show (getExecutionCounter state) nonItNames = filter (not . isItName) allNames- output = [ plain printed+ oput = [ plain printed | not . null $ strip printed ] write state $ "Names: " ++ show allNames -- Display the types of all bound names if the option is on. This is similar to GHCi :set +t. if not $ useShowTypes state- then return $ Display output+ then return $ Display oput else do -- Get all the type strings. types <- forM nonItNames $ \name -> do@@ -1355,11 +1256,11 @@ htmled = unlines $ map formatGetType types return $- case extractPlain output of+ case extractPlain oput of "" -> Display [html htmled] -- Return plain and html versions. Previously there was only a plain version.- text -> Display [plain $ joined ++ "\n" ++ text, html $ htmled ++ mono text]+ txt -> Display [plain $ joined ++ "\n" ++ txt, html $ htmled ++ mono txt] ExecComplete (Left exception) _ -> throw exception ExecBreak{} -> error "Should not break."@@ -1367,20 +1268,20 @@ -- Read from a file handle until we hit a delimiter or until we've read as many characters as -- requested readChars :: Handle -> String -> Int -> IO String-readChars handle delims 0 =+readChars _handle _delims 0 = -- If we're done reading, return nothing. return []-readChars handle delims nchars = do+readChars hdl delims nchars = do -- Try reading a single character. It will throw an exception if the handle is already closed.- tryRead <- gtry $ hGetChar handle :: IO (Either SomeException Char)+ tryRead <- gtry $ hGetChar hdl :: IO (Either SomeException Char) case tryRead of- Right char ->+ Right ch -> -- If this is a delimiter, stop reading.- if char `elem` delims- then return [char]+ if ch `elem` delims+ then return [ch] else do- next <- readChars handle delims (nchars - 1)- return $ char : next+ next <- readChars hdl delims (nchars - 1)+ return $ ch : next -- An error occurs at the end of the stream, so just stop reading. Left _ -> return [] @@ -1403,13 +1304,10 @@ where fixDollarSigns = replace "$" "<span>$</span>" useDashV = "\n Use -v to see a list of the files searched for."- isShowError err =- "No instance for (Show" `isPrefixOf` err &&- isInfixOf " arising from a use of `print'" err formatParseError :: StringLoc -> String -> ErrMsg-formatParseError (Loc line col) =- printf "Parse error (line %d, column %d): %s" line col+formatParseError (Loc ln col) =+ printf "Parse error (line %d, column %d): %s" ln col formatGetType :: String -> String formatGetType = printf "<span class='get-type'>%s</span>"
src/IHaskell/Eval/Hoogle.hs view
@@ -1,29 +1,31 @@-{-# LANGUAGE NoImplicitPrelude, FlexibleInstances, OverloadedStrings #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE OverloadedStrings #-} module IHaskell.Eval.Hoogle ( search, document, render, OutputFormat(..),- HoogleResult,+ HoogleResult(..),+ HoogleResponse(..),+ parseResponse, ) where +import qualified Data.ByteString.Char8 as CBS+import qualified Data.ByteString.Lazy as LBS+import Data.Either (either) import IHaskellPrelude-import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS +import Data.Aeson+import Data.Char (isAlphaNum, isAscii)+import qualified Data.List as List+import qualified Data.Text as T+import Data.Vector (toList) import Network.HTTP.Client import Network.HTTP.Client.TLS-import Data.Aeson-import qualified Data.List as List-import Data.Char (isAscii, isAlphaNum) --import IHaskell.IPython-import StringUtils (split, strip, replace)+import StringUtils (replace, split, strip) -- | Types of formats to render output to. data OutputFormat = Plain -- ^ Render to plain text.@@ -40,17 +42,19 @@ data HoogleResponseList = HoogleResponseList [HoogleResponse] instance FromJSON HoogleResponseList where- parseJSON (Object obj) = do- results <- obj .: "results"- HoogleResponseList <$> mapM parseJSON results+ parseJSON (Array arr) =+ HoogleResponseList <$> mapM parseJSON (toList arr) - parseJSON _ = fail "Expected object with 'results' field."+ parseJSON _ = fail "Expected array." instance FromJSON HoogleResponse where parseJSON (Object obj) =- HoogleResponse <$> obj .: "location" <*> obj .: "self" <*> obj .: "docs"+ HoogleResponse+ <$> obj .: "url"+ <*> (removeMarkup <$> obj .: "item")+ <*> obj .: "docs" - parseJSON _ = fail "Expected object with fields: location, self, docs"+ parseJSON _ = fail "Expected object with fields: url, item, docs" -- | Query Hoogle for the given string. This searches Hoogle using the internet. It returns either -- an error message or the successful JSON result.@@ -64,7 +68,7 @@ where queryUrl :: String -> String- queryUrl = printf "https://www.haskell.org/hoogle/?hoogle=%s&mode=json"+ queryUrl = printf "http://hoogle.haskell.org/?hoogle=%s&mode=json" -- | Copied from the HTTP package. urlEncode :: String -> String@@ -92,27 +96,24 @@ -- | Search for a query on Hoogle. Return all search results. search :: String -> IO [HoogleResult]-search string = do- response <- query string- return $- case response of- Left err -> [NoResult err]- Right json ->- case eitherDecode $ LBS.fromStrict $ CBS.pack json of- Left err -> [NoResult err]- Right results ->- case map SearchResult $ (\(HoogleResponseList l) -> l) results of- [] -> [NoResult "no matching identifiers found."]- res -> res+search string = either ((:[]) . NoResult) parseResponse <$> query string +parseResponse :: String -> [HoogleResult]+parseResponse jsn =+ case eitherDecode $ LBS.fromStrict $ CBS.pack jsn of+ Left err -> [NoResult err]+ Right results ->+ case map SearchResult $ (\(HoogleResponseList l) -> l) results of+ [] -> [NoResult "no matching identifiers found."]+ res -> res+ -- | Look up an identifier on Hoogle. Return documentation for that identifier. If there are many -- identifiers, include documentation for all of them. document :: String -> IO [HoogleResult] document string = do matchingResults <- filter matches <$> search string- let results = map toDocResult matchingResults return $- case results of+ case mapMaybe toDocResult matchingResults of [] -> [NoResult "no matching identifiers found."] res -> res @@ -123,12 +124,14 @@ _ -> False matches _ = False - toDocResult (SearchResult resp) = DocResult resp+ toDocResult (SearchResult resp) = Just $ DocResult resp+ toDocResult (DocResult _) = Nothing+ toDocResult (NoResult _) = Nothing -- | Render a Hoogle search result into an output format. render :: OutputFormat -> HoogleResult -> String render Plain = renderPlain-render HTML = renderHtml+render HTML = renderHtml -- | Render a Hoogle result to plain text. renderPlain :: HoogleResult -> String@@ -163,7 +166,7 @@ | "module" `isPrefixOf` string = let package = extractPackageName loc- in mod ++ " " +++ in mdl ++ " " ++ span "hoogle-module" (link loc $ extractModule string) ++ packageSub package @@ -185,6 +188,12 @@ span "hoogle-class" (link loc $ extractNewtype string) ++ packageSub package + | "type" `isPrefixOf` string =+ let package = extractPackageName loc+ in nwt ++ " " +++ span "hoogle-class" (link loc $ extractType string) +++ packageSub package+ | otherwise = let [name, args] = split "::" string package = extractPackageName loc@@ -201,8 +210,9 @@ extractClass = strip . replace "class" "" extractData = strip . replace "data" "" extractNewtype = strip . replace "newtype" ""+ extractType = strip . replace "newtype" "" pkg = span "hoogle-head" "package"- mod = span "hoogle-head" "module"+ mdl = span "hoogle-head" "module" cls = span "hoogle-head" "class" dat = span "hoogle-head" "data" nwt = span "hoogle-head" "newtype"@@ -224,37 +234,22 @@ packageAndModuleSub (Just package) (Just modname) = span "hoogle-sub" $ "(" ++ pkg ++ " " ++ span "hoogle-package" package ++- ", " ++ mod ++ " " ++ span "hoogle-module" modname ++ ")"+ ", " ++ mdl ++ " " ++ span "hoogle-module" modname ++ ")" renderDocs :: String -> String-renderDocs doc =- let groups = List.groupBy bothAreCode $ lines doc- nonull = filter (not . null . strip)- bothAreCode s1 s2 =- isPrefixOf ">" (strip s1) &&- isPrefixOf ">" (strip s2)- isCode (s:_) = isPrefixOf ">" $ strip s- makeBlock lines =- if isCode lines- then div' "hoogle-code" $ unlines $ nonull lines- else div' "hoogle-text" $ unlines $ nonull lines- in div' "hoogle-doc" $ unlines $ map makeBlock groups+renderDocs doc = div' "hoogle-doc" doc extractPackageName :: String -> Maybe String-extractPackageName link = do- let pieces = split "/" link+extractPackageName lnk = do+ let pieces = split "/" lnk archiveLoc <- List.elemIndex "archive" pieces latestLoc <- List.elemIndex "latest" pieces guard $ latestLoc - archiveLoc == 2 return $ pieces List.!! (latestLoc - 1) extractModuleName :: String -> Maybe String-extractModuleName link = do- let pieces = split "/" link- guard $ not $ null pieces- let html = fromJust $ lastMay pieces- mod = replace "-" "." $ takeWhile (/= '.') html- return mod+extractModuleName lnk =+ replace "-" "." . takeWhile (/= '.') <$> lastMay (split "/" lnk) div' :: String -> String -> String div' = printf "<div class='%s'>%s</div>"@@ -264,3 +259,19 @@ link :: String -> String -> String link = printf "<a target='_blank' href='%s'>%s</a>"++-- | very explicit cleaning of the type signature in the hoogle 5 response,+-- to remove html markup and escaped characters.+removeMarkup :: String -> String+removeMarkup s = T.unpack $ List.foldl (flip ($)) (T.pack s) replaceAll+ where replacements :: [ (T.Text, T.Text) ]+ replacements = [ ( "<span class=name>", "" )+ , ( "</span>", "" )+ , ( "<0>", "" )+ , ( "</0>", "" )+ , ( ">", ">" )+ , ( "<", "<" )+ , ( "<b>", "")+ , ( "</b>", "")+ ]+ replaceAll = uncurry T.replace <$> replacements
src/IHaskell/Eval/Info.hs view
@@ -4,11 +4,6 @@ module IHaskell.Eval.Info (info) where import IHaskellPrelude-import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS import IHaskell.Eval.Evaluate (typeCleaner, Interpreter)
src/IHaskell/Eval/Inspect.hs view
@@ -7,11 +7,6 @@ module IHaskell.Eval.Inspect (inspect) where import IHaskellPrelude-import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS import qualified Prelude as P @@ -22,7 +17,6 @@ import IHaskell.Eval.Evaluate (Interpreter) import IHaskell.Display import IHaskell.Eval.Util (getType)-import IHaskell.Types -- | Characters used in Haskell operators. operatorChars :: String@@ -34,7 +28,7 @@ -- | Compute the identifier that is being queried. getIdentifier :: String -> Int -> String-getIdentifier code pos = identifier+getIdentifier code _pos = identifier where chunks = splitOn whitespace code lastChunk = P.last chunks :: String
src/IHaskell/Eval/Lint.hs view
@@ -3,33 +3,21 @@ module IHaskell.Eval.Lint (lint) where import IHaskellPrelude-import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS -import Prelude (head, tail, last)-import Control.Monad-import Data.List (findIndex)-import Data.Char-import Data.Monoid+import Prelude (last) import Data.Maybe (mapMaybe) import System.IO.Unsafe (unsafePerformIO) import Language.Haskell.Exts.Syntax hiding (Module) import qualified Language.Haskell.Exts.Syntax as SrcExts import Language.Haskell.Exts (parseFileContentsWithMode)-import Language.Haskell.Exts.Build (doE) import Language.Haskell.Exts hiding (Module)-import Language.Haskell.Exts.SrcLoc import Language.Haskell.HLint as HLint import Language.Haskell.HLint3 import IHaskell.Types import IHaskell.Display-import IHaskell.IPython import IHaskell.Eval.Parser hiding (line) import StringUtils (replace) @@ -65,10 +53,9 @@ -- Get hlint settings (flags, classify, hint) <- readMVar hlintSettings- let mode = hseFlags flags -- create 'suggestions'- let modules = mapMaybe (createModule mode) blocks+ let modules = mapMaybe (createModule (hseFlags flags)) blocks ideas = applyHints classify hint (map (\m -> (m, [])) modules) suggestions = mapMaybe showIdea $ filter (not . ignoredIdea) ideas @@ -78,33 +65,33 @@ else [plain $ concatMap plainSuggestion suggestions, html $ htmlSuggestions suggestions] where autoSettings' = do- (fixities, classify, hints) <- autoSettings+ (fixts, classify, hints) <- autoSettings let hidingIgnore = Classify Ignore "Unnecessary hiding" "" ""- return (fixities, hidingIgnore:classify, hints)+ return (fixts, hidingIgnore:classify, hints) ignoredIdea idea = ideaSeverity idea == Ignore showIdea :: Idea -> Maybe LintSuggestion showIdea idea = case ideaTo idea of Nothing -> Nothing- Just whyNot ->+ Just wn -> Just Suggest { line = srcSpanStartLine $ ideaSpan idea , found = showSuggestion $ ideaFrom idea- , whyNot = showSuggestion whyNot+ , whyNot = showSuggestion wn , severity = ideaSeverity idea , suggestion = ideaHint idea } createModule :: ParseMode -> Located CodeBlock -> Maybe ExtsModule-createModule mode (Located line block) =+createModule md (Located ln block) = case block of Expression expr -> unparse $ exprToModule expr Declaration decl -> unparse $ declToModule decl Statement stmt -> unparse $ stmtToModule stmt Import impt -> unparse $ imptToModule impt- Module mod -> unparse $ parseModule mod+ Module mdl -> unparse $ pModule mdl _ -> Nothing where blockStr =@@ -113,8 +100,11 @@ Declaration decl -> decl Statement stmt -> stmt Import impt -> impt- Module mod -> mod+ Module mdl -> mdl + -- TODO: Properly handle the other constructors+ _ -> []+ unparse :: ParseResult a -> Maybe a unparse (ParseOk a) = Just a unparse _ = Nothing@@ -122,49 +112,47 @@ srcSpan :: SrcSpan srcSpan = SrcSpan { srcSpanFilename = "<interactive>"- , srcSpanStartLine = line+ , srcSpanStartLine = ln , srcSpanStartColumn = 0- , srcSpanEndLine = line + length (lines blockStr)+ , srcSpanEndLine = ln + length (lines blockStr) , srcSpanEndColumn = length $ last $ lines blockStr } - loc :: SrcSpanInfo- loc = SrcSpanInfo srcSpan []+ lcn :: SrcSpanInfo+ lcn = SrcSpanInfo srcSpan [] moduleWithDecls :: Decl SrcSpanInfo -> ExtsModule- moduleWithDecls decl = SrcExts.Module loc Nothing [] [] [decl]+ moduleWithDecls decl = SrcExts.Module lcn Nothing [] [] [decl] - parseModule :: String -> ParseResult ExtsModule- parseModule = parseFileContentsWithMode mode+ pModule :: String -> ParseResult ExtsModule+ pModule = parseFileContentsWithMode md declToModule :: String -> ParseResult ExtsModule- declToModule decl = moduleWithDecls <$> parseDeclWithMode mode decl+ declToModule decl = moduleWithDecls <$> parseDeclWithMode md decl exprToModule :: String -> ParseResult ExtsModule- exprToModule exp = moduleWithDecls <$> SpliceDecl loc <$> parseExpWithMode mode exp+ exprToModule exp = moduleWithDecls <$> SpliceDecl lcn <$> parseExpWithMode md exp stmtToModule :: String -> ParseResult ExtsModule stmtToModule stmtStr =- case parseStmtWithMode mode stmtStr of- ParseOk stmt -> ParseOk mod+ case parseStmtWithMode md stmtStr of+ ParseOk _ -> ParseOk $ moduleWithDecls decl ParseFailed a b -> ParseFailed a b where- mod = moduleWithDecls decl- decl :: Decl SrcSpanInfo- decl = SpliceDecl loc expr+ decl = SpliceDecl lcn expr expr :: Exp SrcSpanInfo- expr = Do loc [stmt, ret]+ expr = Do lcn [stmt, ret] stmt :: Stmt SrcSpanInfo- ParseOk stmt = parseStmtWithMode mode stmtStr+ ParseOk stmt = parseStmtWithMode md stmtStr ret :: Stmt SrcSpanInfo- ParseOk ret = Qualifier loc <$> parseExp lintIdent+ ParseOk ret = Qualifier lcn <$> parseExp lintIdent imptToModule :: String -> ParseResult ExtsModule- imptToModule = parseFileContentsWithMode mode+ imptToModule = parseFileContentsWithMode md plainSuggestion :: LintSuggestion -> String plainSuggestion suggest =@@ -177,10 +165,10 @@ toHtml :: LintSuggestion -> String toHtml suggest = concat [ named $ suggestion suggest- , floating "left" $ style severityClass "Found:" +++ , floating "left" $ styl severityClass "Found:" ++ -- Things that look like this get highlighted. styleId "highlight-code" "haskell" (found suggest)- , floating "left" $ style severityClass "Why Not:" +++ , floating "left" $ styl severityClass "Why Not:" ++ -- Things that look like this get highlighted. styleId "highlight-code" "haskell" (whyNot suggest) ]@@ -193,8 +181,8 @@ -- Should not occur _ -> "warning" - style :: String -> String -> String- style = printf "<div class=\"suggestion-%s\">%s</div>"+ styl :: String -> String -> String+ styl = printf "<div class=\"suggestion-%s\">%s</div>" named :: String -> String named = printf "<div class=\"suggestion-name\" style=\"clear:both;\">%s</div>"
src/IHaskell/Eval/ParseShell.hs view
@@ -5,11 +5,6 @@ module IHaskell.Eval.ParseShell (parseShell) where import IHaskellPrelude-import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS import Text.ParserCombinators.Parsec @@ -28,6 +23,7 @@ xs <- scan return $ x : xs +manyTillEnd1 :: Parser a -> Parser [a] -> Parser [a] manyTillEnd1 p end = do x <- p xs <- manyTillEnd p end@@ -36,18 +32,21 @@ unescapedChar :: Parser Char -> Parser String unescapedChar p = try $ do x <- noneOf "\\"- lookAhead p+ _ <- lookAhead p return [x] +quotedString :: Parser [Char] quotedString = do- quote <?> "expected starting quote"+ _ <- quote <?> "expected starting quote" (manyTillEnd anyChar (unescapedChar quote) <* quote) <?> "unexpected in quoted String " +unquotedString :: Parser [Char] unquotedString = manyTillEnd1 anyChar end where end = unescapedChar space <|> (lookAhead eol >> return []) +word :: Parser [Char] word = quotedString <|> unquotedString <?> "word" separator :: Parser String@@ -57,11 +56,11 @@ shellWords :: Parser [String] shellWords = try (eof *> return []) <|> do x <- word- rest1 <- lookAhead (many anyToken)- ss <- separator- rest2 <- lookAhead (many anyToken)+ _rest1 <- lookAhead (many anyToken)+ _ss <- separator+ _rest2 <- lookAhead (many anyToken) xs <- shellWords return $ x : xs parseShell :: String -> Either ParseError [String]-parseShell string = parse shellWords "shell" (string ++ "\n")+parseShell str = parse shellWords "shell" (str ++ "\n")
src/IHaskell/Eval/Parser.hs view
@@ -16,11 +16,6 @@ ) where import IHaskellPrelude-import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS import Data.Char (toLower) import Data.List (maximumBy, inits)@@ -83,8 +78,8 @@ flags <- getSessionDynFlags let output = runParser flags parserModule codeString case output of- Parsed mod- | Just _ <- hsmodName (unLoc mod) -> return [Located 1 $ Module codeString]+ Parsed mdl+ | Just _ <- hsmodName (unLoc mdl) -> return [Located 1 $ Module codeString] _ -> do -- Split input into chunks based on indentation. let chunks = layoutChunks $ removeComments codeString@@ -97,12 +92,12 @@ where parseChunk :: GhcMonad m => String -> LineNumber -> m (Located CodeBlock)- parseChunk chunk line = Located line <$> handleChunk chunk line+ parseChunk chunk ln = Located ln <$> handleChunk where- handleChunk chunk line- | isDirective chunk = return $ parseDirective chunk line- | isPragma chunk = return $ parsePragma chunk line- | otherwise = parseCodeChunk chunk line+ handleChunk+ | isDirective chunk = return $ parseDirective chunk ln+ | isPragma chunk = return $ parsePragma chunk ln+ | otherwise = parseCodeChunk chunk ln processChunks :: GhcMonad m => [Located CodeBlock] -> [Located String] -> m [Located CodeBlock] processChunks accum remaining =@@ -111,10 +106,10 @@ [] -> return $ reverse accum -- If we have more remaining, parse the current chunk and recurse.- Located line chunk:remaining -> do- block <- parseChunk chunk line+ Located ln chunk:remain -> do+ block <- parseChunk chunk ln activateExtensions $ unloc block- processChunks (block : accum) remaining+ processChunks (block : accum) remain -- Test whether a given chunk is a directive. isDirective :: String -> Bool@@ -130,11 +125,11 @@ case stripPrefix "-X" flags of Just ext -> void $ setExtension ext Nothing -> return ()-activateExtensions (Pragma PragmaLanguage extensions) = void $ setAll extensions+activateExtensions (Pragma PragmaLanguage exts) = void $ setAll exts where setAll :: GhcMonad m => [String] -> m (Maybe String)- setAll exts = do- errs <- mapM setExtension exts+ setAll exts' = do+ errs <- mapM setExtension exts' return $ msum errs activateExtensions _ = return () @@ -142,7 +137,7 @@ parseCodeChunk :: GhcMonad m => String -> LineNumber -> m CodeBlock parseCodeChunk code startLine = do flags <- getSessionDynFlags- let + let -- Try each parser in turn. rawResults = map (tryParser code) (parsers flags) @@ -164,13 +159,13 @@ failures :: [ParseOutput a] -> [(ErrMsg, LineNumber, ColumnNumber)] failures [] = []- failures (Failure msg (Loc line col):rest) = (msg, line, col) : failures rest+ failures (Failure msg (Loc ln col):rest) = (msg, ln, col) : failures rest failures (_:rest) = failures rest bestError :: [(ErrMsg, LineNumber, ColumnNumber)] -> CodeBlock- bestError errors = ParseError (Loc (line + startLine - 1) col) msg+ bestError errors = ParseError (Loc (ln + startLine - 1) col) msg where- (msg, line, col) = maximumBy compareLoc errors+ (msg, ln, col) = maximumBy compareLoc errors compareLoc (_, line1, col1) (_, line2, col2) = compare line1 line2 <> compare col1 col2 statementToExpression :: DynFlags -> ParseOutput CodeBlock -> ParseOutput CodeBlock@@ -189,11 +184,11 @@ _ -> False tryParser :: String -> (String -> CodeBlock, String -> ParseOutput String) -> ParseOutput CodeBlock- tryParser string (blockType, parser) =- case parser string of+ tryParser string (blockType, psr) =+ case psr string of Parsed res -> Parsed (blockType res) Failure err loc -> Failure err loc- otherwise -> error "tryParser failed, output was neither Parsed nor Failure"+ _ -> error "tryParser failed, output was neither Parsed nor Failure" parsers :: DynFlags -> [(String -> CodeBlock, String -> ParseOutput String)] parsers flags =@@ -204,10 +199,10 @@ ] where unparser :: Parser a -> String -> ParseOutput String- unparser parser code =- case runParser flags parser code of- Parsed out -> Parsed code- Partial out strs -> Partial code strs+ unparser psr cd =+ case runParser flags psr cd of+ Parsed _ -> Parsed cd+ Partial _ strs -> Partial cd strs Failure err loc -> Failure err loc -- | Find consecutive declarations of the same function and join them into a single declaration.@@ -239,11 +234,11 @@ parsePragma :: String -- ^ Pragma string. -> Int -- ^ Line number at which the directive appears. -> CodeBlock -- ^ Pragma code block or a parse error.-parsePragma ('{':'-':'#':pragma) line =+parsePragma pragma _ln = let commaToSpace :: Char -> Char commaToSpace ',' = ' ' commaToSpace x = x- pragmas = words $ takeWhile (/= '#') $ map commaToSpace pragma+ pragmas = words $ takeWhile (/= '#') $ map commaToSpace $ drop 3 pragma in case pragmas of --empty string pragmas are unsupported [] -> Pragma (PragmaUnsupported "") []@@ -256,8 +251,8 @@ parseDirective :: String -- ^ Directive string. -> Int -- ^ Line number at which the directive appears. -> CodeBlock -- ^ Directive code block or a parse error.-parseDirective (':':'!':directive) line = Directive ShellCmd $ '!' : directive-parseDirective (':':directive) line =+parseDirective (':':'!':directive) _ln = Directive ShellCmd $ '!' : directive+parseDirective (':':directive) ln = case find rightDirective directives of Just (directiveType, _) -> Directive directiveType arg where arg = unwords restLine@@ -267,7 +262,7 @@ case words directive of [] -> "" first:_ -> first- in ParseError (Loc line 1) $ "Unknown directive: '" ++ directiveStart ++ "'."+ in ParseError (Loc ln 1) $ "Unknown directive: '" ++ directiveStart ++ "'." where rightDirective (_, dirname) = case words directive of@@ -298,8 +293,8 @@ let output = runParser flags parserModule moduleSrc case output of Failure{} -> error "Module parsing failed."- Parsed mod ->- case unLoc <$> hsmodName (unLoc mod) of+ Parsed mdl ->+ case unLoc <$> hsmodName (unLoc mdl) of Nothing -> error "Module must have a name." Just name -> return $ split "." $ moduleNameString name- otherwise -> error "getModuleName failed, output was neither Parsed nor Failure"+ _ -> error "getModuleName failed, output was neither Parsed nor Failure"
src/IHaskell/Eval/Util.hs view
@@ -27,29 +27,25 @@ ) where import IHaskellPrelude-import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS+#if MIN_VERSION_ghc(8,6,0)+#else import qualified Data.ByteString.Char8 as CBS+#endif -- GHC imports. import DynFlags+#if MIN_VERSION_ghc(8,6,0)+#else import FastString+#endif import GHC import GhcMonad-import HsImpExp import HscTypes-import InteractiveEval-import Module-import Packages-import RdrName import NameSet import Name import PprTyThing import InstEnv (ClsInst(..)) import Unify (tcMatchTys)-import VarSet (mkVarSet) import qualified Pretty import qualified Outputable as O @@ -63,11 +59,9 @@ import CmdLineParser (warnMsg) #endif -#if MIN_VERSION_ghc(8,0,1) import GHC.LanguageExtensions type ExtensionFlag = Extension-#endif -- | A extension flag that can be set or unset. data ExtFlag = SetFlag ExtensionFlag@@ -87,15 +81,11 @@ Nothing -> Nothing where -- Check if a FlagSpec matches an extension name.- flagMatches ext fs = ext == flagSpecName fs+ flagMatches ex fs = ex == flagSpecName fs -- Check if a FlagSpec matches "No<ExtensionName>". In that case, we disable the extension.- flagMatchesNo ext fs = ext == "No" ++ flagSpecName fs-#if !MIN_VERSION_ghc(7,10,0)-flagSpecName (name, _, _) = name+ flagMatchesNo ex fs = ex == "No" ++ flagSpecName fs -flagSpecFlag (_, flag, _) = flag-#endif -- | Pretty-print dynamic flags (taken from 'InteractiveUI' module of `ghc-bin`) pprDynFlags :: Bool -- ^ Whether to include flags which are on by default -> DynFlags@@ -107,21 +97,14 @@ , O.text "other dynamic, non-language, flag settings:" O.$$ O.nest 2 (O.vcat (map (setting opt) others)) , O.text "warning settings:" O.$$- O.nest 2 (O.vcat (map (setting wopt) warningFlags))+ O.nest 2 (O.vcat (map (setting wopt) wFlags)) ] where -#if MIN_VERSION_ghc(8,0,0)- warningFlags = DynFlags.wWarningFlags-#else- warningFlags = DynFlags.fWarningFlags-#endif+ wFlags = DynFlags.wWarningFlags -#if MIN_VERSION_ghc(7,8,0) opt = gopt-#else- opt = dopt-#endif+ setting test flag | quiet = O.empty :: O.SDoc | is_on = fstr name :: O.SDoc@@ -131,28 +114,28 @@ f = flagSpecFlag flag is_on = test f dflags quiet = not show_all && test f default_dflags == is_on- -#if MIN_VERSION_ghc(8,4,0)++#if MIN_VERSION_ghc(8,6,0)+ default_dflags = defaultDynFlags (settings dflags) (llvmTargets dflags, llvmPasses dflags)+#elif MIN_VERSION_ghc(8,4,0) default_dflags = defaultDynFlags (settings dflags) (llvmTargets dflags) #else default_dflags = defaultDynFlags (settings dflags) #endif- + fstr, fnostr :: String -> O.SDoc fstr str = O.text "-f" O.<> O.text str- + fnostr str = O.text "-fno-" O.<> O.text str- + (ghciFlags, others) = partition (\f -> flagSpecFlag f `elem` flgs) DynFlags.fFlags- + flgs = concat [flgs1, flgs2, flgs3]- + flgs1 = [Opt_PrintExplicitForalls]-#if MIN_VERSION_ghc(7,8,0) flgs2 = [Opt_PrintExplicitKinds]-#else- flgs2 = []-#endif++flgs3 :: [GeneralFlag] flgs3 = [Opt_PrintBindResult, Opt_BreakOnException, Opt_BreakOnError, Opt_PrintEvldWithShow] -- | Pretty-print the base language and active options (taken from `InteractiveUI` module of@@ -184,7 +167,9 @@ quiet = not show_all && test f default_dflags == is_on default_dflags =-#if MIN_VERSION_ghc(8,4,0)+#if MIN_VERSION_ghc(8,6,0)+ defaultDynFlags (settings dflags) (llvmTargets dflags, llvmPasses dflags) `lang_set`+#elif MIN_VERSION_ghc(8,4,0) defaultDynFlags (settings dflags) (llvmTargets dflags) `lang_set` #else defaultDynFlags (settings dflags) `lang_set`@@ -201,7 +186,7 @@ case extensionFlag ext of Nothing -> return $ Just $ "Could not parse extension name: " ++ ext Just flag -> do- setSessionDynFlags $+ _ <- setSessionDynFlags $ case flag of SetFlag ghcFlag -> xopt_set flags ghcFlag UnsetFlag ghcFlag -> xopt_unset flags ghcFlag@@ -216,9 +201,8 @@ (flags', unrecognized, warnings) <- parseDynamicFlags flags (map noLoc ext) -- First, try to check if this flag matches any extension name.- let restorePkg x = x { packageFlags = packageFlags flags } let restoredPkgs = flags' { packageFlags = packageFlags flags }- GHC.setProgramDynFlags restoredPkgs+ _ <- GHC.setProgramDynFlags restoredPkgs GHC.setInteractiveDynFlags restoredPkgs -- Create the parse errors.@@ -251,10 +235,15 @@ where string_txt :: Pretty.TextDetails -> String -> String+#if MIN_VERSION_ghc(8,6,0)+ string_txt = Pretty.txtPrinter+#else string_txt (Pretty.Chr c) s = c : s string_txt (Pretty.Str s1) s2 = s1 ++ s2 string_txt (Pretty.PStr s1) s2 = unpackFS s1 ++ s2 string_txt (Pretty.LStr s1 _) s2 = unpackLitString s1 ++ s2+ string_txt (Pretty.ZStr s1) s2 = CBS.unpack (fastZStringToByteString s1) ++ s2+#endif -- | Initialize the GHC API. Run this as the first thing in the `runGhc`. This initializes some dyn -- flags (@ExtendedDefaultRules@,@@ -271,11 +260,7 @@ originalFlags <- getSessionDynFlags let flag = flip xopt_set unflag = flip xopt_unset-#if MIN_VERSION_ghc(8,0,0) dflags = flag ExtendedDefaultRules . unflag MonomorphismRestriction $ originalFlags-#else- dflags = flag Opt_ExtendedDefaultRules . unflag Opt_MonomorphismRestriction $ originalFlags-#endif #if MIN_VERSION_ghc(8,2,0) pkgFlags = case sandboxPackages of@@ -357,15 +342,14 @@ _ -> False removeImport :: GhcMonad m => String -> m ()-removeImport moduleName = do- flags <- getSessionDynFlags+removeImport modName = do ctx <- getContext- let ctx' = filter (not . (isImportOf $ mkModuleName moduleName)) ctx+ let ctx' = filter (not . (isImportOf $ mkModuleName modName)) ctx setContext ctx' where isImportOf :: ModuleName -> InteractiveImport -> Bool- isImportOf name (IIModule modName) = name == modName+ isImportOf name (IIModule mName) = name == mName isImportOf name (IIDecl impDecl) = name == unLoc (ideclName impDecl) -- | Evaluate a series of declarations. Return all names which were bound by these declarations.@@ -378,7 +362,7 @@ cleanUpDuplicateInstances :: GhcMonad m => m () cleanUpDuplicateInstances = modifySession $ \hscEnv ->- let + let -- Get all class instances ic = hsc_IC hscEnv (clsInsts, famInsts) = ic_instances ic@@ -387,21 +371,9 @@ in hscEnv { hsc_IC = ic { ic_instances = (clsInsts', famInsts) } } where instEq :: ClsInst -> ClsInst -> Bool-#if MIN_VERSION_ghc(8,0,0) -- Only support replacing instances on GHC 7.8 and up- instEq c1 c2- | ClsInst { is_tvs = tpl_tvs, is_tys = tpl_tys, is_cls = cls } <- c1,- ClsInst { is_tys = tpl_tys', is_cls = cls' } <- c2- = cls == cls' && isJust (tcMatchTys tpl_tys tpl_tys')-#elif MIN_VERSION_ghc(7,8,0)- instEq c1 c2- | ClsInst { is_tvs = tpl_tvs, is_tys = tpl_tys, is_cls = cls } <- c1,- ClsInst { is_tys = tpl_tys', is_cls = cls' } <- c2- = let tpl_tv_set = mkVarSet tpl_tvs- in cls == cls' && isJust (tcMatchTys tpl_tv_set tpl_tys tpl_tys')-#else- instEq _ _ = False-#endif+ instEq c1 c2 =+ is_cls c1 == is_cls c2 && isJust (tcMatchTys (is_tys c1) (is_tys c2)) -- | Get the type of an expression and convert it to a string.@@ -430,9 +402,9 @@ -- Filter out types that have parents in the same set. GHCi also does this. let infos = catMaybes maybeInfos- allNames = mkNameSet $ map (getName . getType) infos+ allNames = mkNameSet $ map (getName . getInfoType) infos hasParent info =- case tyThingParent_maybe (getType info) of+ case tyThingParent_maybe (getInfoType info) of Just parent -> getName parent `elemNameSet` allNames Nothing -> False filteredOutput = filter (not . hasParent) infos@@ -442,18 +414,12 @@ where -#if MIN_VERSION_ghc(7,8,0) getInfo' = getInfo False-#else- getInfo' = getInfo-#endif #if MIN_VERSION_ghc(8,4,0)- getType (theType, _, _, _, _) = theType-#elif MIN_VERSION_ghc(7,8,0)- getType (theType, _, _, _) = theType+ getInfoType (theType, _, _, _, _) = theType #else- getType (theType, _, _) = theType+ getInfoType (theType, _, _, _) = theType #endif #if MIN_VERSION_ghc(8,4,0)@@ -462,16 +428,12 @@ showFixity thing fixity O.$$ O.vcat (map GHC.pprInstance classInstances) O.$$ O.vcat (map GHC.pprFamInst famInstances)-#elif MIN_VERSION_ghc(7,8,0)+#else printInfo (thing, fixity, classInstances, famInstances) = pprTyThingInContextLoc thing O.$$ showFixity thing fixity O.$$ O.vcat (map GHC.pprInstance classInstances) O.$$ O.vcat (map GHC.pprFamInst famInstances)-#else- printInfo (thing, fixity, classInstances) =- pprTyThingInContextLoc False thing O.$$ showFixity thing fixity O.$$- O.vcat (map GHC.pprInstance classInstances) #endif showFixity thing fixity = if fixity == GHC.defaultFixity
src/IHaskell/Eval/Widgets.hs view
@@ -14,7 +14,6 @@ import IHaskellPrelude -import Control.Concurrent.Chan (writeChan) import Control.Concurrent.STM (atomically) import Control.Concurrent.STM.TChan import Control.Monad (foldM)@@ -25,8 +24,6 @@ import IHaskell.Display import IHaskell.Eval.Util (unfoldM) import IHaskell.IPython.Types (showMessageType)-import IHaskell.IPython.Message.UUID-import IHaskell.IPython.Message.Writer import IHaskell.Types -- All comm_open messages go here@@ -50,7 +47,7 @@ widgetSend :: IHaskellWidget a => (Widget -> Value -> WidgetMsg) -> a -> Value -> IO ()-widgetSend msgType widget value = queue $ msgType (Widget widget) value+widgetSend mtype widget value = queue $ mtype (Widget widget) value -- | Send a message to open a comm widgetSendOpen :: IHaskellWidget a => a -> Value -> IO ()@@ -82,7 +79,7 @@ -- | Send a `clear_output` message as a [method .= custom] message widgetClearOutput :: IHaskellWidget a => a -> Bool -> IO ()-widgetClearOutput widget wait = queue $ ClrOutput (Widget widget) wait+widgetClearOutput widget w = queue $ ClrOutput (Widget widget) w -- | Handle a single widget message. Takes necessary actions according to the message type, such as -- opening comms, storing and updating widget representation in the kernel state etc.@@ -111,8 +108,8 @@ then return state else do -- Send the comm open, with the initial state- header <- dupHeader replyHeader CommOpenMessage- send $ CommOpen header target_name target_module uuid value+ hdr <- dupHeader replyHeader CommOpenMessage+ send $ CommOpen hdr target_name target_module uuid value -- Send anything else the widget requires. open widget communicate@@ -130,8 +127,8 @@ -- If the widget is not present in the state, we don't close it. if present then do- header <- dupHeader replyHeader CommCloseMessage- send $ CommClose header uuid value+ hdr <- dupHeader replyHeader CommCloseMessage+ send $ CommClose hdr uuid value return newState else return state @@ -148,9 +145,9 @@ let dmsg = WidgetDisplay dispHeader $ unwrap disp sendMessage widget (toJSON $ CustomContent $ toJSON dmsg) - ClrOutput widget wait -> do- header <- dupHeader replyHeader ClearOutputMessage- let cmsg = WidgetClear header wait+ ClrOutput widget w -> do+ hdr <- dupHeader replyHeader ClearOutputMessage+ let cmsg = WidgetClear hdr w sendMessage widget (toJSON $ CustomContent $ toJSON cmsg) where@@ -161,8 +158,8 @@ -- If the widget is present, we send an update message on its comm. when present $ do- header <- dupHeader replyHeader CommDataMessage- send $ CommData header uuid value+ hdr <- dupHeader replyHeader CommDataMessage+ send $ CommData hdr uuid value return state unwrap :: Display -> [DisplayData]@@ -174,27 +171,27 @@ instance ToJSON WidgetDisplay where toJSON (WidgetDisplay replyHeader ddata) =- let pbval = toJSON $ PublishDisplayData replyHeader ddata+ let pbval = toJSON $ PublishDisplayData replyHeader ddata Nothing in toJSON $ IPythonMessage replyHeader pbval DisplayDataMessage -- Override toJSON for ClearOutput data WidgetClear = WidgetClear MessageHeader Bool instance ToJSON WidgetClear where- toJSON (WidgetClear replyHeader wait) =- let clrVal = toJSON $ ClearOutput replyHeader wait+ toJSON (WidgetClear replyHeader w) =+ let clrVal = toJSON $ ClearOutput replyHeader w in toJSON $ IPythonMessage replyHeader clrVal ClearOutputMessage data IPythonMessage = IPythonMessage MessageHeader Value MessageType instance ToJSON IPythonMessage where- toJSON (IPythonMessage replyHeader val msgType) =+ toJSON (IPythonMessage replyHeader val mtype) = object [ "header" .= replyHeader , "parent_header" .= str "" , "metadata" .= str "{}" , "content" .= val- , "msg_type" .= (toJSON . showMessageType $ msgType)+ , "msg_type" .= (toJSON . showMessageType $ mtype) ] str :: String -> String@@ -206,4 +203,4 @@ -> KernelState -> [WidgetMsg] -> IO KernelState-widgetHandler sender header = foldM (handleMessage sender header)+widgetHandler sender hdr = foldM (handleMessage sender hdr)
src/IHaskell/Flags.hs view
@@ -14,22 +14,17 @@ import IHaskellPrelude hiding (Arg(..)) import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS import System.Console.CmdArgs.Explicit import System.Console.CmdArgs.Text import Data.List (findIndex)-import IHaskell.Types -- Command line arguments to IHaskell. A set of arguments is annotated with the mode being invoked. data Args = Args IHaskellMode [Argument] deriving Show data Argument = ConfFile String -- ^ A file with commands to load at startup.- | OverwriteFiles -- ^ Present when output should overwrite existing files. + | OverwriteFiles -- ^ Present when output should overwrite existing files. | GhcLibDir String -- ^ Where to find the GHC libraries. | RTSFlags [String] -- ^ Options for the GHC runtime (e.g. heap-size limit -- or number of threads).@@ -70,7 +65,7 @@ -- | Given a list of command-line arguments, return the IHaskell mode and arguments to process. parseFlags :: [String] -> Either String Args parseFlags flags =- let modeIndex = findIndex (`elem` modeFlags) flags+ let modeIndex = findIndex (`elem` modeFlgs) flags in case modeIndex of Nothing -> -- Treat no mode as 'console'.@@ -82,18 +77,19 @@ let (start, first:end) = splitAt idx flags in process ihaskellArgs $ first : start ++ end where- modeFlags = concatMap modeNames allModes+ modeFlgs = concatMap modeNames allModes allModes :: [Mode Args] allModes = [installKernelSpec, kernel, convert] -- | Get help text for a given IHaskell ode. help :: IHaskellMode -> String-help mode = showText (Wrap 100) $ helpText [] HelpFormatAll $ chooseMode mode+help md = showText (Wrap 100) $ helpText [] HelpFormatAll $ chooseMode md where chooseMode InstallKernelSpec = installKernelSpec chooseMode (Kernel _) = kernel chooseMode ConvertLhs = convert+ chooseMode (ShowDefault _) = error "IHaskell.Flags.help: Should never happen." ghcLibFlag :: Flag Args ghcLibFlag = flagReq ["ghclib", "l"] (store GhcLibDir) "<path>" "Library directory for GHC."@@ -101,8 +97,8 @@ ghcRTSFlag :: Flag Args ghcRTSFlag = flagReq ["use-rtsopts"] storeRTS "\"<flags>\"" "Runtime options (multithreading etc.). See `ghc +RTS -?`."- where storeRTS allRTSFlags (Args mode prev)- = fmap (Args mode . (:prev) . RTSFlags)+ where storeRTS allRTSFlags (Args md prev)+ = fmap (Args md . (:prev) . RTSFlags) . parseRTS . words $ filter (/='"') allRTSFlags parseRTS ("+RTS":fs) -- Ignore if this is included (we already wrap = parseRTS fs -- the ihaskell-kernel call in +RTS <flags> -RTS anyway)@@ -115,13 +111,13 @@ kernelDebugFlag :: Flag Args kernelDebugFlag = flagNone ["debug"] addDebug "Print debugging output from the kernel." where- addDebug (Args mode prev) = Args mode (KernelDebug : prev)+ addDebug (Args md prev) = Args md (KernelDebug : prev) kernelStackFlag :: Flag Args kernelStackFlag = flagNone ["stack"] addStack "Inherit environment from `stack` when it is installed" where- addStack (Args mode prev) = Args mode (KernelspecUseStack : prev)+ addStack (Args md prev) = Args md (KernelspecUseStack : prev) confFlag :: Flag Args confFlag = flagReq ["conf", "c"] (store ConfFile) "<rc.hs>"@@ -131,12 +127,14 @@ installPrefixFlag = flagReq ["prefix"] (store KernelspecInstallPrefix) "<install-dir>" "Installation prefix for kernelspec (see Jupyter's --prefix option)" +helpFlag :: Flag Args helpFlag = flagHelpSimple (add Help) -add flag (Args mode flags) = Args mode $ flag : flags+add :: Argument -> Args -> Args+add flag (Args md flags) = Args md $ flag : flags store :: (String -> Argument) -> String -> Args -> Either String Args-store constructor str (Args mode prev) = Right $ Args mode $ constructor str : prev+store constructor str (Args md prev) = Right $ Args md $ constructor str : prev installKernelSpec :: Mode Args installKernelSpec =@@ -168,14 +166,14 @@ , helpFlag ] - consForce (Args mode prev) = Args mode (OverwriteFiles : prev)+ consForce (Args md prev) = Args md (OverwriteFiles : prev) unnamedArg = Arg (store ConvertFrom) "<file>" False- consStyle style (Args mode prev) = Args mode (ConvertLhsStyle style : prev)+ consStyle style (Args md prev) = Args md (ConvertLhsStyle style : prev) - storeFormat constructor str (Args mode prev) =+ storeFormat constructor str (Args md prev) = case T.toLower (T.pack str) of- "lhs" -> Right $ Args mode $ constructor LhsMarkdown : prev- "ipynb" -> Right $ Args mode $ constructor IpynbFile : prev+ "lhs" -> Right $ Args md $ constructor LhsMarkdown : prev+ "ipynb" -> Right $ Args md $ constructor IpynbFile : prev _ -> Left $ "Unknown format requested: " ++ str storeLhs str previousArgs =@@ -196,13 +194,11 @@ let noMode = mode "IHaskell" defaultReport descr noArgs [helpFlag, versionFlag] defaultReport = Args (ShowDefault helpStr) [] descr = "Haskell for Interactive Computing."- helpFlag = flagHelpSimple (add Help) versionFlag = flagVersion (add Version) helpStr = showText (Wrap 100) $ helpText [] HelpFormatAll ihaskellArgs in noMode { modeGroupModes = toGroup allModes }- where- add flag (Args mode flags) = Args mode $ flag : flags +noArgs :: Arg a noArgs = flagArg unexpected "" where unexpected a = error $ "Unexpected argument: " ++ a
src/IHaskell/IPython.hs view
@@ -1,5 +1,4 @@ {-# language NoImplicitPrelude, DoAndIfThenElse, OverloadedStrings, ExtendedDefaultRules #-}-{-# LANGUAGE CPP #-} -- | Description : Shell scripting wrapper using @Shelly@ for the @notebook@, and -- @console@ commands.@@ -17,11 +16,7 @@ import IHaskellPrelude import qualified Data.Text as T import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS -import Control.Concurrent (threadDelay) import System.Argv0 import qualified Shelly as SH import qualified System.IO as IO@@ -33,12 +28,10 @@ import Data.Text.Lazy.Builder (toLazyText) import Control.Monad (mplus) -import qualified System.IO.Strict as StrictIO import qualified Paths_ihaskell as Paths import qualified GHC.Paths import IHaskell.Types-import System.Posix.Signals import StringUtils (replace, split) @@ -67,9 +60,6 @@ kernelName :: String kernelName = "haskell" -kernelArgs :: [String]-kernelArgs = ["--kernel", kernelName]- ipythonCommand :: SH.Sh SH.FilePath ipythonCommand = do jupyterMay <- SH.which "jupyter"@@ -85,33 +75,6 @@ Nothing -> SH.errorExit "The Jupyter binary could not be located" Just ipython -> return ipython --- | Run the IPython command with any arguments. The kernel is set to IHaskell.-ipython :: Bool -- ^ Whether to suppress output.- -> [Text] -- ^ IPython command line arguments.- -> SH.Sh String -- ^ IPython output.-ipython suppress args = do- liftIO $ installHandler keyboardSignal (CatchOnce $ return ()) Nothing-- -- We have this because using `run` does not let us use stdin.- cmd <- ipythonCommand- SH.runHandles cmd args handles doNothing-- where- handles = [SH.InHandle SH.Inherit, outHandle suppress, errorHandle suppress]- outHandle True = SH.OutHandle SH.CreatePipe- outHandle False = SH.OutHandle SH.Inherit- errorHandle True = SH.ErrorHandle SH.CreatePipe- errorHandle False = SH.ErrorHandle SH.Inherit- doNothing _ stdout _ = if suppress- then liftIO $ StrictIO.hGetContents stdout- else return ""---- | Run while suppressing all output.-quietRun path args = SH.runHandles path args handles nothing- where- handles = [SH.InHandle SH.Inherit, SH.OutHandle SH.CreatePipe, SH.ErrorHandle SH.CreatePipe]- nothing _ _ _ = return ()- fp :: SH.FilePath -> FilePath fp = T.unpack . SH.toTextIgnore @@ -126,17 +89,14 @@ ihaskellDir :: SH.Sh FilePath ihaskellDir = do home <- maybe (error "$HOME not defined.") SH.fromText <$> SH.get_env "HOME"- fp <$> ensure (return (home SH.</> ".ihaskell"))--notebookDir :: SH.Sh SH.FilePath-notebookDir = ensure $ (SH.</> "notebooks") <$> ihaskellDir+ fp <$> ensure (return (home SH.</> (".ihaskell" :: SH.FilePath))) getIHaskellDir :: IO String getIHaskellDir = SH.shelly ihaskellDir defaultConfFile :: IO (Maybe String) defaultConfFile = fmap (fmap fp) . SH.shelly $ do- filename <- (SH.</> "rc.hs") <$> ihaskellDir+ filename <- (SH.</> ("rc.hs" :: SH.FilePath)) <$> ihaskellDir exists <- SH.test_f filename return $ if exists then Just filename@@ -156,17 +116,17 @@ Nothing -> badIPython "No Jupyter / IPython detected -- install Jupyter 3.0+ before using IHaskell." Just path -> do- stdout <- SH.silently (SH.run path ["--version"])- stderr <- SH.lastStderr+ sout <- SH.silently (SH.run path ["--version"])+ serr <- SH.lastStderr let majorVersion = join . fmap listToMaybe . parseVersion . T.unpack- case mplus (majorVersion stderr) (majorVersion stdout) of+ case mplus (majorVersion serr) (majorVersion sout) of Nothing -> badIPython $ T.concat [ "Detected Jupyter, but could not parse version number." , "\n" , "(stdout = "- , stdout+ , sout , ", stderr = "- , stderr+ , serr , ")" ] @@ -183,7 +143,7 @@ -- | Install an IHaskell kernelspec into the right location. The right location is determined by -- using `ipython kernelspec install --user`. installKernelspec :: Bool -> KernelSpecOptions -> SH.Sh ()-installKernelspec replace opts = void $ do+installKernelspec repl opts = void $ do ihaskellPath <- getIHaskellPath confFile <- liftIO $ kernelSpecConfFile opts @@ -196,7 +156,7 @@ ++ ["--ghclib", kernelSpecGhcLibdir opts] ++ (case kernelSpecRTSOptions opts of [] -> []- rtsOpts -> "+RTS" : kernelSpecRTSOptions opts ++ ["-RTS"])+ _ -> "+RTS" : kernelSpecRTSOptions opts ++ ["-RTS"]) ++ ["--stack" | kernelSpecUseStack opts] let kernelSpec = KernelSpec@@ -209,7 +169,7 @@ -- shell out to IPython to install this kernelspec directory. SH.withTmpDir $ \tmp -> do let kernelDir = tmp SH.</> kernelName- let filename = kernelDir SH.</> "kernel.json"+ let filename = kernelDir SH.</> ("kernel.json" :: SH.FilePath) SH.mkdir_p kernelDir SH.writefile filename $ LT.toStrict $ toLazyText $ encodeToTextBuilder $ toJSON kernelSpec@@ -220,43 +180,24 @@ ipython <- locateIPython - let replaceFlag = ["--replace" | replace]+ let replaceFlag = ["--replace" | repl] installPrefixFlag = maybe ["--user"] (\prefix -> ["--prefix", T.pack prefix]) (kernelSpecInstallPrefix opts) cmd = concat [["kernelspec", "install"], installPrefixFlag, [SH.toTextIgnore kernelDir], replaceFlag] SH.silently $ SH.run ipython cmd -kernelSpecCreated :: SH.Sh Bool-kernelSpecCreated = do- ipython <- locateIPython- out <- SH.silently $ SH.run ipython ["kernelspec", "list"]- let kernelspecs = map T.strip $ T.lines out- return $ T.pack kernelName `elem` kernelspecs- -- | Replace "~" with $HOME if $HOME is defined. Otherwise, do nothing. subHome :: String -> IO String subHome path = SH.shelly $ do home <- T.unpack <$> fromMaybe "~" <$> SH.get_env "HOME" return $ replace "~" home path --- | Get the path to an executable. If it doensn't exist, fail with an error message complaining--- about it.-path :: Text -> SH.Sh SH.FilePath-path exe = do- path <- SH.which $ SH.fromText exe- case path of- Nothing -> do- liftIO $ putStrLn $ "Could not find `" ++ T.unpack exe ++ "` executable."- fail $ "`" ++ T.unpack exe ++ "` not on $PATH."- Just exePath -> return exePath- -- | Parse an IPython version string into a list of integers. parseVersion :: String -> Maybe [Int] parseVersion versionStr = let versions = map readMay $ split "." versionStr- parsed = all isJust versions- in if parsed- then Just $ map fromJust versions+ in if all isJust versions+ then Just $ catMaybes versions else Nothing -- | Get the absolute path to this IHaskell executable.@@ -268,7 +209,7 @@ -- If we have an absolute path, that's the IHaskell we're interested in. if FP.isAbsolute f then return f- else + else -- Check whether this is a relative path, or just 'IHaskell' with $PATH resolution done by -- the shell. If it's just 'IHaskell', use the $PATH variable to find where IHaskell lives. if FP.takeFileName f == f@@ -278,16 +219,6 @@ Nothing -> error "ihaskell not on $PATH and not referenced relative to directory." Just path -> return $ T.unpack $ SH.toTextIgnore path else liftIO $ makeAbsolute f-#if !MIN_VERSION_directory(1, 2, 2)--- This is included in later versions of `directory`, but we cannot use later versions because GHC--- library depends on a particular version of it.-makeAbsolute :: FilePath -> IO FilePath-makeAbsolute = fmap FP.normalise . absolutize- where- absolutize path -- avoid the call to `getCurrentDirectory` if we can- | FP.isRelative path = fmap (FP.</> path) getCurrentDirectory- | otherwise = return path-#endif getSandboxPackageConf :: IO (Maybe String) getSandboxPackageConf = SH.shelly $ do myPath <- getIHaskellPath
src/IHaskell/IPython/Stdin.hs view
@@ -26,21 +26,14 @@ module IHaskell.IPython.Stdin (fixStdin, recordParentHeader, recordKernelProfile) where import IHaskellPrelude-import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Char8 as CBS import Control.Concurrent import Control.Applicative ((<$>))-import Control.Concurrent.Chan-import Control.Monad import GHC.IO.Handle import GHC.IO.Handle.Types+import System.FilePath ((</>)) import System.Posix.IO import System.IO.Unsafe-import qualified Data.Map as Map import IHaskell.IPython.Types import IHaskell.IPython.ZeroMQ@@ -55,7 +48,9 @@ fixStdin :: String -> IO () fixStdin dir = do -- Initialize the stdin interface.- profile <- fromJust . readMay <$> readFile (dir ++ "/.kernel-profile")+ let fpath = dir </> ".kernel-profile"+ profile <- fromMaybe (error $ "fixStdin: Failed reading " ++ fpath)+ . readMay <$> readFile fpath interface <- serveStdin profile putMVar stdinInterface interface void $ forkIO $ stdinOnce dir@@ -79,8 +74,8 @@ loop stdinInput oldStdin newStdin = do let FileHandle _ mvar = stdin threadDelay $ 150 * 1000- empty <- isEmptyMVar mvar- if not empty+ e <- isEmptyMVar mvar+ if not e then loop stdinInput oldStdin newStdin else do line <- getInputLine dir@@ -94,17 +89,12 @@ -- Send a request for input. uuid <- UUID.random- parentHeader <- fromJust . readMay <$> readFile (dir ++ "/.last-req-header")- let header = MessageHeader- { username = username parentHeader- , identifiers = identifiers parentHeader- , parentHeader = Just parentHeader- , messageId = uuid- , sessionId = sessionId parentHeader- , metadata = Map.fromList []- , msgType = InputRequestMessage- }- let msg = RequestInput header ""+ let fpath = dir </> ".last-req-header"+ parentHdr <- fromMaybe (error $ "getInputLine: Failed reading " ++ fpath)+ . readMay <$> readFile fpath+ let hdr = MessageHeader (mhIdentifiers parentHdr) (Just parentHdr) mempty+ uuid (mhSessionId parentHdr) (mhUsername parentHdr) InputRequestMessage+ let msg = RequestInput hdr "" writeChan req msg -- Get the reply.@@ -112,8 +102,8 @@ return value recordParentHeader :: String -> MessageHeader -> IO ()-recordParentHeader dir header =- writeFile (dir ++ "/.last-req-header") $ show header+recordParentHeader dir hdr =+ writeFile (dir ++ "/.last-req-header") $ show hdr recordKernelProfile :: String -> Profile -> IO () recordKernelProfile dir profile =
src/IHaskell/Publish.hs view
@@ -1,10 +1,12 @@ {-# language NoImplicitPrelude, DoAndIfThenElse, OverloadedStrings, ExtendedDefaultRules #-}-module IHaskell.Publish (publishResult) where+module IHaskell.Publish+ ( publishResult+ ) where import IHaskellPrelude import qualified Data.Text as T-import qualified Data.Text.Encoding as E+import qualified Data.Time as Time import IHaskell.Display import IHaskell.Types@@ -24,23 +26,27 @@ -> MVar [DisplayData] -- ^ A MVar to use for storing pager output -> Bool -- ^ Whether to use the pager -> EvaluationResult -- ^ The evaluation result+ -> ErrorOccurred -- ^ Whether evaluation completed successfully -> IO ()-publishResult send replyHeader displayed updateNeeded pagerOutput usePager result = do+publishResult send replyHeader displayed updateNeeded poutput upager result success = do let final = case result of IntermediateResult{} -> False FinalResult{} -> True- outs = outputs result+ outs = evaluationOutputs result + -- Get time to send to output for unique labels.+ uniqueLabel <- getUniqueLabel+ -- If necessary, clear all previous output and redraw. clear <- readMVar updateNeeded when clear $ do clearOutput disps <- readMVar displayed- mapM_ sendOutput $ reverse disps+ mapM_ (sendOutput uniqueLabel) $ reverse disps -- Draw this message.- sendOutput outs+ sendOutput uniqueLabel outs -- If this is the final message, add it to the list of completed messages. If it isn't, make sure we -- clear it later by marking update needed as true.@@ -49,30 +55,45 @@ modifyMVar_ displayed (return . (outs :)) -- If this has some pager output, store it for later.- let pager = pagerOut result- unless (null pager) $- if usePager- then modifyMVar_ pagerOutput (return . (++ pager))- else sendOutput $ Display pager+ case result of+ IntermediateResult _ -> pure ()+ FinalResult _ pager _ ->+ unless (null pager) $+ if upager+ then modifyMVar_ poutput (return . (++ pager))+ else sendOutput uniqueLabel $ Display pager where clearOutput = do- header <- dupHeader replyHeader ClearOutputMessage- send $ ClearOutput header True+ hdr <- dupHeader replyHeader ClearOutputMessage+ send $ ClearOutput hdr True - sendOutput (ManyDisplay manyOuts) = mapM_ sendOutput manyOuts- sendOutput (Display outs) = do- header <- dupHeader replyHeader DisplayDataMessage- send $ PublishDisplayData header $ map (convertSvgToHtml . prependCss) outs+ sendOutput uniqueLabel (ManyDisplay manyOuts) =+ mapM_ (sendOutput uniqueLabel) manyOuts+ sendOutput uniqueLabel (Display outs) = case success of+ Success -> do+ hdr <- dupHeader replyHeader DisplayDataMessage+ send $ PublishDisplayData hdr (map (makeUnique uniqueLabel . prependCss) outs) Nothing+ Failure -> do+ hdr <- dupHeader replyHeader ExecuteErrorMessage+ send $ ExecuteError hdr [T.pack (extractPlain outs)] "" "" - convertSvgToHtml (DisplayData MimeSvg svg) = html $ makeSvgImg $ base64 $ E.encodeUtf8 svg- convertSvgToHtml x = x+ prependCss (DisplayData MimeHtml h) =+ DisplayData MimeHtml $ mconcat ["<style>", T.pack ihaskellCSS, "</style>", h]+ prependCss x = x - makeSvgImg :: Base64 -> String- makeSvgImg base64data = T.unpack $ "<img src=\"data:image/svg+xml;base64," <>- base64data <>- "\"/>"+ makeUnique l (DisplayData MimeSvg s) =+ DisplayData MimeSvg+ . T.replace "glyph" ("glyph-" <> l)+ . T.replace "\"clip" ("\"clip-" <> l)+ . T.replace "#clip" ("#clip-" <> l)+ . T.replace "\"image" ("\"image-" <> l)+ . T.replace "#image" ("#image-" <> l)+ . T.replace "linearGradient id=\"linear" ("linearGradient id=\"linear-" <> l)+ . T.replace "#linear" ("#linear-" <> l)+ $ s+ makeUnique _ x = x - prependCss (DisplayData MimeHtml html) =- DisplayData MimeHtml $ mconcat ["<style>", T.pack ihaskellCSS, "</style>", html]- prependCss x = x+ getUniqueLabel =+ fmap (\(Time.UTCTime d s) -> T.pack (show d) <> T.pack (show s))+ Time.getCurrentTime
src/IHaskell/Types.hs view
@@ -12,13 +12,15 @@ MessageType(..), dupHeader, Username,- Metadata(..),+ Metadata, replyType, ExecutionState(..), StreamType(..), MimeType(..), DisplayData(..),+ ErrorOccurred(..), EvaluationResult(..),+ evaluationOutputs, ExecuteReplyStatus(..), KernelState(..), LintStatus(..),@@ -39,17 +41,8 @@ import IHaskellPrelude -import qualified Data.ByteString as BS-import qualified Data.ByteString.Char8 as CBS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.Text as T-import qualified Data.Text.Lazy as LT--import Data.Aeson (Value, (.=), object)-import Data.Aeson.Types (emptyObject)-import qualified Data.ByteString.Char8 as Char+import Data.Aeson (ToJSON (..), Value, (.=), object) import Data.Function (on)-import Data.Semigroup import Data.Serialize import GHC.Generics @@ -100,6 +93,28 @@ -> IO () close _ _ = return () +-- | these instances cause the image, html etc. which look like:+--+-- > Display+-- > [Display]+-- > IO [Display]+-- > IO (IO Display)+--+-- be run the IO and get rendered (if the frontend allows it) in the pretty form.+instance IHaskellDisplay a => IHaskellDisplay (IO a) where+ display = (display =<<)++instance IHaskellDisplay Display where+ display = return++instance IHaskellDisplay DisplayData where+ display disp = return $ Display [disp]++instance IHaskellDisplay a => IHaskellDisplay [a] where+ display disps = do+ displays <- mapM display disps+ return $ ManyDisplay displays+ data Widget = forall a. IHaskellWidget a => Widget a deriving Typeable @@ -157,7 +172,7 @@ defaultKernelState = KernelState { getExecutionCounter = 1 , getLintStatus = LintOn- , useSvg = False+ , useSvg = True , useShowErrors = False , useShowTypes = False , usePager = True@@ -230,29 +245,39 @@ | DisplayWidget instance ToJSON WidgetMethod where- toJSON DisplayWidget = object ["method" .= "display"]- toJSON (UpdateState v) = object ["method" .= "update", "state" .= v]- toJSON (CustomContent v) = object ["method" .= "custom", "content" .= v]+ toJSON DisplayWidget = object ["method" .= ("display" :: Text)]+ toJSON (UpdateState v) = object ["method" .= ("update" :: Text), "state" .= v]+ toJSON (CustomContent v) = object ["method" .= ("custom" :: Text), "content" .= v] -- | Output of evaluation.-data EvaluationResult =- -- | An intermediate result which communicates what has been printed thus- -- far.- IntermediateResult- { outputs :: Display -- ^ Display outputs.- }- |- FinalResult- { outputs :: Display -- ^ Display outputs.- , pagerOut :: [DisplayData] -- ^ Mimebundles to display in the IPython- -- pager.- , commMsgs :: [WidgetMsg] -- ^ Comm operations- }+--+-- A result can either be intermediate or final.+-- Final result has Mimebundles ('DisplayData') and Comm operations+-- ('WidgetMsg') on top of Display outputs.+data EvaluationResult+ -- | An intermediate result which communicates what has been printed thus far.+ = IntermediateResult+ !Display+ | FinalResult+ !Display+ ![DisplayData]+ ![WidgetMsg] deriving Show ++evaluationOutputs :: EvaluationResult -> Display+evaluationOutputs er =+ case er of+ IntermediateResult outputs -> outputs+ FinalResult outputs _ _ -> outputs+ -- | Duplicate a message header, giving it a new UUID and message type. dupHeader :: MessageHeader -> MessageType -> IO MessageHeader-dupHeader header messageType = do+dupHeader hdr messageType = do uuid <- liftIO random+ return hdr { mhMessageId = uuid, mhMsgType = messageType } - return header { messageId = uuid, msgType = messageType }+-- | Whether or not an error occurred.+data ErrorOccurred = Success+ | Failure+ deriving (Show, Eq)
src/IHaskellPrelude.hs view
@@ -8,12 +8,10 @@ Data.Typeable.Typeable, Data.Typeable.cast, -#if MIN_VERSION_ghc(7,8,0) Data.Typeable.Proxy, GHC.Exts.IsString, GHC.Exts.IsList,-#endif System.IO.hPutStrLn, System.IO.hPutStr,@@ -54,8 +52,8 @@ Data.IORef.modifyIORef', Data.IORef.newIORef, - + -- Miscellaneous names Data.Map.Map, GHC.IO.FilePath,@@ -80,20 +78,14 @@ import GHC.Num as X import GHC.Real as X import GHC.Err as X hiding (absentErr)-#if MIN_VERSION_ghc(8,0,0) import GHC.Base as X hiding (Any, mapM, foldr, sequence, many, (<|>), Module(..))-#elif MIN_VERSION_ghc(7,10,0)-import GHC.Base as X hiding (Any, mapM, foldr, sequence, many, (<|>))-#else-import GHC.Base as X hiding (Any)-#endif import Data.List as X hiding (head, last, tail, init, transpose, subsequences, permutations, foldl, foldl1, maximum, minimum, scanl, scanl1, scanr, scanr1, span, break, mapAccumL, mapAccumR, dropWhileEnd, (!!), elemIndices, elemIndex, findIndex, findIndices, zip5, zip6, zip7, zipWith5, zipWith6, zipWith7, unzip5, unzip6, unzip6, delete, union, lookup, intersect, insert, deleteBy,- deleteFirstBy, unionBy, intersectBy, group, groupBy, insertBy,+ unionBy, intersectBy, group, groupBy, insertBy, maximumBy, minimumBy, genericLength, genericDrop, genericTake, genericSplitAt, genericIndex, genericReplicate, inits, tails) @@ -121,13 +113,27 @@ type LText = Data.Text.Lazy.Text -(headMay, tailMay, lastMay, initMay, maximumMay, minimumMay) =- (wrapEmpty head, wrapEmpty tail, wrapEmpty last,- wrapEmpty init, wrapEmpty maximum, wrapEmpty minimum)- where- wrapEmpty :: ([a] -> b) -> [a] -> Maybe b- wrapEmpty _ [] = Nothing- wrapEmpty f xs = Just (f xs)+headMay :: [a] -> Maybe a+headMay = wrapEmpty head++tailMay :: [a] -> Maybe [a]+tailMay = wrapEmpty tail++lastMay :: [a] -> Maybe a+lastMay = wrapEmpty last++initMay :: [a] -> Maybe [a]+initMay = wrapEmpty init++maximumMay :: Ord a => [a] -> Maybe a+maximumMay = wrapEmpty maximum++minimumMay :: Ord a => [a] -> Maybe a+minimumMay = wrapEmpty minimum++wrapEmpty :: ([a] -> b) -> [a] -> Maybe b+wrapEmpty _ [] = Nothing+wrapEmpty f xs = Just (f xs) maximumByMay :: (a -> a -> Ordering) -> [a] -> Maybe a maximumByMay _ [] = Nothing
src/StringUtils.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE NoImplicitPrelude #-} module StringUtils ( strip, lstrip,
src/tests/Hspec.hs view
@@ -7,10 +7,12 @@ import IHaskell.Test.Completion (testCompletions) import IHaskell.Test.Parser (testParser) import IHaskell.Test.Eval (testEval)+import IHaskell.Test.Hoogle (testHoogle) main :: IO ()-main = +main = hspec $ do testParser testEval testCompletions+ testHoogle
src/tests/IHaskell/Test/Completion.hs view
@@ -1,5 +1,9 @@ {-# language NoImplicitPrelude, DoAndIfThenElse, OverloadedStrings, ExtendedDefaultRules #-} {-# LANGUAGE CPP #-}++-- Shelly's types are kinda borked.+{-# OPTIONS_GHC -Wno-type-defaults #-}+ module IHaskell.Test.Completion (testCompletions) where import Prelude@@ -23,12 +27,8 @@ completionTarget) import IHaskell.Test.Util (replace, shouldBeAmong, ghc) -#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>))-#endif- -- | @readCompletePrompt "xs*ys"@ return @(xs, i)@ where i is the location of--- @'*'@ in the input string. +-- @'*'@ in the input string. readCompletePrompt :: String -> (String, Int) readCompletePrompt string = case elemIndex '*' string of@@ -48,23 +48,20 @@ shouldHaveCompletionsInDirectory :: String -> [String] -> IO () shouldHaveCompletionsInDirectory string expected = do- (matched, completions) <- completionEventInDirectory string- let existsInCompletion = (`elem` completions)- unmatched = filter (not . existsInCompletion) expected+ (_, completions) <- completionEventInDirectory string expected `shouldBeAmong` completions +completionHas :: String -> [String] -> IO () completionHas string expected = do- (matched, completions) <- ghc $ do+ (_, completions) <- ghc $ do initCompleter completionEvent string- let existsInCompletion = (`elem` completions)- unmatched = filter (not . existsInCompletion) expected expected `shouldBeAmong` completions initCompleter :: Interpreter () initCompleter = do flags <- getSessionDynFlags- setSessionDynFlags $ flags { hscTarget = HscInterpreted, ghcLink = LinkInMemory }+ _ <- setSessionDynFlags $ flags { hscTarget = HscInterpreted, ghcLink = LinkInMemory } -- Import modules. imports <- mapM parseImportDecl@@ -168,9 +165,8 @@ it "correctly interprets ~ as the environment HOME variable" $ do let shouldHaveCompletions :: String -> [String] -> IO () shouldHaveCompletions string expected = do- (matched, completions) <- withHsHome $ completionEvent string- let existsInCompletion = (`elem` completions)- unmatched = filter (not . existsInCompletion) expected+ (_, completions) <- withHsHome $ completionEvent string+ expected `shouldBeAmong` completions ":! cd ~/*" `shouldHaveCompletions` ["~/dir/"] ":! ~/*" `shouldHaveCompletions` ["~/dir/"]@@ -182,8 +178,6 @@ matchText <- withHsHome $ fst <$> uncurry complete (readCompletePrompt string) matchText `shouldBe` expected - setHomeEvent path = liftIO $ setEnv "HOME" (T.unpack $ toTextIgnore path)- it "generates the correct matchingText on `:! cd ~/*` " $ ":! cd ~/*" `shouldHaveMatchingText` ("~/" :: String) @@ -197,7 +191,7 @@ -> [Shelly.FilePath] -- ^ files relative to temporary directory -> (Shelly.FilePath -> Interpreter a) -> IO a--- | Run an Interpreter action, but first make a temporary directory +-- | Run an Interpreter action, but first make a temporary directory -- with some files and folder and cd to it. inDirectory dirs files action = shelly $ withTmpDir $ \dirPath -> do cd dirPath@@ -207,11 +201,11 @@ where cdEvent path = liftIO $ setCurrentDirectory path wrap :: String -> Interpreter a -> Interpreter a- wrap path action = do+ wrap path actn = do initCompleter pwd <- IHaskell.Eval.Evaluate.liftIO getCurrentDirectory cdEvent path -- change to the temporary directory- out <- action -- run action+ out <- actn -- run action cdEvent pwd -- change back to the original directory return out @@ -223,4 +217,5 @@ , p "dir" </> p "file2.lhs" ] where+ p :: T.Text -> T.Text p = id
src/tests/IHaskell/Test/Eval.hs view
@@ -24,34 +24,34 @@ eval string = do outputAccum <- newIORef [] pagerAccum <- newIORef []- let publish evalResult =+ let publish evalResult _ = case evalResult of IntermediateResult{} -> return ()- FinalResult outs page [] -> do+ FinalResult outs page _ -> do modifyIORef outputAccum (outs :) modifyIORef pagerAccum (page :) noWidgetHandling s _ = return s getTemporaryDirectory >>= setCurrentDirectory let state = defaultKernelState { getLintStatus = LintOff }- interpret GHC.Paths.libdir False $ const $- IHaskell.Eval.Evaluate.evaluate state string publish noWidgetHandling+ _ <- interpret GHC.Paths.libdir False $ const $+ IHaskell.Eval.Evaluate.evaluate state string publish noWidgetHandling out <- readIORef outputAccum- pagerOut <- readIORef pagerAccum- return (reverse out, unlines . map extractPlain . reverse $ pagerOut)+ pagerout <- readIORef pagerAccum+ return (reverse out, unlines . map extractPlain . reverse $ pagerout) becomes :: String -> [String] -> IO () becomes string expected = evaluationComparing comparison string where comparison :: ([Display], String) -> IO ()- comparison (results, pageOut) = do+ comparison (results, _pageOut) = do when (length results /= length expected) $ expectationFailure $ "Expected result to have " ++ show (length expected) ++ " results. Got " ++ show results - forM_ (zip results expected) $ \(ManyDisplay [Display result], expected) -> case extractPlain result of- "" -> expectationFailure $ "No plain-text output in " ++ show result ++ "\nExpected: " ++ expected- str -> str `shouldBe` expected+ forM_ (zip results expected) $ \(ManyDisplay [Display result], expect) -> case extractPlain result of+ "" -> expectationFailure $ "No plain-text output in " ++ show result ++ "\nExpected: " ++ expect+ str -> str `shouldBe` expect evaluationComparing :: (([Display], String) -> IO b) -> String -> IO b evaluationComparing comparison string = do@@ -66,37 +66,30 @@ pages :: String -> [String] -> IO () pages string expected = evaluationComparing comparison string where- comparison (results, pageOut) =+ comparison (_results, pageOut) = strip (stripHtml pageOut) `shouldBe` strip (fixQuotes $ unlines expected) -- A very, very hacky method for removing HTML stripHtml str = go str where- go ('<':str) =- case stripPrefix "script" str of+ go ('<':xs) =+ case stripPrefix "script" xs of Nothing -> go' str- Just str -> dropScriptTag str+ Just s -> dropScriptTag s go (x:xs) = x : go xs go [] = [] - go' ('>':str) = go str- go' (x:xs) = go' xs+ go' ('>':xs) = go xs+ go' (_:xs) = go' xs go' [] = error $ "Unending bracket html tag in string " ++ str - dropScriptTag str =- case stripPrefix "</script>" str of- Just str -> go str- Nothing -> dropScriptTag $ tail str+ dropScriptTag str1 =+ case stripPrefix "</script>" str1 of+ Just s -> go s+ Nothing -> dropScriptTag $ tail str fixQuotes :: String -> String-#if MIN_VERSION_ghc(7, 8, 0) fixQuotes = id-#else- fixQuotes = map $ \char -> case char of- '\8216' -> '`'- '\8217' -> '\''- c -> c-#endif testEval :: Spec@@ -163,6 +156,9 @@ it "prints Unicode characters correctly" $ do "putStrLn \"Héllö, Üñiço∂e!\"" `becomes` ["Héllö, Üñiço∂e!"] "putStrLn \"Привет!\"" `becomes` ["Привет!"]++ it "prints multiline output correctly" $ do+ ":! printf \"hello\\nworld\"" `becomes` ["hello\nworld"] it "evaluates directives" $ do #if MIN_VERSION_ghc(8,2,0)
+ src/tests/IHaskell/Test/Hoogle.hs view
@@ -0,0 +1,63 @@+{-# LANGUAGE QuasiQuotes #-}++module IHaskell.Test.Hoogle ( testHoogle ) where++import Test.Hspec+import Text.RawString.QQ++import IHaskell.Eval.Hoogle+-- import Data.Text (unpack)+-- import qualified Data.Text.IO as T+preludeFmapJson :: String+preludeFmapJson = [r|+[+ {+ "url": "https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap",+ "module": {+ "url": "https://hackage.haskell.org/package/base/docs/Prelude.html",+ "name": "Prelude"+ },+ "package": {+ "url": "https://hackage.haskell.org/package/base",+ "name": "base"+ },+ "item": "<span class=name><0>fmap</0></span> :: Functor f => (a -> b) -> f a -> f b",+ "type": "",+ "docs": ""+ }+]|]++moduleJson :: String+moduleJson = [r|+[+ {+ "url": "https://hackage.haskell.org/package/universum/docs/Universum-Functor-Fmap.html",+ "module": {},+ "package": {+ "url": "https://hackage.haskell.org/package/universum",+ "name": "universum"+ },+ "item": "<b>module</b> Universum.Functor.<span class=name><0>Fmap</0></span>",+ "type": "module",+ "docs": "This module contains useful functions to work with <a>Functor</a> type\nclass.\n"+ }+]|]++testHoogle :: Spec+testHoogle = describe "Hoogle Search" $ do+ describe "fmap search result" $ do+ let results = parseResponse preludeFmapJson :: [HoogleResult]+ it "should find 1 results" $ do+ length results `shouldBe` 1+ let (SearchResult (HoogleResponse loc signature _docUrl)) = head results+ it "should not contain html markup" $ do+ loc `shouldBe` "https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap"+ signature `shouldBe` "fmap :: Functor f => (a -> b) -> f a -> f b"+ describe "module result" $ do+ let results = parseResponse moduleJson :: [HoogleResult]+ let (SearchResult (HoogleResponse _loc signature _docUrl)) = head results+ it "should not contain html markup" $ do+ signature `shouldBe` "module Universum.Functor.Fmap"+ it "should be renderable" $ do+ (render Plain $ head results) `shouldStartWith` "module Universum.Functor.Fmap"+ (render HTML $ head results) `shouldStartWith` "<span class='hoogle-head'>module</span>"
src/tests/IHaskell/Test/Parser.hs view
@@ -1,25 +1,18 @@ {-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE CPP #-}-module IHaskell.Test.Parser (testParser) where +module IHaskell.Test.Parser (testParser) where import Prelude import Data.String.Here (hereLit) import Test.Hspec-import Test.Hspec.Contrib.HUnit-import Test.HUnit (assertBool, assertFailure)+import Test.HUnit (assertFailure) import IHaskell.Test.Util (ghc, strip) import IHaskell.Eval.Parser (parseString, getModuleName, unloc, layoutChunks, Located(..), CodeBlock(..), DirectiveType(..), StringLoc(..), PragmaType(..)) import IHaskell.Eval.ParseShell (parseShell) -#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>))-#endif-- parses :: String -> IO [CodeBlock] parses str = map unloc <$> ghc (parseString str) @@ -234,10 +227,4 @@ |]) >>= (`shouldBe` [Located 2 (Expression "first"), Located 4 (Expression "second")]) where dataKindsError = ParseError (Loc 1 10) msg-#if MIN_VERSION_ghc(7, 10, 0) msg = "Cannot parse data constructor in a data/newtype declaration: 3"-#elif MIN_VERSION_ghc(7, 8, 0)- msg = "Illegal literal in type (use DataKinds to enable): 3"-#else- msg = "Illegal literal in type (use -XDataKinds to enable): 3"-#endif