diff --git a/Hspec.hs b/Hspec.hs
new file mode 100644
--- /dev/null
+++ b/Hspec.hs
@@ -0,0 +1,601 @@
+{-# LANGUAGE QuasiQuotes, OverloadedStrings, ExtendedDefaultRules, CPP #-}
+-- Keep all the language pragmas here so it can be compiled separately.
+module Main where
+import Prelude
+import GHC
+import GHC.Paths
+import Data.IORef
+import Control.Monad
+import Control.Monad.IO.Class ( MonadIO, liftIO )
+import Data.List
+import System.Directory
+import Shelly (Sh, shelly, cmd, (</>), toTextIgnore, cd, withTmpDir, mkdir_p,
+  touchfile)
+import qualified Shelly
+import Control.Applicative ((<$>))
+import Filesystem.Path.CurrentOS (encodeString)
+import System.SetEnv (setEnv)
+import Data.String.Here
+import Data.String.Utils (strip, replace)
+import Data.Monoid
+
+import IHaskell.Eval.Parser
+import IHaskell.Types
+import IHaskell.IPython
+import IHaskell.Eval.Evaluate as Eval hiding (liftIO)
+import qualified IHaskell.Eval.Evaluate as Eval (liftIO)
+
+import IHaskell.Eval.Completion
+import IHaskell.Eval.ParseShell
+
+import Debug.Trace
+
+import Test.Hspec
+import Test.Hspec.HUnit
+import Test.HUnit (assertBool, assertFailure)
+
+traceShowId x = traceShow x x 
+
+doGhc = runGhc (Just libdir)
+
+parses str = do
+  res <- doGhc $ parseString str
+  return $ map unloc res
+
+like parser desired = parser >>= (`shouldBe` desired)
+
+is string blockType = do
+  result <- doGhc $ parseString string
+  map unloc result `shouldBe` [blockType $ strip string]
+
+eval string = do
+  outputAccum <- newIORef []
+  pagerAccum <- newIORef []
+  let publish evalResult = case evalResult of
+        IntermediateResult {} -> return ()
+        FinalResult outs page [] -> do
+          modifyIORef outputAccum (outs :)
+          modifyIORef pagerAccum (page :)
+
+  getTemporaryDirectory >>= setCurrentDirectory
+  let state = defaultKernelState { getLintStatus = LintOff }
+  interpret libdir False $ Eval.evaluate state string publish
+  out <- readIORef outputAccum
+  pagerOut <- readIORef pagerAccum
+  return (reverse out, unlines $ reverse pagerOut)
+
+evaluationComparing comparison string = do
+    let indent (' ':x) = 1 + indent x
+        indent _ = 0
+        empty = null . strip
+        stringLines = filter (not . empty) $ lines string
+        minIndent = minimum (map indent stringLines)
+        newString = unlines $ map (drop minIndent) stringLines
+    eval newString >>= comparison
+
+becomes string expected = evaluationComparing comparison string
+  where
+    comparison :: ([Display], String) -> IO ()
+    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
+
+pages string expected = evaluationComparing comparison string
+  where
+    comparison (results, pageOut) =
+      strip (stripHtml pageOut) `shouldBe` strip (unlines expected)
+
+    -- A very, very hacky method for removing HTML
+    stripHtml str = go str
+      where
+        go ('<':str) = case stripPrefix "script" str of
+          Nothing -> go' str
+          Just str -> dropScriptTag str
+        go (x:xs) = x : go xs
+        go [] = []
+
+        go' ('>':str) = go str
+        go' (x: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
+
+readCompletePrompt :: String -> (String, Int)
+-- | @readCompletePrompt "xs*ys"@ return @(xs, i)@ where i is the location of
+-- @'*'@ in the input string. 
+readCompletePrompt string = case elemIndex '*' string of
+          Nothing -> error "Expected cursor written as '*'."
+          Just idx -> (replace "*" "" string, idx)
+
+completes string expected = completionTarget newString cursorloc `shouldBe` expected
+  where (newString, cursorloc) = readCompletePrompt string
+
+completionEvent :: String -> Interpreter (String, [String])
+completionEvent string = complete newString cursorloc
+  where (newString, cursorloc) = case elemIndex '*' string of
+          Nothing -> error "Expected cursor written as '*'."
+          Just idx -> (replace "*" "" string, idx)      
+
+completionEventInDirectory :: String -> IO (String, [String])
+completionEventInDirectory string  
+  = withHsDirectory $ const $ completionEvent string 
+                          
+
+shouldHaveCompletionsInDirectory :: String -> [String] -> IO ()
+shouldHaveCompletionsInDirectory string expected = do 
+  (matched, completions) <- completionEventInDirectory string  
+  let existsInCompletion = (`elem` completions)
+      unmatched = filter (not . existsInCompletion) expected
+  expected `shouldBeAmong` completions
+
+completionHas string expected 
+  = do (matched, completions) <- doGhc $ 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 }
+
+  -- Import modules.
+  imports <- mapM parseImportDecl ["import Prelude",
+                                   "import qualified Control.Monad",
+                                   "import qualified Data.List as List",
+                                   "import IHaskell.Display",
+                                   "import Data.Maybe as Maybe"]
+  setContext $ map IIDecl imports
+
+inDirectory :: [Shelly.FilePath] -- ^ directories relative to temporary directory
+              -> [Shelly.FilePath] -- ^ files relative to temporary directory
+              -> (Shelly.FilePath -> Interpreter a)
+              -> IO a
+-- | 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
+         mapM_ mkdir_p   dirs
+         mapM_ touchfile files
+         liftIO $ doGhc $ wrap (encodeString dirPath) (action dirPath)
+      where cdEvent path = liftIO $ setCurrentDirectory path --Eval.evaluate defaultKernelState (":! cd " ++ path) noPublish
+            wrap :: FilePath -> Interpreter a -> Interpreter a
+            wrap path action = 
+              do initCompleter
+                 pwd <- Eval.liftIO getCurrentDirectory
+                 cdEvent path   -- change to the temporary directory
+                 out <- action  -- run action
+                 cdEvent pwd    -- change back to the original directory
+                 return out
+
+withHsDirectory :: (Shelly.FilePath -> Interpreter a)  -> IO a
+withHsDirectory = inDirectory ["" </> "dir", "dir" </> "dir1"]
+                    [""</> "file1.hs", "dir" </> "file2.hs",  
+                     "" </> "file1.lhs", "dir" </> "file2.lhs"]
+
+main :: IO ()
+main = hspec $ do
+  parserTests
+  evalTests
+  completionTests
+
+completionTests = do
+  parseShellTests
+  describe "Completion" $ do
+    it "correctly gets the completion identifier without dots" $ do
+       "hello*"              `completes` ["hello"]
+       "hello aa*bb goodbye" `completes` ["aa"]
+       "hello aabb* goodbye" `completes` ["aabb"]
+       "aacc* goodbye"       `completes` ["aacc"]
+       "hello *aabb goodbye" `completes` []
+       "*aabb goodbye"       `completes` []
+
+    it "correctly gets the completion identifier with dots" $ do
+       "hello test.aa*bb goodbye" `completes` ["test", "aa"]
+       "Test.*"                   `completes` ["Test", ""]
+       "Test.Thing*"              `completes` ["Test", "Thing"]
+       "Test.Thing.*"             `completes` ["Test", "Thing", ""]
+       "Test.Thing.*nope"         `completes` ["Test", "Thing", ""]
+
+    it "correctly gets the completion type" $ do
+      completionType "import Data." 12 ["Data", ""]      `shouldBe` ModuleName "Data" ""
+      completionType "import Prel" 11 ["Prel"]           `shouldBe` ModuleName "" "Prel"
+      completionType "import D.B.M" 12 ["D", "B", "M"]   `shouldBe` ModuleName "D.B" "M"
+      completionType " import A." 10 ["A", ""]           `shouldBe` ModuleName "A" ""
+      completionType "import a.x" 10 ["a", "x"]          `shouldBe` Identifier "x"
+      completionType "A.x" 3 ["A", "x"]                 `shouldBe` Qualified "A" "x"
+      completionType "a.x" 3 ["a", "x"]                 `shouldBe` Identifier "x"
+      completionType "pri" 3 ["pri"]                    `shouldBe` Identifier "pri"
+      completionType ":load A" 7 ["A"]                   `shouldBe` HsFilePath ":load A"
+                                                                               "A"
+      completionType ":! cd " 6 [""]                     `shouldBe` FilePath   ":! cd " ""
+
+
+
+    it "properly completes identifiers" $ do
+       "pri*"           `completionHas` ["print"]
+       "ma*"            `completionHas` ["map"]
+       "hello ma*"      `completionHas` ["map"]
+       "print $ catMa*" `completionHas` ["catMaybes"]
+
+    it "properly completes qualified identifiers" $ do
+       "Control.Monad.liftM*"    `completionHas` [ "Control.Monad.liftM"
+                                                 , "Control.Monad.liftM2"
+                                                 , "Control.Monad.liftM5"]
+       "print $ List.intercal*"  `completionHas` ["List.intercalate"]
+       "print $ Data.Maybe.cat*" `completionHas` ["Data.Maybe.catMaybes"]
+       "print $ Maybe.catM*"     `completionHas` ["Maybe.catMaybes"]
+
+    it "properly completes imports" $ do
+      "import Data.*"  `completionHas` ["Data.Maybe", "Data.List"]
+      "import Data.M*" `completionHas` ["Data.Maybe"]
+      "import Prel*"   `completionHas` ["Prelude"]
+
+    it "properly completes haskell file paths on :load directive" $
+         let loading xs = ":load " ++ encodeString xs
+             paths = map encodeString
+         in do 
+            loading ("dir" </> "file*") `shouldHaveCompletionsInDirectory` paths ["dir" </> "file2.hs",
+                                                                                  "dir" </> "file2.lhs"]
+            loading ("" </> "file1*") `shouldHaveCompletionsInDirectory` paths ["" </> "file1.hs",
+                                                                                            "" </> "file1.lhs"]
+            loading ("" </> "file1*") `shouldHaveCompletionsInDirectory` paths ["" </> "file1.hs",
+                                                                                "" </> "file1.lhs"]
+            loading ("" </> "./*") `shouldHaveCompletionsInDirectory` paths ["./" </> "dir/"
+                                                                                       , "./" </> "file1.hs"
+                                                                                       , "./" </> "file1.lhs"]
+            loading ("" </> "./*") `shouldHaveCompletionsInDirectory` paths ["./" </> "dir/"
+                                                                           , "./" </> "file1.hs"
+                                                                           , "./" </> "file1.lhs"]
+
+    it "provides path completions on empty shell cmds " $
+      ":! cd *" `shouldHaveCompletionsInDirectory` map encodeString ["" </> "dir/"
+                                                                    , "" </> "file1.hs"
+                                                                    , "" </> "file1.lhs"]
+
+    let withHsHome action = withHsDirectory $ \dirPath->  do
+          home <- shelly $ Shelly.get_env_text "HOME"
+          setHomeEvent dirPath
+          result <- action
+          setHomeEvent $ Shelly.fromText home
+          return result
+        setHomeEvent path = liftIO $ setEnv "HOME" (encodeString path)
+
+    it "correctly interprets ~ as the environment HOME variable" $ 
+        let shouldHaveCompletions :: String -> [String] -> IO ()
+            shouldHaveCompletions string expected = do 
+              (matched, completions) <- withHsHome $ completionEvent string 
+              let existsInCompletion = (`elem` completions)
+                  unmatched = filter (not . existsInCompletion) expected     
+              expected `shouldBeAmong` completions
+
+        in do 
+          ":! cd ~/*" `shouldHaveCompletions` ["~/dir/"]
+          ":! ~/*" `shouldHaveCompletions` ["~/dir/"]
+          ":load ~/*" `shouldHaveCompletions` ["~/dir/"]
+          ":l ~/*" `shouldHaveCompletions` ["~/dir/"]
+
+    let shouldHaveMatchingText :: String -> String -> IO ()
+        shouldHaveMatchingText string expected = do 
+          matchText <- withHsHome $ fst <$> uncurry complete (readCompletePrompt string)
+          matchText `shouldBe` expected
+
+        setHomeEvent path = liftIO $ setEnv "HOME" (encodeString path)
+
+    it "generates the correct matchingText on `:! cd ~/*` " $ 
+      do ":! cd ~/*" `shouldHaveMatchingText` ("~/" :: String)
+
+    it "generates the correct matchingText on `:load ~/*` " $ 
+      do ":load ~/*" `shouldHaveMatchingText` ("~/" :: String)
+
+    it "generates the correct matchingText on `:l ~/*` " $ 
+      do ":l ~/*" `shouldHaveMatchingText` ("~/" :: String)
+
+evalTests = do
+  describe "Code Evaluation" $ do
+    it "evaluates expressions" $  do
+      "3" `becomes` ["3"]
+      "3+5" `becomes` ["8"]
+      "print 3" `becomes` ["3"]
+      [hereLit|
+        let x = 11
+            z = 10 in
+          x+z
+      |] `becomes` ["21"]
+
+    it "evaluates flags" $  do
+      ":set -package hello" `becomes` ["Warning: -package not supported yet"]
+      ":set -XNoImplicitPrelude" `becomes` []
+
+    it "evaluates multiline expressions" $  do
+      [hereLit|
+        import Control.Monad
+        forM_ [1, 2, 3] $ \x ->
+          print x
+      |] `becomes` ["1\n2\n3"]
+
+    it "evaluates function declarations silently" $ do
+      [hereLit|
+        fun :: [Int] -> Int
+        fun [] = 3
+        fun (x:xs) = 10
+        fun [1, 2]
+      |] `becomes` ["10"]
+
+    it "evaluates data declarations" $ do
+      [hereLit|
+        data X = Y Int
+               | Z String
+               deriving (Show, Eq)
+        print [Y 3, Z "No"]
+        print (Y 3 == Z "No")
+      |] `becomes` ["[Y 3,Z \"No\"]", "False"]
+
+    it "evaluates do blocks in expressions" $ do
+      [hereLit|
+        show (show (do
+            Just 10
+            Nothing
+            Just 100))
+      |] `becomes` ["\"\\\"Nothing\\\"\""]
+
+    it "is silent for imports" $ do
+      "import Control.Monad" `becomes` []
+      "import qualified Control.Monad" `becomes` []
+      "import qualified Control.Monad as CM" `becomes` []
+      "import Control.Monad (when)" `becomes` []
+
+    it "evaluates directives" $ do
+      ":typ 3" `becomes` ["3 :: forall a. Num a => a"]
+      ":k Maybe" `becomes` ["Maybe :: * -> *"]
+#if MIN_VERSION_ghc(7, 8, 0)
+      ":in String" `pages` ["type String = [Char] \t-- Defined in \8216GHC.Base\8217"]
+#else
+      ":in String" `pages` ["type String = [Char] \t-- Defined in `GHC.Base'"]
+#endif
+
+parserTests = do
+  layoutChunkerTests
+  moduleNameTests
+  parseStringTests
+
+layoutChunkerTests = describe "Layout Chunk" $ do
+  it "chunks 'a string'" $
+    map unloc (layoutChunks "a string") `shouldBe` ["a string"]
+
+  it "chunks 'a\\n string'" $
+    map unloc (layoutChunks "a\n string") `shouldBe` ["a\n string"]
+
+  it "chunks 'a\\n string\\nextra'" $
+    map unloc (layoutChunks "a\n string\nextra") `shouldBe` ["a\n string","extra"]
+
+  it "chunks strings with too many lines" $
+    map unloc (layoutChunks "a\n\nstring") `shouldBe` ["a","string"]
+
+  it "parses multiple exprs" $ do
+    let text = [hereLit|
+                 first
+
+                 second
+                 third
+
+                 fourth
+               |]
+    layoutChunks text `shouldBe`
+      [Located 2 "first",
+       Located 4 "second",
+       Located 5 "third",
+       Located 7 "fourth"]
+
+moduleNameTests = describe "Get Module Name" $ do
+  it "parses simple module names" $
+    "module A where\nx = 3" `named` ["A"]
+  it "parses module names with dots" $
+    "module A.B where\nx = 3" `named` ["A", "B"]
+  it "parses module names with exports" $
+    "module A.B.C ( x ) where x = 3" `named` ["A", "B", "C"]
+  it "errors when given unnamed modules" $ do
+    doGhc (getModuleName "x = 3") `shouldThrow` anyException
+  where
+    named str result = do
+      res <- doGhc $ getModuleName str
+      res `shouldBe` result
+
+parseStringTests = describe "Parser" $ do
+  it "parses empty strings" $
+    parses "" `like` []
+
+  it "parses simple imports" $
+    "import Data.Monoid" `is` Import
+
+  it "parses simple arithmetic" $
+    "3 + 5" `is` Expression
+
+  it "parses :type" $
+    parses ":type x\n:ty x" `like` [
+      Directive GetType "x",
+      Directive GetType "x"
+    ]
+
+  it "parses :info" $
+    parses ":info x\n:in x" `like` [
+      Directive GetInfo "x",
+      Directive GetInfo "x"
+    ]
+
+  it "parses :help and :?" $
+    parses ":? x\n:help x" `like` [
+      Directive GetHelp "x",
+      Directive GetHelp "x"
+    ]
+
+  it "parses :set x" $
+    parses ":set x" `like` [
+      Directive SetDynFlag "x"
+    ]
+
+  it "parses :extension x" $
+    parses ":ex x\n:extension x" `like` [
+      Directive SetExtension "x",
+      Directive SetExtension "x"
+    ]
+
+  it "fails to parse :nope" $
+    parses ":nope goodbye" `like` [
+      ParseError (Loc 1 1) "Unknown directive: 'nope'."
+    ]
+
+  it "parses number followed by let stmt" $
+    parses "3\nlet x = expr"  `like` [
+      Expression "3",
+      Statement "let x = expr"
+    ]
+
+  it "parses let x in y" $
+    "let x = 3 in x + 3" `is` Expression
+
+  it "parses a data declaration" $
+    "data X = Y Int" `is` Declaration
+
+  it "parses number followed by type directive" $
+    parses "3\n:t expr" `like` [
+      Expression "3",
+      Directive GetType "expr"
+    ]
+
+  it "parses a <- statement" $
+    "y <- print 'no'" `is` Statement
+
+  it "parses a <- stmt followed by let stmt" $
+    parses "y <- do print 'no'\nlet x = expr" `like` [
+      Statement "y <- do print 'no'",
+      Statement "let x = expr"
+    ]
+
+  it "parses <- followed by let followed by expr" $
+    parses "y <- do print 'no'\nlet x = expr\nexpression" `like` [
+      Statement "y <- do print 'no'",
+      Statement "let x = expr",
+      Expression "expression"
+    ]
+
+  it "parses two print statements" $
+    parses "print yes\nprint no" `like` [
+      Expression "print yes",
+      Expression "print no"
+    ]
+
+  it "parses a pattern-maching function declaration" $
+    "fun [] = 10" `is` Declaration
+
+  it "parses a function decl followed by an expression" $
+    parses "fun [] = 10\nprint 'h'" `like` [
+      Declaration "fun [] = 10",
+      Expression "print 'h'"
+    ]
+
+  it "parses list pattern matching fun decl" $
+    "fun (x : xs) = 100" `is` Declaration
+
+  it "parses two pattern matches as the same declaration" $
+    "fun [] = 10\nfun (x : xs) = 100" `is` Declaration
+
+  it "parses a type signature followed by a declaration" $
+    "fun :: [a] -> Int\nfun [] = 10\nfun (x : xs) = 100" `is` Declaration
+
+  it "parases a simple module" $
+    "module A where x = 3" `is` Module
+
+  it "parses a module with an export" $
+    "module B (x) where x = 3" `is` Module
+
+  it "breaks when a let is incomplete" $
+    parses "let x = 3 in" `like` [
+      ParseError (Loc 1 13) "parse error (possibly incorrect indentation or mismatched brackets)"
+    ]
+
+  it "breaks without data kinds" $
+    parses "data X = 3" `like` [
+#if MIN_VERSION_ghc(7, 8, 0)
+      ParseError (Loc 1 10) "Illegal literal in type (use DataKinds to enable): 3"
+#else
+      ParseError (Loc 1 10) "Illegal literal in type (use -XDataKinds to enable): 3"
+#endif
+    ]
+
+  it "parses statements after imports" $ do
+    parses "import X\nprint 3" `like` [
+        Import "import X",
+        Expression "print 3"
+      ]
+    parses "import X\n\nprint 3" `like` [
+        Import "import X",
+        Expression "print 3"
+      ]
+  it "ignores blank lines properly" $
+    [hereLit|
+      test arg = hello
+        where
+          x = y
+
+          z = w
+    |] `is` Declaration
+  it "doesn't break on long strings" $ do
+    let longString = concat $ replicate 20 "hello "
+    ("img ! src \"" ++ longString ++ "\" ! width \"500\"") `is` Expression
+
+  it "parses do blocks in expression" $ do
+    [hereLit|
+      show (show (do
+        Just 10
+        Nothing
+        Just 100))
+    |] `is` Expression
+  it "correctly locates parsed items" $ do
+    let go = doGhc . parseString
+    go [hereLit|
+        first
+
+        second
+       |] >>= (`shouldBe` [Located 2 (Expression "first"),
+                          Located 4 (Expression "second")])
+
+
+parseShellTests = 
+  describe "Parsing Shell Commands" $ do
+    test "A" ["A"]
+    test ":load A" [":load", "A"]
+    test ":!l ~/Downloads/MyFile\\ Has\\ Spaces.txt" 
+                  [":!l", "~/Downloads/MyFile\\ Has\\ Spaces.txt"]
+    test ":!l \"~/Downloads/MyFile Has Spaces.txt\" /Another/File\\ WithSpaces.doc" 
+                  [":!l", "~/Downloads/MyFile Has Spaces.txt", "/Another/File\\ WithSpaces.doc" ]
+  where
+    test string expected = 
+      it ("parses " ++ string ++ " correctly") $
+            string `shouldParseTo` expected
+
+    shouldParseTo xs ys = fun ys (parseShell xs)
+      where fun ys (Right xs')  = xs' `shouldBe` ys
+            fun ys (Left e)     = assertFailure $ "parseShell returned error: \n" ++ show e
+
+
+-- Useful HSpec expectations ----
+---------------------------------
+
+shouldBeAmong :: (Show a, Eq a) => [a] -> [a] -> Expectation
+-- |
+-- @sublist \`shouldbeAmong\` list@ sets the expectation that @sublist@ elements are 
+-- among those in @list@.
+sublist `shouldBeAmong` list = assertBool errorMsg 
+              $ and [x `elem` list | x <- sublist]
+  where
+    errorMsg = show list ++ " doesn't contain " ++ show sublist
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,25 +1,2 @@
 import Distribution.Simple
-
-import Control.Applicative ((<$>))
-import Data.List (isInfixOf)
-import Codec.Archive.Tar (create)
-import System.Directory (getDirectoryContents)
-
--- This is currently *not used*. build-type is Simple.
--- This is because it breaks installing from Hackage.
-main = defaultMainWithHooks simpleUserHooks {
-    preBuild = makeProfileTar
-  }
-
-makeProfileTar args flags = do
-  putStrLn "Building profile.tar."
-
-  let profileDir = "profile"
-      tarFile = profileDir ++ "/profile.tar"
-  files <- filter realFile <$> filter notProfileTar <$> getDirectoryContents profileDir
-  print files
-  create tarFile profileDir files
-  preBuild simpleUserHooks args flags
-  where
-    notProfileTar str = not $ "profile.tar" `isInfixOf` str
-    realFile str = str /= "." && str /= ".."
+main = defaultMain
diff --git a/html/custom.css b/html/custom.css
new file mode 100644
--- /dev/null
+++ b/html/custom.css
@@ -0,0 +1,96 @@
+/*
+Custom IHaskell CSS.
+*/
+
+/* Styles used for the Hoogle display in the pager */
+.hoogle-doc {
+    display: block;
+    padding-bottom: 1.3em;
+    padding-left: 0.4em;
+}
+.hoogle-code {
+    display: block;
+    font-family: monospace;
+    white-space: pre;
+}
+.hoogle-text {
+    display: block;
+}
+.hoogle-name {
+    color: green;
+    font-weight: bold;
+}
+.hoogle-head {
+    font-weight: bold;
+}
+.hoogle-sub {
+    display: block;
+    margin-left: 0.4em;
+}
+.hoogle-package {
+    font-weight: bold;
+    font-style: italic;
+}
+.hoogle-module {
+    font-weight: bold;
+}
+.hoogle-class {
+    font-weight: bold;
+}
+
+/* Styles used for basic displays */
+.get-type {
+    color: green;
+    font-weight: bold;
+    font-family: monospace;
+    display: block;
+    white-space: pre-wrap;
+}
+
+.show-type {
+    color: green;
+    font-weight: bold;
+    font-family: monospace;
+    margin-left: 1em;
+}
+
+.mono {
+    font-family: monospace;
+    display: block;
+}
+
+.err-msg {
+    color: red;
+    font-style: italic;
+    font-family: monospace;
+    white-space: pre;
+    display: block;
+}
+
+#unshowable {
+    color: red;
+    font-weight: bold;
+}
+
+.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;
+    color: rgb(200, 130, 0);
+}
+.suggestion-error { 
+    font-weight: bold;
+    color: red;
+}
+.suggestion-name {
+    font-weight: bold;
+}
diff --git a/html/kernel.js b/html/kernel.js
new file mode 100644
--- /dev/null
+++ b/html/kernel.js
@@ -0,0 +1,87 @@
+
+define(['require',
+        'codemirror/lib/codemirror',
+        'codemirror/addon/mode/loadmode',
+        'base/js/namespace',
+        'base/js/events',
+        'base/js/utils'],
+        function(require, CodeMirror, CodemirrorLoadmode, IPython, events, utils){
+
+            var onload = function(){
+                console.log('Kernel haskell kernel.js is loading.');
+
+                // add here logic that shoudl be run once per **page load**
+                // like adding specific UI, or changing the default value
+                // of codecell highlight.
+
+                // Set tooltips to be triggered after 800ms
+                IPython.tooltip.time_before_tooltip = 800;
+
+                // IPython keycodes.
+                var space = 32;
+                var downArrow = 40;
+                IPython.keyboard.keycodes.down = downArrow; // space
+
+                IPython.CodeCell.options_default['cm_config']['mode'] = 'haskell';
+
+                utils.requireCodeMirrorMode('haskell', function(){
+                    // Create a multiplexing mode that uses Haskell highlighting by default but
+                    // doesn't highlight command-line directives.
+                    CodeMirror.defineMode("ihaskell", function(config) {
+                        return CodeMirror.multiplexingMode(
+                            CodeMirror.getMode(config, "haskell"),
+                            {
+                                open: /:(?=!)/, // Matches : followed by !, but doesn't consume !
+                               close: /^(?!!)/, // Matches start of line not followed by !, doesn't consume character
+                               mode: CodeMirror.getMode(config, "text/plain"),
+                               delimStyle: "delimit"
+                            }
+                            );
+                    });
+
+                    cells = IPython.notebook.get_cells();
+                    for(var i in cells){
+                        c = cells[i];
+                        if (c.cell_type === 'code') {
+                            // Force the mode to be Haskell
+                            // This is necessary, otherwise sometimes highlighting just doesn't happen.
+                            // This may be an IPython bug.
+                            c.code_mirror.setOption('mode', 'ihaskell');
+                            c.auto_highlight();
+                        }
+                    }
+                });
+
+                // Prevent the pager from surrounding everything with a <pre>
+                IPython.Pager.prototype.append_text = function (text) {
+                    this.pager_element.find(".container").append($('<div/>').html(IPython.utils.autoLinkUrls(text)));
+                };
+
+                events.on('shell_reply.Kernel', function() {
+                    // Add logic here that should be run once per reply.
+
+                    // Highlight things with a .highlight-code class
+                    // The id is the mode with with to highlight
+                    $('.highlight-code').each(function() {
+                        var $this = $(this),
+                        $code = $this.html(),
+                        $unescaped = $('<div/>').html($code).text();
+
+                    $this.empty();
+
+                    // Never highlight this block again.
+                    this.className = "";
+
+                    CodeMirror(this, {
+                        value: $unescaped,
+                        mode: this.id,
+                        lineNumbers: false,
+                        readOnly: true
+                    });
+                    });
+                });
+                console.log('IHaskell kernel.js should have been loaded.')
+            } // end def of onload
+            return {onload:onload};
+        }
+);
diff --git a/html/logo-64x64.png b/html/logo-64x64.png
new file mode 100644
Binary files /dev/null and b/html/logo-64x64.png differ
diff --git a/ihaskell.cabal b/ihaskell.cabal
--- a/ihaskell.cabal
+++ b/ihaskell.cabal
@@ -7,7 +7,7 @@
 -- PVP summary:      +-+------- breaking API changes
 --                   | | +----- non-breaking API additions
 --                   | | | +--- code changes with no API change
-version:             0.5.0.0
+version:             0.6.0.0
 
 -- A short (one-line) description of the package.
 synopsis:            A Haskell backend kernel for the IPython project.
@@ -43,28 +43,32 @@
 cabal-version:       >=1.16
 
 data-files: 
-    installation/ipython.sh
-    installation/virtualenv.sh
-    installation/run.sh
-    profile/profile.tar
+    html/kernel.js
+    html/custom.css
+    html/logo-64x64.png
 
+flag binPkgDb
+  default:             True
+  description:         bin-package-db package needed (needed for GHC >= 7.10)
+
 library
   hs-source-dirs:      src
   default-language:    Haskell2010
   build-depends:       
                        aeson                >=0.6 && < 0.9,
-                       base                 >=4.6 && < 4.8,
+                       base                 >=4.6 && < 4.9,
                        base64-bytestring    >=1.0,
                        bytestring           >=0.10,
                        cereal               >=0.3,
                        classy-prelude       >=0.9.5 && <0.11,
-                       mono-traversable     >=0.6 && < 0.7,
+                       mono-traversable     >=0.6,
                        cmdargs              >=0.10,
                        containers           >=0.5,
                        directory            -any,
                        filepath             -any,
-                       ghc                  ==7.6.* || == 7.8.*,
-                       ghc-parser           >=0.1.4,
+                       ghc                  >=7.6 || < 7.11,
+                       ghc-parser           >=0.1.6,
+                       ghc-paths            ==0.1.*,
                        haskeline            -any,
                        here                 ==1.2.*,
                        hlint                >=1.9 && <2.0,
@@ -78,14 +82,13 @@
                        parsec               -any,
                        process              >=1.1,
                        random               >=1.0,
-                       shelly               ==1.5.*,
+                       shelly               >=1.5,
                        split                >= 0.2,
                        stm                  -any,
                        strict               >=0.3,
                        system-argv0         -any,
                        system-filepath      -any,
                        tar                  -any,
-                       template-haskell     -any, 
                        text                 >=0.11,
                        transformers         -any,
                        unix                 >= 2.6,
@@ -93,7 +96,9 @@
                        utf8-string          -any,
                        uuid                 >=1.3,
                        vector               -any,
-                       ipython-kernel       >=0.3
+                       ipython-kernel       >=0.6
+  if flag(binPkgDb)
+    build-depends:       bin-package-db
 
   exposed-modules: IHaskell.Display
                    IHaskell.Convert
@@ -116,88 +121,91 @@
 --  other-modules: 
 --                   Paths_ihaskell
 
-executable IHaskell
+executable ihaskell
   -- .hs or .lhs file containing the Main module.
   main-is:             src/Main.hs
   ghc-options: -threaded
-
-  default-extensions: DoAndIfThenElse
   
   -- Other library packages from which modules are imported.
   default-language:    Haskell2010
   build-depends:       
-                       base                 >=4.6 && < 4.8,
-                       ghc-paths            ==0.1.*,
+                       base                 >=4.6 && < 4.9,
                        aeson                >=0.6 && < 0.9,
                        bytestring           >=0.10,
                        cereal               >=0.3,
                        classy-prelude       >=0.9.2 && <0.11,
-                       mono-traversable     >=0.6 && < 0.7,
+                       mono-traversable     >=0.6,
                        containers           >=0.5,
                        directory            -any,
-                       ghc                  ==7.6.* || == 7.8.*,
+                       ghc                  >=7.6 && < 7.11,
                        ihaskell             -any,
                        MissingH             >=1.2,
+                       here                 ==1.2.*,
                        text                 -any,
-                       ipython-kernel       >= 0.2,
+                       ipython-kernel       >= 0.6,
                        unix                 >= 2.6
+  if flag(binPkgDb)
+    build-depends:       bin-package-db
 
 Test-Suite hspec
-  hs-source-dirs:      src
-  Type:     exitcode-stdio-1.0
-  Ghc-Options: -threaded
-  Main-Is: Hspec.hs
-  default-language:    Haskell2010
-  build-depends:       
-                       aeson                >=0.6 && < 0.9,
-                       base                 >=4.6 && < 4.8,
-                       base64-bytestring    >=1.0,
-                       bytestring           >=0.10,
-                       cereal               >=0.3,
-                       classy-prelude       >=0.9.2 && <0.11,
-                       mono-traversable     >=0.6 && < 0.7,
-                       cmdargs              >=0.10,
-                       containers           >=0.5,
-                       directory            -any,
-                       filepath             -any,
-                       ghc                  ==7.6.* || == 7.8.*,
-                       ghc-parser           >=0.1.1,
-                       ghc-paths            ==0.1.*,
-                       haskeline            -any,
-                       here                 ==1.2.*,
-                       hlint                >=1.9 && <2.0,
-                       haskell-src-exts     ==1.16.*,
-                       hspec                -any,
-                       HUnit                -any,
-                       MissingH             >=1.2,
-                       mtl                  >=2.1,
-                       parsec               -any,
-                       process              >=1.1,
-                       random               >=1.0,
-                       shelly               ==1.5.*,
-                       split                >= 0.2,
-                       stm                  -any,
-                       strict               >=0.3,
-                       system-argv0         -any,
-                       system-filepath      -any,
-                       tar                  -any,
-                       text                 >=0.11,
-                       http-client          == 0.4.*,
-                       http-client-tls      == 0.2.*,
-                       transformers         -any,
-                       unix                 >= 2.6,
-                       unordered-containers -any,
-                       utf8-string          -any,
-                       uuid                 >=1.3,
-                       vector               -any,
-                       setenv               ==0.1.*,
-                       ipython-kernel       >= 0.2
+    Type: exitcode-stdio-1.0
+    Ghc-Options: -threaded
+    Main-Is: Hspec.hs
+    default-language: Haskell2010
+    build-depends:
+        ihaskell,
+        aeson >=0.6 && < 0.9,
+        base >=4.6 && < 4.9,
+        base64-bytestring >=1.0,
+        bytestring >=0.10,
+        cereal >=0.3,
+        classy-prelude >=0.9.2 && <0.11,
+        mono-traversable >=0.6,
+        cmdargs >=0.10,
+        containers >=0.5,
+        directory -any,
+        filepath -any,
+        ghc >=7.6 && < 7.11,
+        ghc-parser >=0.1.6,
+        ghc-paths ==0.1.*,
+        haskeline -any,
+        here ==1.2.*,
+        hlint >=1.9 && <2.0,
+        haskell-src-exts ==1.16.*,
+        hspec -any,
+        HUnit -any,
+        MissingH >=1.2,
+        mtl >=2.1,
+        parsec -any,
+        process >=1.1,
+        random >=1.0,
+        shelly >=1.5,
+        split >= 0.2,
+        stm -any,
+        strict >=0.3,
+        system-argv0 -any,
+        system-filepath -any,
+        tar -any,
+        text >=0.11,
+        http-client == 0.4.*,
+        http-client-tls == 0.2.*,
+        transformers -any,
+        unix >= 2.6,
+        unordered-containers -any,
+        utf8-string -any,
+        uuid >=1.3,
+        vector -any,
+        setenv ==0.1.*,
+        ipython-kernel >= 0.6
 
+    if flag(binPkgDb)
+        build-depends: bin-package-db
 
-  default-extensions:
-              DoAndIfThenElse
-              OverloadedStrings
-              ExtendedDefaultRules
+    default-extensions:
+        DoAndIfThenElse
+        OverloadedStrings
+        ExtendedDefaultRules
+
 
 source-repository head
   type:     git
diff --git a/installation/ipython.sh b/installation/ipython.sh
deleted file mode 100644
--- a/installation/ipython.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/bash
-set -e
-
-# Which virtualenv to use.
-VIRTUALENV=$1
-
-# Activate the virtualenv.
-source $VIRTUALENV/bin/activate
-
-# Upgrade pip.
-echo "Upgrading pip."
-pip install --upgrade "pip>=1.4.1"
-
-# Install all necessary dependencies with Pip.
-echo "Installing dependency (pyzmq)."
-pip install pyzmq==14.0.1
-
-echo "Installing dependency (markupsafe)."
-pip install markupsafe==0.18
-
-echo "Installing dependency (jinja2)."
-pip install jinja2==2.7.1
-
-echo "Installing dependency (tornado)."
-pip install tornado==3.1.1
-
-echo "Installing dependency (pygments)."
-pip install pygments==1.6
-
-# Install IPython itself.
-echo "Installing IPython (this may take a while)."
-pip install ipython
diff --git a/installation/run.sh b/installation/run.sh
deleted file mode 100644
--- a/installation/run.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-set -e
-
-# Which virtualenv to use.
-VIRTUALENV=$1
-shift
-
-# Activate the virtualenv, if it exists.
-if [[ -f $VIRTUALENV/bin/activate ]]; then
-  source $VIRTUALENV/bin/activate;
-fi
-
-# Run IPython.
-# Quotes around $@ are necessary to deal properly with spaces.
-# Only add IHASKELL_IPYTHON_ARGS to notebook.
-if [[ $1 == "notebook" ]]; then
-    ipython "$@" $IHASKELL_IPYTHON_ARGS
-else
-    ipython "$@"
-fi
diff --git a/installation/virtualenv.sh b/installation/virtualenv.sh
deleted file mode 100644
--- a/installation/virtualenv.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-set -e
-
-# Which version of virtualenv to use.
-VIRTUALENV=virtualenv-1.9.1
-
-# Where to install the virtualenv.
-DESTINATION=$1
-
-# Download virtualenv.
-echo "Downloading virtualenv."
-curl -O https://pypi.python.org/packages/source/v/virtualenv/$VIRTUALENV.tar.gz
-tar xvfz $VIRTUALENV.tar.gz
-cd $VIRTUALENV
-
-# Create a virtualenv.
-echo "Creating a virtualenv."
-python virtualenv.py $DESTINATION
diff --git a/profile/profile.tar b/profile/profile.tar
deleted file mode 100644
Binary files a/profile/profile.tar and /dev/null differ
diff --git a/src/Hspec.hs b/src/Hspec.hs
deleted file mode 100644
--- a/src/Hspec.hs
+++ /dev/null
@@ -1,576 +0,0 @@
-{-# LANGUAGE QuasiQuotes, OverloadedStrings, ExtendedDefaultRules, CPP #-}
--- Keep all the language pragmas here so it can be compiled separately.
-module Main where
-import Prelude
-import GHC
-import GHC.Paths
-import Data.IORef
-import Control.Monad
-import Control.Monad.Trans ( MonadIO, liftIO )
-import Data.List
-import System.Directory
-import Shelly (Sh, shelly, cmd, (</>), toTextIgnore, cd, withTmpDir, mkdir_p,
-  touchfile)
-import qualified Shelly
-import Control.Applicative ((<$>))
-import Filesystem.Path.CurrentOS (encodeString)
-import System.SetEnv (setEnv)
-import Data.String.Here
-import Data.String.Utils (strip, replace)
-import Data.Monoid
-
-import IHaskell.Eval.Parser
-import IHaskell.Types
-import IHaskell.IPython
-import IHaskell.Eval.Evaluate as Eval hiding (liftIO)
-import qualified IHaskell.Eval.Evaluate as Eval (liftIO)
-
-import IHaskell.Eval.Completion
-import IHaskell.Eval.ParseShell
-
-import Debug.Trace
-
-import Test.Hspec
-import Test.Hspec.HUnit
-import Test.HUnit (assertBool, assertFailure)
-
-traceShowId x = traceShow x x 
-
-doGhc = runGhc (Just libdir)
-
-parses str = do
-  res <- doGhc $ parseString str
-  return $ map unloc res
-
-like parser desired = parser >>= (`shouldBe` desired)
-
-is string blockType = do
-  result <- doGhc $ parseString string
-  map unloc result `shouldBe` [blockType $ strip string]
-
-eval string = do
-  outputAccum <- newIORef []
-  pagerAccum <- newIORef []
-  let publish evalResult = case evalResult of
-        IntermediateResult {} -> return ()
-        FinalResult outs page [] -> do
-          modifyIORef outputAccum (outs :)
-          modifyIORef pagerAccum (page :)
-
-  getTemporaryDirectory >>= setCurrentDirectory
-  let state = defaultKernelState { getLintStatus = LintOff }
-  interpret libdir False $ Eval.evaluate state string publish
-  out <- readIORef outputAccum
-  pagerOut <- readIORef pagerAccum
-  return (reverse out, unlines $ reverse pagerOut)
-
-evaluationComparing comparison string = do
-    let indent (' ':x) = 1 + indent x
-        indent _ = 0
-        empty = null . strip
-        stringLines = filter (not . empty) $ lines string
-        minIndent = minimum (map indent stringLines)
-        newString = unlines $ map (drop minIndent) stringLines
-    eval newString >>= comparison
-
-becomes string expected = evaluationComparing comparison string
-  where
-    comparison :: ([Display], String) -> IO ()
-    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
-
-pages string expected = evaluationComparing comparison string
-  where
-    comparison (results, pageOut) =
-      strip pageOut `shouldBe` strip (unlines expected)
-
-readCompletePrompt :: String -> (String, Int)
--- | @readCompletePrompt "xs*ys"@ return @(xs, i)@ where i is the location of
--- @'*'@ in the input string. 
-readCompletePrompt string = case elemIndex '*' string of
-          Nothing -> error "Expected cursor written as '*'."
-          Just idx -> (replace "*" "" string, idx)
-
-completes string expected = completionTarget newString cursorloc `shouldBe` expected
-  where (newString, cursorloc) = readCompletePrompt string
-
-completionEvent :: String -> Interpreter (String, [String])
-completionEvent string = complete newString cursorloc
-  where (newString, cursorloc) = case elemIndex '*' string of
-          Nothing -> error "Expected cursor written as '*'."
-          Just idx -> (replace "*" "" string, idx)      
-
-completionEventInDirectory :: String -> IO (String, [String])
-completionEventInDirectory string  
-  = withHsDirectory $ const $ completionEvent string 
-                          
-
-shouldHaveCompletionsInDirectory :: String -> [String] -> IO ()
-shouldHaveCompletionsInDirectory string expected = do 
-  (matched, completions) <- completionEventInDirectory string  
-  let existsInCompletion = (`elem` completions)
-      unmatched = filter (not . existsInCompletion) expected
-  expected `shouldBeAmong` completions
-
-completionHas string expected 
-  = do (matched, completions) <- doGhc $ 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 }
-
-  -- Import modules.
-  imports <- mapM parseImportDecl ["import Prelude",
-                                   "import qualified Control.Monad",
-                                   "import qualified Data.List as List",
-                                   "import IHaskell.Display",
-                                   "import Data.Maybe as Maybe"]
-  setContext $ map IIDecl imports
-
-inDirectory :: [Shelly.FilePath] -- ^ directories relative to temporary directory
-              -> [Shelly.FilePath] -- ^ files relative to temporary directory
-              -> (Shelly.FilePath -> Interpreter a)
-              -> IO a
--- | 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
-         mapM_ mkdir_p   dirs
-         mapM_ touchfile files
-         liftIO $ doGhc $ wrap (encodeString dirPath) (action dirPath)
-      where cdEvent path = liftIO $ setCurrentDirectory path --Eval.evaluate defaultKernelState (":! cd " ++ path) noPublish
-            wrap :: FilePath -> Interpreter a -> Interpreter a
-            wrap path action = 
-              do initCompleter
-                 pwd <- Eval.liftIO getCurrentDirectory
-                 cdEvent path   -- change to the temporary directory
-                 out <- action  -- run action
-                 cdEvent pwd    -- change back to the original directory
-                 return out
-
-withHsDirectory :: (Shelly.FilePath -> Interpreter a)  -> IO a
-withHsDirectory = inDirectory ["" </> "dir", "dir" </> "dir1"]
-                    [""</> "file1.hs", "dir" </> "file2.hs",  
-                     "" </> "file1.lhs", "dir" </> "file2.lhs"]
-
-main :: IO ()
-main = hspec $ do
-  parserTests
-  evalTests
-  completionTests
-
-completionTests = do
-  parseShellTests
-  describe "Completion" $ do
-    it "correctly gets the completion identifier without dots" $ do
-       "hello*"              `completes` ["hello"]
-       "hello aa*bb goodbye" `completes` ["aa"]
-       "hello aabb* goodbye" `completes` ["aabb"]
-       "aacc* goodbye"       `completes` ["aacc"]
-       "hello *aabb goodbye" `completes` []
-       "*aabb goodbye"       `completes` []
-
-    it "correctly gets the completion identifier with dots" $ do
-       "hello test.aa*bb goodbye" `completes` ["test", "aa"]
-       "Test.*"                   `completes` ["Test", ""]
-       "Test.Thing*"              `completes` ["Test", "Thing"]
-       "Test.Thing.*"             `completes` ["Test", "Thing", ""]
-       "Test.Thing.*nope"         `completes` ["Test", "Thing", ""]
-
-    it "correctly gets the completion type" $ do
-      completionType "import Data." 12 ["Data", ""]      `shouldBe` ModuleName "Data" ""
-      completionType "import Prel" 11 ["Prel"]           `shouldBe` ModuleName "" "Prel"
-      completionType "import D.B.M" 12 ["D", "B", "M"]   `shouldBe` ModuleName "D.B" "M"
-      completionType " import A." 10 ["A", ""]           `shouldBe` ModuleName "A" ""
-      completionType "import a.x" 10 ["a", "x"]          `shouldBe` Identifier "x"
-      completionType "A.x" 3 ["A", "x"]                 `shouldBe` Qualified "A" "x"
-      completionType "a.x" 3 ["a", "x"]                 `shouldBe` Identifier "x"
-      completionType "pri" 3 ["pri"]                    `shouldBe` Identifier "pri"
-      completionType ":load A" 7 ["A"]                   `shouldBe` HsFilePath ":load A"
-                                                                               "A"
-      completionType ":! cd " 6 [""]                     `shouldBe` FilePath   ":! cd " ""
-
-
-
-    it "properly completes identifiers" $ do
-       "pri*"           `completionHas` ["print"]
-       "ma*"            `completionHas` ["map"]
-       "hello ma*"      `completionHas` ["map"]
-       "print $ catMa*" `completionHas` ["catMaybes"]
-
-    it "properly completes qualified identifiers" $ do
-       "Control.Monad.liftM*"    `completionHas` [ "Control.Monad.liftM"
-                                                 , "Control.Monad.liftM2"
-                                                 , "Control.Monad.liftM5"]
-       "print $ List.intercal*"  `completionHas` ["List.intercalate"]
-       "print $ Data.Maybe.cat*" `completionHas` ["Data.Maybe.catMaybes"]
-       "print $ Maybe.catM*"     `completionHas` ["Maybe.catMaybes"]
-
-    it "properly completes imports" $ do
-      "import Data.*"  `completionHas` ["Data.Maybe", "Data.List"]
-      "import Data.M*" `completionHas` ["Data.Maybe"]
-      "import Prel*"   `completionHas` ["Prelude"]
-
-    it "properly completes haskell file paths on :load directive" $
-         let loading xs = ":load " ++ encodeString xs
-             paths = map encodeString
-         in do 
-            loading ("dir" </> "file*") `shouldHaveCompletionsInDirectory` paths ["dir" </> "file2.hs",
-                                                                                  "dir" </> "file2.lhs"]
-            loading ("" </> "file1*") `shouldHaveCompletionsInDirectory` paths ["" </> "file1.hs",
-                                                                                            "" </> "file1.lhs"]
-            loading ("" </> "file1*") `shouldHaveCompletionsInDirectory` paths ["" </> "file1.hs",
-                                                                                "" </> "file1.lhs"]
-            loading ("" </> "./*") `shouldHaveCompletionsInDirectory` paths ["./" </> "dir/"
-                                                                                       , "./" </> "file1.hs"
-                                                                                       , "./" </> "file1.lhs"]
-            loading ("" </> "./*") `shouldHaveCompletionsInDirectory` paths ["./" </> "dir/"
-                                                                           , "./" </> "file1.hs"
-                                                                           , "./" </> "file1.lhs"]
-
-    it "provides path completions on empty shell cmds " $
-      ":! cd *" `shouldHaveCompletionsInDirectory` map encodeString ["" </> "dir/"
-                                                                    , "" </> "file1.hs"
-                                                                    , "" </> "file1.lhs"]
-
-    let withHsHome action = withHsDirectory $ \dirPath->  do
-          home <- shelly $ Shelly.get_env_text "HOME"
-          setHomeEvent dirPath
-          result <- action
-          setHomeEvent $ Shelly.fromText home
-          return result
-        setHomeEvent path = liftIO $ setEnv "HOME" (encodeString path)
-
-    it "correctly interprets ~ as the environment HOME variable" $ 
-        let shouldHaveCompletions :: String -> [String] -> IO ()
-            shouldHaveCompletions string expected = do 
-              (matched, completions) <- withHsHome $ completionEvent string 
-              let existsInCompletion = (`elem` completions)
-                  unmatched = filter (not . existsInCompletion) expected     
-              expected `shouldBeAmong` completions
-
-        in do 
-          ":! cd ~/*" `shouldHaveCompletions` ["~/dir/"]
-          ":! ~/*" `shouldHaveCompletions` ["~/dir/"]
-          ":load ~/*" `shouldHaveCompletions` ["~/dir/"]
-          ":l ~/*" `shouldHaveCompletions` ["~/dir/"]
-
-    let shouldHaveMatchingText :: String -> String -> IO ()
-        shouldHaveMatchingText string expected = do 
-          matchText <- withHsHome $ fst <$> uncurry complete (readCompletePrompt string)
-          matchText `shouldBe` expected
-
-        setHomeEvent path = liftIO $ setEnv "HOME" (encodeString path)
-
-    it "generates the correct matchingText on `:! cd ~/*` " $ 
-      do ":! cd ~/*" `shouldHaveMatchingText` ("~/" :: String)
-
-    it "generates the correct matchingText on `:load ~/*` " $ 
-      do ":load ~/*" `shouldHaveMatchingText` ("~/" :: String)
-
-    it "generates the correct matchingText on `:l ~/*` " $ 
-      do ":l ~/*" `shouldHaveMatchingText` ("~/" :: String)
-
-evalTests = do
-  describe "Code Evaluation" $ do
-    it "evaluates expressions" $  do
-      "3" `becomes` ["3"]
-      "3+5" `becomes` ["8"]
-      "print 3" `becomes` ["3"]
-      [hereLit|
-        let x = 11
-            z = 10 in
-          x+z
-      |] `becomes` ["21"]
-
-    it "evaluates multiline expressions" $  do
-      [hereLit|
-        import Control.Monad
-        forM_ [1, 2, 3] $ \x ->
-          print x
-      |] `becomes` ["1\n2\n3"]
-
-    it "evaluates function declarations silently" $ do
-      [hereLit|
-        fun :: [Int] -> Int
-        fun [] = 3
-        fun (x:xs) = 10
-        fun [1, 2]
-      |] `becomes` ["10"]
-
-    it "evaluates data declarations" $ do
-      [hereLit|
-        data X = Y Int
-               | Z String
-               deriving (Show, Eq)
-        print [Y 3, Z "No"]
-        print (Y 3 == Z "No")
-      |] `becomes` ["[Y 3,Z \"No\"]", "False"]
-
-    it "evaluates do blocks in expressions" $ do
-      [hereLit|
-        show (show (do
-            Just 10
-            Nothing
-            Just 100))
-      |] `becomes` ["\"\\\"Nothing\\\"\""]
-
-    it "is silent for imports" $ do
-      "import Control.Monad" `becomes` []
-      "import qualified Control.Monad" `becomes` []
-      "import qualified Control.Monad as CM" `becomes` []
-      "import Control.Monad (when)" `becomes` []
-
-    it "evaluates directives" $ do
-      ":typ 3" `becomes` ["3 :: forall a. Num a => a"]
-      ":k Maybe" `becomes` ["Maybe :: * -> *"]
-      ":in String" `pages` ["type String = [Char] \t-- Defined in `GHC.Base'"]
-
-parserTests = do
-  layoutChunkerTests
-  moduleNameTests
-  parseStringTests
-
-layoutChunkerTests = describe "Layout Chunk" $ do
-  it "chunks 'a string'" $
-    map unloc (layoutChunks "a string") `shouldBe` ["a string"]
-
-  it "chunks 'a\\n string'" $
-    map unloc (layoutChunks "a\n string") `shouldBe` ["a\n string"]
-
-  it "chunks 'a\\n string\\nextra'" $
-    map unloc (layoutChunks "a\n string\nextra") `shouldBe` ["a\n string","extra"]
-
-  it "chunks strings with too many lines" $
-    map unloc (layoutChunks "a\n\nstring") `shouldBe` ["a","string"]
-
-  it "parses multiple exprs" $ do
-    let text = [hereLit|
-                 first
-
-                 second
-                 third
-
-                 fourth
-               |]
-    layoutChunks text `shouldBe`
-      [Located 2 "first",
-       Located 4 "second",
-       Located 5 "third",
-       Located 7 "fourth"]
-
-moduleNameTests = describe "Get Module Name" $ do
-  it "parses simple module names" $
-    "module A where\nx = 3" `named` ["A"]
-  it "parses module names with dots" $
-    "module A.B where\nx = 3" `named` ["A", "B"]
-  it "parses module names with exports" $
-    "module A.B.C ( x ) where x = 3" `named` ["A", "B", "C"]
-  it "errors when given unnamed modules" $ do
-    doGhc (getModuleName "x = 3") `shouldThrow` anyException
-  where
-    named str result = do
-      res <- doGhc $ getModuleName str
-      res `shouldBe` result
-
-parseStringTests = describe "Parser" $ do
-  it "parses empty strings" $
-    parses "" `like` []
-
-  it "parses simple imports" $
-    "import Data.Monoid" `is` Import
-
-  it "parses simple arithmetic" $
-    "3 + 5" `is` Expression
-
-  it "parses :type" $
-    parses ":type x\n:ty x" `like` [
-      Directive GetType "x",
-      Directive GetType "x"
-    ]
-
-  it "parses :info" $
-    parses ":info x\n:in x" `like` [
-      Directive GetInfo "x",
-      Directive GetInfo "x"
-    ]
-
-  it "parses :help and :?" $
-    parses ":? x\n:help x" `like` [
-      Directive GetHelp "x",
-      Directive GetHelp "x"
-    ]
-
-  it "parses :set x" $
-    parses ":set x" `like` [
-      Directive SetDynFlag "x"
-    ]
-
-  it "parses :extension x" $
-    parses ":ex x\n:extension x" `like` [
-      Directive SetExtension "x",
-      Directive SetExtension "x"
-    ]
-
-  it "fails to parse :nope" $
-    parses ":nope goodbye" `like` [
-      ParseError (Loc 1 1) "Unknown directive: 'nope'."
-    ]
-
-  it "parses number followed by let stmt" $
-    parses "3\nlet x = expr"  `like` [
-      Expression "3",
-      Statement "let x = expr"
-    ]
-
-  it "parses let x in y" $
-    "let x = 3 in x + 3" `is` Expression
-
-  it "parses a data declaration" $
-    "data X = Y Int" `is` Declaration
-
-  it "parses number followed by type directive" $
-    parses "3\n:t expr" `like` [
-      Expression "3",
-      Directive GetType "expr"
-    ]
-
-  it "parses a <- statement" $
-    "y <- print 'no'" `is` Statement
-
-  it "parses a <- stmt followed by let stmt" $
-    parses "y <- do print 'no'\nlet x = expr" `like` [
-      Statement "y <- do print 'no'",
-      Statement "let x = expr"
-    ]
-
-  it "parses <- followed by let followed by expr" $
-    parses "y <- do print 'no'\nlet x = expr\nexpression" `like` [
-      Statement "y <- do print 'no'",
-      Statement "let x = expr",
-      Expression "expression"
-    ]
-
-  it "parses two print statements" $
-    parses "print yes\nprint no" `like` [
-      Expression "print yes",
-      Expression "print no"
-    ]
-
-  it "parses a pattern-maching function declaration" $
-    "fun [] = 10" `is` Declaration
-
-  it "parses a function decl followed by an expression" $
-    parses "fun [] = 10\nprint 'h'" `like` [
-      Declaration "fun [] = 10",
-      Expression "print 'h'"
-    ]
-
-  it "parses list pattern matching fun decl" $
-    "fun (x : xs) = 100" `is` Declaration
-
-  it "parses two pattern matches as the same declaration" $
-    "fun [] = 10\nfun (x : xs) = 100" `is` Declaration
-
-  it "parses a type signature followed by a declaration" $
-    "fun :: [a] -> Int\nfun [] = 10\nfun (x : xs) = 100" `is` Declaration
-
-  it "parases a simple module" $
-    "module A where x = 3" `is` Module
-
-  it "parses a module with an export" $
-    "module B (x) where x = 3" `is` Module
-
-  it "breaks when a let is incomplete" $
-    parses "let x = 3 in" `like` [
-      ParseError (Loc 1 13) "parse error (possibly incorrect indentation or mismatched brackets)"
-    ]
-
-  it "breaks without data kinds" $
-    parses "data X = 3" `like` [
-#if MIN_VERSION_ghc(7, 8, 0)
-      ParseError (Loc 1 10) "Illegal literal in type (use DataKinds to enable): 3"
-#else
-      ParseError (Loc 1 10) "Illegal literal in type (use -XDataKinds to enable): 3"
-#endif
-    ]
-
-  it "parses statements after imports" $ do
-    parses "import X\nprint 3" `like` [
-        Import "import X",
-        Expression "print 3"
-      ]
-    parses "import X\n\nprint 3" `like` [
-        Import "import X",
-        Expression "print 3"
-      ]
-  it "ignores blank lines properly" $
-    [hereLit|
-      test arg = hello
-        where
-          x = y
-
-          z = w
-    |] `is` Declaration
-  it "doesn't break on long strings" $ do
-    let longString = concat $ replicate 20 "hello "
-    ("img ! src \"" ++ longString ++ "\" ! width \"500\"") `is` Expression
-
-  it "parses do blocks in expression" $ do
-    [hereLit|
-      show (show (do
-        Just 10
-        Nothing
-        Just 100))
-    |] `is` Expression
-  it "correctly locates parsed items" $ do
-    let go = doGhc . parseString
-    go [hereLit|
-        first
-
-        second
-       |] >>= (`shouldBe` [Located 2 (Expression "first"),
-                          Located 4 (Expression "second")])
-
-
-parseShellTests = 
-  describe "Parsing Shell Commands" $ do
-    test "A" ["A"]
-    test ":load A" [":load", "A"]
-    test ":!l ~/Downloads/MyFile\\ Has\\ Spaces.txt" 
-                  [":!l", "~/Downloads/MyFile\\ Has\\ Spaces.txt"]
-    test ":!l \"~/Downloads/MyFile Has Spaces.txt\" /Another/File\\ WithSpaces.doc" 
-                  [":!l", "~/Downloads/MyFile Has Spaces.txt", "/Another/File\\ WithSpaces.doc" ]
-  where
-    test string expected = 
-      it ("parses " ++ string ++ " correctly") $
-            string `shouldParseTo` expected
-
-    shouldParseTo xs ys = fun ys (parseShell xs)
-      where fun ys (Right xs')  = xs' `shouldBe` ys
-            fun ys (Left e)     = assertFailure $ "parseShell returned error: \n" ++ show e
-
-
--- Useful HSpec expectations ----
----------------------------------
-
-shouldBeAmong :: (Show a, Eq a) => [a] -> [a] -> Expectation
--- |
--- @sublist \`shouldbeAmong\` list@ sets the expectation that @sublist@ elements are 
--- among those in @list@.
-sublist `shouldBeAmong` list = assertBool errorMsg 
-              $ and [x `elem` list | x <- sublist]
-  where
-    errorMsg = show list ++ " doesn't contain " ++ show sublist
diff --git a/src/IHaskell/BrokenPackages.hs b/src/IHaskell/BrokenPackages.hs
--- a/src/IHaskell/BrokenPackages.hs
+++ b/src/IHaskell/BrokenPackages.hs
@@ -1,40 +1,38 @@
-{-# LANGUAGE OverloadedStrings, NoImplicitPrelude #-}
+{-# LANGUAGE OverloadedStrings, NoImplicitPrelude, FlexibleContexts #-}
+
 module IHaskell.BrokenPackages (getBrokenPackages) where
 
-import ClassyPrelude hiding ((<|>))
+import           ClassyPrelude hiding ((<|>))
 
-import Text.Parsec
-import Text.Parsec.String
-import Control.Applicative hiding ((<|>), many)
+import           Text.Parsec
+import           Text.Parsec.String
+import           Control.Applicative hiding ((<|>), many)
 
-import Data.String.Utils (startswith)
+import           Data.String.Utils (startswith)
 
-import Shelly
+import           Shelly
 
-data BrokenPackage = BrokenPackage {
-        packageID :: String,
-        brokenDeps :: [String]
-      }
+data BrokenPackage = BrokenPackage { packageID :: String, brokenDeps :: [String] }
 
 instance Show BrokenPackage where
   show = 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.
+-- | 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"]
   checkOut <- lastStderr
-  
+
   -- Get rid of extraneous things
-  let rightStart str = startswith "There are problems" str || 
-                       startswith "  dependency" str 
+  let rightStart str = startswith "There are problems" str ||
+                       startswith "  dependency" str
       ghcPkgOutput = unlines . filter rightStart . lines $ unpack checkOut
 
-  return $ case parse (many check) "ghc-pkg output" ghcPkgOutput of
-    Left err -> []
-    Right pkgs -> map show pkgs
+  return $
+    case parse (many check) "ghc-pkg output" ghcPkgOutput of
+      Left err   -> []
+      Right pkgs -> map show pkgs
 
 check :: Parser BrokenPackage
 check = string "There are problems in package "
diff --git a/src/IHaskell/Convert.hs b/src/IHaskell/Convert.hs
--- a/src/IHaskell/Convert.hs
+++ b/src/IHaskell/Convert.hs
@@ -1,27 +1,31 @@
 -- | Description : mostly reversible conversion between ipynb and lhs 
 module IHaskell.Convert (convert) where
-import Control.Monad.Identity (Identity(Identity), unless, when)
-import IHaskell.Convert.Args (ConvertSpec(ConvertSpec, convertInput, convertLhsStyle, convertOutput, convertOverwriteFiles, convertToIpynb), fromJustConvertSpec, toConvertSpec)
-import IHaskell.Convert.IpynbToLhs (ipynbToLhs)
-import IHaskell.Convert.LhsToIpynb (lhsToIpynb)
-import IHaskell.Flags (Argument)
-import System.Directory (doesFileExist)
-import Text.Printf (printf)
 
+import           Control.Monad.Identity (Identity(Identity), unless, when)
+import           IHaskell.Convert.Args (ConvertSpec(..), fromJustConvertSpec, toConvertSpec)
+import           IHaskell.Convert.IpynbToLhs (ipynbToLhs)
+import           IHaskell.Convert.LhsToIpynb (lhsToIpynb)
+import           IHaskell.Flags (Argument)
+import           System.Directory (doesFileExist)
+import           Text.Printf (printf)
+
 -- | used by @IHaskell convert@
 convert :: [Argument] -> IO ()
-convert args = case fromJustConvertSpec (toConvertSpec args) of
-  ConvertSpec { convertToIpynb = Identity toIpynb,
-                convertInput = Identity inputFile,
-                convertOutput = Identity outputFile,
-                convertLhsStyle = Identity lhsStyle,
-                convertOverwriteFiles = force }
+convert args =
+  case fromJustConvertSpec (toConvertSpec args) of
+    ConvertSpec
+      { convertToIpynb = Identity toIpynb
+      , convertInput = Identity inputFile
+      , convertOutput = Identity outputFile
+      , convertLhsStyle = Identity lhsStyle
+      , convertOverwriteFiles = force
+      }
       | toIpynb -> do
-        unless force (failIfExists outputFile)
-        lhsToIpynb lhsStyle inputFile outputFile
+          unless force (failIfExists outputFile)
+          lhsToIpynb lhsStyle inputFile outputFile
       | otherwise -> do
-        unless force (failIfExists outputFile)
-        ipynbToLhs lhsStyle inputFile outputFile
+          unless force (failIfExists outputFile)
+          ipynbToLhs lhsStyle inputFile outputFile
 
 -- | Call fail when the named file already exists.
 failIfExists :: FilePath -> IO ()
@@ -29,5 +33,3 @@
   exists <- doesFileExist file
   when exists $ fail $
     printf "File %s already exists. To force supply --force." file
-
-
diff --git a/src/IHaskell/Convert/Args.hs b/src/IHaskell/Convert/Args.hs
--- a/src/IHaskell/Convert/Args.hs
+++ b/src/IHaskell/Convert/Args.hs
@@ -1,107 +1,101 @@
 -- | Description: interpret flags parsed by "IHaskell.Flags"
-module IHaskell.Convert.Args
-  (ConvertSpec(..),
-   fromJustConvertSpec,
-   toConvertSpec,
-  ) where
+module IHaskell.Convert.Args (ConvertSpec(..), fromJustConvertSpec, toConvertSpec) where
 
-import Control.Applicative ((<$>))
-import Control.Monad.Identity (Identity(Identity))
-import Data.Char (toLower)
-import Data.List (partition)
-import Data.Maybe (fromMaybe)
+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)
-
+import           IHaskell.Flags (Argument(..), LhsStyle, lhsStyleBird, NotebookFormat(..))
+import           System.FilePath ((<.>), dropExtension, takeExtension)
+import           Text.Printf (printf)
 
 -- | ConvertSpec is the accumulator for command line arguments
-data ConvertSpec f = ConvertSpec
-  { convertToIpynb :: f Bool,
-    convertInput :: f FilePath,
-    convertOutput :: f FilePath,
-    convertLhsStyle :: f (LhsStyle T.Text),
-    convertOverwriteFiles :: Bool
-  }
+data ConvertSpec f =
+       ConvertSpec
+         { convertToIpynb :: f Bool
+         , convertInput :: f FilePath
+         , convertOutput :: f FilePath
+         , convertLhsStyle :: f (LhsStyle T.Text)
+         , convertOverwriteFiles :: Bool
+         }
 
--- | Convert a possibly-incomplete specification for what to convert
--- into one which can be executed. Calls error when data is missing.
-fromJustConvertSpec ::  ConvertSpec Maybe -> ConvertSpec Identity
-fromJustConvertSpec convertSpec = convertSpec {
-        convertToIpynb = Identity toIpynb,
-        convertInput = Identity inputFile,
-        convertOutput = Identity outputFile,
-        convertLhsStyle = Identity $ fromMaybe
-                                        (T.pack <$> lhsStyleBird)
-                                        (convertLhsStyle convertSpec)
-      }
+-- | Convert a possibly-incomplete specification for what to convert into one which can be executed.
+-- Calls error when data is missing.
+fromJustConvertSpec :: ConvertSpec Maybe -> ConvertSpec Identity
+fromJustConvertSpec convertSpec = convertSpec
+  { convertToIpynb = Identity toIpynb
+  , convertInput = Identity inputFile
+  , convertOutput = Identity outputFile
+  , convertLhsStyle = Identity $ fromMaybe (T.pack <$> lhsStyleBird) (convertLhsStyle convertSpec)
+  }
   where
     toIpynb = fromMaybe (error "Error: direction for conversion unknown")
-                            (convertToIpynb convertSpec)
-    (inputFile, outputFile) = case (convertInput convertSpec, convertOutput convertSpec) of
+                (convertToIpynb convertSpec)
+    (inputFile, outputFile) =
+      case (convertInput convertSpec, convertOutput convertSpec) of
         (Nothing, Nothing) -> error "Error: no files specified for conversion"
-        (Just i, Nothing) | toIpynb -> (i, dropExtension i <.> "ipynb")
-                          | otherwise -> (i, dropExtension i <.> "lhs")
-        (Nothing, Just o) | toIpynb -> (dropExtension o <.> "lhs", o)
-                          | otherwise -> (dropExtension o <.> "ipynb", o)
+        (Just i, Nothing)
+          | toIpynb -> (i, dropExtension i <.> "ipynb")
+          | otherwise -> (i, dropExtension i <.> "lhs")
+        (Nothing, Just o)
+          | toIpynb -> (dropExtension o <.> "lhs", o)
+          | otherwise -> (dropExtension o <.> "ipynb", o)
         (Just i, Just o) -> (i, o)
 
 -- | Does this @Argument@ explicitly request a file format?
-isFormatSpec ::  Argument -> Bool
+isFormatSpec :: Argument -> Bool
 isFormatSpec (ConvertToFormat _) = True
 isFormatSpec (ConvertFromFormat _) = True
 isFormatSpec _ = False
 
-
 toConvertSpec :: [Argument] -> ConvertSpec Maybe
-toConvertSpec args = mergeArgs otherArgs
-                                (mergeArgs formatSpecArgs initialConvertSpec)
+toConvertSpec args = mergeArgs otherArgs (mergeArgs formatSpecArgs initialConvertSpec)
   where
     (formatSpecArgs, otherArgs) = partition isFormatSpec args
     initialConvertSpec = ConvertSpec Nothing Nothing Nothing Nothing False
 
-mergeArgs ::  [Argument] -> ConvertSpec Maybe -> ConvertSpec Maybe
+mergeArgs :: [Argument] -> ConvertSpec Maybe -> ConvertSpec Maybe
 mergeArgs args initialConvertSpec = foldr mergeArg initialConvertSpec args
 
-mergeArg ::  Argument -> ConvertSpec Maybe -> ConvertSpec Maybe
+mergeArg :: Argument -> ConvertSpec Maybe -> ConvertSpec Maybe
 mergeArg OverwriteFiles convertSpec = convertSpec { convertOverwriteFiles = True }
 mergeArg (ConvertLhsStyle lhsStyle) convertSpec
   | Just previousLhsStyle <- convertLhsStyle convertSpec,
-    previousLhsStyle /= fmap T.pack lhsStyle = error $ printf
-                                              "Conflicting lhs styles requested: <%s> and <%s>"
-                                              (show lhsStyle) (show previousLhsStyle)
+    previousLhsStyle /= fmap T.pack lhsStyle
+  = error $ printf "Conflicting lhs styles requested: <%s> and <%s>" (show lhsStyle)
+              (show previousLhsStyle)
   | otherwise = convertSpec { convertLhsStyle = Just (T.pack <$> lhsStyle) }
 mergeArg (ConvertFrom inputFile) convertSpec
   | Just previousInputFile <- convertInput convertSpec,
-    previousInputFile /= inputFile = error $ printf "Multiple input files specified: <%s> and <%s>"
-                                              inputFile previousInputFile
-  | otherwise = convertSpec {
-          convertInput = Just inputFile,
-          convertToIpynb = case (convertToIpynb convertSpec, fromExt inputFile) of
-                              (prev, Nothing) -> prev
-                              (prev @ (Just _), _) -> prev
-                              (Nothing, format) -> fmap (== LhsMarkdown) format
-        }
-
+    previousInputFile /= inputFile
+  = error $ printf "Multiple input files specified: <%s> and <%s>" inputFile previousInputFile
+  | otherwise = convertSpec
+      { convertInput = Just inputFile
+      , convertToIpynb = case (convertToIpynb convertSpec, fromExt inputFile) of
+        (prev, Nothing)    -> prev
+        (prev@(Just _), _) -> prev
+        (Nothing, format)  -> fmap (== LhsMarkdown) format
+      }
 mergeArg (ConvertTo outputFile) convertSpec
   | Just previousOutputFile <- convertOutput convertSpec,
-    previousOutputFile /= outputFile = error $ printf "Multiple output files specified: <%s> and <%s>"
-                                              outputFile previousOutputFile
-  | otherwise = convertSpec {
-          convertOutput = Just outputFile,
-          convertToIpynb = case (convertToIpynb convertSpec, fromExt outputFile) of
-                              (prev, Nothing)         -> prev
-                              (prev @ (Just _), _) -> prev
-                              (Nothing, format) -> fmap (== IpynbFile) format
-        }
-
+    previousOutputFile /= outputFile
+  = error $ printf "Multiple output files specified: <%s> and <%s>" outputFile previousOutputFile
+  | otherwise = convertSpec
+      { convertOutput = Just outputFile
+      , convertToIpynb = case (convertToIpynb convertSpec, fromExt outputFile) of
+        (prev, Nothing)    -> prev
+        (prev@(Just _), _) -> prev
+        (Nothing, format)  -> fmap (== IpynbFile) format
+      }
 mergeArg unexpectedArg _ = error $ "IHaskell.Convert.mergeArg: impossible argument: "
-                                      ++ show unexpectedArg
+                                   ++ show unexpectedArg
 
 -- | Guess the format based on the file extension.
-fromExt ::  FilePath -> Maybe NotebookFormat
-fromExt s = case map toLower (takeExtension s) of
-  ".lhs" -> Just LhsMarkdown
-  ".ipynb" -> Just IpynbFile
-  _ -> Nothing
+fromExt :: FilePath -> Maybe NotebookFormat
+fromExt s =
+  case map toLower (takeExtension s) of
+    ".lhs"   -> Just LhsMarkdown
+    ".ipynb" -> Just IpynbFile
+    _        -> Nothing
diff --git a/src/IHaskell/Convert/IpynbToLhs.hs b/src/IHaskell/Convert/IpynbToLhs.hs
--- a/src/IHaskell/Convert/IpynbToLhs.hs
+++ b/src/IHaskell/Convert/IpynbToLhs.hs
@@ -1,66 +1,66 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ScopedTypeVariables #-}
+
 module IHaskell.Convert.IpynbToLhs (ipynbToLhs) where
 
-import Control.Applicative ((<$>))
-import Data.Aeson (decode, Object, Value(Array, Object, String))
+import           Control.Applicative ((<$>))
+import           Data.Aeson (decode, Object, Value(Array, Object, String))
 import qualified Data.ByteString.Lazy as L (readFile)
 import qualified Data.HashMap.Strict as M (lookup)
-import Data.Maybe (fromMaybe)
-import Data.Monoid ((<>), Monoid(mempty))
+import           Data.Maybe (fromMaybe)
+import           Data.Monoid ((<>), Monoid(mempty))
 import qualified Data.Text.Lazy as T (concat, fromStrict, Text, unlines)
 import qualified Data.Text.Lazy.IO as T (writeFile)
-import Data.Vector (Vector)
+import           Data.Vector (Vector)
 import qualified Data.Vector as V (map, mapM, toList)
-import IHaskell.Flags (LhsStyle(lhsBeginCode, lhsBeginOutput, lhsCodePrefix, lhsEndCode, lhsEndOutput, lhsOutputPrefix))
+import           IHaskell.Flags (LhsStyle(..))
 
 ipynbToLhs :: LhsStyle T.Text
-  -> FilePath -- ^ the filename of an ipython notebook
-  -> FilePath -- ^ the filename of the literate haskell to write
-  -> IO ()
+           -> FilePath -- ^ the filename of an ipython notebook
+           -> FilePath -- ^ the filename of the literate haskell to write
+           -> IO ()
 ipynbToLhs sty from to = do
   Just (js :: Object) <- decode <$> L.readFile from
-  case M.lookup "worksheets" js of
-    Just (Array worksheets)
-      | [ Object worksheet ] <- V.toList worksheets,
-        Just (Array cells) <- M.lookup "cells" worksheet ->
-            T.writeFile to $ T.unlines $ V.toList
-              $ V.map (\(Object y) -> convCell sty y) cells
-    _ -> error "IHaskell.Convert.ipynbTolhs: json does not follow expected schema"  
+  case M.lookup "cells" js of
+    Just (Array cells) ->
+      T.writeFile to $ T.unlines $ V.toList $ V.map (\(Object y) -> convCell sty y) cells
+    _ -> error "IHaskell.Convert.ipynbTolhs: json does not follow expected schema"
 
 concatWithPrefix :: T.Text -- ^ the prefix to add to every line
-  -> Vector Value          -- ^ a json array of text lines
-  -> Maybe T.Text
+                 -> Vector Value          -- ^ a json array of text lines
+                 -> Maybe T.Text
 concatWithPrefix p arr = T.concat . map (p <>) . V.toList <$> V.mapM toStr arr
 
 toStr :: Value -> Maybe T.Text
 toStr (String x) = Just (T.fromStrict x)
 toStr _ = Nothing
 
--- | @convCell sty cell@ converts a single cell in JSON into text suitable
--- for the type of lhs file described by the @sty@
+-- | @convCell sty cell@ converts a single cell in JSON into text suitable for the type of lhs file
+-- described by the @sty@
 convCell :: LhsStyle T.Text -> Object -> T.Text
 convCell _sty object
   | Just (String "markdown") <- M.lookup "cell_type" object,
-    Just (Array xs)          <- M.lookup "source" object,
-    ~ (Just s) <- concatWithPrefix "" xs = s
+    Just (Array xs) <- M.lookup "source" object,
+    ~(Just s) <- concatWithPrefix "" xs
+  = s
 convCell sty object
-    | Just (String "code") <- M.lookup "cell_type" object,
-      Just (Array i)       <- M.lookup "input" object,
-      Just (Array o)       <- M.lookup "outputs" object,
-     ~ (Just i) <- concatWithPrefix (lhsCodePrefix sty) i,
-      o <- fromMaybe mempty (convOutputs sty o) = "\n" <>
-              lhsBeginCode sty <> i <> lhsEndCode sty <> "\n" <> o <> "\n"
+  | Just (String "code") <- M.lookup "cell_type" object,
+    Just (Array i) <- M.lookup "source" object,
+    Just (Array o) <- M.lookup "outputs" object,
+    ~(Just i) <- concatWithPrefix (lhsCodePrefix sty) i,
+    o <- fromMaybe mempty (convOutputs sty o)
+  = "\n" <>
+    lhsBeginCode sty <> i <> lhsEndCode sty <> "\n" <> o <> "\n"
 convCell _ _ = "IHaskell.Convert.convCell: unknown cell"
 
-convOutputs ::  LhsStyle T.Text
-  -> Vector Value -- ^ JSON array of output lines containing text or markup
-  -> Maybe T.Text
+convOutputs :: LhsStyle T.Text
+            -> Vector Value -- ^ JSON array of output lines containing text or markup
+            -> Maybe T.Text
 convOutputs sty array = do
   outputLines <- V.mapM (getTexts (lhsOutputPrefix sty)) array
   return $ lhsBeginOutput sty <> T.concat (V.toList outputLines) <> lhsEndOutput sty
 
-getTexts ::  T.Text -> Value -> Maybe T.Text
+getTexts :: T.Text -> Value -> Maybe T.Text
 getTexts p (Object object)
   | Just (Array text) <- M.lookup "text" object = concatWithPrefix p text
 getTexts _ _ = Nothing
diff --git a/src/IHaskell/Convert/LhsToIpynb.hs b/src/IHaskell/Convert/LhsToIpynb.hs
--- a/src/IHaskell/Convert/LhsToIpynb.hs
+++ b/src/IHaskell/Convert/LhsToIpynb.hs
@@ -1,103 +1,121 @@
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE CPP #-}
+
 module IHaskell.Convert.LhsToIpynb (lhsToIpynb) where
 
-import Control.Applicative ((<$>))
-import Data.Aeson ((.=), encode, object, Value(Array, Bool, Number, String))
+import           Control.Applicative ((<$>))
+import           Control.Monad (mplus)
+import           Data.Aeson ((.=), encode, object, Value(Array, Bool, Number, String, Null))
 import qualified Data.ByteString.Lazy as L (writeFile)
-import Data.Char (isSpace)
-import Data.Monoid (Monoid(mempty))
+import           Data.Char (isSpace)
+import           Data.Monoid (Monoid(mempty))
 import qualified Data.Text as TS (Text)
-import qualified Data.Text.Lazy as T (dropWhile, lines, stripPrefix, Text, toStrict)
+import qualified Data.Text.Lazy as T (dropWhile, lines, stripPrefix, Text, toStrict, snoc, strip)
 import qualified Data.Text.Lazy.IO as T (readFile)
 import qualified Data.Vector as V (fromList, singleton)
-import IHaskell.Flags (LhsStyle(LhsStyle))
+import           IHaskell.Flags (LhsStyle(LhsStyle))
 
 lhsToIpynb :: LhsStyle T.Text -> FilePath -> FilePath -> IO ()
 lhsToIpynb sty from to = do
-  classed <-  classifyLines sty . T.lines <$> T.readFile from
+  classed <- classifyLines sty . T.lines <$> T.readFile from
   L.writeFile to . encode . encodeCells $ groupClassified classed
 
-data CellLine a = CodeLine a | OutputLine a | MarkdownLine a
-                deriving Show
+data CellLine a = CodeLine a
+                | OutputLine a
+                | MarkdownLine a
+  deriving Show
 
-isCode ::  CellLine t -> Bool
+isCode :: CellLine t -> Bool
 isCode (CodeLine _) = True
 isCode _ = False
 
-isOutput ::  CellLine t -> Bool
+isOutput :: CellLine t -> Bool
 isOutput (OutputLine _) = True
 isOutput _ = False
 
-isMD ::  CellLine t -> Bool
+isMD :: CellLine t -> Bool
 isMD (MarkdownLine _) = True
 isMD _ = False
 
-isEmptyMD ::  (Eq a, Monoid a) => CellLine a -> Bool
+isEmptyMD :: (Eq a, Monoid a) => CellLine a -> Bool
 isEmptyMD (MarkdownLine a) = a == mempty
 isEmptyMD _ = False
 
-
-untag ::  CellLine t -> t
+untag :: CellLine t -> t
 untag (CodeLine a) = a
 untag (OutputLine a) = a
 untag (MarkdownLine a) = a
 
-data Cell a = Code a a | Markdown a
-            deriving (Show)
+data Cell a = Code a a
+            | Markdown a
+  deriving Show
 
 encodeCells :: [Cell [T.Text]] -> Value
 encodeCells xs = object $
-  [ "worksheets" .= Array (V.singleton (object
-    [ "cells" .= Array (V.fromList (map cellToVal xs)) ] ))
-  ] ++ boilerplate
+  ["cells" .= Array (V.fromList (map cellToVal xs))]
+  ++ boilerplate
 
 cellToVal :: Cell [T.Text] -> Value
 cellToVal (Code i o) = object $
-  [ "cell_type" .= String "code",
-    "collapsed" .= Bool False,
-    "language" .= String "python", -- is what it IPython gives us
-    "metadata" .= object [],
-     "input" .= arrayFromTxt i,
-     "outputs" .= Array 
-        (V.fromList (
-           [ object ["text" .= arrayFromTxt o,
-             "metadata" .= object [],
-             "output_type" .= String "display_data" ]
-          | _ <- take 1 o])) ]
-
+  [ "cell_type" .= String "code"
+  , "execution_count" .= Null
+  , "metadata" .= object ["collapsed" .= Bool False]
+  , "source" .= arrayFromTxt i
+  , "outputs" .= Array
+                   (V.fromList
+                      ([object
+                          [ "text" .= arrayFromTxt o
+                          , "metadata" .= object []
+                          , "output_type" .= String "display_data"
+                          ] | _ <- take 1 o]))
+  ]
 cellToVal (Markdown txt) = object $
-  [ "cell_type" .= String "markdown",
-    "metadata" .= object [],
-    "source" .= arrayFromTxt txt ]
+  [ "cell_type" .= String "markdown"
+  , "metadata" .= object ["hidden" .= Bool False]
+  , "source" .= arrayFromTxt txt
+  ]
 
 -- | arrayFromTxt makes a JSON array of string s
-arrayFromTxt ::  [T.Text] -> Value
-arrayFromTxt i = Array (V.fromList (map (String . T.toStrict) i))
+arrayFromTxt :: [T.Text] -> Value
+arrayFromTxt i = Array (V.fromList $ map stringify i)
+  where
+    stringify = String . T.toStrict . flip T.snoc '\n'
 
--- | ihaskell needs this boilerplate at the upper level to interpret the
--- json describing cells and output correctly.
+-- | ihaskell needs this boilerplate at the upper level to interpret the json describing cells and
+-- output correctly.
 boilerplate :: [(TS.Text, Value)]
 boilerplate =
-  [ "metadata" .= object [ "language" .= String "haskell", "name" .= String ""],
-    "nbformat" .= Number 3,
-    "nbformat_minor" .= Number 0 ]
+  ["metadata" .= object [kernelspec, lang], "nbformat" .= Number 4, "nbformat_minor" .= Number 0]
+  where
+    kernelspec = "kernelspec" .= object
+                                   [ "display_name" .= String "Haskell"
+                                   , "language" .= String "haskell"
+                                   , "name" .= String "haskell"
+                                   ]
+    lang = "language_info" .= object ["name" .= String "haskell", "version" .= String VERSION_ghc]
 
 groupClassified :: [CellLine T.Text] -> [Cell [T.Text]]
-groupClassified (CodeLine a : x)
-    | (c,x) <- span isCode x,
-      (_,x) <- span isEmptyMD x,
-      (o,x) <- span isOutput x = Code (a : map untag c) (map untag o) : groupClassified x
-groupClassified (MarkdownLine a : x)
-    | (m,x) <- span isMD x = Markdown (a: map untag m) : groupClassified x
-groupClassified (OutputLine a : x ) = Markdown [a] : groupClassified x
+groupClassified (CodeLine a:x)
+  | (c, x) <- span isCode x,
+    (_, x) <- span isEmptyMD x,
+    (o, x) <- span isOutput x
+  = Code (a : map untag c) (map untag o) : groupClassified x
+groupClassified (MarkdownLine a:x)
+  | (m, x) <- span isMD x = Markdown (a : map untag m) : groupClassified x
+groupClassified (OutputLine a:x) = Markdown [a] : groupClassified x
 groupClassified [] = []
 
 classifyLines :: LhsStyle T.Text -> [T.Text] -> [CellLine T.Text]
-classifyLines sty@(LhsStyle c o _ _ _ _) (l:ls) = case (sp c, sp o) of
-    (Just a, Nothing) -> CodeLine a : classifyLines sty ls
-    (Nothing, Just a) -> OutputLine a : classifyLines sty ls
-    (Nothing,Nothing) -> MarkdownLine l : classifyLines sty ls
-    _ -> error "IHaskell.Convert.classifyLines"
-  where sp c = T.stripPrefix (T.dropWhile isSpace c) (T.dropWhile isSpace l)
-classifyLines _ [] = []    
-
+classifyLines sty@(LhsStyle c o _ _ _ _) (l:ls) =
+  case (sp c, sp o) of
+    (Just a, Nothing)  -> CodeLine a : classifyLines sty ls
+    (Nothing, Just a)  -> OutputLine a : classifyLines sty ls
+    (Nothing, Nothing) -> MarkdownLine l : classifyLines sty ls
+    _                  -> error "IHaskell.Convert.classifyLines"
+  where
+    sp x = T.stripPrefix (dropSpace x) (dropSpace l) `mplus` blankCodeLine x
+    blankCodeLine x = if T.strip x == T.strip l
+                        then Just ""
+                        else Nothing
+    dropSpace = T.dropWhile isSpace
+classifyLines _ [] = []
diff --git a/src/IHaskell/Display.hs b/src/IHaskell/Display.hs
--- a/src/IHaskell/Display.hs
+++ b/src/IHaskell/Display.hs
@@ -1,56 +1,66 @@
 {-# LANGUAGE NoImplicitPrelude, OverloadedStrings, FlexibleInstances #-}
 
--- | If you are interested in the IHaskell library for the purpose of
--- augmenting the IHaskell notebook or writing your own display mechanisms
--- and widgets, this module contains all functions you need. 
+-- | If you are interested in the IHaskell library for the purpose of augmenting the IHaskell
+-- notebook or writing your own display mechanisms and widgets, this module contains all functions
+-- you need.
 --
--- In order to create a display mechanism for a particular data type, write
--- a module named (for example) @IHaskell.Display.YourThing@ in a package named @ihaskell-yourThing@.
--- (Note the capitalization - it's important!) Then, in that module, add an
--- instance of @IHaskellDisplay@ for your data type. Similarly, to create
--- a widget, add an instance of @IHaskellWidget@. 
+-- In order to create a display mechanism for a particular data type, write a module named (for
+-- example) @IHaskell.Display.YourThing@ in a package named @ihaskell-yourThing@. (Note the
+-- capitalization - it's important!) Then, in that module, add an instance of @IHaskellDisplay@ for
+-- your data type. Similarly, to create a widget, add an instance of @IHaskellWidget@.
 --
--- An example of creating a display is provided in the <http://gibiansky.github.io/IHaskell/demo.html demo notebook>.
+-- An example of creating a display is provided in the
+-- <http://gibiansky.github.io/IHaskell/demo.html demo notebook>.
 --
 module IHaskell.Display (
-  -- * Rich display and interactive display typeclasses and types
-  IHaskellDisplay(..),
-  Display(..),
-  DisplayData(..),
-  IHaskellWidget(..),
+    -- * Rich display and interactive display typeclasses and types
+    IHaskellDisplay(..),
+    Display(..),
+    DisplayData(..),
+    IHaskellWidget(..),
 
-  -- ** Interactive use functions
-  printDisplay,
+    -- ** Interactive use functions
+    printDisplay,
 
-  -- * Constructors for displays
-  plain, html, png, jpg, svg, latex, javascript, many,
+    -- * Constructors for displays
+    plain,
+    html,
+    png,
+    jpg,
+    svg,
+    latex,
+    javascript,
+    many,
 
-  -- ** Image and data encoding functions
-  Width, Height, Base64(..),
-  encode64, base64,
+    -- ** Image and data encoding functions
+    Width,
+    Height,
+    Base64(..),
+    encode64,
+    base64,
 
-  -- ** Utilities
-  switchToTmpDir,
+    -- ** Utilities
+    switchToTmpDir,
 
-  -- * Internal only use
-  displayFromChan,
-  serializeDisplay,
-  Widget(..),
-  ) where
+    -- * Internal only use
+    displayFromChan,
+    serializeDisplay,
+    Widget(..),
+    ) where
 
-import ClassyPrelude
-import Data.Serialize as Serialize
-import Data.ByteString hiding (map, pack)
-import Data.String.Utils (rstrip) 
+import           ClassyPrelude
+import           Data.Serialize as Serialize
+import           Data.ByteString hiding (map, pack)
+import           Data.String.Utils (rstrip)
 import qualified Data.ByteString.Base64 as Base64
 import qualified Data.ByteString.Char8 as Char
-import Data.Aeson (Value)
-import System.Directory(getTemporaryDirectory, setCurrentDirectory)
+import           Data.Aeson (Value)
+import           System.Directory (getTemporaryDirectory, setCurrentDirectory)
 
-import Control.Concurrent.STM.TChan
-import System.IO.Unsafe (unsafePerformIO)
+import           Control.Concurrent.STM.TChan
+import           System.IO.Unsafe (unsafePerformIO)
 
-import IHaskell.Types
+import           IHaskell.Types
 
 type Base64 = Text
 
@@ -61,8 +71,7 @@
 -- > IO [Display]
 -- > IO (IO Display)
 --
--- be run the IO and get rendered (if the frontend allows it) in the pretty
--- form.
+-- 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 =<<)
 
@@ -101,15 +110,15 @@
 javascript :: String -> DisplayData
 javascript = DisplayData MimeJavascript . pack
 
--- | Generate a PNG display of the given width and height. Data must be
--- provided in a Base64 encoded manner, suitable for embedding into HTML.
--- The @base64@ function may be used to encode data into this format.
+-- | Generate a PNG display of the given width and height. Data must be provided in a Base64 encoded
+-- manner, suitable for embedding into HTML. The @base64@ function may be used to encode data into
+-- this format.
 png :: Width -> Height -> Base64 -> DisplayData
 png width height = DisplayData (MimePng width height)
 
--- | Generate a JPG display of the given width and height. Data must be
--- provided in a Base64 encoded manner, suitable for embedding into HTML.
--- The @base64@ function may be used to encode data into this format.
+-- | Generate a JPG display of the given width and height. Data must be provided in a Base64 encoded
+-- manner, suitable for embedding into HTML. The @base64@ function may be used to encode data into
+-- this format.
 jpg :: Width -> Height -> Base64 -> DisplayData
 jpg width height = DisplayData (MimeJpg width height)
 
@@ -121,42 +130,37 @@
 base64 :: ByteString -> Base64
 base64 = decodeUtf8 . Base64.encode
 
--- | For internal use within IHaskell.
--- Serialize displays to a ByteString.
+-- | For internal use within IHaskell. Serialize displays to a ByteString.
 serializeDisplay :: Display -> ByteString
 serializeDisplay = Serialize.encode
 
--- | Items written to this chan will be included in the output sent
--- to the frontend (ultimately the browser), the next time IHaskell
--- has an item to display.
+-- | Items written to this chan will be included in the output sent to the frontend (ultimately the
+-- browser), the next time IHaskell has an item to display.
 {-# NOINLINE displayChan #-}
 displayChan :: TChan Display
 displayChan = unsafePerformIO newTChanIO
 
--- | Take everything that was put into the 'displayChan' at that point
--- out, and make a 'Display' out of it.
+-- | Take everything that was put into the 'displayChan' at that point out, and make a 'Display' out
+-- of it.
 displayFromChan :: IO (Maybe Display)
 displayFromChan =
   Just . many <$> unfoldM (atomically $ tryReadTChan displayChan)
 
--- | This is unfoldM from monad-loops. It repeatedly runs an IO action
--- until it return Nothing, and puts all the Justs in a list.
--- If you find yourself using more functionality from monad-loops, just add
--- the package dependency instead of copying more code from it.
+-- | This is unfoldM from monad-loops. It repeatedly runs an IO action until it return Nothing, and
+-- puts all the Justs in a list. If you find yourself using more functionality from monad-loops,
+-- just add the package dependency instead of copying more code from it.
 unfoldM :: IO (Maybe a) -> IO [a]
-unfoldM f = maybe (return []) (\r -> (r:) <$> unfoldM f) =<< f
+unfoldM f = maybe (return []) (\r -> (r :) <$> unfoldM f) =<< f
 
--- | Write to the display channel. The contents will be displayed in the
--- notebook once the current execution call ends.
+-- | Write to the display channel. The contents will be displayed in the notebook once the current
+-- execution call ends.
 printDisplay :: IHaskellDisplay a => a -> IO ()
 printDisplay disp = display disp >>= atomically . writeTChan displayChan
 
--- | 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.
+-- | 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 = void (try switchDir :: IO (Either SomeException ()))
-  where 
+  where
     switchDir =
-      getTemporaryDirectory  >>=
+      getTemporaryDirectory >>=
       setCurrentDirectory
-
diff --git a/src/IHaskell/Eval/Completion.hs b/src/IHaskell/Eval/Completion.hs
--- a/src/IHaskell/Eval/Completion.hs
+++ b/src/IHaskell/Eval/Completion.hs
@@ -1,4 +1,6 @@
-{-# LANGUAGE NoImplicitPrelude, OverloadedStrings, DoAndIfThenElse #-}
+{-# LANGUAGE CPP, NoImplicitPrelude, OverloadedStrings, DoAndIfThenElse #-}
+{-# LANGUAGE TypeFamilies, FlexibleContexts #-}
+
 {- |
 Description:    Generates tab completion options.
 
@@ -11,52 +13,67 @@
 -}
 module IHaskell.Eval.Completion (complete, completionTarget, completionType, CompletionType(..)) where
 
-import ClassyPrelude hiding (init, last, head, liftIO)
---import Prelude
+import           ClassyPrelude hiding (init, last, head, liftIO)
 
-import Control.Applicative ((<$>))
-import Data.ByteString.UTF8 hiding (drop, take)
-import Data.Char
-import Data.List (nub, init, last, head, elemIndex)
-import Data.List.Split
-import Data.List.Split.Internals
-import Data.Maybe (fromJust)
-import Data.String.Utils (strip, startswith, endswith, replace)
+import           Control.Applicative ((<$>))
+import           Data.ByteString.UTF8 hiding (drop, take, lines, length)
+import           Data.Char
+import           Data.List (nub, init, last, head, elemIndex)
+import           Data.List.Split
+import           Data.List.Split.Internals
+import           Data.Maybe (fromJust)
+import           Data.String.Utils (strip, startswith, endswith, replace)
 import qualified Data.String.Utils as StringUtils
-import System.Environment (getEnv)
+import           System.Environment (getEnv)
 
-import GHC
-import DynFlags
-import GhcMonad
-import PackageConfig
-import Outputable (showPpr)
+import           GHC hiding (Qualified)
+#if MIN_VERSION_ghc(7,10,0)
+import           GHC.PackageDb (ExposedModule(exposedName))
+#endif
+import           DynFlags
+import           GhcMonad
+import           PackageConfig
+import           Outputable (showPpr)
 
 
-import System.Directory
-import System.FilePath
-import MonadUtils (MonadIO)
-
-import System.Console.Haskeline.Completion
+import           System.Directory
+import           System.FilePath
+import           MonadUtils (MonadIO)
 
-import IHaskell.Types
-import IHaskell.Eval.Evaluate (Interpreter)
-import IHaskell.Eval.ParseShell (parseShell)
+import           System.Console.Haskeline.Completion
 
+import           IHaskell.Types
+import           IHaskell.Eval.Evaluate (Interpreter)
+import           IHaskell.Eval.ParseShell (parseShell)
 
-data CompletionType
-     = Empty
-     | Identifier String
-     | DynFlag String
-     | Qualified String String
-     | ModuleName String String
-     | HsFilePath String String
-     | FilePath   String String
-     | KernelOption String
-     | Extension String
-     deriving (Show, Eq)
+data CompletionType = Empty
+                    | Identifier String
+                    | DynFlag String
+                    | Qualified String String
+                    | ModuleName String String
+                    | HsFilePath String String
+                    | FilePath String String
+                    | KernelOption String
+                    | Extension String
+  deriving (Show, Eq)
+#if MIN_VERSION_ghc(7,10,0)
+extName (FlagSpec { flagSpecName = name }) = name
+#else
+extName (name, _, _) = name
 
+exposedName = id
+#endif
 complete :: String -> Int -> Interpreter (String, [String])
-complete line pos = do
+complete code posOffset = do
+  -- Get the line of code which is being completed and offset within that line
+  let findLine offset (first:rest) =
+        if offset <= length first
+          then (offset, first)
+          else findLine (offset - length first - 1) rest
+      findLine _ [] = error $ "Could not find line: " ++ show (map length $ lines code, posOffset)
+      (pos, line) = findLine posOffset (lines code)
+
+
   flags <- getSessionDynFlags
   rdrNames <- map (showPpr flags) <$> getRdrNamesInScope
   scopeNames <- nub <$> map (showPpr flags) <$> getNamesInScope
@@ -65,72 +82,69 @@
       qualNames = nub $ scopeNames ++ filter isQualified rdrNames
 
   let Just db = pkgDatabase flags
-      getNames = map moduleNameString . exposedModules
+      getNames = map (moduleNameString . exposedName) . exposedModules
       moduleNames = nub $ concatMap getNames db
 
   let target = completionTarget line pos
       completion = completionType line pos target
 
-  let matchedText = case completion of
-        HsFilePath _ match ->  match
-        FilePath   _ match ->  match
-        otherwise       -> intercalate "." target
-
-  options <-
+  let matchedText =
         case completion of
-          Empty -> return []
+          HsFilePath _ match -> match
+          FilePath _ match   -> match
+          otherwise          -> intercalate "." target
 
-          Identifier candidate ->
-            return $ filter (candidate `isPrefixOf`) unqualNames
+  options <- case completion of
+               Empty -> return []
 
-          Qualified moduleName candidate -> do
-            trueName <- getTrueModuleName moduleName
-            let prefix = intercalate "." [trueName, candidate]
-                completions = filter (prefix `isPrefixOf`)  qualNames
-                falsifyName = replace trueName moduleName
-            return $ map falsifyName completions
+               Identifier candidate ->
+                 return $ filter (candidate `isPrefixOf`) unqualNames
 
-          ModuleName previous candidate -> do
-            let prefix = if null previous
-                         then candidate
-                         else intercalate "." [previous, candidate]
-            return $ filter (prefix `isPrefixOf`) moduleNames
+               Qualified moduleName candidate -> do
+                 trueName <- getTrueModuleName moduleName
+                 let prefix = intercalate "." [trueName, candidate]
+                     completions = filter (prefix `isPrefixOf`) qualNames
+                     falsifyName = replace trueName moduleName
+                 return $ map falsifyName completions
 
-          DynFlag ext -> do
-            -- Possibly leave out the fLangFlags? The
-            -- -XUndecidableInstances vs. obsolete
-            -- -fallow-undecidable-instances.
-            let extName (name, _, _) = name
+               ModuleName previous candidate -> do
+                 let prefix = if null previous
+                                then candidate
+                                else intercalate "." [previous, candidate]
+                 return $ filter (prefix `isPrefixOf`) moduleNames
 
-                kernelOptNames = concatMap getSetName kernelOpts
-                otherNames = ["-package","-Wall","-w"]
+               DynFlag ext -> do
+                 -- Possibly leave out the fLangFlags? The -XUndecidableInstances vs. obsolete
+                 -- -fallow-undecidable-instances.
+                 let kernelOptNames = concatMap getSetName kernelOpts
+                     otherNames = ["-package", "-Wall", "-w"]
 
-                fNames = map extName fFlags ++
-                         map extName fWarningFlags ++
-                         map extName fLangFlags
-                fNoNames = map ("no"++) fNames
-                fAllNames = map ("-f"++) (fNames ++ fNoNames)
+                     fNames = map extName fFlags ++
+                              map extName fWarningFlags ++
+                              map extName fLangFlags
+                     fNoNames = map ("no" ++) fNames
+                     fAllNames = map ("-f" ++) (fNames ++ fNoNames)
 
-                xNames = map extName xFlags
-                xNoNames = map ("No" ++) xNames
-                xAllNames = map ("-X"++) (xNames ++ xNoNames)
+                     xNames = map extName xFlags
+                     xNoNames = map ("No" ++) xNames
+                     xAllNames = map ("-X" ++) (xNames ++ xNoNames)
 
-                allNames = xAllNames ++ otherNames ++ fAllNames
+                     allNames = xAllNames ++ otherNames ++ fAllNames
 
-            return $ filter (ext `isPrefixOf`) allNames
+                 return $ filter (ext `isPrefixOf`) allNames
 
-          Extension ext -> do
-            let extName (name, _, _) = name
-                xNames = map extName xFlags
-                xNoNames = map ("No" ++) xNames
-            return $ filter (ext `isPrefixOf`) $ xNames ++ xNoNames
+               Extension ext -> do
+                 let xNames = map extName xFlags
+                     xNoNames = map ("No" ++) xNames
+                 return $ filter (ext `isPrefixOf`) $ xNames ++ xNoNames
 
-          HsFilePath lineUpToCursor match -> completePathWithExtensions [".hs", ".lhs"] lineUpToCursor
+               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)
+               KernelOption str -> return $
+                 filter (str `isPrefixOf`) (concatMap getOptionName kernelOpts)
 
   return (matchedText, options)
 
@@ -143,113 +157,118 @@
   -- Get all imports that we use.
   imports <- ClassyPrelude.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.
+  -- 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
+    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.
+               -> Int                -- ^ Location of the cursor in the line.
                -> [String]          -- ^ The identifier being completed (pieces separated by dots).
                -> CompletionType
 completionType line loc target
   -- File and directory completions are special
-  | startswith ":!" stripped
-    = fileComplete FilePath
-  | startswith ":l" stripped
-    = fileComplete HsFilePath
+  | startswith ":!" stripped =
+      fileComplete FilePath
+  | startswith ":l" stripped =
+      fileComplete HsFilePath
 
   -- Complete :set, :opt, and :ext
-  | startswith ":s" stripped
-    = DynFlag candidate
-  | startswith ":o" stripped
-    = KernelOption candidate
-  | startswith ":e" stripped
-    = Extension candidate
+  | startswith ":s" stripped =
+      DynFlag candidate
+  | startswith ":o" stripped =
+      KernelOption candidate
+  | startswith ":e" stripped =
+      Extension candidate
 
-  -- Use target for other completions.
-  -- If it's empty, no completion.
-  | null target
-    = Empty
+  -- Use target for other completions. If it's empty, no completion.
+  | null target =
+      Empty
 
   -- When in a string, complete filenames.
-  | cursorInString line loc
-    = FilePath (getStringTarget lineUpToCursor) (getStringTarget lineUpToCursor)
+  | cursorInString line loc =
+      FilePath (getStringTarget lineUpToCursor) (getStringTarget lineUpToCursor)
 
   -- Complete module names in imports and elsewhere.
-  | startswith "import" stripped && isModName
-    = ModuleName dotted candidate
-  | isModName && (not . null . init) target
-    = Qualified dotted candidate
+  | startswith "import" stripped && isModName =
+      ModuleName dotted candidate
+  | isModName && (not . null . init) target =
+      Qualified dotted candidate
 
   -- Default to completing identifiers.
-  | otherwise
-    = Identifier candidate
-  where stripped = strip line
-        dotted = dots target
-        candidate | null target = ""
-                  | otherwise = last target
-        dots = intercalate "." . init
-        isModName = all isCapitalized (init target)
-        isCapitalized = isUpper . head
-        lineUpToCursor = take loc line
-        fileComplete filePath = case parseShell lineUpToCursor of
-          Right xs -> filePath lineUpToCursor $
-                       if endswith (last xs) lineUpToCursor
-                       then last xs
-                       else []
-          Left  _  -> Empty
+  | otherwise =
+      Identifier candidate
+  where
+    stripped = strip line
+    dotted = dots target
+    candidate
+      | null target = ""
+      | otherwise = last target
+    dots = intercalate "." . init
+    isModName = all isCapitalized (init target)
 
-        cursorInString str loc = nquotes (take loc str) `mod` 2 /= 0
+    isCapitalized [] = False
+    isCapitalized (x:_) = isUpper x
 
-        nquotes ('\\':'"':xs) = nquotes xs
-        nquotes ('"':xs) = 1 + nquotes xs
-        nquotes (_:xs) = nquotes xs
-        nquotes [] = 0
+    lineUpToCursor = take loc line
+    fileComplete filePath =
+      case parseShell lineUpToCursor of
+        Right xs -> filePath lineUpToCursor $
+          if endswith (last xs) lineUpToCursor
+            then last xs
+            else []
+        Left _ -> Empty
 
-        -- Get the bit of a string that might be a filename completion.
-        -- Logic is a bit convoluted, but basically go backwards from the
-        -- end, stopping at any quote or space, unless they are escaped.
-        getStringTarget :: String -> String
-        getStringTarget = go "" . reverse
-          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
+    cursorInString str loc = nquotes (take loc str) `mod` 2 /= 0
 
+    nquotes ('\\':'"':xs) = nquotes xs
+    nquotes ('"':xs) = 1 + nquotes xs
+    nquotes (_:xs) = nquotes xs
+    nquotes [] = 0
+
+    -- Get the bit of a string that might be a filename completion. Logic is a bit convoluted, but
+    -- basically go backwards from the end, stopping at any quote or space, unless they are escaped.
+    getStringTarget :: String -> String
+    getStringTarget = go "" . reverse
+      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
+
 -- | Get the word under a given cursor location.
 completionTarget :: String -> Int -> [String]
 completionTarget code cursor = expandCompletionPiece pieceToComplete
   where
     pieceToComplete = map fst <$> find (elem cursor . map snd) pieces
-    pieces = splitAlongCursor $ split splitter $ zip code [1 .. ]
-    splitter = defaultSplitter {
-      -- Split using only the characters, which are the first elements of
-      -- the (char, index) tuple
-      delimiter = Delimiter [uncurry isDelim],
+    pieces = splitAlongCursor $ split splitter $ zip code [1 ..]
+    splitter = defaultSplitter
+      { 
+      -- Split using only the characters, which are the first elements of the (char, index) tuple
+      delimiter = Delimiter [uncurry isDelim]
       -- Condense multiple delimiters into one and then drop them.
-      condensePolicy = Condense,
-      delimPolicy = Drop
-    }
+      , condensePolicy = Condense
+      , delimPolicy = Drop
+      }
 
     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 [] = []
     splitAlongCursor (x:xs) =
-      case elemIndex cursor $  map snd x of
-        Nothing -> x:splitAlongCursor xs
-        Just idx -> take (idx + 1) x:drop (idx + 1) x:splitAlongCursor xs
+      case elemIndex cursor $ map snd x of
+        Nothing  -> x : splitAlongCursor xs
+        Just idx -> take (idx + 1) x : drop (idx + 1) x : splitAlongCursor xs
 
     -- These are never part of an identifier.
     neverIdent :: String
@@ -260,10 +279,11 @@
 
 getHome :: IO String
 getHome = do
-  homeEither <- try  $ getEnv "HOME" :: IO (Either SomeException String)
-  return $ case homeEither of
-    Left _ -> "~"
-    Right home -> home
+  homeEither <- try $ getEnv "HOME" :: IO (Either SomeException String)
+  return $
+    case homeEither of
+      Left _     -> "~"
+      Right home -> home
 
 dirExpand :: String -> IO String
 dirExpand str = do
@@ -277,7 +297,8 @@
 
 completePath :: String -> Interpreter [String]
 completePath line = completePathFilter acceptAll acceptAll line ""
-  where acceptAll = const True
+  where
+    acceptAll = const True
 
 completePathWithExtensions :: [String] -> String -> Interpreter [String]
 completePathWithExtensions extensions line =
@@ -285,7 +306,8 @@
   where
     acceptAll = const True
     extensionIsOneOf exts str = any correctEnding exts
-      where correctEnding ext = endswith ext str
+      where
+        correctEnding ext = endswith ext str
 
 completePathFilter :: (String -> Bool)      -- ^ File filter: test whether to include this file.
                    -> (String -> Bool)      -- ^ Directory filter: test whether to include this directory.
@@ -297,21 +319,19 @@
   expanded <- dirExpand left
   completions <- map replacement <$> snd <$> completeFilename (reverse expanded, right)
 
-  -- Split up into files and directories.
-  -- Filter out ones we don't want.
+  -- Split up into files and directories. Filter out ones we don't want.
   areDirs <- mapM doesDirectoryExist completions
-  let dirs  = filter includeDirectory $ map fst $ filter snd         $ zip completions areDirs
-      files = filter includeFile      $ map fst $ filter (not . snd) $ zip completions areDirs
+  let dirs = filter includeDirectory $ map fst $ filter snd $ zip completions areDirs
+      files = filter includeFile $ map fst $ filter (not . snd) $ zip completions areDirs
 
-  -- Return directories before files. However, stick everything that starts
-  -- with a dot after everything else.  If we wanted to keep original
-  -- order, we could instead use
+  -- Return directories before files. However, stick everything that starts with a dot after
+  -- everything else. If we wanted to keep original order, we could instead use
   --   filter (`elem` (dirs ++ files)) completions
   suggestions <- mapM unDirExpand $ dirs ++ files
   let isHidden str = startswith "." . last . StringUtils.split "/" $
         if endswith "/" str
-        then init str
-        else str
+          then init str
+          else str
       visible = filter (not . isHidden) suggestions
-      hidden  = filter isHidden suggestions
+      hidden = filter isHidden suggestions
   return $ visible ++ hidden
diff --git a/src/IHaskell/Eval/Evaluate.hs b/src/IHaskell/Eval/Evaluate.hs
--- a/src/IHaskell/Eval/Evaluate.hs
+++ b/src/IHaskell/Eval/Evaluate.hs
@@ -1,1216 +1,1257 @@
-{-# LANGUAGE DoAndIfThenElse, NoOverloadedStrings, TypeSynonymInstances, CPP #-}
-
-{- | Description : Wrapper around GHC API, exposing a single `evaluate` interface that runs
-                   a statement, declaration, import, or directive.
-
-This module exports all functions used for evaluation of IHaskell input.
--}
-module IHaskell.Eval.Evaluate (
-  interpret, evaluate, Interpreter, liftIO, typeCleaner, globalImports
-  ) where
-
-import ClassyPrelude hiding (init, last, liftIO, head, hGetContents, tail, try)
-import Control.Concurrent (forkIO, threadDelay)
-import Prelude (putChar, head, tail, last, init, (!!))
-import Data.List.Utils
-import Data.List (findIndex, and)
-import Data.String.Utils
-import Text.Printf
-import Data.Char as Char
-import Data.Dynamic
-import Data.Typeable
-import qualified Data.Serialize as Serialize
-import qualified Language.Haskell.TH as TH  
-import System.Directory
-import Filesystem.Path.CurrentOS (encodeString)
-import System.Posix.IO
-import System.IO (hGetChar, hFlush)
-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 System.Environment (getEnv)
-import qualified Data.Map as Map
-
-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
-import GhcMonad (liftIO, withSession)
-import GHC hiding (Stmt, TypeSig)
-import Exception hiding (evaluate)
-import Outputable hiding ((<>))
-import Packages
-import Module
-import qualified Pretty
-import FastString
-import Bag
-import ErrUtils (errMsgShortDoc, errMsgExtraInfo)
-
-import qualified System.IO.Strict as StrictIO
-
-import IHaskell.Types
-import IHaskell.IPython
-import IHaskell.Eval.Parser
-import IHaskell.Eval.Lint
-import IHaskell.Display
-import qualified IHaskell.Eval.Hoogle as Hoogle
-import IHaskell.Eval.Util
-import IHaskell.BrokenPackages
-import qualified  IHaskell.IPython.Message.UUID      as UUID
-
-import Paths_ihaskell (version)
-import Data.Version (versionBranch)
-
-data ErrorOccurred = Success | Failure deriving (Show, Eq)
-
--- | Enable debugging output
-debug :: Bool
-debug = False    
-
--- | Set GHC's verbosity for debugging
-ghcVerbosity :: Maybe Int
-ghcVerbosity = Nothing -- Just 5
-
-ignoreTypePrefixes :: [String]
-ignoreTypePrefixes = ["GHC.Types", "GHC.Base", "GHC.Show", "System.IO",
-                      "GHC.Float", ":Interactive", "GHC.Num", "GHC.IO",
-                      "GHC.Integer.Type"]
-
-typeCleaner :: String -> String
-typeCleaner = useStringType . foldl' (.) id (map (`replace` "") fullPrefixes)
-  where
-    fullPrefixes = map (++ ".") ignoreTypePrefixes
-    useStringType = replace "[Char]" "String"
-
-write :: GhcMonad m => String -> m ()
-write x = when debug $ 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
-
-globalImports :: [String]
-globalImports =
-  [ "import IHaskell.Display()"
-  , "import qualified Prelude as IHaskellPrelude"
-  , "import qualified System.Directory as IHaskellDirectory"
-  , "import qualified IHaskell.Display"
-  , "import qualified IHaskell.IPython.Stdin"
-  , "import qualified System.Posix.IO as IHaskellIO"
-  , "import qualified System.IO as IHaskellSysIO"
-  , "import qualified Language.Haskell.TH as IHaskellTH"
-  ]
-
--- | 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 environment.
-interpret :: String -> Bool -> Interpreter a -> IO a
-interpret libdir allowedStdin action = runGhc (Just libdir) $ do
-  -- If we're in a sandbox, add the relevant package database
-  sandboxPackages <- liftIO getSandboxPackageConf
-  initGhci sandboxPackages
-  case ghcVerbosity of
-    Just verb -> do dflags <- getSessionDynFlags
-                    void $ setSessionDynFlags $ dflags { verbosity = verb }
-    Nothing   -> return ()
-
-  initializeImports
-
-  -- Close stdin so it can't be used.
-  -- Otherwise it'll block the kernel forever.
-  dir <- liftIO getIHaskellDir
-  let cmd = printf "IHaskell.IPython.Stdin.fixStdin \"%s\"" dir
-  when allowedStdin $ void $
-    runStmt cmd RunToCompletion
-
-  initializeItVariable
-
-  -- Run the rest of the interpreter
-  action
-
--- | Initialize our GHC session with imports and a value for 'it'.
-initializeImports :: Interpreter ()
-initializeImports = do
-  -- Load packages that start with ihaskell-*, aren't just IHaskell,
-  -- and depend directly on the right version of the ihaskell library.
-  -- Also verify that the packages we load are not broken.
-  dflags <- getSessionDynFlags
-  broken <- liftIO getBrokenPackages
-  displayPackages <- liftIO $ do
-    (dflags, _) <- initPackages dflags
-    let Just db = pkgDatabase dflags
-        packageNames = map (packageIdString . packageConfigId) db
-
-        initStr = "ihaskell-"
-
-        -- Name of the ihaskell package, e.g. "ihaskell-1.2.3.4"
-        iHaskellPkgName = initStr ++ intercalate "." (map show (versionBranch version))
-
-        dependsOnRight pkg = not $ null $ do
-            pkg <- db
-            depId <- depends pkg
-            dep <- filter ((== depId) . installedPackageId) db
-            guard (iHaskellPkgName `isPrefixOf` packageIdString (packageConfigId dep))
-
-        -- ideally the Paths_ihaskell module could provide a way to get the
-        -- hash too (ihaskell-0.2.0.5-f2bce922fa881611f72dfc4a854353b9),
-        -- for now. Things will end badly if you also happen to have an
-        -- ihaskell-0.2.0.5-ce34eadc18cf2b28c8d338d0f3755502 installed.
-        iHaskellPkg = case filter (== iHaskellPkgName) packageNames of
-                [x] -> x
-                [] -> error ("cannot find required haskell library: " ++ iHaskellPkgName)
-                _ -> error ("multiple haskell packages " ++ iHaskellPkgName ++ " found")
-
-        displayPkgs = [ pkgName
-                  | pkgName <- packageNames,
-                    Just (x:_) <- [stripPrefix initStr pkgName],
-                    pkgName `notElem` broken,
-                    isAlpha x]
-
-    return displayPkgs
-
-  -- Generate import statements all Display modules.
-  let capitalize :: String -> String
-      capitalize (first:rest) = Char.toUpper first : rest
-
-      importFmt = "import IHaskell.Display.%s"
-
-      toImportStmt :: String -> String
-      toImportStmt = printf importFmt . capitalize . (!! 1) . split "-"
-
-      displayImports = map toImportStmt displayPackages
-
-  -- Import implicit prelude.
-  importDecl <- parseImportDecl "import Prelude"
-  let implicitPrelude = importDecl { ideclImplicit = True }
-
-  -- Import modules.
-  mapM_ (write . ("Importing " ++ )) displayImports
-  imports <- mapM parseImportDecl $ globalImports ++ displayImports
-  setContext $ map IIDecl $ implicitPrelude : imports
-
--- | Give a value for the `it` variable.
-initializeItVariable :: Interpreter ()
-initializeItVariable = do
-  -- This is required due to the way we handle `it` in the wrapper
-  -- statements - if it doesn't exist, the first statement will fail.
-  write "Setting `it` to unit."
-  void $ runStmt "let it = ()" RunToCompletion
-
--- | Publisher for IHaskell outputs.  The first argument indicates whether
--- this output is final (true) or intermediate (false).
-type Publisher = (EvaluationResult -> IO ())
-
--- | Output of a command evaluation.
-data EvalOut = EvalOut {
-    evalStatus :: ErrorOccurred,
-    evalResult :: Display,
-    evalState :: KernelState,
-    evalPager :: String,
-    evalComms :: [CommInfo]
-  }
-
--- | Evaluate some IPython input code.
-evaluate :: KernelState                  -- ^ The kernel state.
-         -> String                       -- ^ Haskell code or other interpreter commands.
-         -> (EvaluationResult -> IO ())   -- ^ Function used to publish data outputs.
-         -> Interpreter KernelState
-evaluate kernelState code output = do
-  cmds <- parseString (strip code)
-  let execCount = getExecutionCounter kernelState
-
-  when (getLintStatus kernelState /= LintOff) $ liftIO $ do
-    lintSuggestions <- lint cmds
-    unless (noResults lintSuggestions) $
-      output $ FinalResult lintSuggestions "" []
-
-  updated <- runUntilFailure kernelState (map unloc cmds ++ [storeItCommand execCount])
-  return updated {
-    getExecutionCounter = execCount + 1
-  }
-  where
-    noResults (Display res) = null res
-    noResults (ManyDisplay res) = all noResults res
-
-    runUntilFailure :: KernelState -> [CodeBlock] -> Interpreter KernelState
-    runUntilFailure state [] = return state
-    runUntilFailure state (cmd:rest) = do
-      evalOut <- evalCommand output cmd state
-
-      -- Get displayed channel outputs.
-      -- Merge them with normal display outputs.
-      dispsIO <- extractValue "IHaskell.Display.displayFromChan"
-      dispsMay <- liftIO dispsIO
-      let result =
-            case dispsMay of
-              Nothing -> evalResult evalOut
-              Just disps -> evalResult evalOut <> disps
-          helpStr = evalPager evalOut
-
-      -- Output things only if they are non-empty.
-      let empty = noResults result && null helpStr && null (evalComms evalOut)
-      unless empty $
-        liftIO $ output $ FinalResult result helpStr (evalComms evalOut)
-
-      -- Make sure to clear all comms we've started.
-      let newState = evalState evalOut { evalComms = [] }
-
-      case evalStatus evalOut of
-        Success -> runUntilFailure newState rest
-        Failure -> return newState
-
-    storeItCommand execCount = Statement $ printf "let it%d = it" execCount
-
-    extractValue :: Typeable a => String -> Interpreter a
-    extractValue expr = do
-      compiled <- dynCompileExpr expr
-      case fromDynamic compiled of
-        Nothing -> error "Expecting value!"
-        Just result -> return result
-
-safely :: KernelState -> Interpreter EvalOut -> Interpreter EvalOut
-safely state = ghandle handler . ghandle sourceErrorHandler
-  where
-    handler :: SomeException -> Interpreter EvalOut
-    handler exception =
-      return EvalOut {
-        evalStatus = Failure,
-        evalResult = displayError $ show exception,
-        evalState = state,
-        evalPager = "",
-        evalComms = []
-      }
-
-    sourceErrorHandler :: SourceError -> Interpreter EvalOut
-    sourceErrorHandler srcerr = do
-      let msgs = bagToList $ srcErrorMessages srcerr
-      errStrs <- forM msgs $ \msg -> do
-        shortStr <- doc $ errMsgShortDoc msg
-        contextStr <- doc $ errMsgExtraInfo msg
-        return $ unlines [shortStr, contextStr]
-
-      let fullErr = unlines errStrs
-
-      return EvalOut {
-        evalStatus = Failure,
-        evalResult = displayError fullErr,
-        evalState = state,
-        evalPager = "",
-        evalComms = []
-      }
-
-wrapExecution :: KernelState
-              -> Interpreter Display
-              -> Interpreter EvalOut
-wrapExecution state exec = safely state $ exec >>= \res ->
-    return EvalOut {
-      evalStatus = Success,
-      evalResult = res,
-      evalState = state,
-      evalPager = "",
-      evalComms = []
-    }
-
--- | Return the display data for this command, as well as whether it
--- resulted in an error.
-evalCommand :: Publisher -> CodeBlock -> KernelState -> Interpreter EvalOut
-evalCommand _ (Import importStr) state = wrapExecution state $ do
-  write $ "Import: " ++ importStr
-  evalImport importStr
-
-  -- Warn about `it` variable.
-  return $ if "Test.Hspec" `isInfixOf` importStr
-           then displayError $ "Warning: Hspec is unusable in IHaskell until the resolution of GHC bug #8639." ++
-                                "\nThe variable `it` is shadowed and cannot be accessed, even in qualified form."
-           else mempty
-
-evalCommand _ (Module contents) state = wrapExecution state $ do
-  write $ "Module:\n" ++ contents
-
-  -- Write the module contents to a temporary file in our work directory
-  namePieces <- getModuleName contents
-  liftIO (print namePieces)
-  let directory = "./" ++ intercalate "/" (init namePieces) ++ "/"
-      filename = last namePieces ++ ".hs"
-  liftIO $ do
-    createDirectoryIfMissing True directory
-    writeFile (fpFromString $ directory ++ filename) contents
-
-  -- Clear old modules of this name
-  let modName = intercalate "." namePieces
-  removeTarget $ TargetModule $ mkModuleName modName
-  removeTarget $ TargetFile filename Nothing
-
-  -- Remember which modules we've loaded before.
-  importedModules <- getContext
-
-  let -- Get the dot-delimited pieces of the module name.
-      moduleNameOf :: InteractiveImport -> [String]
-      moduleNameOf (IIDecl decl) = split "." . moduleNameString . unLoc . ideclName $ decl
-      moduleNameOf (IIModule imp) = split "." . moduleNameString $ imp
-
-      -- 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 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 the original *.hs fails and then fails.
-  case find preventsLoading importedModules of
-    -- If something prevents loading this module, return an error.
-    Just previous -> do
-      let prevLoaded = intercalate "." (moduleNameOf previous)
-      return $ displayError $
-        printf "Can't load module %s because already loaded %s" modName prevLoaded
-
-    -- Since nothing prevents loading the module, compile and load it.
-    Nothing -> doLoadModule modName modName
-
--- | Directives set via `:set`.
-evalCommand output (Directive SetDynFlag flags) state =
-  case words flags of
-    -- For a single flag.
-    [flag] -> do
-      write $ "DynFlags: " ++ flags
-
-      -- Check if this is setting kernel options.
-      case find (elem flag . getSetName) kernelOpts of
-        -- If this is a kernel option, just set it.
-        Just (KernelOpt _ _ updater) ->
-            return EvalOut {
-              evalStatus = Success,
-              evalResult = mempty,
-              evalState = updater state,
-              evalPager = "",
-              evalComms = []
-            }
-
-        -- If not a kernel option, must be a dyn flag.
-        Nothing -> do
-          errs <- setFlags [flag]
-          let display = case errs of
-                [] -> mempty
-                _ -> displayError $ intercalate "\n" errs
-
-          -- For -XNoImplicitPrelude, remove the Prelude import.
-          -- For -XImplicitPrelude, add it back in.
-          case flag of
-            "-XNoImplicitPrelude" ->
-              evalImport "import qualified Prelude as Prelude"
-            "-XImplicitPrelude" -> do
-              importDecl <- parseImportDecl "import Prelude"
-              let implicitPrelude = importDecl { ideclImplicit = True }
-              imports <- getContext
-              setContext $ IIDecl implicitPrelude : imports
-            _ -> return ()
-
-          return EvalOut {
-            evalStatus = Success,
-            evalResult = display,
-            evalState = state,
-            evalPager = "",
-            evalComms = []
-          }
-
-    -- Apply many flags.
-    flag:manyFlags -> do
-      firstEval <- evalCommand output (Directive SetDynFlag flag) state
-      case evalStatus firstEval of
-        Failure -> return firstEval
-        Success -> do
-          let newState = evalState firstEval
-              results  = evalResult firstEval
-          restEval <- evalCommand output (Directive SetDynFlag $ unwords manyFlags) newState
-          return restEval {
-            evalResult = results ++ evalResult restEval
-          }
-
-evalCommand output (Directive SetExtension opts) state = do
-  write $ "Extension: " ++ opts
-  let set = concatMap (" -X" ++) $ words opts
-  evalCommand output (Directive SetDynFlag set) state
-
-evalCommand output (Directive LoadModule mods) state = wrapExecution state $ do
-  write $ "Load Module: " ++ mods
-  let stripped@(firstChar:remainder) = mods
-      (modules, removeModule) =
-        case firstChar of
-          '+' -> (words remainder, False)
-          '-' -> (words remainder, True)
-          _ -> (words stripped, False)
-
-  forM_ modules $ \modl ->
-    if removeModule
-    then removeImport modl
-    else evalImport $ "import " ++ modl
-
-  return mempty
-
-evalCommand a (Directive SetOption opts) state = do
-  write $ "Option: " ++ opts
-  let (existing, nonExisting) = partition optionExists $ words opts
-  if not $ null nonExisting
-  then
-    let err = "No such options: " ++ intercalate ", " nonExisting in
-    return EvalOut {
-      evalStatus = Failure,
-      evalResult = displayError err,
-      evalState = state,
-      evalPager = "",
-      evalComms = []
-    }
-  else
-    let options = mapMaybe findOption $ words opts
-        updater = foldl' (.) id $ map getUpdateKernelState options in
-      return EvalOut {
-        evalStatus = Success,
-        evalResult = mempty,
-        evalState = updater state,
-        evalPager = "",
-        evalComms = []
-      }
-  where
-    optionExists = isJust . findOption
-    findOption opt =
-      find (elem opt . getOptionName) kernelOpts
-
-evalCommand _ (Directive GetType expr) state = wrapExecution state $ do
-  write $ "Type: " ++ expr
-  formatType <$> ((expr ++ " :: ") ++ ) <$> getType expr
-
-evalCommand _ (Directive GetKind expr) state = wrapExecution state $ do
-  write $ "Kind: " ++ expr
-  (_, kind) <- GHC.typeKind False expr
-  flags <- getSessionDynFlags
-  let typeStr = showSDocUnqual flags $ ppr kind
-  return $ formatType $ expr ++ " :: " ++ typeStr
-
-evalCommand _ (Directive LoadFile name) state = wrapExecution state $ do
-  write $ "Load: " ++ name
-
-  let filename = if endswith ".hs" name
-                 then name
-                 else name ++ ".hs"
-  contents <- readFile $ fpFromString filename
-  modName <- intercalate "." <$> getModuleName contents
-  doLoadModule filename modName
-
-evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
-  case words 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
-
-      let directory = replace "~" home $ unwords dirs
-      exists <- liftIO $ doesDirectoryExist directory
-      if exists
-      then do
-        -- Set the directory in IHaskell native code, for future shell
-        -- commands. This doesn't set it for user code, though.
-        liftIO $ setCurrentDirectory directory
-
-        -- Set the directory for user code.
-        let cmd = printf "IHaskellDirectory.setCurrentDirectory \"%s\"" $
-                  replace " " "\\ " $
-                  replace "\"" "\\\"" directory
-        runStmt cmd RunToCompletion
-        return mempty
-      else
-        return $ displayError $ printf "No such directory: '%s'" directory
-    cmd -> liftIO $ do
-      (readEnd, writeEnd) <- createPipe
-      handle <- fdToHandle writeEnd
-      pipe <- fdToHandle readEnd
-      let initProcSpec = shell $ unwords cmd
-          procSpec = initProcSpec {
-            std_in = Inherit,
-            std_out = UseHandle handle,
-            std_err = UseHandle handle
-          }
-      (_, _, _, process) <- createProcess procSpec
-
-      -- Accumulate output from the process.
-      outputAccum <- liftIO $ newMVar ""
-
-      -- Start a loop to publish intermediate results.
-      let
-        -- Compute how long to wait between reading pieces of the output.
-        -- `threadDelay` takes an argument of microseconds.
-        ms = 1000
-        delay = 100 * ms
-
-        -- Maximum size of the output (after which we truncate).
-        maxSize = 100 * 1000
-        incSize = 200
-        output str = publish $ IntermediateResult $ Display [plain str]
-
-        loop = do
-          -- Wait and then check if the computation is done.
-          threadDelay delay
-
-          -- Read next chunk and append to accumulator.
-          nextChunk <- readChars pipe "\n" incSize
-          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
-            -- Write to frontend and repeat.
-            readMVar outputAccum >>= output
-            loop
-          else do
-            out <- readMVar outputAccum
-            case fromJust exitCode of
-              ExitSuccess -> return $ Display [plain out]
-              ExitFailure code -> do
-                let errMsg = "Process exited with error code " ++ show code
-                    htmlErr = printf "<span class='err-msg'>%s</span>" errMsg
-                return $ Display [plain $ out ++ "\n" ++ errMsg,
-                                  html $ printf "<span class='mono'>%s</span>" out ++ htmlErr]
-
-      loop
-
-
--- This is taken largely from GHCi's info section in InteractiveUI.
-evalCommand _ (Directive GetHelp _) state = do
-  write "Help via :help or :?."
-  return EvalOut {
-    evalStatus = Success,
-    evalResult = Display [out],
-    evalState = state,
-    evalPager = "",
-    evalComms = []
-  }
-  where out = plain $ intercalate "\n"
-          ["The following commands are available:"
-          ,"    :extension <Extension>    -  Enable a GHC extension."
-          ,"    :extension No<Extension>  -  Disable a GHC extension."
-          ,"    :type <expression>        -  Print expression type."
-          ,"    :info <name>              -  Print all info for a name."
-          ,"    :hoogle <query>           -  Search for a query on Hoogle."
-          ,"    :doc <ident>              -  Get documentation for an identifier via Hogole."
-          ,"    :set -XFlag -Wall         -  Set an option (like ghci)."
-          ,"    :option <opt>             -  Set an option."
-          ,"    :option no-<opt>          -  Unset an option."
-          ,"    :?, :help                 -  Show this help text."
-          ,""
-          ,"Any prefix of the commands will also suffice, e.g. use :ty for :type."
-          ,""
-          ,"Options:"
-          ,"  lint          - enable or disable linting."
-          ,"  svg           - use svg output (cannot be resized)."
-          ,"  show-types    - show types of all bound names"
-          ,"  show-errors   - display Show instance missing errors normally."
-          ]
-
--- This is taken largely from GHCi's info section in InteractiveUI.
-evalCommand _ (Directive GetInfo str) state = safely state $ do
-  write $ "Info: " ++ str
-  -- Get all the info for all the names we're given.
-  strings <- getDescription str
-
-  let output = case getFrontend state of
-        IPythonConsole -> unlines strings
-        IPythonNotebook -> unlines (map htmlify strings)
-      htmlify str =
-        printf "<div style='background: rgb(247, 247, 247);'><form><textarea id='code'>%s</textarea></form></div>" str
-        ++ script
-      script =
-        "<script>CodeMirror.fromTextArea(document.getElementById('code'), {mode: 'haskell', readOnly: 'nocursor'});</script>"
-
-  return EvalOut {
-    evalStatus = Success,
-    evalResult = mempty,
-    evalState = state,
-    evalPager = output,
-    evalComms = []
-  }
-
-evalCommand _ (Directive SearchHoogle query) state = safely state $ do
-  results <- liftIO $ Hoogle.search query
-  return $ hoogleResults state results
-
-evalCommand _ (Directive GetDoc query) state = safely state $ do
-  results <- liftIO $ Hoogle.document query
-  return $ hoogleResults state results
-
-evalCommand output (Statement stmt) state = wrapExecution state $ do
-  write $ "Statement:\n" ++ stmt
-  let outputter str = output $ IntermediateResult $ Display [plain str]
-  (printed, result) <- capturedStatement outputter stmt
-  case result of
-    RunOk names -> do
-      dflags <- getSessionDynFlags
-
-      let allNames = map (showPpr dflags) names
-          isItName name =
-            name == "it" ||
-            name == "it" ++ show (getExecutionCounter state)
-          nonItNames = filter (not . isItName) allNames
-          output = [plain printed | not . null $ strip printed]
-
-      write $ "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
-      else do
-        -- Get all the type strings.
-        types <- forM nonItNames $ \name -> do
-          theType <- showSDocUnqual dflags . ppr <$> exprType name
-          return $ name ++ " :: " ++ theType
-
-        let joined = unlines types
-            htmled = unlines $ map formatGetType types
-
-        return $ case extractPlain output 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]
-
-    RunException exception -> throw exception
-    RunBreak{} -> error "Should not break."
-
-evalCommand output (Expression expr) state = do
-  write $ "Expression:\n" ++ expr
-
-  -- Try to use `display` to convert our type into the output
-  -- Dislay If typechecking fails and there is no appropriate
-  -- typeclass instance, this will throw an exception and thus `attempt` will
-  -- return False, and we just resort to plaintext.
-  let displayExpr = printf "(IHaskell.Display.display (%s))" expr :: String
-  canRunDisplay <- attempt $ exprType displayExpr
-
-  -- Check if this is a widget.
-  let widgetExpr = printf "(IHaskell.Display.Widget (%s))" expr :: String
-  isWidget <- attempt $ exprType widgetExpr
-
-  -- Check if this is a template haskell declaration 
-  let declExpr = printf "((id :: IHaskellTH.DecsQ -> IHaskellTH.DecsQ) (%s))" expr::String
-  let anyExpr = printf "((id :: IHaskellPrelude.Int -> IHaskellPrelude.Int) (%s))" expr :: String
-  isTHDeclaration <- liftM2 (&&) (attempt $ exprType declExpr) (not <$> attempt (exprType anyExpr))
-
-  write $ "Can Display: " ++ show canRunDisplay
-  write $ "Is Widget: " ++ show isWidget 
-  write $ "Is Declaration: " ++ show isTHDeclaration 
-
-  if isTHDeclaration 
-    -- If it typechecks as a DecsQ, we do not want to display the DecsQ, 
-    -- we just want the declaration made. 
-  then do 
-          write $ "Suppressing display for template haskell declaration"  
-          GHC.runDecls expr  
-          return EvalOut {
-                    evalStatus = Success,
-                    evalResult = mempty,  
-                    evalState = state,
-                    evalPager = "",
-                    evalComms = []
-                }
-  else do  
-      if canRunDisplay
-      then do
-        -- Use the display. As a result, `it` is set to the output.
-        out <- useDisplay displayExpr
-
-        -- Register the `it` object as a widget.
-        if isWidget
-        then registerWidget out
-        else return out
-      else do
-        -- Evaluate this expression as though it's just a statement.
-        -- The output is bound to 'it', so we can then use it.
-        evalOut <- evalCommand output (Statement expr) state
-
-        let out = evalResult evalOut
-            showErr = isShowError out
-
-        -- If evaluation failed, return the failure.  If it was successful, we
-        -- may be able to use the IHaskellDisplay typeclass.
-        return $ if not showErr || useShowErrors state
-                then evalOut
-                else postprocessShowError evalOut
-
-  where
-    -- Try to evaluate an action. Return True if it succeeds and False if
-    -- it throws an exception. The result of the action is discarded.
-    attempt :: Interpreter a -> Interpreter Bool
-    attempt action = gcatch (action >> return True) failure
-      where failure :: SomeException -> Interpreter Bool
-            failure _ = return False
-
-    -- Check if the error is due to trying to print something that doesn't
-    -- implement the Show typeclass.
-    isShowError (ManyDisplay _) = False
-    isShowError (Display errs) =
-        -- Note that we rely on this error message being 'type cleaned', so
-        -- that `Show` is not displayed as GHC.Show.Show.
-        startswith "No instance for (Show" msg &&
-        isInfixOf " arising from a use of `print'" msg
-      where msg = extractPlain errs
-
-    isSvg (DisplayData mime _) = mime == MimeSvg
-
-    removeSvg :: Display -> Display
-    removeSvg (Display disps) = Display $ filter (not . isSvg) disps
-    removeSvg (ManyDisplay disps) = ManyDisplay $ map removeSvg disps
-
-    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 binary and
-      -- back gives very strange errors - all the types match but it
-      -- refuses to decode back into a Display.
-      -- Suppress output, so as not to mess up console.
-
-      -- First, evaluate the expression in such a way that we have access to `it`.
-      io <- isIO expr
-      let stmtTemplate = if io
-                         then "it <- (%s)"
-                         else "let { it = %s }"
-      evalOut <- evalCommand output (Statement $ printf stmtTemplate expr) state
-      case evalStatus evalOut of
-        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
-
-          -- Convert from the bytestring into a display.
-          case fromDynamic displayedBytestring of
-            Nothing -> error "Expecting lazy Bytestring"
-            Just bytestringIO -> do
-              bytestring <- liftIO bytestringIO
-              case Serialize.decode bytestring of
-                Left err -> error err
-                Right display ->
-                  return $
-                    if useSvg state
-                    then display :: Display
-                    else removeSvg display
-
-    registerWidget :: EvalOut -> Ghc EvalOut
-    registerWidget evalOut =
-      case evalStatus evalOut of
-        Failure -> return evalOut
-        Success -> do
-          element <- dynCompileExpr "IHaskell.Display.Widget it"
-          case fromDynamic element of
-            Nothing -> error "Expecting widget"
-            Just widget -> do
-              -- Stick the widget in the kernel state.
-              uuid <- liftIO UUID.random
-              let state = evalState evalOut
-                  newComms = Map.insert uuid widget $ openComms state
-                  state' = state { openComms = newComms }
-
-              -- Store the fact that we should start this comm.
-              return evalOut {
-                evalComms = CommInfo widget uuid (targetName widget) : evalComms evalOut,
-                evalState = state'
-              }
-
-    isIO expr = attempt $ exprType $ printf "((\\x -> x) :: IO a -> IO a) (%s)" expr
-
-    postprocessShowError :: EvalOut -> EvalOut
-    postprocessShowError evalOut = evalOut { evalResult = Display $ map postprocess disps }
-      where
-        Display disps = evalResult evalOut
-        text = extractPlain disps
-
-        postprocess (DisplayData MimeHtml _) = html $ printf fmt unshowableType (formatErrorWithClass "err-msg collapse" text) script
-          where
-            fmt = "<div class='collapse-group'><span class='btn' href='#' id='unshowable'>Unshowable:<span class='show-type'>%s</span></span>%s</div><script>%s</script>"
-            script = unlines [
-                "$('#unshowable').on('click', function(e) {",
-                "    e.preventDefault();",
-                "    var $this = $(this);",
-                "    var $collapse = $this.closest('.collapse-group').find('.err-msg');",
-                "    $collapse.collapse('toggle');",
-                "});"
-              ]
-
-        postprocess other = other
-
-        unshowableType = fromMaybe "" $ do
-          let pieces = words text
-              before = takeWhile (/= "arising") pieces
-              after = init $ unwords $ tail $ dropWhile (/= "(Show") before
-
-          firstChar <- headMay after
-          return $ if firstChar == '('
-                   then init $ tail after
-                   else after
-
-
-
-evalCommand _ (Declaration decl) state = wrapExecution state $ do
-  write $ "Declaration:\n" ++ decl
-  boundNames <- evalDeclarations decl
-  let nonDataNames = filter (not . isUpper . head) boundNames
-
-  -- 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 mempty
-  else do
-    -- Get all the type strings.
-    dflags <- getSessionDynFlags
-    types <- forM nonDataNames $ \name -> do
-      theType <- showSDocUnqual dflags . ppr <$> exprType name
-      return $ name ++ " :: " ++ theType
-
-    return $ Display [html $ unlines $ map formatGetType types]
-
-evalCommand _ (TypeSignature sig) state = wrapExecution state $
-  -- We purposefully treat this as a "success" because that way execution
-  -- continues. Empty type signatures are likely due to a parse error later
-  -- on, and we want that to be displayed.
-  return $ displayError $ "The type signature " ++ sig ++
-                          "\nlacks an accompanying binding."
-
-evalCommand _ (ParseError loc err) state = do
-  write "Parse Error."
-  return EvalOut {
-    evalStatus = Failure,
-    evalResult = displayError $ formatParseError loc err,
-    evalState = state,
-    evalPager = "",
-    evalComms = []
-  }
-
-evalCommand _ (Pragma (PragmaUnsupported pragmaType) pragmas) state = wrapExecution state $
-  return $ displayError $ "Pragmas of type " ++ pragmaType ++
-                          "\nare not supported."
-
-evalCommand output (Pragma PragmaLanguage pragmas) state = do
-  write $ "Got LANGUAGE pragma " ++ show pragmas
-  evalCommand output (Directive SetExtension $ unwords pragmas) state
-
-hoogleResults :: KernelState -> [Hoogle.HoogleResult] -> EvalOut
-hoogleResults state results = EvalOut {
-    evalStatus = Success,
-    evalResult = mempty,
-    evalState = state,
-    evalPager = output,
-    evalComms = []
-  }
-  where
-    fmt =
-        case getFrontend state of
-          IPythonNotebook -> Hoogle.HTML
-          IPythonConsole -> Hoogle.Plain
-    output = unlines $ map (Hoogle.render fmt) results
-
--- 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
-
--- If we're done reading, return nothing.
-readChars handle delims 0 = return []
-
-readChars handle 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)
-  case tryRead of
-    Right char ->
-      -- If this is a delimiter, stop reading.
-      if char `elem` delims
-      then return [char]
-      else do
-        next <- readChars handle delims (nchars - 1)
-        return $ char:next
-    -- An error occurs at the end of the stream, so just stop reading.
-    Left _ -> return []
-
-
-doLoadModule :: String -> String -> Ghc Display
-doLoadModule name modName = do
-  -- Remember which modules we've loaded before.
-  importedModules <- getContext
-
-  flip gcatch (unload importedModules) $ do
-    -- Compile loaded modules.
-    flags <- getSessionDynFlags
-#if MIN_VERSION_ghc(7,8,0)
-    let objTarget = defaultObjectTarget platform
-        platform = targetPlatform flags
-#else
-    let objTarget = defaultObjectTarget
-#endif
-    setSessionDynFlags flags{ hscTarget = objTarget }
-
-    -- Clear old targets to be sure.
-    setTargets []
-    load LoadAllTargets
-
-    -- Load the new target.
-    target <- guessTarget name Nothing
-    addTarget target
-    result <- load LoadAllTargets
-
-    -- Reset the context, since loading things screws it up.
-    initializeItVariable
-
-    -- Add imports
-    importDecl <- parseImportDecl $ "import " ++ modName
-    let implicitImport = importDecl { ideclImplicit = True }
-    setContext $ IIDecl implicitImport : importedModules
-
-    -- Switch back to interpreted mode.
-    flags <- getSessionDynFlags
-    setSessionDynFlags flags{ hscTarget = HscInterpreted }
-
-    case result of
-      Succeeded -> return mempty
-      Failed -> return $ displayError $ "Failed to load module " ++ modName
-  where
-    unload :: [InteractiveImport] -> SomeException -> Ghc Display
-    unload imported exception = do
-      print $ show exception
-      -- Explicitly clear targets
-      setTargets []
-      load LoadAllTargets
-
-      -- Switch to interpreted mode!
-      flags <- getSessionDynFlags
-      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
-
-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 = runStmt s RunToCompletion
-      itVariable = var "it_var_temp_"
-
-  goStmt $ printf "let %s = it" itVariable
-  val <- act
-  goStmt $ printf "let it = %s" itVariable
-  act
-
-capturedStatement :: (String -> IO ())         -- ^ Function used to publish intermediate output.
-                  -> String                            -- ^ Statement to evaluate.
-                  -> Interpreter (String, RunResult)   -- ^ Return the output and result.
-capturedStatement output stmt = do
-  -- Generate random variable names to use so that we cannot accidentally
-  -- override the variables by using the right names in the terminal.
-  gen <- liftIO getStdGen
-  let
-    -- Variable names generation.
-    rand = take 20 $ randomRs ('0', '9') gen
-    var name = name ++ rand
-
-    -- Variables for the pipe input and outputs.
-    readVariable = var "file_read_var_"
-    writeVariable = var "file_write_var_"
-
-    -- Variable where to store old stdout.
-    oldVariable = var "old_var_"
-
-    -- Variable used to store true `it` value.
-    itVariable = var "it_var_"
-
-    voidpf str = printf $ str ++ " IHaskellPrelude.>> IHaskellPrelude.return ()"
-
-    -- Statements run before the thing we're evaluating.
-    initStmts =
-      [ printf "let %s = it" itVariable
-      , printf "(%s, %s) <- IHaskellIO.createPipe" readVariable writeVariable
-      , printf "%s <- IHaskellIO.dup IHaskellIO.stdOutput" oldVariable
-      , voidpf "IHaskellIO.dupTo %s IHaskellIO.stdOutput" writeVariable
-      , voidpf "IHaskellSysIO.hSetBuffering IHaskellSysIO.stdout IHaskellSysIO.NoBuffering"
-      , printf "let it = %s" itVariable
-      ]
-
-    -- Statements run after evaluation.
-    postStmts =
-      [ printf "let %s = it" itVariable
-      , voidpf "IHaskellSysIO.hFlush IHaskellSysIO.stdout"
-      , voidpf "IHaskellIO.dupTo %s IHaskellIO.stdOutput" oldVariable
-      , voidpf "IHaskellIO.closeFd %s" writeVariable
-      , printf "let it = %s" itVariable
-      ]
-    pipeExpr = printf "let %s = %s" (var "pipe_var_") readVariable
-
-    goStmt :: String -> Ghc RunResult
-    goStmt s = runStmt s RunToCompletion
-
-  -- Initialize evaluation context.
-  void $ forM initStmts goStmt
-
-  -- 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.
-  Just (_, hValues, _) <- withSession $ liftIO . flip hscStmt pipeExpr
-  -- Then convert the HValue into an executable bit, and read the value.
-  pipe <- liftIO $ do
-    fd <- head <$> unsafeCoerce hValues
-    fdToHandle fd
-
-  -- Read from a file handle until we hit a delimiter or until we've read
-  -- as many characters as requested
-  let
-    readChars :: Handle -> String -> Int -> IO String
-
-    -- If we're done reading, return nothing.
-    readChars handle delims 0 = return []
-
-    readChars handle 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)
-      case tryRead of
-        Right char ->
-          -- If this is a delimiter, stop reading.
-          if char `elem` delims
-          then return [char]
-          else do
-            next <- readChars handle delims (nchars - 1)
-            return $ char:next
-        -- An error occurs at the end of the stream, so just stop reading.
-        Left _ -> return []
-
-
-  -- Keep track of whether execution has completed.
-  completed <- liftIO $ newMVar False
-  finishedReading <- liftIO newEmptyMVar
-  outputAccum <- liftIO $ newMVar ""
-
-  -- Start a loop to publish intermediate results.
-  let
-    -- Compute how long to wait between reading pieces of the output.
-    -- `threadDelay` takes an argument of microseconds.
-    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
-
-    loop = do
-      -- Wait and then check if the computation is done.
-      threadDelay delay
-      computationDone <- readMVar completed
-
-      if not computationDone
-      then do
-        -- Read next chunk and append to accumulator.
-        nextChunk <- readChars pipe "\n" 100
-        modifyMVar_ outputAccum (return . (++ nextChunk))
-
-        -- Write to frontend and repeat.
-        readMVar outputAccum >>= output
-        loop
-      else do
-        -- Read remainder of output and accumulate it.
-        nextChunk <- readChars pipe "" maxSize
-        modifyMVar_ outputAccum (return . (++ nextChunk))
-
-        -- We're done reading.
-        putMVar finishedReading True
-
-  liftIO $ forkIO loop
-
-  result <- gfinally (goStmt stmt) $ do
-    -- Execution is done.
-    liftIO $ modifyMVar_ completed (const $ return True)
-
-    -- Finalize evaluation context.
-    void $ forM postStmts goStmt
-
-    -- Once context is finalized, reading can finish.
-    -- Wait for reading to finish to that the output accumulator is
-    -- completely filled.
-    liftIO $ takeMVar finishedReading
-
-  printedOutput <- liftIO $ readMVar outputAccum
-  return (printedOutput, result)
-
-formatError :: ErrMsg -> String
-formatError = formatErrorWithClass "err-msg"
-
-formatErrorWithClass :: String -> ErrMsg -> String
-formatErrorWithClass cls =
-    printf "<span class='%s'>%s</span>" cls .
-    replace "\n" "<br/>" .
-    replace useDashV "" .
-    replace "Ghci" "IHaskell" .
-    fixDollarSigns .
-    rstrip .
-    typeCleaner
-  where
-    fixDollarSigns = replace "$" "<span>$</span>"
-    useDashV = "\nUse -v to see a list of the files searched for."
-    isShowError err =
-      startswith "No instance for (Show" 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
-
-formatGetType :: String -> String
-formatGetType = printf "<span class='get-type'>%s</span>"
-
-formatType :: String -> Display
-formatType typeStr =  Display [plain typeStr, html $ formatGetType typeStr]
+{-# LANGUAGE DoAndIfThenElse, NoOverloadedStrings, TypeSynonymInstances, GADTs, CPP #-}
+
+{- | Description : Wrapper around GHC API, exposing a single `evaluate` interface that runs
+                   a statement, declaration, import, or directive.
+
+This module exports all functions used for evaluation of IHaskell input.
+-}
+module IHaskell.Eval.Evaluate (
+    interpret,
+    evaluate,
+    Interpreter,
+    liftIO,
+    typeCleaner,
+    globalImports,
+    ) where
+
+import           ClassyPrelude hiding (init, last, liftIO, head, hGetContents, tail, try)
+import           Control.Concurrent (forkIO, threadDelay)
+import           Prelude (putChar, head, tail, last, init, (!!))
+import           Data.List.Utils
+import           Data.List (findIndex, and, foldl1, nubBy)
+import           Data.String.Utils
+import           Text.Printf
+import           Data.Char as Char
+import           Data.Dynamic
+import           Data.Typeable
+import qualified Data.Serialize as Serialize
+import           System.Directory
+import           Filesystem.Path.CurrentOS (encodeString)
+#if !MIN_VERSION_base(4,8,0)
+import           System.Posix.IO (createPipe)
+#endif
+import           System.Posix.IO (fdToHandle)
+import           System.IO (hGetChar, hFlush)
+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           System.Environment (getEnv)
+import qualified Data.Map as Map
+
+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
+import           GhcMonad (liftIO, withSession)
+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           ErrUtils (errMsgShortDoc, errMsgExtraInfo)
+
+import qualified System.IO.Strict as StrictIO
+
+import           IHaskell.Types
+import           IHaskell.IPython
+import           IHaskell.Eval.Parser
+import           IHaskell.Eval.Lint
+import           IHaskell.Display
+import qualified IHaskell.Eval.Hoogle as Hoogle
+import           IHaskell.Eval.Util
+import           IHaskell.BrokenPackages
+import qualified IHaskell.IPython.Message.UUID as UUID
+
+import           Paths_ihaskell (version)
+import           Data.Version (versionBranch)
+
+data ErrorOccurred = Success
+                   | Failure
+  deriving (Show, Eq)
+
+-- | Set GHC's verbosity for debugging
+ghcVerbosity :: Maybe Int
+ghcVerbosity = Nothing -- Just 5
+
+ignoreTypePrefixes :: [String]
+ignoreTypePrefixes = [ "GHC.Types"
+                     , "GHC.Base"
+                     , "GHC.Show"
+                     , "System.IO"
+                     , "GHC.Float"
+                     , ":Interactive"
+                     , "GHC.Num"
+                     , "GHC.IO"
+                     , "GHC.Integer.Type"
+                     ]
+
+typeCleaner :: String -> String
+typeCleaner = useStringType . foldl' (.) id (map (`replace` "") fullPrefixes)
+  where
+    fullPrefixes = map (++ ".") ignoreTypePrefixes
+    useStringType = replace "[Char]" "String"
+
+write :: GhcMonad m => KernelState -> String -> m ()
+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
+globalImports :: [String]
+globalImports =
+  [ "import IHaskell.Display()"
+  , "import qualified Prelude as IHaskellPrelude"
+  , "import qualified System.Directory as IHaskellDirectory"
+  , "import qualified IHaskell.Display"
+  , "import qualified IHaskell.IPython.Stdin"
+  , "import qualified System.Posix.IO as IHaskellIO"
+  , "import qualified System.IO as IHaskellSysIO"
+  , "import qualified Language.Haskell.TH as IHaskellTH"
+  ]
+
+-- | 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
+-- environment.
+interpret :: String -> Bool -> Interpreter a -> IO a
+interpret libdir allowedStdin action = runGhc (Just libdir) $ do
+  -- If we're in a sandbox, add the relevant package database
+  sandboxPackages <- liftIO getSandboxPackageConf
+  initGhci sandboxPackages
+  case ghcVerbosity of
+    Just verb -> do
+      dflags <- getSessionDynFlags
+      void $ setSessionDynFlags $ dflags { verbosity = verb }
+    Nothing -> return ()
+
+  initializeImports
+
+  -- Close stdin so it can't be used. Otherwise it'll block the kernel forever.
+  dir <- liftIO getIHaskellDir
+  let cmd = printf "IHaskell.IPython.Stdin.fixStdin \"%s\"" dir
+  when allowedStdin $ void $
+    runStmt cmd RunToCompletion
+
+  initializeItVariable
+
+  -- Run the rest of the interpreter
+  action
+#if MIN_VERSION_ghc(7,10,0)
+packageIdString' dflags = packageKeyPackageIdString dflags
+#else
+packageIdString' dflags = packageIdString
+#endif
+-- | Initialize our GHC session with imports and a value for 'it'.
+initializeImports :: Interpreter ()
+initializeImports = do
+  -- Load packages that start with ihaskell-*, aren't just IHaskell, and depend directly on the right
+  -- version of the ihaskell library. Also verify that the packages we load are not broken.
+  dflags <- getSessionDynFlags
+  broken <- liftIO getBrokenPackages
+  displayPackages <- liftIO $ do
+                       (dflags, _) <- initPackages dflags
+                       let Just db = pkgDatabase dflags
+                           packageNames = map (packageIdString' dflags . packageConfigId) db
+
+                           initStr = "ihaskell-"
+
+                           -- Name of the ihaskell package, e.g. "ihaskell-1.2.3.4"
+                           iHaskellPkgName = initStr ++ intercalate "."
+                                                          (map show (versionBranch version))
+
+                           dependsOnRight pkg = not $ null $ do
+                             pkg <- db
+                             depId <- depends pkg
+                             dep <- filter ((== depId) . installedPackageId) db
+                             guard
+                               (iHaskellPkgName `isPrefixOf` packageIdString (packageConfigId dep))
+
+                           -- ideally the Paths_ihaskell module could provide a way to get the hash too
+                           -- (ihaskell-0.2.0.5-f2bce922fa881611f72dfc4a854353b9), for now. Things will end badly if you also
+                           -- happen to have an ihaskell-0.2.0.5-ce34eadc18cf2b28c8d338d0f3755502 installed.
+                           iHaskellPkg =
+                             case filter (== iHaskellPkgName) packageNames of
+                               [x] -> x
+                               [] -> error
+                                       ("cannot find required haskell library: " ++ iHaskellPkgName)
+                               _ -> error
+                                      ("multiple haskell packages " ++ iHaskellPkgName ++ " found")
+
+                           displayPkgs = [pkgName | pkgName <- packageNames
+                                                  , Just (x:_) <- [stripPrefix initStr pkgName]
+                                                  , pkgName `notElem` broken
+                                                  , isAlpha x]
+
+                       return displayPkgs
+
+  -- Generate import statements all Display modules.
+  let capitalize :: String -> String
+      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
+      toImportStmt = printf importFmt . concat . map capitalize . dropFirstAndLast . split "-"
+
+      displayImports = map toImportStmt displayPackages
+
+  -- Import implicit prelude.
+  importDecl <- parseImportDecl "import Prelude"
+  let implicitPrelude = importDecl { ideclImplicit = True }
+
+  -- Import modules.
+  imports <- mapM parseImportDecl $ globalImports ++ displayImports
+  setContext $ map IIDecl $ implicitPrelude : imports
+
+-- | Give a value for the `it` variable.
+initializeItVariable :: Interpreter ()
+initializeItVariable = do
+  -- This is required due to the way we handle `it` in the wrapper statements - if it doesn't exist,
+  -- the first statement will fail.
+  void $ runStmt "let it = ()" RunToCompletion
+
+-- | Publisher for IHaskell outputs. The first argument indicates whether this output is final
+-- (true) or intermediate (false).
+type Publisher = (EvaluationResult -> IO ())
+
+-- | Output of a command evaluation.
+data EvalOut =
+       EvalOut
+         { evalStatus :: ErrorOccurred
+         , evalResult :: Display
+         , evalState :: KernelState
+         , evalPager :: String
+         , evalComms :: [CommInfo]
+         }
+
+cleanString :: String -> String
+cleanString x = if allBrackets
+                  then clean
+                  else str
+  where
+    str = strip x
+    l = lines str
+    allBrackets = all (fAny [isPrefixOf ">", null]) l
+    fAny fs x = any ($x) fs
+    clean = unlines $ map removeBracket l
+    removeBracket ('>':xs) = xs
+    removeBracket [] = []
+    -- should never happen:
+    removeBracket other = error $ "Expected bracket as first char, but got string: " ++ other
+
+-- | Evaluate some IPython input code.
+evaluate :: KernelState                  -- ^ The kernel state.
+         -> String                       -- ^ Haskell code or other interpreter commands.
+         -> (EvaluationResult -> IO ())   -- ^ Function used to publish data outputs.
+         -> Interpreter KernelState
+evaluate kernelState code output = do
+  cmds <- parseString (cleanString code)
+  let execCount = getExecutionCounter kernelState
+
+  -- Extract all parse errors.
+  let justError x@ParseError{} = Just x
+      justError _ = Nothing
+      errs = mapMaybe (justError . unloc) cmds
+
+  updated <- case errs of
+               -- Only run things if there are no parse errors.
+               [] -> do
+                 when (getLintStatus kernelState /= LintOff) $ liftIO $ do
+                   lintSuggestions <- lint cmds
+                   unless (noResults lintSuggestions) $
+                     output $ FinalResult lintSuggestions "" []
+
+                 runUntilFailure kernelState (map unloc cmds ++ [storeItCommand execCount])
+               -- Print all parse errors.
+               errs -> do
+                 forM_ errs $ \err -> do
+                   out <- evalCommand output err kernelState
+                   liftIO $ output $ FinalResult (evalResult out) "" []
+                 return kernelState
+
+  return updated { getExecutionCounter = execCount + 1 }
+
+  where
+    noResults (Display res) = null res
+    noResults (ManyDisplay res) = all noResults res
+
+    runUntilFailure :: KernelState -> [CodeBlock] -> Interpreter KernelState
+    runUntilFailure state [] = return state
+    runUntilFailure state (cmd:rest) = do
+      evalOut <- evalCommand output cmd state
+
+      -- Get displayed channel outputs. Merge them with normal display outputs.
+      dispsIO <- extractValue "IHaskell.Display.displayFromChan"
+      dispsMay <- liftIO dispsIO
+      let result =
+            case dispsMay of
+              Nothing    -> evalResult evalOut
+              Just disps -> evalResult evalOut <> disps
+          helpStr = evalPager evalOut
+
+      -- Output things only if they are non-empty.
+      let empty = noResults result && null helpStr && null (evalComms evalOut)
+      unless empty $
+        liftIO $ output $ FinalResult result helpStr (evalComms evalOut)
+
+      -- Make sure to clear all comms we've started.
+      let newState = evalState evalOut { evalComms = [] }
+
+      case evalStatus evalOut of
+        Success -> runUntilFailure newState rest
+        Failure -> return newState
+
+    storeItCommand execCount = Statement $ printf "let it%d = it" execCount
+
+    extractValue :: Typeable a => String -> Interpreter a
+    extractValue expr = do
+      compiled <- dynCompileExpr expr
+      case fromDynamic compiled of
+        Nothing     -> error "Expecting value!"
+        Just result -> return result
+
+safely :: KernelState -> Interpreter EvalOut -> Interpreter EvalOut
+safely state = ghandle handler . ghandle sourceErrorHandler
+  where
+    handler :: SomeException -> Interpreter EvalOut
+    handler exception =
+      return
+        EvalOut
+          { evalStatus = Failure
+          , evalResult = displayError $ show exception
+          , evalState = state
+          , evalPager = ""
+          , evalComms = []
+          }
+
+    sourceErrorHandler :: SourceError -> Interpreter EvalOut
+    sourceErrorHandler srcerr = do
+      let msgs = bagToList $ srcErrorMessages srcerr
+      errStrs <- forM msgs $ \msg -> do
+                   shortStr <- doc $ errMsgShortDoc msg
+                   contextStr <- doc $ errMsgExtraInfo msg
+                   return $ unlines [shortStr, contextStr]
+
+      let fullErr = unlines errStrs
+
+      return
+        EvalOut
+          { evalStatus = Failure
+          , evalResult = displayError fullErr
+          , evalState = state
+          , evalPager = ""
+          , evalComms = []
+          }
+
+wrapExecution :: KernelState
+              -> Interpreter Display
+              -> Interpreter EvalOut
+wrapExecution state exec = safely state $
+  exec >>= \res ->
+    return
+      EvalOut
+        { evalStatus = Success
+        , evalResult = res
+        , evalState = state
+        , evalPager = ""
+        , evalComms = []
+        }
+
+-- | Return the display data for this command, as well as whether it resulted in an error.
+evalCommand :: Publisher -> CodeBlock -> KernelState -> Interpreter EvalOut
+evalCommand _ (Import importStr) state = wrapExecution state $ do
+  write state $ "Import: " ++ importStr
+  evalImport importStr
+
+  -- Warn about `it` variable.
+  return $ if "Test.Hspec" `isInfixOf` importStr
+             then displayError $ "Warning: Hspec is unusable in IHaskell until the resolution of GHC bug #8639." ++
+                                 "\nThe variable `it` is shadowed and cannot be accessed, even in qualified form."
+             else mempty
+
+evalCommand _ (Module contents) state = wrapExecution state $ do
+  write state $ "Module:\n" ++ contents
+
+  -- Write the module contents to a temporary file in our work directory
+  namePieces <- getModuleName contents
+  let directory = "./" ++ intercalate "/" (init namePieces) ++ "/"
+      filename = last namePieces ++ ".hs"
+  liftIO $ do
+    createDirectoryIfMissing True directory
+    writeFile (fpFromString $ directory ++ filename) contents
+
+  -- Clear old modules of this name
+  let modName = intercalate "." namePieces
+  removeTarget $ TargetModule $ mkModuleName modName
+  removeTarget $ TargetFile filename Nothing
+
+  -- Remember which modules we've loaded before.
+  importedModules <- getContext
+
+  let 
+      -- Get the dot-delimited pieces of the module name.
+      moduleNameOf :: InteractiveImport -> [String]
+      moduleNameOf (IIDecl decl) = split "." . moduleNameString . unLoc . ideclName $ decl
+      moduleNameOf (IIModule imp) = split "." . moduleNameString $ imp
+
+      -- 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
+        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
+  -- the original *.hs fails and then fails.
+  case find preventsLoading importedModules of
+    -- If something prevents loading this module, return an error.
+    Just previous -> do
+      let prevLoaded = intercalate "." (moduleNameOf previous)
+      return $ displayError $
+        printf "Can't load module %s because already loaded %s" modName prevLoaded
+
+    -- Since nothing prevents loading the module, compile and load it.
+    Nothing -> doLoadModule modName modName
+
+-- | Directives set via `:set`.
+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
+  let flags = words flagsStr
+
+      -- Get the kernel state updater for any IHaskell flag; Nothing for things that aren't IHaskell
+      -- flags.
+      ihaskellFlagUpdater :: String -> Maybe (KernelState -> KernelState)
+      ihaskellFlagUpdater flag = getUpdateKernelState <$> find (elem flag . getSetName) kernelOpts
+
+      (ihaskellFlags, ghcFlags) = partition (isJust . ihaskellFlagUpdater) flags
+
+  write state $ "IHaskell Flags: " ++ unwords ihaskellFlags
+  write state $ "GHC Flags: " ++ unwords ghcFlags
+
+  if null flags
+    then do
+      flags <- getSessionDynFlags
+      return
+        EvalOut
+          { evalStatus = Success
+          , evalResult = Display
+                           [ plain $ showSDoc flags $ vcat
+                                                        [ pprDynFlags False flags
+                                                        , pprLanguages False flags
+                                                        ]
+                           ]
+          , evalState = state
+          , evalPager = ""
+          , evalComms = []
+          }
+    else do
+      -- Apply all IHaskell flag updaters to the state to get the new state
+      let state' = (foldl' (.) id (map (fromJust . ihaskellFlagUpdater) ihaskellFlags)) state
+      errs <- setFlags ghcFlags
+      let display =
+            case errs of
+              [] -> mempty
+              _  -> displayError $ intercalate "\n" errs
+
+      -- For -XNoImplicitPrelude, remove the Prelude import. For -XImplicitPrelude, add it back in.
+      if "-XNoImplicitPrelude" `elem` flags
+        then evalImport "import qualified Prelude as Prelude"
+        else when ("-XImplicitPrelude" `elem` flags) $ do
+          importDecl <- parseImportDecl "import Prelude"
+          let implicitPrelude = importDecl { ideclImplicit = True }
+          imports <- getContext
+          setContext $ IIDecl implicitPrelude : imports
+
+      return
+        EvalOut
+          { evalStatus = Success
+          , evalResult = display
+          , evalState = state'
+          , evalPager = ""
+          , evalComms = []
+          }
+
+evalCommand output (Directive SetExtension opts) state = do
+  write state $ "Extension: " ++ opts
+  let set = concatMap (" -X" ++) $ words opts
+  evalCommand output (Directive SetDynFlag set) state
+
+evalCommand output (Directive LoadModule mods) state = wrapExecution state $ do
+  write state $ "Load Module: " ++ mods
+  let stripped@(firstChar:remainder) = mods
+      (modules, removeModule) =
+        case firstChar of
+          '+' -> (words remainder, False)
+          '-' -> (words remainder, True)
+          _   -> (words stripped, False)
+
+  forM_ modules $ \modl -> if removeModule
+                             then removeImport modl
+                             else evalImport $ "import " ++ modl
+
+  return mempty
+
+evalCommand a (Directive SetOption opts) state = do
+  write state $ "Option: " ++ opts
+  let (existing, nonExisting) = partition optionExists $ words opts
+  if not $ null nonExisting
+    then let err = "No such options: " ++ intercalate ", " nonExisting
+         in return
+              EvalOut
+                { evalStatus = Failure
+                , evalResult = displayError err
+                , evalState = state
+                , evalPager = ""
+                , evalComms = []
+                }
+    else let options = mapMaybe findOption $ words opts
+             updater = foldl' (.) id $ map getUpdateKernelState options
+         in return
+              EvalOut
+                { evalStatus = Success
+                , evalResult = mempty
+                , evalState = updater state
+                , evalPager = ""
+                , evalComms = []
+                }
+
+  where
+    optionExists = isJust . findOption
+    findOption opt =
+      find (elem opt . getOptionName) kernelOpts
+
+evalCommand _ (Directive GetType expr) state = wrapExecution state $ do
+  write state $ "Type: " ++ expr
+  formatType <$> ((expr ++ " :: ") ++) <$> getType expr
+
+evalCommand _ (Directive GetKind expr) state = wrapExecution state $ do
+  write state $ "Kind: " ++ expr
+  (_, kind) <- GHC.typeKind False expr
+  flags <- getSessionDynFlags
+  let typeStr = showSDocUnqual flags $ ppr kind
+  return $ formatType $ expr ++ " :: " ++ typeStr
+
+evalCommand _ (Directive LoadFile names) state = wrapExecution state $ do
+  write state $ "Load: " ++ names
+
+  displays <- forM (words names) $ \name -> do
+                let filename = if endswith ".hs" name
+                                 then name
+                                 else name ++ ".hs"
+                contents <- readFile $ fpFromString filename
+                modName <- intercalate "." <$> getModuleName contents
+                doLoadModule filename modName
+  return (ManyDisplay displays)
+
+evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
+  case words 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
+
+      let directory = replace "~" home $ unwords dirs
+      exists <- liftIO $ doesDirectoryExist directory
+      if exists
+        then do
+          -- Set the directory in IHaskell native code, for future shell commands. This doesn't set it for
+          -- user code, though.
+          liftIO $ setCurrentDirectory directory
+
+          -- Set the directory for user code.
+          let cmd = printf "IHaskellDirectory.setCurrentDirectory \"%s\"" $
+                replace " " "\\ " $
+                  replace "\"" "\\\"" directory
+          runStmt cmd RunToCompletion
+          return mempty
+        else return $ displayError $ printf "No such directory: '%s'" directory
+    cmd -> liftIO $ do
+      (pipe, handle) <- createPipe'
+      let initProcSpec = shell $ unwords cmd
+          procSpec = initProcSpec
+            { std_in = Inherit
+            , std_out = UseHandle handle
+            , std_err = UseHandle handle
+            }
+      (_, _, _, process) <- createProcess procSpec
+
+      -- Accumulate output from the process.
+      outputAccum <- liftIO $ newMVar ""
+
+      -- Start a loop to publish intermediate results.
+      let 
+          -- Compute how long to wait between reading pieces of the output. `threadDelay` takes an
+          -- argument of microseconds.
+          ms = 1000
+          delay = 100 * ms
+
+          -- Maximum size of the output (after which we truncate).
+          maxSize = 100 * 1000
+          incSize = 200
+          output str = publish $ IntermediateResult $ Display [plain str]
+
+          loop = do
+            -- Wait and then check if the computation is done.
+            threadDelay delay
+
+            -- Read next chunk and append to accumulator.
+            nextChunk <- readChars pipe "\n" incSize
+            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
+                -- Write to frontend and repeat.
+                readMVar outputAccum >>= output
+                loop
+              else do
+                out <- readMVar outputAccum
+                case fromJust exitCode of
+                  ExitSuccess -> return $ Display [plain out]
+                  ExitFailure code -> do
+                    let errMsg = "Process exited with error code " ++ show code
+                        htmlErr = printf "<span class='err-msg'>%s</span>" errMsg
+                    return $ Display
+                               [ plain $ out ++ "\n" ++ errMsg
+                               , html $ printf "<span class='mono'>%s</span>" out ++ htmlErr
+                               ]
+
+      loop
+      where 
+#if MIN_VERSION_base(4,8,0)
+        createPipe' = createPipe
+#else
+        createPipe' = do
+          (readEnd, writeEnd) <- createPipe
+          handle <- fdToHandle writeEnd
+          pipe <- fdToHandle readEnd
+          return (pipe, handle)
+#endif
+-- This is taken largely from GHCi's info section in InteractiveUI.
+evalCommand _ (Directive GetHelp _) state = do
+  write state "Help via :help or :?."
+  return
+    EvalOut
+      { evalStatus = Success
+      , evalResult = Display [out]
+      , evalState = state
+      , evalPager = ""
+      , evalComms = []
+      }
+
+  where
+    out = plain $ intercalate "\n"
+                    [ "The following commands are available:"
+                    , "    :extension <Extension>    -  Enable a GHC extension."
+                    , "    :extension No<Extension>  -  Disable a GHC extension."
+                    , "    :type <expression>        -  Print expression type."
+                    , "    :info <name>              -  Print all info for a name."
+                    , "    :hoogle <query>           -  Search for a query on Hoogle."
+                    , "    :doc <ident>              -  Get documentation for an identifier via Hogole."
+                    , "    :set -XFlag -Wall         -  Set an option (like ghci)."
+                    , "    :option <opt>             -  Set an option."
+                    , "    :option no-<opt>          -  Unset an option."
+                    , "    :?, :help                 -  Show this help text."
+                    , ""
+                    , "Any prefix of the commands will also suffice, e.g. use :ty for :type."
+                    , ""
+                    , "Options:"
+                    , "  lint        – enable or disable linting."
+                    , "  svg         – use svg output (cannot be resized)."
+                    , "  show-types  – show types of all bound names"
+                    , "  show-errors – display Show instance missing errors normally."
+                    , "  pager       – use the pager to display results of :info, :doc, :hoogle, etc."
+                    ]
+
+-- This is taken largely from GHCi's info section in InteractiveUI.
+evalCommand _ (Directive GetInfo str) state = safely state $ do
+  write state $ "Info: " ++ str
+  -- Get all the info for all the names we're given.
+  strings <- getDescription str
+
+  -- TODO: Make pager work without html by porting to newer architecture
+  let output = unlines (map htmlify strings)
+      htmlify str =
+        printf
+          "<div style='background: rgb(247, 247, 247);'><form><textarea id='code'>%s</textarea></form></div>"
+          str
+        ++ script
+      script =
+        "<script>CodeMirror.fromTextArea(document.getElementById('code'), {mode: 'haskell', readOnly: 'nocursor'});</script>"
+
+  return
+    EvalOut
+      { evalStatus = Success
+      , evalResult = mempty
+      , evalState = state
+      , evalPager = output
+      , evalComms = []
+      }
+
+evalCommand _ (Directive SearchHoogle query) state = safely state $ do
+  results <- liftIO $ Hoogle.search query
+  return $ hoogleResults state results
+
+evalCommand _ (Directive GetDoc query) state = safely state $ do
+  results <- liftIO $ Hoogle.document query
+  return $ hoogleResults state results
+
+evalCommand output (Statement stmt) state = wrapExecution state $ do
+  write state $ "Statement:\n" ++ stmt
+  let outputter str = output $ IntermediateResult $ Display [plain str]
+  (printed, result) <- capturedStatement outputter stmt
+  case result of
+    RunOk names -> do
+      dflags <- getSessionDynFlags
+
+      let allNames = map (showPpr dflags) names
+          isItName name =
+            name == "it" ||
+            name == "it" ++ show (getExecutionCounter state)
+          nonItNames = filter (not . isItName) allNames
+          output = [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
+        else do
+          -- Get all the type strings.
+          types <- forM nonItNames $ \name -> do
+                     theType <- showSDocUnqual dflags . ppr <$> exprType name
+                     return $ name ++ " :: " ++ theType
+
+          let joined = unlines types
+              htmled = unlines $ map formatGetType types
+
+          return $
+            case extractPlain output 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]
+
+    RunException exception -> throw exception
+    RunBreak{} -> error "Should not break."
+
+evalCommand output (Expression expr) state = do
+  write state $ "Expression:\n" ++ expr
+
+  -- Try to use `display` to convert our type into the output Dislay If typechecking fails and there
+  -- is no appropriate typeclass instance, this will throw an exception and thus `attempt` will return
+  -- False, and we just resort to plaintext.
+  let displayExpr = printf "(IHaskell.Display.display (%s))" expr :: String
+  canRunDisplay <- attempt $ exprType displayExpr
+
+  -- Check if this is a widget.
+  let widgetExpr = printf "(IHaskell.Display.Widget (%s))" expr :: String
+  isWidget <- attempt $ exprType widgetExpr
+
+  -- Check if this is a template haskell declaration 
+  let declExpr = printf "((id :: IHaskellTH.DecsQ -> IHaskellTH.DecsQ) (%s))" expr :: String
+  let anyExpr = printf "((id :: IHaskellPrelude.Int -> IHaskellPrelude.Int) (%s))" expr :: String
+  isTHDeclaration <- liftM2 (&&) (attempt $ exprType declExpr) (not <$> attempt (exprType anyExpr))
+
+  write state $ "Can Display: " ++ show canRunDisplay
+  write state $ "Is Widget: " ++ show isWidget
+  write state $ "Is Declaration: " ++ show isTHDeclaration
+
+  if isTHDeclaration
+    then 
+    -- 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
+      return
+        EvalOut
+          { evalStatus = Success
+          , evalResult = mempty
+          , evalState = state
+          , evalPager = ""
+          , evalComms = []
+          }
+    else do
+      if canRunDisplay
+        then do
+          -- Use the display. As a result, `it` is set to the output.
+          out <- useDisplay displayExpr
+
+          -- Register the `it` object as a widget.
+          if isWidget
+            then registerWidget out
+            else return out
+        else do
+          -- Evaluate this expression as though it's just a statement. The output is bound to 'it', so we can
+          -- then use it.
+          evalOut <- evalCommand output (Statement expr) state
+
+          let out = evalResult evalOut
+              showErr = isShowError out
+
+          -- If evaluation failed, return the failure. If it was successful, we may be able to use the
+          -- IHaskellDisplay typeclass.
+          return $ if not showErr || useShowErrors state
+                     then evalOut
+                     else postprocessShowError evalOut
+
+  where
+    -- Try to evaluate an action. Return True if it succeeds and False if it throws an exception. The
+    -- result of the action is discarded.
+    attempt :: Interpreter a -> Interpreter Bool
+    attempt action = gcatch (action >> return True) failure
+      where
+        failure :: SomeException -> Interpreter Bool
+        failure _ = return False
+
+    -- Check if the error is due to trying to print something that doesn't implement the Show typeclass.
+    isShowError (ManyDisplay _) = False
+    isShowError (Display errs) =
+      -- Note that we rely on this error message being 'type cleaned', so that `Show` is not displayed as
+      -- GHC.Show.Show. This is also very fragile!
+      startswith "No instance for (Show" msg &&
+      isInfixOf "print it" msg
+      where
+        msg = extractPlain errs
+
+    isSvg (DisplayData mime _) = mime == MimeSvg
+
+    removeSvg :: Display -> Display
+    removeSvg (Display disps) = Display $ filter (not . isSvg) disps
+    removeSvg (ManyDisplay disps) = ManyDisplay $ map removeSvg disps
+
+    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
+      -- binary and back gives very strange errors - all the types match but it refuses to decode back
+      -- into a Display. Suppress output, so as not to mess up console. First, evaluate the expression in
+      -- such a way that we have access to `it`.
+      io <- isIO expr
+      let stmtTemplate = if io
+                           then "it <- (%s)"
+                           else "let { it = %s }"
+      evalOut <- evalCommand output (Statement $ printf stmtTemplate expr) state
+      case evalStatus evalOut of
+        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
+
+          -- Convert from the bytestring into a display.
+          case fromDynamic displayedBytestring of
+            Nothing -> error "Expecting lazy Bytestring"
+            Just bytestringIO -> do
+              bytestring <- liftIO bytestringIO
+              case Serialize.decode bytestring of
+                Left err -> error err
+                Right display ->
+                  return $
+                    if useSvg state
+                      then display :: Display
+                      else removeSvg display
+
+    registerWidget :: EvalOut -> Ghc EvalOut
+    registerWidget evalOut =
+      case evalStatus evalOut of
+        Failure -> return evalOut
+        Success -> do
+          element <- dynCompileExpr "IHaskell.Display.Widget it"
+          case fromDynamic element of
+            Nothing -> error "Expecting widget"
+            Just widget -> do
+              -- Stick the widget in the kernel state.
+              uuid <- liftIO UUID.random
+              let state = evalState evalOut
+                  newComms = Map.insert uuid widget $ openComms state
+                  state' = state { openComms = newComms }
+
+              -- Store the fact that we should start this comm.
+              return
+                evalOut
+                  { evalComms = CommInfo widget uuid (targetName widget) : evalComms evalOut
+                  , evalState = state'
+                  }
+
+    isIO expr = attempt $ exprType $ printf "((\\x -> x) :: IO a -> IO a) (%s)" expr
+
+    postprocessShowError :: EvalOut -> EvalOut
+    postprocessShowError evalOut = evalOut { evalResult = Display $ map postprocess disps }
+      where
+        Display disps = evalResult evalOut
+        text = extractPlain disps
+
+        postprocess (DisplayData MimeHtml _) = html $ printf
+                                                        fmt
+                                                        unshowableType
+                                                        (formatErrorWithClass "err-msg collapse"
+                                                           text)
+                                                        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
+                       [ "$('#unshowable').on('click', function(e) {"
+                       , "    e.preventDefault();"
+                       , "    var $this = $(this);"
+                       , "    var $collapse = $this.closest('.collapse-group').find('.err-msg');"
+                       , "    $collapse.collapse('toggle');"
+                       , "});"
+                       ]
+
+        postprocess other = other
+
+        unshowableType = fromMaybe "" $ do
+          let pieces = words text
+              before = takeWhile (/= "arising") pieces
+              after = init $ unwords $ tail $ dropWhile (/= "(Show") before
+
+          firstChar <- headMay after
+          return $ if firstChar == '('
+                     then init $ tail after
+                     else after
+
+
+evalCommand _ (Declaration decl) state = wrapExecution state $ do
+  write state $ "Declaration:\n" ++ decl
+  boundNames <- evalDeclarations decl
+  let nonDataNames = filter (not . isUpper . head) boundNames
+
+  -- 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 mempty
+    else do
+      -- Get all the type strings.
+      dflags <- getSessionDynFlags
+      types <- forM nonDataNames $ \name -> do
+                 theType <- showSDocUnqual dflags . ppr <$> exprType name
+                 return $ name ++ " :: " ++ theType
+
+      return $ Display [html $ unlines $ map formatGetType types]
+
+evalCommand _ (TypeSignature sig) state = wrapExecution state $
+  -- We purposefully treat this as a "success" because that way execution continues. Empty type
+  -- signatures are likely due to a parse error later on, and we want that to be displayed.
+  return $ displayError $ "The type signature " ++ sig ++ "\nlacks an accompanying binding."
+
+evalCommand _ (ParseError loc err) state = do
+  write state "Parse Error."
+  return
+    EvalOut
+      { evalStatus = Failure
+      , evalResult = displayError $ formatParseError loc err
+      , evalState = state
+      , evalPager = ""
+      , evalComms = []
+      }
+
+evalCommand _ (Pragma (PragmaUnsupported pragmaType) pragmas) state = wrapExecution state $
+  return $ displayError $ "Pragmas of type " ++ pragmaType ++ "\nare not supported."
+
+evalCommand output (Pragma PragmaLanguage pragmas) state = do
+  write state $ "Got LANGUAGE pragma " ++ show pragmas
+  evalCommand output (Directive SetExtension $ unwords pragmas) state
+
+hoogleResults :: KernelState -> [Hoogle.HoogleResult] -> EvalOut
+hoogleResults state results =
+  EvalOut
+    { evalStatus = Success
+    , evalResult = mempty
+    , evalState = state
+    , evalPager = output
+    , evalComms = []
+    }
+  where
+    -- TODO: Make pager work with plaintext
+    fmt = Hoogle.HTML
+    output = unlines $ map (Hoogle.render fmt) results
+
+doLoadModule :: String -> String -> Ghc Display
+doLoadModule name modName = do
+  -- Remember which modules we've loaded before.
+  importedModules <- getContext
+
+  flip gcatch (unload importedModules) $ do
+    -- Compile loaded modules.
+    flags <- getSessionDynFlags
+    errRef <- liftIO $ newIORef []
+    setSessionDynFlags
+      flags
+        { hscTarget = objTarget flags
+        , log_action = \dflags sev srcspan ppr msg -> modifyIORef errRef (showSDoc flags msg :)
+        }
+
+    -- Load the new target.
+    target <- guessTarget name Nothing
+    oldTargets <- getTargets
+    -- Add a target, but make sure targets are unique!
+    addTarget target
+    getTargets >>= return . (nubBy ((==) `on` targetId)) >>= setTargets
+    result <- load LoadAllTargets
+
+    -- Reset the context, since loading things screws it up.
+    initializeItVariable
+
+    -- Reset targets if we failed.
+    case result of
+      Failed      -> setTargets oldTargets
+      Succeeded{} -> return ()
+
+    -- Add imports
+    setContext $
+      case result of
+        Failed    -> importedModules
+        Succeeded -> IIDecl (simpleImportDecl $ mkModuleName modName) : importedModules
+
+    -- Switch back to interpreted mode.
+    setSessionDynFlags flags
+
+    case result of
+      Succeeded -> return mempty
+      Failed -> do
+        errorStrs <- unlines <$> reverse <$> liftIO (readIORef errRef)
+        return $ displayError $ "Failed to load module " ++ modName ++ "\n" ++ errorStrs
+
+  where
+    unload :: [InteractiveImport] -> SomeException -> Ghc Display
+    unload imported exception = do
+      print $ show exception
+      -- Explicitly clear targets
+      setTargets []
+      load LoadAllTargets
+
+      -- Switch to interpreted mode!
+      flags <- getSessionDynFlags
+      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 = runStmt s RunToCompletion
+      itVariable = var "it_var_temp_"
+
+  goStmt $ printf "let %s = it" itVariable
+  val <- act
+  goStmt $ printf "let it = %s" itVariable
+  act
+
+capturedStatement :: (String -> IO ())         -- ^ Function used to publish intermediate output.
+                  -> String                            -- ^ Statement to evaluate.
+                  -> Interpreter (String, RunResult)   -- ^ Return the output and result.
+capturedStatement output stmt = do
+  -- Generate random variable names to use so that we cannot accidentally override the variables by
+  -- using the right names in the terminal.
+  gen <- liftIO getStdGen
+  let 
+      -- Variable names generation.
+      rand = take 20 $ randomRs ('0', '9') gen
+      var name = name ++ rand
+
+      -- Variables for the pipe input and outputs.
+      readVariable = var "file_read_var_"
+      writeVariable = var "file_write_var_"
+
+      -- Variable where to store old stdout.
+      oldVariable = var "old_var_"
+
+      -- Variable used to store true `it` value.
+      itVariable = var "it_var_"
+
+      voidpf str = printf $ str ++ " IHaskellPrelude.>> IHaskellPrelude.return ()"
+
+      -- Statements run before the thing we're evaluating.
+      initStmts =
+        [ printf "let %s = it" itVariable
+        , printf "(%s, %s) <- IHaskellIO.createPipe" readVariable writeVariable
+        , printf "%s <- IHaskellIO.dup IHaskellIO.stdOutput" oldVariable
+        , voidpf "IHaskellIO.dupTo %s IHaskellIO.stdOutput" writeVariable
+        , voidpf "IHaskellSysIO.hSetBuffering IHaskellSysIO.stdout IHaskellSysIO.NoBuffering"
+        , printf "let it = %s" itVariable
+        ]
+
+      -- Statements run after evaluation.
+      postStmts =
+        [ printf "let %s = it" itVariable
+        , voidpf "IHaskellSysIO.hFlush IHaskellSysIO.stdout"
+        , voidpf "IHaskellIO.dupTo %s IHaskellIO.stdOutput" oldVariable
+        , voidpf "IHaskellIO.closeFd %s" writeVariable
+        , printf "let it = %s" itVariable
+        ]
+      pipeExpr = printf "let %s = %s" (var "pipe_var_") readVariable
+
+      goStmt :: String -> Ghc RunResult
+      goStmt s = runStmt s RunToCompletion
+
+  -- Initialize evaluation context.
+  void $ forM initStmts goStmt
+
+  -- 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.
+  Just (_, hValues, _) <- withSession $ liftIO . flip hscStmt pipeExpr
+  -- Then convert the HValue into an executable bit, and read the value.
+  pipe <- liftIO $ do
+            fd <- head <$> unsafeCoerce hValues
+            fdToHandle fd
+
+
+  -- Keep track of whether execution has completed.
+  completed <- liftIO $ newMVar False
+  finishedReading <- liftIO newEmptyMVar
+  outputAccum <- liftIO $ newMVar ""
+
+  -- Start a loop to publish intermediate results.
+  let 
+      -- Compute how long to wait between reading pieces of the output. `threadDelay` takes an
+      -- argument of microseconds.
+      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
+
+      loop = do
+        -- Wait and then check if the computation is done.
+        threadDelay delay
+        computationDone <- readMVar completed
+
+        if not computationDone
+          then do
+            -- Read next chunk and append to accumulator.
+            nextChunk <- readChars pipe "\n" 100
+            modifyMVar_ outputAccum (return . (++ nextChunk))
+
+            -- Write to frontend and repeat.
+            readMVar outputAccum >>= output
+            loop
+          else do
+            -- Read remainder of output and accumulate it.
+            nextChunk <- readChars pipe "" maxSize
+            modifyMVar_ outputAccum (return . (++ nextChunk))
+
+            -- We're done reading.
+            putMVar finishedReading True
+
+  liftIO $ forkIO loop
+
+  result <- gfinally (goStmt stmt) $ do
+              -- Execution is done.
+              liftIO $ modifyMVar_ completed (const $ return True)
+
+              -- Finalize evaluation context.
+              void $ forM postStmts goStmt
+
+              -- Once context is finalized, reading can finish. Wait for reading to finish to that the output
+              -- accumulator is completely filled.
+              liftIO $ takeMVar finishedReading
+
+  printedOutput <- liftIO $ readMVar outputAccum
+  return (printedOutput, result)
+
+-- 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 =
+  -- If we're done reading, return nothing.
+  return []
+readChars handle 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)
+  case tryRead of
+    Right char ->
+      -- If this is a delimiter, stop reading.
+      if char `elem` delims
+        then return [char]
+        else do
+          next <- readChars handle delims (nchars - 1)
+          return $ char : next
+    -- An error occurs at the end of the stream, so just stop reading.
+    Left _ -> return []
+
+formatError :: ErrMsg -> String
+formatError = formatErrorWithClass "err-msg"
+
+formatErrorWithClass :: String -> ErrMsg -> String
+formatErrorWithClass cls =
+  printf "<span class='%s'>%s</span>" cls .
+  replace "\n" "<br/>" .
+  replace useDashV "" .
+  replace "Ghci" "IHaskell" .
+  replace "‘interactive:" "‘" .
+  fixDollarSigns .
+  rstrip .
+  typeCleaner
+  where
+    fixDollarSigns = replace "$" "<span>$</span>"
+    useDashV = "\nUse -v to see a list of the files searched for."
+    isShowError err =
+      startswith "No instance for (Show" 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
+
+formatGetType :: String -> String
+formatGetType = printf "<span class='get-type'>%s</span>"
+
+formatType :: String -> Display
+formatType typeStr = Display [plain typeStr, html $ formatGetType typeStr]
 
 displayError :: ErrMsg -> Display
 displayError msg = Display [plain . typeCleaner $ msg, html $ formatError msg]
diff --git a/src/IHaskell/Eval/Hoogle.hs b/src/IHaskell/Eval/Hoogle.hs
--- a/src/IHaskell/Eval/Hoogle.hs
+++ b/src/IHaskell/Eval/Hoogle.hs
@@ -1,41 +1,38 @@
 {-# LANGUAGE NoImplicitPrelude, FlexibleInstances, OverloadedStrings #-}
+
 module IHaskell.Eval.Hoogle (
-  search,
-  document,
-  render,
-  OutputFormat(..),
-  HoogleResult
-  ) where
+    search,
+    document,
+    render,
+    OutputFormat(..),
+    HoogleResult,
+    ) where
 
-import ClassyPrelude hiding (last, span, div)
-import Text.Printf
-import Network.HTTP.Client
-import Network.HTTP.Client.TLS
-import Data.Aeson
-import Data.String.Utils
-import Data.List (elemIndex, (!!), last)
+import           ClassyPrelude hiding (last, span, div)
+import           Text.Printf
+import           Network.HTTP.Client
+import           Network.HTTP.Client.TLS
+import           Data.Aeson
+import           Data.String.Utils
+import           Data.List (elemIndex, (!!), last)
+import           Data.Char (isAscii, isAlphaNum)
 import qualified Data.ByteString.Lazy.Char8 as Char
+import qualified Prelude as P
 
 
-import IHaskell.IPython
+import           IHaskell.IPython
 
 -- | Types of formats to render output to.
-data OutputFormat
-     = Plain      -- ^ Render to plain text.
-     | HTML       -- ^ Render to HTML.
+data OutputFormat = Plain      -- ^ Render to plain text.
+                  | HTML       -- ^ Render to HTML.
 
-data HoogleResponse = HoogleResponse {
-    location :: String,
-    self :: String,
-    docs :: String
-  }
+data HoogleResponse = HoogleResponse { location :: String, self :: String, docs :: String }
   deriving (Eq, Show)
 
-data HoogleResult
-     = SearchResult HoogleResponse
-     | DocResult HoogleResponse
-     | NoResult String
-     deriving Show
+data HoogleResult = SearchResult HoogleResponse
+                  | DocResult HoogleResponse
+                  | NoResult String
+  deriving Show
 
 instance FromJSON [HoogleResponse] where
   parseJSON (Object obj) = do
@@ -46,57 +43,80 @@
 
 instance FromJSON HoogleResponse where
   parseJSON (Object obj) =
-    HoogleResponse      <$>
-    obj .: "location" <*>
-    obj .: "self"     <*>
-    obj .: "docs"
+    HoogleResponse <$> obj .: "location" <*> obj .: "self" <*> obj .: "docs"
 
   parseJSON _ = fail "Expected object with fields: location, self, docs"
 
--- | Query Hoogle for the given string.
--- This searches Hoogle using the internet. It returns either an error
--- message or the successful JSON result.
+-- | Query Hoogle for the given string. This searches Hoogle using the internet. It returns either
+-- an error message or the successful JSON result.
 query :: String -> IO (Either String String)
 query str = do
-  request <- parseUrl $ queryUrl str
+  request <- parseUrl $ queryUrl $ urlEncode str
   response <- try $ withManager tlsManagerSettings $ httpLbs request
-  return $ case response of
-    Left err -> Left $ show (err :: SomeException)
-    Right resp -> Right $ Char.unpack $ responseBody resp
+  return $
+    case response of
+      Left err   -> Left $ show (err :: SomeException)
+      Right resp -> Right $ Char.unpack $ responseBody resp
+
   where
     queryUrl :: String -> String
     queryUrl = printf "https://www.haskell.org/hoogle/?hoogle=%s&mode=json"
 
--- | Search for a query on Hoogle.
--- Return all search results.
+-- | Copied from the HTTP package.
+urlEncode :: String -> String
+urlEncode [] = []
+urlEncode (ch:t)
+  | (isAscii ch && isAlphaNum ch) || ch `P.elem` "-_.~" = ch : urlEncode t
+  | not (isAscii ch) = P.foldr escape (urlEncode t) (eightBs [] (P.fromEnum ch))
+  | otherwise = escape (P.fromEnum ch) (urlEncode t)
+  where
+    escape :: Int -> String -> String
+    escape b rs = '%' : showH (b `P.div` 16) (showH (b `mod` 16) rs)
+
+    showH :: Int -> String -> String
+    showH x xs
+      | x <= 9 = toEnum (o_0 + x) : xs
+      | otherwise = toEnum (o_A + (x - 10)) : xs
+      where
+        o_0 = P.fromEnum '0'
+        o_A = P.fromEnum 'A'
+
+    eightBs :: [Int] -> Int -> [Int]
+    eightBs acc x
+      | x <= 255 = x : acc
+      | otherwise = eightBs ((x `mod` 256) : acc) (x `P.div` 256)
+
+-- | 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 $ Char.pack json of
-        Left err -> [NoResult err]
-        Right results ->
-          case map SearchResult results of
-            [] -> [NoResult "no matching identifiers found."]
-            res -> res
+  return $
+    case response of
+      Left err -> [NoResult err]
+      Right json ->
+        case eitherDecode $ Char.pack json of
+          Left err -> [NoResult err]
+          Right results ->
+            case map SearchResult results of
+              []  -> [NoResult "no matching identifiers found."]
+              res -> res
 
--- | Look up an identifier on Hoogle.
--- Return documentation for that identifier. If there are many
+-- | 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
-    [] -> [NoResult "no matching identifiers found."]
-    res -> res
+  return $
+    case results of
+      []  -> [NoResult "no matching identifiers found."]
+      res -> res
+
   where
     matches (SearchResult resp) =
       case split " " $ self resp of
         name:_ -> strip string == strip name
-        _ -> False
+        _      -> False
     matches _ = False
 
     toDocResult (SearchResult resp) = DocResult resp
@@ -104,25 +124,18 @@
 -- | 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
-
 renderPlain (NoResult res) =
   "No response available: " ++ res
 
 renderPlain (SearchResult resp) =
-  printf "%s\nURL: %s\n%s"
-  (self resp)
-  (location resp)
-  (docs resp)
+  printf "%s\nURL: %s\n%s" (self resp) (location resp) (docs resp)
 
 renderPlain (DocResult resp) =
-  printf "%s\nURL: %s\n%s"
-  (self resp)
-  (location resp)
-  (docs resp)
+  printf "%s\nURL: %s\n%s" (self resp) (location resp) (docs resp)
 
 -- | Render a Hoogle result to HTML.
 renderHtml :: HoogleResult -> String
@@ -141,37 +154,37 @@
 
 renderSelf :: String -> String -> String
 renderSelf string loc
-  | startswith "package" string
-    = pkg ++ " " ++ span "hoogle-package" (link loc $ extractPackage string)
+  | startswith "package" string =
+      pkg ++ " " ++ span "hoogle-package" (link loc $ extractPackage string)
 
-  | startswith "module" string
-    = let package = extractPackageName loc in
-        mod ++ " " ++
-        span "hoogle-module" (link loc $ extractModule string) ++
-        packageSub package
+  | startswith "module" string =
+      let package = extractPackageName loc
+      in mod ++ " " ++
+                span "hoogle-module" (link loc $ extractModule string) ++
+                packageSub package
 
-  | startswith "class" string
-    = let package = extractPackageName loc in
-        cls ++ " " ++
-        span "hoogle-class" (link loc $ extractClass string) ++
-        packageSub package
+  | startswith "class" string =
+      let package = extractPackageName loc
+      in cls ++ " " ++
+                span "hoogle-class" (link loc $ extractClass string) ++
+                packageSub package
 
-  | startswith "data" string
-    = let package = extractPackageName loc in
-        dat ++ " " ++
-        span "hoogle-class" (link loc $ extractData string) ++
-        packageSub package
+  | startswith "data" string =
+      let package = extractPackageName loc
+      in dat ++ " " ++
+                span "hoogle-class" (link loc $ extractData string) ++
+                packageSub package
 
-  | otherwise
-    = let [name, args] = split "::" string
+  | otherwise =
+      let [name, args] = split "::" string
           package = extractPackageName loc
-          modname = extractModuleName loc in
-        span "hoogle-name" (unicodeReplace $
-          link loc (strip name) ++
-          " :: " ++
-          strip args)
-        ++ packageAndModuleSub package modname
-
+          modname = extractModuleName loc
+      in span "hoogle-name"
+           (unicodeReplace $
+              link loc (strip name) ++
+              " :: " ++
+              strip args)
+         ++ packageAndModuleSub package modname
   where
     extractPackage = strip . replace "package" ""
     extractModule = strip . replace "module" ""
@@ -184,10 +197,10 @@
 
     unicodeReplace :: String -> String
     unicodeReplace =
-     replace "forall" "&#x2200;" .
-     replace "=>"     "&#x21D2;" .
-     replace "->"     "&#x2192;" .
-     replace "::"     "&#x2237;"
+      replace "forall" "&#x2200;" .
+      replace "=>" "&#x21D2;" .
+      replace "->" "&#x2192;" .
+      replace "::" "&#x2237;"
 
     packageSub Nothing = ""
     packageSub (Just package) =
@@ -197,26 +210,25 @@
     packageAndModuleSub Nothing _ = ""
     packageAndModuleSub (Just package) Nothing = packageSub (Just package)
     packageAndModuleSub (Just package) (Just modname) =
-        span "hoogle-sub" $
-          "(" ++ pkg ++ " " ++ span "hoogle-package" package ++
-           ", " ++ mod ++ " " ++ span "hoogle-module" modname ++ ")"
+      span "hoogle-sub" $
+        "(" ++ pkg ++ " " ++ span "hoogle-package" package ++
+                             ", " ++ mod ++ " " ++ span "hoogle-module" modname ++ ")"
 
 renderDocs :: String -> String
 renderDocs doc =
   let groups = groupBy bothAreCode $ lines doc
       nonull = filter (not . null . strip)
       bothAreCode s1 s2 =
-        startswith ">" (strip s1) &&
-        startswith ">" (strip s2)
+                           startswith ">" (strip s1) &&
+                           startswith ">" (strip s2)
       isCode (s:_) = startswith ">" $ 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
+                         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
 
-extractPackageName :: String ->  Maybe String
+extractPackageName :: String -> Maybe String
 extractPackageName link = do
   let pieces = split "/" link
   archiveLoc <- elemIndex "archive" pieces
@@ -224,7 +236,7 @@
   guard $ latestLoc - archiveLoc == 2
   return $ pieces !! (latestLoc - 1)
 
-extractModuleName :: String ->  Maybe String
+extractModuleName :: String -> Maybe String
 extractModuleName link = do
   let pieces = split "/" link
   guard $ not $ null pieces
diff --git a/src/IHaskell/Eval/Info.hs b/src/IHaskell/Eval/Info.hs
--- a/src/IHaskell/Eval/Info.hs
+++ b/src/IHaskell/Eval/Info.hs
@@ -1,23 +1,21 @@
 {-# LANGUAGE NoImplicitPrelude, OverloadedStrings #-}
-{- | Description : Inspect type and function information and documentation.
--}
-module IHaskell.Eval.Info (
-  info
-  ) where
 
-import ClassyPrelude hiding (liftIO)
+{- | Description : Inspect type and function information and documentation.  -}
+module IHaskell.Eval.Info (info) where
 
-import IHaskell.Eval.Evaluate (typeCleaner, Interpreter)
+import           ClassyPrelude hiding (liftIO)
 
-import GHC
-import Outputable
-import Exception
+import           IHaskell.Eval.Evaluate (typeCleaner, Interpreter)
 
+import           GHC
+import           Outputable
+import           Exception
+
 info :: String -> Interpreter String
 info name = ghandle handler $ do
   dflags <- getSessionDynFlags
   result <- exprType name
-  return $ typeCleaner $ showPpr dflags result 
-  where 
+  return $ typeCleaner $ showPpr dflags result
+  where
     handler :: SomeException -> Interpreter String
     handler _ = return ""
diff --git a/src/IHaskell/Eval/Lint.hs b/src/IHaskell/Eval/Lint.hs
--- a/src/IHaskell/Eval/Lint.hs
+++ b/src/IHaskell/Eval/Lint.hs
@@ -1,45 +1,44 @@
 {-# LANGUAGE NoImplicitPrelude, QuasiQuotes, ViewPatterns #-}
-module IHaskell.Eval.Lint ( 
-  lint
-  ) where
 
-import Data.String.Utils (replace, startswith, strip, split)
-import Prelude (head, tail, last)
-import ClassyPrelude hiding (last)
-import Control.Monad
-import Data.List (findIndex)
-import Text.Printf
-import Data.String.Here
-import Data.Char
-import Data.Monoid
-import Data.Maybe (mapMaybe)
-import System.IO.Unsafe (unsafePerformIO)
+module IHaskell.Eval.Lint (lint) where
 
-import Language.Haskell.Exts.Annotated.Syntax hiding (Module)
+import           Data.String.Utils (replace, startswith, strip, split)
+import           Prelude (head, tail, last)
+import           ClassyPrelude hiding (last)
+import           Control.Monad
+import           Data.List (findIndex)
+import           Text.Printf
+import           Data.String.Here
+import           Data.Char
+import           Data.Monoid
+import           Data.Maybe (mapMaybe)
+import           System.IO.Unsafe (unsafePerformIO)
+
+import           Language.Haskell.Exts.Annotated.Syntax hiding (Module)
 import qualified Language.Haskell.Exts.Annotated.Syntax as SrcExts
-import Language.Haskell.Exts.Annotated (parseFileContentsWithMode)
-import Language.Haskell.Exts.Annotated.Build (doE)
-import Language.Haskell.Exts.Annotated hiding (Module)
-import Language.Haskell.Exts.SrcLoc
+import           Language.Haskell.Exts.Annotated (parseFileContentsWithMode)
+import           Language.Haskell.Exts.Annotated.Build (doE)
+import           Language.Haskell.Exts.Annotated hiding (Module)
+import           Language.Haskell.Exts.SrcLoc
 
-import Language.Haskell.HLint as HLint
-import Language.Haskell.HLint2
+import           Language.Haskell.HLint as HLint
+import           Language.Haskell.HLint2
 
-import IHaskell.Types
-import IHaskell.Display
-import IHaskell.IPython
-import IHaskell.Eval.Parser hiding (line)
+import           IHaskell.Types
+import           IHaskell.Display
+import           IHaskell.IPython
+import           IHaskell.Eval.Parser hiding (line)
 
 type ExtsModule = SrcExts.Module SrcSpanInfo
 
-data LintSuggestion
-  = Suggest {
-    line :: LineNumber,
-    found :: String,
-    whyNot :: String,
-    severity :: Severity,
-    suggestion :: String
-  }
+data LintSuggestion =
+       Suggest
+         { line :: LineNumber
+         , found :: String
+         , whyNot :: String
+         , severity :: Severity
+         , suggestion :: String
+         }
   deriving (Eq, Show)
 
 -- Store settings for Hlint once it's initialized.
@@ -51,8 +50,8 @@
 lintIdent :: String
 lintIdent = "lintIdentAEjlkQeh"
 
--- | Given parsed code chunks, perform linting and output a displayable
--- report on linting warnings and errors.
+-- | Given parsed code chunks, perform linting and output a displayable report on linting warnings
+-- and errors.
 lint :: [Located CodeBlock] -> IO Display
 lint blocks = do
   -- Initialize hlint settings
@@ -66,63 +65,62 @@
 
   -- create 'suggestions'
   let modules = mapMaybe (createModule mode) blocks
-      ideas = applyHints classify hint (map (\m->(m,[])) modules)
+      ideas = applyHints classify hint (map (\m -> (m, [])) modules)
       suggestions = mapMaybe showIdea ideas
 
   return $ Display $
     if null suggestions
-    then []
-    else 
-      [plain $ concatMap plainSuggestion suggestions,
-       html $ htmlSuggestions suggestions]
+      then []
+      else [plain $ concatMap plainSuggestion suggestions, html $ htmlSuggestions suggestions]
 
 showIdea :: Idea -> Maybe LintSuggestion
-showIdea idea = 
+showIdea idea =
   case ideaTo idea of
     Nothing -> Nothing
-    Just whyNot -> Just Suggest {
-      line = srcSpanStartLine $ ideaSpan idea,
-      found = showSuggestion $ ideaFrom idea,
-      whyNot = showSuggestion whyNot,
-      severity = ideaSeverity idea,
-      suggestion = ideaHint idea
-  }
+    Just whyNot -> Just
+                     Suggest
+                       { line = srcSpanStartLine $ ideaSpan idea
+                       , found = showSuggestion $ ideaFrom idea
+                       , whyNot = showSuggestion whyNot
+                       , severity = ideaSeverity idea
+                       , suggestion = ideaHint idea
+                       }
 
 createModule :: ParseMode -> Located CodeBlock -> Maybe ExtsModule
-createModule mode (Located line block) = 
+createModule mode (Located line 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
-    _ -> Nothing
+    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
+    _                -> Nothing
   where
-    blockStr = 
+    blockStr =
       case block of
-        Expression expr   -> expr
-        Declaration decl  -> decl 
-        Statement stmt    -> stmt
-        Import impt       -> impt
-        Module mod        -> mod
+        Expression expr  -> expr
+        Declaration decl -> decl
+        Statement stmt   -> stmt
+        Import impt      -> impt
+        Module mod       -> mod
 
     unparse :: ParseResult a -> Maybe a
     unparse (ParseOk a) = Just a
     unparse _ = Nothing
 
     srcSpan :: SrcSpan
-    srcSpan = SrcSpan {
-      srcSpanFilename = "<interactive>",
-      srcSpanStartLine = line,
-      srcSpanStartColumn = 0,
-      srcSpanEndLine = line + length (lines blockStr),
-      srcSpanEndColumn = length $ last $ lines blockStr
-    }
+    srcSpan = SrcSpan
+      { srcSpanFilename = "<interactive>"
+      , srcSpanStartLine = line
+      , srcSpanStartColumn = 0
+      , srcSpanEndLine = line + length (lines blockStr)
+      , srcSpanEndColumn = length $ last $ lines blockStr
+      }
 
     loc :: SrcSpanInfo
     loc = SrcSpanInfo srcSpan []
 
-    moduleWithDecls :: Decl SrcSpanInfo -> ExtsModule 
+    moduleWithDecls :: Decl SrcSpanInfo -> ExtsModule
     moduleWithDecls decl = SrcExts.Module loc Nothing [] [] [decl]
 
     parseModule :: String -> ParseResult ExtsModule
@@ -135,9 +133,10 @@
     exprToModule exp = moduleWithDecls <$> SpliceDecl loc <$> parseExpWithMode mode exp
 
     stmtToModule :: String -> ParseResult ExtsModule
-    stmtToModule stmtStr = case parseStmtWithMode mode stmtStr of
-      ParseOk stmt -> ParseOk mod
-      ParseFailed a b -> ParseFailed a b
+    stmtToModule stmtStr =
+      case parseStmtWithMode mode stmtStr of
+        ParseOk stmt    -> ParseOk mod
+        ParseFailed a b -> ParseFailed a b
       where
         mod = moduleWithDecls decl
 
@@ -157,35 +156,31 @@
     imptToModule = parseFileContentsWithMode mode
 
 plainSuggestion :: LintSuggestion -> String
-plainSuggestion suggest = 
-  printf "Line %d: %s\nFound:\n%s\nWhy not:\n%s"
-    (line suggest)
-    (suggestion suggest)
-    (found suggest)
+plainSuggestion suggest =
+  printf "Line %d: %s\nFound:\n%s\nWhy not:\n%s" (line suggest) (suggestion suggest) (found suggest)
     (whyNot suggest)
 
 htmlSuggestions :: [LintSuggestion] -> String
-htmlSuggestions = concatMap toHtml 
+htmlSuggestions = concatMap toHtml
   where
     toHtml :: LintSuggestion -> String
-    toHtml suggest = concat 
-      [
-      named $ suggestion suggest,
-      floating "left" $ style severityClass "Found:" ++
-             -- Things that look like this get highlighted.
-             styleId "highlight-code" "haskell" (found suggest),
-      floating "left" $ style severityClass "Why Not:" ++
-             -- Things that look like this get highlighted.
-             styleId "highlight-code" "haskell" (whyNot suggest)
-      ]
-
+    toHtml suggest = concat
+                       [ named $ suggestion suggest
+                       , floating "left" $ style severityClass "Found:" ++
+                                           -- Things that look like this get highlighted.
+                                           styleId "highlight-code" "haskell" (found suggest)
+                       , floating "left" $ style severityClass "Why Not:" ++
+                                           -- Things that look like this get highlighted.
+                                           styleId "highlight-code" "haskell" (whyNot suggest)
+                       ]
       where
-        severityClass = case severity suggest of
-          Error -> "error"
-          Warning -> "warning"
+        severityClass =
+          case severity suggest of
+            Error -> "error"
+            Warning -> "warning"
 
-          -- Should not occur
-          _ -> "warning"
+            -- Should not occur
+            _ -> "warning"
 
     style :: String -> String -> String
     style cls thing = [i| <div class="suggestion-${cls}">${thing}</div> |]
@@ -195,37 +190,32 @@
 
     styleId :: String -> String -> String -> String
     styleId cls id thing = [i| <div class="${cls}" id="${id}">${thing}</div> |]
-    
+
     floating :: String -> String -> String
     floating dir thing = [i| <div class="suggestion-row" style="float: ${dir};">${thing}</div> |]
 
-
 showSuggestion :: String -> String
-showSuggestion = remove lintIdent .  dropDo
+showSuggestion = remove lintIdent . dropDo
   where
     remove str = replace str ""
 
     -- Drop leading '  do ', and blank spaces following.
     dropDo :: String -> String
-    dropDo string = 
+    dropDo string =
       -- If this is not a statement, we don't need to drop the do statement.
       if lintIdent `isInfixOf` string
-      then unlines . clean . lines $ string
-      else string
+        then unlines . clean . lines $ string
+        else string
 
     clean :: [String] -> [String]
-    -- If the first line starts with a `do`...
-    -- Note that hlint always indents by two spaces in its output.
-    clean ((stripPrefix "  do " -> Just a) : as) =
-        -- Take all indented lines and unindent them.
-        let unindented = catMaybes
-                $ takeWhile isJust
-                $ map (stripPrefix "     ") as
-            fullDo = a:unindented
-            afterDo = drop (length unindented) as
-        in 
-          -- 
-          fullDo ++ clean afterDo
+    -- If the first line starts with a `do`... Note that hlint always indents by two spaces in its
+    -- output.
+    clean ((stripPrefix "  do " -> Just a):as) =
+      -- Take all indented lines and unindent them.
+      let unindented = catMaybes $ takeWhile isJust $ map (stripPrefix "     ") as
+          fullDo = a : unindented
+          afterDo = drop (length unindented) as
+      in fullDo ++ clean afterDo
 
     -- Ignore other list elements - just proceed onwards.
     clean (x:xs) = x : clean xs
diff --git a/src/IHaskell/Eval/ParseShell.hs b/src/IHaskell/Eval/ParseShell.hs
--- a/src/IHaskell/Eval/ParseShell.hs
+++ b/src/IHaskell/Eval/ParseShell.hs
@@ -1,16 +1,15 @@
-
 -- | This module splits a shell command line into a list of strings,
 --   one for each command / filename
-module IHaskell.Eval.ParseShell (parseShell) where 
+module IHaskell.Eval.ParseShell (parseShell) where
 
-import Prelude hiding (words)
-import Text.ParserCombinators.Parsec hiding (manyTill)
-import Control.Applicative hiding ((<|>), many, optional)
+import           Prelude hiding (words)
+import           Text.ParserCombinators.Parsec hiding (manyTill)
+import           Control.Applicative hiding ((<|>), many, optional)
 
 eol :: Parser Char
 eol = oneOf "\n\r" <?> "end of line"
 
-quote :: Parser Char 
+quote :: Parser Char
 quote = char '\"'
 
 -- | @manyTill p end@ from hidden @manyTill@ in that it appends the result of @end@
@@ -18,16 +17,17 @@
 manyTill p end = scan
   where
     scan = end <|> do
-      x <- p
-      xs <- scan
-      return $ x:xs
+             x <- p
+             xs <- scan
+             return $ x : xs
 
-manyTill1 p end = do x <- p 
-                     xs <- manyTill p end 
-                     return $ x : xs
+manyTill1 p end = do
+  x <- p
+  xs <- manyTill p end
+  return $ x : xs
 
-unescapedChar :: Parser Char -> Parser String 
-unescapedChar p = try $ do 
+unescapedChar :: Parser Char -> Parser String
+unescapedChar p = try $ do
   x <- noneOf "\\"
   lookAhead p
   return [x]
@@ -36,8 +36,9 @@
   quote <?> "expected starting quote"
   (manyTill anyChar (unescapedChar quote) <* quote) <?> "unexpected in quoted String "
 
-unquotedString = manyTill1 anyChar end  
-  where end = unescapedChar space 
+unquotedString = manyTill1 anyChar end
+  where
+    end = unescapedChar space
           <|> (lookAhead eol >> return [])
 
 word = quotedString <|> unquotedString <?> "word"
@@ -48,12 +49,12 @@
 -- | Input must terminate in a space character (like a \n)
 words :: Parser [String]
 words = try (eof *> return []) <|> do
-  x <-  word 
-  rest1 <- lookAhead (many anyToken)
-  ss <-  separator 
-  rest2 <-  lookAhead (many anyToken)
-  xs <-  words 
-  return $ x : xs 
+          x <- word
+          rest1 <- lookAhead (many anyToken)
+          ss <- separator
+          rest2 <- lookAhead (many anyToken)
+          xs <- words
+          return $ x : xs
 
 parseShell :: String -> Either ParseError [String]
 parseShell string = parse words "shell" (string ++ "\n")
diff --git a/src/IHaskell/Eval/Parser.hs b/src/IHaskell/Eval/Parser.hs
--- a/src/IHaskell/Eval/Parser.hs
+++ b/src/IHaskell/Eval/Parser.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE NoImplicitPrelude, OverloadedStrings #-}
+
 module IHaskell.Eval.Parser (
     parseString,
     CodeBlock(..),
@@ -14,67 +15,55 @@
     PragmaType(..),
     ) where
 
--- Hide 'unlines' to use our own 'joinLines' instead.
-import ClassyPrelude hiding (head, tail, liftIO, unlines, maximumBy)
+import           ClassyPrelude hiding (head, liftIO, maximumBy)
 
-import Data.List (findIndex, maximumBy, maximum, inits)
-import Data.String.Utils (startswith, strip, split)
-import Data.List.Utils (subIndex)
-import Prelude (init, last, head, tail)
-import Control.Monad (msum)
+import           Data.List (maximumBy, inits)
+import           Data.String.Utils (startswith, strip, split)
+import           Prelude (head, tail)
+import           Control.Monad (msum)
 
-import Bag
-import ErrUtils hiding (ErrMsg)
-import FastString
-import GHC hiding (Located)
-import GhcMonad
-import Lexer
-import OrdList
-import Outputable hiding ((<>))
-import SrcLoc hiding (Located)
-import StringBuffer
+import           GHC hiding (Located)
 
-import Language.Haskell.GHC.Parser
-import IHaskell.Eval.Util
+import           Language.Haskell.GHC.Parser
+import           IHaskell.Eval.Util
 
--- | A block of code to be evaluated.
--- Each block contains a single element - one declaration, statement,
--- expression, etc. If parsing of the block failed, the block is instead
--- a ParseError, which has the error location and error message.
-data CodeBlock
-  = Expression String                  -- ^ A Haskell expression.
-  | Declaration String                 -- ^ A data type or function declaration.
-  | Statement String                   -- ^ A Haskell statement (as if in a `do` block).
-  | Import String                      -- ^ An import statement.
-  | TypeSignature String               -- ^ A lonely type signature (not above a function declaration).
-  | Directive DirectiveType String     -- ^ An IHaskell directive.
-  | Module String                      -- ^ A full Haskell module, to be compiled and loaded.
-  | ParseError StringLoc ErrMsg        -- ^ An error indicating that parsing the code block failed.
-  | Pragma PragmaType [String]         -- ^ A list of GHC pragmas (from a {-# LANGUAGE ... #-} block)
+-- | A block of code to be evaluated. Each block contains a single element - one declaration,
+-- statement, expression, etc. If parsing of the block failed, the block is instead a ParseError,
+-- which has the error location and error message.
+data CodeBlock = Expression String              -- ^ A Haskell expression.
+               | Declaration String             -- ^ A data type or function declaration.
+               | Statement String               -- ^ A Haskell statement (as if in a `do` block).
+               | Import String                  -- ^ An import statement.
+               | TypeSignature String           -- ^ A lonely type signature (not above a function
+                                                -- declaration).
+               | Directive DirectiveType String -- ^ An IHaskell directive.
+               | Module String                  -- ^ A full Haskell module, to be compiled and loaded.
+               | ParseError StringLoc ErrMsg    -- ^ An error indicating that parsing the code block
+                                                -- failed.
+               | Pragma PragmaType [String]     -- ^ A list of GHC pragmas (from a {-# LANGUAGE ... #-}
+                                                -- block)
   deriving (Show, Eq)
 
--- | Directive types. Each directive is associated with a string in the
--- directive code block.
-data DirectiveType
-  = GetType         -- ^ Get the type of an expression via ':type' (or unique prefixes)
-  | GetInfo         -- ^ Get info about the identifier via ':info' (or unique prefixes)
-  | SetDynFlag      -- ^ Enable or disable an extensions, packages etc. via `:set`. Emulates GHCi's `:set`
-  | LoadFile        -- ^ Load a Haskell module.
-  | SetOption       -- ^ Set IHaskell kernel option `:option`.
-  | SetExtension    -- ^ `:extension Foo` is a shortcut for `:set -XFoo`
-  | ShellCmd        -- ^ Execute a shell command.
-  | GetHelp         -- ^ General help via ':?' or ':help'.
-  | SearchHoogle    -- ^ Search for something via Hoogle.
-  | GetDoc          -- ^ Get documentation for an identifier via Hoogle.
-  | GetKind         -- ^ Get the kind of a type via ':kind'.
-  | LoadModule      -- ^ Load and unload modules via ':module'.
+-- | Directive types. Each directive is associated with a string in the directive code block.
+data DirectiveType = GetType      -- ^ Get the type of an expression via ':type' (or unique prefixes)
+                   | GetInfo      -- ^ Get info about the identifier via ':info' (or unique prefixes)
+                   | SetDynFlag   -- ^ Enable or disable an extensions, packages etc. via `:set`.
+                                  -- Emulates GHCi's `:set`
+                   | LoadFile     -- ^ Load a Haskell module.
+                   | SetOption    -- ^ Set IHaskell kernel option `:option`.
+                   | SetExtension -- ^ `:extension Foo` is a shortcut for `:set -XFoo`
+                   | ShellCmd     -- ^ Execute a shell command.
+                   | GetHelp      -- ^ General help via ':?' or ':help'.
+                   | SearchHoogle -- ^ Search for something via Hoogle.
+                   | GetDoc       -- ^ Get documentation for an identifier via Hoogle.
+                   | GetKind      -- ^ Get the kind of a type via ':kind'.
+                   | LoadModule   -- ^ Load and unload modules via ':module'.
   deriving (Show, Eq)
 
--- | Pragma types. Only LANGUAGE pragmas are currently supported.
--- Other pragma types are kept around as a string for error reporting.
-data PragmaType
-  = PragmaLanguage
-  | PragmaUnsupported String
+-- | Pragma types. Only LANGUAGE pragmas are currently supported. Other pragma types are kept around
+-- as a string for error reporting.
+data PragmaType = PragmaLanguage
+                | PragmaUnsupported String
   deriving (Show, Eq)
 
 -- | Parse a string into code blocks.
@@ -84,17 +73,18 @@
   flags <- getSessionDynFlags
   let output = runParser flags parserModule codeString
   case output of
-    Parsed {} -> return [Located 1 $ Module codeString]
-    Failure {} -> do
+    Parsed mod
+      | Just _ <- hsmodName (unLoc mod) -> return [Located 1 $ Module codeString]
+    _ -> do
       -- Split input into chunks based on indentation.
       let chunks = layoutChunks $ removeComments codeString
       result <- joinFunctions <$> processChunks [] chunks
 
-      -- Return to previous flags. When parsing, flags can be set to make
-      -- sure parsing works properly. But we don't want those flags to be
-      -- set during evaluation until the right time.
-      setSessionDynFlags flags
+      -- Return to previous flags. When parsing, flags can be set to make sure parsing works properly. But
+      -- we don't want those flags to be set during evaluation until the right time.
+      _ <- setSessionDynFlags flags
       return result
+
   where
     parseChunk :: GhcMonad m => String -> LineNumber -> m (Located CodeBlock)
     parseChunk chunk line = Located line <$> handleChunk chunk line
@@ -111,7 +101,7 @@
         [] -> return $ reverse accum
 
         -- If we have more remaining, parse the current chunk and recurse.
-        Located line chunk:remaining ->  do
+        Located line chunk:remaining -> do
           block <- parseChunk chunk line
           activateExtensions $ unloc block
           processChunks (block : accum) remaining
@@ -124,16 +114,12 @@
     isPragma :: String -> Bool
     isPragma = startswith "{-#" . strip
 
-    -- Number of lines in this string.
-    nlines :: String -> Int
-    nlines = length . lines
-
 activateExtensions :: GhcMonad m => CodeBlock -> m ()
 activateExtensions (Directive SetExtension ext) = void $ setExtension ext
 activateExtensions (Directive SetDynFlag flags) =
   case stripPrefix "-X" flags of
     Just ext -> void $ setExtension ext
-    Nothing -> return ()
+    Nothing  -> return ()
 activateExtensions (Pragma PragmaLanguage extensions) = void $ setAll extensions
   where
     setAll :: GhcMonad m => [String] -> m (Maybe String)
@@ -145,20 +131,21 @@
 -- | Parse a single chunk of code, as indicated by the layout of the code.
 parseCodeChunk :: GhcMonad m => String -> LineNumber -> m CodeBlock
 parseCodeChunk code startLine = do
-    flags <- getSessionDynFlags
-    let
-        -- Try each parser in turn.
-        rawResults = map (tryParser code) (parsers flags)
+  flags <- getSessionDynFlags
+  let 
+      -- Try each parser in turn.
+      rawResults = map (tryParser code) (parsers flags)
 
-        -- Convert statements into expressions where we can
-        results = map (statementToExpression flags) rawResults in
-      case successes results of
-        -- If none of them succeeded, choose the best error message to
-        -- display. Only one of the error messages is actually relevant.
-        [] -> return $ bestError $ failures results
+      -- Convert statements into expressions where we can
+      results = map (statementToExpression flags) rawResults
+  case successes results of
+    -- If none of them succeeded, choose the best error message to display. Only one of the error
+    -- messages is actually relevant.
+    [] -> return $ bestError $ failures results
 
-        -- If one of the parsers succeeded
-        result:_ -> return result
+    -- If one of the parsers succeeded
+    result:_ -> return result
+
   where
     successes :: [ParseOutput a] -> [a]
     successes [] = []
@@ -178,46 +165,50 @@
 
     statementToExpression :: DynFlags -> ParseOutput CodeBlock -> ParseOutput CodeBlock
     statementToExpression flags (Parsed (Statement stmt)) = Parsed result
-      where result = if isExpr flags stmt
-                     then Expression stmt
-                     else Statement stmt
+      where
+        result = if isExpr flags stmt
+                   then Expression stmt
+                   else Statement stmt
     statementToExpression _ other = other
 
     -- Check whether a string is a valid expression.
     isExpr :: DynFlags -> String -> Bool
-    isExpr flags str = case runParser flags parserExpression str of
-      Parsed {} -> True
-      _ -> False
+    isExpr flags str =
+      case runParser flags parserExpression str of
+        Parsed{} -> True
+        _        -> False
 
     tryParser :: String -> (String -> CodeBlock, String -> ParseOutput String) -> ParseOutput CodeBlock
-    tryParser string (blockType, parser) = case parser string of
-      Parsed res -> Parsed (blockType res)
-      Failure err loc -> Failure err loc
+    tryParser string (blockType, parser) =
+      case parser string of
+        Parsed res      -> Parsed (blockType res)
+        Failure err loc -> Failure err loc
+        otherwise       -> error "tryParser failed, output was neither Parsed nor Failure"
 
     parsers :: DynFlags -> [(String -> CodeBlock, String -> ParseOutput String)]
     parsers flags =
-      [ (Import,        unparser parserImport)
+      [ (Import, unparser parserImport)
       , (TypeSignature, unparser parserTypeSignature)
-      , (Statement,     unparser parserStatement)
-      , (Declaration,   unparser parserDeclaration)
+      , (Statement, unparser parserStatement)
+      , (Declaration, unparser parserDeclaration)
       ]
       where
         unparser :: Parser a -> String -> ParseOutput String
         unparser parser code =
           case runParser flags parser code of
-            Parsed out -> Parsed code
+            Parsed out       -> Parsed code
             Partial out strs -> Partial code strs
-            Failure err loc -> Failure err loc
+            Failure err loc  -> Failure err loc
 
--- | Find consecutive declarations of the same function and join them into
--- a single declaration. These declarations may also include a type
--- signature, which is also joined with the subsequent declarations.
+-- | Find consecutive declarations of the same function and join them into a single declaration.
+-- These declarations may also include a type signature, which is also joined with the subsequent
+-- declarations.
 joinFunctions :: [Located CodeBlock] -> [Located CodeBlock]
 joinFunctions [] = []
 joinFunctions blocks =
   if signatureOrDecl $ unloc $ head blocks
-  then Located lnum (conjoin $ map unloc decls) : joinFunctions rest
-  else head blocks : joinFunctions (tail blocks)
+    then Located lnum (conjoin $ map unloc decls) : joinFunctions rest
+    else head blocks : joinFunctions (tail blocks)
   where
     decls = takeWhile (signatureOrDecl . unloc) blocks
     rest = drop (length decls) blocks
@@ -234,7 +225,6 @@
     conjoin :: [CodeBlock] -> CodeBlock
     conjoin = Declaration . intercalate "\n" . map str
 
-
 -- | Parse a pragma of the form {-# LANGUAGE ... #-}
 parsePragma :: String       -- ^ Pragma string.
             -> Int          -- ^ Line number at which the directive appears.
@@ -242,10 +232,11 @@
 parsePragma ('{':'-':'#':pragma) line =
   let commaToSpace :: Char -> Char
       commaToSpace ',' = ' '
-      commaToSpace x   = x
-      pragmas = words $ takeWhile (/= '#') $ map commaToSpace pragma in
-  case pragmas of
-    [] -> Pragma (PragmaUnsupported "") []  --empty string pragmas are unsupported
+      commaToSpace x = x
+      pragmas = words $ takeWhile (/= '#') $ map commaToSpace pragma
+  in case pragmas of
+    --empty string pragmas are unsupported
+    [] -> Pragma (PragmaUnsupported "") []
     "LANGUAGE":xs -> Pragma PragmaLanguage xs
     x:xs -> Pragma (PragmaUnsupported x) xs
 
@@ -253,51 +244,50 @@
 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 = case find rightDirective directives of
-  Just (directiveType, _) -> Directive directiveType arg
-    where arg = unwords restLine
-          _:restLine = words directive
-  Nothing ->
-    let directiveStart = case words directive of
-          [] -> ""
-          first:_ -> first in
-      ParseError (Loc line 1) $ "Unknown directive: '" ++ directiveStart ++ "'."
+parseDirective (':':'!':directive) line = Directive ShellCmd $ '!' : directive
+parseDirective (':':directive) line =
+  case find rightDirective directives of
+    Just (directiveType, _) -> Directive directiveType arg
+      where arg = unwords restLine
+            _:restLine = words directive
+    Nothing ->
+      let directiveStart =
+                            case words directive of
+                              []      -> ""
+                              first:_ -> first
+      in ParseError (Loc line 1) $ "Unknown directive: '" ++ directiveStart ++ "'."
   where
-    rightDirective (_, dirname) = case words directive of
-      [] -> False
-      dir:_ -> dir `elem` tail (inits dirname)
+    rightDirective (_, dirname) =
+      case words directive of
+        []    -> False
+        dir:_ -> dir `elem` tail (inits dirname)
     directives =
-      [ (LoadModule,   "module")
-      , (GetType,      "type")
-      , (GetKind,      "kind")
-      , (GetInfo,      "info")
+      [ (LoadModule, "module")
+      , (GetType, "type")
+      , (GetKind, "kind")
+      , (GetInfo, "info")
       , (SearchHoogle, "hoogle")
-      , (GetDoc,       "documentation")
-      , (SetDynFlag,   "set")
-      , (LoadFile,     "load")
-      , (SetOption,    "option")
+      , (GetDoc, "documentation")
+      , (SetDynFlag, "set")
+      , (LoadFile, "load")
+      , (SetOption, "option")
       , (SetExtension, "extension")
-      , (GetHelp,      "?")
-      , (GetHelp,      "help")
+      , (GetHelp, "?")
+      , (GetHelp, "help")
       ]
 parseDirective _ _ = error "Directive must start with colon!"
 
--- | Parse a module and return the name declared in the 'module X where'
--- line. That line is required, and if it does not exist, this will error.
--- Names with periods in them are returned piece y piece.
+-- | Parse a module and return the name declared in the 'module X where' line. That line is
+-- required, and if it does not exist, this will error. Names with periods in them are returned
+-- piece y piece.
 getModuleName :: GhcMonad m => String -> m [String]
 getModuleName moduleSrc = do
   flags <- getSessionDynFlags
   let output = runParser flags parserModule moduleSrc
   case output of
-    Failure {} -> error "Module parsing failed."
+    Failure{} -> error "Module parsing failed."
     Parsed mod ->
       case unLoc <$> hsmodName (unLoc mod) of
-        Nothing -> error "Module must have a name."
+        Nothing   -> error "Module must have a name."
         Just name -> return $ split "." $ moduleNameString name
-
--- Not the same as 'unlines', due to trailing \n
-joinLines :: [String] -> String
-joinLines = intercalate "\n"
+    otherwise -> error "getModuleName failed, output was neither Parsed nor Failure"
diff --git a/src/IHaskell/Eval/Util.hs b/src/IHaskell/Eval/Util.hs
--- a/src/IHaskell/Eval/Util.hs
+++ b/src/IHaskell/Eval/Util.hs
@@ -1,26 +1,29 @@
 {-# LANGUAGE CPP, NoImplicitPrelude #-}
+
 module IHaskell.Eval.Util (
-  -- * Initialization
-  initGhci,
+    -- * Initialization
+    initGhci,
 
-  -- * Flags and extensions
-  -- ** Set and unset flags.
-  extensionFlag, setExtension,
-  ExtFlag(..),
-  setFlags,
+    -- * Flags and extensions ** Set and unset flags.
+    extensionFlag,
+    setExtension,
+    ExtFlag(..),
+    setFlags,
 
-  -- * Code Evaluation
-  evalImport,
-  removeImport,
-  evalDeclarations,
-  getType,
-  getDescription,
+    -- * Code Evaluation
+    evalImport,
+    removeImport,
+    evalDeclarations,
+    getType,
+    getDescription,
 
-  -- * Pretty printing
-  doc,
-  ) where
+    -- * Pretty printing
+    doc,
+    pprDynFlags,
+    pprLanguages,
+    ) where
 
-import           ClassyPrelude
+import           ClassyPrelude hiding ((<>))
 
 -- GHC imports.
 import           DynFlags
@@ -48,35 +51,114 @@
 import           Data.List (nubBy)
 
 -- | A extension flag that can be set or unset.
-data ExtFlag
-     = SetFlag   ExtensionFlag
-     | UnsetFlag ExtensionFlag
+data ExtFlag = SetFlag ExtensionFlag
+             | UnsetFlag ExtensionFlag
 
--- | Find the extension that corresponds to a given flag. Create the
--- corresponding 'ExtFlag' via @SetFlag@ or @UnsetFlag@.
--- If no such extension exist, yield @Nothing@.
+-- | Find the extension that corresponds to a given flag. Create the corresponding 'ExtFlag' via
+-- @SetFlag@ or @UnsetFlag@. If no such extension exist, yield @Nothing@.
 extensionFlag :: String         -- Extension name, such as @"DataKinds"@
               -> Maybe ExtFlag
 extensionFlag ext =
   case find (flagMatches ext) xFlags of
-    Just (_, flag, _) -> Just $ SetFlag flag
-    -- If it doesn't match an extension name, try matching against
-    -- disabling an extension.
+    Just fs -> Just $ SetFlag $ flagSpecFlag fs
+    -- If it doesn't match an extension name, try matching against disabling an extension.
     Nothing ->
       case find (flagMatchesNo ext) xFlags of
-        Just (_, flag, _) -> Just $ UnsetFlag flag
+        Just fs -> Just $ UnsetFlag $ flagSpecFlag fs
         Nothing -> Nothing
-
   where
     -- Check if a FlagSpec matches an extension name.
-    flagMatches ext (name, _, _) = ext == name
+    flagMatches ext fs = ext == flagSpecName fs
 
-    -- Check if a FlagSpec matches "No<ExtensionName>".
-    -- In that case, we disable the extension.
-    flagMatchesNo ext (name, _, _) = ext == "No" ++ name
+    -- 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
 
--- | Set an extension and update flags.
--- Return @Nothing@ on success. On failure, return an error message.
+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
+            -> SDoc
+pprDynFlags show_all dflags =
+  vcat
+    [ text "GHCi-specific dynamic flag settings:" $$
+      nest 2 (vcat (map (setting opt) ghciFlags))
+    , text "other dynamic, non-language, flag settings:" $$
+      nest 2 (vcat (map (setting opt) others))
+    , text "warning settings:" $$
+      nest 2 (vcat (map (setting wopt) DynFlags.fWarningFlags))
+    ]
+  where
+
+#if MIN_VERSION_ghc(7,8,0)
+    opt = gopt
+#else
+    opt = dopt
+#endif
+    setting test flag
+      | quiet = empty
+      | is_on = fstr name
+      | otherwise = fnostr name
+      where
+        name = flagSpecName flag
+        f = flagSpecFlag flag
+        is_on = test f dflags
+        quiet = not show_all && test f default_dflags == is_on
+    
+    default_dflags = defaultDynFlags (settings dflags)
+    
+    fstr str = text "-f" <> text str
+    
+    fnostr str = text "-fno-" <> 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 = [Opt_PrintBindResult, Opt_BreakOnException, Opt_BreakOnError, Opt_PrintEvldWithShow]
+
+-- | Pretty-print the base language and active options (taken from `InteractiveUI` module of
+-- `ghc-bin`)
+pprLanguages :: Bool      -- ^ Whether to include flags which are on by default
+             -> DynFlags
+             -> SDoc
+pprLanguages show_all dflags =
+  vcat
+    [text "base language is: " <>
+     case language dflags of
+       Nothing          -> text "Haskell2010"
+       Just Haskell98   -> text "Haskell98"
+       Just Haskell2010 -> text "Haskell2010", (if show_all
+                                                  then text "all active language options:"
+                                                  else text "with the following modifiers:") $$
+                                               nest 2 (vcat (map (setting xopt) DynFlags.xFlags))]
+  where
+    setting test flag
+      | quiet = empty
+      | is_on = text "-X" <> text name
+      | otherwise = text "-XNo" <> text name
+      where
+        name = flagSpecName flag
+        f = flagSpecFlag flag
+        is_on = test f dflags
+        quiet = not show_all && test f default_dflags == is_on
+
+    default_dflags =
+      defaultDynFlags (settings dflags) `lang_set`
+      case language dflags of
+        Nothing -> Just Haskell2010
+        other   -> other
+
+-- | Set an extension and update flags. Return @Nothing@ on success. On failure, return an error
+-- message.
 setExtension :: GhcMonad m => String -> m (Maybe String)
 setExtension ext = do
   flags <- getSessionDynFlags
@@ -85,37 +167,35 @@
     Just flag -> do
       setSessionDynFlags $
         case flag of
-          SetFlag ghcFlag -> xopt_set flags ghcFlag
+          SetFlag ghcFlag   -> xopt_set flags ghcFlag
           UnsetFlag ghcFlag -> xopt_unset flags ghcFlag
       return Nothing
 
--- | Set a list of flags, as per GHCi's `:set`.
--- This was adapted from GHC's InteractiveUI.hs (newDynFlags).
--- It returns a list of error messages.
+-- | Set a list of flags, as per GHCi's `:set`. This was adapted from GHC's InteractiveUI.hs
+-- (newDynFlags). It returns a list of error messages.
 setFlags :: GhcMonad m => [String] -> m [String]
 setFlags ext = do
-    -- Try to parse flags.
-    flags <- getSessionDynFlags
-    (flags', unrecognized, warnings) <- parseDynamicFlags flags (map noLoc ext)
+  -- Try to parse flags.
+  flags <- getSessionDynFlags
+  (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.setInteractiveDynFlags restoredPkgs
+  -- 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.setInteractiveDynFlags restoredPkgs
 
-    -- Create the parse errors.
-    let noParseErrs = map (("Could not parse: " ++) . unLoc) unrecognized
-        allWarns = map unLoc warnings ++
-                     ["-package not supported yet" | packageFlags flags /= packageFlags flags']
-        warnErrs    = map ("Warning: " ++) allWarns
-    return $ noParseErrs ++ warnErrs
+  -- Create the parse errors.
+  let noParseErrs = map (("Could not parse: " ++) . unLoc) unrecognized
+      allWarns = map unLoc warnings ++
+                 ["-package not supported yet" | packageFlags flags /= packageFlags flags']
+      warnErrs = map ("Warning: " ++) allWarns
+  return $ noParseErrs ++ warnErrs
 
--- | Convert an 'SDoc' into a string. This is similar to the family of
--- 'showSDoc' functions, but does not impose an arbitrary width limit on
--- the output (in terms of number of columns). Instead, it respsects the
--- 'pprCols' field in the structure returned by 'getSessionDynFlags', and
--- thus gives a configurable width of output.
+-- | Convert an 'SDoc' into a string. This is similar to the family of 'showSDoc' functions, but
+-- does not impose an arbitrary width limit on the output (in terms of number of columns). Instead,
+-- it respsects the 'pprCols' field in the structure returned by 'getSessionDynFlags', and thus
+-- gives a configurable width of output.
 doc :: GhcMonad m => SDoc -> m String
 doc sdoc = do
   flags <- getSessionDynFlags
@@ -124,15 +204,16 @@
   let cols = pprCols flags
       d = runSDoc sdoc (initSDocContext flags style)
   return $ Pretty.fullRender Pretty.PageMode cols 1.5 string_txt "" d
+
   where
     string_txt :: Pretty.TextDetails -> String -> String
-    string_txt (Pretty.Chr c)   s  = c:s
-    string_txt (Pretty.Str s1)  s2 = s1 ++ s2
+    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
 
--- | Initialize the GHC API. Run this as the first thing in the `runGhc`.
--- This initializes some dyn flags (@ExtendedDefaultRules@,
+-- | Initialize the GHC API. Run this as the first thing in the `runGhc`. This initializes some dyn
+-- flags (@ExtendedDefaultRules@,
 -- @NoMonomorphismRestriction@), sets the target to interpreted, link in
 -- memory, sets a reasonable output width, and potentially a few other
 -- things. It should be invoked before other functions from this module.
@@ -142,27 +223,28 @@
 -- (and only the first time) it is called.
 initGhci :: GhcMonad m => Maybe String -> m ()
 initGhci sandboxPackages = do
-  -- Initialize dyn flags.
-  -- Start with -XExtendedDefaultRules and -XNoMonomorphismRestriction.
+  -- Initialize dyn flags. Start with -XExtendedDefaultRules and -XNoMonomorphismRestriction.
   originalFlags <- getSessionDynFlags
   let flag = flip xopt_set
       unflag = flip xopt_unset
       dflags = flag Opt_ExtendedDefaultRules . unflag Opt_MonomorphismRestriction $ originalFlags
-      pkgConfs = case sandboxPackages of
-        Nothing -> extraPkgConfs originalFlags
-        Just path ->
-          let pkg  = PkgConfFile path in
-            (pkg:) . extraPkgConfs originalFlags
+      pkgConfs =
+        case sandboxPackages of
+          Nothing -> extraPkgConfs originalFlags
+          Just path ->
+            let pkg = PkgConfFile path
+            in (pkg :) . extraPkgConfs originalFlags
 
-  void $ setSessionDynFlags $ dflags { hscTarget = HscInterpreted,
-                                       ghcLink = LinkInMemory,
-                                       pprCols = 300,
-                                       extraPkgConfs = pkgConfs }
+  void $ setSessionDynFlags $ dflags
+    { hscTarget = HscInterpreted
+    , ghcLink = LinkInMemory
+    , pprCols = 300
+    , extraPkgConfs = pkgConfs
+    }
 
--- | Evaluate a single import statement.
--- If this import statement is importing a module which was previously
--- imported implicitly (such as `Prelude`) or if this module has a `hiding`
--- annotation, the previous import is removed.
+-- | Evaluate a single import statement. If this import statement is importing a module which was
+-- previously imported implicitly (such as `Prelude`) or if this module has a `hiding` annotation,
+-- the previous import is removed.
 evalImport :: GhcMonad m => String -> m ()
 evalImport imports = do
   importDecl <- parseImportDecl imports
@@ -173,8 +255,8 @@
 
       -- If this is a `hiding` import, remove previous non-`hiding` imports.
       oldImps = if isHiddenImport importDecl
-                then filter (not . importOf importDecl) context
-                else noImplicit
+                  then filter (not . importOf importDecl) context
+                  else noImplicit
 
   -- Replace the context.
   setContext $ IIDecl importDecl : oldImps
@@ -193,9 +275,10 @@
 
     -- Check whether an import is hidden.
     isHiddenImport :: ImportDecl RdrName -> Bool
-    isHiddenImport imp = case ideclHiding imp of
-                           Just (True, _) -> True
-                           _ -> False
+    isHiddenImport imp =
+      case ideclHiding imp of
+        Just (True, _) -> True
+        _              -> False
 
 removeImport :: GhcMonad m => String -> m ()
 removeImport moduleName = do
@@ -209,8 +292,7 @@
     isImportOf name (IIModule modName) = name == modName
     isImportOf name (IIDecl impDecl) = name == unLoc (ideclName impDecl)
 
--- | Evaluate a series of declarations.
--- Return all names which were bound by these declarations.
+-- | Evaluate a series of declarations. Return all names which were bound by these declarations.
 evalDeclarations :: GhcMonad m => String -> m [String]
 evalDeclarations decl = do
   names <- runDecls decl
@@ -221,7 +303,7 @@
 cleanUpDuplicateInstances :: GhcMonad m => m ()
 cleanUpDuplicateInstances = modifySession $ \hscEnv ->
   let 
-      -- Get all class instancesj
+      -- Get all class instances
       ic = hsc_IC hscEnv
       (clsInsts, famInsts) = ic_instances ic
       -- Remove duplicates
@@ -229,11 +311,16 @@
   in hscEnv { hsc_IC = ic { ic_instances = (clsInsts', famInsts) } }
   where
     instEq :: ClsInst -> ClsInst -> Bool
-    instEq ClsInst{is_tvs = tpl_tvs,is_tys = tpl_tys} ClsInst{is_tys = tpl_tys'} =
-      let tpl_tv_set = mkVarSet tpl_tvs
-      in isJust $ tcMatchTys tpl_tv_set tpl_tys tpl_tys'
-
-
+#if MIN_VERSION_ghc(7,8,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
+      = let tpl_tv_set = mkVarSet tpl_tvs
+        in cls == cls' && isJust (tcMatchTys tpl_tv_set tpl_tys tpl_tys')
+#else
+    instEq _ _ = False
+#endif
 -- | Get the type of an expression and convert it to a string.
 getType :: GhcMonad m => String -> m String
 getType expr = do
@@ -245,21 +332,23 @@
 -- | A wrapper around @getInfo@. Return info about each name in the string.
 getDescription :: GhcMonad m => String -> m [String]
 getDescription str = do
-  names     <- parseName str
+  names <- parseName str
   maybeInfos <- mapM getInfo' names
 
-  -- Filter out types that have parents in the same set.
-  -- GHCi also does this.
+  -- Filter out types that have parents in the same set. GHCi also does this.
   let infos = catMaybes maybeInfos
       allNames = mkNameSet $ map (getName . getType) infos
-      hasParent info = case tyThingParent_maybe (getType info) of
-        Just parent -> getName parent `elemNameSet` allNames
-        Nothing -> False
+      hasParent info =
+        case tyThingParent_maybe (getType info) of
+          Just parent -> getName parent `elemNameSet` allNames
+          Nothing     -> False
       filteredOutput = filter (not . hasParent) infos
 
   -- Print nicely
   mapM (doc . printInfo) filteredOutput
+
   where
+
 #if MIN_VERSION_ghc(7,8,0)
     getInfo' = getInfo False
 #else
@@ -274,15 +363,16 @@
 
 #if MIN_VERSION_ghc(7,8,0)
     printInfo (thing, fixity, classInstances, famInstances) =
-          pprTyThingInContextLoc thing $$
-          showFixity thing fixity $$
-          vcat (map GHC.pprInstance classInstances) $$
-          vcat (map GHC.pprFamInst famInstances)
+      pprTyThingInContextLoc thing $$
+      showFixity thing fixity $$
+      vcat (map GHC.pprInstance classInstances) $$
+      vcat (map GHC.pprFamInst famInstances)
 #else
     printInfo (thing, fixity, classInstances) =
-          pprTyThingInContextLoc False thing $$ showFixity thing fixity $$ vcat (map GHC.pprInstance classInstances)
+      pprTyThingInContextLoc False thing $$ showFixity thing fixity $$
+      vcat (map GHC.pprInstance classInstances)
 #endif
     showFixity thing fixity =
       if fixity == GHC.defaultFixity
-      then empty
-      else ppr fixity <+> pprInfixName (getName thing)
+        then empty
+        else ppr fixity <+> pprInfixName (getName thing)
diff --git a/src/IHaskell/Flags.hs b/src/IHaskell/Flags.hs
--- a/src/IHaskell/Flags.hs
+++ b/src/IHaskell/Flags.hs
@@ -1,11 +1,12 @@
 {-# LANGUAGE NoImplicitPrelude, DeriveFunctor #-}
+
 module IHaskell.Flags (
     IHaskellMode(..),
     Argument(..),
     Args(..),
     LhsStyle(..),
-    lhsStyleBird,
     NotebookFormat(..),
+    lhsStyleBird,
     parseFlags,
     help,
     ) where
@@ -16,32 +17,31 @@
 import           Data.List (findIndex)
 import           IHaskell.Types
 
--- Command line arguments to IHaskell.  A set of aruments is annotated with
--- the mode being invoked.
+-- 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 = ServeFrom String    -- ^ Which directory to serve notebooks from.
-              | Extension String    -- ^ An extension to load at startup.
-              | ConfFile String     -- ^ A file with commands to load at startup.
-              | IPythonFrom String  -- ^ Which executable to use for IPython.
+data Argument = ConfFile String     -- ^ A file with commands to load at startup.
               | OverwriteFiles      -- ^ Present when output should overwrite existing files. 
+              | GhcLibDir String    -- ^ Where to find the GHC libraries.
+              | KernelDebug         -- ^ Spew debugging output from the kernel.
+              | Help                -- ^ Display help text.
               | ConvertFrom String
               | ConvertTo String
               | ConvertFromFormat NotebookFormat
               | ConvertToFormat NotebookFormat
               | ConvertLhsStyle (LhsStyle String)
-              | GhcLibDir String    -- ^ Where to find the GHC libraries.
-              | Help                -- ^ Display help text.
   deriving (Eq, Show)
 
-data LhsStyle string = LhsStyle { lhsCodePrefix :: string  -- ^ @>@
-                                , lhsOutputPrefix :: string  -- ^ @<<@
-                                , lhsBeginCode :: string  -- ^ @\\begin{code}@
-                                , lhsEndCode :: string  -- ^ @\\end{code}@
-                                , lhsBeginOutput :: string  -- ^ @\\begin{verbatim}@
-                                , lhsEndOutput :: string  -- ^ @\\end{verbatim}@
-                                }
+data LhsStyle string =
+       LhsStyle
+         { lhsCodePrefix :: string  -- ^ @>@
+         , lhsOutputPrefix :: string  -- ^ @<<@
+         , lhsBeginCode :: string  -- ^ @\\begin{code}@
+         , lhsEndCode :: string  -- ^ @\\end{code}@
+         , lhsBeginOutput :: string  -- ^ @\\begin{verbatim}@
+         , lhsEndOutput :: string  -- ^ @\\end{verbatim}@
+         }
   deriving (Eq, Functor, Show)
 
 data NotebookFormat = LhsMarkdown
@@ -49,74 +49,69 @@
   deriving (Eq, Show)
 
 -- Which mode IHaskell is being invoked in.
--- `None` means no mode was specified.
 data IHaskellMode = ShowHelp String
-                  | Notebook
-                  | Console
+                  | InstallKernelSpec
                   | ConvertLhs
                   | Kernel (Maybe String)
-                  | View (Maybe ViewFormat) (Maybe String)
   deriving (Eq, Show)
 
--- | Given a list of command-line arguments, return the IHaskell mode and
--- arguments to process.
+-- | 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 in
-    case modeIndex of
-      Nothing -> Left $ "No mode provided. Modes available are: " ++ show modeFlags ++ "\n" ++
-                       pack (showText (Wrap 100) $ helpText [] HelpFormatAll ihaskellArgs)
-      Just 0 -> process ihaskellArgs flags
+parseFlags flags =
+  let modeIndex = findIndex (`elem` modeFlags) flags
+  in case modeIndex of
+    Nothing ->
+      -- Treat no mode as 'console'.
+      if "--help" `elem` flags
+        then Left $ pack (showText (Wrap 100) $ helpText [] HelpFormatAll ihaskellArgs)
+        else process ihaskellArgs flags
+    Just 0 -> process ihaskellArgs flags
 
+    Just idx ->
       -- If mode not first, move it to be first.
-      Just idx -> 
-        let (start, first:end) = splitAt idx flags in
-          process ihaskellArgs $ first:start ++ end
+      let (start, first:end) = splitAt idx flags
+      in process ihaskellArgs $ first : start ++ end
   where
     modeFlags = concatMap modeNames allModes
 
 allModes :: [Mode Args]
-allModes = [console, notebook, view, kernel, convert]
+allModes = [installKernelSpec, kernel, convert]
 
 -- | Get help text for a given IHaskell ode.
 help :: IHaskellMode -> String
 help mode = showText (Wrap 100) $ helpText [] HelpFormatAll $ chooseMode mode
   where
-    chooseMode Console = console
-    chooseMode Notebook = notebook
+    chooseMode InstallKernelSpec = installKernelSpec
     chooseMode (Kernel _) = kernel
     chooseMode ConvertLhs = convert
 
-ipythonFlag :: Flag Args
-ipythonFlag = flagReq ["ipython", "i"] (store IPythonFrom) "<path>" "Executable for IPython."
-
 ghcLibFlag :: Flag Args
 ghcLibFlag = flagReq ["ghclib", "l"] (store GhcLibDir) "<path>" "Library directory for GHC."
 
-universalFlags :: [Flag Args]
-universalFlags = [ flagReq ["extension", "e", "X"] (store Extension) "<ghc-extension>"
-                     "Extension to enable at start."
-                 , flagReq ["conf", "c"] (store ConfFile) "<rc.hs>"
-                     "File with commands to execute at start; replaces ~/.ihaskell/rc.hs."
-                 , flagHelpSimple (add Help)
-                 ]
+kernelDebugFlag :: Flag Args
+kernelDebugFlag = flagNone ["debug"] addDebug "Print debugging output from the kernel."
   where
-    add flag (Args mode flags) = Args mode $ flag : flags
+    addDebug (Args mode prev) = Args mode (KernelDebug : prev)
 
+confFlag :: Flag Args
+confFlag = flagReq ["conf", "c"] (store ConfFile) "<rc.hs>"
+             "File with commands to execute at start; replaces ~/.ihaskell/rc.hs."
+
+helpFlag = flagHelpSimple (add Help)
+
+add flag (Args mode flags) = Args mode $ flag : flags
+
 store :: (String -> Argument) -> String -> Args -> Either String Args
 store constructor str (Args mode prev) = Right $ Args mode $ constructor str : prev
 
-notebook :: Mode Args
-notebook = mode "notebook" (Args Notebook []) "Browser-based notebook interface." noArgs $
-  flagReq ["serve","s"] (store ServeFrom) "<dir>" "Directory to serve notebooks from.":
-  ipythonFlag:
-  universalFlags
-
-console :: Mode Args
-console = mode "console" (Args Console []) "Console-based interactive repl." noArgs $ ipythonFlag : universalFlags
+installKernelSpec :: Mode Args
+installKernelSpec =
+  mode "install" (Args InstallKernelSpec []) "Install the Jupyter kernelspec." noArgs
+    [ghcLibFlag, kernelDebugFlag, confFlag, helpFlag]
 
 kernel :: Mode Args
-kernel = mode "kernel" (Args (Kernel Nothing) []) "Invoke the IHaskell kernel." kernelArg [ghcLibFlag]
+kernel = mode "kernel" (Args (Kernel Nothing) []) "Invoke the IHaskell kernel." kernelArg
+           [ghcLibFlag, kernelDebugFlag, confFlag]
   where
     kernelArg = flagArg update "<json-kernel-file>"
     update filename (Args _ flags) = Right $ Args (Kernel $ Just filename) flags
@@ -125,85 +120,51 @@
 convert = mode "convert" (Args ConvertLhs []) description unnamedArg convertFlags
   where
     description = "Convert between Literate Haskell (*.lhs) and Ipython notebooks (*.ipynb)."
-    convertFlags = universalFlags ++ [ flagReq ["input", "i"] (store ConvertFrom) "<file>"
-                                         "File to read."
-                                     , flagReq ["output", "o"] (store ConvertTo) "<file>"
-                                         "File to write."
-                                     , flagReq ["from", "f"] (storeFormat ConvertFromFormat)
-                                         "lhs|ipynb" "Format of the file to read."
-                                     , flagReq ["to", "t"] (storeFormat ConvertToFormat) "lhs|ipynb"
-                                         "Format of the file to write."
-                                     , flagNone ["force"] consForce
-                                         "Overwrite existing files with output."
-                                     , flagReq ["style", "s"] storeLhs "bird|tex"
-                                         "Type of markup used for the literate haskell file"
-                                     , flagNone ["bird"] (consStyle lhsStyleBird)
-                                         "Literate haskell uses >"
-                                     , flagNone ["tex"] (consStyle lhsStyleTex)
-                                         "Literate haskell uses \\begin{code}"
-                                     ]
+    convertFlags = [ flagReq ["input", "i"] (store ConvertFrom) "<file>" "File to read."
+                   , flagReq ["output", "o"] (store ConvertTo) "<file>" "File to write."
+                   , flagReq ["from", "f"] (storeFormat ConvertFromFormat) "lhs|ipynb"
+                       "Format of the file to read."
+                   , flagReq ["to", "t"] (storeFormat ConvertToFormat) "lhs|ipynb"
+                       "Format of the file to write."
+                   , flagNone ["force"] consForce "Overwrite existing files with output."
+                   , flagReq ["style", "s"] storeLhs "bird|tex"
+                       "Type of markup used for the literate haskell file"
+                   , flagNone ["bird"] (consStyle lhsStyleBird) "Literate haskell uses >"
+                   , flagNone ["tex"] (consStyle lhsStyleTex) "Literate haskell uses \\begin{code}"
+                   , helpFlag
+                   ]
 
     consForce (Args mode prev) = Args mode (OverwriteFiles : prev)
     unnamedArg = Arg (store ConvertFrom) "<file>" False
     consStyle style (Args mode prev) = Args mode (ConvertLhsStyle style : prev)
 
-    storeFormat constructor str (Args mode prev) = case toLower str of
-      "lhs" -> Right $ Args mode $ constructor LhsMarkdown : prev
-      "ipynb" -> Right $ Args mode $ constructor IpynbFile : prev
-      _ -> Left $ "Unknown format requested: " ++ str
+    storeFormat constructor str (Args mode prev) =
+      case toLower str of
+        "lhs"   -> Right $ Args mode $ constructor LhsMarkdown : prev
+        "ipynb" -> Right $ Args mode $ constructor IpynbFile : prev
+        _       -> Left $ "Unknown format requested: " ++ str
 
-    storeLhs str previousArgs = case toLower str of
-      "bird" -> success lhsStyleBird
-      "tex"  -> success lhsStyleTex
-      _      -> Left $ "Unknown lhs style: " ++ str
+    storeLhs str previousArgs =
+      case toLower str of
+        "bird" -> success lhsStyleBird
+        "tex"  -> success lhsStyleTex
+        _      -> Left $ "Unknown lhs style: " ++ str
       where
         success lhsStyle = Right $ consStyle lhsStyle previousArgs
 
 lhsStyleBird, lhsStyleTex :: LhsStyle String
 lhsStyleBird = LhsStyle "> " "\n<< " "" "" "" ""
-lhsStyleTex  = LhsStyle "" "" "\\begin{code}" "\\end{code}" "\\begin{verbatim}" "\\end{verbatim}"
 
+lhsStyleTex = LhsStyle "" "" "\\begin{code}" "\\end{code}" "\\begin{verbatim}" "\\end{verbatim}"
 
-view :: Mode Args
-view =
-  let empty = mode "view" (Args (View Nothing Nothing) []) "View IHaskell notebook." noArgs flags in
-    empty {
-      modeNames = ["view"],
-      modeCheck  =
-        \a@(Args (View fmt file) _) ->
-          if not (isJust fmt && isJust file)
-          then Left "Syntax: IHaskell view <format> <name>[.ipynb]"
-          else Right a,
-      modeHelp = concat [
-        "Convert an IHaskell notebook to another format.\n",
-        "Notebooks are searched in the IHaskell directory and the current directory.\n",
-        "Available formats are " ++ intercalate ", " (map show 
-          ["pdf", "html", "ipynb", "markdown", "latex"]),
-        "."
-      ],
-      modeArgs = ([formatArg, filenameArg] ++ fst (modeArgs empty),
-                  snd $ modeArgs empty)
-                                                    
-  }
-  where
-    flags = [ipythonFlag, flagHelpSimple (add Help)]
-    formatArg = flagArg updateFmt "<format>"
-    filenameArg = flagArg updateFile "<name>[.ipynb]"
-    updateFmt fmtStr (Args (View _ s) flags) = 
-      case readMay fmtStr of
-        Just fmt -> Right $ Args (View (Just fmt) s) flags
-        Nothing -> Left $ "Invalid format '" ++ fmtStr ++ "'."
-    updateFile name (Args (View f _) flags) = Right $ Args (View f (Just name)) flags
-    add flag (Args mode flags) = Args mode $ flag : flags
-  
 ihaskellArgs :: Mode Args
 ihaskellArgs =
-  let descr = "Haskell for Interactive Computing." 
+  let descr = "Haskell for Interactive Computing."
       helpStr = showText (Wrap 100) $ helpText [] HelpFormatAll ihaskellArgs
       onlyHelp = [flagHelpSimple (add Help)]
-      noMode = mode "IHaskell" (Args (ShowHelp helpStr) []) descr noArgs onlyHelp in
-    noMode { modeGroupModes = toGroup allModes }
-  where 
+      noMode = mode "IHaskell" (Args (ShowHelp helpStr) []) descr noArgs onlyHelp
+  in noMode { modeGroupModes = toGroup allModes }
+  where
     add flag (Args mode flags) = Args mode $ flag : flags
 
 noArgs = flagArg unexpected ""
diff --git a/src/IHaskell/IPython.hs b/src/IHaskell/IPython.hs
--- a/src/IHaskell/IPython.hs
+++ b/src/IHaskell/IPython.hs
@@ -1,94 +1,90 @@
-{-# LANGUAGE NoImplicitPrelude, OverloadedStrings #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE DoAndIfThenElse #-}
+
 -- | Description : Shell scripting wrapper using @Shelly@ for the @notebook@, and
 --                 @console@ commands.
 module IHaskell.IPython (
-  setupIPython,
-  runConsole,
-  runNotebook,
-  readInitInfo,
-  defaultConfFile,
-  getIHaskellDir,
-  getSandboxPackageConf,
-  nbconvert,
-  subHome,
-  ViewFormat(..),
-  WhichIPython(..),
-) where
+    replaceIPythonKernelspec,
+    defaultConfFile,
+    getIHaskellDir,
+    getSandboxPackageConf,
+    subHome,
+    kernelName,
+    KernelSpecOptions(..),
+    defaultKernelSpecOptions,
+    ) where
 
-import            ClassyPrelude
-import            Control.Concurrent (threadDelay)
-import            Prelude (read, reads, init)
-import            Shelly hiding (find, trace, path, (</>))
-import            System.Argv0
-import            System.Directory
-import qualified  Filesystem.Path.CurrentOS as FS
-import            Data.List.Utils (split)
-import            Data.String.Utils (rstrip, endswith, strip, replace)
-import            Text.Printf
-import Data.Maybe (fromJust)
+import           ClassyPrelude
+import           Control.Concurrent (threadDelay)
+import           Prelude (read, reads, init)
+import           Shelly hiding (find, trace, path, (</>))
+import           System.Argv0
+import           System.Directory
+import qualified Filesystem.Path.CurrentOS as FS
+import           Data.List.Utils (split)
+import           Data.String.Utils (rstrip, endswith, strip, replace)
+import           Text.Printf
+import qualified Data.Text as T
+import           Data.Maybe (fromJust)
+import           System.Exit (exitFailure)
+import           Data.Aeson (toJSON)
+import           Data.Aeson.Encode (encodeToTextBuilder)
+import           Data.Text.Lazy.Builder (toLazyText)
 
 import qualified System.IO.Strict as StrictIO
 import qualified Paths_ihaskell as Paths
 import qualified Codec.Archive.Tar as Tar
 
-import IHaskell.Types
-import            System.Posix.Signals
+import qualified GHC.Paths
+import           IHaskell.Types
+import           System.Posix.Signals
 
--- | Which IPython to use.
-data WhichIPython
-     = DefaultIPython           -- ^ Use the one that IHaskell tries to install.
-     | ExplicitIPython String   -- ^ Use the command-line flag provided one.
-     deriving Eq
+data KernelSpecOptions =
+       KernelSpecOptions
+         { kernelSpecGhcLibdir :: String           -- ^ GHC libdir.
+         , kernelSpecDebug :: Bool                 -- ^ Spew debugging output?
+         , kernelSpecConfFile :: IO (Maybe String) -- ^ Filename of profile JSON file.
+         }
 
--- | The IPython profile name.
-ipythonProfile :: String
-ipythonProfile = "haskell"
+defaultKernelSpecOptions :: KernelSpecOptions
+defaultKernelSpecOptions = KernelSpecOptions
+  { kernelSpecGhcLibdir = GHC.Paths.libdir
+  , kernelSpecDebug = False
+  , kernelSpecConfFile = defaultConfFile
+  }
 
--- | The current IPython profile version.
--- This must be the same as the file in the profile.tar.
--- The filename used is @profileVersionFile@.
-profileVersion :: String
-profileVersion = "0.4.2.0"
+-- | The IPython kernel name.
+kernelName :: IsString a => a
+kernelName = "haskell"
 
--- | Filename in the profile where the version ins kept.
-profileVersionFile :: FilePath
-profileVersionFile = ".profile_version"
+kernelArgs :: IsString a => [a]
+kernelArgs = ["--kernel", kernelName]
 
--- | Run IPython with any arguments.
-ipython :: WhichIPython -- ^ Which IPython to use (user-provided or IHaskell-installed).
-        -> Bool         -- ^ Whether to suppress output.
+-- | 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 String    -- ^ IPython output.
-ipython which suppress args
-  | which == DefaultIPython = do
-      runCmd <- liftIO $ Paths.getDataFileName "installation/run.sh"
-      venv <- fpToText <$> ipythonDir
-      let cmdArgs = [pack runCmd, venv] ++ args
-      -- If we have PYTHONDONTWRITEBYTECODE enabled, everything breaks.
-      setenv "PYTHONDONTWRITEBYTECODE" ""
-
-      liftIO $ installHandler keyboardSignal (CatchOnce $ return ()) Nothing
+ipython suppress args = do
+  liftIO $ installHandler keyboardSignal (CatchOnce $ return ()) Nothing
 
-      -- We have this because using `run` does not let us use stdin.
-      runHandles "bash" cmdArgs handles doNothing
-  | otherwise = do
-      let ExplicitIPython exe = which
-      runHandles (fpFromString exe) args handles doNothing
+  -- We have this because using `run` does not let us use stdin.
+  runHandles "ipython" args handles doNothing
 
-  where handles = [InHandle Inherit, outHandle suppress, errorHandle suppress]
-        outHandle True = OutHandle CreatePipe
-        outHandle False = OutHandle Inherit
-        errorHandle True =  ErrorHandle CreatePipe
-        errorHandle False = ErrorHandle Inherit
-        doNothing _ stdout _ = if suppress 
-                                then liftIO $ StrictIO.hGetContents stdout
-                                else return ""
+  where
+    handles = [InHandle Inherit, outHandle suppress, errorHandle suppress]
+    outHandle True = OutHandle CreatePipe
+    outHandle False = OutHandle Inherit
+    errorHandle True = ErrorHandle CreatePipe
+    errorHandle False = ErrorHandle Inherit
+    doNothing _ stdout _ = if suppress
+                             then liftIO $ StrictIO.hGetContents stdout
+                             else return ""
 
 -- | Run while suppressing all output.
 quietRun path args = runHandles path args handles nothing
   where
-    handles =  [InHandle Inherit, OutHandle CreatePipe, ErrorHandle CreatePipe]
+    handles = [InHandle Inherit, OutHandle CreatePipe, ErrorHandle CreatePipe]
     nothing _ _ _ = return ()
 
 -- | Create the directory and return it.
@@ -107,18 +103,9 @@
 ipythonDir :: Sh FilePath
 ipythonDir = ensure $ (</> "ipython") <$> ihaskellDir
 
-ipythonExePath :: WhichIPython -> Sh FilePath
-ipythonExePath which = 
-  case which of
-    DefaultIPython -> (</> ("bin" </> "ipython")) <$> ipythonDir
-    ExplicitIPython path -> return $ fromString path
-
 notebookDir :: Sh FilePath
 notebookDir = ensure $ (</> "notebooks") <$> ihaskellDir
 
-ipythonSourceDir :: Sh FilePath
-ipythonSourceDir = ensure $ (</> "ipython-src") <$> ihaskellDir
-
 getIHaskellDir :: IO String
 getIHaskellDir = shelly $ fpToString <$> ihaskellDir
 
@@ -127,85 +114,90 @@
   filename <- (</> "rc.hs") <$> ihaskellDir
   exists <- test_f filename
   return $ if exists
-           then Just $ fpToString filename
-           else Nothing
+             then Just $ fpToString filename
+             else Nothing
 
--- | Find a notebook and then convert it into the provided format.
--- Notebooks are searched in the current directory as well as the IHaskell
--- notebook directory (in that order).
-nbconvert :: WhichIPython -> ViewFormat -> String -> IO ()
-nbconvert which fmt name = void . shelly $ do
-  curdir <- pwd
-  nbdir <- notebookDir
+replaceIPythonKernelspec :: KernelSpecOptions -> IO ()
+replaceIPythonKernelspec kernelSpecOpts = shelly $ do
+  verifyIPythonVersion
+  installKernelspec True kernelSpecOpts
 
-  -- Find which of the options is available. 
-  let notebookOptions = [
-        curdir </> fpFromString name,
-        curdir </> fpFromString (name ++ ".ipynb"),
-        nbdir  </> fpFromString name,
-        nbdir  </> fpFromString (name ++ ".ipynb")
-        ]
-  maybeNb <- headMay <$> filterM test_f notebookOptions
-  case maybeNb of
-    Nothing -> do
-      putStrLn $ "Cannot find notebook: " ++ pack name
-      putStrLn "Tried:"
-      mapM_ (putStrLn . ("  " ++) . fpToText) notebookOptions 
+-- | Verify that a proper version of IPython is installed and accessible.
+verifyIPythonVersion :: Sh ()
+verifyIPythonVersion = do
+  pathMay <- which "ipython"
+  case pathMay of
+    Nothing -> badIPython "No IPython detected -- install IPython 3.0+ before using IHaskell."
+    Just path -> do
+      output <- unpack <$> silently (run path ["--version"])
+      case parseVersion output of
+        Just (3:_) -> return ()
+        Just (2:_) -> oldIPython
+        Just (1:_) -> oldIPython
+        Just (0:_) -> oldIPython
+        _          -> badIPython "Detected IPython, but could not parse version number."
 
-    Just notebook ->
-      let viewArgs = case fmt of
-            Pdf ->  ["--to=latex", "--post=pdf"]
-            Html -> ["--to=html", "--template=ihaskell"]
-            fmt ->  ["--to=" ++ show fmt] in
-      void $ runIHaskell which ipythonProfile "nbconvert" $ viewArgs ++ [fpToString notebook]
+  where
+    badIPython :: Text -> Sh ()
+    badIPython message = liftIO $ do
+      hPutStrLn stderr message
+      exitFailure
+    oldIPython = badIPython "Detected old version of IPython. IHaskell requires 3.0.0 or up."
 
--- | Set up IPython properly.
-setupIPython :: WhichIPython -> IO ()
+-- | Install an IHaskell kernelspec into the right location. The right location is determined by
+-- using `ipython kernelspec install --user`.
+installKernelspec :: Bool -> KernelSpecOptions -> Sh ()
+installKernelspec replace opts = void $ do
+  ihaskellPath <- getIHaskellPath
+  confFile <- liftIO $ kernelSpecConfFile opts
 
-setupIPython (ExplicitIPython path) = do
-  exists <- shelly $
-    test_f $ fromString path
+  let kernelFlags :: [String]
+      kernelFlags =
+        ["--debug" | kernelSpecDebug opts] ++
+        (case confFile of
+           Nothing   -> []
+           Just file -> ["--conf", file])
+        ++ ["--ghclib", kernelSpecGhcLibdir opts]
 
-  unless exists $
-    fail $ "Cannot find IPython at " ++ path
+  let kernelSpec = KernelSpec
+        { kernelDisplayName = "Haskell"
+        , kernelLanguage = kernelName
+        , kernelCommand = [ihaskellPath, "kernel", "{connection_file}"] ++ kernelFlags
+        }
 
-setupIPython DefaultIPython = do
-  installed <- ipythonInstalled
-  when (not installed) $ do
-    path <- shelly $ which "ipython"
-    case path of
-      Just ipythonPath -> checkIPythonVersion ipythonPath
-      Nothing -> badIPython "Did not detect IHaskell-installed or system IPython."
-  where
-    checkIPythonVersion :: FilePath -> IO ()
-    checkIPythonVersion path = do
-      output <- unpack <$> shelly (silently $ run path ["--version"])
-      case parseVersion output of
-        Just (3:_) -> putStrLn "Using system-wide dev version of IPython."
-        Just (2:_) -> putStrLn "Using system-wide IPython."
-        Just (1:_) -> badIPython "Detected old version of IPython. IHaskell requires 2.0.0 or up."
-        Just (0:_) -> badIPython "Detected old version of IPython. IHaskell requires 2.0.0 or up."
-        _ -> badIPython "Detected IPython, but could not parse version number."
+  -- Create a temporary directory. Use this temporary directory to make a kernelspec directory; then,
+  -- shell out to IPython to install this kernelspec directory.
+  withTmpDir $ \tmp -> do
+    let kernelDir = tmp </> kernelName
+    let filename = kernelDir </> "kernel.json"
 
-    badIPython :: Text -> IO ()
-    badIPython reason = void $ do
-        putStrLn reason
-        putStrLn "IHaskell will now proceed to install IPython (locally for itself)."
-        putStrLn "Installing IPython in IHaskell's virtualenv in 10 seconds. Ctrl-C to cancel."
-        threadDelay $ 1000 * 1000 * 10
-        installIPython
+    mkdir_p kernelDir
+    writefile filename $ toStrict $ toLazyText $ encodeToTextBuilder $ toJSON kernelSpec
+    let files = ["kernel.js", "logo-64x64.png"]
+    forM_ files $ \file -> do
+      src <- liftIO $ Paths.getDataFileName $ "html/" ++ file
+      cp (fpFromString src) (tmp </> kernelName </> fpFromString file)
 
+    Just ipython <- which "ipython"
+    let replaceFlag = ["--replace" | replace]
+        cmd = ["kernelspec", "install", "--user", fpToText kernelDir] ++ replaceFlag
+    silently $ run ipython cmd
 
--- | Replace "~" with $HOME if $HOME is defined.
--- Otherwise, do nothing.
+kernelSpecCreated :: Sh Bool
+kernelSpecCreated = do
+  Just ipython <- which "ipython"
+  out <- silently $ run ipython ["kernelspec", "list"]
+  let kernelspecs = map T.strip $ lines out
+  return $ kernelName `elem` kernelspecs
+
+-- | Replace "~" with $HOME if $HOME is defined. Otherwise, do nothing.
 subHome :: String -> IO String
 subHome path = shelly $ do
   home <- unpack <$> fromMaybe "~" <$> 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.
+-- | Get the path to an executable. If it doensn't exist, fail with an error message complaining
+-- about it.
 path :: Text -> Sh FilePath
 path exe = do
   path <- which $ fromText exe
@@ -217,129 +209,18 @@
 
 -- | Parse an IPython version string into a list of integers.
 parseVersion :: String -> Maybe [Int]
-parseVersion versionStr = 
+parseVersion versionStr =
   let versions = map read' $ split "." versionStr
-      parsed = all isJust versions in
-    if parsed
-    then Just $ map fromJust versions
-    else Nothing
-  where 
+      parsed = all isJust versions
+  in if parsed
+       then Just $ map fromJust versions
+       else Nothing
+  where
     read' :: String -> Maybe Int
-    read' x = 
+    read' x =
       case reads x of
         [(n, _)] -> Just n
-        _ -> Nothing
-
--- | Run an IHaskell application using the given profile.
-runIHaskell :: WhichIPython
-           -> String   -- ^ IHaskell profile name. 
-           -> String    -- ^ IPython app name.
-           -> [String]  -- ^ Arguments to IPython.
-           -> Sh ()
-runIHaskell which profile app args = void $ do
-  -- Try to locate the profile. Do not die if it doesn't exist.
-  errExit False $ ipython which True ["locate", "profile", pack profile]
-
-  -- If the profile doesn't exist, create it.
-  exitCode <- lastExitCode
-  if exitCode /= 0
-  then liftIO $ do
-    putStrLn "Creating IPython profile."
-    setupIPythonProfile which profile
-  -- If the profile exists, update it if necessary.
-  else updateIPythonProfile which profile
-
-  -- Run the IHaskell command.
-  ipython which False $ map pack $ [app, "--profile", profile] ++ args
-
-runConsole :: WhichIPython -> InitInfo -> IO ()
-runConsole which initInfo = void . shelly $ do
-  writeInitInfo initInfo
-  runIHaskell which ipythonProfile "console" []
-
-runNotebook :: WhichIPython -> InitInfo -> Maybe String -> IO ()
-runNotebook which initInfo maybeServeDir = void . shelly $ do
-  notebookDirStr <- fpToString <$> notebookDir
-  let args = case maybeServeDir of 
-               Nothing -> ["--notebook-dir", unpack notebookDirStr]
-               Just dir -> ["--notebook-dir", dir]
-
-  writeInitInfo initInfo
-  runIHaskell which ipythonProfile "notebook" args
-
-writeInitInfo :: InitInfo -> Sh ()
-writeInitInfo info = do
-  filename <- (</> ".last-arguments") <$> ihaskellDir
-  liftIO $ writeFile filename $ show info
-
-readInitInfo :: IO InitInfo
-readInitInfo = shelly $ do
-  filename <- (</>  ".last-arguments") <$> ihaskellDir
-  exists <- test_f filename
-  if exists
-  then read <$> liftIO (readFile filename)
-  else do
-    dir <- fromMaybe "." <$> fmap unpack <$> get_env "HOME"
-    return InitInfo { extensions = [], initCells = [], initDir = dir, frontend = IPythonNotebook }
-
--- | Create the IPython profile.
-setupIPythonProfile :: WhichIPython
-                    -> String -- ^ IHaskell profile name.
-                    -> IO ()
-setupIPythonProfile which profile = shelly $ do
-  -- Create the IPython profile.
-  void $ ipython which True ["profile", "create", pack profile]
-
-  -- Find the IPython profile directory. Make sure to get rid of trailing
-  -- newlines from the output of the `ipython locate` call.
-  ipythonDir <- pack <$> rstrip <$> ipython which True ["locate"]
-  let profileDir = ipythonDir ++ "/profile_" ++ pack profile ++ "/"
-
-  liftIO $ copyProfile profileDir
-  insertIHaskellPath profileDir
-
--- | Update the IPython profile.
-updateIPythonProfile :: WhichIPython
-                     -> String -- ^ IHaskell profile name.
-                     -> Sh ()
-updateIPythonProfile which profile = do
-  -- Find out whether the profile exists.
-  dir <- pack <$> rstrip <$> errExit False (ipython which True ["locate", "profile", pack profile])
-  exitCode <- lastExitCode
-  updated <- if exitCode == 0 && dir /= ""
-            then do
-              let versionFile = fpFromText dir </> profileVersionFile
-              fileExists <- test_f versionFile
-              if not fileExists
-              then return False
-              else liftIO $ do
-                contents <- StrictIO.readFile $ fpToString versionFile
-                return $ strip contents == profileVersion
-            else return False
-
-  when (not updated) $ do
-    putStrLn "Updating IPython profile."
-    liftIO $ copyProfile dir
-    insertIHaskellPath $ dir ++ "/"
-
--- | Copy the profile files into the IPython profile. 
-copyProfile :: Text -> IO ()
-copyProfile profileDir = do
-  profileTar <- Paths.getDataFileName "profile/profile.tar"
-  putStrLn $ pack $ "Loading profile from " ++ profileTar 
-  Tar.extract (unpack profileDir) profileTar 
-
--- | Insert the IHaskell path into the IPython configuration.
-insertIHaskellPath :: Text -> Sh ()
-insertIHaskellPath profileDir = do
-  path <- getIHaskellPath
-  let filename = profileDir ++ "ipython_config.py"
-      template = "exe = '%s'.replace(' ', '\\\\ ')"
-      exeLine = printf template $ unpack path :: String
-
-  liftIO $ do
-    contents <- StrictIO.readFile $ unpack filename
-    writeFile (fromText filename) $ exeLine ++ "\n" ++ contents
+        _        -> Nothing
 
 -- | Get the absolute path to this IHaskell executable.
 getIHaskellPath :: Sh String
@@ -349,62 +230,33 @@
 
   -- If we have an absolute path, that's the IHaskell we're interested in.
   if FS.absolute f
-  then return $ FS.encodeString f
-  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.
+    then return $ FS.encodeString f
+    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 FS.filename f == f
-    then do
-      ihaskellPath <- which "IHaskell"
-      case ihaskellPath of
-        Nothing -> error "IHaskell not on $PATH and not referenced relative to directory."
-        Just path -> return $ FS.encodeString path
-    else do
-      -- If it's actually a relative path, make it absolute.
-      cd <- liftIO getCurrentDirectory
-      return $ FS.encodeString $ FS.decodeString cd FS.</> f
+      then do
+        ihaskellPath <- which "ihaskell"
+        case ihaskellPath of
+          Nothing   -> error "ihaskell not on $PATH and not referenced relative to directory."
+          Just path -> return $ FS.encodeString path
+      else do
+        -- If it's actually a relative path, make it absolute.
+        cd <- liftIO getCurrentDirectory
+        return $ FS.encodeString $ FS.decodeString cd FS.</> f
 
 getSandboxPackageConf :: IO (Maybe String)
 getSandboxPackageConf = shelly $ do
   myPath <- getIHaskellPath
   let sandboxName = ".cabal-sandbox"
-  if not $ sandboxName`isInfixOf` myPath
-  then return Nothing
-  else do
-    let pieces = split "/" myPath
-        sandboxDir = intercalate "/" $ takeWhile (/= sandboxName) pieces  ++ [sandboxName]
-    subdirs <- ls $ fpFromString sandboxDir
-    let confdirs = filter (endswith "packages.conf.d") $ map fpToString subdirs
-    case confdirs of
-      [] -> return Nothing
-      dir:_ -> 
-        return $ Just dir
-
--- | Check whether IPython is properly installed.
-ipythonInstalled :: IO Bool
-ipythonInstalled = shelly $ do
-  ipythonPath <- ipythonExePath DefaultIPython
-  test_f ipythonPath
-
--- | Install IPython from source.
-installIPython :: IO ()
-installIPython = shelly $ do
-  -- Print a message and wait a little.
-  liftIO $ do
-    putStrLn "Installing IPython for IHaskell. This may take a while."
-    threadDelay $ 500 * 1000
-
-  -- Set up the virtualenv.
-  virtualenvScript <- liftIO $ Paths.getDataFileName "installation/virtualenv.sh"
-  venvDir <- fpToText <$> ipythonDir
-  runTmp virtualenvScript [venvDir]
-
-  -- Set up Python depenencies.
-  setenv "ARCHFLAGS" "-Wno-error=unused-command-line-argument-hard-error-in-future"
-  installScript <- liftIO $ Paths.getDataFileName "installation/ipython.sh"
-  runTmp installScript [venvDir]
-
-runTmp script args = withTmpDir $ \tmp -> do
-  cd tmp
-  run_ "bash" $ pack script: args
+  if not $ sandboxName `isInfixOf` myPath
+    then return Nothing
+    else do
+      let pieces = split "/" myPath
+          sandboxDir = intercalate "/" $ takeWhile (/= sandboxName) pieces ++ [sandboxName]
+      subdirs <- ls $ fpFromString sandboxDir
+      let confdirs = filter (endswith "packages.conf.d") $ map fpToString subdirs
+      case confdirs of
+        [] -> return Nothing
+        dir:_ ->
+          return $ Just dir
diff --git a/src/IHaskell/Types.hs b/src/IHaskell/Types.hs
--- a/src/IHaskell/Types.hs
+++ b/src/IHaskell/Types.hs
@@ -1,80 +1,48 @@
-{-# LANGUAGE NoImplicitPrelude, OverloadedStrings, DeriveDataTypeable, DeriveGeneric #-}
-{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE NoImplicitPrelude, OverloadedStrings, DeriveDataTypeable, DeriveGeneric, ExistentialQuantification #-}
+
 -- | Description : All message type definitions.
 module IHaskell.Types (
-  Message (..),
-  MessageHeader (..),
-  MessageType(..),
-  Username,
-  Metadata(..),
-  replyType,
-  ExecutionState (..),
-  StreamType(..),
-  MimeType(..),
-  DisplayData(..),
-  EvaluationResult(..),
-  ExecuteReplyStatus(..),
-  InitInfo(..),
-  KernelState(..),
-  LintStatus(..),
-  Width, Height,
-  FrontendType(..),
-  ViewFormat(..),
-  Display(..),
-  defaultKernelState,
-  extractPlain,
-  kernelOpts,
-  KernelOpt(..),
-  IHaskellDisplay(..),
-  IHaskellWidget(..),
-  Widget(..),
-  CommInfo(..),
-  ) where
-
-import            ClassyPrelude
-import qualified  Data.ByteString.Char8           as Char
-import            Data.Serialize
-import            GHC.Generics
-import            Data.Map                        (Map, empty)
-import            Data.Aeson                      (Value)
-
-import            Text.Read                       as Read hiding (pfail, String)
-import            Text.ParserCombinators.ReadP
-
-import IHaskell.IPython.Kernel
-
-data ViewFormat
-     = Pdf
-     | Html
-     | Ipynb
-     | Markdown
-     | Latex
-     deriving Eq
+    Message(..),
+    MessageHeader(..),
+    MessageType(..),
+    Username,
+    Metadata(..),
+    replyType,
+    ExecutionState(..),
+    StreamType(..),
+    MimeType(..),
+    DisplayData(..),
+    EvaluationResult(..),
+    ExecuteReplyStatus(..),
+    KernelState(..),
+    LintStatus(..),
+    Width,
+    Height,
+    Display(..),
+    defaultKernelState,
+    extractPlain,
+    kernelOpts,
+    KernelOpt(..),
+    IHaskellDisplay(..),
+    IHaskellWidget(..),
+    Widget(..),
+    CommInfo(..),
+    KernelSpec(..),
+    ) where
 
-instance Show ViewFormat where
-  show Pdf         = "pdf"
-  show Html        = "html"
-  show Ipynb       = "ipynb"
-  show Markdown    = "markdown"
-  show Latex       = "latex"
+import           ClassyPrelude
+import qualified Data.ByteString.Char8 as Char
+import           Data.Serialize
+import           GHC.Generics
+import           Data.Map (Map, empty)
+import           Data.Aeson (Value)
 
-instance Read ViewFormat where
-  readPrec = Read.lift $ do
-    str <- munch (const True)
-    case str of
-      "pdf" -> return Pdf
-      "html" -> return Html
-      "ipynb" -> return Ipynb
-      "notebook" -> return Ipynb
-      "latex" -> return Latex
-      "markdown" -> return Markdown
-      "md" -> return Markdown
-      _ -> pfail
+import           IHaskell.IPython.Kernel
 
 -- | A class for displayable Haskell types.
 --
--- IHaskell's displaying of results behaves as if these two
--- overlapping/undecidable instances also existed:
+-- IHaskell's displaying of results behaves as if these two overlapping/undecidable instances also
+-- existed:
 -- 
 -- > instance (Show a) => IHaskellDisplay a
 -- > instance Show a where shows _ = id
@@ -83,12 +51,10 @@
 
 -- | Display as an interactive widget.
 class IHaskellDisplay a => IHaskellWidget a where
-  -- | Output target name for this widget.
-  -- The actual input parameter should be ignored.
+  -- | Output target name for this widget. The actual input parameter should be ignored.
   targetName :: a -> String
 
-  -- | Called when the comm is opened. Allows additional messages to be sent
-  -- after comm open.
+  -- | Called when the comm is opened. Allows additional messages to be sent after comm open.
   open :: a               -- ^ Widget to open a comm port with.
        -> (Value -> IO ()) -- ^ Way to respond to the message.
        -> IO ()
@@ -108,7 +74,7 @@
   close _ _ = return ()
 
 data Widget = forall a. IHaskellWidget a => Widget a
-            deriving Typeable
+  deriving Typeable
 
 instance IHaskellDisplay Widget where
   display (Widget widget) = display widget
@@ -122,101 +88,92 @@
 instance Show Widget where
   show _ = "<Widget>"
 
-
--- | Wrapper for ipython-kernel's DisplayData which allows sending multiple
--- results from the same expression.
+-- | Wrapper for ipython-kernel's DisplayData which allows sending multiple results from the same
+-- expression.
 data Display = Display [DisplayData]
              | ManyDisplay [Display]
-             deriving (Show, Typeable, Generic)
+  deriving (Show, Typeable, Generic)
+
 instance Serialize Display
 
 instance Monoid Display where
-    mempty = Display []
-    ManyDisplay a `mappend` ManyDisplay b = ManyDisplay (a ++ b)
-    ManyDisplay a `mappend` b             = ManyDisplay (a ++ [b])
-    a             `mappend` ManyDisplay b = ManyDisplay (a : b)
-    a             `mappend` b             = ManyDisplay [a,b]
+  mempty = Display []
+  ManyDisplay a `mappend` ManyDisplay b = ManyDisplay (a ++ b)
+  ManyDisplay a `mappend` b = ManyDisplay (a ++ [b])
+  a `mappend` ManyDisplay b = ManyDisplay (a : b)
+  a `mappend` b = ManyDisplay [a, b]
 
 instance Semigroup Display where
   a <> b = a `mappend` b
 
 -- | All state stored in the kernel between executions.
-data KernelState = KernelState
-  { getExecutionCounter :: Int,
-    getLintStatus :: LintStatus,  -- Whether to use hlint, and what arguments to pass it. 
-    getFrontend :: FrontendType,
-    useSvg :: Bool,
-    useShowErrors :: Bool,
-    useShowTypes :: Bool,
-    usePager :: Bool,
-    openComms :: Map UUID Widget
-  }
+data KernelState =
+       KernelState
+         { getExecutionCounter :: Int
+         , getLintStatus :: LintStatus   -- Whether to use hlint, and what arguments to pass it. 
+         , useSvg :: Bool
+         , useShowErrors :: Bool
+         , useShowTypes :: Bool
+         , usePager :: Bool
+         , openComms :: Map UUID Widget
+         , kernelDebug :: Bool
+         }
   deriving Show
 
 defaultKernelState :: KernelState
 defaultKernelState = KernelState
-  { getExecutionCounter = 1,
-    getLintStatus = LintOn,
-    getFrontend = IPythonConsole,
-    useSvg = True,
-    useShowErrors = False,
-    useShowTypes = False,
-    usePager = True,
-    openComms = empty
+  { getExecutionCounter = 1
+  , getLintStatus = LintOn
+  , useSvg = True
+  , useShowErrors = False
+  , useShowTypes = False
+  , usePager = False
+  , openComms = empty
+  , kernelDebug = False
   }
 
-data FrontendType
-     = IPythonConsole
-     | IPythonNotebook
-     deriving (Show, Eq, Read)
-
 -- | Kernel options to be set via `:set` and `:option`.
-data KernelOpt = KernelOpt {
-    getOptionName :: [String],                          -- ^ Ways to set this option via `:option`
-    getSetName :: [String],                             -- ^ Ways to set this option via `:set`
-    getUpdateKernelState :: KernelState -> KernelState   -- ^ Function to update the kernel state.
-  }
+data KernelOpt =
+       KernelOpt
+         { getOptionName :: [String]                          -- ^ Ways to set this option via `:option`
+         , getSetName :: [String]                             -- ^ Ways to set this option via `:set`
+         , getUpdateKernelState :: KernelState -> KernelState -- ^ Function to update the kernel
+                                                              -- state.
+         }
 
 kernelOpts :: [KernelOpt]
 kernelOpts =
-  [ KernelOpt ["lint"]           []     $ \state -> state { getLintStatus = LintOn }
-  , KernelOpt ["no-lint"]        []     $ \state -> state { getLintStatus = LintOff }
-  , KernelOpt ["svg"]            []     $ \state -> state { useSvg        = True }
-  , KernelOpt ["no-svg"]         []     $ \state -> state { useSvg        = False }
-  , KernelOpt ["show-types"]     ["+t"] $ \state -> state { useShowTypes  = True }
-  , KernelOpt ["no-show-types"]  ["-t"] $ \state -> state { useShowTypes  = False }
-  , KernelOpt ["show-errors"]    []     $ \state -> state { useShowErrors = True }
-  , KernelOpt ["no-show-errors"] []     $ \state -> state { useShowErrors = False }
-  , KernelOpt ["pager"]          []     $ \state -> state { usePager = True }
-  , KernelOpt ["no-pager"]       []     $ \state -> state { usePager = False }
+  [ KernelOpt ["lint"] [] $ \state -> state { getLintStatus = LintOn }
+  , KernelOpt ["no-lint"] [] $ \state -> state { getLintStatus = LintOff }
+  , KernelOpt ["svg"] [] $ \state -> state { useSvg = True }
+  , KernelOpt ["no-svg"] [] $ \state -> state { useSvg = False }
+  , KernelOpt ["show-types"] ["+t"] $ \state -> state { useShowTypes = True }
+  , KernelOpt ["no-show-types"] ["-t"] $ \state -> state { useShowTypes = False }
+  , KernelOpt ["show-errors"] [] $ \state -> state { useShowErrors = True }
+  , KernelOpt ["no-show-errors"] [] $ \state -> state { useShowErrors = False }
+  , KernelOpt ["pager"] [] $ \state -> state { usePager = True }
+  , KernelOpt ["no-pager"] [] $ \state -> state { usePager = False }
   ]
 
--- | Initialization information for the kernel.
-data InitInfo = InitInfo {
-  extensions :: [String],   -- ^ Extensions to enable at start.
-  initCells :: [String],    -- ^ Code blocks to run before start.
-  initDir :: String,        -- ^ Which directory this kernel should pretend to operate in.
-  frontend :: FrontendType  -- ^ What frontend this serves.
-  }
-  deriving (Show, Read)
-
 -- | Current HLint status.
-data LintStatus
-     = LintOn
-     | LintOff
-     deriving (Eq, Show)
+data LintStatus = LintOn
+                | LintOff
+  deriving (Eq, Show)
 
 data CommInfo = CommInfo Widget UUID String
+  deriving Show
 
 -- | 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 :: String,       -- ^ Text to display in the IPython pager.
-    startComms :: [CommInfo]  -- ^ Comms to start.
-  }
+                      -- | An intermediate result which communicates what has been printed thus
+                      -- far.
+                        IntermediateResult
+                          { outputs :: Display      -- ^ Display outputs.
+                          }
+                      |
+                        FinalResult
+                          { outputs :: Display        -- ^ Display outputs.
+                          , pagerOut :: String        -- ^ Text to display in the IPython pager.
+                          , startComms :: [CommInfo]  -- ^ Comms to start.
+                          }
+  deriving Show
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -1,7 +1,8 @@
-{-# LANGUAGE NoImplicitPrelude, CPP, OverloadedStrings, ScopedTypeVariables #-}
+{-# LANGUAGE NoImplicitPrelude, CPP, OverloadedStrings, ScopedTypeVariables, QuasiQuotes #-}
+
 -- | Description : Argument parsing and basic messaging loop, using Haskell
 --                 Chans to communicate with the ZeroMQ sockets.
-module Main where
+module Main (main) where
 
 -- Prelude imports.
 import           ClassyPrelude hiding (last, liftIO, readChan, writeChan)
@@ -17,6 +18,7 @@
 import           Text.Printf
 import           System.Posix.Signals
 import qualified Data.Map as Map
+import           Data.String.Here (hereFile)
 
 -- IHaskell imports.
 import           IHaskell.Convert (convert)
@@ -35,7 +37,6 @@
 
 -- GHC API imports.
 import           GHC hiding (extensions, language)
-import qualified GHC.Paths
 
 -- | Compute the GHC API version number using the dist/build/autogen/cabal_macros.h
 ghcVersionInts :: [Int]
@@ -44,7 +45,14 @@
     dotToSpace '.' = ' '
     dotToSpace x = x
 
+ihaskellCSS :: String
+ihaskellCSS = [hereFile|html/custom.css|]
 
+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 <$> map unpack <$> getArgs
@@ -52,95 +60,43 @@
     Left errorMessage -> hPutStrLn stderr errorMessage
     Right args        -> ihaskell args
 
-chooseIPython [] = return DefaultIPython
-chooseIPython (IPythonFrom path:_) = ExplicitIPython <$> subHome path
-chooseIPython (_:xs) = chooseIPython xs
-
 ihaskell :: Args -> IO ()
--- If no mode is specified, print help text.
 ihaskell (Args (ShowHelp help) _) = putStrLn $ pack help
-
 ihaskell (Args ConvertLhs args) = showingHelp ConvertLhs args $ convert args
-
-ihaskell (Args Console flags) = showingHelp Console flags $ do
-  ipython <- chooseIPython flags
-  setupIPython ipython
-
-  flags <- addDefaultConfFile flags
-  info <- initInfo IPythonConsole flags
-  runConsole ipython info
-
-ihaskell (Args mode@(View (Just fmt) (Just name)) args) = showingHelp mode args $ do
-  ipython <- chooseIPython args
-  nbconvert ipython fmt name
-
-ihaskell (Args Notebook flags) = showingHelp Notebook flags $ do
-  ipython <- chooseIPython flags
-  setupIPython ipython
-
-  let server = case mapMaybe serveDir flags of
-                 [] -> Nothing
-                 xs -> Just $ last xs
-
-  flags <- addDefaultConfFile flags
-
-  undirInfo <- initInfo IPythonNotebook flags
-  curdir <- getCurrentDirectory
-  let info = undirInfo { initDir = curdir }
-
-  runNotebook ipython info server
-  where
-    serveDir (ServeFrom dir) = Just dir
-    serveDir _ = Nothing
-
-ihaskell (Args (Kernel (Just filename)) flags) = do
-  initInfo <- readInitInfo
-  runKernel libdir filename initInfo
-
-  where
-    libdir = case flags of
-      []              -> GHC.Paths.libdir
-      [GhcLibDir dir] -> dir
-
-
--- | Add a conf file to the arguments if none exists.
-addDefaultConfFile :: [Argument] -> IO [Argument]
-addDefaultConfFile flags = do
-  def <- defaultConfFile
-  case (find isConfFile flags, def) of
-    (Nothing, Just file) -> return $ ConfFile file : flags
-    _ -> return flags
-  where
-    isConfFile (ConfFile _) = True
-    isConfFile _ = False
+ihaskell (Args InstallKernelSpec args) = showingHelp InstallKernelSpec args $ do
+  let kernelSpecOpts = parseKernelArgs args
+  replaceIPythonKernelspec kernelSpecOpts
+ihaskell (Args (Kernel (Just filename)) args) = do
+  let kernelSpecOpts = parseKernelArgs args
+  runKernel kernelSpecOpts filename
 
 showingHelp :: IHaskellMode -> [Argument] -> IO () -> IO ()
 showingHelp mode flags act =
-  case find (==Help) flags of
+  case find (== Help) flags of
     Just _ ->
       putStrLn $ pack $ help mode
     Nothing ->
       act
 
 -- | Parse initialization information from the flags.
-initInfo :: FrontendType -> [Argument] -> IO InitInfo
-initInfo front [] = return InitInfo { extensions = [], initCells = [], initDir = ".", frontend = front }
-initInfo front (flag:flags) = do
-  info <- initInfo front flags
-  case flag of
-    Extension ext -> return info { extensions = ext:extensions info }
-    ConfFile filename -> do
-      cell <- readFile (fpFromText $ pack filename)
-      return info { initCells = cell:initCells info }
-    _ -> return info
+parseKernelArgs :: [Argument] -> KernelSpecOptions
+parseKernelArgs = foldl' addFlag defaultKernelSpecOptions
+  where
+    addFlag kernelSpecOpts (ConfFile filename) =
+      kernelSpecOpts { kernelSpecConfFile = return (Just filename) }
+    addFlag kernelSpecOpts KernelDebug =
+      kernelSpecOpts { kernelSpecDebug = True }
+    addFlag kernelSpecOpts (GhcLibDir libdir) =
+      kernelSpecOpts { kernelSpecGhcLibdir = libdir }
+    addFlag kernelSpecOpts flag = error $ "Unknown flag" ++ show flag
 
 -- | Run the IHaskell language kernel.
-runKernel :: String -- ^ GHC libdir.
-          -> String    -- ^ Filename of profile JSON file.
-          -> InitInfo  -- ^ Initialization information from the invocation.
+runKernel :: KernelSpecOptions -- ^ Various options from when the kernel was installed.
+          -> String            -- ^ File with kernel profile JSON (ports, etc).
           -> IO ()
-runKernel libdir profileSrc initInfo = do
-  setCurrentDirectory $ initDir initInfo
+runKernel kernelOpts profileSrc = do
+  let debug = kernelSpecDebug kernelOpts
+      libdir = kernelSpecGhcLibdir kernelOpts
 
   -- Parse the profile file.
   Just profile <- liftM decode . readFile . fpFromText $ pack profileSrc
@@ -150,33 +106,31 @@
   Stdin.recordKernelProfile dir profile
 
   -- Serve on all sockets and ports defined in the profile.
-  interface <- serveProfile profile
+  interface <- serveProfile profile debug
 
   -- Create initial state in the directory the kernel *should* be in.
   state <- initialKernelState
   modifyMVar_ state $ \kernelState -> return $
-    kernelState { getFrontend = frontend initInfo }
+    kernelState { kernelDebug = debug }
 
   -- Receive and reply to all messages on the shell socket.
   interpret libdir True $ 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).
+    -- 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
 
-    -- Initialize the context by evaluating everything we got from the
-    -- command line flags. This includes enabling some extensions and also
-    -- running some code.
-    let extLines = map (":extension " ++) $ extensions initInfo
-        noPublish _ = return ()
+    -- Initialize the context by evaluating everything we got from the command line flags.
+    let noPublish _ = return ()
         evaluator line = void $ do
           -- Create a new state each time.
           stateVar <- liftIO initialKernelState
           state <- liftIO $ takeMVar stateVar
           evaluate state line noPublish
 
-    mapM_ evaluator extLines
-    mapM_ evaluator $ initCells initInfo
+    confFile <- liftIO $ kernelSpecConfFile kernelOpts
+    case confFile of
+      Just filename -> liftIO (readFile $ fpFromString filename) >>= evaluator
+      Nothing       -> return ()
 
     forever $ do
       -- Read the request from the request channel.
@@ -185,34 +139,34 @@
       -- Create a header for the reply.
       replyHeader <- createReplyHeader (header request)
 
-      -- We handle comm messages and normal ones separately.
-      -- The normal ones are a standard request/response style, while comms
-      -- can be anything, and don't necessarily require a response.
+      -- We handle comm messages and normal ones separately. The normal ones are a standard
+      -- request/response style, while comms can be anything, and don't necessarily require a response.
       if isCommMessage request
-      then liftIO $ do
-        oldState <- takeMVar state
-        let replier = writeChan (iopubChannel interface)
-        newState <- handleComm replier oldState request replyHeader
-        putMVar state newState
-        writeChan (shellReplyChannel interface) SendNothing
-      else do
-        -- Create the reply, possibly modifying kernel state.
-        oldState <- liftIO $ takeMVar state
-        (newState, reply) <- replyTo interface request replyHeader oldState
-        liftIO $ putMVar state newState
+        then liftIO $ do
+          oldState <- takeMVar state
+          let replier = writeChan (iopubChannel interface)
+          newState <- handleComm replier oldState request replyHeader
+          putMVar state newState
+          writeChan (shellReplyChannel interface) SendNothing
+        else do
+          -- Create the reply, possibly modifying kernel state.
+          oldState <- liftIO $ takeMVar state
+          (newState, reply) <- replyTo interface request replyHeader oldState
+          liftIO $ putMVar state newState
 
-        -- Write the reply to the reply channel.
-        liftIO $ writeChan (shellReplyChannel interface) reply
+          -- Write the reply to the reply channel.
+          liftIO $ writeChan (shellReplyChannel interface) reply
+
   where
     ignoreCtrlC =
-      installHandler keyboardSignal (CatchOnce $ putStrLn "Press Ctrl-C again to quit kernel.") Nothing
+      installHandler keyboardSignal (CatchOnce $ putStrLn "Press Ctrl-C again to quit kernel.")
+        Nothing
 
     isCommMessage req = msgType (header req) `elem` [CommDataMessage, CommCloseMessage]
 
 -- Initial kernel state.
 initialKernelState :: IO (MVar KernelState)
-initialKernelState =
-  newMVar defaultKernelState
+initialKernelState = newMVar defaultKernelState
 
 -- | Duplicate a message header, giving it a new UUID and message type.
 dupHeader :: MessageHeader -> MessageType -> IO MessageHeader
@@ -229,62 +183,59 @@
   let repType = fromMaybe err (replyType $ msgType parent)
       err = error $ "No reply for message " ++ show (msgType parent)
 
-  return MessageHeader {
-    identifiers = identifiers parent,
-    parentHeader = Just parent,
-    metadata = Map.fromList [],
-    messageId = newMessageId,
-    sessionId = sessionId parent,
-    username = username parent,
-    msgType = repType
-  }
+  return
+    MessageHeader
+      { identifiers = identifiers parent
+      , parentHeader = Just parent
+      , metadata = Map.fromList []
+      , messageId = newMessageId
+      , sessionId = sessionId parent
+      , username = username parent
+      , msgType = repType
+      }
 
 -- | Compute a reply to a message.
 replyTo :: ZeroMQInterface -> Message -> MessageHeader -> KernelState -> Interpreter (KernelState, Message)
-
--- Reply to kernel info requests with a kernel info reply. No computation
--- needs to be done, as a kernel info reply is a static object (all info is
--- hard coded into the representation of that message type).
+-- Reply to kernel info requests with a kernel info reply. No computation needs to be done, as a
+-- kernel info reply is a static object (all info is hard coded into the representation of that
+-- message type).
 replyTo _ KernelInfoRequest{} replyHeader state =
-  return (state, KernelInfoReply {
-    header = replyHeader,
-    language = "haskell",
-    versionList = ghcVersionInts
-   })
+  return
+    (state, KernelInfoReply
+              { header = replyHeader
+              , language = "haskell"
+              , versionList = ghcVersionInts
+              })
 
--- 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
-    exitSuccess
+-- 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
+  exitSuccess
 
--- Reply to an execution request. The reply itself does not require
--- computation, but this causes messages to be sent to the IOPub socket
--- with the output of the code in the execution request.
-replyTo interface req@ExecuteRequest{ getCode = code } replyHeader state = do
- -- Convenience function to send a message to the IOPub socket.
+-- Reply to an execution request. The reply itself does not require computation, but this causes
+-- messages to be sent to the IOPub socket with the output of the code in the execution request.
+replyTo interface req@ExecuteRequest { getCode = code } replyHeader state = do
+  -- Convenience function to send a message to the IOPub socket.
   let send msg = liftIO $ writeChan (iopubChannel interface) msg
 
   -- Log things so that we can use stdin.
   dir <- liftIO getIHaskellDir
   liftIO $ Stdin.recordParentHeader dir $ header req
 
-  -- Notify the frontend that the kernel is busy computing.
-  -- All the headers are copies of the reply header with a different
-  -- message type, because this preserves the session ID, parent header,
-  -- and other important information.
+  -- Notify the frontend that the kernel is busy computing. All the headers are copies of the reply
+  -- header with a different message type, because this preserves the session ID, parent header, and
+  -- other important information.
   busyHeader <- liftIO $ dupHeader replyHeader StatusMessage
   send $ PublishStatus busyHeader Busy
 
-  -- Construct a function for publishing output as this is going.
-  -- This function accepts a boolean indicating whether this is the final
-  -- output and the thing to display. Store the final outputs in a list so
-  -- that when we receive an updated non-final output, we can clear the
-  -- entire output and re-display with the updated output.
-  displayed    <- liftIO $ newMVar []
+  -- Construct a function for publishing output as this is going. This function accepts a boolean
+  -- indicating whether this is the final output and the thing to display. Store the final outputs in
+  -- a list so that when we receive an updated non-final output, we can clear the entire output and
+  -- re-display with the updated output.
+  displayed <- liftIO $ newMVar []
   updateNeeded <- liftIO $ newMVar False
-  pagerOutput  <- liftIO $ newMVar ""
+  pagerOutput <- liftIO $ newMVar ""
   let clearOutput = do
         header <- dupHeader replyHeader ClearOutputMessage
         send $ ClearOutput header True
@@ -292,12 +243,16 @@
       sendOutput (ManyDisplay manyOuts) = mapM_ sendOutput manyOuts
       sendOutput (Display outs) = do
         header <- dupHeader replyHeader DisplayDataMessage
-        send $ PublishDisplayData header "haskell" $ map convertSvgToHtml outs
+        send $ PublishDisplayData header "haskell" $ map (convertSvgToHtml . prependCss) outs
 
       convertSvgToHtml (DisplayData MimeSvg svg) = html $ makeSvgImg $ base64 $ encodeUtf8 svg
       convertSvgToHtml x = x
       makeSvgImg base64data = unpack $ "<img src=\"data:image/svg+xml;base64," ++ base64data ++ "\"/>"
 
+      prependCss (DisplayData MimeHtml html) =
+        DisplayData MimeHtml $concat ["<style>", pack ihaskellCSS, "</style>", html]
+      prependCss x = x
+
       startComm :: CommInfo -> IO ()
       startComm (CommInfo widget uuid target) = do
         -- Send the actual comm open.
@@ -312,9 +267,10 @@
 
       publish :: EvaluationResult -> IO ()
       publish result = do
-        let final = case result of
-                      IntermediateResult {} -> False
-                      FinalResult {} -> True
+        let final =
+              case result of
+                IntermediateResult{} -> False
+                FinalResult{}        -> True
             outs = outputs result
 
         -- If necessary, clear all previous output and redraw.
@@ -327,12 +283,11 @@
         -- Draw this message.
         sendOutput 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.
+        -- 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.
         modifyMVar_ updateNeeded (const $ return $ not final)
         when final $ do
-          modifyMVar_ displayed (return . (outs:))
+          modifyMVar_ displayed (return . (outs :))
 
           -- Start all comms that need to be started.
           mapM_ startComm $ startComms result
@@ -341,8 +296,8 @@
           let pager = pagerOut result
           unless (null pager) $
             if usePager state
-            then modifyMVar_ pagerOutput (return . (++ pager ++ "\n"))
-            else sendOutput $ Display [html pager]
+              then modifyMVar_ pagerOutput (return . (++ pager ++ "\n"))
+              else sendOutput $ Display [html pager]
 
   let execCount = getExecutionCounter state
   -- Let all frontends know the execution count and code that's about to run
@@ -358,34 +313,47 @@
 
   -- Take pager output if we're using the pager.
   pager <- if usePager state
-          then liftIO $ readMVar pagerOutput
-          else return ""
-  return (updatedState, ExecuteReply {
-    header = replyHeader,
-    pagerOutput = pager,
-    executionCounter = execCount,
-    status = Ok
-  })
+             then liftIO $ readMVar pagerOutput
+             else return ""
+  return
+    (updatedState, ExecuteReply
+                     { header = replyHeader
+                     , pagerOutput = pager
+                     , executionCounter = execCount
+                     , status = Ok
+                     })
 
 
 replyTo _ req@CompleteRequest{} replyHeader state = do
-  let line = getCodeLine req
-  (matchedText, completions) <- complete (unpack line) (getCursorPos req)
+  let code = getCode req
+      pos = getCursorPos req
+  (matchedText, completions) <- complete (unpack code) pos
 
-  let reply =  CompleteReply replyHeader (map pack completions) (pack matchedText) line True
-  return (state,  reply)
+  let start = pos - length matchedText
+      end = pos
+      reply = CompleteReply replyHeader (map pack completions) start end Map.empty True
+  return (state, reply)
 
--- | Reply to the object_info_request message. Given an object name, return
--- | the associated type calculated by GHC.
-replyTo _ ObjectInfoRequest{objectName = oname} replyHeader state = do
+-- Reply to the object_info_request message. Given an object name, return the associated type
+-- calculated by GHC.
+replyTo _ ObjectInfoRequest { objectName = oname } replyHeader state = do
   docs <- pack <$> info (unpack oname)
-  let reply = ObjectInfoReply {
-                header = replyHeader,
-                objectName = oname,
-                objectFound = strip docs /= "",
-                objectTypeString = docs,
-                objectDocString  = docs
-              }
+  let reply = ObjectInfoReply
+        { header = replyHeader
+        , objectName = oname
+        , objectFound = strip docs /= ""
+        , objectTypeString = docs
+        , objectDocString = docs
+        }
+  return (state, reply)
+
+-- TODO: Implement history_reply.
+replyTo _ HistoryRequest{} replyHeader state = do
+  let reply = HistoryReply
+        { header = replyHeader
+        -- FIXME
+        , historyReply = []
+        }
   return (state, reply)
 
 handleComm :: (Message -> IO ()) -> KernelState -> Message -> MessageHeader -> IO KernelState
