Compare commits

..

No commits in common. "master" and "v0.6.2.0" have entirely different histories.

274 changed files with 102386 additions and 35072 deletions

View File

@ -1,30 +0,0 @@
*.hi
Untitled*.ipynb
main/Main
.stack-work
notebooks/Test.ipynb
notebooks/Untitled.ipynb
notebooks/Untitled0.ipynb
*.dyn_o
*.dyn_hi
*.o
dist
IHaskell/GHC
env
.shelly
.ihaskell_capture
.ipynb_checkpoints
Hspec
todo
profile/profile.tar
.cabal-sandbox
cabal.sandbox.config
.tmp1
.tmp2
.tmp3
.stack-work
src/Hspec
notebooks
dist
**/dist
**/.stack-work

3
.ghci
View File

@ -1,4 +1,5 @@
:set -package ghc
:set -package ghc-paths
:set -optP-include -optPdist/build/autogen/cabal_macros.h
:set -i. -isrc -idist/build/autogen
:set prompt "\ESC[34mλ> \ESC[m"
:set -XDoAndIfThenElse -XOverloadedStrings

View File

@ -1,74 +0,0 @@
name: Docker
on:
push:
branches:
paths:
- '.dockerignore'
- '.github/workflows/docker.yml'
- 'Dockerfile'
- 'LICENSE'
- 'ghc-parser/**'
- 'html/**'
- 'ihaskell-display/**'
- 'ihaskell.cabal'
- 'ipython-kernel/**'
- 'jupyterlab-ihaskell/**'
- 'main/**'
- 'src/**'
- 'stack.yaml'
pull_request:
paths:
- '.dockerignore'
- '.github/workflows/docker.yml'
- 'Dockerfile'
- 'LICENSE'
- 'ghc-parser/**'
- 'html/**'
- 'ihaskell-display/**'
- 'ihaskell.cabal'
- 'ipython-kernel/**'
- 'jupyterlab-ihaskell/**'
- 'main/**'
- 'src/**'
- 'stack.yaml'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
if: (github.event_name != 'pull_request' && ! github.event.pull_request.head.repo.fork) || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork)
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v3
- uses: haskell-actions/setup@v2
name: Setup Haskell Stack
with:
enable-stack: true
stack-version: "latest"
- name: Check Dockerfile GHC version matches
run: |
set -e
STACK_GHC_VERSION=$(stack exec -- ghc --version | awk '{ print $NF }')
DOCKER_GHC_VERSION=$(sed -n 's/ARG GHC_VERSION=\(.*\)/\1/p' Dockerfile)
if [[ ${STACK_GHC_VERSION} != ${DOCKER_GHC_VERSION} ]]; then
echo 'GHC_VERSION in Dockerfile does not match stack resolver'
echo "GHC_VERSION should be ${STACK_GHC_VERSION}"
exit 1
fi
- uses: elgohr/Publish-Docker-Github-Action@v5
with:
name: gibiansky/ihaskell
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
snapshot: true
no_push: ${{ github.event_name == 'pull_request' }}

View File

@ -1,74 +0,0 @@
name: Nix
on:
schedule:
- cron: '0 2 * * *'
push:
paths-ignore:
- 'demo/**'
- 'docker/**'
- 'Dockerfile'
- '.dockerignore'
- '.ghci'
- '.gitignore'
- 'images/**'
- 'notebooks/**'
- 'stack/**'
- 'README.md'
- '*.yaml'
pull_request:
paths-ignore:
- 'demo/**'
- 'docker/**'
- 'Dockerfile'
- '.dockerignore'
- '.ghci'
- '.gitignore'
- 'images/**'
- 'notebooks/**'
- 'stack/**'
- 'README.md'
- '*.yaml'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
if: (github.event_name != 'pull_request' && ! github.event.pull_request.head.repo.fork) || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
env:
- ihaskell-env-ghc96
- ihaskell-env-ghc98
- ihaskell-env-ghc910
- ihaskell-env-display-ghc96
- ihaskell-env-display-ghc98
# - ihaskell-env-display-ghc910
steps:
- uses: actions/checkout@v3
- uses: cachix/install-nix-action@v22
with:
install_url: https://releases.nixos.org/nix/nix-2.22.1/install
- uses: cachix/cachix-action@v14
with:
name: ihaskell
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
- name: Build environment ${{matrix.env}}
run: |
nix build .#${{matrix.env}}
- name: Check acceptance test for ${{matrix.env}}
# Don't bother running it with the display envs since we already run it
# with the basic envs, and it doesn't test any display stuff.
if: ${{ !(contains(matrix.env, fromJSON('"display"'))) }}
run: |
nix build .#checks.x86_64-linux.${{matrix.env}} -L

View File

@ -1,108 +0,0 @@
name: Stack (--nix)
on:
push:
paths-ignore:
- 'demo/**'
- 'docker/**'
- 'Dockerfile'
- '.dockerignore'
- '.ghci'
- '.gitignore'
- 'images/**'
- 'nix/**'
- 'notebooks/**'
- 'flake.nix'
- 'flake.lock'
- 'README.md'
- 'README.md'
- 'hie.yaml'
pull_request:
paths-ignore:
- 'demo/**'
- 'docker/**'
- 'Dockerfile'
- '.dockerignore'
- '.ghci'
- '.gitignore'
- 'images/**'
- 'nix/**'
- 'notebooks/**'
- 'flake.nix'
- 'flake.lock'
- 'README.md'
- 'README.md'
- 'hie.yaml'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
linux:
if: (github.event_name != 'pull_request' && ! github.event.pull_request.head.repo.fork) || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
stack-yaml:
# - 'stack/stack-8.2.yaml'
# - 'stack/stack-8.4.yaml'
# - 'stack/stack-8.6.yaml'
# - 'stack/stack-8.8.yaml'
- 'stack/stack-8.10.yaml'
- 'stack/stack-9.0.yaml'
- 'stack/stack-9.2.yaml'
- 'stack/stack-9.4.yaml'
- 'stack/stack-9.6.yaml'
steps:
- uses: actions/checkout@v3
- uses: cachix/install-nix-action@v22
with:
install_url: https://releases.nixos.org/nix/nix-2.13.3/install
- uses: haskell-actions/setup@v2
name: Setup Haskell Stack
with:
enable-stack: true
stack-version: "latest"
- uses: actions/cache@v3
name: Cache Haskell dependencies
with:
path: |
~/.stack/stack.sqlite3
~/.stack/snapshots
key: ${{ matrix.stack-yaml }}-nix-${{ hashFiles(format('./{0}', matrix.stack-yaml)) }}
restore-keys: |
${{ matrix.stack-yaml }}-nix-${{ hashFiles(format('./{0}', matrix.stack-yaml)) }}
${{ matrix.stack-yaml }}-nix-
- name: Build
run: |
export "NIX_PATH=nixpkgs=$(nix run .#print-nixpkgs-master)"
stack build --nix --stack-yaml ${{matrix.stack-yaml}}
- name: Test
run: |
export "NIX_PATH=nixpkgs=$(nix run .#print-nixpkgs-master)"
stack test --nix --stack-yaml ${{matrix.stack-yaml}}
- name: Run integration test
run: |
set -e
export "NIX_PATH=nixpkgs=$(nix run .#print-nixpkgs-master)"
nix build .#jupyterlab
export PATH="$(pwd)/result/bin:$(pwd)/.local/bin:$PATH"
stack install --nix --stack-yaml ${{matrix.stack-yaml}}
ihaskell install --stack --stack-flag="--nix" --stack-flag="--stack-yaml=$(realpath ${{matrix.stack-yaml}})"
# Ensure that IHaskell notebook remains unchanged.
# Run the notebook to regenerate the outputs, then compare the new notebook to the old one.
test/acceptance.nbconvert.sh jupyter nbconvert

View File

@ -1,110 +0,0 @@
name: Stack
on:
push:
paths-ignore:
- 'demo/**'
- 'docker/**'
- 'Dockerfile'
- '.dockerignore'
- '.ghci'
- '.gitignore'
- 'images/**'
- 'nix/**'
- 'notebooks/**'
- 'flake.nix'
- 'flake.lock'
- 'README.md'
- 'hie.yaml'
pull_request:
paths-ignore:
- 'demo/**'
- 'docker/**'
- 'Dockerfile'
- '.dockerignore'
- '.ghci'
- '.gitignore'
- 'images/**'
- 'nix/**'
- 'notebooks/**'
- 'flake.nix'
- 'flake.lock'
- 'README.md'
- 'hie.yaml'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
linux:
if: (github.event_name != 'pull_request' && ! github.event.pull_request.head.repo.fork) || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork)
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
stack-yaml:
- 'stack/stack-8.4.yaml'
- 'stack/stack-8.6.yaml'
- 'stack/stack-8.8.yaml'
- 'stack/stack-8.10.yaml'
- 'stack/stack-9.0.yaml'
- 'stack/stack-9.2.yaml'
- 'stack/stack-9.4.yaml'
- 'stack/stack-9.6.yaml'
- 'stack.yaml'
steps:
- uses: actions/checkout@v3
- uses: cachix/install-nix-action@v22
with:
install_url: https://releases.nixos.org/nix/nix-2.13.3/install
- uses: haskell-actions/setup@v2
name: Setup Haskell Stack
with:
enable-stack: true
stack-version: "latest"
- uses: actions/cache@v3
name: Cache Haskell dependencies
with:
path: |
~/.stack/stack.sqlite3
~/.stack/snapshots
key: ${{ matrix.stack-yaml }}-${{ hashFiles(format('./{0}', matrix.stack-yaml)) }}
restore-keys: |
${{ matrix.stack-yaml }}-${{ hashFiles(format('./{0}', matrix.stack-yaml)) }}
${{ matrix.stack-yaml }}-
- name: Install system dependencies
run: |
sudo apt update
sudo apt install libmagic-dev libgmp-dev libblas-dev liblapack-dev libcairo2-dev libpango1.0-dev libzmq3-dev jq
- name: Test ipython-kernel
run: |
stack build ipython-kernel --flag ipython-kernel:examples --stack-yaml ${{matrix.stack-yaml}}
- name: Build
run: |
export "NIX_PATH=nixpkgs=$(nix run .#print-nixpkgs-master)"
stack build ihaskell --stack-yaml ${{matrix.stack-yaml}}
- name: Test
run: |
stack test --stack-yaml ${{matrix.stack-yaml}}
- name: Run integration test
run: |
nix build .#jupyterlab
export PATH="$(pwd)/result/bin:$(pwd)/.local/bin:$PATH"
stack install --stack-yaml ${{matrix.stack-yaml}}
ihaskell install --stack --stack-flag="--stack-yaml=$(realpath ${{matrix.stack-yaml}})"
# Ensure that IHaskell notebook remains unchanged.
# Run the notebook to regenerate the outputs, then compare the new notebook to the old one.
test/acceptance.nbconvert.sh jupyter nbconvert

View File

@ -1,23 +0,0 @@
name: Update flake.lock
on:
workflow_dispatch:
schedule:
- cron: '1 1 * * 0'
jobs:
lockfile:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v13
- name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@v23
with:
pr-title: "Update `flake.lock`"
pr-labels: |
dependencies
automated
token: ${{ secrets.GH_TOKEN_FLAKE_LOCK_UPDATE }}

10
.gitignore vendored
View File

@ -1,7 +1,4 @@
*.hi
Untitled*.ipynb
main/Main
.stack-work
notebooks/Test.ipynb
notebooks/Untitled.ipynb
notebooks/Untitled0.ipynb
@ -22,10 +19,3 @@ cabal.sandbox.config
.tmp1
.tmp2
.tmp3
stack.yaml.lock
result
default.nix
dist-*/
cabal.project.local
cabal.config
tsconfig.tsbuildinfo

72
.travis.yml Normal file
View File

@ -0,0 +1,72 @@
# Taken from multi-ghc-travis
# NB: don't set `language: haskell` here
# The following enables several GHC versions to be tested; often it's enough to test only against the last release in a major GHC version. Feel free to omit lines listings versions you don't need/want testing for.
env:
- CABALVER=1.18 GHCVER=7.6.3
- CABALVER=1.18 GHCVER=7.8.4 # see note about Alex/Happy for GHC >= 7.8
- CABALVER=1.22 GHCVER=7.10.1
# - CABALVER=head GHCVER=head # see section about GHC HEAD snapshots
# Note: the distinction between `before_install` and `install` is not important.
before_install:
- travis_retry sudo add-apt-repository -y ppa:hvr/ghc
- travis_retry sudo apt-get update
- travis_retry sudo apt-get install cabal-install-$CABALVER ghc-$GHCVER # see note about happy/alex
- travis_retry sudo apt-get install libmagic-dev
- travis_retry sudo apt-get install python3
- travis_retry git clone http://www.github.com/zeromq/zeromq4-x.git libzmq
- export OLDPWD=$(pwd) && cd libzmq && travis_retry ./autogen.sh && travis_retry ./configure && make && travis_retry sudo make install && travis_retry sudo ldconfig && cd $OLDPWD
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$HOME/.cabal/bin:$PATH
- |
if [ $GHCVER = "head" ] || [ ${GHCVER%.*} = "7.8" ] || [ ${GHCVER%.*} = "7.10" ]; then
travis_retry sudo apt-get install happy-1.19.4 alex-3.1.3
travis_retry sudo apt-get install libblas-dev liblapack-dev
export PATH=/opt/alex/3.1.3/bin:/opt/happy/1.19.4/bin:$PATH
else
travis_retry sudo apt-get install happy alex
fi
install:
- cabal --version
- echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"
- travis_retry cabal update
- travis_retry cabal install cpphs
- travis_retry cabal install gtk2hs-buildtools
- |
if [ ${GHCVER%.*} = "7.8" ]; then
travis_retry cabal install arithmoi==0.4.* -fllvm
travis_retry git clone http://www.github.com/gibiansky/hindent
cd hindent && travis_retry cabal install && cd ..
fi
# Here starts the actual work to be performed for the package under test; any command which exits with a non-zero exit code causes the build to fail.
script:
- |
if [ ${GHCVER%.*} = "7.8" ] || [ ${GHCVER%.*} = "7.10" ]; then
travis_retry ./build.sh all
else
travis_retry ./build.sh ihaskell
fi
# Build and run the test suite
- travis_retry cabal install --only-dependencies --enable-tests
- travis_retry cabal configure --enable-tests
- |
if [ ${GHCVER%.*} = "7.8" ]; then
travis_retry cabal test --show-details=always
fi
- |
if [ ${GHCVER%.*} = "7.8" ]; then
./verify_formatting.py
fi
- cabal sdist
# The following scriptlet checks that the resulting source distribution can be built & installed
- export SRC_TGZ=$(cabal info . | awk '{print $2 ".tar.gz";exit}') ;
cd dist/;
if [ -f "$SRC_TGZ" ]; then
cabal install --force-reinstalls "$SRC_TGZ";
else
echo "expected '$SRC_TGZ' not found";
exit 1;
fi

View File

@ -1,96 +0,0 @@
# should match the GHC version of the stack.yaml resolver
# checked in CI
ARG GHC_VERSION=9.6.4
FROM haskell:${GHC_VERSION} AS ihaskell_base
# Install Ubuntu packages needed for IHaskell runtime
RUN apt-get update && \
apt-get install -y libzmq5 \
&& \
rm -rf /var/lib/apt/lists/*
FROM ihaskell_base AS builder
# Install Ubuntu packages needed for IHaskell build
RUN apt-get update && \
apt-get install -y libzmq3-dev pkg-config \
&& \
rm -rf /var/lib/apt/lists/*
WORKDIR /build
# Build snapshot
COPY stack.yaml stack.yaml
COPY ihaskell.cabal ihaskell.cabal
COPY ipython-kernel ipython-kernel
COPY ghc-parser ghc-parser
COPY ihaskell-display ihaskell-display
RUN stack setup
RUN stack build ihaskell --only-snapshot
# Build IHaskell itself.
# Don't just `COPY .` so that changes in e.g. README.md don't trigger rebuild.
COPY src src
COPY html html
COPY main main
COPY jupyterlab-ihaskell jupyterlab-ihaskell
COPY LICENSE LICENSE
RUN stack install ihaskell --local-bin-path ./bin/
# Save resolver used to build IHaskell
RUN sed -n 's/resolver: \(.*\)#.*/\1/p' stack.yaml | tee resolver.txt
# Save third-party data files
RUN mkdir /data && \
snapshot_install_root=$(stack path --snapshot-install-root) && \
cp $(find ${snapshot_install_root} -name hlint.yaml) /data
FROM ihaskell_base AS ihaskell
# Install JupyterLab
RUN apt-get update && \
apt-get install -y python3-pip && \
rm -rf /var/lib/apt/lists/*
RUN pip3 install -U pip
RUN pip3 install -U jupyterlab notebook
# Create runtime user
ENV NB_USER jovyan
ENV NB_UID 1000
RUN adduser --disabled-password \
--gecos "Default user" \
--uid ${NB_UID} \
${NB_USER}
# Create directory for storing ihaskell files
ENV IHASKELL_DATA_DIR /usr/local/lib/ihaskell
RUN mkdir -p ${IHASKELL_DATA_DIR} && chown ${NB_UID} ${IHASKELL_DATA_DIR}
# Set up + set hlint data directory
ENV HLINT_DATA_DIR /usr/local/lib/hlint
COPY --from=builder --chown=${NB_UID} /data/hlint.yaml ${HLINT_DATA_DIR}/
ENV hlint_datadir ${HLINT_DATA_DIR}
# Set current user + directory
WORKDIR /home/${NB_USER}/src
RUN chown -R ${NB_UID} /home/${NB_USER}/src
USER ${NB_UID}
# Set up global project
COPY --from=builder --chown=${NB_UID} /build/resolver.txt /tmp/
RUN stack setup --resolver=$(cat /tmp/resolver.txt) --system-ghc
RUN stack config set system-ghc --global true
# Set up env file
RUN stack exec env --system-ghc > ${IHASKELL_DATA_DIR}/env
# Install + setup IHaskell
COPY --from=builder --chown=${NB_UID} /build/bin/ihaskell /usr/local/bin/
COPY --from=builder --chown=${NB_UID} /build/html ${IHASKELL_DATA_DIR}/html
COPY --from=builder --chown=${NB_UID} /build/jupyterlab-ihaskell ${IHASKELL_DATA_DIR}/jupyterlab-ihaskell
RUN export ihaskell_datadir=${IHASKELL_DATA_DIR} && \
ihaskell install --env-file ${IHASKELL_DATA_DIR}/env
RUN jupyter notebook --generate-config
CMD ["jupyter", "notebook", "--ip", "0.0.0.0"]

601
Hspec.hs Normal file
View File

@ -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 hiding (Qualified)
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 . map extractPlain . 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

380
README.md
View File

@ -1,227 +1,219 @@
![jupyter](https://i.imgur.com/S16l2Hw.png) ![IHaskell](https://i.imgur.com/qhXXFbA.png) [![Build Status](https://github.com/gibiansky/IHaskell/workflows/CI/badge.svg)](https://github.com/gibiansky/IHaskell/actions?query=workflow%3ACI) [![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/gibiansky/IHaskell/mybinder)
### Status
[![Join the chat at https://gitter.im/gibiansky/IHaskell](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gibiansky/IHaskell?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Build Status](https://travis-ci.org/gibiansky/IHaskell.svg?branch=master)](https://travis-ci.org/gibiansky/IHaskell)
![IHaskell](https://raw.github.com/gibiansky/IHaskell/master/html/logo-64x64.png)
# IHaskell
IHaskell is a kernel for the [Jupyter project](http://ipython.org), which allows you to use Haskell inside Jupyter frontends (including the console and notebook).
> You can now try IHaskell directly in your browser at [CoCalc](https://cocalc.com) or [mybinder.org](https://mybinder.org/v2/gh/gibiansky/IHaskell/mybinder).
>
> Alternatively, watch a [talk and demo](http://begriffs.com/posts/2016-01-20-ihaskell-notebook.html) showing off IHaskell features.
IHaskell is a kernel for the [Jupyter project](https://jupyter.org), which allows you to use Haskell inside Jupyter frontends (including the console and notebook). It currently supports GHC 8.4 through 9.10.
For a tour of some IHaskell features, check out the [demo Notebook](http://nbviewer.org/github/gibiansky/IHaskell/blob/master/notebooks/IHaskell.ipynb). More example notebooks are available on the [wiki](https://github.com/gibiansky/IHaskell/wiki).
For a tour of some IHaskell features, check out the [demo Notebook](http://nbviewer.ipython.org/github/gibiansky/IHaskell/blob/master/notebooks/IHaskell.ipynb). More example notebooks are available on the [wiki](https://github.com/gibiansky/IHaskell/wiki).
The [wiki](https://github.com/gibiansky/IHaskell/wiki) also has more extensive documentation of IHaskell features.
### Shell Usage
![IPython Console](https://raw.github.com/gibiansky/IHaskell/master/images/ihaskell-console.png)
![IPython Notebook](https://raw.github.com/gibiansky/IHaskell/master/images/ihaskell-notebook.png)
### Interactive In-Browser Notebook
![IPython Notebook](https://raw.github.com/gibiansky/IHaskell/master/images/ihaskell-notebook.png)
# Installation
## Linux
# Source Installation
Some prerequisites; adapt to your distribution.
**Note:** IHaskell does not support Windows. To use on Windows, install
Virtualbox, install Ubuntu or another Linux distribution, and proceed with the
install instructions.
**How to get help:** Feel free to open an issue [on Github](https://github.com/gibiansky/IHaskell/issues?direction=desc&sort=updated&state=open) or join the [Gitter channel](https://gitter.im/gibiansky/IHaskell?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge).
### Install Using Installation Scripts
#### Ubuntu:
If you are using a modern version of Ubuntu, clone the repository and then run the `ubuntu-install.sh` script:
```bash
sudo apt-get install -y python3-pip git libtinfo-dev libzmq3-dev libcairo2-dev libpango1.0-dev libmagic-dev libblas-dev liblapack-dev
```
Install `stack`, clone this repository, install Python requirements, install
`ihaskell`, and install the Jupyter kernelspec with `ihaskell`.
These instructions assume you don't already have Stack or a Jupyter
installation, please skip the relevant steps if this is not the case.
```bash
curl -sSL https://get.haskellstack.org/ | sh
git clone https://github.com/gibiansky/IHaskell
git clone http://www.github.com/gibiansky/IHaskell
cd IHaskell
pip3 install -r requirements.txt
stack install --fast
ihaskell install --stack
./ubuntu-install.sh
```
This script will ask you for `sudo` permissions in order to install IHaskell dependencies. The script is readable and easy to inspect if you wish to know what it does before giving it root permissions.
#### Mac OS X:
Run Jupyter.
On Mac OS X, clone the repository and then run the `macos-install.sh` script:
```bash
stack exec jupyter -- notebook
```
## Mac
You need to have [Homebrew](https://brew.sh) installed.
If you do not have it yet run `/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"` in your terminal.
You also need the Xcode command line tools.
You can install them by running `xcode-select --install` in the terminal and following the prompts.
These instructions assume you don't already have Stack or a Jupyter
installation, please skip the relevant steps if this is not the case.
```bash
brew install python3 zeromq libmagic cairo pkg-config haskell-stack pango
git clone https://github.com/gibiansky/IHaskell
git clone http://www.github.com/gibiansky/IHaskell
cd IHaskell
pip3 install -r requirements.txt
stack install --fast
ihaskell install --stack
./macos-install.sh
```
Note that you must have [Homebrew](http://brew.sh/) installed for this script to work.
### Installing Manually
#### Install IPython
Install IPython 3.0 or above:
```bash
pip install ipython[all]
```
This may require root permissions on some systems, in which case put a `sudo` before that command before running it.
Once this is done, running `ipython --version` should print out `3.0` or above.
Note that IHaskell *requires* 3.0 or above; IHaskell *will not work* with IPython 2 or earlier.
#### Install Haskell
Install GHC and Cabal. You must have appropriate versions of both:
```bash
ghc --numeric-version # Should be 7.6.* or 7.8.*
cabal --version # Should be 1.18.* or newer
```
These may be installed in a number of ways, including the [Haskell Platform](http://www.haskell.org/platform/), as a [standalone Mac app](https://github.com/ghcformacosx/ghc-dot-app), via Homebrew with `brew install ghc cabal-install`, and so on.
#### Install ZeroMQ
Install ZeroMQ, a library IHaskell uses for asynchronous communication.
- **Mac OS X**: With [Homebrew](http://brew.sh/) installed, run `brew install zeromq`. (If using 32-bit Haskell Platform, you *may* need to use `brew install zeromq --universal`. YMMV.)
- **Ubuntu**: Run `sudo apt-get install libzmq3-dev`.
- **Other**: You can install ZeroMQ from source or use another package manager:
```bash
# Compiling from source:
git clone git@github.com:zeromq/zeromq4-x.git libzmq
cd libzmq
./autogen.sh && ./configure && make
sudo make install
sudo ldconfig
```
If your own platform has a package and I haven't included instructions for it, feel free to send me an email or a PR on this README.
#### Install Haskell Tools
First, make sure that executables installed by `cabal` are on your shell `PATH`:
```bash
# If you have a ~/.cabal/bin folder:
export PATH=~/.cabal/bin:$PATH
# If you have a ~/Library/Haskell/bin folder on OS X:
export PATH=~/Library/Haskell/bin:$PATH
```
If you have Homebrew installed to a location that `stack` does not expect (e.g. `/opt/homebrew`), you'd need to specify `--extra-include-dirs ${HOMEBREW_PREFIX}/include --extra-lib-dirs ${HOMEBREW_PREFIX}/lib` to the `stack` command.
Then, install the `happy` parser generator tool and `cpphs` preprocessor:
```bash
cabal install happy cpphs
```
Run Jupyter.
#### Build IHaskell
Install IHaskell! You may install it from Hackage via `cabal install`:
```bash
cabal install ihaskell --reorder-goals
```
As IHaskell updates frequently, you may also want to clone the repository and install from there:
```bash
git clone http://www.github.com/gibiansky/IHaskell
cd IHaskell
./build.sh ihaskell # Build and install IHaskell
```
The build script, `build.sh`, is a script for building IHaskell and dependencies. It has the following modes:
- `ihaskell`: Build and install `ihaskell` and the two dependencies from this repository, `ipython-kernel` and `ghc-parser`.
- `quick`: Just install `ihaskell`, do not bother recompiling and reinstalling its dependencies (`ipython-kernel` and `ghc-parser`).
- `display`: Install `ihaskell` and all the support libraries in `ihaskell-display/`.
- `all`: Install everything, including `ihaskell`, the dependencies, and all the support libraries in `ihaskell-display/`.
It is run via `./build.sh all` or equivalent.
IHaskell may also be built in a sandbox, via something like:
```bash
cd IHaskell
cabal sandbox init
cabal sandbox add-source ihaskell-display/* ghc-parser ipython-kernel
cabal install . ihaskell-display/*
```
You may also need to use `--extra-lib-dirs` and `--extra-include-dirs`, if
`cabal` cannot find relevant libraries. For example:
```bash
cabal install . ihaskell-display/* --extra-lib-dirs=`brew --prefix libmagic`/lib --extra-include-dirs=`brew --prefix libmagic`/include
```
#### Run IHaskell
Run IHaskell:
- `ihaskell install` to install the IHaskell kernel into Jupyter.
- `ipython notebook` for the browser-based interactive notebook.
- `ipython console --kernel haskell` for a REPL.
#### (Optional) Install Support Libraries
IHaskell comes with many support libraries, such as `ihaskell-diagrams`, `ihaskell-parsec`, and so on, which add rich and interactive displays for common libraries.
You can install these with `cabal install`. To install all of them, clone this repository and run `./build.sh all` to install IHaskell and all of its display support libraries.
You may run into some issues with installing the `cairo` dependency on Macs. To fix this, you can install `gcc` via `brew` and then use it to install `cairo`:
```bash
brew install gcc49
cabal install cairo --with-gcc=gcc-4.9
```
### Gotchas
These are simply some problems have had and solutions to them.
**Problem**: You have Anaconda or Enthought or some other python distribution, and for unknown reasons IHaskell just hangs after the first input.
**Solution**: Anaconda and Enthought cause problems. Get rid of them.
**Problem**: You get an error when `pyzmq` is compiling that looks somewhat like
```
cc1: error: -Werror=unused-command-line-argument-hard-error-in-future: No option -Wunused-command-line-argument-hard-error-in-future
```
**Solution:** Rerun the command after changing the `ARCHFLAGS` variable via
```bash
export ARCHFLAGS=-Wno-error=unused-command-line-argument-hard-error-in-future
```
**Problem**: You'd like to have IHaskell run some code every time it starts up, like `~/.ghci` or `~/.bashrc`.
**Solution**: IHaskell uses `~/.ihaskell/rc.hs` as its default configuration file; if you put code into that file (it may or may not exist), it will be loaded on startup. You can substitute a different file by passing the `--conf=myfile.hs` argument to `ihaskell install` to reconfigure the kernel.
**Note**: You may have some trouble due to browser caches with the notebook interface if you also use IPython's notebook interface or have used it in the past. If something doesn't work or IPython says it can't connect to the notebook server, make sure to clear the browser cache in whatever browser you're using, or try another browser.
# Contributing
IHaskell is a young project, and I'd love your help getting it to a stable and useful point. There's a lot to do, and if you'd like to contribute, feel free to get in touch with me via my email at andrew period gibiansky at gmail - although browsing the code should be enough to get you started, I'm more than happy to answer any questions myself.
**For package maintainers:** IHaskell has an ability to display data types it knows about with a rich format based on images or HTML. In order to do so, an external package `ihaskell-something` must be created and installed. Writing these packages is simply - they must just contain instance of the `IHaskellDisplay` typeclass, defined in `IHaskell.Display`, and for a package `ihaskell-something` should have a single module `IHaskell.Display.Something`. If you have a package with interesting data types that would benefit from a rich display format, please get in contact with me (andrew dot gibiansky at gmail) to write one of these packages! A sample package is available [here](https://github.com/gibiansky/IHaskell/tree/master/ihaskell-display/ihaskell-basic).
# Developer Notes
Before diving in, you should read the [brief description of IPython kernel architectures](http://andrew.gibiansky.com/blog/ipython/ipython-kernels/)
and read the [complete messaging protocol specification](http://ipython.org/ipython-doc/dev/development/messaging.html).
Please format your code with `hindent --style gibiansky` before submitting it; Travis CI automatically checks for code style before merging!
**Loading IHaskell into GHCi for testing:**
Use one of the methods below to access IHaskell files in GHCi. Once inside GHCi, you can load an IHaskell file; for example, `:load IHaskell/Config.hs`.
**Using cabal repl**
If you have the latest version of cabal (>v1.18.0), the simplest thing to do is
```bash
stack exec jupyter -- notebook
cd <path-to-IHaskell>
cabal repl
```
_Tested on macOS Sierra (10.12.6)_
This will hide all packages not listed in `IHaskell.cabal`
## Windows
**Using GHCi directly**
IHaskell does not support Windows, however it can be used on Windows 10 via
Windows Subsystem for Linux (WSL). If WSL is not installed, follow the
[Installation Guide for Windows 10](https://docs.microsoft.com/en-us/windows/wsl/install-win10).
The following assumes that Ubuntu is picked as the Linux distribution.
In the Ubuntu app, follow the steps above for Linux.
Jupyter Notebook is now ready to use. In the Ubuntu app, launch a Notebook
Server, without opening the notebook in a browser:
If you don't want to use `cabal repl`, you can just call ghci which can read the `.ghci` file included in the repository for the options.
```bash
jupyter notebook --no-browser
cd <path-to-IHaskell>
chmod 600 .ghci # trust the .ghci file
ghci
```
Returning to Windows 10, open a browser and copy and paste the URL output in the
step above (the token will differ).
```bash
Or copy and paste one of these URLs:
http://localhost:8888/?token=9ca8a725ddb1fdded176d9e0e675ba557ebb5fbef6c65fdf
```
_Tested on Windows 10 (build 18362.175) with Ubuntu 18.04 on WSL_
Alternatively, install Virtualbox, install Ubuntu or another Linux distribution,
and proceed with the install instructions.
## Docker
To quickly run a Jupyter notebook with the IHaskell kernel, try the `Dockerfile`
in the top directory.
```bash
docker build -t ihaskell:latest .
docker run --rm -p 8888:8888 ihaskell:latest
```
Or use the continuously updated Docker image
[on Docker Hub](https://hub.docker.com/r/gibiansky/ihaskell).
```sh
docker run --rm -p 8888:8888 gibiansky/ihaskell
```
In order to mount your own local files into the Docker container
use following command:
```sh
docker run --rm -p 8888:8888 -v "$PWD":/home/jovyan/src gibiansky/ihaskell
```
Be aware that the directory you're mounting must contain
a `stack.yaml` file.
A simple version would be:
```yaml
resolver: lts-16.23
packages: []
```
It's recommended to use the same LTS version as the IHaskell image is using itself
(as can be seen in [its stack.yaml](./stack.yaml)).
This guarantees that stack doesn't have to first perform
a lengthy installation of GHC before running your notebook.
You can also use the following script to run IHaskell in Docker: https://gist.github.com/brandonchinn178/928d6137bfd17961b9584a8f96c18827
## Nix
If you have the `nix` package manager installed, you can create an IHaskell
notebook environment with one command. For example:
```bash
$ nix-build -I nixpkgs=https://github.com/NixOS/nixpkgs/tarball/nixos-23.05 release.nix --argstr compiler ghc928 --arg packages "haskellPackages: [ haskellPackages.lens ]"
<result path>
$ <result path>/bin/jupyter notebook
```
It might take a while the first time, but subsequent builds will be much
faster. You can use the
[https://ihaskell.cachix.org](https://app.cachix.org/cache/ihaskell) cache for
prebuilt artifacts.
The IHaskell display modules are not loaded by default and have to be specified as additional packages:
```bash
$ nix-build -I nixpkgs=https://github.com/NixOS/nixpkgs/tarball/nixos-23.05 release.nix --argstr compiler ghc928 --arg packages "haskellPackages: [ haskellPackages.ihaskell-blaze haskellPackages.ihaskell-charts ]"
```
For more examples of using IHaskell with Nix, see https://github.com/vaibhavsagar/notebooks.
# Developing
IHaskell is regularly updated to work with the latest version of GHC. To read how this is done, and how the development environment is set up, please see [this blog post](https://vaibhavsagar.com/blog/2021/05/02/updating-ihaskell-newer-ghc).
## Nix flake
There is also a Nix flake that provides a developer environment. For details on Nix flakes, please see the documentation at https://wiki.nixos.org/wiki/Flakes.
After this, IHaskell can be compiled as follows:
```bash
nix develop # This opens a new shell with all dependencies installed
cabal update # Make sure Cabal's package index is up-to-date
cabal build # Builds IHaskell
```
Note that this shell also provides `haskell-language-server`, which can be used in your editor if it supports it. Opening your editor from within the `nix develop` shell should allow it to see `haskell-language-server`.
# Troubleshooting
## Where are my packages? (IHaskell + Stack)
Stack manages separate environments for every package. By default your notebooks
will only have access to a few packages that happen to be required for
IHaskell. To make packages available add them to the stack.yaml in the IHaskell
directory and run `stack install --fast`.
Packages should be added to the `packages:` section and can take the following
form
([reproduced here from the stack documentation](https://github.com/commercialhaskell/stack/blob/master/doc/yaml_configuration.md#packages)). If
you've already installed a package by `stack install` you can simply list its
name even if it's local.
Then in the ghci session you can type things like:
```
- package-name
- location: .
- location: dir1/dir2
- location: https://example.com/foo/bar/baz-0.0.2.tar.gz
- location: http://github.com/yesodweb/wai/archive/2f8a8e1b771829f4a8a77c0111352ce45a14c30f.zip
- location:
git: git@github.com:commercialhaskell/stack.git
commit: 6a86ee32e5b869a877151f74064572225e1a0398
- location:
hg: https://example.com/hg/repo
commit: da39a3ee5e6b4b0d3255bfef95601890afd80709
:set -package setenv
:load src/Hspec.hs
hspec parserTests
:browse IHaskell.Types
```
## The kernel keeps dying (IHaskell + Stack)
The default instructions globally install IHaskell with support for only one
version of GHC. If you've e.g. installed an `lts-10` IHaskell and are using it
with an `lts-9` project the mismatch between GHC 8.2 and GHC 8.0 will cause
this error. Stack also has the notion of a 'global project' located at
`~/.stack/global-project/` and the `stack.yaml` for that project should be on
the same LTS as the version of IHaskell installed to avoid this issue.

View File

@ -1,3 +1,2 @@
import Distribution.Simple
import Distribution.Simple
main = defaultMain

78
build.sh Executable file
View File

@ -0,0 +1,78 @@
#!/bin/sh
set -e
print_help () {
echo "Run build.sh from inside the ihaskell directory to install packages in this repository:"
echo " ./build.sh ihaskell # Install ihaskell and its dependencies"
echo " ./build.sh quick # Install ihaskell, but not its dependencies"
echo " ./build.sh all # Install ihaskell, dependencies, and all display packages"
echo " ./build.sh display # Install ihaskell and display libraries"
echo
echo "If this is your first time installing ihaskell, run './build.sh ihaskell'."
}
# Verify that we're in the ihaskell directory.
if [ ! -e ihaskell.cabal ]; then
print_help;
exit 1
fi
if [ $# -lt 1 ]; then
print_help;
exit 1
fi
if [ ! $1 = "all" ] && [ ! $1 = "ihaskell" ] && [ ! $1 = "display" ] && [ ! $1 = "quick" ]; then
print_help;
exit 1;
fi
# What to install.
INSTALLS=""
# Remove my kernelspec
rm -rf ~/.ipython/kernels/haskell
# Compile dependencies.
if [ $# -gt 0 ]; then
if [ $1 = "all" ] || [ $1 = "ihaskell" ]; then
INSTALLS="$INSTALLS ghc-parser ipython-kernel"
fi
fi
# Always make ihaskell itself
INSTALLS="$INSTALLS ."
# Install ihaskell-display packages.
if [ $# -gt 0 ]; then
if [ $1 = "display" ] || [ $1 = "all" ]; then
# Install all the display libraries
cd ihaskell-display
for dir in `ls`
do
INSTALLS="$INSTALLS ihaskell-display/$dir"
done
cd ..
fi
fi
# Clean all required directories, just in case.
TOP=`pwd`
for pkg in $INSTALLS
do
cd ./$pkg
cabal clean
cd $TOP
done
# Stick a "./" before everything.
INSTALL_DIRS=`echo $INSTALLS | tr ' ' '\n' | sed 's#^#./#' | tr ' ' '\n'`
echo CMD: cabal install --constraint "arithmoi -llvm" -j $INSTALL_DIRS --force-reinstalls --max-backjumps=-1 --reorder-goals
cabal install --constraint "arithmoi -llvm" -j $INSTALL_DIRS --force-reinstalls --max-backjumps=-1 --reorder-goals
if hash ihaskell 2>/dev/null; then
ihaskell install 2>/dev/null || echo "The command \"ihaskell install\" failed. Please check your 'ipython --version'. 3.0 or up is required but it is $(ipython --version)!"
else
echo "Reminder: run 'ihaskell install' to install the IHaskell kernel to Jupyter."
fi

View File

@ -1,20 +0,0 @@
packages: .
./ipython-kernel
./ghc-parser
./ihaskell-display/ihaskell-aeson
./ihaskell-display/ihaskell-blaze
./ihaskell-display/ihaskell-charts
./ihaskell-display/ihaskell-diagrams
./ihaskell-display/ihaskell-gnuplot
./ihaskell-display/ihaskell-graphviz
./ihaskell-display/ihaskell-hatex
./ihaskell-display/ihaskell-juicypixels
./ihaskell-display/ihaskell-magic
./ihaskell-display/ihaskell-plot
./ihaskell-display/ihaskell-static-canvas
./ihaskell-display/ihaskell-widgets
source-repository-package
type: git
location: https://github.com/jeffreyrosenbluth/static-canvas.git
tag: 1aad0f192828ded72dced14e42ac4e6baa6dab6f

File diff suppressed because one or more lines are too long

BIN
demo/doc-demo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
demo/hoogle-demo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

BIN
demo/info-demo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
demo/json-demo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

149
flake.lock generated
View File

@ -1,149 +0,0 @@
{
"nodes": {
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1733328505,
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"hls": {
"inputs": {
"flake-compat": "flake-compat",
"flake-utils": [
"flake-utils"
],
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1744298016,
"narHash": "sha256-8zoVia97o0Fo1ADis9FLXdvkSbRN69o5fPgemqlfKdQ=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "c5c3ca4a992cc79e742bbfe9ee013467732d3aa4",
"type": "github"
},
"original": {
"owner": "haskell",
"repo": "haskell-language-server",
"type": "github"
}
},
"nix-filter": {
"locked": {
"lastModified": 1731533336,
"narHash": "sha256-oRam5PS1vcrr5UPgALW0eo1m/5/pls27Z/pabHNy2Ms=",
"owner": "numtide",
"repo": "nix-filter",
"rev": "f7653272fd234696ae94229839a99b73c9ab7de0",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "nix-filter",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1739019272,
"narHash": "sha256-7Fu7oazPoYCbDzb9k8D/DdbKrC3aU1zlnc39Y8jy/s8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "fa35a3c8e17a3de613240fea68f876e5b4896aec",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs24_11": {
"locked": {
"lastModified": 1744492897,
"narHash": "sha256-qqKO4FOo/vPmNIaRPcLqwfudUlQ29iNdI1IbCZfjmxs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "86484f6076aac9141df2bfcddbf7dcfce5e0c6bb",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-24.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgsMaster": {
"locked": {
"lastModified": 1744514369,
"narHash": "sha256-N9uWy2Ti5H5gYDoAPEcJ0i4dAbdQD9auJ2YjskbMqOc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "ae29fc75d2d3ee07f88ce1a252d76964eb1efb24",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "master",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"hls": "hls",
"nix-filter": "nix-filter",
"nixpkgs24_11": "nixpkgs24_11",
"nixpkgsMaster": "nixpkgsMaster"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

109
flake.nix
View File

@ -1,109 +0,0 @@
{
description = "A Haskell kernel for IPython.";
inputs.nixpkgs24_11.url = "github:NixOS/nixpkgs/release-24.11";
inputs.nixpkgsMaster.url = "github:NixOS/nixpkgs/master";
inputs.flake-utils.url = "github:numtide/flake-utils";
inputs.hls = {
url = "github:haskell/haskell-language-server";
inputs.flake-utils.follows = "flake-utils";
};
inputs.nix-filter.url = "github:numtide/nix-filter";
nixConfig = {
extra-substituters = [ "https://ihaskell.cachix.org" ];
extra-trusted-public-keys = [ "ihaskell.cachix.org-1:WoIvex/Ft/++sjYW3ntqPUL3jDGXIKDpX60pC8d5VLM="];
};
outputs = { self, nixpkgs24_11, nixpkgsMaster, flake-utils, hls, nix-filter, ... }:
flake-utils.lib.eachDefaultSystem (system: let
baseOverlay = _self: _super: { inherit nix-filter; };
pkgsMaster = import nixpkgsMaster { inherit system; overlays = [baseOverlay]; };
jupyterlab = pkgsMaster.python3.withPackages (ps: [ ps.jupyterlab ps.notebook ]);
# Map from GHC version to release function
versions = let
mkVersion = pkgsSrc: compiler: overlays: extraArgs: {
name = compiler;
value = (import pkgsSrc { inherit system; overlays = [baseOverlay] ++ overlays; }).callPackage ./nix/release.nix ({
inherit compiler;
} // extraArgs);
};
in
pkgsMaster.lib.listToAttrs [
(mkVersion nixpkgs24_11 "ghc96" [(import ./nix/overlay-9.6.nix)] {})
(mkVersion nixpkgs24_11 "ghc98" [(import ./nix/overlay-9.8.nix)] {})
(mkVersion nixpkgsMaster "ghc910" [(import ./nix/overlay-9.10.nix)] {})
];
# Helper function for building environments with a given set of packages
mkEnvs = prefix: packages: pkgsMaster.lib.mapAttrs' (version: releaseFn: {
name = prefix + version;
value = (releaseFn {
# Note: this can be changed to other Jupyter systems like jupyter-console
extraEnvironmentBinaries = [ jupyterlab ];
systemPackages = p: with p; [
gnuplot # for the ihaskell-gnuplot runtime
];
inherit packages;
});
}) versions;
# Basic envs with Jupyterlab and IHaskell
envs = mkEnvs "ihaskell-env-" (_: []);
# Envs with Jupyterlab, IHaskell, and all display packages
displayEnvs = mkEnvs "ihaskell-env-display-" (p: with p; map (n: builtins.getAttr n p) (import ./nix/displays.nix));
# Executables only, pulled from passthru on the envs
exes = pkgsMaster.lib.mapAttrs' (envName: env: {
name = builtins.replaceStrings ["-env"] [""] envName;
value = env.ihaskellExe;
}) envs;
devShells = pkgsMaster.lib.mapAttrs' (version: releaseFn: {
name = "ihaskell-dev-" + version;
value = pkgsMaster.callPackage ./nix/mkDevShell.nix {
inherit hls system version;
haskellPackages = (releaseFn {}).haskellPackages;
ihaskellOverlay = (releaseFn {}).ihaskellOverlay;
};
}) versions;
in {
packages = envs // displayEnvs // exes // devShells // rec {
# For easily testing that everything builds
allEnvs = pkgsMaster.linkFarm "ihaskell-envs" envs;
allDisplayEnvs = pkgsMaster.linkFarm "ihaskell-display-envs" displayEnvs;
allExes = pkgsMaster.linkFarm "ihaskell-exes" exes;
allDevShells = pkgsMaster.linkFarm "ihaskell-dev-shells" devShells;
# To use in CI
inherit jupyterlab;
print-nixpkgs-master = pkgsMaster.writeShellScriptBin "print-nixpkgs-master.sh" "echo ${pkgsMaster.path}";
};
# Run the acceptance tests on each env
checks = pkgsMaster.lib.mapAttrs (envName: env:
pkgsMaster.stdenv.mkDerivation {
name = envName + "-check";
src = pkgsMaster.callPackage ./nix/ihaskell-src.nix {};
nativeBuildInputs = with pkgsMaster; [jq bash];
doCheck = true;
checkPhase = ''
mkdir -p home
export HOME=$(pwd)/home
bash ./test/acceptance.nbconvert.sh ${env}/bin/jupyter nbconvert
'';
installPhase = ''
touch $out
'';
}
) envs;
defaultPackage = self.packages.${system}.ihaskell-env-ghc96;
devShell = self.packages.${system}.ihaskell-dev-ghc96;
});
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,8 @@
import Distribution.Simple
import Distribution.Simple
import System.Cmd
main = defaultMain
main = defaultMainWithHooks simpleUserHooks{
preConf = \args confFlags -> do
system "./build-parser.sh"
preConf simpleUserHooks args confFlags
}

22
ghc-parser/build-parser.sh Executable file
View File

@ -0,0 +1,22 @@
#!/bin/bash -e
# Called from Setup.hs.
function make_parser {
SRCDIR=$1
SRCNAME=$2
# Preprocess the GHC parser we're using to CPP subs.
cpphs --linepragma --text ${SRCNAME}.y.pp -OParser.y
# Compile the parser and remove intermediate file.
happy Parser.y
rm Parser.y
# Move output Haskell to source directory.
mkdir -p $SRCDIR/Language/Haskell/GHC
mv Parser.hs $SRCDIR/Language/Haskell/GHC/HappyParser.hs
}
make_parser src-7.6 HaskellParser76
make_parser src-7.8.2 HaskellParser782
make_parser src-7.8.3 HaskellParser783

View File

@ -1,6 +1,4 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE TypeApplications #-}
module Language.Haskell.GHC.Parser (
-- Parser handling
runParser,
@ -19,65 +17,24 @@ module Language.Haskell.GHC.Parser (
parserTypeSignature,
parserModule,
parserExpression,
parsePragmasIntoDynFlags,
-- Haskell string preprocessing.
removeComments,
layoutChunks,
) where
import Data.List (intercalate, findIndex, isInfixOf)
import Data.Char (isAlphaNum)
import Data.List (intercalate, findIndex)
#if MIN_VERSION_ghc(9,8,0)
import GHC.Driver.Config.Parser (initParserOpts)
import GHC.Parser.Errors.Types (PsMessage(..))
import GHC.Types.Error (defaultDiagnosticOpts, getMessages, MsgEnvelope(..))
import GHC.Utils.Error (diagnosticMessage, formatBulleted)
import GHC.Utils.Outputable (defaultSDocContext, renderWithContext)
#elif MIN_VERSION_ghc(9,6,0)
import GHC.Driver.Config.Parser (initParserOpts)
import GHC.Parser.Errors.Types (PsMessage(..))
import GHC.Types.Error (getMessages, MsgEnvelope(..))
import GHC.Utils.Error (diagnosticMessage, defaultDiagnosticOpts, formatBulleted)
import GHC.Utils.Outputable (defaultSDocContext, renderWithContext)
#elif MIN_VERSION_ghc(9,4,0)
import GHC.Driver.Config.Parser (initParserOpts)
import GHC.Types.Error (diagnosticMessage, getMessages, MsgEnvelope(..))
import GHC.Utils.Error (formatBulleted)
import GHC.Utils.Outputable (defaultSDocContext)
#elif MIN_VERSION_ghc(9,2,0)
import GHC.Driver.Config (initParserOpts)
import GHC.Parser.Errors.Ppr (pprError)
#endif
#if MIN_VERSION_ghc(9,0,0)
import GHC.Data.Bag
import GHC.Driver.Session (parseDynamicFilePragma)
import GHC.Data.FastString
import GHC.Parser.Header (getOptions)
import GHC.Parser.Lexer hiding (buffer)
import GHC.Data.OrdList
import GHC.Utils.Panic (handleGhcException)
import qualified GHC.Types.SrcLoc as SrcLoc
import GHC.Data.StringBuffer hiding (len)
#else
import Bag
import DynFlags (parseDynamicFilePragma)
import FastString
import HeaderInfo (getOptions)
import Lexer hiding (buffer)
import OrdList
import Panic (handleGhcException)
import qualified SrcLoc as SrcLoc
import StringBuffer hiding (len)
#endif
#if MIN_VERSION_ghc(8,10,0)
#else
import ErrUtils hiding (ErrMsg)
#endif
import FastString
import GHC hiding (Located)
import Lexer
import OrdList
import Outputable hiding ((<>))
import SrcLoc hiding (Located)
import StringBuffer
import GHC hiding (Located, Parsed, parser)
import qualified Language.Haskell.GHC.HappyParser as Parse
-- | A line number in an input string.
@ -111,29 +68,12 @@ data Located a = Located {
data Parser a = Parser (P a)
-- Our parsers.
parserStatement :: Parser (Maybe (LStmt GhcPs (LHsExpr GhcPs)))
parserStatement = Parser Parse.fullStatement
parserImport :: Parser (LImportDecl GhcPs)
parserImport = Parser Parse.fullImport
parserDeclaration :: Parser (OrdList (LHsDecl GhcPs))
parserDeclaration = Parser Parse.fullDeclaration
parserExpression :: Parser (LHsExpr GhcPs)
parserExpression = Parser Parse.fullExpression
parserTypeSignature :: Parser (SrcLoc.Located (OrdList (LHsDecl GhcPs)))
parserTypeSignature = Parser Parse.fullTypeSignature
#if MIN_VERSION_ghc(9,6,0)
parserModule :: Parser (SrcLoc.Located (HsModule GhcPs))
#elif MIN_VERSION_ghc(9,0,0)
parserModule :: Parser (SrcLoc.Located HsModule)
#else
parserModule :: Parser (SrcLoc.Located (HsModule GhcPs))
#endif
parserModule = Parser Parse.fullModule
parserStatement = Parser Parse.fullStatement
parserImport = Parser Parse.fullImport
parserDeclaration = Parser Parse.fullDeclaration
parserExpression = Parser Parse.fullExpression
parserTypeSignature = Parser Parse.fullTypeSignature
parserModule = Parser Parse.fullModule
-- | Run a GHC parser on a string. Return success or failure with
-- associated information for both.
@ -141,94 +81,47 @@ runParser :: DynFlags -> Parser a -> String -> ParseOutput a
runParser flags (Parser parser) str =
-- Create an initial parser state.
let filename = "<interactive>"
location = SrcLoc.mkRealSrcLoc (mkFastString filename) 1 1
location = mkRealSrcLoc (mkFastString filename) 1 1
buffer = stringToStringBuffer str
#if MIN_VERSION_ghc(9,2,0)
parseState = initParserState (initParserOpts flags) buffer location in
#else
parseState = mkPState flags buffer location in
#endif
-- Convert a GHC parser output into our own.
toParseOut (unP parser parseState)
toParseOut $ unP parser parseState
where
toParseOut :: ParseResult a -> ParseOutput a
#if MIN_VERSION_ghc(9,4,0)
toParseOut (PFailed pstate) =
let realSpan = SrcLoc.psRealSpan $ last_loc pstate
errMsg = printErrorBag (getMessages $ errors pstate)
ln = srcLocLine $ SrcLoc.realSrcSpanStart realSpan
col = srcLocCol $ SrcLoc.realSrcSpanStart realSpan
in Failure errMsg $ Loc ln col
#elif MIN_VERSION_ghc(9,2,0)
toParseOut (PFailed pstate) =
let realSpan = SrcLoc.psRealSpan $ last_loc pstate
errMsg = printErrorBag (errors pstate)
ln = srcLocLine $ SrcLoc.realSrcSpanStart realSpan
col = srcLocCol $ SrcLoc.realSrcSpanStart realSpan
in Failure errMsg $ Loc ln col
#elif MIN_VERSION_ghc(9,0,0)
toParseOut (PFailed pstate) =
let realSpan = SrcLoc.psRealSpan $ last_loc pstate
errMsg = printErrorBag $ snd $ (messages pstate) flags
ln = srcLocLine $ SrcLoc.realSrcSpanStart realSpan
col = srcLocCol $ SrcLoc.realSrcSpanStart realSpan
in Failure errMsg $ Loc ln col
#elif MIN_VERSION_ghc(8,10,0)
toParseOut (PFailed pstate) =
let realSpan = last_loc pstate
errMsg = printErrorBag $ snd $ (messages pstate) flags
ln = srcLocLine $ SrcLoc.realSrcSpanStart realSpan
col = srcLocCol $ SrcLoc.realSrcSpanStart realSpan
in Failure errMsg $ Loc ln col
#else
toParseOut (PFailed _ spn@(RealSrcSpan realSpan) err) =
let errMsg = printErrorBag $ unitBag $ mkPlainErrMsg flags spn err
ln = srcLocLine $ SrcLoc.realSrcSpanStart realSpan
col = srcLocCol $ SrcLoc.realSrcSpanStart realSpan
in Failure errMsg $ Loc ln col
#endif
toParseOut (PFailed span@(RealSrcSpan realSpan) err) =
let errMsg = printErrorBag $ unitBag $ mkPlainErrMsg flags span err
line = srcLocLine $ realSrcSpanStart realSpan
col = srcLocCol $ realSrcSpanStart realSpan
in Failure errMsg $ Loc line col
#if MIN_VERSION_ghc(8,10,0)
#else
toParseOut (PFailed _ spn err) =
let errMsg = printErrorBag $ unitBag $ mkPlainErrMsg flags spn err
toParseOut (PFailed span err) =
let errMsg = printErrorBag $ unitBag $ mkPlainErrMsg flags span err
in Failure errMsg $ Loc 0 0
#endif
toParseOut (POk _parseState result) =
Parsed result
toParseOut (POk parseState result) =
let parseEnd = realSrcSpanStart $ last_loc parseState
endLine = srcLocLine parseEnd
endCol = srcLocCol parseEnd
(before, after) = splitAtLoc endLine endCol str
in Parsed result
-- Convert the bag of errors into an error string.
#if MIN_VERSION_ghc(9,8,0)
printErrorBag bag = joinLines . map (renderWithContext defaultSDocContext . formatBulleted . diagnosticMessage (defaultDiagnosticOpts @PsMessage) . errMsgDiagnostic) $ bagToList bag
#elif MIN_VERSION_ghc(9,6,0)
printErrorBag bag = joinLines . map (renderWithContext defaultSDocContext . formatBulleted defaultSDocContext . diagnosticMessage (defaultDiagnosticOpts @PsMessage) . errMsgDiagnostic) $ bagToList bag
#elif MIN_VERSION_ghc(9,4,0)
printErrorBag bag = joinLines . map (show . formatBulleted defaultSDocContext . diagnosticMessage . errMsgDiagnostic) $ bagToList bag
#elif MIN_VERSION_ghc(9,2,0)
printErrorBag bag = joinLines . map (show . pprError) $ bagToList bag
#else
printErrorBag bag = joinLines . map show $ bagToList bag
#endif
-- Taken from http://blog.shaynefletcher.org/2019/06/have-ghc-parsing-respect-dynamic-pragmas.html
parsePragmasIntoDynFlags :: DynFlags -> FilePath -> String -> IO (Maybe DynFlags)
parsePragmasIntoDynFlags flags filepath str =
catchErrors $ do
#if MIN_VERSION_ghc(9,4,0)
let opts = snd $ getOptions (initParserOpts flags) (stringToStringBuffer str) filepath
#else
let opts = getOptions flags (stringToStringBuffer str) filepath
#endif
(flags', _, _) <- parseDynamicFilePragma flags opts
return $ Just flags'
-- | Split a string at a given line and column. The column is included in
-- the second part of the split.
splitAtLoc :: LineNumber -> ColumnNumber -> String -> (String, String)
splitAtLoc line col string =
if line > length (lines string)
then (string, "")
else (before, after)
where
catchErrors :: IO (Maybe DynFlags) -> IO (Maybe DynFlags)
catchErrors act =
handleGhcException reportErr (handleSourceError reportErr act)
reportErr e = do
putStrLn $ "error : " ++ show e
return Nothing
(beforeLines, afterLines) = splitAt line $ lines string
theLine = last beforeLines
(beforeChars, afterChars) = splitAt (col - 1) theLine
before = joinLines (init beforeLines) ++ '\n' : beforeChars
after = joinLines $ afterChars : afterLines
-- Not the same as 'unlines', due to trailing \n
joinLines :: [String] -> String
@ -238,13 +131,11 @@ joinLines = intercalate "\n"
-- A chunk is a line and all lines immediately following that are indented
-- beyond the indentation of the first line. This parses Haskell layout
-- rules properly, and allows using multiline expressions via indentation.
--
-- Quasiquotes are allowed via a post-processing step.
layoutChunks :: String -> [Located String]
layoutChunks = joinQuasiquotes . go 1
layoutChunks = go 1
where
go :: LineNumber -> String -> [Located String]
go ln = filter (not . null . unloc) . map (fmap strip) . layoutLines ln . lines
go line = filter (not . null . unloc) . map (fmap strip) . layoutLines line . lines
-- drop spaces on left and right
strip = dropRight . dropLeft
@ -258,13 +149,13 @@ layoutChunks = joinQuasiquotes . go 1
layoutLines _ [] = []
-- Use the indent of the first line to find the end of the first block.
layoutLines lineIdx xs@(firstLine:rest) =
layoutLines lineIdx all@(firstLine:rest) =
let firstIndent = indentLevel firstLine
blockEnded ln = indentLevel ln <= firstIndent in
blockEnded line = indentLevel line <= firstIndent in
case findIndex blockEnded rest of
-- If the first block doesn't end, return the whole string, since
-- that just means the block takes up the entire string.
Nothing -> [Located lineIdx $ intercalate "\n" xs]
Nothing -> [Located lineIdx $ intercalate "\n" all]
-- We found the end of the block. Split this bit out and recurse.
Just idx ->
@ -283,7 +174,6 @@ layoutChunks = joinQuasiquotes . go 1
indentLevel _ = 0
-- | Drop comments from Haskell source.
-- Simply gets rid of them, does not replace them in any way.
removeComments :: String -> String
@ -306,7 +196,6 @@ removeComments = removeOneLineComments . removeMultilineComments 0 0
where
dropLine = removeOneLineComments . dropWhile (/= '\n')
removeMultilineComments :: Int -> Int -> String -> String
removeMultilineComments nesting pragmaNesting str =
case str of
-- Don't remove comments after cmd directives
@ -347,44 +236,7 @@ removeComments = removeOneLineComments . removeMultilineComments 0 0
-- Take a part of a string that ends in an unescaped quote.
takeString str = case str of
escaped@('\\':'"':_) -> escaped
'"':_ -> "\""
escaped@('\\':'"':rest) -> escaped
'"':rest -> "\""
x:xs -> x:takeString xs
[] -> []
-- | Post processing step to combine quasiquoted blocks into single blocks.
-- This is necessary because quasiquoted blocks don't follow normal indentation rules.
joinQuasiquotes :: [Located String] -> [Located String]
joinQuasiquotes = reverse . joinQuasiquotes' . reverse
where
-- This operates by finding |] and then joining blocks until a line
-- that has some corresponding [...|. This is still a hack, but close to
-- good enough.
joinQuasiquotes' [] = []
joinQuasiquotes' (block:blocks) =
if "|]" `isInfixOf` unloc block
then
let (pieces, rest) = break (hasQuasiquoteStart . unloc) blocks
in case rest of
[] -> block : joinQuasiquotes' blocks
startBlock:blocks' ->
concatBlocks (block : pieces ++ [startBlock]) : joinQuasiquotes blocks'
else block : joinQuasiquotes' blocks
-- Combine a lit of reversed blocks into a single, non-reversed block.
concatBlocks :: [Located String] -> Located String
concatBlocks blocks = Located (line $ last blocks) $ joinLines $ map unloc $ reverse blocks
-- Does this string have a [...| in it?
hasQuasiquoteStart :: String -> Bool
hasQuasiquoteStart str =
case break (== '[') str of
(_, "") -> False
(_, _:rest) ->
case break (== '|') rest of
(_, "") -> False
(chars, _) -> all isIdentChar chars
isIdentChar :: Char -> Bool
isIdentChar c = isAlphaNum c || c == '_' || c == '\''

View File

@ -2,43 +2,46 @@
-- documentation, see http://haskell.org/cabal/users-guide/
name: ghc-parser
version: 0.2.7.0
version: 0.1.6.0
synopsis: Haskell source parser from GHC.
-- description:
homepage: https://github.com/IHaskell/IHaskell
homepage: https://github.com/gibiansky/IHaskell
license: MIT
license-file: LICENSE
author: Andrew Gibiansky
maintainer: andrew.gibiansky@gmail.com
-- copyright:
category: Language
build-type: Simple
build-type: Custom
-- extra-source-files:
cabal-version: 1.16
cabal-version: >=1.16
extra-source-files:
build-parser.sh
HaskellParser76.y.pp
HaskellParser782.y.pp
HaskellParser783.y.pp
src-7.10/Language/Haskell/GHC/HappyParser.hs
library
ghc-options: -Wall
build-tools: happy, cpphs
exposed-modules: Language.Haskell.GHC.Parser,
Language.Haskell.GHC.HappyParser
-- other-modules:
-- other-extensions:
build-depends: base >=4.9 && < 5,
ghc >=8.4 && <9.11
if impl(ghc >= 8.4) && impl(ghc < 8.10)
hs-source-dirs: generic-src src-8.4
if impl(ghc >= 8.10) && impl(ghc < 9.0)
hs-source-dirs: generic-src src-8.10
if impl(ghc >= 9.0) && impl(ghc < 9.2)
hs-source-dirs: generic-src src-9.0
if impl(ghc >= 9.2) && impl(ghc < 9.4)
hs-source-dirs: generic-src src-9.2
if impl(ghc >= 9.4) && impl(ghc < 9.6)
hs-source-dirs: generic-src src-9.4
if impl(ghc >= 9.6) && impl(ghc < 9.8)
hs-source-dirs: generic-src src-9.6
if impl(ghc >= 9.8) && impl(ghc < 9.12)
hs-source-dirs: generic-src src-9.8
build-depends: base >=4.6 && <4.9,
ghc >=7.6 && <7.11
if impl(ghc >= 7.6) && impl(ghc < 7.8)
hs-source-dirs: generic-src src-7.6
else
if impl(ghc >= 7.8) && impl(ghc < 7.8.3)
hs-source-dirs: generic-src src-7.8.2
else
if impl(ghc < 7.10)
hs-source-dirs: generic-src src-7.8.3
else
hs-source-dirs: generic-src src-7.10
default-language: Haskell2010

View File

@ -0,0 +1,42 @@
module Language.Haskell.GHC.HappyParser
( fullStatement
, fullImport
, fullDeclaration
, fullExpression
, fullTypeSignature
, fullModule
) where
import Parser
import SrcLoc
-- compiler/hsSyn
import HsSyn
-- compiler/utils
import OrdList
-- compiler/parser
import RdrHsSyn
import Lexer
-- compiler/basicTypes
import RdrName
fullStatement :: P (Maybe (LStmt RdrName (LHsExpr RdrName)))
fullStatement = parseStmt
fullImport :: P (LImportDecl RdrName)
fullImport = parseImport
fullDeclaration :: P (OrdList (LHsDecl RdrName))
fullDeclaration = parseDeclaration
fullExpression :: P (LHsExpr RdrName)
fullExpression = parseExpression
fullTypeSignature :: P (Located (OrdList (LHsDecl RdrName)))
fullTypeSignature = parseTypeSignature
fullModule :: P (Located (HsModule RdrName))
fullModule = parseModule

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,40 +0,0 @@
module Language.Haskell.GHC.HappyParser
( fullStatement
, fullImport
, fullDeclaration
, fullExpression
, fullTypeSignature
, fullModule
) where
import Parser
import SrcLoc
-- compiler/hsSyn
import GHC.Hs
-- compiler/utils
import OrdList
-- compiler/parser
import Lexer
import RdrHsSyn (runECP_P)
fullStatement :: P (Maybe (LStmt GhcPs (LHsExpr GhcPs)))
fullStatement = parseStmt
fullImport :: P (LImportDecl GhcPs)
fullImport = parseImport
fullDeclaration :: P (OrdList (LHsDecl GhcPs))
fullDeclaration = fmap unitOL parseDeclaration
fullExpression :: P (LHsExpr GhcPs)
fullExpression = runECP_P =<< parseExpression
fullTypeSignature :: P (Located (OrdList (LHsDecl GhcPs)))
fullTypeSignature = fmap (noLoc . unitOL) parseTypeSignature
fullModule :: P (Located (HsModule GhcPs))
fullModule = parseModule

View File

@ -1,38 +0,0 @@
module Language.Haskell.GHC.HappyParser
( fullStatement
, fullImport
, fullDeclaration
, fullExpression
, fullTypeSignature
, fullModule
) where
import Parser
import SrcLoc
-- compiler/hsSyn
import HsSyn
-- compiler/utils
import OrdList
-- compiler/parser
import Lexer
fullStatement :: P (Maybe (LStmt GhcPs (LHsExpr GhcPs)))
fullStatement = parseStmt
fullImport :: P (LImportDecl GhcPs)
fullImport = parseImport
fullDeclaration :: P (OrdList (LHsDecl GhcPs))
fullDeclaration = fmap unitOL parseDeclaration
fullExpression :: P (LHsExpr GhcPs)
fullExpression = parseExpression
fullTypeSignature :: P (Located (OrdList (LHsDecl GhcPs)))
fullTypeSignature = fmap (noLoc . unitOL) parseTypeSignature
fullModule :: P (Located (HsModule GhcPs))
fullModule = parseModule

View File

@ -1,40 +0,0 @@
module Language.Haskell.GHC.HappyParser
( fullStatement
, fullImport
, fullDeclaration
, fullExpression
, fullTypeSignature
, fullModule
) where
import GHC.Parser
import GHC.Types.SrcLoc
-- compiler/hsSyn
import GHC.Hs
-- compiler/utils
import GHC.Data.OrdList
-- compiler/parser
import GHC.Parser.Lexer
import GHC.Parser.PostProcess (runECP_P)
fullStatement :: P (Maybe (LStmt GhcPs (LHsExpr GhcPs)))
fullStatement = parseStmt
fullImport :: P (LImportDecl GhcPs)
fullImport = parseImport
fullDeclaration :: P (OrdList (LHsDecl GhcPs))
fullDeclaration = fmap unitOL parseDeclaration
fullExpression :: P (LHsExpr GhcPs)
fullExpression = runECP_P =<< parseExpression
fullTypeSignature :: P (Located (OrdList (LHsDecl GhcPs)))
fullTypeSignature = fmap (noLoc . unitOL) parseTypeSignature
fullModule :: P (Located HsModule)
fullModule = parseModule

View File

@ -1,40 +0,0 @@
module Language.Haskell.GHC.HappyParser
( fullStatement
, fullImport
, fullDeclaration
, fullExpression
, fullTypeSignature
, fullModule
) where
import GHC.Parser
import GHC.Types.SrcLoc
-- compiler/hsSyn
import GHC.Hs
-- compiler/utils
import GHC.Data.OrdList
-- compiler/parser
import GHC.Parser.Lexer
import GHC.Parser.PostProcess (ECP(..), runPV)
fullStatement :: P (Maybe (LStmt GhcPs (LHsExpr GhcPs)))
fullStatement = parseStmt
fullImport :: P (LImportDecl GhcPs)
fullImport = parseImport
fullDeclaration :: P (OrdList (LHsDecl GhcPs))
fullDeclaration = fmap unitOL parseDeclaration
fullExpression :: P (LHsExpr GhcPs)
fullExpression = parseExpression >>= \p -> runPV $ unECP p
fullTypeSignature :: P (Located (OrdList (LHsDecl GhcPs)))
fullTypeSignature = fmap (noLoc . unitOL) parseTypeSignature
fullModule :: P (Located HsModule)
fullModule = parseModule

View File

@ -1,40 +0,0 @@
module Language.Haskell.GHC.HappyParser
( fullStatement
, fullImport
, fullDeclaration
, fullExpression
, fullTypeSignature
, fullModule
) where
import GHC.Parser
import GHC.Types.SrcLoc
-- compiler/hsSyn
import GHC.Hs
-- compiler/utils
import GHC.Data.OrdList
-- compiler/parser
import GHC.Parser.Lexer
import GHC.Parser.PostProcess (ECP(..), runPV)
fullStatement :: P (Maybe (LStmt GhcPs (LHsExpr GhcPs)))
fullStatement = parseStmt
fullImport :: P (LImportDecl GhcPs)
fullImport = parseImport
fullDeclaration :: P (OrdList (LHsDecl GhcPs))
fullDeclaration = fmap unitOL parseDeclaration
fullExpression :: P (LHsExpr GhcPs)
fullExpression = parseExpression >>= \p -> runPV $ unECP p
fullTypeSignature :: P (Located (OrdList (LHsDecl GhcPs)))
fullTypeSignature = fmap (noLoc . unitOL) parseTypeSignature
fullModule :: P (Located HsModule)
fullModule = parseModule

View File

@ -1,40 +0,0 @@
module Language.Haskell.GHC.HappyParser
( fullStatement
, fullImport
, fullDeclaration
, fullExpression
, fullTypeSignature
, fullModule
) where
import GHC.Parser
import GHC.Types.SrcLoc
-- compiler/hsSyn
import GHC.Hs
-- compiler/utils
import GHC.Data.OrdList
-- compiler/parser
import GHC.Parser.Lexer
import GHC.Parser.PostProcess (ECP(..), runPV)
fullStatement :: P (Maybe (LStmt GhcPs (LHsExpr GhcPs)))
fullStatement = parseStmt
fullImport :: P (LImportDecl GhcPs)
fullImport = parseImport
fullDeclaration :: P (OrdList (LHsDecl GhcPs))
fullDeclaration = fmap unitOL parseDeclaration
fullExpression :: P (LHsExpr GhcPs)
fullExpression = parseExpression >>= \p -> runPV $ unECP p
fullTypeSignature :: P (Located (OrdList (LHsDecl GhcPs)))
fullTypeSignature = fmap (noLoc . unitOL) parseTypeSignature
fullModule :: P (Located (HsModule GhcPs))
fullModule = parseModule

View File

@ -1,40 +0,0 @@
module Language.Haskell.GHC.HappyParser
( fullStatement
, fullImport
, fullDeclaration
, fullExpression
, fullTypeSignature
, fullModule
) where
import GHC.Parser
import GHC.Types.SrcLoc
-- compiler/hsSyn
import GHC.Hs
-- compiler/utils
import GHC.Data.OrdList
-- compiler/parser
import GHC.Parser.Lexer
import GHC.Parser.PostProcess (ECP(..), runPV)
fullStatement :: P (Maybe (LStmt GhcPs (LHsExpr GhcPs)))
fullStatement = parseStmt
fullImport :: P (LImportDecl GhcPs)
fullImport = parseImport
fullDeclaration :: P (OrdList (LHsDecl GhcPs))
fullDeclaration = fmap unitOL parseDeclaration
fullExpression :: P (LHsExpr GhcPs)
fullExpression = parseExpression >>= \p -> runPV $ unECP p
fullTypeSignature :: P (Located (OrdList (LHsDecl GhcPs)))
fullTypeSignature = fmap (noLoc . unitOL) parseTypeSignature
fullModule :: P (Located (HsModule GhcPs))
fullModule = parseModule

View File

@ -1,36 +0,0 @@
cradle:
cabal:
- path: "./ghc-parser"
component: "ghc-parser:lib:ghc-parser"
- path: "./ipython-kernel"
component: "ipython-kernel:lib:ipython-kernel"
- path: "./src"
component: "ihaskell:lib:ihaskell"
- path: "./main"
component: "ihaskell:exe:ihaskell"
- path: "./test"
component: "ihaskell:hspec"
- path: "./ihaskell-display/ihaskell-aeson"
component: "ihaskell-aeson:lib:ihaskell-aeson"
- path: "./ihaskell-display/ihaskell-blaze"
component: "ihaskell-blaze:lib:ihaskell-blaze"
- path: "./ihaskell-display/ihaskell-charts"
component: "ihaskell-charts:lib:ihaskell-charts"
- path: "./ihaskell-display/ihaskell-diagrams"
component: "ihaskell-diagrams:lib:ihaskell-diagrams"
- path: "./ihaskell-display/ihaskell-gnuplot"
component: "ihaskell-gnuplot:lib:ihaskell-gnuplot"
- path: "./ihaskell-display/ihaskell-hatex"
component: "ihaskell-hatex:lib:ihaskell-hatex"
- path: "./ihaskell-display/ihaskell-juicypixels"
component: "ihaskell-juicypixels:lib:ihaskell-juicypixels"
- path: "./ihaskell-display/ihaskell-magic"
component: "ihaskell-magic:lib:ihaskell-magic"
- path: "./ihaskell-display/ihaskell-plot"
component: "ihaskell-plot:lib:ihaskell-plot"
- path: "./ihaskell-display/ihaskell-static-canvas"
component: "ihaskell-static-canvas:lib:ihaskell-static-canvas"
- path: "./ihaskell-display/ihaskell-widgets"
component: "ihaskell-widgets:lib:ihaskell-widgets"
- path: "./ihaskell-display/ihaskell-graphviz"
component: "ihaskell-graphviz:lib:ihaskell-graphviz"

96
html/custom.css Normal file
View File

@ -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;
}

View File

@ -22,7 +22,7 @@ define(['require',
var downArrow = 40;
IPython.keyboard.keycodes.down = downArrow; // space
IPython.CodeCell.options_default['cm_config']['mode'] = 'ihaskell';
IPython.CodeCell.options_default['cm_config']['mode'] = 'haskell';
utils.requireCodeMirrorMode('haskell', function(){
// Create a multiplexing mode that uses Haskell highlighting by default but
@ -52,6 +52,9 @@ define(['require',
}
});
if(IPython.notebook.set_codemirror_mode){
// this first one will not be necessary in the future
// neither should the loop on all cells above
IPython.notebook.set_codemirror_mode('null')
IPython.notebook.set_codemirror_mode('ihaskell')
}

BIN
html/logo-64x64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -1,89 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64"
height="64"
viewBox="0 0 48.000001 47.999999"
version="1.1"
id="svg18"
sodipodi:docname="haskell_logo.svg"
inkscape:version="0.92.3 (unknown)">
<metadata
id="metadata22">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2495"
inkscape:window-height="1416"
id="namedview20"
showgrid="false"
units="px"
inkscape:zoom="4.9870803"
inkscape:cx="87.017073"
inkscape:cy="4.4960944"
inkscape:window-x="65"
inkscape:window-y="24"
inkscape:window-maximized="1"
inkscape:current-layer="svg18" />
<defs
id="defs5">
<clipPath
id="clip1">
<path
d="M 0,340.15625 H 481.89062 V 0 H 0 Z m 0,0"
id="path2"
inkscape:connector-curvature="0" />
</clipPath>
</defs>
<g
id="surface0"
transform="matrix(0.09960767,0,0,0.09960767,-0.1503886,5.8491832)">
<g
clip-path="url(#clip1)"
id="g15"
style="clip-rule:nonzero">
<path
style="fill:#453b61;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,340.15625 113.38672,170.07812 0,0 H 85.039062 L 198.42578,170.07812 85.039062,340.15625 Z m 0,0"
id="path7"
inkscape:connector-curvature="0" />
<path
style="fill:#5f5286;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 113.38672,340.15625 226.77344,170.07812 113.38672,0 h 85.03906 L 425.19531,340.15625 H 340.15625 L 269.29297,233.85937 198.42578,340.15625 Z m 0,0"
id="path9"
inkscape:connector-curvature="0" />
<path
style="fill:#8e508a;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 387.40234,240.94531 -37.79297,-56.6914 132.28125,-0.004 v 56.69531 z m 0,0"
id="path11"
inkscape:connector-curvature="0" />
<path
style="fill:#8e508a;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 330.71094,155.90625 -37.79688,-56.691406 188.97656,-0.0039 v 56.695316 z m 0,0"
id="path13"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -1,17 +1,17 @@
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE NoImplicitPrelude, TypeSynonymInstances, QuasiQuotes #-}
module IHaskell.Display.Aeson () where
import Data.Text as T
import Data.ByteString.Lazy as LBS
import Data.Text.Encoding as E
import ClassyPrelude
import Data.Textual.Encoding
import Data.Aeson
import Data.Aeson.Encode.Pretty
import Data.String.Here
import IHaskell.Display
instance IHaskellDisplay Value where
display renderable = return $ Display [plain json]
display renderable = return $ Display [plain json, html dom]
where
json = T.unpack $ E.decodeUtf8 $ LBS.toStrict $ encodePretty renderable
json = unpack $ decodeUtf8 $ encodePretty renderable
dom = [i|<div class="highlight-code" id="javascript">${json}</div>|]

View File

@ -1,3 +1,2 @@
import Distribution.Simple
import Distribution.Simple
main = defaultMain

View File

@ -1,19 +1,19 @@
-- The name of the package.
name: ihaskell-aeson
-- The package version. See the Haskell package versioning policy (PVP)
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- http://www.haskell.org/haskellwiki/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.3.0.1
version: 0.2.0.0
-- A short (one-line) description of the package.
synopsis: IHaskell display instances for Aeson
-- A longer description of the package.
-- description:
-- description:
-- URL for the project homepage or repository.
homepage: http://www.github.com/gibiansky/ihaskell
@ -27,41 +27,46 @@ license-file: LICENSE
-- The package author(s).
author: Andrew Gibiansky
-- An email address to which users can send suggestions, bug reports, and
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: andrew.gibiansky@gmail.com
-- A copyright notice.
-- copyright:
-- copyright:
category: Development
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- Extra files to be distributed with the package, such as examples or a
-- README.
-- extra-source-files:
-- extra-source-files:
-- Constraint on the version of Cabal needed to build this package.
cabal-version: 1.16
cabal-version: >=1.16
library
-- Modules exported by the library.
exposed-modules: IHaskell.Display.Aeson
-- Modules included in this library but not exported.
-- other-modules:
-- other-modules:
-- Language extensions.
default-extensions: DoAndIfThenElse
OverloadedStrings
-- Other library packages from which modules are imported.
build-depends: base >=4.9 && <5,
text,
bytestring,
build-depends: base >=4.6 && <4.9,
here,
classy-prelude >=0.7,
aeson >= 0.7,
aeson-pretty >= 0.7,
chunked-data >=0.1,
ihaskell >= 0.5
-- Directories containing source files.
-- hs-source-dirs:
-- hs-source-dirs:
-- Base language which the package is written in.
default-language: Haskell2010

View File

@ -0,0 +1,19 @@
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
module IHaskell.Display.Basic () where
import IHaskell.Display
import Text.Printf
instance Show a => IHaskellDisplay (Maybe a) where
display just = return $ Display [stringDisplay, htmlDisplay]
where
stringDisplay = plain (show just)
htmlDisplay = html str
str =
case just of
Nothing -> "<span style='color: red; font-weight: bold;'>Nothing</span>"
Just x -> printf
"<span style='color: green; font-weight: bold;'>Just</span><span style='font-family: monospace;'>%s</span>"
(show x)

View File

@ -0,0 +1,4 @@
IHaskell-Display
================
Instances of IHaskellDisplay for default prelude data types.

View File

@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain

View File

@ -1,22 +1,25 @@
-- The name of the package.
name: ihaskell-graphviz
-- Initial ihaskell-display.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
-- The package version. See the Haskell package versioning policy (PVP)
-- The name of the package.
name: ihaskell-basic
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- http://www.haskell.org/haskellwiki/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.1.0.0
version: 0.2.0.0
-- A short (one-line) description of the package.
synopsis: IHaskell display instance for GraphViz (external binary)
synopsis: IHaskell display instances for basic types
-- A longer description of the package.
-- description:
-- description:
-- URL for the project homepage or repository.
homepage: http://www.github.com/gibiansky/ihaskell
homepage: http://www.github.com/gibiansky/IHaskell
-- The license under which the package is released.
license: MIT
@ -25,44 +28,46 @@ license: MIT
license-file: LICENSE
-- The package author(s).
author: Lucas DiCioccio <lucas@dicioccio.fr>
author: Andrew Gibiansky
-- An email address to which users can send suggestions, bug reports, and
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: Lucas DiCioccio <lucas@dicioccio.fr>,
Vaibhav Sagar <vaibhavsagar@gmail.com>,
Andrew Gibiansky <andrew.gibiansky@gmail.com>
maintainer: andrew.gibiansky@gmail.com
-- A copyright notice.
-- copyright:
-- copyright:
category: Development
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- Extra files to be distributed with the package, such as examples or a
-- README.
-- extra-source-files:
-- extra-source-files:
-- Constraint on the version of Cabal needed to build this package.
cabal-version: 1.16
cabal-version: >=1.16
library
-- Modules exported by the library.
exposed-modules: IHaskell.Display.Graphviz
exposed-modules: IHaskell.Display.Basic
-- Modules included in this library but not exported.
-- other-modules:
-- other-modules:
-- Language extensions.
default-extensions: DoAndIfThenElse
OverloadedStrings
-- Other library packages from which modules are imported.
build-depends: base >=4.9 && <5,
bytestring,
text,
process,
ihaskell >= 0.6.2
build-depends: base >=4.6 && <4.9,
classy-prelude >=0.6,
ihaskell >= 0.5
-- Directories containing source files.
-- hs-source-dirs:
-- hs-source-dirs:
-- Base language which the package is written in.
default-language: Haskell2010

View File

@ -2,7 +2,6 @@
module IHaskell.Display.Blaze () where
import IHaskell.CSS (ihaskellCSS)
import IHaskell.Display
import Text.Printf
@ -16,4 +15,4 @@ instance IHaskellDisplay (MarkupM a) where
where
str = renderMarkup (void val)
stringDisplay = plain str
htmlDisplay = html' (Just ihaskellCSS) str
htmlDisplay = html str

View File

@ -1,3 +1,2 @@
import Distribution.Simple
import Distribution.Simple
main = defaultMain

View File

@ -1,22 +1,22 @@
-- Initial ihaskell-display.cabal generated by cabal init. For further
-- Initial ihaskell-display.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
-- The name of the package.
name: ihaskell-blaze
-- The package version. See the Haskell package versioning policy (PVP)
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- http://www.haskell.org/haskellwiki/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.3.0.1
version: 0.2.0.0
-- A short (one-line) description of the package.
synopsis: IHaskell display instances for blaze-html types
-- A longer description of the package.
-- description:
-- description:
-- URL for the project homepage or repository.
homepage: http://www.github.com/gibiansky/ihaskell
@ -30,40 +30,45 @@ license-file: LICENSE
-- The package author(s).
author: Andrew Gibiansky
-- An email address to which users can send suggestions, bug reports, and
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: andrew.gibiansky@gmail.com
-- A copyright notice.
-- copyright:
-- copyright:
category: Development
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- Extra files to be distributed with the package, such as examples or a
-- README.
-- extra-source-files:
-- extra-source-files:
-- Constraint on the version of Cabal needed to build this package.
cabal-version: 1.16
cabal-version: >=1.16
library
-- Modules exported by the library.
exposed-modules: IHaskell.Display.Blaze
-- Modules included in this library but not exported.
-- other-modules:
-- other-modules:
-- Language extensions.
default-extensions: DoAndIfThenElse
OverloadedStrings
-- Other library packages from which modules are imported.
build-depends: base >=4.9 && <5,
build-depends: base >=4.6 && <4.9,
classy-prelude >=0.6,
blaze-html >= 0.6,
blaze-markup >= 0.5,
ihaskell >= 0.5
-- Directories containing source files.
-- hs-source-dirs:
-- hs-source-dirs:
-- Base language which the package is written in.
default-language: Haskell2010

View File

@ -1,15 +1,15 @@
{-# LANGUAGE NoImplicitPrelude, CPP #-}
module IHaskell.Display.Charts () where
import ClassyPrelude
import System.Directory
import Data.Default.Class
import Graphics.Rendering.Chart.Renderable
import Graphics.Rendering.Chart.Backend.Cairo
import qualified Data.ByteString.Char8 as Char
import qualified Data.ByteString as BS
import qualified Data.Text.Encoding as T.Encoding
import System.IO.Temp
import System.IO.Unsafe
import System.FilePath ((</>))
import IHaskell.Display
@ -31,22 +31,21 @@ instance IHaskellDisplay (Renderable a) where
chartData :: Renderable a -> FileFormat -> IO DisplayData
chartData renderable format = do
-- We should not have to round-trip this ByteString to a temp file.
-- https://github.com/IHaskell/IHaskell/issues/1248
withSystemTempDirectory "ihaskell-charts" $ \tmpdir -> do
switchToTmpDir
-- Write the PNG image.
let
filename = tmpdir </> "ihaskell-chart.png"
let filename = ".ihaskell-chart.png"
opts = def { _fo_format = format, _fo_size = (width, height) }
renderableToFile opts filename renderable
mkFile opts filename renderable
-- Convert to base64.
imgData <- readFile filename
return $
case format of
PNG -> do
-- Convert to base64.
imgData <- Char.readFile filename
pure $ png width height $ base64 imgData
SVG -> do
imgData <- BS.readFile filename
pure $ svg $ T.Encoding.decodeUtf8 imgData
_ -> error "Unreachable case, not PNG or SVG"
PNG -> png width height $ base64 imgData
SVG -> svg $ Char.unpack imgData
#if MIN_VERSION_Chart_cairo(1,3,0)
mkFile opts filename renderable = renderableToFile opts filename renderable
#else
mkFile opts filename renderable = renderableToFile opts renderable filename
#endif

View File

@ -1,3 +1,2 @@
import Distribution.Simple
import Distribution.Simple
main = defaultMain

File diff suppressed because one or more lines are too long

View File

@ -1,19 +1,19 @@
-- The name of the package.
name: ihaskell-charts
-- The package version. See the Haskell package versioning policy (PVP)
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- http://www.haskell.org/haskellwiki/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.3.0.1
version: 0.2.0.0
-- A short (one-line) description of the package.
synopsis: IHaskell display instances for charts types
-- A longer description of the package.
-- description:
-- description:
-- URL for the project homepage or repository.
homepage: http://www.github.com/gibiansky/ihaskell
@ -27,46 +27,48 @@ license-file: LICENSE
-- The package author(s).
author: Andrew Gibiansky
-- An email address to which users can send suggestions, bug reports, and
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: andrew.gibiansky@gmail.com
-- A copyright notice.
-- copyright:
-- copyright:
category: Development
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- Extra files to be distributed with the package, such as examples or a
-- README.
-- extra-source-files:
-- extra-source-files:
-- Constraint on the version of Cabal needed to build this package.
cabal-version: 1.16
cabal-version: >=1.16
library
-- Modules exported by the library.
exposed-modules: IHaskell.Display.Charts
-- Modules included in this library but not exported.
-- other-modules:
-- other-modules:
-- Language extensions.
default-extensions: DoAndIfThenElse
OverloadedStrings
-- Other library packages from which modules are imported.
build-depends: base >=4.9 && <5,
build-depends: base >=4.6 && <4.9,
classy-prelude >=0.10.5,
bytestring,
text,
data-default-class,
directory,
filepath,
temporary,
Chart,
Chart-cairo >=1.2,
ihaskell >= 0.6.2
ihaskell >= 0.5
-- Directories containing source files.
-- hs-source-dirs:
-- hs-source-dirs:
-- Base language which the package is written in.
default-language: Haskell2010

File diff suppressed because one or more lines are too long

View File

@ -1,74 +1,53 @@
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE NoImplicitPrelude, TypeSynonymInstances, FlexibleInstances #-}
module IHaskell.Display.Diagrams
( diagram, animation
, ManuallySized, withSizeSpec, withImgWidth, withImgHeight
, ManuallySampled, withAnimFps
) where
module IHaskell.Display.Diagrams (diagram, animation) where
import ClassyPrelude
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as Char
import qualified Data.Text.Encoding as T.Encoding
import System.Directory
import System.IO.Temp
import System.FilePath ((</>))
import Diagrams.Backend.Cairo
import qualified Data.ByteString.Char8 as Char
import System.IO.Unsafe
import Diagrams.Prelude
import Diagrams.Backend.Cairo
import IHaskell.Display
import IHaskell.Display.Diagrams.Animation
import IHaskell.Display.Diagrams.ImgSize
instance IHaskellDisplay (ManuallySized (QDiagram Cairo V2 Double Any)) where
instance IHaskellDisplay (QDiagram Cairo V2 Double Any) where
display renderable = do
png <- diagramData renderable PNG
svg <- diagramData renderable SVG
return $ Display [png, svg]
diagramData :: ManuallySized (Diagram Cairo) -> OutputType -> IO DisplayData
diagramData (ManuallySized renderable imgWidth imgHeight) format = do
-- We should not have to round-trip this ByteString to a temp file.
-- https://github.com/IHaskell/IHaskell/issues/1248
withSystemTempDirectory "ihaskell-diagram" $ \tmpdir -> do
let path = case format of
SVG -> tmpdir </> "ihaskell-diagram.svg"
PNG -> tmpdir </> "ihaskell-diagram.png"
_ -> error "Unreachable case"
diagramData :: Diagram Cairo -> OutputType -> IO DisplayData
diagramData renderable format = do
switchToTmpDir
-- Write the image.
renderCairo path (mkSizeSpec2D (Just imgWidth)
(Just imgHeight)) renderable
-- Compute width and height.
let w = width renderable
h = height renderable
aspect = w / h
imgHeight = 300
imgWidth = aspect * imgHeight
case format of
PNG -> do
-- Convert to base64.
imgData <- Char.readFile path
pure $ png (floor imgWidth) (floor imgHeight) $ base64 imgData
SVG -> do
imgData <- BS.readFile path
pure $ svg (T.Encoding.decodeUtf8 imgData)
_ -> error "Unreachable case"
-- Write the image.
let filename = ".ihaskell-diagram." ++ extension format
renderCairo filename (mkSizeSpec2D (Just imgWidth) (Just imgHeight)) renderable
-- Convert to base64.
imgData <- readFile filename
let value =
case format of
PNG -> png (floor imgWidth) (floor imgHeight) $ base64 imgData
SVG -> svg $ Char.unpack imgData
return value
where
extension SVG = "svg"
extension PNG = "png"
-- Rendering hint.
diagram :: Diagram Cairo -> Diagram Cairo
diagram = id
instance (b ~ Cairo, v ~ V2, s ~ Double, m ~ Any)
=> ManuallySizeable (QDiagram a v s m) where
withSizeSpec spec renderable = ManuallySized renderable imgWidth imgHeight
where
aspect = width renderable / height renderable
V2 imgWidth imgHeight = case getSpec spec of
V2 (Just w) (Just h) -> V2 w h
V2 (Just w) Nothing -> V2 w (w/aspect)
V2 Nothing (Just h) -> V2 (aspect*h) h
V2 Nothing Nothing -> (defaultDiagonal / sqrt (1 + aspect^2))
*^ V2 aspect 1
-- w^2 + h^2 = defaultDiagonal^2 / (1+aspect^2)
-- * (aspect^2 + 1)
-- = defaultDiagonal^2
-- w/h = aspect/1 = aspect
defaultDiagonal = 500
instance IHaskellDisplay (QDiagram Cairo V2 Double Any) where
display = display . withSizeSpec (mkSizeSpec2D Nothing Nothing)

View File

@ -1,141 +1,55 @@
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DeriveFunctor, DeriveGeneric #-}
{-# LANGUAGE NoImplicitPrelude, TypeSynonymInstances, FlexibleInstances #-}
module IHaskell.Display.Diagrams.Animation
( animation
, ManuallySampled, withAnimFps
) where
module IHaskell.Display.Diagrams.Animation (animation) where
import qualified Data.ByteString.Char8 as CBS
import qualified Data.Text as T
import ClassyPrelude hiding (filename)
import GHC.Generics (Generic)
import Data.Maybe (fromMaybe)
import Diagrams.Prelude
import Diagrams.Backend.Cairo
import Diagrams.Backend.Cairo.CmdLine (GifOpts(..))
import Diagrams.Backend.CmdLine (DiagramOpts(..), mainRender)
import System.IO.Temp
import System.FilePath ((</>))
import IHaskell.Display
import IHaskell.Display.Diagrams.ImgSize
data ManuallySampled a = ManuallySampled
{ contentToSample :: a
, signalManualSampleRate :: Maybe Rational
} deriving (Show, Functor, Generic)
class ManuallySamplable a where
withSamplingSpec :: Maybe Rational -> a -> ManuallySampled a
defaultFps = 30
withAnimFps :: ManuallySamplable a => Rational -> a -> ManuallySampled a
withAnimFps fps = withSamplingSpec (Just fps)
instance IHaskellDisplay (ManuallySized (ManuallySampled (QAnimation Cairo V2 Double Any))) where
instance IHaskellDisplay (QAnimation Cairo V2 Double Any) where
display renderable = do
gif <- animationData renderable
return $ Display [html' Nothing $ "<img src=\"data:image/gif;base64," ++ gif ++ "\" />"]
return $ Display [html $ "<img src=\"data:image/gif;base64,"
++ gif ++ "\" />"]
animationData :: Animation Cairo V2 Double -> IO String
animationData renderable = do
switchToTmpDir
animationData :: ManuallySized (ManuallySampled (Animation Cairo V2 Double)) -> IO String
animationData (ManuallySized (ManuallySampled renderable fps) imgWidth imgHeight) = do
-- We should not have to round-trip this ByteString to a temp file.
-- https://github.com/IHaskell/IHaskell/issues/1248
withSystemTempDirectory "ihaskell-diagram" $ \tmpdir -> do
-- Generate the frames
let fps = 30
animAdjusted = animEnvelope' fps renderable
frames = simulate fps animAdjusted
timediff = 100 `div` ceiling fps :: Int
frameSet = map (\x -> (x # bg white, timediff)) frames
let path = tmpdir </> "ihaskell-diagram.gif"
-- Compute width and height.
let shape = activeStart animAdjusted
w = width shape
h = height shape
aspect = w / h
imgHeight = 300
imgWidth = aspect * imgHeight
-- Generate the frames
let actualFps = fromMaybe defaultFps fps
animAdjusted = animEnvelope' actualFps renderable
frames = simulate actualFps animAdjusted
timediff = 100 `div` ceiling actualFps :: Int
frameSet = map (\x -> (x # bg white, timediff)) frames
-- Write the image.
let diagOpts = DiagramOpts
{ _width = Just . ceiling $ imgWidth
, _height = Just . ceiling $ imgHeight
, _output = path
}
gifOpts = GifOpts { _dither = True, _noLooping = False, _loopRepeat = Nothing }
mainRender (diagOpts, gifOpts) frameSet
-- Convert to ascii represented base64 encoding
imgData <- CBS.readFile path
return . T.unpack . base64 $ imgData
-- Write the image.
let filename = ".ihaskell-diagram.gif"
diagOpts = DiagramOpts
{ _width = Just . ceiling $ imgWidth
, _height = Just . ceiling $ imgHeight
, _output = filename
}
gifOpts = GifOpts { _dither = True, _noLooping = False, _loopRepeat = Nothing }
mainRender (diagOpts, gifOpts) frameSet
-- Convert to ascii represented base64 encoding
imgData <- readFile filename
return . unpack . base64 $ imgData
-- Rendering hint.
animation :: Animation Cairo V2 Double -> Animation Cairo V2 Double
animation = id
getImgSize renderable sizeSpec fps = out
where
actualFps = fromMaybe defaultFps fps
shape = activeStart $ animEnvelope' actualFps renderable
aspect = width shape / height shape
out = case getSpec sizeSpec of
V2 (Just w) (Just h) -> V2 w h
V2 (Just w) Nothing -> V2 w (w/aspect)
V2 Nothing (Just h) -> V2 (aspect*h) h
V2 Nothing Nothing -> (defaultDiagonal / sqrt (1 + aspect^2))
*^ V2 aspect 1
-- w^2 + h^2 = defaultDiagonal^2 / (1+aspect^2)
-- * (aspect^2 + 1)
-- = defaultDiagonal^2
-- w/h = aspect/1 = aspect
defaultDiagonal = 500
instance (b ~ Cairo, v ~ V2, s ~ Double, m ~ Any)
=> ManuallySamplable (QAnimation b v s m) where
withSamplingSpec fps renderable = ManuallySampled renderable fps
instance (b ~ Cairo, v ~ V2, s ~ Double, m ~ Any)
=> ManuallySamplable (ManuallySized (QAnimation b v s m)) where
withSamplingSpec fps sizedRenderable = ManuallySampled sizedRenderable fps
instance (b ~ Cairo, v ~ V2, s ~ Double, m ~ Any)
=> ManuallySizeable (QAnimation b v s m) where
withSizeSpec spec renderable = ManuallySized renderable imgWidth imgHeight
where
fps = Nothing
V2 imgWidth imgHeight = getImgSize renderable spec fps
instance (b ~ Cairo, v ~ V2, s ~ Double, m ~ Any)
=> ManuallySizeable (ManuallySampled (QAnimation b v s m)) where
withSizeSpec spec (ManuallySampled renderable fps) = out
where
out = ManuallySized (ManuallySampled renderable fps) w h
V2 w h = getImgSize renderable spec fps
instance IHaskellDisplay (QAnimation Cairo V2 Double Any) where
display = display . withSizeSpec (mkSizeSpec2D Nothing Nothing) . withSamplingSpec fps
where
fps = Nothing
instance IHaskellDisplay (ManuallySized (QAnimation Cairo V2 Double Any)) where
display (ManuallySized renderable w h) = out
where
fps = Nothing
sizeSpec = mkSizeSpec2D (Just w) (Just h)
out = display . withSizeSpec sizeSpec $ withSamplingSpec fps renderable
instance IHaskellDisplay (ManuallySampled (QAnimation Cairo V2 Double Any)) where
display = display . withSizeSpec (mkSizeSpec2D Nothing Nothing)
instance IHaskellDisplay (ManuallySampled (ManuallySized (QAnimation Cairo V2 Double Any))) where
display (ManuallySampled (ManuallySized renderable w h) fps) = out
where
sizeSpec = mkSizeSpec2D (Just w) (Just h)
out = display . withSizeSpec sizeSpec $ withSamplingSpec fps renderable

View File

@ -1,24 +0,0 @@
{-# LANGUAGE DeriveFunctor, DeriveGeneric #-}
module IHaskell.Display.Diagrams.ImgSize where
import GHC.Generics (Generic)
import Diagrams.Prelude (SizeSpec, mkSizeSpec2D, V2)
data ManuallySized a = ManuallySized
{ contentToDisplay :: a
, imgManualWidth :: Double
, imgManualHeight :: Double
} deriving (Show, Functor, Generic)
class ManuallySizeable a where
withSizeSpec :: SizeSpec V2 Double -> a -> ManuallySized a
withImgWidth :: ManuallySizeable a => Int -> a -> ManuallySized a
withImgWidth imgWidth = withSizeSpec $ mkSizeSpec2D (Just $ fromIntegral imgWidth)
Nothing
withImgHeight :: ManuallySizeable a => Int -> a -> ManuallySized a
withImgHeight imgHeight = withSizeSpec $ mkSizeSpec2D Nothing
(Just $ fromIntegral imgHeight)

View File

@ -1,3 +1,2 @@
import Distribution.Simple
import Distribution.Simple
main = defaultMain

File diff suppressed because one or more lines are too long

View File

@ -1,19 +1,19 @@
-- The name of the package.
name: ihaskell-diagrams
-- The package version. See the Haskell package versioning policy (PVP)
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- http://www.haskell.org/haskellwiki/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.4.0.0
version: 0.2.1.0
-- A short (one-line) description of the package.
synopsis: IHaskell display instances for diagram types
-- A longer description of the package.
-- description:
-- description:
-- URL for the project homepage or repository.
homepage: http://www.github.com/gibiansky/ihaskell
@ -27,51 +27,52 @@ license-file: LICENSE
-- The package author(s).
author: Andrew Gibiansky
-- An email address to which users can send suggestions, bug reports, and
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: andrew.gibiansky@gmail.com
-- A copyright notice.
-- copyright:
-- copyright:
category: Development
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- Extra files to be distributed with the package, such as examples or a
-- README.
-- extra-source-files:
-- extra-source-files:
-- Constraint on the version of Cabal needed to build this package.
cabal-version: 1.16
cabal-version: >=1.16
library
-- Modules exported by the library.
exposed-modules: IHaskell.Display.Diagrams
-- Modules included in this library but not exported.
other-modules: IHaskell.Display.Diagrams.Animation
IHaskell.Display.Diagrams.ImgSize
-- Language extensions.
default-extensions: DoAndIfThenElse
OverloadedStrings
-- Other library packages from which modules are imported.
build-depends: base >=4.9 && <5,
text,
build-depends: base >=4.6 && <4.9,
classy-prelude >=0.10.5,
bytestring,
directory,
temporary,
filepath,
-- Use diagrams wrapper package to ensure all same versions of subpackages
diagrams >= 1.3,
diagrams-lib,
diagrams-cairo,
ihaskell >= 0.11,
ihaskell >= 0.5,
-- The active package, used to represent animations
active >= 0.2
-- Directories containing source files.
-- hs-source-dirs:
-- hs-source-dirs:
-- Base language which the package is written in.
default-language: Haskell2010

File diff suppressed because one or more lines are too long

View File

@ -1,79 +0,0 @@
{-
(This paragraph was made obsolete by PR #1362 which unified the 3 types
of plot. But it's still useful commentary.)
There are 3 types of plots to consider in haskell-gnuplot: Plot, Frame and Multiplot.
Plot types are the actual plots, whereas Frame types are plots with additional options
e.g. custom axes tics, graph title etc.. Multiplots are collections of 2D and/or 3D plots.
We have to create instances of IHaskellDisplay for all of these types.
Note: To stop gnuplot from printing the filepath ontop of the canvas, you have to set
the gnuplot option "key" to "noautotitle".
Code: Graphics.Gnuplot.Frame.cons (Graphics.Gnuplot.Frame.OptionSet.add
(Graphics.Gnuplot.Frame.Option.key "")
["noautotitle"] $ ...)
-}
module IHaskell.Display.Gnuplot where
import qualified Graphics.Gnuplot.Plot as P
import qualified Graphics.Gnuplot.Frame as F
import qualified Graphics.Gnuplot.MultiPlot as M
import qualified Graphics.Gnuplot.Terminal.PNG as Pn
import qualified Graphics.Gnuplot.Terminal.SVG as Sv
import qualified Graphics.Gnuplot.Display as D
import qualified Graphics.Gnuplot.Graph as G
import qualified Data.ByteString.Char8 as Char
import qualified Data.ByteString as BS
import qualified Data.Text.Encoding as T.Encoding
import System.IO.Temp
import System.FilePath ((</>))
import Graphics.Gnuplot.Advanced (plot)
import IHaskell.Display
-- Plot-types
instance G.C graph => IHaskellDisplay (P.T graph) where
display = graphDataDisplayBoth
-- Frame-types
instance G.C graph => IHaskellDisplay (F.T graph) where
display = graphDataDisplayBoth
-- Type: Multiplot
instance IHaskellDisplay M.T where
display = graphDataDisplayBoth
-- Width and height
w = 300
h = 300
graphDataPNG :: D.C gfx => gfx -> IO DisplayData
graphDataPNG graph = do
-- We should not have to round-trip this ByteString to a temp file.
-- https://github.com/IHaskell/IHaskell/issues/1248
withSystemTempDirectory "ihaskell-gnuplot" $ \tmpdir -> do
let path = tmpdir </> "ihaskell-gnuplot.png"
-- Write the image.
plot (Pn.cons path) graph
-- Read back, and convert to base64.
imgData <- Char.readFile path
return $ png w h $ base64 imgData
graphDataSVG :: D.C gfx => gfx -> IO DisplayData
graphDataSVG graph = do
withSystemTempDirectory "ihaskell-gnuplot" $ \tmpdir -> do
let path = tmpdir </> "ihaskell-gnuplot.svg"
-- Write the image.
plot (Sv.cons path) graph
-- Read back
imgData <- BS.readFile path
return $ svg $ T.Encoding.decodeUtf8 imgData
graphDataDisplayBoth :: D.C gfx => gfx -> IO Display
graphDataDisplayBoth fig = do
pngDisp <- graphDataPNG fig
svgDisp <- graphDataSVG fig
return $ Display [pngDisp, svgDisp]

View File

@ -1,3 +0,0 @@
import Distribution.Simple
main = defaultMain

View File

@ -1,118 +0,0 @@
-- | A module to help displaying information using the amazing Graphviz
-- (https://www.graphviz.org/) graph layouts.
--
-- You need to install and have graphviz tools available in your environment.
--
-- Currently only 'dot' is provided as a proof-of-concept. This module may be
-- split in two (a package as an helper to Graphviz command line and this
-- package to provide 'IHaskellDisplay' instances.
--
-- Minimal notebook example:
--
-- @ import IHaskell.Display.Graphviz @
-- @ dot "digraph { l -> o; o -> v; v -> e; h -> a ; a -> s; s -> k ; k -> e ; e -> l ; l -> l}" @
module IHaskell.Display.Graphviz (
dot
, neato
, fdp
, sfdp
, circo
, twopi
, nop
, nop2
, osage
, patchwork
, Graphviz(..)
) where
import System.Process (readProcess)
import qualified Data.Text as T
import IHaskell.Display
-- | The body of a Graphviz program.
--
-- e.g. @ graph { a -- b } @
type GraphvizProgramBody = String
-- | Main Graphviz object.
data Graphviz
= Dot !GraphvizProgramBody
-- ^ A Graphviz plotted using [Dot](https://graphviz.org/docs/layouts/dot/)
| Neato !GraphvizProgramBody
-- ^ A Graphviz plotted using [Neato](https://graphviz.org/docs/layouts/neato/)
| Fdp !GraphvizProgramBody
-- ^ A Graphviz plotted using [Fdp](https://graphviz.org/docs/layouts/fdp/)
| Sfdp !GraphvizProgramBody
-- ^ A Graphviz plotted using [Sfdp](https://graphviz.org/docs/layouts/sfdp/)
| Circo !GraphvizProgramBody
-- ^ A Graphviz plotted using [Circo](https://graphviz.org/docs/layouts/circo/)
| Twopi !GraphvizProgramBody
-- ^ A Graphviz plotted using [Twopi](https://graphviz.org/docs/layouts/twopi/)
| Nop !GraphvizProgramBody
-- ^ A Graphviz plotted using [Nop](https://graphviz.org/docs/layouts/nop/)
| Nop2 !GraphvizProgramBody
-- ^ A Graphviz plotted using [Nop2](https://graphviz.org/docs/layouts/nop2/)
| Osage !GraphvizProgramBody
-- ^ A Graphviz plotted using [Osage](https://graphviz.org/docs/layouts/osage/)
| Patchwork !GraphvizProgramBody
-- ^ A Graphviz plotted using [Patchwork](https://graphviz.org/docs/layouts/patchwork/)
-- | Create a 'Graphviz' using 'dot'.
dot :: GraphvizProgramBody -> Graphviz
dot = Dot
-- | Create a 'Graphviz' using 'neato'.
neato :: GraphvizProgramBody -> Graphviz
neato = Neato
-- | Create a 'Graphviz' using 'fdp'.
fdp :: GraphvizProgramBody -> Graphviz
fdp = Fdp
-- | Create a 'Graphviz' using 'sfdp'.
sfdp :: GraphvizProgramBody -> Graphviz
sfdp = Sfdp
-- | Create a 'Graphviz' using 'circo'.
circo :: GraphvizProgramBody -> Graphviz
circo = Circo
-- | Create a 'Graphviz' using 'twopi'.
twopi :: GraphvizProgramBody -> Graphviz
twopi = Twopi
-- | Create a 'Graphviz' using 'nop'.
nop :: GraphvizProgramBody -> Graphviz
nop = Nop
-- | Create a 'Graphviz' using 'nop2'.
nop2 :: GraphvizProgramBody -> Graphviz
nop2 = Nop2
-- | Create a 'Graphviz' using 'osage'.
osage :: GraphvizProgramBody -> Graphviz
osage = Osage
-- | Create a 'Graphviz' using 'patchwork'.
patchwork :: GraphvizProgramBody -> Graphviz
patchwork = Patchwork
instance IHaskellDisplay Graphviz where
display fig = do
svgDisp <- graphDataSVG fig
return $ Display [svgDisp]
graphDataSVG :: Graphviz -> IO DisplayData
graphDataSVG prog = svg . T.pack <$> case prog of
Dot dotBody -> readProcess "dot" ["-Tsvg"] dotBody
Neato dotBody -> readProcess "neato" ["-Tsvg"] dotBody
Fdp dotBody -> readProcess "fdp" ["-Tsvg"] dotBody
Sfdp dotBody -> readProcess "sfdp" ["-Tsvg"] dotBody
Circo dotBody -> readProcess "circo" ["-Tsvg"] dotBody
Twopi dotBody -> readProcess "twopi" ["-Tsvg"] dotBody
Nop dotBody -> readProcess "nop" ["-Tsvg"] dotBody
Nop2 dotBody -> readProcess "nop2" ["-Tsvg"] dotBody
Osage dotBody -> readProcess "osage" ["-Tsvg"] dotBody
Patchwork dotBody -> readProcess "patchwork" ["-Tsvg"] dotBody

View File

@ -1,3 +0,0 @@
import Distribution.Simple
main = defaultMain

View File

@ -1,3 +1,2 @@
import Distribution.Simple
import Distribution.Simple
main = defaultMain

View File

@ -1,7 +1,7 @@
name: ihaskell-hatex
version: 0.2.1.1
version: 0.2.0.0
synopsis: IHaskell display instances for hatex
-- description:
-- description:
homepage: http://www.github.com/gibiansky/IHaskell
license: MIT
license-file: LICENSE
@ -9,14 +9,14 @@ author: Adam Vogt
maintainer: andrew.gibiansky@gmail.com
category: Development
build-type: Simple
cabal-version: 1.16
cabal-version: >=1.16
library
exposed-modules: IHaskell.Display.Hatex
build-depends: base >=4.9 && <5,
build-depends: base >=4.6 && <4.9,
text,
HaTeX >= 3.9,
ihaskell >= 0.5
default-language: Haskell2010

View File

@ -1,36 +1,94 @@
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
{-# LANGUAGE NoImplicitPrelude, TypeSynonymInstances, FlexibleInstances #-}
module IHaskell.Display.Juicypixels (module IHaskell.Display, module Codec.Picture) where
import qualified Codec.Picture as P
import Codec.Picture (Image(..))
import Codec.Picture.Png (PngSavable, encodePng)
import IHaskell.Display (IHaskellDisplay, Display(..), display, png, base64)
import Data.ByteString.Lazy (ByteString, toStrict)
import Codec.Picture
import ClassyPrelude
import IHaskell.Display
import System.Directory
import System.IO.Unsafe
instance IHaskellDisplay (Image P.Pixel8) where
display = return . format
-- instances
instance IHaskellDisplay DynamicImage where
display = displayImageAsJpg
instance IHaskellDisplay (Image P.Pixel16) where
display = return . format
instance IHaskellDisplay (Image Pixel8) where
display = displayImageAsJpg . ImageY8
instance IHaskellDisplay (Image P.PixelYA8) where
display = return . format
instance IHaskellDisplay (Image Pixel16) where
display = displayImageAsJpg . ImageY16
instance IHaskellDisplay (Image P.PixelYA16) where
display = return . format
instance IHaskellDisplay (Image PixelF) where
display = displayImageAsJpg . ImageYF
instance IHaskellDisplay (Image P.PixelRGB8) where
display = return . format
instance IHaskellDisplay (Image PixelYA8) where
display = displayImageAsJpg . ImageYA8
instance IHaskellDisplay (Image P.PixelRGB16) where
display = return . format
instance IHaskellDisplay (Image PixelYA16) where
display = displayImageAsJpg . ImageYA16
instance IHaskellDisplay (Image P.PixelRGBA8) where
display = return . format
instance IHaskellDisplay (Image PixelRGB8) where
display = displayImageAsJpg . ImageRGB8
instance IHaskellDisplay (Image P.PixelRGBA16) where
display = return . format
instance IHaskellDisplay (Image PixelRGB16) where
display = displayImageAsJpg . ImageRGB16
format :: PngSavable a => Image a -> Display
format im@(Image w h _) = Display [png w h . base64 . toStrict . encodePng $ im]
instance IHaskellDisplay (Image PixelRGBF) where
display = displayImageAsJpg . ImageRGBF
instance IHaskellDisplay (Image PixelRGBA8) where
display = displayImageAsJpg . ImageRGBA8
instance IHaskellDisplay (Image PixelRGBA16) where
display = displayImageAsJpg . ImageRGBA16
instance IHaskellDisplay (Image PixelYCbCr8) where
display = displayImageAsJpg . ImageYCbCr8
instance IHaskellDisplay (Image PixelCMYK8) where
display = displayImageAsJpg . ImageCMYK8
instance IHaskellDisplay (Image PixelCMYK16) where
display = displayImageAsJpg . ImageCMYK16
-- main rendering function
displayImageAsJpg :: DynamicImage -> IO Display
displayImageAsJpg renderable = do
switchToTmpDir
let filename = ".ihaskell.juicypixels.jpg"
-- Write the image
saveJpgImage 95 filename renderable
-- Convert to base64.
imgData <- readFile filename
return $ Display [jpg (imWidth renderable) (imHeight renderable) $ base64 imgData]
-- The type DynamicImage does not have a function to extract width and height
imWidth :: DynamicImage -> Int
imWidth img = w
where
(w, h) = imWidthHeight img
imHeight :: DynamicImage -> Int
imHeight img = h
where
(w, h) = imWidthHeight img
-- Helper functions to pattern match on the DynamicImage Constructors
imWidthHeight :: DynamicImage -> (Int, Int)
imWidthHeight (ImageY8 im) = imWH im
imWidthHeight (ImageY16 im) = imWH im
imWidthHeight (ImageYF im) = imWH im
imWidthHeight (ImageYA8 im) = imWH im
imWidthHeight (ImageYA16 im) = imWH im
imWidthHeight (ImageRGB8 im) = imWH im
imWidthHeight (ImageRGB16 im) = imWH im
imWidthHeight (ImageRGBF im) = imWH im
imWidthHeight (ImageRGBA8 im) = imWH im
imWidthHeight (ImageRGBA16 im) = imWH im
imWidthHeight (ImageYCbCr8 im) = imWH im
imWidthHeight (ImageCMYK8 im) = imWH im
imWidthHeight (ImageCMYK16 im) = imWH im
imWH :: (Image a) -> (Int, Int)
imWH im = (imageWidth im, imageHeight im)

View File

@ -1,3 +1,2 @@
import Distribution.Simple
import Distribution.Simple
main = defaultMain

View File

@ -1,23 +1,23 @@
-- The name of the package.
name: ihaskell-juicypixels
-- The package version. See the Haskell package versioning policy (PVP)
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- http://www.haskell.org/haskellwiki/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 1.1.0.1
version: 0.2.0.0
-- A short (one-line) description of the package.
synopsis: IHaskell - IHaskellDisplay instances of the image types of the JuicyPixels package.
-- A longer description of the package.
description: IHaskellDisplay instances of the image types of the JuicyPixels package. They are displayed as .JPG images.
DynamicImages and Images of types PixelRGBA16, PixelRGBA8, PixelRGB16, PixelRGB8,
DynamicImages and Images of types PixelRGBA16, PixelRGBA8, PixelRGB16, PixelRGB8,
PixelYA16, PixelYA8, Pixel16, Pixel8, PixelCMYK16, PixelCMYK8, PixelF, Pixel32
are supported.
The module IHaskell.Juicypixels re-exports the modules IHaskell.Display and Codec.Picture.
are supported.
The module IHaskell.Juicypixels re-exports the modules IHaskell.Display and Codec.Picture.
-- URL for the project homepage or repository.
homepage: http://www.github.com/gibiansky/ihaskell
@ -29,45 +29,48 @@ license: MIT
license-file: LICENSE
-- The package author(s).
author: Roland Senn,
Will Yager
author: Roland Senn
-- An email address to which users can send suggestions, bug reports, and
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: rsx@bluewin.ch,
will.yager@gmail.com
maintainer: rsx@bluewin.ch
-- A copyright notice.
-- copyright:
-- copyright:
category: Development
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- Extra files to be distributed with the package, such as examples or a
-- README.
-- extra-source-files:
-- extra-source-files:
-- Constraint on the version of Cabal needed to build this package.
cabal-version: 1.16
cabal-version: >=1.16
library
-- Modules exported by the library.
exposed-modules: IHaskell.Display.Juicypixels
-- Modules included in this library but not exported.
-- other-modules:
-- other-modules:
-- Language extensions.
default-extensions: DoAndIfThenElse
OverloadedStrings
-- Other library packages from which modules are imported.
build-depends: base >=4.9 && <5,
build-depends: base >=4.6 && <4.9,
classy-prelude >=0.10.5,
bytestring,
directory,
JuicyPixels >= 3.1.3,
ihaskell >= 0.6.2
ihaskell >= 0.5
-- Directories containing source files.
-- hs-source-dirs:
-- hs-source-dirs:
-- Base language which the package is written in.
default-language: Haskell2010

View File

@ -1,57 +1,42 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"hidden": false
},
"source": [
"# Notebook test\n",
"\n",
"This IHaskell noteook should just test, whether IHaskell and JuicyPixels are properly installed and working.\n",
"\n",
"Just click in the box below and click on the \"Run\" command in the above menu. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"hidden": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}\n",
"\n",
"import IHaskell.Display.Juicypixels\n",
"import Codec.Picture\n",
" \n",
"myImage = generateImage pixelRenderer 250 300\n",
" where pixelRenderer x y = PixelRGB8 (fromIntegral x) (fromIntegral y) 128\n",
" \n",
"myImage "
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Haskell",
"language": "haskell",
"name": "haskell"
},
"language_info": {
"codemirror_mode": "ihaskell",
"file_extension": ".hs",
"mimetype": "text/x-haskell",
"name": "haskell",
"pygments_lexer": "Haskell",
"version": "8.10.4"
}
"language": "haskell",
"name": "",
"signature": "sha256:b9b36c162e37a16b252cd1f06e4c2991e0a80710e7fc184be82dd6b18e6dcf01"
},
"nbformat": 4,
"nbformat_minor": 4
}
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "code",
"collapsed": false,
"input": [
"{-# LANGUAGE NoImplicitPrelude, TypeSynonymInstances, FlexibleInstances #-}\n",
"\n",
"import IHaskell.Display.Juicypixels\n",
" \n",
"myImage = generateImage pixelRenderer 250 300\n",
" where pixelRenderer x y = PixelRGB8 (fromIntegral x) (fromIntegral y) 128\n",
" \n",
"myImage "
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 8
},
{
"cell_type": "code",
"collapsed": false,
"input": [],
"language": "python",
"metadata": {},
"outputs": []
}
],
"metadata": {}
}
]
}

View File

@ -2,23 +2,21 @@
module IHaskell.Display.Magic () where
import IHaskell.Display
import Magic
import qualified Data.ByteString as B
import qualified Data.ByteString.Unsafe as B
import qualified Data.ByteString.Base64 as Base64
import qualified Data.ByteString.Char8 as Char
import Data.ByteString.UTF8
import qualified Data.ByteString.UTF8 as B
import qualified Data.ByteString.Unsafe as B
import Text.Read
import Data.Char
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Text.Read
import Magic
import IHaskell.CSS (ihaskellCSS)
import IHaskell.Display
import IHaskell.IPython.Types (MimeType(MimeSvg))
import Data.ByteString.UTF8
instance IHaskellDisplay T.Text where
display = display . T.encodeUtf8
@ -37,7 +35,7 @@ withClass :: MagicClass -> B.ByteString -> DisplayData
withClass SVG = DisplayData MimeSvg . T.decodeUtf8
withClass (PNG w h) = png w h . T.decodeUtf8 . Base64.encode
withClass JPG = jpg 400 300 . T.decodeUtf8 . Base64.encode
withClass HTML = html' (Just ihaskellCSS) . B.toString
withClass HTML = html . B.toString
withClass LaTeX = latex . B.toString
withClass _ = plain . B.toString

View File

@ -10,14 +10,6 @@ cd ihaskell-magic
cabal install
```
On OSX:
```bash
brew install libmagic
brew link libmagic
stack install ihaskell-magic --extra-lib-dirs=/usr/local/lib --extra-include-dirs=/usr/local/include
```
The instances provided allow displaying images and text with markup using just one line:
```haskell
import qualified Data.ByteString as B

View File

@ -1,3 +1,2 @@
import Distribution.Simple
import Distribution.Simple
main = defaultMain

View File

@ -1,22 +1,22 @@
-- Initial ihaskell-display.cabal generated by cabal init. For further
-- Initial ihaskell-display.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
-- The name of the package.
name: ihaskell-magic
-- The package version. See the Haskell package versioning policy (PVP)
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- http://www.haskell.org/haskellwiki/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.3.0.1
version: 0.2.0.0
-- A short (one-line) description of the package.
synopsis: IHaskell display instances for bytestrings
-- A longer description of the package.
-- description:
-- description:
-- URL for the project homepage or repository.
homepage: http://www.github.com/gibiansky/IHaskell
@ -30,34 +30,39 @@ license-file: LICENSE
-- The package author(s).
author: Adam Vogt
-- An email address to which users can send suggestions, bug reports, and
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: andrew.gibiansky@gmail.com
-- A copyright notice.
-- copyright:
-- copyright:
category: Development
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- Extra files to be distributed with the package, such as examples or a
-- README.
-- extra-source-files:
-- extra-source-files:
-- Constraint on the version of Cabal needed to build this package.
cabal-version: 1.16
cabal-version: >=1.16
library
-- Modules exported by the library.
exposed-modules: IHaskell.Display.Magic
-- Modules included in this library but not exported.
-- other-modules:
-- other-modules:
-- Language extensions.
default-extensions: DoAndIfThenElse
OverloadedStrings
-- Other library packages from which modules are imported.
build-depends: base >=4.9 && <5,
build-depends: base >=4.6 && <4.9,
classy-prelude >=0.6,
magic >= 1.0.8,
text,
bytestring,
@ -65,10 +70,10 @@ library
base64-bytestring,
ipython-kernel,
ihaskell >= 0.5
-- Directories containing source files.
-- hs-source-dirs:
-- hs-source-dirs:
-- Base language which the package is written in.
default-language: Haskell2010

View File

@ -1,44 +0,0 @@
module IHaskell.Display.Matplotlib where
import qualified Data.ByteString.Char8 as Char
import qualified Data.ByteString.UTF8 as BSU
import qualified Data.Text.Encoding as T.Encoding
import System.IO.Temp
import System.FilePath ((</>))
import Graphics.Matplotlib
import IHaskell.Display
instance IHaskellDisplay Matplotlib where
display = graphDataDisplayBoth
-- Width and height
w, h :: Int
w = 300
h = 300
graphDataPNG :: Matplotlib -> IO DisplayData
graphDataPNG m = do
withSystemTempDirectory "ihaskell-matplotlib" $ \tmpdir -> do
let path = tmpdir </> "ihaskell-matplotlib.png"
-- Write the image.
res <- file path m
case res of
Left _ -> error "Matplotlib could not generate an immage"
Right _ -> do
-- Read back, and convert to base64.
imgData <- Char.readFile path
return $ png w h $ base64 imgData
graphDataSVG :: Matplotlib -> IO DisplayData
graphDataSVG m = do
res <- toSvg m
case res of
Left s -> error s
Right f -> return $ svg $ T.Encoding.decodeUtf8 $ BSU.fromString f
graphDataDisplayBoth :: Matplotlib -> IO Display
graphDataDisplayBoth fig = do
pngDisp <- graphDataPNG fig
svgDisp <- graphDataSVG fig
return $ Display [pngDisp, svgDisp]

View File

@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2025 Andrea Rossato andrea.rossato@unitn.it
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File diff suppressed because one or more lines are too long

View File

@ -1,3 +0,0 @@
import Distribution.Simple
main = defaultMain

View File

@ -1,69 +0,0 @@
-- The name of the package.
name: ihaskell-matplotlib
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- http://www.haskell.org/haskellwiki/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.1.0.1
-- A short (one-line) description of the package.
synopsis: IHaskell display instance for matplotlib (from matplotlib package)
-- A longer description of the package.
-- description:
-- URL for the project homepage or repository.
homepage: http://www.github.com/gibiansky/ihaskell
-- The license under which the package is released.
license: MIT
-- The file containing the license text.
license-file: LICENSE
-- The package author(s).
author: Andrea Rossatoe <andrea.rossato@unitn.it>
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: Andrea Rossato <andrea.rossato@unitn.it>,
Andrew Gibiansky <andrew.gibiansky@gmail.com>
-- A copyright notice.
-- copyright:
category: Development
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- README.
-- extra-source-files:
-- Constraint on the version of Cabal needed to build this package.
cabal-version: 1.16
library
-- Modules exported by the library.
exposed-modules: IHaskell.Display.Matplotlib
ghc-options: -O -Wall -Wno-orphans
-- Other library packages from which modules are imported.
build-depends: base >=4.9 && <5,
bytestring,
utf8-string,
text,
temporary,
filepath,
matplotlib,
ihaskell >= 0.6.2
-- Directories containing source files.
-- hs-source-dirs:
-- Base language which the package is written in.
default-language: Haskell2010

View File

@ -0,0 +1,49 @@
{-# LANGUAGE NoImplicitPrelude, TypeSynonymInstances, QuasiQuotes, FlexibleInstances, OverloadedStrings #-}
module IHaskell.Display.Parsec () where
import ClassyPrelude hiding (fromList)
import System.Random
import Data.String.Here
import Data.HashMap.Strict as Map
import Text.Parsec (parse, sourceLine, sourceColumn)
import Text.Parsec.String (Parser)
import Text.Parsec.Error (errorPos, ParseError)
import Data.Aeson
import IHaskell.Display
instance Show a => IHaskellDisplay (Parser a) where
display renderable = return $ many [Display [javascript js], Display [html dom]]
where
dom = [hereFile|widget.html|]
js = [hereFile|widget.js|]
-- | Text to parse.
data ParseText = ParseText String
instance FromJSON ParseText where
parseJSON (Object v) = ParseText <$> v .: "text"
parseJSON _ = fail "Expecting object"
-- | Output of parsing.
instance Show a => ToJSON (Either ParseError a) where
toJSON (Left err) = object
[ "status" .= ("error" :: String)
, "line" .= sourceLine (errorPos err)
, "col" .= sourceColumn (errorPos err)
, "msg" .= show err
]
toJSON (Right result) = object ["status" .= ("success" :: String), "result" .= show result]
instance Show a => IHaskellWidget (Parser a) where
-- Name for this widget.
targetName _ = "parsec"
-- When we rece
comm widget (Object dict) publisher = do
let key = "text" :: Text
Just (String text) = Map.lookup key dict
result = parse widget "<interactive>" $ unpack text
publisher $ toJSON result

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2013 Lucas DiCioccio
Copyright (c) 2013 Andrew Gibiansky
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View File

@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain

View File

@ -1,19 +1,19 @@
-- The name of the package.
name: ihaskell-gnuplot
name: ihaskell-parsec
-- The package version. See the Haskell package versioning policy (PVP)
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- http://www.haskell.org/haskellwiki/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.1.0.1
version: 0.2.1.0
-- A short (one-line) description of the package.
synopsis: IHaskell display instance for Gnuplot (from gnuplot package)
synopsis: IHaskell display instances for Parsec
-- A longer description of the package.
-- description:
-- description:
-- URL for the project homepage or repository.
homepage: http://www.github.com/gibiansky/ihaskell
@ -25,45 +25,50 @@ license: MIT
license-file: LICENSE
-- The package author(s).
author: Doro Rose <dororose@hotmail.com>
author: Andrew Gibiansky
-- An email address to which users can send suggestions, bug reports, and
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: Doro Rose <dororose@hotmail.com>,
Andrew Gibiansky <andrew.gibiansky@gmail.com>
maintainer: andrew.gibiansky@gmail.com
-- A copyright notice.
-- copyright:
-- copyright:
category: Development
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
extra-source-files: widget.html, widget.js
-- Extra files to be distributed with the package, such as examples or a
-- README.
-- extra-source-files:
-- extra-source-files:
-- Constraint on the version of Cabal needed to build this package.
cabal-version: 1.16
cabal-version: >=1.16
library
-- Modules exported by the library.
exposed-modules: IHaskell.Display.Gnuplot
exposed-modules: IHaskell.Display.Parsec
-- Modules included in this library but not exported.
-- other-modules:
-- other-modules:
-- Language extensions.
default-extensions: DoAndIfThenElse
OverloadedStrings
-- Other library packages from which modules are imported.
build-depends: base >=4.9 && <5,
bytestring,
text,
temporary,
filepath,
gnuplot >= 0.5.4,
ihaskell >= 0.6.2
build-depends: base >=4.6 && <4.9,
aeson >=0.7 && <0.9,
unordered-containers,
classy-prelude,
random >= 1,
parsec,
here,
ihaskell >= 0.5
-- Directories containing source files.
-- hs-source-dirs:
-- hs-source-dirs:
-- Base language which the package is written in.
default-language: Haskell2010

View File

@ -0,0 +1,6 @@
<!-- CodeMirror component -->
<link rel="stylesheet" href="/static/components/codemirror/addon/lint/lint.css">
<!-- Parsec widget DOM -->
<form><textarea id="parsec-editor">Insert parser text here...</textarea></form>
<pre id="parsec-output"></pre>

View File

@ -0,0 +1,288 @@
// Only load this script once.
var kernel = IPython.notebook.kernel;
var initialized = kernel !== undefined && kernel != null;
console.log("Initialized", initialized);
if (initialized && window.parsecWidgetRegistered === undefined) {
// Do not load this script again.
window.parsecWidgetRegistered = true;
// Codemirror lint.js
// Must be included here, otherwise linting cannot happen the first time the widget is loaded.
(function() {
"use strict";
var GUTTER_ID = "CodeMirror-lint-markers";
var SEVERITIES = /^(?:error|warning)$/;
function showTooltip(e, content) {
var tt = document.createElement("div");
tt.className = "CodeMirror-lint-tooltip";
tt.appendChild(content.cloneNode(true));
document.body.appendChild(tt);
function position(e) {
if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position);
tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px";
tt.style.left = (e.clientX + 5) + "px";
}
CodeMirror.on(document, "mousemove", position);
position(e);
if (tt.style.opacity != null) tt.style.opacity = 1;
return tt;
}
function rm(elt) {
if (elt.parentNode) elt.parentNode.removeChild(elt);
}
function hideTooltip(tt) {
if (!tt.parentNode) return;
if (tt.style.opacity == null) rm(tt);
tt.style.opacity = 0;
setTimeout(function() { rm(tt); }, 600);
}
function showTooltipFor(e, content, node) {
var tooltip = showTooltip(e, content);
function hide() {
CodeMirror.off(node, "mouseout", hide);
if (tooltip) { hideTooltip(tooltip); tooltip = null; }
}
var poll = setInterval(function() {
if (tooltip) for (var n = node;; n = n.parentNode) {
if (n == document.body) return;
if (!n) { hide(); break; }
}
if (!tooltip) return clearInterval(poll);
}, 400);
CodeMirror.on(node, "mouseout", hide);
}
function LintState(cm, options, hasGutter) {
this.marked = [];
this.options = options;
this.timeout = null;
this.hasGutter = hasGutter;
this.onMouseOver = function(e) { onMouseOver(cm, e); };
}
function parseOptions(cm, options) {
if (options instanceof Function) return {getAnnotations: options};
if (!options || options === true) options = {};
if (!options.getAnnotations) options.getAnnotations = cm.getHelper(CodeMirror.Pos(0, 0), "lint");
if (!options.getAnnotations) throw new Error("Required option 'getAnnotations' missing (lint addon)");
return options;
}
function clearMarks(cm) {
var state = cm.state.lint;
if (state.hasGutter) cm.clearGutter(GUTTER_ID);
for (var i = 0; i < state.marked.length; ++i)
state.marked[i].clear();
state.marked.length = 0;
}
function makeMarker(labels, severity, multiple, tooltips) {
var marker = document.createElement("div"), inner = marker;
marker.className = "CodeMirror-lint-marker-" + severity;
if (multiple) {
inner = marker.appendChild(document.createElement("div"));
inner.className = "CodeMirror-lint-marker-multiple";
}
if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) {
showTooltipFor(e, labels, inner);
});
return marker;
}
function getMaxSeverity(a, b) {
if (a == "error") return a;
else return b;
}
function groupByLine(annotations) {
var lines = [];
for (var i = 0; i < annotations.length; ++i) {
var ann = annotations[i], line = ann.from.line;
(lines[line] || (lines[line] = [])).push(ann);
}
return lines;
}
function annotationTooltip(ann) {
var severity = ann.severity;
if (!SEVERITIES.test(severity)) severity = "error";
var tip = document.createElement("div");
tip.className = "CodeMirror-lint-message-" + severity;
tip.appendChild(document.createTextNode(ann.message));
return tip;
}
function startLinting(cm) {
var state = cm.state.lint, options = state.options;
if (options.async)
options.getAnnotations(cm, updateLinting, options);
else
updateLinting(cm, options.getAnnotations(cm.getValue(), options.options));
}
function updateLinting(cm, annotationsNotSorted) {
clearMarks(cm);
var state = cm.state.lint, options = state.options;
var annotations = groupByLine(annotationsNotSorted);
for (var line = 0; line < annotations.length; ++line) {
var anns = annotations[line];
if (!anns) continue;
var maxSeverity = null;
var tipLabel = state.hasGutter && document.createDocumentFragment();
for (var i = 0; i < anns.length; ++i) {
var ann = anns[i];
var severity = ann.severity;
if (!SEVERITIES.test(severity)) severity = "error";
maxSeverity = getMaxSeverity(maxSeverity, severity);
if (options.formatAnnotation) ann = options.formatAnnotation(ann);
if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann));
if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, {
className: "CodeMirror-lint-mark-" + severity,
__annotation: ann
}));
}
if (state.hasGutter)
cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1,
state.options.tooltips));
}
if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
}
function onChange(cm) {
var state = cm.state.lint;
clearTimeout(state.timeout);
state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
}
function popupSpanTooltip(ann, e) {
var target = e.target || e.srcElement;
showTooltipFor(e, annotationTooltip(ann), target);
}
// When the mouseover fires, the cursor might not actually be over
// the character itself yet. These pairs of x,y offsets are used to
// probe a few nearby points when no suitable marked range is found.
var nearby = [0, 0, 0, 5, 0, -5, 5, 0, -5, 0];
function onMouseOver(cm, e) {
if (!/\bCodeMirror-lint-mark-/.test((e.target || e.srcElement).className)) return;
for (var i = 0; i < nearby.length; i += 2) {
var spans = cm.findMarksAt(cm.coordsChar({left: e.clientX + nearby[i],
top: e.clientY + nearby[i + 1]}));
for (var j = 0; j < spans.length; ++j) {
var span = spans[j], ann = span.__annotation;
if (ann) return popupSpanTooltip(ann, e);
}
}
}
function optionHandler(cm, val, old) {
if (old && old != CodeMirror.Init) {
clearMarks(cm);
cm.off("change", onChange);
CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver);
delete cm.state.lint;
}
if (val) {
var gutters = cm.getOption("gutters"), hasLintGutter = false;
for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true;
var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter);
cm.on("change", onChange);
if (state.options.tooltips != false)
CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver);
startLinting(cm);
}
}
CodeMirror.defineOption("lintWith", false, optionHandler); // deprecated
CodeMirror.defineOption("lint", false, optionHandler); // deprecated
})();
var parsecWidgetCounter = 0;
// Register the comm target.
var ParsecWidget = function (comm) {
this.comm = comm;
this.comm.on_msg($.proxy(this.handler, this));
// Get the cell that was probably executed.
// The msg_id:cell mapping will make this possible without guessing.
this.cell = IPython.notebook.get_cell(IPython.notebook.get_selected_index()-1);
// Store this widget so we can use it from callbacks.
var widget = this;
// Editor options.
var options = {
lineNumbers: true,
// Show parsec errors as lint errors.
gutters: ["CodeMirror-lint-markers"],
lintWith: {
"getAnnotations": function(cm, update, opts) {
var errs = [];
if (widget.hasError) {
var col = widget.error["col"];
var line = widget.error["line"];
errs = [{
from: CodeMirror.Pos(line - 1, col - 1),
to: CodeMirror.Pos(line - 1, col),
message: widget.error["msg"],
severity: "error"
}];
}
update(cm, errs);
},
"async": true,
}
};
// Create the editor.
var out = this.cell.output_area.element;
this.textarea = out.find("#parsec-editor")[0];
this.output = out.find("#parsec-output")[0];
// Give the elements a different name.
this.textarea.id += parsecWidgetCounter;
this.output.id += parsecWidgetCounter;
parsecWidgetCounter++;
var editor = CodeMirror.fromTextArea(this.textarea, options);
var editor = editor;
// Update every key press.
editor.on("keyup", function() {
var text = editor.getDoc().getValue();
comm.send({"text": text});
});
};
ParsecWidget.prototype.handler = function(msg) {
var data = msg.content.data;
this.hasError = (data["status"] == "error");
console.log(this.hasError);
if (this.hasError) {
this.output.innerHTML = data["msg"];
this.error = data;
} else {
this.output.innerHTML = data["result"];
}
};
// Register this widget.
IPython.notebook.kernel.comm_manager.register_target('parsec', IPython.utils.always_new(ParsecWidget));
console.log("Registering Parsec widget.");
}

View File

@ -1,46 +1,46 @@
{-# LANGUAGE NoImplicitPrelude #-}
module IHaskell.Display.Plot where
import ClassyPrelude
import qualified Data.ByteString.Char8 as Char
import qualified Data.ByteString as BS
import qualified Data.Text.Encoding as T.Encoding
import Graphics.Rendering.Plot
import Control.Monad (void)
import Control.Applicative ((<$>))
import System.IO.Temp
import System.FilePath ((</>))
import IHaskell.Display
instance IHaskellDisplay (Figure a) where
display fig = do
let figure = void fig
let figure = fig >> return ()
pngDisp <- figureData figure PNG
svgDisp <- figureData figure SVG
return $ Display [pngDisp, svgDisp]
figureData :: Figure () -> OutputType -> IO DisplayData
figureData figure format = do
withSystemTempDirectory "ihaskell-plot" $ \tmpdir -> do
switchToTmpDir
-- Width and height
let size = 300
w = size
h = size
-- Width and height
let size = 300
w = size
h = size
let path = case format of
PNG -> tmpdir </> "ihaskell-plot.png"
SVG -> tmpdir </> "ihaslell-plot.svg"
_ -> error "Unreachable case"
-- Write the image.
let fname = ".ihaskell-plot." ++ extension format
writeFigure format fname (w, h) figure
-- Write the image.
writeFigure format path (w, h) figure
-- Read back, and convert to base64.
imgData <- readFile fname
let value =
case format of
PNG -> png w h $ base64 imgData
SVG -> svg $ Char.unpack imgData
_ -> error "Unsupported format for display"
case format of
PNG -> do
-- Read back, and convert to base64.
imgData <- Char.readFile path
pure $ png w h $ base64 imgData
SVG -> do
imgData <- BS.readFile path
pure $ svg $ T.Encoding.decodeUtf8 imgData
_ -> error "Unreachable case"
return value
where
extension SVG = "svg"
extension PNG = "png"
extension _ = ""

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,2 @@
import Distribution.Simple
import Distribution.Simple
main = defaultMain

View File

@ -1,19 +1,19 @@
-- The name of the package.
name: ihaskell-plot
-- The package version. See the Haskell package versioning policy (PVP)
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- http://www.haskell.org/haskellwiki/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.3.0.1
version: 0.1.0.0
-- A short (one-line) description of the package.
synopsis: IHaskell display instance for Plot (from plot package)
-- A longer description of the package.
-- description:
-- description:
-- URL for the project homepage or repository.
homepage: http://www.github.com/gibiansky/ihaskell
@ -27,44 +27,45 @@ license-file: LICENSE
-- The package author(s).
author: Sumit Sahrawat <sumit.sahrawat.apm13@itbhu.ac.in>
-- An email address to which users can send suggestions, bug reports, and
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: Sumit Sahrawat <sumit.sahrawat.apm13@itbhu.ac.in>,
Andrew Gibiansky <andrew.gibiansky@gmail.com>
-- A copyright notice.
-- copyright:
-- copyright:
category: Development
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- Extra files to be distributed with the package, such as examples or a
-- README.
-- extra-source-files:
-- extra-source-files:
-- Constraint on the version of Cabal needed to build this package.
cabal-version: 1.16
cabal-version: >=1.16
library
-- Modules exported by the library.
exposed-modules: IHaskell.Display.Plot
-- Modules included in this library but not exported.
-- other-modules:
-- other-modules:
-- Language extensions.
default-extensions: DoAndIfThenElse
OverloadedStrings
-- Other library packages from which modules are imported.
build-depends: base >=4.9 && <5,
build-depends: base >=4.6 && <4.9,
classy-prelude >= 0.10.5,
plot,
bytestring,
text,
temporary,
filepath,
hmatrix >= 0.10,
ihaskell >= 0.6.2
ihaskell >= 0.5
-- Directories containing source files.
-- hs-source-dirs:
-- hs-source-dirs:
-- Base language which the package is written in.
default-language: Haskell2010

View File

@ -1,3 +1,2 @@
import Distribution.Simple
import Distribution.Simple
main = defaultMain

View File

@ -1,5 +1,5 @@
name: ihaskell-rlangqq
version: 0.3.0.0
version: 0.2.1.1
synopsis: a rDisp quasiquote to show plots from Rlang-QQ in IHaskell
license: BSD3
license-file: LICENSE
@ -7,22 +7,22 @@ author: Adam Vogt <vogt.adam@gmail.com>
maintainer: Adam Vogt <vogt.adam@gmail.com>
category: Development
build-type: Simple
cabal-version: 1.16
cabal-version: >=1.10
library
exposed-modules: IHaskell.Display.Rlangqq
other-extensions: TupleSections, TemplateHaskell
build-depends: base <5,
Rlang-QQ >= 0.3,
directory >=1.2,
filepath >=1.3,
bytestring >=0.10,
base64-bytestring >=1.0,
ihaskell >= 0.6.2,
ihaskell-blaze >=0.3,
blaze-html >=0.6,
split >=0.2,
directory >=1.2 && <1.3,
filepath >=1.3 && <1.5,
bytestring >=0.10 && <0.11,
base64-bytestring >=1.0 && <1.1,
ihaskell >= 0.6.1,
ihaskell-blaze >=0.2 && <0.3,
blaze-html >=0.6 && <0.9,
split >=0.2 && <0.3,
stm -any,
xformat >=0.1,
xformat >=0.1 && <0.2,
template-haskell >= 2.8
default-language: Haskell2010

View File

@ -1,3 +1,2 @@
import Distribution.Simple
import Distribution.Simple
main = defaultMain

Some files were not shown because too many files have changed in this diff Show More