Commit 87cf416d authored by Adam Vogt's avatar Adam Vogt

Merge branch 'master' of https://github.com/gibiansky/IHaskell

parents 7cda8c7a 144fa549
......@@ -40,47 +40,49 @@ category: Development
build-type: Simple
-- Constraint on the version of Cabal needed to build this package.
cabal-version: >=1.8
cabal-version: >=1.16
data-files:
profile/profile.tar
library
hs-source-dirs: src
build-depends: base ==4.6.*,
cereal == 0.3.*,
HTTP,
base64-bytestring >= 1.0,
process >= 1.1,
hlint,
cmdargs >= 0.10,
tar,
ipython-kernel,
ghc-parser,
unix >= 2.6,
hspec,
aeson >=0.6,
MissingH >=1.2,
classy-prelude >=0.7,
bytestring >=0.10,
containers >=0.5,
ghc ==7.6.*,
ghc-paths ==0.1.*,
random >=1.0,
split >= 0.2,
utf8-string,
strict >=0.3,
shelly >=1.3,
system-argv0,
directory,
here,
system-filepath,
filepath,
mtl >= 2.1,
transformers,
haskeline,
HUnit,
parsec
default-language: Haskell2010
build-depends:
base ==4.6.*,
aeson >=0.6,
base64-bytestring >=1.0,
bytestring >=0.10,
cereal ==0.3.*,
classy-prelude >=0.7,
cmdargs >=0.10,
containers >=0.5,
directory -any,
filepath -any,
ghc ==7.6.*,
ghc-parser -any,
ghc-paths ==0.1.*,
haskeline -any,
here -any,
hlint -any,
hspec -any,
HTTP -any,
HUnit -any,
ipython-kernel -any,
MissingH >=1.2,
mtl >=2.1,
parsec -any,
process >=1.1,
random >=1.0,
shelly >=1.3,
split >= 0.2,
strict >=0.3,
system-argv0 -any,
system-filepath -any,
tar -any,
transformers -any,
unix >= 2.6,
utf8-string -any
exposed-modules: IHaskell.Display
IHaskell.Eval.Completion
......@@ -121,81 +123,84 @@ executable IHaskell
extensions: DoAndIfThenElse
-- Other library packages from which modules are imported.
build-depends: base ==4.6.*,
cereal == 0.3.*,
HTTP,
base64-bytestring >= 1.0,
process >= 1.1,
hlint,
cmdargs >= 0.10,
tar,
ghc-parser,
ipython-kernel,
unix >= 2.6,
hspec,
aeson >=0.6,
MissingH >=1.2,
classy-prelude >=0.7,
bytestring >=0.10,
containers >=0.5,
ghc ==7.6.*,
ghc-paths ==0.1.*,
random >=1.0,
split >= 0.2,
utf8-string,
strict >=0.3,
shelly >=1.3,
system-argv0,
directory,
here,
system-filepath,
filepath,
mtl >= 2.1,
transformers,
haskeline,
HUnit,
parsec
default-language: Haskell2010
build-depends:
base ==4.6.*,
aeson >=0.6,
base64-bytestring >=1.0,
bytestring >=0.10,
cereal ==0.3.*,
classy-prelude >=0.7,
cmdargs >=0.10,
containers >=0.5,
directory -any,
filepath -any,
ghc ==7.6.*,
ghc-parser -any,
ghc-paths ==0.1.*,
haskeline -any,
here -any,
hlint -any,
hspec -any,
HTTP -any,
HUnit -any,
ipython-kernel -any,
MissingH >=1.2,
mtl >=2.1,
parsec -any,
process >=1.1,
random >=1.0,
shelly >=1.3,
split >= 0.2,
strict >=0.3,
system-argv0 -any,
system-filepath -any,
tar -any,
transformers -any,
unix >= 2.6,
utf8-string -any
Test-Suite hspec
hs-source-dirs: src
Type: exitcode-stdio-1.0
Ghc-Options: -threaded
Main-Is: Hspec.hs
build-depends: base ==4.6.*,
cereal == 0.3.*,
HTTP,
base64-bytestring >= 1.0,
process >= 1.1,
hlint,
cmdargs >= 0.10,
tar,
ghc-parser,
ipython-kernel,
unix >= 2.6,
hspec,
aeson >=0.6,
MissingH >=1.2,
classy-prelude >=0.7,
bytestring >=0.10,
containers >=0.5,
ghc ==7.6.*,
ghc-paths ==0.1.*,
random >=1.0,
split >= 0.2,
utf8-string,
strict >=0.3,
shelly >=1.3,
system-argv0,
directory,
here,
system-filepath,
filepath,
mtl >= 2.1,
transformers,
haskeline,
HUnit,
setenv,
parsec
default-language: Haskell2010
build-depends:
base ==4.6.*,
aeson >=0.6,
base64-bytestring >=1.0,
bytestring >=0.10,
cereal ==0.3.*,
classy-prelude >=0.7,
cmdargs >=0.10,
containers >=0.5,
directory -any,
filepath -any,
ghc ==7.6.*,
ghc-parser -any,
ghc-paths ==0.1.*,
haskeline -any,
here -any,
hlint -any,
hspec -any,
HTTP -any,
HUnit -any,
ipython-kernel -any,
MissingH >=1.2,
mtl >=2.1,
parsec -any,
process >=1.1,
random >=1.0,
shelly >=1.3,
split >= 0.2,
strict >=0.3,
system-argv0 -any,
system-filepath -any,
tar -any,
transformers -any,
unix >= 2.6,
utf8-string -any
extensions: DoAndIfThenElse
OverloadedStrings
......
{
"metadata": {
"language": "haskell",
"name": ""
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- First of all, we can evaluate simple expressions.\n",
"3 + 5\n",
"\"Hello, \" ++ \"World!\""
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"text": [
"8"
]
},
{
"metadata": {},
"output_type": "display_data",
"text": [
"\"Hello, World!\""
]
}
],
"prompt_number": 1
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- Unlike in GHCi, we can have multi-line expressions.\n",
"concat [\n",
" \"Hello,\",\n",
" \", \",\n",
" \"World!\"\n",
" ] :: String\n",
" \n",
"-- We can also have normal Haskell declarations, without `let`.\n",
"-- As long as you group type signatures and declarations together,\n",
"-- you can use pattern matching and add type signatures.\n",
"thing :: String -> Int -> Int\n",
"thing \"no\" _ = 100\n",
"thing str int = int + length str\n",
"\n",
"thing \"no\" 10\n",
"thing \"ah\" 10"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"text": [
"\"Hello,, World!\""
]
},
{
"metadata": {},
"output_type": "display_data",
"text": [
"100"
]
},
{
"metadata": {},
"output_type": "display_data",
"text": [
"12"
]
}
],
"prompt_number": 2
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- We can also do IO.\n",
"print \"What's going on?\""
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"text": [
"\"What's going on?\""
]
}
],
"prompt_number": 3
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- We can disable extensions.\n",
":ext NoEmptyDataDecls\n",
"data Thing"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<span style='color: red; font-style: italic;'>`Thing' has no constructors<br/> (-XEmptyDataDecls permits this)</span>"
],
"metadata": {},
"output_type": "display_data",
"text": [
"`Thing' has no constructors\n",
" (-XEmptyDataDecls permits this)"
]
}
],
"prompt_number": 4
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- And enable extensions.\n",
":ext EmptyDataDecls\n",
"data Thing"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 5
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- Various data declarations work fine.\n",
"data One\n",
" = A String\n",
" | B Int\n",
" deriving Show\n",
"\n",
"print [A \"Hello\", B 10]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"text": [
"[A \"Hello\",B 10]"
]
}
],
"prompt_number": 6
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- We can look at types like in GHCi.\n",
":ty 3 + 3"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<span style='font-weight: bold; color: green;'>forall a. Num a => a</span>"
],
"metadata": {},
"output_type": "display_data",
"text": [
"forall a. Num a => a"
]
}
],
"prompt_number": 7
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- And we can inspect info of things!\n",
":info Integral"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"text": [
"class (Real a, Enum a) => Integral a where\n",
" quot :: a -> a -> a\n",
" rem :: a -> a -> a\n",
" div :: a -> a -> a\n",
" mod :: a -> a -> a\n",
" quotRem :: a -> a -> (a, a)\n",
" divMod :: a -> a -> (a, a)\n",
" toInteger :: a -> Integer\n",
" \t-- Defined in `GHC.Real'\n",
"instance Integral Integer -- Defined in `GHC.Real'\n",
"instance Integral Int -- Defined in `GHC.Real'"
]
}
],
"prompt_number": 8
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- Results are printed as we go, even from a single expression.\n",
"import Control.Monad\n",
"import Control.Concurrent\n",
"\n",
"forM_ [1..5] $ \\x -> do\n",
" print x\n",
" threadDelay $ 100 * 1000"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"text": [
"1\n",
"2\n",
"3\n",
"4\n",
"5"
]
}
],
"prompt_number": 9
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- We can display Maybes fancily for Show-able types.\n",
"Just ()\n",
"Nothing\n",
"\n",
"-- But it dies if it's not showable.\n",
"data NoShow = X Int\n",
"Just (X 3)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<span style='color: green; font-weight: bold;'>Just</span><span style='font-family: monospace;'>()</span>"
],
"metadata": {},
"output_type": "display_data",
"text": [
"Just ()"
]
},
{
"html": [
"<span style='color: red; font-weight: bold;'>Nothing</span>"
],
"metadata": {},
"output_type": "display_data",
"text": [
"Nothing"
]
},
{
"html": [
"<span style='color: red; font-style: italic;'>No instance for (Show NoShow)<br/> arising from a use of `print'<br/>Possible fix:<br/> add an instance declaration for (Show NoShow)</span>"
],
"metadata": {},
"output_type": "display_data",
"text": [
"No instance for (Show NoShow)\n",
" arising from a use of `print'\n",
"Possible fix:\n",
" add an instance declaration for (Show NoShow)"
]
}
],
"prompt_number": 10
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- Aeson JSON data types are displayed nicely.\n",
":ext OverloadedStrings\n",
"\n",
"import Data.Aeson\n",
"\n",
"data Coord = Coord { x :: Double, y :: Double }\n",
"instance ToJSON Coord where\n",
" toJSON (Coord x y) = object [\"x\" .= x, \"y\" .= y]\n",
"\n",
"Null\n",
"Bool True\n",
"toJSON (Coord 3 2)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<div class=\"highlight-code\" id=\"javascript\">null</div>"
],
"metadata": {},
"output_type": "display_data",
"text": [
"null"
]
},
{
"html": [
"<div class=\"highlight-code\" id=\"javascript\">true</div>"
],
"metadata": {},
"output_type": "display_data",
"text": [
"true"
]
},
{
"html": [
"<div class=\"highlight-code\" id=\"javascript\">{\n",
" \"x\": 3.0,\n",
" \"y\": 2.0\n",
"}</div>"
],
"metadata": {},
"output_type": "display_data",
"text": [
"{\n",
" \"x\": 3.0,\n",
" \"y\": 2.0\n",
"}"
]
}
],
"prompt_number": 11
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- Small bits of HTML generated via Blaze are displayed.\n",
"import Prelude hiding (div, id)\n",
"import Text.Blaze.Html4.Strict hiding (map, style)\n",
"import Text.Blaze.Html4.Strict.Attributes\n",
"\n",
"div ! style \"color: red\" $ do\n",
" p \"This is an example of BlazeMarkup syntax.\"\n",
" b \"Hello\"\n",
" \n",
"forM [1..5] $ \\size -> do\n",
" let s = toValue $ size * 80\n",
" img ! src \"/static/base/images/ipynblogo.png\" ! width s"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<div style=\"color: red\">\n",
" <p>\n",
" This is an example of BlazeMarkup syntax.\n",
" </p>\n",
" <b>\n",
" Hello\n",
" </b>\n",
"</div>\n"
],
"metadata": {},
"output_type": "display_data",
"text": [
"<div style=\"color: red\">\n",
" <p>\n",
" This is an example of BlazeMarkup syntax.\n",
" </p>\n",
" <b>\n",
" Hello\n",
" </b>\n",
"</div>"
]
},
{
"html": [
"<img src=\"/static/base/images/ipynblogo.png\" width=\"80\">\n",
"<img src=\"/static/base/images/ipynblogo.png\" width=\"160\">\n",
"<img src=\"/static/base/images/ipynblogo.png\" width=\"240\">\n",
"<img src=\"/static/base/images/ipynblogo.png\" width=\"320\">\n",
"<img src=\"/static/base/images/ipynblogo.png\" width=\"400\">\n"
],
"metadata": {},
"output_type": "display_data",
"text": [
"<img src=\"/static/base/images/ipynblogo.png\" width=\"80\">\n",
"<img src=\"/static/base/images/ipynblogo.png\" width=\"160\">\n",
"<img src=\"/static/base/images/ipynblogo.png\" width=\"240\">\n",
"<img src=\"/static/base/images/ipynblogo.png\" width=\"320\">\n",
"<img src=\"/static/base/images/ipynblogo.png\" width=\"400\">"
]
}
],
"prompt_number": 12
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- We can draw diagrams, right in the notebook.\n",
":extension NoMonomorphismRestriction\n",
"import Diagrams.Prelude\n",
"\n",
"-- By Brent Yorgey\n",
"-- Draw a Sierpinski triangle!\n",
"sierpinski 1 = eqTriangle 1\n",
"sierpinski n = s\n",
" ===\n",
" (s ||| s) # centerX\n",
" where s = sierpinski (n-1)\n",
"\n",
"-- The `diagram` function is used to display them in the notebook.\n",
"diagram $ sierpinski 4\n",
" # centerXY\n",
" # fc black\n",
" `atop` square 10\n",
" # fc white"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"png": "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"
}
],
"prompt_number": 13
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- We can draw small charts in the notebook.\n",
"-- This example is taken from the haskell-chart documentation.\n",
"import Graphics.Rendering.Chart \n",
"import Data.Default.Class\n",
"import Control.Lens\n",
"\n",
"let values = [\n",
" (\"Mexico City\" , 19.2, 0),\n",
" (\"Mumbai\" , 12.9, 10), \n",
" (\"Sydney\" , 4.3, 0),\n",
" (\"London\" , 8.3, 0), \n",
" (\"New York\" , 8.2, 25)]\n",
" \n",
"pitem (s, v, o) = pitem_value .~ v\n",
" $ pitem_label .~ s\n",
" $ pitem_offset .~ o\n",
" $ def \n",
"\n",
"-- Convert to a renderable in order to display it.\n",
"toRenderable \n",
" $ pie_title .~ \"Relative Population\"\n",
" $ pie_plot . pie_data .~ map pitem values\n",
" $ def"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"png": "iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAIAAAAVFBUnAAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nOzdd3xUVf7/8dekEHokSCgqLE1kERBX6QhBMAIWBAR2IRFs+BUQQVgVcLFgWxBcRXYXo8viooCFIgJKKNJ7UQIIBkIvoSQQUiaTub8/ZuXHQibcJDNzJzPv58M/NPnMve9RmXxyzrnn2AzDQEREREQ8J8TqACIiIiKBRg2WiIiIiIepwRIRERHxMDVYIiIiIh6mBktERETEw8KsDiAi3tW/f/9ffvnl8j+Gh4fXq1evV69eDz300HVf27Jly7y8vEWLFlWpUsVdzYkTJzIzM6tVq1auXDmgfv36hmGsWrWqRo0ank0eHR3dunXrYcOGlS9fvphXdsfa9ysigcSmbRpEAtudd965ffv2a7/+6aefDhw4sODXhoeHOxyOY8eOFdA9dO7cOTExcc6cOY8++ihgs9mAQ4cO1axZs3jB80/epk2b1atXu+7icda+XxEJJJoiFAkKL7300vr169evXz937tx69eoB7777rjdutGXLli1btlSrVs1TF3z++efXr1+/fPny//u//wPWrl27evVqT128+Dz+fkUkMKjBEgkKderUadmyZcuWLbt37/7iiy8CBw8edA1gp6WlDR48+Lbbbvvd737Xs2fPvXv35nuFQ4cO/fGPf7ztttsaNGgQFxc3b948YOjQoTt37gQmTpz44YcfAiNHjnzhhRfOnz8/bty4Dh06jB8/3vXyPXv2dOjQITY21m63m78p8Lvf/a5ly5YxMTFTpkxx9TGu4ry8vPHjxzdt2rR8+fINGzYcMWJEVlaW68odOnTo3r37qlWrevbsWa9evf79+x8/ftx1Ndd3O3TokJmZ6fpKTExMhw4dTp06Vcz3W0AkwOl0durUqVOnThs3buzZs2f9+vWfeeaZfEcWRSRAGCIS0Jo1awZMmzbt8lfi4+OBNm3aGIaRm5vbpEkToHLlyvXr1wcqVaq0a9cuV2VYWBhw7NgxwzDq1KkD1K5du02bNiEhITabbd26df3797/hhhuAhg0bvvXWW8ZvSw4OHTr0xRdfADVq1HBd6s033wR69ep13Ztelfz99993/WN6enqZMmWAmTNnGobRr18/172aNGkSEhICtG3b1ul0nj59GoiIiChbtmxUVJSrpnbt2llZWYZhuL4LXLhwwXVZ1xxfSkpKMd9vAZEMw8jLy3N9q3LlyrfffntoaChQtWrVnJwcL/w3FxHrqcESCXCuNiU6Orp+/fr169d39QeNGjXav3+/YRgzZswAGjRokJ2dbRiGa1XW008/7Xrt5YYjOTm5Q4cO/fr1c329VatWwOTJkw3D6NSpEzBnzhzXty43HJmZmRUrVgS2bdt2+SXffvvtdW96VfK2bds+++yzjz/+eK1atVydU3Jy8rZt21w32rhxo2EY+/fvd/Vec+fOvdxCueKtWLHCFSMhIcEw3WAV4f0WEMm4osH68MMPDcPYtWuX6x93797t4f/eIuIfNEUoEhTy8vLsdnt2dnZ6ejpgt9tdP/J37NgBOJ3O+Pj4Pn36uJ7aW758+VUvr1Onzueff96+ffv+/fvfcccd69evd12kgDuWKVOmd+/ewKJFi1JTUzdu3BgdHX3//febv6nLmjVrpk6d+umnnx46dKh69er/+te/6tSp4wrQuHHj5s2bA/Xq1Wvfvj2wbt0616tsNtugQYOADh06xMTEAElJSeb/dRXh/V43ksuDDz4INGrUyNXpZmRkmE8lIiWItmkQCQpvv/32U089BZw7d65NmzZ79+6dNm3ae++95/oBHxoa6nQ6gRo1avTq1evyzNplycnJrVq1Sk1NbdOmTbdu3SpXruyuH7pSfHx8QkLCd999d/PNNzudzn79+rmGiEze1GX48OF9+vQJCQmJjo6++eabXZNrrhVUrh7FxfX3l1dWlS1b1jWABERGRgKue11mGAaQm5tr5PckdRHe73UjubiG0wDXHKKIBCo1WCLBJSoqqlOnTnv37t29ezfQoEEDICwsbM6cOTabLSUlZdeuXdfuAvXll1+mpqZ269Zt4cKFhmE0bNjQzL3atm1bp06djRs3RkREAI899pjr6yZv6lKrVq0WLVpc9cXGjRsDGzZsSE9Pj4yMzMvLW7Zs2eWvA5cuXdq2bdudd96Zm5vr+pbr2cmoqKiIiIicnJxjx45VrFhx69at+d60CO/3upFEJKjoVyiRoFOpUiV+G1np06ePa4F5v379xo4d26ZNmwcffPDa2TTXUvT9+/d/+eWXzz77rGtSzzUmVLZsWSAhIWHRokVXvcpms8XFxTmdzpUrVzZt2rRp06aur5u8aQHuu+8+V/PUsGHDYcOG3XrrrampqTfddFNcXNzlmm7dug0aNKhevXrHjh2LjIz84x//CISGhv7+978HOnToMGjQoJ49e+a7pVYR3q+ZSCISRKxdAiYi3nbtU4Qff/wxULFixdOnTxuGsXr16ttuu831gRAWFjZu3Li8vDxX5eVF37m5uffee6+rF2nTpo1rS6ouXboYhvH111+7JuPi4+ON/32qzjCM5ORk11cmTZp0ZaoCbnpV8stPEV7lyJEj99xzz+WPsttvvz0pKcn4bRl7WFjYyy+/XLp0aSA6OjoxMfHyC9etW+caLQsPD3/99derVq3KNYvci/Z+3UUyrljkfu7cOddXXFOimzZtKvx/UhEpAbSTu4jgdDoPHDiQnp7esGFD1whNvo4dO2az2a7d5dzhcJw9e7Zs2bIVKlTw+E0LdurUqV9//bVmzZq33HKL6yupqanR0dFhYWG5ubmZmZmHDx+uX7++a+XWlQ4dOlS5cuWCT90p2vu9NpKIBCE1WCISUK5ssKzOIiLBS2uwRERERDxMTxGKSECJiorav3+/l06DFhExSVOEIiIiIh6mKUIRERERD1ODJSIiIuJharBEREREPEwNloiIiIiHqcESERER8TA1WCIiIiIepgZLRERExMPUYImIiIh4mBosEREREQ9TgyUiIiLiYWqwRERERDxMDZaIiIiIh6nBEhEREfEwNVgiIiIiHqYGS0RERMTD1GCJiIiIeJgaLBEREREPU4MlIiIi4mFqsEREREQ8TA2WiIiIiIepwRIRERHxMDVYIiIiIh6mBktERETEw9RgiYiIiHiYGiwRERERD1ODJSIiIuJharBEREREPEwNloiIiIiHqcESERER8TA1WCIiIiIepgZLRERExMPUYImIiIh4mBosEREREQ9TgyUiIiLiYWqwRETIzMxMSUkp8st3797tuSwiEgjUYImIsG3btqeeeqrIL+/Ro4cHw4hIAFCDJSKSD6fTOWLEiFq1avXp0ycpKQn47rvvxowZ06FDh9q1a3/11VdOp3P48OH169cfNmyYYRjXvuSqeqvfkIj4VJjVAURE/NGmTZuSkpK2b99++PDhV1555ZtvvklPT581a9aaNWt27tz56quvRkZG/vzzz2vWrJk+fXp6evq1L+nVq9eV9b169bL6PYmI72gES0QkH0uWLBkwYEBUVNQdd9xx8ODBS5cuAQ899FD16tVj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NHSo9uzhVBZChNutW2/V5Mlq1szuKEAw4y5C4Ia0bq1PP1XfvoqMtDsKcMOiojR4sDZupF0BN4ozWIA5Nm3S8OHatEkXL9odBcg5r1eNGmnSJFWvbncUICRQsADTpKVpzhw984wOH2aOA4KGy6VSpfT88+raVW6uagAmoWABJtu3TyNHKjlZ587ZHQW4lshIdeum0aPZzA6YjIIF5Io1azRmjL75hiuGCFBer2JjNX686te3OwoQiihYQG5JT9eyZRozRjt36tIlu9MAf/B4VK2anntOrVrJ5bI7DRCiKFhA7kpNVWKiJk7U8eNszILNXC4VKaLhwzVgAPe9ArmLggVYYc8ejRunhQt19qzdUeBILpeiotStm554QhUr2p0GcAAKFmCdL77QhAlas4b977BUZKTatdNTT6lqVbujAI5BwQKs9uWXmjBB77/P2Szkujx5lJCgoUN16612RwEchoIF2GPdOj3/vD77TBcu2B0Focjn0733asQINWxodxTAkShYgG0yMvT++5owQZs3U7NgmogINWmixx5TkyZ2RwEcjIIF2O+LL/Tyy1q+nL1ZuCFRUerZUw8/rGrV7I4COB4FCwgUW7fq9de1YIF++42BDsgBl0v586tbNz36qKpUsTsNAEkULCDQbN2qadO0eDE1C9fmcik6Wt2765FHFBNjdxoAl6FgAYFo927NmqV58/Tzz0yBh4GwMN10k/r3V8+eKlrU7jQAsqBgAYHr/HmtXKnp07V5M9uz8LuICDVqpCFD1LSp3G670wDwg4IFBLqMDK1fr5kz9e67OnOG64YO5XYrKkqtWqlfP917L88QBAIdBQsIGrt2acECvfmm9u3TxYt2p4FVwsN1883q1UtduqhMGbvTAMgeChYQZNLTtXGjkpK0YoVOnWKHVsjyeFS4sLp3V/fuqlEjW5/ywgsv9OnTp0iRIrkcDcC1UbCAYHX4sBYv1oIF2r5d58/bnQYmcbnk86lqVXXvrk6dVKJEDj63b9++5cqVGz16dK6lA5BdFCwguGVk6PPPtXy5kpN16BAT4YOYz6dy5dShgx54QHXrXs8rbNu27b777tu7d6/X6zU7HYCcoWABISI9XVu26N13tXChfvqJphU0wsNVurQSEtSqlerVu9EbAxs2bPjQQw917tzZcPXMmTN79uyJiYnx+XyZH9m1a1dERETp0qWv/rIpKSnnzp0rXrx4jsLs3Llz27Zt1apVu/nmm6/7RYAgRcECQs3Zs1q7Vm+/rfffV0oKVw8DVHi4ChXS/ferVSs1b648ecx52eTk5MmTJ3/22WeGqx999FHz5s1nz57drVs3SampqWXKlImPj585c+bVX/arr746dOhQq1atsp/kkUce+fnnn+++++6zZ89u2bLllVdeOXbs2KFDh6pVqzZnzpznnnsu+y8FBCOGqAChJjJSbdpozhwdPKiPP9bIkbrtNkVEcGO//TL3V912m0aO1Cef6OBBzZ2rdu1Ma1eS2rZte+jQoS1btvg7IDY2dunSpZn//N5779W97GLkpEmTKlWqdNddd23btu3ZZ5+dOnWqpMmTJ7/yyitHjx7ds2dPenr6wIEDy5Qp07x584MHD6anpw8dOrR8+fKdOnXavn375V/lnXfe2bdv3/Lly4cNGzZq1KhJkybt378/80V69+49derUl156qUePHlu3bpW0bdu23r17m/ZbAAQGChYQssLCdOedGjdOW7fqX//S2LGqU0d58sjjsTuZw7jdioxU7doaO1YbNmjrVo0bpzvvzJX/EB6Pp3///tOmTfN3QNGiRVNTU0+cOCFpyZIlrVu3zvz4rl273n777c2bNz/22GMJCQn9+vWbOHHipk2bpk+f3qNHj5MnTx48eHDz5s0//fTTvn372rZt+/rrr2/evHn79u1ff/31008//T8767/++ut69er9+cvKlSvHxsZmvsgrr7wSGxs7ZMiQqlWrvvPOO5JWrlxZtWpV838vAFuF2R0AQK7zeFS/vurX19ix2rFDH3+sTz7Rhg06flznzzO5NFe4XAoPV5EiatJEjRqpQQNVqGDRl+7fv3+lSpWOHDliuNspIyOjVatWycnJnTt3PnbsWNmyZb/55htJ7733XqVKldasWSPp6NGjERERTz75ZKNGjebOnVugQIHMz/3ggw86duzo8Xj69Olz5MiRN954o1evXtHR0dHR0Xv27Dlz5kxUVFTmkWFhYadPn756zvj4+B49eowaNWrVqlULFiww87cACAAULMBZKldW5crq21eS9u3Txo1av15r1+roUV28SNm6UV6v8uVTgwZq3FhNm6pKFRsyFCpUKD4+/vXXX/c3ryE+Pr5Xr14REREtWrT484MHDhzIyMjIPLM1cuRIr9ebJ0+eixcv5s2b989j9u7de8cdd0gKCwsrVqzY/v37Y2NjM5cuXrx4+Y7eSpUqvfTSS3/+8oMPPnj//ffvvPPOy2PExMScO3fu+++/P3fuXKVKlUz4NwcCCZcIAecqX17dumnWLO3YoQ8/1D//qfbtVbKkfD4ecpddbrd8PhUurBYtNH68Vq3S7t1avlyPPmpPu8o0cODAGTNmXPQz779MmTJnz56dNm1afHz8nx9s0aJF3rx5Bw4c2Lt37w8//DA1NXXcuHHvvvvu4MGDU1NTM49p2rTphg0bJL399tujRo2Ki4v717/+JWnPnj3R0dGXV7HmzZvv37//q6++knTx4sWJEye2a9fuz9X09PTMf2jbtu3gwYMfeOABc//1gUDAmygARUbqnns0dKiWLtWBA9qyRa+/rr59VaWKIiMVxpnuK3m9ypNHZcqoY0e9+KLWr9eBA1q1SiNGKC5Of1xPs1ONGjViYmKSk5P9HdCmTZvU1NSYmJg/P3LPPff88ssvTZs2rVGjRtu2bR9//PEBAwY0b968efPmzz77bOYxLVu23Lt3b8OGDcePH5+5un379iZNmtSrV2/IkCGXv36BAgXmz5//9NNPx8bGVq5cuWbNmvfee2/mUsWKFb/77rvM+xbbtWu3bt26Tp06mf9bANiNMQ0ArubgQX37rb79Vl9/rX//WwcO6OJFxz0JMSxM4eEqXlx16qhmTVWrpmrVVLFiQN+YuWzZsilTpvib1+DPkSNHfD5fwYIFr3JMSkpKgcta5OHDhwsUKJDHz52QhqtpaWnp6enh4eH79+/v2bPn+vXrcxQSCAoULAA58PPP+vZbbd2q777TDz/oxx+Vmqq0NKWlhcj+LZdLYWEKC1PevKpS5b9/3X67ypa1O1xOpKWlVaxYMTk5+fK7+QLK6tWrX3jhhTFjxjRu3NjuLID5KFgArl9amvbu1Y8/atcu/fijfvhBu3fr+HFduqRLl5SWpj822wQit1sejzweeb0qU0YVKigmRpUr6+abFROjcuWCfp7F888//8MPP8ybN8/uIMZOnz7tdrv9nfoCgh0FC4DJzpzRgQP66ScdOqRDh7R/v/bt07FjOnpUx48rPf2Kv3LvHcjtlsv1e4tyu+V2q2BBlSyp4sVVpozKl1fZsipT5ve/h4fnVgwbHTlypHz58ueDYZZ/cnLy5TvugRBAwQJgnYsX9csv+uUXHTumX37RiRP67Tf9+qtSUn7/+8mT+vVX/frrf5/wc/r0/z5X0eVSgQK/374XGSlJ+fIpXz5FRalAAeXLp7x5FRWlvHlVoICKFlWRIipRQkWKKCLC6n9fAI5FwQIAADAZYxoAAABMRsECAAAwGQULAADAZBQsAAAAk1GwAAAATEbBAgAAMBkFCwAAwGQULAAAAJNRsAAAAExGwQIAADAZBQsAAMBkFCwAAACTUbAAAABMRsECAAAwGQULAADAZBQsAAAAk1GwAAAATEbBAgAAMBkFCwAAwGQULAAAAJNRsAAAAExGwQIAADAZBQsAAMBkFCwAAACTUbAAAABMRsECAAAwGQULAADAZBQsAAAAk1GwAAAATEbBAgAAMBkFCwAAwGQULAAAAJNRsAAAAExGwQIAADAZBQsAAMBkFCwAAACTUbAAAABMRsECAAAwGQULAADAZBQsAAAAk1GwAAAATEbBAgAAMBkFCwAAwGQULAAAAJNRsAAAAExGwQIAADAZBQsAAMBkFCwAAACTUbAAAABMRsECAAAwGQULAADAZBQsAAAAk1GwAAAATEbBAgAAMBkFCwAAwGQULAAAAJNRsAAAAExGwQIAADAZBQsAAMBkFCwAAACTUbAAAABMRsECAAAwGQULAADAZBQsAAAAk/1/XBLJiIakI7EAAAAASUVORK5CYII="
}
],
"prompt_number": 14
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- There is also hlint integration enabled by default.\n",
"-- If you write sketchy code, it will tell you:\n",
"f :: Int -> Int\n",
"f x = x + 1\n",
"\n",
"-- Most warnings are orange...\n",
"f $ 3\n",
"\n",
"-- But more severe warnings are red.\n",
"putStrLn (show 3)\n",
"do\n",
" return 3"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
" <table class=\"suggestion-table\"> <tr class=\"suggestion-row\"> <td class=\"suggestion-cell\"> <div class=\"suggestion-name\"> Redundant $</div> </td> </tr> <tr class=\"suggestion-row\"> <td class=\"suggestion-cell\"> <div class=\"suggestion-warning\">Found:</div> <div class=\"highlight-code\" id=\"haskell\"> f $ 3\n",
"</div> </td> <td class=\"suggestion-cell\"> <div class=\"suggestion-warning\">Why Not:</div> <div class=\"highlight-code\" id=\"haskell\"> f 3\n",
"</div> </td> </tr> <tr class=\"suggestion-row\"> <td class=\"suggestion-cell\"> <div class=\"suggestion-name\"> Use print</div> </td> </tr> <tr class=\"suggestion-row\"> <td class=\"suggestion-cell\"> <div class=\"suggestion-error\">Found:</div> <div class=\"highlight-code\" id=\"haskell\"> putStrLn (show 3)\n",
"</div> </td> <td class=\"suggestion-cell\"> <div class=\"suggestion-error\">Why Not:</div> <div class=\"highlight-code\" id=\"haskell\"> print 3\n",
"</div> </td> </tr> <tr class=\"suggestion-row\"> <td class=\"suggestion-cell\"> <div class=\"suggestion-name\"> Redundant do</div> </td> </tr> <tr class=\"suggestion-row\"> <td class=\"suggestion-cell\"> <div class=\"suggestion-error\">Found:</div> <div class=\"highlight-code\" id=\"haskell\"> do return 3\n",
"</div> </td> <td class=\"suggestion-cell\"> <div class=\"suggestion-error\">Why Not:</div> <div class=\"highlight-code\" id=\"haskell\"> return 3\n",
"</div> </td> </tr> </table> "
],
"metadata": {},
"output_type": "display_data",
"text": [
"Line 3: Redundant $\n",
"Found:\n",
" f $ 3\n",
"\n",
"Why not:\n",
" f 3\n",
"Line 4: Use print\n",
"Found:\n",
" putStrLn (show 3)\n",
"\n",
"Why not:\n",
" print 3\n",
"Line 5: Redundant do\n",
"Found:\n",
" do return 3\n",
"\n",
"Why not:\n",
" return 3"
]
},
{
"metadata": {},
"output_type": "display_data",
"text": [
"4"
]
},
{
"metadata": {},
"output_type": "display_data",
"text": [
"3"
]
},
{
"metadata": {},
"output_type": "display_data",
"text": [
"3"
]
}
],
"prompt_number": 15
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- If hlint annoys you, though, you can turn it off.\n",
"-- Note that this only takes effect in the next cell execution.\n",
":hlint off"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 16
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- You could similarly use `:hlint on` to turn it back on.\n",
"f $ 3"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"text": [
"4"
]
}
],
"prompt_number": 17
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- If your code isn't running fast enough, you can just put it into a module.\n",
"module A.B where\n",
"\n",
"fib 0 = 1\n",
"fib 1 = 1\n",
"fib n = fib (n-1) + fib (n-2)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 18
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- The module is automatically imported unqualified.\n",
"-- Note that since a new module is imported, all previous\n",
"-- bound identifiers are now unbound.\n",
"print $ A.B.fib 20\n",
"print $ fib 20"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"text": [
"10946"
]
},
{
"metadata": {},
"output_type": "display_data",
"text": [
"10946"
]
}
],
"prompt_number": 19
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"-- But if you re-import it qualified, the previous import goes away.\n",
"import qualified A.B as Fib\n",
"\n",
"Fib.fib 20\n",
"fib 20"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"text": [
"10946"
]
},
{
"html": [
"<span style='color: red; font-style: italic;'>Not in scope: `fib'<br/>Perhaps you meant `Fib.fib' (imported from A.B)</span>"
],
"metadata": {},
"output_type": "display_data",
"text": [
"Not in scope: `fib'\n",
"Perhaps you meant `Fib.fib' (imported from A.B)"
]
}
],
"prompt_number": 20
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Thanks!\n",
"---\n",
"\n",
"I hope you've enjoyed this little demo of **IHaskell**!\n",
"\n",
"All of this is running using [IPython notebook][http://ipython.org], communicating asynchronously with a language evaluator kernel written in pure Haskell. \n",
"\n",
"In addition to code cells, you can also type Markdown, and have it be displayed as HTML - just like I'm doing with this cell.\n",
"\n",
"I hope you find IHaskell useful, and please report any bugs or features requests [on Github](https://github.com/gibiansky/IHaskell/issues). If you have any comments, want to contribute, or just want to get in touch, don't hesitate to contact me at Andrew dot Gibiansky at Gmail."
]
}
],
"metadata": {}
}
]
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -14,7 +14,7 @@ maintainer: andrew.gibiansky@gmail.com
category: Language
build-type: Custom
-- extra-source-files:
cabal-version: >=1.10
cabal-version: >=1.16
extra-source-files:
build-parser.sh
......@@ -25,7 +25,8 @@ library
Language.Haskell.GHC.HappyParser
-- other-modules:
-- other-extensions:
build-depends: base >=4.6 && <4.7, ghc == 7.6.3
build-depends: base >=4.6 && <4.7,
ghc ==7.6.*
-- hs-source-dirs:
default-language: Haskell2010
......@@ -10,7 +10,7 @@ import Data.String.Here
import IHaskell.Display
instance IHaskellDisplay Value where
display renderable = return [plain json, html dom]
display renderable = return $ Display [plain json, html dom]
where
json = unpack $ decodeUtf8 $ encodePretty renderable
dom = [i|<div class="highlight-code" id="javascript">${json}</div>|]
......@@ -6,7 +6,7 @@ import IHaskell.Display
import Text.Printf
instance Show a => IHaskellDisplay (Maybe a) where
display just = return [stringDisplay, htmlDisplay]
display just = return $ Display [stringDisplay, htmlDisplay]
where
stringDisplay = plain (show just)
htmlDisplay = html str
......
......@@ -10,7 +10,7 @@ import Text.Blaze.Internal
import Control.Monad
instance IHaskellDisplay (MarkupM a) where
display val = return [stringDisplay, htmlDisplay]
display val = return $ Display [stringDisplay, htmlDisplay]
where
str = renderMarkup (void val)
stringDisplay = plain str
......
......@@ -26,7 +26,7 @@ instance IHaskellDisplay (Renderable a) where
-- but SVGs are not resizable in the IPython notebook.
svgDisp <- chartData renderable SVG
return [pngDisp, svgDisp]
return $ Display [pngDisp, svgDisp]
chartData :: Renderable a -> FileFormat -> IO DisplayData
chartData renderable format = do
......
......@@ -16,7 +16,7 @@ instance IHaskellDisplay (Diagram Cairo R2) where
display renderable = do
png <- diagramData renderable PNG
svg <- diagramData renderable SVG
return [png, svg]
return $ Display [png, svg]
diagramData :: Diagram Cairo R2 -> OutputType -> IO DisplayData
diagramData renderable format = do
......
......@@ -24,7 +24,7 @@ instance IHaskellDisplay B.ByteString where
m <- magicOpen []
magicLoadDefault m
f <- B.unsafeUseAsCStringLen x (magicCString m)
return [withClass (parseMagic f) x]
return $ Display [withClass (parseMagic f) x]
b64 :: B.ByteString -> String
b64 = Char.unpack . Base64.encode
......
......@@ -27,11 +27,11 @@ library
hs-source-dirs: src
default-language: Haskell2010
build-depends: base >=4.6 && <4.7,
bytestring >= 0.10,
aeson >= 0.6,
text >= 0.11,
containers >= 0.5,
unix >= 2.6,
uuid >= 1.3,
cereal == 0.3.*,
zeromq4-haskell >= 0.1
aeson >=0.6,
bytestring >=0.10,
cereal ==0.3.*,
containers >=0.5,
text >=0.11,
unix >=2.6,
uuid >=1.3,
zeromq4-haskell >=0.1
......@@ -101,7 +101,7 @@ instance ToJSON StreamType where
-- | Convert a MIME type and value into a JSON dictionary pair.
displayDataToJson :: DisplayData -> (Text, Value)
displayDataToJson (Display mimeType dataStr) = pack (show mimeType) .= dataStr
displayDataToJson (DisplayData mimeType dataStr) = pack (show mimeType) .= dataStr
----- Constants -----
......
......@@ -341,13 +341,13 @@ replyType ShutdownRequestMessage = Just ShutdownReplyMessage
replyType _ = Nothing
-- | Data for display: a string with associated MIME type.
data DisplayData = Display MimeType ByteString deriving (Typeable, Generic)
data DisplayData = DisplayData MimeType ByteString deriving (Typeable, Generic)
-- We can't print the actual data, otherwise this will be printed every
-- time it gets computed because of the way the evaluator is structured.
-- See how `displayExpr` is computed.
instance Show DisplayData where
show _ = "Display"
show _ = "DisplayData"
-- Allow DisplayData serialization
instance Serialize DisplayData
......@@ -369,9 +369,9 @@ extractPlain :: [DisplayData] -> String
extractPlain disps =
case find isPlain disps of
Nothing -> ""
Just (Display PlainText bytestr) -> Char.unpack bytestr
Just (DisplayData PlainText bytestr) -> Char.unpack bytestr
where
isPlain (Display mime _) = mime == PlainText
isPlain (DisplayData mime _) = mime == PlainText
instance Show MimeType where
show PlainText = "text/plain"
......
......@@ -7,3 +7,7 @@ c.Session.keyfile = b''
# Syntax highlight properly in Haskell notebooks.
c.NbConvertBase.default_language = "haskell"
# Where to look for templates.
template_path = "/".join(__file__.split("/")[:-1] + ["templates"])
c.TemplateExporter.template_path = [template_path]
{%- extends 'html_full.tpl' -%}
{%- block header -%}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>{{resources['metadata']['name']}}</title>
{% for css in resources.inlining.css -%}
<style type="text/css">
{{ css }}
</style>
{% endfor %}
<style type="text/css">
/* Overrides of notebook CSS for static HTML export */
body {
overflow: visible;
padding: 8px;
}
.input_area {
padding: 0.2em;
}
pre {
padding: 0.2em;
border: none;
margin: 0px;
font-size: 13px;
}
</style>
<!-- Our custom CSS -->
<style type="text/css">
/*
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;
}
/* Styles used for basic displays */
.get-type {
color: green;
font-weight: bold;
font-family: monospace;
display: block;
white-space: pre;
}
.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;
}
</style>
<script src="https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS_HTML" type="text/javascript"></script>
<script type="text/javascript">
init_mathjax = function() {
if (window.MathJax) {
// MathJax loaded
MathJax.Hub.Config({
tex2jax: {
inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ]
},
displayAlign: 'left', // Change this to 'center' to center equations.
"HTML-CSS": {
styles: {'.MathJax_Display': {"margin": 0}}
}
});
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
}
}
init_mathjax();
</script>
</head>
{%- endblock header -%}
{% block body %}
<body>
{{ super() }}
</body>
{%- endblock body %}
......@@ -72,15 +72,13 @@ evaluationComparing comparison string = do
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
let isPlain (Display PlainText _) = True
isPlain _ = False
forM_ (zip results expected) $ \(result, expected) ->
forM_ (zip results expected) $ \(Display result, expected) ->
case extractPlain result of
"" -> expectationFailure $ "No plain-text output in " ++ show result ++ "\nExpected: " ++ expected
str -> str `shouldBe` expected
......
......@@ -5,12 +5,13 @@ module IHaskell.Display (
serializeDisplay,
Width, Height, Base64,
encode64, base64,
DisplayData
Display(..),
DisplayData(..),
) where
import ClassyPrelude
import Data.Serialize as Serialize
import Data.ByteString
import Data.ByteString hiding (map)
import Data.String.Utils (rstrip)
import qualified Data.ByteString.Base64 as Base64
import qualified Data.ByteString.Char8 as Char
......@@ -27,52 +28,62 @@ type Base64 = ByteString
-- > instance (Show a) => IHaskellDisplay a
-- > instance Show a where shows _ = id
class IHaskellDisplay a where
display :: a -> IO [DisplayData]
display :: a -> IO Display
-- | these instances cause the image, html etc. which look like:
--
-- > DisplayData
-- > [DisplayData]
-- > IO [DisplayData]
-- > IO (IO DisplayData)
-- > Display
-- > [Display]
-- > IO [Display]
-- > IO (IO Display)
--
-- be run the IO and get rendered (if the frontend allows it) in the pretty
-- form.
instance IHaskellDisplay a => IHaskellDisplay (IO a) where
display = (display =<<)
instance IHaskellDisplay DisplayData where
display disp = return [disp]
display = (display =<<)
instance IHaskellDisplay [DisplayData] where
instance IHaskellDisplay Display where
display = return
instance IHaskellDisplay DisplayData where
display disp = return $ Display [disp]
instance IHaskellDisplay a => IHaskellDisplay [a] where
display disps = do
displays <- mapM display disps
return $ ManyDisplay displays
-- | Encode many displays into a single one. All will be output.
many :: [Display] -> Display
many = ManyDisplay
-- | Generate a plain text display.
plain :: String -> DisplayData
plain = Display PlainText . Char.pack . rstrip
plain = DisplayData PlainText . Char.pack . rstrip
-- | Generate an HTML display.
html :: String -> DisplayData
html = Display MimeHtml . Char.pack
html = DisplayData MimeHtml . Char.pack
-- | Genreate an SVG display.
svg :: String -> DisplayData
svg = Display MimeSvg . Char.pack
svg = DisplayData MimeSvg . Char.pack
-- | Genreate a LaTeX display.
latex :: String -> DisplayData
latex = Display MimeLatex . Char.pack
latex = DisplayData MimeLatex . Char.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.
png :: Width -> Height -> Base64 -> DisplayData
png width height = Display (MimePng width height)
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.
jpg :: Width -> Height -> Base64 -> DisplayData
jpg width height = Display (MimeJpg width height)
jpg width height = DisplayData (MimeJpg width height)
-- | Convert from a string into base 64 encoded data.
encode64 :: String -> Base64
......@@ -84,5 +95,5 @@ base64 = Base64.encode
-- | For internal use within IHaskell.
-- Serialize displays to a ByteString.
serializeDisplay :: [DisplayData] -> ByteString
serializeDisplay :: Display -> ByteString
serializeDisplay = Serialize.encode
......@@ -116,9 +116,19 @@ interpret allowedStdin action = runGhc (Just libdir) $ do
-- Set the dynamic session flags
originalFlags <- getSessionDynFlags
let dflags = xopt_set originalFlags Opt_ExtendedDefaultRules
-- If we're in a sandbox, add the relevant package database
sandboxPackages <- liftIO getSandboxPackageConf
let pkgConfs = case sandboxPackages of
Nothing -> extraPkgConfs dflags
Just path ->
let pkg = PkgConfFile path in
(pkg:) . extraPkgConfs dflags
void $ setSessionDynFlags $ dflags { hscTarget = HscInterpreted,
ghcLink = LinkInMemory,
pprCols = 300 }
pprCols = 300,
extraPkgConfs = pkgConfs }
initializeImports
......@@ -206,7 +216,7 @@ type Publisher = (EvaluationResult -> IO ())
-- | Output of a command evaluation.
data EvalOut = EvalOut {
evalStatus :: ErrorOccurred,
evalResult :: [DisplayData],
evalResult :: Display,
evalState :: KernelState,
evalPager :: String
}
......@@ -222,7 +232,7 @@ evaluate kernelState code output = do
when (getLintStatus kernelState /= LintOff) $ liftIO $ do
lintSuggestions <- lint cmds
unless (null lintSuggestions) $
unless (noResults lintSuggestions) $
output $ FinalResult lintSuggestions ""
updated <- runUntilFailure kernelState (map unloc cmds ++ [storeItCommand execCount])
......@@ -230,6 +240,9 @@ evaluate kernelState code output = do
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
......@@ -238,7 +251,7 @@ evaluate kernelState code output = do
-- Output things only if they are non-empty.
let result = evalResult evalOut
helpStr = evalPager evalOut
unless (null result && null helpStr) $
unless (noResults result && null helpStr) $
liftIO $ output $ FinalResult result helpStr
let newState = evalState evalOut
......@@ -280,8 +293,10 @@ safely state = ghandle handler . ghandle sourceErrorHandler
doc :: GhcMonad m => SDoc -> m String
doc sdoc = do
flags <- getSessionDynFlags
unqual <- getPrintUnqual
let style = mkUserStyle unqual AllTheWay
let cols = pprCols flags
d = runSDoc sdoc (initSDocContext flags defaultUserStyle)
d = runSDoc sdoc (initSDocContext flags style)
return $ Pretty.fullRender Pretty.PageMode cols 1.5 string_txt "" d
where
string_txt :: Pretty.TextDetails -> String -> String
......@@ -292,7 +307,7 @@ doc sdoc = do
wrapExecution :: KernelState
-> Interpreter [DisplayData]
-> Interpreter Display
-> Interpreter EvalOut
wrapExecution state exec = safely state $ exec >>= \res ->
return EvalOut {
......@@ -318,7 +333,7 @@ evalCommand _ (Import importStr) state = wrapExecution state $ do
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 []
else Display []
where
implicitImportOf :: ImportDecl RdrName -> InteractiveImport -> Bool
implicitImportOf _ (IIModule _) = False
......@@ -372,7 +387,7 @@ evalCommand _ (Directive SetExtension exts) state = wrapExecution state $ do
write $ "Extension: " ++ exts
results <- mapM setExtension (words exts)
case catMaybes results of
[] -> return []
[] -> return $ Display []
errors -> return $ displayError $ intercalate "\n" errors
evalCommand _ (Directive GetType expr) state = wrapExecution state $ do
......@@ -404,7 +419,7 @@ evalCommand _ (Directive SetOpt option) state = do
newState = setOpt opt state
out = case newState of
Nothing -> displayError $ "Unknown option: " ++ opt
Just _ -> []
Just _ -> Display []
return EvalOut {
evalStatus = if isJust newState then Success else Failure,
......@@ -452,7 +467,7 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
if exists
then do
setCurrentDirectory directory
return []
return $ Display []
else
return $ displayError $ printf "No such directory: '%s'" directory
cmd -> do
......@@ -480,7 +495,7 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
-- Maximum size of the output (after which we truncate).
maxSize = 100 * 1000
incSize = 200
output str = publish $ IntermediateResult [plain str]
output str = publish $ IntermediateResult $ Display [plain str]
loop = do
-- Wait and then check if the computation is done.
......@@ -506,12 +521,12 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
else do
out <- readMVar outputAccum
case fromJust exitCode of
ExitSuccess -> return [plain out]
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 [plain $ out ++ "\n" ++ errMsg,
html $ printf "<span class='mono'>%s</span>" out ++ htmlErr]
return $ Display [plain $ out ++ "\n" ++ errMsg,
html $ printf "<span class='mono'>%s</span>" out ++ htmlErr]
loop
......@@ -521,7 +536,7 @@ evalCommand _ (Directive GetHelp _) state = do
write "Help via :help or :?."
return EvalOut {
evalStatus = Success,
evalResult = [out],
evalResult = Display [out],
evalState = state,
evalPager = ""
}
......@@ -585,7 +600,7 @@ evalCommand _ (Directive GetInfo str) state = safely state $ do
return EvalOut {
evalStatus = Success,
evalResult = [],
evalResult = Display [],
evalState = state,
evalPager = output
}
......@@ -600,7 +615,7 @@ evalCommand _ (Directive GetDoc query) state = safely state $ do
evalCommand output (Statement stmt) state = wrapExecution state $ do
write $ "Statement:\n" ++ stmt
let outputter str = output $ IntermediateResult [plain str]
let outputter str = output $ IntermediateResult $ Display [plain str]
(printed, result) <- capturedStatement outputter stmt
case result of
RunOk names -> do
......@@ -618,7 +633,7 @@ evalCommand output (Statement stmt) state = wrapExecution state $ do
-- 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 output
then return $ Display output
else do
-- Get all the type strings.
types <- forM nonItNames $ \name -> do
......@@ -629,11 +644,11 @@ evalCommand output (Statement stmt) state = wrapExecution state $ do
htmled = unlines $ map formatGetType types
return $ case extractPlain output of
"" -> [html htmled]
"" -> Display [html htmled]
-- Return plain and html versions.
-- Previously there was only a plain version.
text ->
text -> Display
[plain $ joined ++ "\n" ++ text,
html $ htmled ++ mono text]
......@@ -642,36 +657,29 @@ evalCommand output (Statement stmt) state = wrapExecution state $ do
evalCommand output (Expression expr) state = do
write $ "Expression:\n" ++ expr
-- 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
-- Try to use `display` to convert our type into the output
-- DisplayData. If typechecking fails and there is no appropriate
-- 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
let out = evalResult evalOut
showErr = isShowError out
write $ printf "%s: Attempting %s" (if canRunDisplay then "Success" else "Failure") displayExpr
write $ "Show Error: " ++ show showErr
write $ show out
-- If evaluation failed, return the failure. If it was successful, we
-- may be able to use the IHaskellDisplay typeclass.
if not canRunDisplay
then return $ if not showErr || useShowErrors state
then evalOut
else postprocessShowError evalOut
else case evalStatus evalOut of
Success -> useDisplay displayExpr
-- If something other than the show failed, don't use display, just
-- show the error message.
Failure -> if showErr
then useDisplay displayExpr
else return evalOut
if canRunDisplay
then useDisplay displayExpr
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
......@@ -683,24 +691,27 @@ evalCommand output (Expression expr) state = do
-- Check if the error is due to trying to print something that doesn't
-- implement the Show typeclass.
isShowError errs =
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
isPlain (Display mime _) = mime == PlainText
isSvg (Display mime _) = mime == MimeSvg
isSvg (DisplayData mime _) = mime == MimeSvg
removeSvg (Display disps) = Display $ filter (not . isSvg) disps
removeSvg (ManyDisplay disps) = ManyDisplay $ map removeSvg disps
useDisplay displayExpr = wrapExecution state $ do
-- If there are instance matches, convert the object into
-- a [DisplayData]. We also serialize it into a bytestring. We get
-- a Display. We also serialize it into a bytestring. We get
-- the bytestring 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 [DisplayData].
-- refuses to decode back into a Display.
-- Suppress output, so as not to mess up console.
out <- capturedStatement (const $ return ()) displayExpr
......@@ -710,20 +721,19 @@ evalCommand output (Expression expr) state = do
Just bytestring ->
case Serialize.decode bytestring of
Left err -> error err
Right displayData -> do
write $ show displayData
Right display -> do
return $
if useSvg state
then displayData
else filter (not . isSvg) displayData
then display
else removeSvg display
postprocessShowError :: EvalOut -> EvalOut
postprocessShowError evalOut = evalOut { evalResult = map postprocess disps }
postprocessShowError evalOut = evalOut { evalResult = Display $ map postprocess disps }
where
disps = evalResult evalOut
Display disps = evalResult evalOut
text = extractPlain disps
postprocess (Display MimeHtml _) = html $ printf fmt unshowableType (formatErrorWithClass "err-msg collapse" text) script
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 [
......@@ -760,14 +770,14 @@ evalCommand _ (Declaration decl) state = wrapExecution state $ do
-- 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 []
then return $ Display []
else do
-- Get all the type strings.
types <- forM nonDataNames $ \name -> do
theType <- showSDocUnqual dflags . ppr <$> exprType name
return $ name ++ " :: " ++ theType
return [html $ unlines $ map formatGetType types]
return $ Display [html $ unlines $ map formatGetType types]
evalCommand _ (TypeSignature sig) state = wrapExecution state $
-- We purposefully treat this as a "success" because that way execution
......@@ -789,7 +799,7 @@ evalCommand _ (ParseError loc err) state = do
hoogleResults :: KernelState -> [Hoogle.HoogleResult] -> EvalOut
hoogleResults state results = EvalOut {
evalStatus = Success,
evalResult = [],
evalResult = Display [],
evalState = state,
evalPager = output
}
......@@ -823,7 +833,7 @@ readChars handle delims nchars = do
Left _ -> return []
doLoadModule :: String -> String -> Ghc [DisplayData]
doLoadModule :: String -> String -> Ghc Display
doLoadModule name modName = flip gcatch unload $ do
-- Compile loaded modules.
flags <- getSessionDynFlags
......@@ -851,10 +861,10 @@ doLoadModule name modName = flip gcatch unload $ do
setSessionDynFlags flags{ hscTarget = HscInterpreted }
case result of
Succeeded -> return []
Succeeded -> return $ Display []
Failed -> return $ displayError $ "Failed to load module " ++ modName
where
unload :: SomeException -> Ghc [DisplayData]
unload :: SomeException -> Ghc Display
unload exception = do
-- Explicitly clear targets
setTargets []
......@@ -1033,11 +1043,11 @@ formatParseError (Loc line col) =
formatGetType :: String -> String
formatGetType = printf "<span class='get-type'>%s</span>"
formatType :: String -> [DisplayData]
formatType typeStr = [plain typeStr, html $ formatGetType typeStr]
formatType :: String -> Display
formatType typeStr = Display [plain typeStr, html $ formatGetType typeStr]
displayError :: ErrMsg -> [DisplayData]
displayError msg = [plain . fixStdinError . typeCleaner $ msg, html $ formatError msg]
displayError :: ErrMsg -> Display
displayError msg = Display [plain . fixStdinError . typeCleaner $ msg, html $ formatError msg]
fixStdinError :: ErrMsg -> ErrMsg
fixStdinError err =
......
......@@ -38,7 +38,7 @@ lintIdent = "lintIdentAEjlkQeh"
-- | Given parsed code chunks, perform linting and output a displayable
-- report on linting warnings and errors.
lint :: [Located CodeBlock] -> IO [DisplayData]
lint :: [Located CodeBlock] -> IO Display
lint blocks = do
let validBlocks = map makeValid blocks
fileContents = joinBlocks validBlocks
......@@ -50,8 +50,8 @@ lint blocks = do
suggestions <- catMaybes <$> map parseSuggestion <$> hlint [filename, "--quiet"]
return $
if null suggestions
then []
else
then Display []
else Display
[plain $ concatMap plainSuggestion suggestions, html $ htmlSuggestions suggestions]
where
-- Join together multiple valid file blocks into a single file.
......
......@@ -12,6 +12,7 @@ module IHaskell.IPython (
readInitInfo,
defaultConfFile,
getIHaskellDir,
getSandboxPackageConf,
nbconvert,
ViewFormat(..),
) where
......@@ -23,7 +24,7 @@ import System.Argv0
import System.Directory
import qualified Filesystem.Path.CurrentOS as FS
import Data.List.Utils (split)
import Data.String.Utils (rstrip)
import Data.String.Utils (rstrip, endswith)
import Text.Printf
import qualified System.IO.Strict as StrictIO
......@@ -121,8 +122,9 @@ nbconvert fmt name = void . shellyNoDir $ do
Just notebook ->
let viewArgs = case fmt of
Pdf -> ["--to=latex", "--post=pdf"]
fmt -> ["--to=" ++ show fmt] in
Pdf -> ["--to=latex", "--post=pdf"]
Html -> ["--to=html", "--template=ihaskell"]
fmt -> ["--to=" ++ show fmt] in
void $ runIHaskell ipythonProfile "nbconvert" $ viewArgs ++ [fpToString notebook]
-- | Set up IPython properly.
......@@ -391,3 +393,19 @@ getIHaskellPath = 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 = shellyNoDir $ 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
......@@ -20,12 +20,15 @@ module IHaskell.Types (
Width, Height,
FrontendType(..),
ViewFormat(..),
Display(..),
defaultKernelState,
extractPlain
) where
import ClassyPrelude
import qualified Data.ByteString.Char8 as Char
import Data.Serialize
import GHC.Generics
import Text.Read as Read hiding (pfail, String)
import Text.ParserCombinators.ReadP
......@@ -60,6 +63,12 @@ instance Read ViewFormat where
"md" -> return Markdown
_ -> pfail
-- | 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)
instance Serialize Display
-- | All state stored in the kernel between executions.
data KernelState = KernelState
......@@ -108,9 +117,9 @@ data EvaluationResult =
-- | An intermediate result which communicates what has been printed thus
-- far.
IntermediateResult {
outputs :: [DisplayData] -- ^ Display outputs.
outputs :: Display -- ^ Display outputs.
}
| FinalResult {
outputs :: [DisplayData], -- ^ Display outputs.
outputs :: Display, -- ^ Display outputs.
pagerOut :: String -- ^ Text to display in the IPython pager.
}
......@@ -252,7 +252,8 @@ replyTo interface req@ExecuteRequest{ getCode = code } replyHeader state = do
header <- dupHeader replyHeader ClearOutputMessage
send $ ClearOutput header True
sendOutput outs = do
sendOutput (ManyDisplay manyOuts) = mapM_ sendOutput manyOuts
sendOutput (Display outs) = do
header <- dupHeader replyHeader DisplayDataMessage
send $ PublishDisplayData header "haskell" outs
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment