Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
gargantext-ihaskell
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
gargantext
gargantext-ihaskell
Commits
7ba7c4d1
Commit
7ba7c4d1
authored
Mar 21, 2015
by
Andrew Gibiansky
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #444 from gibiansky/enforce-proper-formatting
Enforce proper formatting
parents
be10d383
dadd074f
Changes
21
Show whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
1985 additions
and
1901 deletions
+1985
-1901
.gitignore
.gitignore
+3
-0
.travis.yml
.travis.yml
+7
-1
BrokenPackages.hs
src/IHaskell/BrokenPackages.hs
+17
-19
Convert.hs
src/IHaskell/Convert.hs
+21
-19
Args.hs
src/IHaskell/Convert/Args.hs
+67
-73
IpynbToLhs.hs
src/IHaskell/Convert/IpynbToLhs.hs
+29
-27
LhsToIpynb.hs
src/IHaskell/Convert/LhsToIpynb.hs
+67
-65
Display.hs
src/IHaskell/Display.hs
+75
-71
Completion.hs
src/IHaskell/Eval/Completion.hs
+190
-194
Evaluate.hs
src/IHaskell/Eval/Evaluate.hs
+652
-629
Hoogle.hs
src/IHaskell/Eval/Hoogle.hs
+114
-128
Info.hs
src/IHaskell/Eval/Info.hs
+10
-12
Lint.hs
src/IHaskell/Eval/Lint.hs
+104
-114
ParseShell.hs
src/IHaskell/Eval/ParseShell.hs
+23
-22
Parser.hs
src/IHaskell/Eval/Parser.hs
+127
-121
Util.hs
src/IHaskell/Eval/Util.hs
+172
-179
Flags.hs
src/IHaskell/Flags.hs
+20
-19
IPython.hs
src/IHaskell/IPython.hs
+31
-29
Types.hs
src/IHaskell/Types.hs
+97
-92
Main.hs
src/Main.hs
+85
-87
verify_formatting.py
verify_formatting.py
+74
-0
No files found.
.gitignore
View file @
7ba7c4d1
...
@@ -16,3 +16,6 @@ todo
...
@@ -16,3 +16,6 @@ todo
profile/profile.tar
profile/profile.tar
.cabal-sandbox
.cabal-sandbox
cabal.sandbox.config
cabal.sandbox.config
.tmp1
.tmp2
.tmp3
.travis.yml
View file @
7ba7c4d1
...
@@ -14,6 +14,7 @@ before_install:
...
@@ -14,6 +14,7 @@ before_install:
-
travis_retry sudo apt-get update
-
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 cabal-install-$CABALVER ghc-$GHCVER
# see note about happy/alex
-
travis_retry sudo apt-get install libmagic-dev
-
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
-
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 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
-
export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$HOME/.cabal/bin:$PATH
...
@@ -34,6 +35,8 @@ install:
...
@@ -34,6 +35,8 @@ install:
-
|
-
|
if [ ${GHCVER%.*} = "7.8" ]; then
if [ ${GHCVER%.*} = "7.8" ]; then
travis_retry cabal install arithmoi==0.4.* -fllvm
travis_retry cabal install arithmoi==0.4.* -fllvm
travis_retry git clone http://www.github.com/gibiansky/hindent
cd hindent && cabal install && cd ..
fi
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.
# 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.
...
@@ -47,7 +50,10 @@ script:
...
@@ -47,7 +50,10 @@ script:
# Build and run the test suite
# Build and run the test suite
-
travis_retry cabal configure --enable-tests
-
travis_retry cabal configure --enable-tests
-
travis_retry cabal test --show-details=always
-
travis_retry cabal test --show-details=always
-
|
if [ ${GHCVER%.*} = "7.8" ]; then
./verify_formatting.py
fi
-
cabal sdist
-
cabal sdist
# The following scriptlet checks that the resulting source distribution can be built & installed
# The following scriptlet checks that the resulting source distribution can be built & installed
...
...
src/IHaskell/BrokenPackages.hs
View file @
7ba7c4d1
{-# LANGUAGE OverloadedStrings, NoImplicitPrelude, FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings, NoImplicitPrelude, FlexibleContexts #-}
module
IHaskell.BrokenPackages
(
getBrokenPackages
)
where
module
IHaskell.BrokenPackages
(
getBrokenPackages
)
where
import
ClassyPrelude
hiding
((
<|>
))
import
ClassyPrelude
hiding
((
<|>
))
...
@@ -11,17 +12,13 @@ import Data.String.Utils (startswith)
...
@@ -11,17 +12,13 @@ import Data.String.Utils (startswith)
import
Shelly
import
Shelly
data
BrokenPackage
=
BrokenPackage
{
data
BrokenPackage
=
BrokenPackage
{
packageID
::
String
,
brokenDeps
::
[
String
]
}
packageID
::
String
,
brokenDeps
::
[
String
]
}
instance
Show
BrokenPackage
where
instance
Show
BrokenPackage
where
show
=
packageID
show
=
packageID
-- | Get a list of broken packages.
-- | Get a list of broken packages. This function internally shells out to `ghc-pkg`, and parses the
-- This function internally shells out to `ghc-pkg`, and parses the output
-- output in order to determine what packages are broken.
-- in order to determine what packages are broken.
getBrokenPackages
::
IO
[
String
]
getBrokenPackages
::
IO
[
String
]
getBrokenPackages
=
shelly
$
do
getBrokenPackages
=
shelly
$
do
silently
$
errExit
False
$
run
"ghc-pkg"
[
"check"
]
silently
$
errExit
False
$
run
"ghc-pkg"
[
"check"
]
...
@@ -32,7 +29,8 @@ getBrokenPackages = shelly $ do
...
@@ -32,7 +29,8 @@ getBrokenPackages = shelly $ do
startswith
" dependency"
str
startswith
" dependency"
str
ghcPkgOutput
=
unlines
.
filter
rightStart
.
lines
$
unpack
checkOut
ghcPkgOutput
=
unlines
.
filter
rightStart
.
lines
$
unpack
checkOut
return
$
case
parse
(
many
check
)
"ghc-pkg output"
ghcPkgOutput
of
return
$
case
parse
(
many
check
)
"ghc-pkg output"
ghcPkgOutput
of
Left
err
->
[]
Left
err
->
[]
Right
pkgs
->
map
show
pkgs
Right
pkgs
->
map
show
pkgs
...
...
src/IHaskell/Convert.hs
View file @
7ba7c4d1
-- | Description : mostly reversible conversion between ipynb and lhs
-- | Description : mostly reversible conversion between ipynb and lhs
module
IHaskell.Convert
(
convert
)
where
module
IHaskell.Convert
(
convert
)
where
import
Control.Monad.Identity
(
Identity
(
Identity
),
unless
,
when
)
import
Control.Monad.Identity
(
Identity
(
Identity
),
unless
,
when
)
import
IHaskell.Convert.Args
(
ConvertSpec
(
ConvertSpec
,
convertInput
,
convertLhsStyle
,
convertOutput
,
convertOverwriteFiles
,
convertToIpynb
),
fromJustConvertSpec
,
toConvertSpec
)
import
IHaskell.Convert.Args
(
ConvertSpec
(
..
),
fromJustConvertSpec
,
toConvertSpec
)
import
IHaskell.Convert.IpynbToLhs
(
ipynbToLhs
)
import
IHaskell.Convert.IpynbToLhs
(
ipynbToLhs
)
import
IHaskell.Convert.LhsToIpynb
(
lhsToIpynb
)
import
IHaskell.Convert.LhsToIpynb
(
lhsToIpynb
)
import
IHaskell.Flags
(
Argument
)
import
IHaskell.Flags
(
Argument
)
...
@@ -10,12 +11,15 @@ import Text.Printf (printf)
...
@@ -10,12 +11,15 @@ import Text.Printf (printf)
-- | used by @IHaskell convert@
-- | used by @IHaskell convert@
convert
::
[
Argument
]
->
IO
()
convert
::
[
Argument
]
->
IO
()
convert
args
=
case
fromJustConvertSpec
(
toConvertSpec
args
)
of
convert
args
=
ConvertSpec
{
convertToIpynb
=
Identity
toIpynb
,
case
fromJustConvertSpec
(
toConvertSpec
args
)
of
convertInput
=
Identity
inputFile
,
ConvertSpec
convertOutput
=
Identity
outputFile
,
{
convertToIpynb
=
Identity
toIpynb
convertLhsStyle
=
Identity
lhsStyle
,
,
convertInput
=
Identity
inputFile
convertOverwriteFiles
=
force
}
,
convertOutput
=
Identity
outputFile
,
convertLhsStyle
=
Identity
lhsStyle
,
convertOverwriteFiles
=
force
}
|
toIpynb
->
do
|
toIpynb
->
do
unless
force
(
failIfExists
outputFile
)
unless
force
(
failIfExists
outputFile
)
lhsToIpynb
lhsStyle
inputFile
outputFile
lhsToIpynb
lhsStyle
inputFile
outputFile
...
@@ -29,5 +33,3 @@ failIfExists file = do
...
@@ -29,5 +33,3 @@ failIfExists file = do
exists
<-
doesFileExist
file
exists
<-
doesFileExist
file
when
exists
$
fail
$
when
exists
$
fail
$
printf
"File %s already exists. To force supply --force."
file
printf
"File %s already exists. To force supply --force."
file
src/IHaskell/Convert/Args.hs
View file @
7ba7c4d1
-- | Description: interpret flags parsed by "IHaskell.Flags"
-- | Description: interpret flags parsed by "IHaskell.Flags"
module
IHaskell.Convert.Args
module
IHaskell.Convert.Args
(
ConvertSpec
(
..
),
fromJustConvertSpec
,
toConvertSpec
)
where
(
ConvertSpec
(
..
),
fromJustConvertSpec
,
toConvertSpec
,
)
where
import
Control.Applicative
((
<$>
))
import
Control.Applicative
((
<$>
))
import
Control.Monad.Identity
(
Identity
(
Identity
))
import
Control.Monad.Identity
(
Identity
(
Identity
))
...
@@ -15,35 +11,36 @@ import IHaskell.Flags (Argument(..), LhsStyle, lhsStyleBird, NotebookFormat(..))
...
@@ -15,35 +11,36 @@ import IHaskell.Flags (Argument(..), LhsStyle, lhsStyleBird, NotebookFormat(..))
import
System.FilePath
((
<.>
),
dropExtension
,
takeExtension
)
import
System.FilePath
((
<.>
),
dropExtension
,
takeExtension
)
import
Text.Printf
(
printf
)
import
Text.Printf
(
printf
)
-- | ConvertSpec is the accumulator for command line arguments
-- | ConvertSpec is the accumulator for command line arguments
data
ConvertSpec
f
=
ConvertSpec
data
ConvertSpec
f
=
{
convertToIpynb
::
f
Bool
,
ConvertSpec
convertInput
::
f
FilePath
,
{
convertToIpynb
::
f
Bool
convertOutput
::
f
FilePath
,
,
convertInput
::
f
FilePath
convertLhsStyle
::
f
(
LhsStyle
T
.
Text
),
,
convertOutput
::
f
FilePath
convertOverwriteFiles
::
Bool
,
convertLhsStyle
::
f
(
LhsStyle
T
.
Text
)
,
convertOverwriteFiles
::
Bool
}
}
-- | Convert a possibly-incomplete specification for what to convert
-- | Convert a possibly-incomplete specification for what to convert
into one which can be executed.
--
into one which can be executed.
Calls error when data is missing.
-- Calls error when data is missing.
fromJustConvertSpec
::
ConvertSpec
Maybe
->
ConvertSpec
Identity
fromJustConvertSpec
::
ConvertSpec
Maybe
->
ConvertSpec
Identity
fromJustConvertSpec
convertSpec
=
convertSpec
{
fromJustConvertSpec
convertSpec
=
convertSpec
convertToIpynb
=
Identity
toIpynb
,
{
convertToIpynb
=
Identity
toIpynb
convertInput
=
Identity
inputFile
,
,
convertInput
=
Identity
inputFile
convertOutput
=
Identity
outputFile
,
,
convertOutput
=
Identity
outputFile
convertLhsStyle
=
Identity
$
fromMaybe
,
convertLhsStyle
=
Identity
$
fromMaybe
(
T
.
pack
<$>
lhsStyleBird
)
(
convertLhsStyle
convertSpec
)
(
T
.
pack
<$>
lhsStyleBird
)
(
convertLhsStyle
convertSpec
)
}
}
where
where
toIpynb
=
fromMaybe
(
error
"Error: direction for conversion unknown"
)
toIpynb
=
fromMaybe
(
error
"Error: direction for conversion unknown"
)
(
convertToIpynb
convertSpec
)
(
convertToIpynb
convertSpec
)
(
inputFile
,
outputFile
)
=
case
(
convertInput
convertSpec
,
convertOutput
convertSpec
)
of
(
inputFile
,
outputFile
)
=
case
(
convertInput
convertSpec
,
convertOutput
convertSpec
)
of
(
Nothing
,
Nothing
)
->
error
"Error: no files specified for conversion"
(
Nothing
,
Nothing
)
->
error
"Error: no files specified for conversion"
(
Just
i
,
Nothing
)
|
toIpynb
->
(
i
,
dropExtension
i
<.>
"ipynb"
)
(
Just
i
,
Nothing
)
|
toIpynb
->
(
i
,
dropExtension
i
<.>
"ipynb"
)
|
otherwise
->
(
i
,
dropExtension
i
<.>
"lhs"
)
|
otherwise
->
(
i
,
dropExtension
i
<.>
"lhs"
)
(
Nothing
,
Just
o
)
|
toIpynb
->
(
dropExtension
o
<.>
"lhs"
,
o
)
(
Nothing
,
Just
o
)
|
toIpynb
->
(
dropExtension
o
<.>
"lhs"
,
o
)
|
otherwise
->
(
dropExtension
o
<.>
"ipynb"
,
o
)
|
otherwise
->
(
dropExtension
o
<.>
"ipynb"
,
o
)
(
Just
i
,
Just
o
)
->
(
i
,
o
)
(
Just
i
,
Just
o
)
->
(
i
,
o
)
...
@@ -53,10 +50,8 @@ isFormatSpec (ConvertToFormat _) = True
...
@@ -53,10 +50,8 @@ isFormatSpec (ConvertToFormat _) = True
isFormatSpec
(
ConvertFromFormat
_
)
=
True
isFormatSpec
(
ConvertFromFormat
_
)
=
True
isFormatSpec
_
=
False
isFormatSpec
_
=
False
toConvertSpec
::
[
Argument
]
->
ConvertSpec
Maybe
toConvertSpec
::
[
Argument
]
->
ConvertSpec
Maybe
toConvertSpec
args
=
mergeArgs
otherArgs
toConvertSpec
args
=
mergeArgs
otherArgs
(
mergeArgs
formatSpecArgs
initialConvertSpec
)
(
mergeArgs
formatSpecArgs
initialConvertSpec
)
where
where
(
formatSpecArgs
,
otherArgs
)
=
partition
isFormatSpec
args
(
formatSpecArgs
,
otherArgs
)
=
partition
isFormatSpec
args
initialConvertSpec
=
ConvertSpec
Nothing
Nothing
Nothing
Nothing
False
initialConvertSpec
=
ConvertSpec
Nothing
Nothing
Nothing
Nothing
False
...
@@ -68,40 +63,39 @@ mergeArg :: Argument -> ConvertSpec Maybe -> ConvertSpec Maybe
...
@@ -68,40 +63,39 @@ mergeArg :: Argument -> ConvertSpec Maybe -> ConvertSpec Maybe
mergeArg
OverwriteFiles
convertSpec
=
convertSpec
{
convertOverwriteFiles
=
True
}
mergeArg
OverwriteFiles
convertSpec
=
convertSpec
{
convertOverwriteFiles
=
True
}
mergeArg
(
ConvertLhsStyle
lhsStyle
)
convertSpec
mergeArg
(
ConvertLhsStyle
lhsStyle
)
convertSpec
|
Just
previousLhsStyle
<-
convertLhsStyle
convertSpec
,
|
Just
previousLhsStyle
<-
convertLhsStyle
convertSpec
,
previousLhsStyle
/=
fmap
T
.
pack
lhsStyle
=
error
$
printf
previousLhsStyle
/=
fmap
T
.
pack
lhsStyle
"Conflicting lhs styles requested: <%s> and <%s>"
=
error
$
printf
"Conflicting lhs styles requested: <%s> and <%s>"
(
show
lhsStyle
)
(
show
lhsStyle
)
(
show
previousLhsStyle
)
(
show
previousLhsStyle
)
|
otherwise
=
convertSpec
{
convertLhsStyle
=
Just
(
T
.
pack
<$>
lhsStyle
)
}
|
otherwise
=
convertSpec
{
convertLhsStyle
=
Just
(
T
.
pack
<$>
lhsStyle
)
}
mergeArg
(
ConvertFrom
inputFile
)
convertSpec
mergeArg
(
ConvertFrom
inputFile
)
convertSpec
|
Just
previousInputFile
<-
convertInput
convertSpec
,
|
Just
previousInputFile
<-
convertInput
convertSpec
,
previousInputFile
/=
inputFile
=
error
$
printf
"Multiple input files specified: <%s> and <%s>"
previousInputFile
/=
inputFile
inputFile
previousInputFile
=
error
$
printf
"Multiple input files specified: <%s> and <%s>"
inputFile
previousInputFile
|
otherwise
=
convertSpec
{
|
otherwise
=
convertSpec
convertInput
=
Just
inputFile
,
{
convertInput
=
Just
inputFile
convertToIpynb
=
case
(
convertToIpynb
convertSpec
,
fromExt
inputFile
)
of
,
convertToIpynb
=
case
(
convertToIpynb
convertSpec
,
fromExt
inputFile
)
of
(
prev
,
Nothing
)
->
prev
(
prev
,
Nothing
)
->
prev
(
prev
@
(
Just
_
),
_
)
->
prev
(
prev
@
(
Just
_
),
_
)
->
prev
(
Nothing
,
format
)
->
fmap
(
==
LhsMarkdown
)
format
(
Nothing
,
format
)
->
fmap
(
==
LhsMarkdown
)
format
}
}
mergeArg
(
ConvertTo
outputFile
)
convertSpec
mergeArg
(
ConvertTo
outputFile
)
convertSpec
|
Just
previousOutputFile
<-
convertOutput
convertSpec
,
|
Just
previousOutputFile
<-
convertOutput
convertSpec
,
previousOutputFile
/=
outputFile
=
error
$
printf
"Multiple output files specified: <%s> and <%s>"
previousOutputFile
/=
outputFile
outputFile
previousOutputFile
=
error
$
printf
"Multiple output files specified: <%s> and <%s>"
outputFile
previousOutputFile
|
otherwise
=
convertSpec
{
|
otherwise
=
convertSpec
convertOutput
=
Just
outputFile
,
{
convertOutput
=
Just
outputFile
convertToIpynb
=
case
(
convertToIpynb
convertSpec
,
fromExt
outputFile
)
of
,
convertToIpynb
=
case
(
convertToIpynb
convertSpec
,
fromExt
outputFile
)
of
(
prev
,
Nothing
)
->
prev
(
prev
,
Nothing
)
->
prev
(
prev
@
(
Just
_
),
_
)
->
prev
(
prev
@
(
Just
_
),
_
)
->
prev
(
Nothing
,
format
)
->
fmap
(
==
IpynbFile
)
format
(
Nothing
,
format
)
->
fmap
(
==
IpynbFile
)
format
}
}
mergeArg
unexpectedArg
_
=
error
$
"IHaskell.Convert.mergeArg: impossible argument: "
mergeArg
unexpectedArg
_
=
error
$
"IHaskell.Convert.mergeArg: impossible argument: "
++
show
unexpectedArg
++
show
unexpectedArg
-- | Guess the format based on the file extension.
-- | Guess the format based on the file extension.
fromExt
::
FilePath
->
Maybe
NotebookFormat
fromExt
::
FilePath
->
Maybe
NotebookFormat
fromExt
s
=
case
map
toLower
(
takeExtension
s
)
of
fromExt
s
=
case
map
toLower
(
takeExtension
s
)
of
".lhs"
->
Just
LhsMarkdown
".lhs"
->
Just
LhsMarkdown
".ipynb"
->
Just
IpynbFile
".ipynb"
->
Just
IpynbFile
_
->
Nothing
_
->
Nothing
src/IHaskell/Convert/IpynbToLhs.hs
View file @
7ba7c4d1
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ScopedTypeVariables #-}
module
IHaskell.Convert.IpynbToLhs
(
ipynbToLhs
)
where
module
IHaskell.Convert.IpynbToLhs
(
ipynbToLhs
)
where
import
Control.Applicative
((
<$>
))
import
Control.Applicative
((
<$>
))
...
@@ -12,7 +13,7 @@ import qualified Data.Text.Lazy as T (concat, fromStrict, Text, unlines)
...
@@ -12,7 +13,7 @@ import qualified Data.Text.Lazy as T (concat, fromStrict, Text, unlines)
import
qualified
Data.Text.Lazy.IO
as
T
(
writeFile
)
import
qualified
Data.Text.Lazy.IO
as
T
(
writeFile
)
import
Data.Vector
(
Vector
)
import
Data.Vector
(
Vector
)
import
qualified
Data.Vector
as
V
(
map
,
mapM
,
toList
)
import
qualified
Data.Vector
as
V
(
map
,
mapM
,
toList
)
import
IHaskell.Flags
(
LhsStyle
(
lhsBeginCode
,
lhsBeginOutput
,
lhsCodePrefix
,
lhsEndCode
,
lhsEndOutput
,
lhsOutputPrefix
))
import
IHaskell.Flags
(
LhsStyle
(
..
))
ipynbToLhs
::
LhsStyle
T
.
Text
ipynbToLhs
::
LhsStyle
T
.
Text
->
FilePath
-- ^ the filename of an ipython notebook
->
FilePath
-- ^ the filename of an ipython notebook
...
@@ -22,8 +23,7 @@ ipynbToLhs sty from to = do
...
@@ -22,8 +23,7 @@ ipynbToLhs sty from to = do
Just
(
js
::
Object
)
<-
decode
<$>
L
.
readFile
from
Just
(
js
::
Object
)
<-
decode
<$>
L
.
readFile
from
case
M
.
lookup
"cells"
js
of
case
M
.
lookup
"cells"
js
of
Just
(
Array
cells
)
->
Just
(
Array
cells
)
->
T
.
writeFile
to
$
T
.
unlines
$
V
.
toList
T
.
writeFile
to
$
T
.
unlines
$
V
.
toList
$
V
.
map
(
\
(
Object
y
)
->
convCell
sty
y
)
cells
$
V
.
map
(
\
(
Object
y
)
->
convCell
sty
y
)
cells
_
->
error
"IHaskell.Convert.ipynbTolhs: json does not follow expected schema"
_
->
error
"IHaskell.Convert.ipynbTolhs: json does not follow expected schema"
concatWithPrefix
::
T
.
Text
-- ^ the prefix to add to every line
concatWithPrefix
::
T
.
Text
-- ^ the prefix to add to every line
...
@@ -35,19 +35,21 @@ toStr :: Value -> Maybe T.Text
...
@@ -35,19 +35,21 @@ toStr :: Value -> Maybe T.Text
toStr
(
String
x
)
=
Just
(
T
.
fromStrict
x
)
toStr
(
String
x
)
=
Just
(
T
.
fromStrict
x
)
toStr
_
=
Nothing
toStr
_
=
Nothing
-- | @convCell sty cell@ converts a single cell in JSON into text suitable
-- | @convCell sty cell@ converts a single cell in JSON into text suitable
for the type of lhs file
--
for the type of lhs file
described by the @sty@
-- described by the @sty@
convCell
::
LhsStyle
T
.
Text
->
Object
->
T
.
Text
convCell
::
LhsStyle
T
.
Text
->
Object
->
T
.
Text
convCell
_sty
object
convCell
_sty
object
|
Just
(
String
"markdown"
)
<-
M
.
lookup
"cell_type"
object
,
|
Just
(
String
"markdown"
)
<-
M
.
lookup
"cell_type"
object
,
Just
(
Array
xs
)
<-
M
.
lookup
"source"
object
,
Just
(
Array
xs
)
<-
M
.
lookup
"source"
object
,
~
(
Just
s
)
<-
concatWithPrefix
""
xs
=
s
~
(
Just
s
)
<-
concatWithPrefix
""
xs
=
s
convCell
sty
object
convCell
sty
object
|
Just
(
String
"code"
)
<-
M
.
lookup
"cell_type"
object
,
|
Just
(
String
"code"
)
<-
M
.
lookup
"cell_type"
object
,
Just
(
Array
i
)
<-
M
.
lookup
"source"
object
,
Just
(
Array
i
)
<-
M
.
lookup
"source"
object
,
Just
(
Array
o
)
<-
M
.
lookup
"outputs"
object
,
Just
(
Array
o
)
<-
M
.
lookup
"outputs"
object
,
~
(
Just
i
)
<-
concatWithPrefix
(
lhsCodePrefix
sty
)
i
,
~
(
Just
i
)
<-
concatWithPrefix
(
lhsCodePrefix
sty
)
i
,
o
<-
fromMaybe
mempty
(
convOutputs
sty
o
)
=
"
\n
"
<>
o
<-
fromMaybe
mempty
(
convOutputs
sty
o
)
=
"
\n
"
<>
lhsBeginCode
sty
<>
i
<>
lhsEndCode
sty
<>
"
\n
"
<>
o
<>
"
\n
"
lhsBeginCode
sty
<>
i
<>
lhsEndCode
sty
<>
"
\n
"
<>
o
<>
"
\n
"
convCell
_
_
=
"IHaskell.Convert.convCell: unknown cell"
convCell
_
_
=
"IHaskell.Convert.convCell: unknown cell"
...
...
src/IHaskell/Convert/LhsToIpynb.hs
View file @
7ba7c4d1
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE CPP #-}
module
IHaskell.Convert.LhsToIpynb
(
lhsToIpynb
)
where
module
IHaskell.Convert.LhsToIpynb
(
lhsToIpynb
)
where
import
Control.Applicative
((
<$>
))
import
Control.Applicative
((
<$>
))
...
@@ -19,7 +20,9 @@ lhsToIpynb sty from to = do
...
@@ -19,7 +20,9 @@ lhsToIpynb sty from to = do
classed
<-
classifyLines
sty
.
T
.
lines
<$>
T
.
readFile
from
classed
<-
classifyLines
sty
.
T
.
lines
<$>
T
.
readFile
from
L
.
writeFile
to
.
encode
.
encodeCells
$
groupClassified
classed
L
.
writeFile
to
.
encode
.
encodeCells
$
groupClassified
classed
data
CellLine
a
=
CodeLine
a
|
OutputLine
a
|
MarkdownLine
a
data
CellLine
a
=
CodeLine
a
|
OutputLine
a
|
MarkdownLine
a
deriving
Show
deriving
Show
isCode
::
CellLine
t
->
Bool
isCode
::
CellLine
t
->
Bool
...
@@ -38,37 +41,39 @@ isEmptyMD :: (Eq a, Monoid a) => CellLine a -> Bool
...
@@ -38,37 +41,39 @@ isEmptyMD :: (Eq a, Monoid a) => CellLine a -> Bool
isEmptyMD
(
MarkdownLine
a
)
=
a
==
mempty
isEmptyMD
(
MarkdownLine
a
)
=
a
==
mempty
isEmptyMD
_
=
False
isEmptyMD
_
=
False
untag
::
CellLine
t
->
t
untag
::
CellLine
t
->
t
untag
(
CodeLine
a
)
=
a
untag
(
CodeLine
a
)
=
a
untag
(
OutputLine
a
)
=
a
untag
(
OutputLine
a
)
=
a
untag
(
MarkdownLine
a
)
=
a
untag
(
MarkdownLine
a
)
=
a
data
Cell
a
=
Code
a
a
|
Markdown
a
data
Cell
a
=
Code
a
a
deriving
(
Show
)
|
Markdown
a
deriving
Show
encodeCells
::
[
Cell
[
T
.
Text
]]
->
Value
encodeCells
::
[
Cell
[
T
.
Text
]]
->
Value
encodeCells
xs
=
object
$
encodeCells
xs
=
object
$
[
"cells"
.=
Array
(
V
.
fromList
(
map
cellToVal
xs
))
]
[
"cells"
.=
Array
(
V
.
fromList
(
map
cellToVal
xs
))
]
++
boilerplate
++
boilerplate
cellToVal
::
Cell
[
T
.
Text
]
->
Value
cellToVal
::
Cell
[
T
.
Text
]
->
Value
cellToVal
(
Code
i
o
)
=
object
$
cellToVal
(
Code
i
o
)
=
object
$
[
"cell_type"
.=
String
"code"
,
[
"cell_type"
.=
String
"code"
"execution_count"
.=
Null
,
,
"execution_count"
.=
Null
"metadata"
.=
object
[
"collapsed"
.=
Bool
False
],
,
"metadata"
.=
object
[
"collapsed"
.=
Bool
False
]
"source"
.=
arrayFromTxt
i
,
,
"source"
.=
arrayFromTxt
i
"outputs"
.=
Array
,
"outputs"
.=
Array
(
V
.
fromList
(
(
V
.
fromList
[
object
[
"text"
.=
arrayFromTxt
o
,
([
object
"metadata"
.=
object
[]
,
[
"text"
.=
arrayFromTxt
o
"output_type"
.=
String
"display_data"
]
,
"metadata"
.=
object
[]
|
_
<-
take
1
o
]))
]
,
"output_type"
.=
String
"display_data"
]
|
_
<-
take
1
o
]))
]
cellToVal
(
Markdown
txt
)
=
object
$
cellToVal
(
Markdown
txt
)
=
object
$
[
"cell_type"
.=
String
"markdown"
,
[
"cell_type"
.=
String
"markdown"
"metadata"
.=
object
[
"hidden"
.=
Bool
False
],
,
"metadata"
.=
object
[
"hidden"
.=
Bool
False
]
"source"
.=
arrayFromTxt
txt
]
,
"source"
.=
arrayFromTxt
txt
]
-- | arrayFromTxt makes a JSON array of string s
-- | arrayFromTxt makes a JSON array of string s
arrayFromTxt
::
[
T
.
Text
]
->
Value
arrayFromTxt
::
[
T
.
Text
]
->
Value
...
@@ -76,44 +81,41 @@ arrayFromTxt i = Array (V.fromList $ map stringify i)
...
@@ -76,44 +81,41 @@ arrayFromTxt i = Array (V.fromList $ map stringify i)
where
where
stringify
=
String
.
T
.
toStrict
.
flip
T
.
snoc
'
\n
'
stringify
=
String
.
T
.
toStrict
.
flip
T
.
snoc
'
\n
'
-- | ihaskell needs this boilerplate at the upper level to interpret the
-- | ihaskell needs this boilerplate at the upper level to interpret the
json describing cells and
--
json describing cells and
output correctly.
-- output correctly.
boilerplate
::
[(
TS
.
Text
,
Value
)]
boilerplate
::
[(
TS
.
Text
,
Value
)]
boilerplate
=
boilerplate
=
[
"metadata"
.=
object
[
kernelspec
,
lang
]
[
"metadata"
.=
object
[
kernelspec
,
lang
],
"nbformat"
.=
Number
4
,
"nbformat_minor"
.=
Number
0
]
,
"nbformat"
.=
Number
4
,
"nbformat_minor"
.=
Number
0
]
where
where
kernelspec
=
"kernelspec"
.=
object
[
kernelspec
=
"kernelspec"
.=
object
"display_name"
.=
String
"Haskell"
[
"display_name"
.=
String
"Haskell"
,
"language"
.=
String
"haskell"
,
"language"
.=
String
"haskell"
,
"name"
.=
String
"haskell"
,
"name"
.=
String
"haskell"
]
]
lang
=
"language_info"
.=
object
[
lang
=
"language_info"
.=
object
[
"name"
.=
String
"haskell"
,
"version"
.=
String
VERSION_ghc
]
"name"
.=
String
"haskell"
,
"version"
.=
String
VERSION_ghc
]
groupClassified
::
[
CellLine
T
.
Text
]
->
[
Cell
[
T
.
Text
]]
groupClassified
::
[
CellLine
T
.
Text
]
->
[
Cell
[
T
.
Text
]]
groupClassified
(
CodeLine
a
:
x
)
groupClassified
(
CodeLine
a
:
x
)
|
(
c
,
x
)
<-
span
isCode
x
,
|
(
c
,
x
)
<-
span
isCode
x
,
(
_
,
x
)
<-
span
isEmptyMD
x
,
(
_
,
x
)
<-
span
isEmptyMD
x
,
(
o
,
x
)
<-
span
isOutput
x
=
Code
(
a
:
map
untag
c
)
(
map
untag
o
)
:
groupClassified
x
(
o
,
x
)
<-
span
isOutput
x
groupClassified
(
MarkdownLine
a
:
x
)
=
Code
(
a
:
map
untag
c
)
(
map
untag
o
)
:
groupClassified
x
|
(
m
,
x
)
<-
span
isMD
x
=
Markdown
(
a
:
map
untag
m
)
:
groupClassified
x
groupClassified
(
MarkdownLine
a
:
x
)
groupClassified
(
OutputLine
a
:
x
)
=
Markdown
[
a
]
:
groupClassified
x
|
(
m
,
x
)
<-
span
isMD
x
=
Markdown
(
a
:
map
untag
m
)
:
groupClassified
x
groupClassified
(
OutputLine
a
:
x
)
=
Markdown
[
a
]
:
groupClassified
x
groupClassified
[]
=
[]
groupClassified
[]
=
[]
classifyLines
::
LhsStyle
T
.
Text
->
[
T
.
Text
]
->
[
CellLine
T
.
Text
]
classifyLines
::
LhsStyle
T
.
Text
->
[
T
.
Text
]
->
[
CellLine
T
.
Text
]
classifyLines
sty
@
(
LhsStyle
c
o
_
_
_
_
)
(
l
:
ls
)
=
case
(
sp
c
,
sp
o
)
of
classifyLines
sty
@
(
LhsStyle
c
o
_
_
_
_
)
(
l
:
ls
)
=
case
(
sp
c
,
sp
o
)
of
(
Just
a
,
Nothing
)
->
CodeLine
a
:
classifyLines
sty
ls
(
Just
a
,
Nothing
)
->
CodeLine
a
:
classifyLines
sty
ls
(
Nothing
,
Just
a
)
->
OutputLine
a
:
classifyLines
sty
ls
(
Nothing
,
Just
a
)
->
OutputLine
a
:
classifyLines
sty
ls
(
Nothing
,
Nothing
)
->
MarkdownLine
l
:
classifyLines
sty
ls
(
Nothing
,
Nothing
)
->
MarkdownLine
l
:
classifyLines
sty
ls
_
->
error
"IHaskell.Convert.classifyLines"
_
->
error
"IHaskell.Convert.classifyLines"
where
where
sp
x
=
T
.
stripPrefix
(
dropSpace
x
)
(
dropSpace
l
)
`
mplus
`
blankCodeLine
x
sp
x
=
T
.
stripPrefix
(
dropSpace
x
)
(
dropSpace
l
)
`
mplus
`
blankCodeLine
x
blankCodeLine
x
=
if
T
.
strip
x
==
T
.
strip
l
then
Just
""
else
Nothing
blankCodeLine
x
=
if
T
.
strip
x
==
T
.
strip
l
then
Just
""
else
Nothing
dropSpace
=
T
.
dropWhile
isSpace
dropSpace
=
T
.
dropWhile
isSpace
classifyLines
_
[]
=
[]
classifyLines
_
[]
=
[]
src/IHaskell/Display.hs
View file @
7ba7c4d1
{-# LANGUAGE NoImplicitPrelude, OverloadedStrings, FlexibleInstances #-}
{-# LANGUAGE NoImplicitPrelude, OverloadedStrings, FlexibleInstances #-}
-- | If you are interested in the IHaskell library for the purpose of
-- | If you are interested in the IHaskell library for the purpose of
augmenting the IHaskell
--
augmenting the IHaskell notebook or writing your own display mechanism
s
--
notebook or writing your own display mechanisms and widgets, this module contains all function
s
--
and widgets, this module contains all functions you need.
--
you need.
--
--
-- In order to create a display mechanism for a particular data type, write
-- In order to create a display mechanism for a particular data type, write a module named (for
-- a module named (for example) @IHaskell.Display.YourThing@ in a package named @ihaskell-yourThing@.
-- example) @IHaskell.Display.YourThing@ in a package named @ihaskell-yourThing@. (Note the
-- (Note the capitalization - it's important!) Then, in that module, add an
-- capitalization - it's important!) Then, in that module, add an instance of @IHaskellDisplay@ for
-- instance of @IHaskellDisplay@ for your data type. Similarly, to create
-- your data type. Similarly, to create a widget, add an instance of @IHaskellWidget@.
-- a widget, add an instance of @IHaskellWidget@.
--
--
-- An example of creating a display is provided in the <http://gibiansky.github.io/IHaskell/demo.html demo notebook>.
-- An example of creating a display is provided in the
-- <http://gibiansky.github.io/IHaskell/demo.html demo notebook>.
--
--
module
IHaskell.Display
(
module
IHaskell.Display
(
-- * Rich display and interactive display typeclasses and types
-- * Rich display and interactive display typeclasses and types
...
@@ -23,11 +23,21 @@ module IHaskell.Display (
...
@@ -23,11 +23,21 @@ module IHaskell.Display (
printDisplay
,
printDisplay
,
-- * Constructors for displays
-- * Constructors for displays
plain
,
html
,
png
,
jpg
,
svg
,
latex
,
javascript
,
many
,
plain
,
html
,
png
,
jpg
,
svg
,
latex
,
javascript
,
many
,
-- ** Image and data encoding functions
-- ** Image and data encoding functions
Width
,
Height
,
Base64
(
..
),
Width
,
encode64
,
base64
,
Height
,
Base64
(
..
),
encode64
,
base64
,
-- ** Utilities
-- ** Utilities
switchToTmpDir
,
switchToTmpDir
,
...
@@ -45,7 +55,7 @@ import Data.String.Utils (rstrip)
...
@@ -45,7 +55,7 @@ import Data.String.Utils (rstrip)
import
qualified
Data.ByteString.Base64
as
Base64
import
qualified
Data.ByteString.Base64
as
Base64
import
qualified
Data.ByteString.Char8
as
Char
import
qualified
Data.ByteString.Char8
as
Char
import
Data.Aeson
(
Value
)
import
Data.Aeson
(
Value
)
import
System.Directory
(
getTemporaryDirectory
,
setCurrentDirectory
)
import
System.Directory
(
getTemporaryDirectory
,
setCurrentDirectory
)
import
Control.Concurrent.STM.TChan
import
Control.Concurrent.STM.TChan
import
System.IO.Unsafe
(
unsafePerformIO
)
import
System.IO.Unsafe
(
unsafePerformIO
)
...
@@ -61,8 +71,7 @@ type Base64 = Text
...
@@ -61,8 +71,7 @@ type Base64 = Text
-- > IO [Display]
-- > IO [Display]
-- > IO (IO Display)
-- > IO (IO Display)
--
--
-- be run the IO and get rendered (if the frontend allows it) in the pretty
-- be run the IO and get rendered (if the frontend allows it) in the pretty form.
-- form.
instance
IHaskellDisplay
a
=>
IHaskellDisplay
(
IO
a
)
where
instance
IHaskellDisplay
a
=>
IHaskellDisplay
(
IO
a
)
where
display
=
(
display
=<<
)
display
=
(
display
=<<
)
...
@@ -101,15 +110,15 @@ latex = DisplayData MimeLatex . pack
...
@@ -101,15 +110,15 @@ latex = DisplayData MimeLatex . pack
javascript
::
String
->
DisplayData
javascript
::
String
->
DisplayData
javascript
=
DisplayData
MimeJavascript
.
pack
javascript
=
DisplayData
MimeJavascript
.
pack
-- | Generate a PNG display of the given width and height. Data must be
-- | Generate a PNG display of the given width and height. Data must be
provided in a Base64 encoded
--
provided in a Base64 encoded manner, suitable for embedding into HTML.
--
manner, suitable for embedding into HTML. The @base64@ function may be used to encode data into
--
The @base64@ function may be used to encode data into
this format.
-- this format.
png
::
Width
->
Height
->
Base64
->
DisplayData
png
::
Width
->
Height
->
Base64
->
DisplayData
png
width
height
=
DisplayData
(
MimePng
width
height
)
png
width
height
=
DisplayData
(
MimePng
width
height
)
-- | Generate a JPG display of the given width and height. Data must be
-- | Generate a JPG display of the given width and height. Data must be
provided in a Base64 encoded
--
provided in a Base64 encoded manner, suitable for embedding into HTML.
--
manner, suitable for embedding into HTML. The @base64@ function may be used to encode data into
--
The @base64@ function may be used to encode data into
this format.
-- this format.
jpg
::
Width
->
Height
->
Base64
->
DisplayData
jpg
::
Width
->
Height
->
Base64
->
DisplayData
jpg
width
height
=
DisplayData
(
MimeJpg
width
height
)
jpg
width
height
=
DisplayData
(
MimeJpg
width
height
)
...
@@ -121,42 +130,37 @@ encode64 str = base64 $ Char.pack str
...
@@ -121,42 +130,37 @@ encode64 str = base64 $ Char.pack str
base64
::
ByteString
->
Base64
base64
::
ByteString
->
Base64
base64
=
decodeUtf8
.
Base64
.
encode
base64
=
decodeUtf8
.
Base64
.
encode
-- | For internal use within IHaskell.
-- | For internal use within IHaskell. Serialize displays to a ByteString.
-- Serialize displays to a ByteString.
serializeDisplay
::
Display
->
ByteString
serializeDisplay
::
Display
->
ByteString
serializeDisplay
=
Serialize
.
encode
serializeDisplay
=
Serialize
.
encode
-- | Items written to this chan will be included in the output sent
-- | Items written to this chan will be included in the output sent to the frontend (ultimately the
-- to the frontend (ultimately the browser), the next time IHaskell
-- browser), the next time IHaskell has an item to display.
-- has an item to display.
{-# NOINLINE displayChan #-}
{-# NOINLINE displayChan #-}
displayChan
::
TChan
Display
displayChan
::
TChan
Display
displayChan
=
unsafePerformIO
newTChanIO
displayChan
=
unsafePerformIO
newTChanIO
-- | Take everything that was put into the 'displayChan' at that point
-- | Take everything that was put into the 'displayChan' at that point
out, and make a 'Display' out
-- o
ut, and make a 'Display' out o
f it.
-- of it.
displayFromChan
::
IO
(
Maybe
Display
)
displayFromChan
::
IO
(
Maybe
Display
)
displayFromChan
=
displayFromChan
=
Just
.
many
<$>
unfoldM
(
atomically
$
tryReadTChan
displayChan
)
Just
.
many
<$>
unfoldM
(
atomically
$
tryReadTChan
displayChan
)
-- | This is unfoldM from monad-loops. It repeatedly runs an IO action
-- | This is unfoldM from monad-loops. It repeatedly runs an IO action until it return Nothing, and
-- until it return Nothing, and puts all the Justs in a list.
-- puts all the Justs in a list. If you find yourself using more functionality from monad-loops,
-- If you find yourself using more functionality from monad-loops, just add
-- just add the package dependency instead of copying more code from it.
-- the package dependency instead of copying more code from it.
unfoldM
::
IO
(
Maybe
a
)
->
IO
[
a
]
unfoldM
::
IO
(
Maybe
a
)
->
IO
[
a
]
unfoldM
f
=
maybe
(
return
[]
)
(
\
r
->
(
r
:
)
<$>
unfoldM
f
)
=<<
f
unfoldM
f
=
maybe
(
return
[]
)
(
\
r
->
(
r
:
)
<$>
unfoldM
f
)
=<<
f
-- | Write to the display channel. The contents will be displayed in the
-- | Write to the display channel. The contents will be displayed in the
notebook once the current
--
notebook once the current
execution call ends.
-- execution call ends.
printDisplay
::
IHaskellDisplay
a
=>
a
->
IO
()
printDisplay
::
IHaskellDisplay
a
=>
a
->
IO
()
printDisplay
disp
=
display
disp
>>=
atomically
.
writeTChan
displayChan
printDisplay
disp
=
display
disp
>>=
atomically
.
writeTChan
displayChan
-- | Convenience function for client libraries. Switch to a temporary
-- | Convenience function for client libraries. Switch to a temporary directory so that any files we
-- directory so that any files we create aren't visible. On Unix, this is
-- create aren't visible. On Unix, this is usually /tmp.
-- usually /tmp.
switchToTmpDir
=
void
(
try
switchDir
::
IO
(
Either
SomeException
()
))
switchToTmpDir
=
void
(
try
switchDir
::
IO
(
Either
SomeException
()
))
where
where
switchDir
=
switchDir
=
getTemporaryDirectory
>>=
getTemporaryDirectory
>>=
setCurrentDirectory
setCurrentDirectory
src/IHaskell/Eval/Completion.hs
View file @
7ba7c4d1
{-# LANGUAGE CPP, NoImplicitPrelude, OverloadedStrings, DoAndIfThenElse #-}
{-# LANGUAGE CPP, NoImplicitPrelude, OverloadedStrings, DoAndIfThenElse #-}
{-# LANGUAGE TypeFamilies, FlexibleContexts #-}
{-# LANGUAGE TypeFamilies, FlexibleContexts #-}
{- |
{- |
Description: Generates tab completion options.
Description: Generates tab completion options.
...
@@ -13,7 +14,6 @@ This has a limited amount of context sensitivity. It distinguishes between four
...
@@ -13,7 +14,6 @@ This has a limited amount of context sensitivity. It distinguishes between four
module
IHaskell.Eval.Completion
(
complete
,
completionTarget
,
completionType
,
CompletionType
(
..
))
where
module
IHaskell.Eval.Completion
(
complete
,
completionTarget
,
completionType
,
CompletionType
(
..
))
where
import
ClassyPrelude
hiding
(
init
,
last
,
head
,
liftIO
)
import
ClassyPrelude
hiding
(
init
,
last
,
head
,
liftIO
)
--import Prelude
import
Control.Applicative
((
<$>
))
import
Control.Applicative
((
<$>
))
import
Data.ByteString.UTF8
hiding
(
drop
,
take
,
lines
,
length
)
import
Data.ByteString.UTF8
hiding
(
drop
,
take
,
lines
,
length
)
...
@@ -46,9 +46,7 @@ import IHaskell.Types
...
@@ -46,9 +46,7 @@ import IHaskell.Types
import
IHaskell.Eval.Evaluate
(
Interpreter
)
import
IHaskell.Eval.Evaluate
(
Interpreter
)
import
IHaskell.Eval.ParseShell
(
parseShell
)
import
IHaskell.Eval.ParseShell
(
parseShell
)
data
CompletionType
=
Empty
data
CompletionType
=
Empty
|
Identifier
String
|
Identifier
String
|
DynFlag
String
|
DynFlag
String
|
Qualified
String
String
|
Qualified
String
String
...
@@ -58,10 +56,15 @@ data CompletionType
...
@@ -58,10 +56,15 @@ data CompletionType
|
KernelOption
String
|
KernelOption
String
|
Extension
String
|
Extension
String
deriving
(
Show
,
Eq
)
deriving
(
Show
,
Eq
)
#
if
MIN_VERSION_ghc
(
7
,
10
,
0
)
extName
(
FlagSpec
{
flagSpecName
=
name
})
=
name
#
else
extName
(
name
,
_
,
_
)
=
name
exposedName
=
id
#
endif
complete
::
String
->
Int
->
Interpreter
(
String
,
[
String
])
complete
::
String
->
Int
->
Interpreter
(
String
,
[
String
])
complete
code
posOffset
=
do
complete
code
posOffset
=
do
-- Get the line of code which is being completed and offset within that line
-- Get the line of code which is being completed and offset within that line
let
findLine
offset
(
first
:
rest
)
=
let
findLine
offset
(
first
:
rest
)
=
if
offset
<=
length
first
if
offset
<=
length
first
...
@@ -78,10 +81,6 @@ complete code posOffset = do
...
@@ -78,10 +81,6 @@ complete code posOffset = do
unqualNames
=
nub
$
filter
(
not
.
isQualified
)
rdrNames
unqualNames
=
nub
$
filter
(
not
.
isQualified
)
rdrNames
qualNames
=
nub
$
scopeNames
++
filter
isQualified
rdrNames
qualNames
=
nub
$
scopeNames
++
filter
isQualified
rdrNames
#
if
!
MIN_VERSION_ghc
(
7
,
10
,
0
)
let
exposedName
=
id
#
endif
let
Just
db
=
pkgDatabase
flags
let
Just
db
=
pkgDatabase
flags
getNames
=
map
(
moduleNameString
.
exposedName
)
.
exposedModules
getNames
=
map
(
moduleNameString
.
exposedName
)
.
exposedModules
moduleNames
=
nub
$
concatMap
getNames
db
moduleNames
=
nub
$
concatMap
getNames
db
...
@@ -89,19 +88,13 @@ complete code posOffset = do
...
@@ -89,19 +88,13 @@ complete code posOffset = do
let
target
=
completionTarget
line
pos
let
target
=
completionTarget
line
pos
completion
=
completionType
line
pos
target
completion
=
completionType
line
pos
target
let
matchedText
=
case
completion
of
let
matchedText
=
case
completion
of
HsFilePath
_
match
->
match
HsFilePath
_
match
->
match
FilePath
_
match
->
match
FilePath
_
match
->
match
otherwise
->
intercalate
"."
target
otherwise
->
intercalate
"."
target
#
if
MIN_VERSION_ghc
(
7
,
10
,
0
)
options
<-
case
completion
of
let
extName
(
FlagSpec
{
flagSpecName
=
name
})
=
name
#
else
let
extName
(
name
,
_
,
_
)
=
name
#
endif
options
<-
case
completion
of
Empty
->
return
[]
Empty
->
return
[]
Identifier
candidate
->
Identifier
candidate
->
...
@@ -121,21 +114,20 @@ complete code posOffset = do
...
@@ -121,21 +114,20 @@ complete code posOffset = do
return
$
filter
(
prefix
`
isPrefixOf
`)
moduleNames
return
$
filter
(
prefix
`
isPrefixOf
`)
moduleNames
DynFlag
ext
->
do
DynFlag
ext
->
do
-- Possibly leave out the fLangFlags? The
-- Possibly leave out the fLangFlags? The -XUndecidableInstances vs. obsolete
-- -XUndecidableInstances vs. obsolete
-- -fallow-undecidable-instances.
-- -fallow-undecidable-instances.
let
kernelOptNames
=
concatMap
getSetName
kernelOpts
let
kernelOptNames
=
concatMap
getSetName
kernelOpts
otherNames
=
[
"-package"
,
"-Wall"
,
"-w"
]
otherNames
=
[
"-package"
,
"-Wall"
,
"-w"
]
fNames
=
map
extName
fFlags
++
fNames
=
map
extName
fFlags
++
map
extName
fWarningFlags
++
map
extName
fWarningFlags
++
map
extName
fLangFlags
map
extName
fLangFlags
fNoNames
=
map
(
"no"
++
)
fNames
fNoNames
=
map
(
"no"
++
)
fNames
fAllNames
=
map
(
"-f"
++
)
(
fNames
++
fNoNames
)
fAllNames
=
map
(
"-f"
++
)
(
fNames
++
fNoNames
)
xNames
=
map
extName
xFlags
xNames
=
map
extName
xFlags
xNoNames
=
map
(
"No"
++
)
xNames
xNoNames
=
map
(
"No"
++
)
xNames
xAllNames
=
map
(
"-X"
++
)
(
xNames
++
xNoNames
)
xAllNames
=
map
(
"-X"
++
)
(
xNames
++
xNoNames
)
allNames
=
xAllNames
++
otherNames
++
fAllNames
allNames
=
xAllNames
++
otherNames
++
fAllNames
...
@@ -146,7 +138,8 @@ complete code posOffset = do
...
@@ -146,7 +138,8 @@ complete code posOffset = do
xNoNames
=
map
(
"No"
++
)
xNames
xNoNames
=
map
(
"No"
++
)
xNames
return
$
filter
(
ext
`
isPrefixOf
`)
$
xNames
++
xNoNames
return
$
filter
(
ext
`
isPrefixOf
`)
$
xNames
++
xNoNames
HsFilePath
lineUpToCursor
match
->
completePathWithExtensions
[
".hs"
,
".lhs"
]
lineUpToCursor
HsFilePath
lineUpToCursor
match
->
completePathWithExtensions
[
".hs"
,
".lhs"
]
lineUpToCursor
FilePath
lineUpToCursor
match
->
completePath
lineUpToCursor
FilePath
lineUpToCursor
match
->
completePath
lineUpToCursor
...
@@ -164,8 +157,8 @@ getTrueModuleName name = do
...
@@ -164,8 +157,8 @@ getTrueModuleName name = do
-- Get all imports that we use.
-- Get all imports that we use.
imports
<-
ClassyPrelude
.
catMaybes
<$>
map
onlyImportDecl
<$>
getContext
imports
<-
ClassyPrelude
.
catMaybes
<$>
map
onlyImportDecl
<$>
getContext
-- Find the ones that have a qualified name attached.
-- Find the ones that have a qualified name attached.
If this name isn't one of them, it already is
--
If this name isn't one of them, it already is
the true name.
-- the true name.
flags
<-
getSessionDynFlags
flags
<-
getSessionDynFlags
let
qualifiedImports
=
filter
(
isJust
.
ideclAs
)
imports
let
qualifiedImports
=
filter
(
isJust
.
ideclAs
)
imports
hasName
imp
=
name
==
(
showPpr
flags
.
fromJust
.
ideclAs
)
imp
hasName
imp
=
name
==
(
showPpr
flags
.
fromJust
.
ideclAs
)
imp
...
@@ -180,40 +173,41 @@ completionType :: String -- ^ The line on which the completion is bei
...
@@ -180,40 +173,41 @@ completionType :: String -- ^ The line on which the completion is bei
->
CompletionType
->
CompletionType
completionType
line
loc
target
completionType
line
loc
target
-- File and directory completions are special
-- File and directory completions are special
|
startswith
":!"
stripped
|
startswith
":!"
stripped
=
=
fileComplete
FilePath
fileComplete
FilePath
|
startswith
":l"
stripped
|
startswith
":l"
stripped
=
=
fileComplete
HsFilePath
fileComplete
HsFilePath
-- Complete :set, :opt, and :ext
-- Complete :set, :opt, and :ext
|
startswith
":s"
stripped
|
startswith
":s"
stripped
=
=
DynFlag
candidate
DynFlag
candidate
|
startswith
":o"
stripped
|
startswith
":o"
stripped
=
=
KernelOption
candidate
KernelOption
candidate
|
startswith
":e"
stripped
|
startswith
":e"
stripped
=
=
Extension
candidate
Extension
candidate
-- Use target for other completions.
-- Use target for other completions. If it's empty, no completion.
-- If it's empty, no completion.
|
null
target
=
|
null
target
Empty
=
Empty
-- When in a string, complete filenames.
-- When in a string, complete filenames.
|
cursorInString
line
loc
|
cursorInString
line
loc
=
=
FilePath
(
getStringTarget
lineUpToCursor
)
(
getStringTarget
lineUpToCursor
)
FilePath
(
getStringTarget
lineUpToCursor
)
(
getStringTarget
lineUpToCursor
)
-- Complete module names in imports and elsewhere.
-- Complete module names in imports and elsewhere.
|
startswith
"import"
stripped
&&
isModName
|
startswith
"import"
stripped
&&
isModName
=
=
ModuleName
dotted
candidate
ModuleName
dotted
candidate
|
isModName
&&
(
not
.
null
.
init
)
target
|
isModName
&&
(
not
.
null
.
init
)
target
=
=
Qualified
dotted
candidate
Qualified
dotted
candidate
-- Default to completing identifiers.
-- Default to completing identifiers.
|
otherwise
|
otherwise
=
=
Identifier
candidate
Identifier
candidate
where
stripped
=
strip
line
where
stripped
=
strip
line
dotted
=
dots
target
dotted
=
dots
target
candidate
|
null
target
=
""
candidate
|
null
target
=
""
|
otherwise
=
last
target
|
otherwise
=
last
target
dots
=
intercalate
"."
.
init
dots
=
intercalate
"."
.
init
isModName
=
all
isCapitalized
(
init
target
)
isModName
=
all
isCapitalized
(
init
target
)
...
@@ -222,7 +216,8 @@ completionType line loc target
...
@@ -222,7 +216,8 @@ completionType line loc target
isCapitalized
(
x
:
_
)
=
isUpper
x
isCapitalized
(
x
:
_
)
=
isUpper
x
lineUpToCursor
=
take
loc
line
lineUpToCursor
=
take
loc
line
fileComplete
filePath
=
case
parseShell
lineUpToCursor
of
fileComplete
filePath
=
case
parseShell
lineUpToCursor
of
Right
xs
->
filePath
lineUpToCursor
$
Right
xs
->
filePath
lineUpToCursor
$
if
endswith
(
last
xs
)
lineUpToCursor
if
endswith
(
last
xs
)
lineUpToCursor
then
last
xs
then
last
xs
...
@@ -236,18 +231,18 @@ completionType line loc target
...
@@ -236,18 +231,18 @@ completionType line loc target
nquotes
(
_
:
xs
)
=
nquotes
xs
nquotes
(
_
:
xs
)
=
nquotes
xs
nquotes
[]
=
0
nquotes
[]
=
0
-- Get the bit of a string that might be a filename completion.
-- Get the bit of a string that might be a filename completion. Logic is a bit convoluted, but
-- Logic is a bit convoluted, but basically go backwards from the
-- basically go backwards from the end, stopping at any quote or space, unless they are escaped.
-- end, stopping at any quote or space, unless they are escaped.
getStringTarget
::
String
->
String
getStringTarget
::
String
->
String
getStringTarget
=
go
""
.
reverse
getStringTarget
=
go
""
.
reverse
where
where
go
acc
rest
=
case
rest
of
go
acc
rest
=
'"'
:
'
\\
'
:
rem
->
go
(
'"'
:
acc
)
rem
case
rest
of
'"'
:
'
\\
'
:
rem
->
go
(
'"'
:
acc
)
rem
'"'
:
rem
->
acc
'"'
:
rem
->
acc
' '
:
'
\\
'
:
rem
->
go
(
' '
:
acc
)
rem
' '
:
'
\\
'
:
rem
->
go
(
' '
:
acc
)
rem
' '
:
rem
->
acc
' '
:
rem
->
acc
x
:
rem
->
go
(
x
:
acc
)
rem
x
:
rem
->
go
(
x
:
acc
)
rem
[]
->
acc
[]
->
acc
-- | Get the word under a given cursor location.
-- | Get the word under a given cursor location.
...
@@ -255,14 +250,14 @@ completionTarget :: String -> Int -> [String]
...
@@ -255,14 +250,14 @@ completionTarget :: String -> Int -> [String]
completionTarget
code
cursor
=
expandCompletionPiece
pieceToComplete
completionTarget
code
cursor
=
expandCompletionPiece
pieceToComplete
where
where
pieceToComplete
=
map
fst
<$>
find
(
elem
cursor
.
map
snd
)
pieces
pieceToComplete
=
map
fst
<$>
find
(
elem
cursor
.
map
snd
)
pieces
pieces
=
splitAlongCursor
$
split
splitter
$
zip
code
[
1
..
]
pieces
=
splitAlongCursor
$
split
splitter
$
zip
code
[
1
..
]
splitter
=
defaultSplitter
{
splitter
=
defaultSplitter
-- Split using only the characters, which are the first elements of
{
-- the (char, index) tuple
--
Split using only the characters, which are the first elements of
the (char, index) tuple
delimiter
=
Delimiter
[
uncurry
isDelim
]
,
delimiter
=
Delimiter
[
uncurry
isDelim
]
-- Condense multiple delimiters into one and then drop them.
-- Condense multiple delimiters into one and then drop them.
condensePolicy
=
Condense
,
,
condensePolicy
=
Condense
delimPolicy
=
Drop
,
delimPolicy
=
Drop
}
}
isDelim
::
Char
->
Int
->
Bool
isDelim
::
Char
->
Int
->
Bool
...
@@ -272,8 +267,8 @@ completionTarget code cursor = expandCompletionPiece pieceToComplete
...
@@ -272,8 +267,8 @@ completionTarget code cursor = expandCompletionPiece pieceToComplete
splitAlongCursor
[]
=
[]
splitAlongCursor
[]
=
[]
splitAlongCursor
(
x
:
xs
)
=
splitAlongCursor
(
x
:
xs
)
=
case
elemIndex
cursor
$
map
snd
x
of
case
elemIndex
cursor
$
map
snd
x
of
Nothing
->
x
:
splitAlongCursor
xs
Nothing
->
x
:
splitAlongCursor
xs
Just
idx
->
take
(
idx
+
1
)
x
:
drop
(
idx
+
1
)
x
:
splitAlongCursor
xs
Just
idx
->
take
(
idx
+
1
)
x
:
drop
(
idx
+
1
)
x
:
splitAlongCursor
xs
-- These are never part of an identifier.
-- These are never part of an identifier.
neverIdent
::
String
neverIdent
::
String
...
@@ -285,7 +280,8 @@ completionTarget code cursor = expandCompletionPiece pieceToComplete
...
@@ -285,7 +280,8 @@ completionTarget code cursor = expandCompletionPiece pieceToComplete
getHome
::
IO
String
getHome
::
IO
String
getHome
=
do
getHome
=
do
homeEither
<-
try
$
getEnv
"HOME"
::
IO
(
Either
SomeException
String
)
homeEither
<-
try
$
getEnv
"HOME"
::
IO
(
Either
SomeException
String
)
return
$
case
homeEither
of
return
$
case
homeEither
of
Left
_
->
"~"
Left
_
->
"~"
Right
home
->
home
Right
home
->
home
...
@@ -301,7 +297,8 @@ unDirExpand str = do
...
@@ -301,7 +297,8 @@ unDirExpand str = do
completePath
::
String
->
Interpreter
[
String
]
completePath
::
String
->
Interpreter
[
String
]
completePath
line
=
completePathFilter
acceptAll
acceptAll
line
""
completePath
line
=
completePathFilter
acceptAll
acceptAll
line
""
where
acceptAll
=
const
True
where
acceptAll
=
const
True
completePathWithExtensions
::
[
String
]
->
String
->
Interpreter
[
String
]
completePathWithExtensions
::
[
String
]
->
String
->
Interpreter
[
String
]
completePathWithExtensions
extensions
line
=
completePathWithExtensions
extensions
line
=
...
@@ -309,7 +306,8 @@ completePathWithExtensions extensions line =
...
@@ -309,7 +306,8 @@ completePathWithExtensions extensions line =
where
where
acceptAll
=
const
True
acceptAll
=
const
True
extensionIsOneOf
exts
str
=
any
correctEnding
exts
extensionIsOneOf
exts
str
=
any
correctEnding
exts
where
correctEnding
ext
=
endswith
ext
str
where
correctEnding
ext
=
endswith
ext
str
completePathFilter
::
(
String
->
Bool
)
-- ^ File filter: test whether to include this file.
completePathFilter
::
(
String
->
Bool
)
-- ^ File filter: test whether to include this file.
->
(
String
->
Bool
)
-- ^ Directory filter: test whether to include this directory.
->
(
String
->
Bool
)
-- ^ Directory filter: test whether to include this directory.
...
@@ -321,15 +319,13 @@ completePathFilter includeFile includeDirectory left right = liftIO $ do
...
@@ -321,15 +319,13 @@ completePathFilter includeFile includeDirectory left right = liftIO $ do
expanded
<-
dirExpand
left
expanded
<-
dirExpand
left
completions
<-
map
replacement
<$>
snd
<$>
completeFilename
(
reverse
expanded
,
right
)
completions
<-
map
replacement
<$>
snd
<$>
completeFilename
(
reverse
expanded
,
right
)
-- Split up into files and directories.
-- Split up into files and directories. Filter out ones we don't want.
-- Filter out ones we don't want.
areDirs
<-
mapM
doesDirectoryExist
completions
areDirs
<-
mapM
doesDirectoryExist
completions
let
dirs
=
filter
includeDirectory
$
map
fst
$
filter
snd
$
zip
completions
areDirs
let
dirs
=
filter
includeDirectory
$
map
fst
$
filter
snd
$
zip
completions
areDirs
files
=
filter
includeFile
$
map
fst
$
filter
(
not
.
snd
)
$
zip
completions
areDirs
files
=
filter
includeFile
$
map
fst
$
filter
(
not
.
snd
)
$
zip
completions
areDirs
-- Return directories before files. However, stick everything that starts
-- Return directories before files. However, stick everything that starts with a dot after
-- with a dot after everything else. If we wanted to keep original
-- everything else. If we wanted to keep original order, we could instead use
-- order, we could instead use
-- filter (`elem` (dirs ++ files)) completions
-- filter (`elem` (dirs ++ files)) completions
suggestions
<-
mapM
unDirExpand
$
dirs
++
files
suggestions
<-
mapM
unDirExpand
$
dirs
++
files
let
isHidden
str
=
startswith
"."
.
last
.
StringUtils
.
split
"/"
$
let
isHidden
str
=
startswith
"."
.
last
.
StringUtils
.
split
"/"
$
...
...
src/IHaskell/Eval/Evaluate.hs
View file @
7ba7c4d1
...
@@ -6,7 +6,12 @@
...
@@ -6,7 +6,12 @@
This module exports all functions used for evaluation of IHaskell input.
This module exports all functions used for evaluation of IHaskell input.
-}
-}
module
IHaskell.Eval.Evaluate
(
module
IHaskell.Eval.Evaluate
(
interpret
,
evaluate
,
Interpreter
,
liftIO
,
typeCleaner
,
globalImports
interpret
,
evaluate
,
Interpreter
,
liftIO
,
typeCleaner
,
globalImports
,
)
where
)
where
import
ClassyPrelude
hiding
(
init
,
last
,
liftIO
,
head
,
hGetContents
,
tail
,
try
)
import
ClassyPrelude
hiding
(
init
,
last
,
liftIO
,
head
,
hGetContents
,
tail
,
try
)
...
@@ -77,16 +82,25 @@ import qualified IHaskell.IPython.Message.UUID as UUID
...
@@ -77,16 +82,25 @@ import qualified IHaskell.IPython.Message.UUID as UUID
import
Paths_ihaskell
(
version
)
import
Paths_ihaskell
(
version
)
import
Data.Version
(
versionBranch
)
import
Data.Version
(
versionBranch
)
data
ErrorOccurred
=
Success
|
Failure
deriving
(
Show
,
Eq
)
data
ErrorOccurred
=
Success
|
Failure
deriving
(
Show
,
Eq
)
-- | Set GHC's verbosity for debugging
-- | Set GHC's verbosity for debugging
ghcVerbosity
::
Maybe
Int
ghcVerbosity
::
Maybe
Int
ghcVerbosity
=
Nothing
-- Just 5
ghcVerbosity
=
Nothing
-- Just 5
ignoreTypePrefixes
::
[
String
]
ignoreTypePrefixes
::
[
String
]
ignoreTypePrefixes
=
[
"GHC.Types"
,
"GHC.Base"
,
"GHC.Show"
,
"System.IO"
,
ignoreTypePrefixes
=
[
"GHC.Types"
"GHC.Float"
,
":Interactive"
,
"GHC.Num"
,
"GHC.IO"
,
,
"GHC.Base"
"GHC.Integer.Type"
]
,
"GHC.Show"
,
"System.IO"
,
"GHC.Float"
,
":Interactive"
,
"GHC.Num"
,
"GHC.IO"
,
"GHC.Integer.Type"
]
typeCleaner
::
String
->
String
typeCleaner
::
String
->
String
typeCleaner
=
useStringType
.
foldl'
(
.
)
id
(
map
(`
replace
`
""
)
fullPrefixes
)
typeCleaner
=
useStringType
.
foldl'
(
.
)
id
(
map
(`
replace
`
""
)
fullPrefixes
)
...
@@ -98,14 +112,12 @@ write :: GhcMonad m => KernelState -> String -> m ()
...
@@ -98,14 +112,12 @@ write :: GhcMonad m => KernelState -> String -> m ()
write
state
x
=
when
(
kernelDebug
state
)
$
liftIO
$
hPutStrLn
stderr
$
"DEBUG: "
++
x
write
state
x
=
when
(
kernelDebug
state
)
$
liftIO
$
hPutStrLn
stderr
$
"DEBUG: "
++
x
type
Interpreter
=
Ghc
type
Interpreter
=
Ghc
#
if
MIN_VERSION_ghc
(
7
,
8
,
0
)
#
if
MIN_VERSION_ghc
(
7
,
8
,
0
)
-- GHC 7.8 exports a MonadIO instance for Ghc
-- GHC 7.8 exports a MonadIO instance for Ghc
#
else
#
else
instance
MonadIO
.
MonadIO
Interpreter
where
instance
MonadIO
.
MonadIO
Interpreter
where
liftIO
=
MonadUtils
.
liftIO
liftIO
=
MonadUtils
.
liftIO
#
endif
#
endif
globalImports
::
[
String
]
globalImports
::
[
String
]
globalImports
=
globalImports
=
[
"import IHaskell.Display()"
[
"import IHaskell.Display()"
...
@@ -118,23 +130,23 @@ globalImports =
...
@@ -118,23 +130,23 @@ globalImports =
,
"import qualified Language.Haskell.TH as IHaskellTH"
,
"import qualified Language.Haskell.TH as IHaskellTH"
]
]
-- | Run an interpreting action. This is effectively runGhc with
-- | Run an interpreting action. This is effectively runGhc with
initialization and importing. First
--
initialization and importing. First argument indicates whether `stdin`
--
argument indicates whether `stdin` is handled specially, which cannot be done in a testing
--
is handled specially, which cannot be done in a testing
environment.
-- environment.
interpret
::
String
->
Bool
->
Interpreter
a
->
IO
a
interpret
::
String
->
Bool
->
Interpreter
a
->
IO
a
interpret
libdir
allowedStdin
action
=
runGhc
(
Just
libdir
)
$
do
interpret
libdir
allowedStdin
action
=
runGhc
(
Just
libdir
)
$
do
-- If we're in a sandbox, add the relevant package database
-- If we're in a sandbox, add the relevant package database
sandboxPackages
<-
liftIO
getSandboxPackageConf
sandboxPackages
<-
liftIO
getSandboxPackageConf
initGhci
sandboxPackages
initGhci
sandboxPackages
case
ghcVerbosity
of
case
ghcVerbosity
of
Just
verb
->
do
dflags
<-
getSessionDynFlags
Just
verb
->
do
dflags
<-
getSessionDynFlags
void
$
setSessionDynFlags
$
dflags
{
verbosity
=
verb
}
void
$
setSessionDynFlags
$
dflags
{
verbosity
=
verb
}
Nothing
->
return
()
Nothing
->
return
()
initializeImports
initializeImports
-- Close stdin so it can't be used.
-- Close stdin so it can't be used. Otherwise it'll block the kernel forever.
-- Otherwise it'll block the kernel forever.
dir
<-
liftIO
getIHaskellDir
dir
<-
liftIO
getIHaskellDir
let
cmd
=
printf
"IHaskell.IPython.Stdin.fixStdin
\"
%s
\"
"
dir
let
cmd
=
printf
"IHaskell.IPython.Stdin.fixStdin
\"
%s
\"
"
dir
when
allowedStdin
$
void
$
when
allowedStdin
$
void
$
...
@@ -144,48 +156,51 @@ interpret libdir allowedStdin action = runGhc (Just libdir) $ do
...
@@ -144,48 +156,51 @@ interpret libdir allowedStdin action = runGhc (Just libdir) $ do
-- Run the rest of the interpreter
-- Run the rest of the interpreter
action
action
#
if
MIN_VERSION_ghc
(
7
,
10
,
0
)
packageIdString'
dflags
=
packageKeyPackageIdString
dflags
#
else
packageIdString'
dflags
=
packageIdString
#
endif
-- | Initialize our GHC session with imports and a value for 'it'.
-- | Initialize our GHC session with imports and a value for 'it'.
initializeImports
::
Interpreter
()
initializeImports
::
Interpreter
()
initializeImports
=
do
initializeImports
=
do
-- Load packages that start with ihaskell-*, aren't just IHaskell,
-- Load packages that start with ihaskell-*, aren't just IHaskell, and depend directly on the right
-- and depend directly on the right version of the ihaskell library.
-- version of the ihaskell library. Also verify that the packages we load are not broken.
-- Also verify that the packages we load are not broken.
dflags
<-
getSessionDynFlags
dflags
<-
getSessionDynFlags
broken
<-
liftIO
getBrokenPackages
broken
<-
liftIO
getBrokenPackages
displayPackages
<-
liftIO
$
do
displayPackages
<-
liftIO
$
do
(
dflags
,
_
)
<-
initPackages
dflags
(
dflags
,
_
)
<-
initPackages
dflags
let
Just
db
=
pkgDatabase
dflags
let
Just
db
=
pkgDatabase
dflags
#
if
MIN_VERSION_ghc
(
7
,
10
,
0
)
packageNames
=
map
(
packageIdString'
dflags
.
packageConfigId
)
db
packageIdString
=
packageKeyPackageIdString
dflags
#
endif
packageNames
=
map
(
packageIdString
.
packageConfigId
)
db
initStr
=
"ihaskell-"
initStr
=
"ihaskell-"
-- Name of the ihaskell package, e.g. "ihaskell-1.2.3.4"
-- Name of the ihaskell package, e.g. "ihaskell-1.2.3.4"
iHaskellPkgName
=
initStr
++
intercalate
"."
(
map
show
(
versionBranch
version
))
iHaskellPkgName
=
initStr
++
intercalate
"."
(
map
show
(
versionBranch
version
))
dependsOnRight
pkg
=
not
$
null
$
do
dependsOnRight
pkg
=
not
$
null
$
do
pkg
<-
db
pkg
<-
db
depId
<-
depends
pkg
depId
<-
depends
pkg
dep
<-
filter
((
==
depId
)
.
installedPackageId
)
db
dep
<-
filter
((
==
depId
)
.
installedPackageId
)
db
guard
(
iHaskellPkgName
`
isPrefixOf
`
packageIdString
(
packageConfigId
dep
))
guard
(
iHaskellPkgName
`
isPrefixOf
`
packageIdString
(
packageConfigId
dep
))
-- ideally the Paths_ihaskell module could provide a way to get the
-- hash too (ihaskell-0.2.0.5-f2bce922fa881611f72dfc4a854353b9),
-- ideally the Paths_ihaskell module could provide a way to get the hash too
-- for now. Things will end badly if you also happen to have an
-- (ihaskell-0.2.0.5-f2bce922fa881611f72dfc4a854353b9), for now. Things will end badly if you also
-- ihaskell-0.2.0.5-ce34eadc18cf2b28c8d338d0f3755502 installed.
-- happen to have an ihaskell-0.2.0.5-ce34eadc18cf2b28c8d338d0f3755502 installed.
iHaskellPkg
=
case
filter
(
==
iHaskellPkgName
)
packageNames
of
iHaskellPkg
=
case
filter
(
==
iHaskellPkgName
)
packageNames
of
[
x
]
->
x
[
x
]
->
x
[]
->
error
(
"cannot find required haskell library: "
++
iHaskellPkgName
)
[]
->
error
_
->
error
(
"multiple haskell packages "
++
iHaskellPkgName
++
" found"
)
(
"cannot find required haskell library: "
++
iHaskellPkgName
)
_
->
error
(
"multiple haskell packages "
++
iHaskellPkgName
++
" found"
)
displayPkgs
=
[
pkgName
displayPkgs
=
[
pkgName
|
pkgName
<-
packageNames
|
pkgName
<-
packageNames
,
,
Just
(
x
:
_
)
<-
[
stripPrefix
initStr
pkgName
]
Just
(
x
:
_
)
<-
[
stripPrefix
initStr
pkgName
],
,
pkgName
`
notElem
`
broken
pkgName
`
notElem
`
broken
,
,
isAlpha
x
]
isAlpha
x
]
return
displayPkgs
return
displayPkgs
...
@@ -214,25 +229,28 @@ initializeImports = do
...
@@ -214,25 +229,28 @@ initializeImports = do
-- | Give a value for the `it` variable.
-- | Give a value for the `it` variable.
initializeItVariable
::
Interpreter
()
initializeItVariable
::
Interpreter
()
initializeItVariable
=
do
initializeItVariable
=
do
-- This is required due to the way we handle `it` in the wrapper
-- This is required due to the way we handle `it` in the wrapper
statements - if it doesn't exist,
--
statements - if it doesn't exist,
the first statement will fail.
-- the first statement will fail.
void
$
runStmt
"let it = ()"
RunToCompletion
void
$
runStmt
"let it = ()"
RunToCompletion
-- | Publisher for IHaskell outputs.
The first argument indicates whether
-- | Publisher for IHaskell outputs.
The first argument indicates whether this output is final
--
this output is final
(true) or intermediate (false).
-- (true) or intermediate (false).
type
Publisher
=
(
EvaluationResult
->
IO
()
)
type
Publisher
=
(
EvaluationResult
->
IO
()
)
-- | Output of a command evaluation.
-- | Output of a command evaluation.
data
EvalOut
=
EvalOut
{
data
EvalOut
=
evalStatus
::
ErrorOccurred
,
EvalOut
evalResult
::
Display
,
{
evalStatus
::
ErrorOccurred
evalState
::
KernelState
,
,
evalResult
::
Display
evalPager
::
String
,
,
evalState
::
KernelState
evalComms
::
[
CommInfo
]
,
evalPager
::
String
,
evalComms
::
[
CommInfo
]
}
}
cleanString
::
String
->
String
cleanString
::
String
->
String
cleanString
x
=
if
allBrackets
then
clean
else
str
cleanString
x
=
if
allBrackets
then
clean
else
str
where
where
str
=
strip
x
str
=
strip
x
l
=
lines
str
l
=
lines
str
...
@@ -274,9 +292,8 @@ evaluate kernelState code output = do
...
@@ -274,9 +292,8 @@ evaluate kernelState code output = do
liftIO
$
output
$
FinalResult
(
evalResult
out
)
""
[]
liftIO
$
output
$
FinalResult
(
evalResult
out
)
""
[]
return
kernelState
return
kernelState
return
updated
{
return
updated
{
getExecutionCounter
=
execCount
+
1
}
getExecutionCounter
=
execCount
+
1
}
where
where
noResults
(
Display
res
)
=
null
res
noResults
(
Display
res
)
=
null
res
noResults
(
ManyDisplay
res
)
=
all
noResults
res
noResults
(
ManyDisplay
res
)
=
all
noResults
res
...
@@ -286,8 +303,7 @@ evaluate kernelState code output = do
...
@@ -286,8 +303,7 @@ evaluate kernelState code output = do
runUntilFailure
state
(
cmd
:
rest
)
=
do
runUntilFailure
state
(
cmd
:
rest
)
=
do
evalOut
<-
evalCommand
output
cmd
state
evalOut
<-
evalCommand
output
cmd
state
-- Get displayed channel outputs.
-- Get displayed channel outputs. Merge them with normal display outputs.
-- Merge them with normal display outputs.
dispsIO
<-
extractValue
"IHaskell.Display.displayFromChan"
dispsIO
<-
extractValue
"IHaskell.Display.displayFromChan"
dispsMay
<-
liftIO
dispsIO
dispsMay
<-
liftIO
dispsIO
let
result
=
let
result
=
...
@@ -322,12 +338,13 @@ safely state = ghandle handler . ghandle sourceErrorHandler
...
@@ -322,12 +338,13 @@ safely state = ghandle handler . ghandle sourceErrorHandler
where
where
handler
::
SomeException
->
Interpreter
EvalOut
handler
::
SomeException
->
Interpreter
EvalOut
handler
exception
=
handler
exception
=
return
EvalOut
{
return
evalStatus
=
Failure
,
EvalOut
evalResult
=
displayError
$
show
exception
,
{
evalStatus
=
Failure
evalState
=
state
,
,
evalResult
=
displayError
$
show
exception
evalPager
=
""
,
,
evalState
=
state
evalComms
=
[]
,
evalPager
=
""
,
evalComms
=
[]
}
}
sourceErrorHandler
::
SourceError
->
Interpreter
EvalOut
sourceErrorHandler
::
SourceError
->
Interpreter
EvalOut
...
@@ -340,28 +357,30 @@ safely state = ghandle handler . ghandle sourceErrorHandler
...
@@ -340,28 +357,30 @@ safely state = ghandle handler . ghandle sourceErrorHandler
let
fullErr
=
unlines
errStrs
let
fullErr
=
unlines
errStrs
return
EvalOut
{
return
evalStatus
=
Failure
,
EvalOut
evalResult
=
displayError
fullErr
,
{
evalStatus
=
Failure
evalState
=
state
,
,
evalResult
=
displayError
fullErr
evalPager
=
""
,
,
evalState
=
state
evalComms
=
[]
,
evalPager
=
""
,
evalComms
=
[]
}
}
wrapExecution
::
KernelState
wrapExecution
::
KernelState
->
Interpreter
Display
->
Interpreter
Display
->
Interpreter
EvalOut
->
Interpreter
EvalOut
wrapExecution
state
exec
=
safely
state
$
exec
>>=
\
res
->
wrapExecution
state
exec
=
safely
state
$
return
EvalOut
{
exec
>>=
\
res
->
evalStatus
=
Success
,
return
evalResult
=
res
,
EvalOut
evalState
=
state
,
{
evalStatus
=
Success
evalPager
=
""
,
,
evalResult
=
res
evalComms
=
[]
,
evalState
=
state
,
evalPager
=
""
,
evalComms
=
[]
}
}
-- | Return the display data for this command, as well as whether it
-- | Return the display data for this command, as well as whether it resulted in an error.
-- resulted in an error.
evalCommand
::
Publisher
->
CodeBlock
->
KernelState
->
Interpreter
EvalOut
evalCommand
::
Publisher
->
CodeBlock
->
KernelState
->
Interpreter
EvalOut
evalCommand
_
(
Import
importStr
)
state
=
wrapExecution
state
$
do
evalCommand
_
(
Import
importStr
)
state
=
wrapExecution
state
$
do
write
state
$
"Import: "
++
importStr
write
state
$
"Import: "
++
importStr
...
@@ -393,21 +412,21 @@ evalCommand _ (Module contents) state = wrapExecution state $ do
...
@@ -393,21 +412,21 @@ evalCommand _ (Module contents) state = wrapExecution state $ do
-- Remember which modules we've loaded before.
-- Remember which modules we've loaded before.
importedModules
<-
getContext
importedModules
<-
getContext
let
-- Get the dot-delimited pieces of the module name.
let
-- Get the dot-delimited pieces of the module name.
moduleNameOf
::
InteractiveImport
->
[
String
]
moduleNameOf
::
InteractiveImport
->
[
String
]
moduleNameOf
(
IIDecl
decl
)
=
split
"."
.
moduleNameString
.
unLoc
.
ideclName
$
decl
moduleNameOf
(
IIDecl
decl
)
=
split
"."
.
moduleNameString
.
unLoc
.
ideclName
$
decl
moduleNameOf
(
IIModule
imp
)
=
split
"."
.
moduleNameString
$
imp
moduleNameOf
(
IIModule
imp
)
=
split
"."
.
moduleNameString
$
imp
-- Return whether this module prevents the loading of the one we're
-- Return whether this module prevents the loading of the one we're trying to load. If a module B
-- trying to load. If a module B exist, we cannot load A.B. All
-- exist, we cannot load A.B. All modules must have unique last names (where A.B has last name B).
-- modules must have unique last names (where A.B has last name B).
-- However, we *can* just reload a module.
-- However, we *can* just reload a module.
preventsLoading
mod
=
preventsLoading
mod
=
let
pieces
=
moduleNameOf
mod
in
let
pieces
=
moduleNameOf
mod
last
namePieces
==
last
pieces
&&
namePieces
/=
pieces
in
last
namePieces
==
last
pieces
&&
namePieces
/=
pieces
-- If we've loaded anything with the same last name, we can't use this.
-- If we've loaded anything with the same last name, we can't use this.
Otherwise, GHC tries to load
--
Otherwise, GHC tries to load
the original *.hs fails and then fails.
-- the original *.hs fails and then fails.
case
find
preventsLoading
importedModules
of
case
find
preventsLoading
importedModules
of
-- If something prevents loading this module, return an error.
-- If something prevents loading this module, return an error.
Just
previous
->
do
Just
previous
->
do
...
@@ -425,7 +444,8 @@ evalCommand output (Directive SetDynFlag flagsStr) state = safely state $ do
...
@@ -425,7 +444,8 @@ evalCommand output (Directive SetDynFlag flagsStr) state = safely state $ do
-- Find which flags are IHaskell flags, and which are GHC flags
-- Find which flags are IHaskell flags, and which are GHC flags
let
flags
=
words
flagsStr
let
flags
=
words
flagsStr
-- Get the kernel state updater for any IHaskell flag; Nothing for things that aren't IHaskell flags.
-- Get the kernel state updater for any IHaskell flag; Nothing for things that aren't IHaskell
-- flags.
ihaskellFlagUpdater
::
String
->
Maybe
(
KernelState
->
KernelState
)
ihaskellFlagUpdater
::
String
->
Maybe
(
KernelState
->
KernelState
)
ihaskellFlagUpdater
flag
=
getUpdateKernelState
<$>
find
(
elem
flag
.
getSetName
)
kernelOpts
ihaskellFlagUpdater
flag
=
getUpdateKernelState
<$>
find
(
elem
flag
.
getSetName
)
kernelOpts
...
@@ -437,38 +457,44 @@ evalCommand output (Directive SetDynFlag flagsStr) state = safely state $ do
...
@@ -437,38 +457,44 @@ evalCommand output (Directive SetDynFlag flagsStr) state = safely state $ do
if
null
flags
if
null
flags
then
do
then
do
flags
<-
getSessionDynFlags
flags
<-
getSessionDynFlags
return
EvalOut
{
return
evalStatus
=
Success
,
EvalOut
evalResult
=
Display
[
plain
$
showSDoc
flags
$
vcat
[
pprDynFlags
False
flags
,
pprLanguages
False
flags
]],
{
evalStatus
=
Success
evalState
=
state
,
,
evalResult
=
Display
evalPager
=
""
,
[
plain
$
showSDoc
flags
$
vcat
evalComms
=
[]
[
pprDynFlags
False
flags
,
pprLanguages
False
flags
]
]
,
evalState
=
state
,
evalPager
=
""
,
evalComms
=
[]
}
}
else
do
else
do
-- Apply all IHaskell flag updaters to the state to get the new state
-- Apply all IHaskell flag updaters to the state to get the new state
let
state'
=
(
foldl'
(
.
)
id
(
map
(
fromJust
.
ihaskellFlagUpdater
)
ihaskellFlags
))
state
let
state'
=
(
foldl'
(
.
)
id
(
map
(
fromJust
.
ihaskellFlagUpdater
)
ihaskellFlags
))
state
errs
<-
setFlags
ghcFlags
errs
<-
setFlags
ghcFlags
let
display
=
case
errs
of
let
display
=
case
errs
of
[]
->
mempty
[]
->
mempty
_
->
displayError
$
intercalate
"
\n
"
errs
_
->
displayError
$
intercalate
"
\n
"
errs
-- For -XNoImplicitPrelude, remove the Prelude import.
-- For -XNoImplicitPrelude, remove the Prelude import. For -XImplicitPrelude, add it back in.
-- For -XImplicitPrelude, add it back in.
if
"-XNoImplicitPrelude"
`
elem
`
flags
if
"-XNoImplicitPrelude"
`
elem
`
flags
then
evalImport
"import qualified Prelude as Prelude"
then
evalImport
"import qualified Prelude as Prelude"
else
else
when
(
"-XImplicitPrelude"
`
elem
`
flags
)
$
do
when
(
"-XImplicitPrelude"
`
elem
`
flags
)
$
do
importDecl
<-
parseImportDecl
"import Prelude"
importDecl
<-
parseImportDecl
"import Prelude"
let
implicitPrelude
=
importDecl
{
ideclImplicit
=
True
}
let
implicitPrelude
=
importDecl
{
ideclImplicit
=
True
}
imports
<-
getContext
imports
<-
getContext
setContext
$
IIDecl
implicitPrelude
:
imports
setContext
$
IIDecl
implicitPrelude
:
imports
return
EvalOut
{
return
evalStatus
=
Success
,
EvalOut
evalResult
=
display
,
{
evalStatus
=
Success
evalState
=
state'
,
,
evalResult
=
display
evalPager
=
""
,
,
evalState
=
state'
evalComms
=
[]
,
evalPager
=
""
,
evalComms
=
[]
}
}
evalCommand
output
(
Directive
SetExtension
opts
)
state
=
do
evalCommand
output
(
Directive
SetExtension
opts
)
state
=
do
...
@@ -485,8 +511,7 @@ evalCommand output (Directive LoadModule mods) state = wrapExecution state $ do
...
@@ -485,8 +511,7 @@ evalCommand output (Directive LoadModule mods) state = wrapExecution state $ do
'-'
->
(
words
remainder
,
True
)
'-'
->
(
words
remainder
,
True
)
_
->
(
words
stripped
,
False
)
_
->
(
words
stripped
,
False
)
forM_
modules
$
\
modl
->
forM_
modules
$
\
modl
->
if
removeModule
if
removeModule
then
removeImport
modl
then
removeImport
modl
else
evalImport
$
"import "
++
modl
else
evalImport
$
"import "
++
modl
...
@@ -496,25 +521,26 @@ evalCommand a (Directive SetOption opts) state = do
...
@@ -496,25 +521,26 @@ evalCommand a (Directive SetOption opts) state = do
write
state
$
"Option: "
++
opts
write
state
$
"Option: "
++
opts
let
(
existing
,
nonExisting
)
=
partition
optionExists
$
words
opts
let
(
existing
,
nonExisting
)
=
partition
optionExists
$
words
opts
if
not
$
null
nonExisting
if
not
$
null
nonExisting
then
then
let
err
=
"No such options: "
++
intercalate
", "
nonExisting
let
err
=
"No such options: "
++
intercalate
", "
nonExisting
i
n
in
retur
n
return
EvalOut
{
EvalOut
evalStatus
=
Failure
,
{
evalStatus
=
Failure
evalResult
=
displayError
err
,
,
evalResult
=
displayError
err
evalState
=
state
,
,
evalState
=
state
evalPager
=
""
,
,
evalPager
=
""
evalComms
=
[]
,
evalComms
=
[]
}
}
else
else
let
options
=
mapMaybe
findOption
$
words
opts
let
options
=
mapMaybe
findOption
$
words
opt
s
updater
=
foldl'
(
.
)
id
$
map
getUpdateKernelState
option
s
updater
=
foldl'
(
.
)
id
$
map
getUpdateKernelState
options
i
n
in
retur
n
return
EvalOut
{
EvalOut
evalStatus
=
Success
,
{
evalStatus
=
Success
evalResult
=
mempty
,
,
evalResult
=
mempty
evalState
=
updater
state
,
,
evalState
=
updater
state
evalPager
=
""
,
,
evalPager
=
""
evalComms
=
[]
,
evalComms
=
[]
}
}
where
where
optionExists
=
isJust
.
findOption
optionExists
=
isJust
.
findOption
findOption
opt
=
findOption
opt
=
...
@@ -522,7 +548,7 @@ evalCommand a (Directive SetOption opts) state = do
...
@@ -522,7 +548,7 @@ evalCommand a (Directive SetOption opts) state = do
evalCommand
_
(
Directive
GetType
expr
)
state
=
wrapExecution
state
$
do
evalCommand
_
(
Directive
GetType
expr
)
state
=
wrapExecution
state
$
do
write
state
$
"Type: "
++
expr
write
state
$
"Type: "
++
expr
formatType
<$>
((
expr
++
" :: "
)
++
)
<$>
getType
expr
formatType
<$>
((
expr
++
" :: "
)
++
)
<$>
getType
expr
evalCommand
_
(
Directive
GetKind
expr
)
state
=
wrapExecution
state
$
do
evalCommand
_
(
Directive
GetKind
expr
)
state
=
wrapExecution
state
$
do
write
state
$
"Kind: "
++
expr
write
state
$
"Kind: "
++
expr
...
@@ -546,7 +572,8 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
...
@@ -546,7 +572,8 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
"cd"
:
dirs
->
do
"cd"
:
dirs
->
do
-- Get home so we can replace '~` with it.
-- Get home so we can replace '~` with it.
homeEither
<-
liftIO
(
try
$
getEnv
"HOME"
::
IO
(
Either
SomeException
String
))
homeEither
<-
liftIO
(
try
$
getEnv
"HOME"
::
IO
(
Either
SomeException
String
))
let
home
=
case
homeEither
of
let
home
=
case
homeEither
of
Left
_
->
"~"
Left
_
->
"~"
Right
val
->
val
Right
val
->
val
...
@@ -554,8 +581,8 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
...
@@ -554,8 +581,8 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
exists
<-
liftIO
$
doesDirectoryExist
directory
exists
<-
liftIO
$
doesDirectoryExist
directory
if
exists
if
exists
then
do
then
do
-- Set the directory in IHaskell native code, for future shell
-- Set the directory in IHaskell native code, for future shell commands. This doesn't set it for
-- commands. This doesn't set it for
user code, though.
--
user code, though.
liftIO
$
setCurrentDirectory
directory
liftIO
$
setCurrentDirectory
directory
-- Set the directory for user code.
-- Set the directory for user code.
...
@@ -564,21 +591,14 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
...
@@ -564,21 +591,14 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
replace
"
\"
"
"
\\\"
"
directory
replace
"
\"
"
"
\\\"
"
directory
runStmt
cmd
RunToCompletion
runStmt
cmd
RunToCompletion
return
mempty
return
mempty
else
else
return
$
displayError
$
printf
"No such directory: '%s'"
directory
return
$
displayError
$
printf
"No such directory: '%s'"
directory
cmd
->
liftIO
$
do
cmd
->
liftIO
$
do
#
if
MIN_VERSION_base
(
4
,
8
,
0
)
(
pipe
,
handle
)
<-
createPipe'
(
pipe
,
handle
)
<-
createPipe
#
else
(
readEnd
,
writeEnd
)
<-
createPipe
handle
<-
fdToHandle
writeEnd
pipe
<-
fdToHandle
readEnd
#
endif
let
initProcSpec
=
shell
$
unwords
cmd
let
initProcSpec
=
shell
$
unwords
cmd
procSpec
=
initProcSpec
{
procSpec
=
initProcSpec
std_in
=
Inherit
,
{
std_in
=
Inherit
std_out
=
UseHandle
handle
,
,
std_out
=
UseHandle
handle
std_err
=
UseHandle
handle
,
std_err
=
UseHandle
handle
}
}
(
_
,
_
,
_
,
process
)
<-
createProcess
procSpec
(
_
,
_
,
_
,
process
)
<-
createProcess
procSpec
...
@@ -587,8 +607,8 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
...
@@ -587,8 +607,8 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
-- Start a loop to publish intermediate results.
-- Start a loop to publish intermediate results.
let
let
-- Compute how long to wait between reading pieces of the output.
-- Compute how long to wait between reading pieces of the output. `threadDelay` takes an
-- `threadDelay` takes an
argument of microseconds.
--
argument of microseconds.
ms
=
1000
ms
=
1000
delay
=
100
*
ms
delay
=
100
*
ms
...
@@ -625,43 +645,56 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
...
@@ -625,43 +645,56 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
ExitFailure
code
->
do
ExitFailure
code
->
do
let
errMsg
=
"Process exited with error code "
++
show
code
let
errMsg
=
"Process exited with error code "
++
show
code
htmlErr
=
printf
"<span class='err-msg'>%s</span>"
errMsg
htmlErr
=
printf
"<span class='err-msg'>%s</span>"
errMsg
return
$
Display
[
plain
$
out
++
"
\n
"
++
errMsg
,
return
$
Display
html
$
printf
"<span class='mono'>%s</span>"
out
++
htmlErr
]
[
plain
$
out
++
"
\n
"
++
errMsg
,
html
$
printf
"<span class='mono'>%s</span>"
out
++
htmlErr
]
loop
loop
where
#
if
MIN_VERSION_base
(
4
,
8
,
0
)
createPipe'
=
createPipe
#
else
createPipe'
=
do
(
readEnd
,
writeEnd
)
<-
createPipe
handle
<-
fdToHandle
writeEnd
pipe
<-
fdToHandle
readEnd
return
(
pipe
,
handle
)
#
endif
-- This is taken largely from GHCi's info section in InteractiveUI.
-- This is taken largely from GHCi's info section in InteractiveUI.
evalCommand
_
(
Directive
GetHelp
_
)
state
=
do
evalCommand
_
(
Directive
GetHelp
_
)
state
=
do
write
state
"Help via :help or :?."
write
state
"Help via :help or :?."
return
EvalOut
{
return
evalStatus
=
Success
,
EvalOut
evalResult
=
Display
[
out
],
{
evalStatus
=
Success
evalState
=
state
,
,
evalResult
=
Display
[
out
]
evalPager
=
""
,
,
evalState
=
state
evalComms
=
[]
,
evalPager
=
""
,
evalComms
=
[]
}
}
where
out
=
plain
$
intercalate
"
\n
"
[
"The following commands are available:"
where
,
" :extension <Extension> - Enable a GHC extension."
out
=
plain
$
intercalate
"
\n
"
,
" :extension No<Extension> - Disable a GHC extension."
[
"The following commands are available:"
,
" :type <expression> - Print expression type."
,
" :extension <Extension> - Enable a GHC extension."
,
" :info <name> - Print all info for a name."
,
" :extension No<Extension> - Disable a GHC extension."
,
" :hoogle <query> - Search for a query on Hoogle."
,
" :type <expression> - Print expression type."
,
" :doc <ident> - Get documentation for an identifier via Hogole."
,
" :info <name> - Print all info for a name."
,
" :set -XFlag -Wall - Set an option (like ghci)."
,
" :hoogle <query> - Search for a query on Hoogle."
,
" :option <opt> - Set an option."
,
" :doc <ident> - Get documentation for an identifier via Hogole."
,
" :option no-<opt> - Unset an option."
,
" :set -XFlag -Wall - Set an option (like ghci)."
,
" :?, :help - Show this help text."
,
" :option <opt> - Set an option."
,
""
,
" :option no-<opt> - Unset an option."
,
"Any prefix of the commands will also suffice, e.g. use :ty for :type."
,
" :?, :help - Show this help text."
,
""
,
""
,
"Options:"
,
"Any prefix of the commands will also suffice, e.g. use :ty for :type."
,
" lint – enable or disable linting."
,
""
,
" svg – use svg output (cannot be resized)."
,
"Options:"
,
" show-types – show types of all bound names"
,
" lint – enable or disable linting."
,
" show-errors – display Show instance missing errors normally."
,
" svg – use svg output (cannot be resized)."
,
" pager – use the pager to display results of :info, :doc, :hoogle, etc."
,
" show-types – show types of all bound names"
,
" show-errors – display Show instance missing errors normally."
,
" pager – use the pager to display results of :info, :doc, :hoogle, etc."
]
]
-- This is taken largely from GHCi's info section in InteractiveUI.
-- This is taken largely from GHCi's info section in InteractiveUI.
...
@@ -673,17 +706,20 @@ evalCommand _ (Directive GetInfo str) state = safely state $ do
...
@@ -673,17 +706,20 @@ evalCommand _ (Directive GetInfo str) state = safely state $ do
-- TODO: Make pager work without html by porting to newer architecture
-- TODO: Make pager work without html by porting to newer architecture
let
output
=
unlines
(
map
htmlify
strings
)
let
output
=
unlines
(
map
htmlify
strings
)
htmlify
str
=
htmlify
str
=
printf
"<div style='background: rgb(247, 247, 247);'><form><textarea id='code'>%s</textarea></form></div>"
str
printf
"<div style='background: rgb(247, 247, 247);'><form><textarea id='code'>%s</textarea></form></div>"
str
++
script
++
script
script
=
script
=
"<script>CodeMirror.fromTextArea(document.getElementById('code'), {mode: 'haskell', readOnly: 'nocursor'});</script>"
"<script>CodeMirror.fromTextArea(document.getElementById('code'), {mode: 'haskell', readOnly: 'nocursor'});</script>"
return
EvalOut
{
return
evalStatus
=
Success
,
EvalOut
evalResult
=
mempty
,
{
evalStatus
=
Success
evalState
=
state
,
,
evalResult
=
mempty
evalPager
=
output
,
,
evalState
=
state
evalComms
=
[]
,
evalPager
=
output
,
evalComms
=
[]
}
}
evalCommand
_
(
Directive
SearchHoogle
query
)
state
=
safely
state
$
do
evalCommand
_
(
Directive
SearchHoogle
query
)
state
=
safely
state
$
do
...
@@ -711,8 +747,7 @@ evalCommand output (Statement stmt) state = wrapExecution state $ do
...
@@ -711,8 +747,7 @@ evalCommand output (Statement stmt) state = wrapExecution state $ do
write
state
$
"Names: "
++
show
allNames
write
state
$
"Names: "
++
show
allNames
-- Display the types of all bound names if the option is on.
-- Display the types of all bound names if the option is on. This is similar to GHCi :set +t.
-- This is similar to GHCi :set +t.
if
not
$
useShowTypes
state
if
not
$
useShowTypes
state
then
return
$
Display
output
then
return
$
Display
output
else
do
else
do
...
@@ -724,14 +759,12 @@ evalCommand output (Statement stmt) state = wrapExecution state $ do
...
@@ -724,14 +759,12 @@ evalCommand output (Statement stmt) state = wrapExecution state $ do
let
joined
=
unlines
types
let
joined
=
unlines
types
htmled
=
unlines
$
map
formatGetType
types
htmled
=
unlines
$
map
formatGetType
types
return
$
case
extractPlain
output
of
return
$
case
extractPlain
output
of
""
->
Display
[
html
htmled
]
""
->
Display
[
html
htmled
]
-- Return plain and html versions.
-- Return plain and html versions. Previously there was only a plain version.
-- Previously there was only a plain version.
text
->
Display
[
plain
$
joined
++
"
\n
"
++
text
,
html
$
htmled
++
mono
text
]
text
->
Display
[
plain
$
joined
++
"
\n
"
++
text
,
html
$
htmled
++
mono
text
]
RunException
exception
->
throw
exception
RunException
exception
->
throw
exception
RunBreak
{}
->
error
"Should not break."
RunBreak
{}
->
error
"Should not break."
...
@@ -739,10 +772,9 @@ evalCommand output (Statement stmt) state = wrapExecution state $ do
...
@@ -739,10 +772,9 @@ evalCommand output (Statement stmt) state = wrapExecution state $ do
evalCommand
output
(
Expression
expr
)
state
=
do
evalCommand
output
(
Expression
expr
)
state
=
do
write
state
$
"Expression:
\n
"
++
expr
write
state
$
"Expression:
\n
"
++
expr
-- Try to use `display` to convert our type into the output
-- Try to use `display` to convert our type into the output Dislay If typechecking fails and there
-- Dislay If typechecking fails and there is no appropriate
-- is no appropriate typeclass instance, this will throw an exception and thus `attempt` will return
-- typeclass instance, this will throw an exception and thus `attempt` will
-- False, and we just resort to plaintext.
-- return False, and we just resort to plaintext.
let
displayExpr
=
printf
"(IHaskell.Display.display (%s))"
expr
::
String
let
displayExpr
=
printf
"(IHaskell.Display.display (%s))"
expr
::
String
canRunDisplay
<-
attempt
$
exprType
displayExpr
canRunDisplay
<-
attempt
$
exprType
displayExpr
...
@@ -751,7 +783,7 @@ evalCommand output (Expression expr) state = do
...
@@ -751,7 +783,7 @@ evalCommand output (Expression expr) state = do
isWidget
<-
attempt
$
exprType
widgetExpr
isWidget
<-
attempt
$
exprType
widgetExpr
-- Check if this is a template haskell declaration
-- Check if this is a template haskell declaration
let
declExpr
=
printf
"((id :: IHaskellTH.DecsQ -> IHaskellTH.DecsQ) (%s))"
expr
::
String
let
declExpr
=
printf
"((id :: IHaskellTH.DecsQ -> IHaskellTH.DecsQ) (%s))"
expr
::
String
let
anyExpr
=
printf
"((id :: IHaskellPrelude.Int -> IHaskellPrelude.Int) (%s))"
expr
::
String
let
anyExpr
=
printf
"((id :: IHaskellPrelude.Int -> IHaskellPrelude.Int) (%s))"
expr
::
String
isTHDeclaration
<-
liftM2
(
&&
)
(
attempt
$
exprType
declExpr
)
(
not
<$>
attempt
(
exprType
anyExpr
))
isTHDeclaration
<-
liftM2
(
&&
)
(
attempt
$
exprType
declExpr
)
(
not
<$>
attempt
(
exprType
anyExpr
))
...
@@ -760,17 +792,19 @@ evalCommand output (Expression expr) state = do
...
@@ -760,17 +792,19 @@ evalCommand output (Expression expr) state = do
write
state
$
"Is Declaration: "
++
show
isTHDeclaration
write
state
$
"Is Declaration: "
++
show
isTHDeclaration
if
isTHDeclaration
if
isTHDeclaration
-- If it typechecks as a DecsQ, we do not want to display the DecsQ,
then
-- we just want the declaration made.
-- If it typechecks as a DecsQ, we do not want to display the DecsQ, we just want the
then
do
-- declaration made.
do
write
state
$
"Suppressing display for template haskell declaration"
write
state
$
"Suppressing display for template haskell declaration"
GHC
.
runDecls
expr
GHC
.
runDecls
expr
return
EvalOut
{
return
evalStatus
=
Success
,
EvalOut
evalResult
=
mempty
,
{
evalStatus
=
Success
evalState
=
state
,
,
evalResult
=
mempty
evalPager
=
""
,
,
evalState
=
state
evalComms
=
[]
,
evalPager
=
""
,
evalComms
=
[]
}
}
else
do
else
do
if
canRunDisplay
if
canRunDisplay
...
@@ -783,36 +817,37 @@ evalCommand output (Expression expr) state = do
...
@@ -783,36 +817,37 @@ evalCommand output (Expression expr) state = do
then
registerWidget
out
then
registerWidget
out
else
return
out
else
return
out
else
do
else
do
-- Evaluate this expression as though it's just a statement.
-- Evaluate this expression as though it's just a statement. The output is bound to 'it', so we can
-- The output is bound to 'it', so we can
then use it.
--
then use it.
evalOut
<-
evalCommand
output
(
Statement
expr
)
state
evalOut
<-
evalCommand
output
(
Statement
expr
)
state
let
out
=
evalResult
evalOut
let
out
=
evalResult
evalOut
showErr
=
isShowError
out
showErr
=
isShowError
out
-- If evaluation failed, return the failure. If it was successful, w
e
-- If evaluation failed, return the failure. If it was successful, we may be able to use th
e
-- may be able to use the
IHaskellDisplay typeclass.
--
IHaskellDisplay typeclass.
return
$
if
not
showErr
||
useShowErrors
state
return
$
if
not
showErr
||
useShowErrors
state
then
evalOut
then
evalOut
else
postprocessShowError
evalOut
else
postprocessShowError
evalOut
where
where
-- Try to evaluate an action. Return True if it succeeds and False if
-- Try to evaluate an action. Return True if it succeeds and False if
it throws an exception. The
--
it throws an exception. The
result of the action is discarded.
-- result of the action is discarded.
attempt
::
Interpreter
a
->
Interpreter
Bool
attempt
::
Interpreter
a
->
Interpreter
Bool
attempt
action
=
gcatch
(
action
>>
return
True
)
failure
attempt
action
=
gcatch
(
action
>>
return
True
)
failure
where
failure
::
SomeException
->
Interpreter
Bool
where
failure
::
SomeException
->
Interpreter
Bool
failure
_
=
return
False
failure
_
=
return
False
-- Check if the error is due to trying to print something that doesn't
-- Check if the error is due to trying to print something that doesn't implement the Show typeclass.
-- implement the Show typeclass.
isShowError
(
ManyDisplay
_
)
=
False
isShowError
(
ManyDisplay
_
)
=
False
isShowError
(
Display
errs
)
=
isShowError
(
Display
errs
)
=
-- Note that we rely on this error message being 'type cleaned', so
-- Note that we rely on this error message being 'type cleaned', so that `Show` is not displayed as
-- that `Show` is not displayed as
GHC.Show.Show. This is also very fragile!
--
GHC.Show.Show. This is also very fragile!
startswith
"No instance for (Show"
msg
&&
startswith
"No instance for (Show"
msg
&&
isInfixOf
"print it"
msg
isInfixOf
"print it"
msg
where
msg
=
extractPlain
errs
where
msg
=
extractPlain
errs
isSvg
(
DisplayData
mime
_
)
=
mime
==
MimeSvg
isSvg
(
DisplayData
mime
_
)
=
mime
==
MimeSvg
...
@@ -821,16 +856,12 @@ evalCommand output (Expression expr) state = do
...
@@ -821,16 +856,12 @@ evalCommand output (Expression expr) state = do
removeSvg
(
ManyDisplay
disps
)
=
ManyDisplay
$
map
removeSvg
disps
removeSvg
(
ManyDisplay
disps
)
=
ManyDisplay
$
map
removeSvg
disps
useDisplay
displayExpr
=
do
useDisplay
displayExpr
=
do
-- If there are instance matches, convert the object into
-- If there are instance matches, convert the object into a Display. We also serialize it into a
-- a Display. We also serialize it into a bytestring. We get
-- bytestring. We get the bytestring IO action as a dynamic and then convert back to a bytestring,
-- the bytestring IO action as a dynamic and then convert back to
-- which we promptly unserialize. Note that attempting to do this without the serialization to
-- a bytestring, which we promptly unserialize. Note that
-- binary and back gives very strange errors - all the types match but it refuses to decode back
-- attempting to do this without the serialization to binary and
-- into a Display. Suppress output, so as not to mess up console. First, evaluate the expression in
-- back gives very strange errors - all the types match but it
-- such a way that we have access to `it`.
-- refuses to decode back into a Display.
-- Suppress output, so as not to mess up console.
-- First, evaluate the expression in such a way that we have access to `it`.
io
<-
isIO
expr
io
<-
isIO
expr
let
stmtTemplate
=
if
io
let
stmtTemplate
=
if
io
then
"it <- (%s)"
then
"it <- (%s)"
...
@@ -872,9 +903,10 @@ evalCommand output (Expression expr) state = do
...
@@ -872,9 +903,10 @@ evalCommand output (Expression expr) state = do
state'
=
state
{
openComms
=
newComms
}
state'
=
state
{
openComms
=
newComms
}
-- Store the fact that we should start this comm.
-- Store the fact that we should start this comm.
return
evalOut
{
return
evalComms
=
CommInfo
widget
uuid
(
targetName
widget
)
:
evalComms
evalOut
,
evalOut
evalState
=
state'
{
evalComms
=
CommInfo
widget
uuid
(
targetName
widget
)
:
evalComms
evalOut
,
evalState
=
state'
}
}
isIO
expr
=
attempt
$
exprType
$
printf
"((
\\
x -> x) :: IO a -> IO a) (%s)"
expr
isIO
expr
=
attempt
$
exprType
$
printf
"((
\\
x -> x) :: IO a -> IO a) (%s)"
expr
...
@@ -885,16 +917,21 @@ evalCommand output (Expression expr) state = do
...
@@ -885,16 +917,21 @@ evalCommand output (Expression expr) state = do
Display
disps
=
evalResult
evalOut
Display
disps
=
evalResult
evalOut
text
=
extractPlain
disps
text
=
extractPlain
disps
postprocess
(
DisplayData
MimeHtml
_
)
=
html
$
printf
fmt
unshowableType
(
formatErrorWithClass
"err-msg collapse"
text
)
script
postprocess
(
DisplayData
MimeHtml
_
)
=
html
$
printf
fmt
unshowableType
(
formatErrorWithClass
"err-msg collapse"
text
)
script
where
where
fmt
=
"<div class='collapse-group'><span class='btn btn-default' href='#' id='unshowable'>Unshowable:<span class='show-type'>%s</span></span>%s</div><script>%s</script>"
fmt
=
"<div class='collapse-group'><span class='btn btn-default' href='#' id='unshowable'>Unshowable:<span class='show-type'>%s</span></span>%s</div><script>%s</script>"
script
=
unlines
[
script
=
unlines
"$('#unshowable').on('click', function(e) {"
,
[
"$('#unshowable').on('click', function(e) {"
" e.preventDefault();"
,
,
" e.preventDefault();"
" var $this = $(this);"
,
,
" var $this = $(this);"
" var $collapse = $this.closest('.collapse-group').find('.err-msg');"
,
,
" var $collapse = $this.closest('.collapse-group').find('.err-msg');"
" $collapse.collapse('toggle');"
,
,
" $collapse.collapse('toggle');"
"});"
,
"});"
]
]
postprocess
other
=
other
postprocess
other
=
other
...
@@ -910,14 +947,12 @@ evalCommand output (Expression expr) state = do
...
@@ -910,14 +947,12 @@ evalCommand output (Expression expr) state = do
else
after
else
after
evalCommand
_
(
Declaration
decl
)
state
=
wrapExecution
state
$
do
evalCommand
_
(
Declaration
decl
)
state
=
wrapExecution
state
$
do
write
state
$
"Declaration:
\n
"
++
decl
write
state
$
"Declaration:
\n
"
++
decl
boundNames
<-
evalDeclarations
decl
boundNames
<-
evalDeclarations
decl
let
nonDataNames
=
filter
(
not
.
isUpper
.
head
)
boundNames
let
nonDataNames
=
filter
(
not
.
isUpper
.
head
)
boundNames
-- Display the types of all bound names if the option is on.
-- Display the types of all bound names if the option is on. This is similar to GHCi :set +t.
-- This is similar to GHCi :set +t.
if
not
$
useShowTypes
state
if
not
$
useShowTypes
state
then
return
mempty
then
return
mempty
else
do
else
do
...
@@ -930,53 +965,50 @@ evalCommand _ (Declaration decl) state = wrapExecution state $ do
...
@@ -930,53 +965,50 @@ evalCommand _ (Declaration decl) state = wrapExecution state $ do
return
$
Display
[
html
$
unlines
$
map
formatGetType
types
]
return
$
Display
[
html
$
unlines
$
map
formatGetType
types
]
evalCommand
_
(
TypeSignature
sig
)
state
=
wrapExecution
state
$
evalCommand
_
(
TypeSignature
sig
)
state
=
wrapExecution
state
$
-- We purposefully treat this as a "success" because that way execution
-- We purposefully treat this as a "success" because that way execution continues. Empty type
-- continues. Empty type signatures are likely due to a parse error later
-- signatures are likely due to a parse error later on, and we want that to be displayed.
-- on, and we want that to be displayed.
return
$
displayError
$
"The type signature "
++
sig
++
"
\n
lacks an accompanying binding."
return
$
displayError
$
"The type signature "
++
sig
++
"
\n
lacks an accompanying binding."
evalCommand
_
(
ParseError
loc
err
)
state
=
do
evalCommand
_
(
ParseError
loc
err
)
state
=
do
write
state
"Parse Error."
write
state
"Parse Error."
return
EvalOut
{
return
evalStatus
=
Failure
,
EvalOut
evalResult
=
displayError
$
formatParseError
loc
err
,
{
evalStatus
=
Failure
evalState
=
state
,
,
evalResult
=
displayError
$
formatParseError
loc
err
evalPager
=
""
,
,
evalState
=
state
evalComms
=
[]
,
evalPager
=
""
,
evalComms
=
[]
}
}
evalCommand
_
(
Pragma
(
PragmaUnsupported
pragmaType
)
pragmas
)
state
=
wrapExecution
state
$
evalCommand
_
(
Pragma
(
PragmaUnsupported
pragmaType
)
pragmas
)
state
=
wrapExecution
state
$
return
$
displayError
$
"Pragmas of type "
++
pragmaType
++
return
$
displayError
$
"Pragmas of type "
++
pragmaType
++
"
\n
are not supported."
"
\n
are not supported."
evalCommand
output
(
Pragma
PragmaLanguage
pragmas
)
state
=
do
evalCommand
output
(
Pragma
PragmaLanguage
pragmas
)
state
=
do
write
state
$
"Got LANGUAGE pragma "
++
show
pragmas
write
state
$
"Got LANGUAGE pragma "
++
show
pragmas
evalCommand
output
(
Directive
SetExtension
$
unwords
pragmas
)
state
evalCommand
output
(
Directive
SetExtension
$
unwords
pragmas
)
state
hoogleResults
::
KernelState
->
[
Hoogle
.
HoogleResult
]
->
EvalOut
hoogleResults
::
KernelState
->
[
Hoogle
.
HoogleResult
]
->
EvalOut
hoogleResults
state
results
=
EvalOut
{
hoogleResults
state
results
=
evalStatus
=
Success
,
EvalOut
evalResult
=
mempty
,
{
evalStatus
=
Success
evalState
=
state
,
,
evalResult
=
mempty
evalPager
=
output
,
,
evalState
=
state
evalComms
=
[]
,
evalPager
=
output
,
evalComms
=
[]
}
}
where
where
-- TODO: Make pager work with plaintext
-- TODO: Make pager work with plaintext
fmt
=
Hoogle
.
HTML
fmt
=
Hoogle
.
HTML
output
=
unlines
$
map
(
Hoogle
.
render
fmt
)
results
output
=
unlines
$
map
(
Hoogle
.
render
fmt
)
results
-- Read from a file handle until we hit a delimiter or until we've read
-- Read from a file handle until we hit a delimiter or until we've read
as many characters as
--
as many characters as
requested
-- requested
readChars
::
Handle
->
String
->
Int
->
IO
String
readChars
::
Handle
->
String
->
Int
->
IO
String
-- If we're done reading, return nothing.
-- If we're done reading, return nothing.
readChars
handle
delims
0
=
return
[]
readChars
handle
delims
0
=
return
[]
readChars
handle
delims
nchars
=
do
readChars
handle
delims
nchars
=
do
-- Try reading a single character. It will throw an exception if the
-- Try reading a single character. It will throw an exception if the handle is already closed.
-- handle is already closed.
tryRead
<-
gtry
$
hGetChar
handle
::
IO
(
Either
SomeException
Char
)
tryRead
<-
gtry
$
hGetChar
handle
::
IO
(
Either
SomeException
Char
)
case
tryRead
of
case
tryRead
of
Right
char
->
Right
char
->
...
@@ -985,11 +1017,10 @@ readChars handle delims nchars = do
...
@@ -985,11 +1017,10 @@ readChars handle delims nchars = do
then
return
[
char
]
then
return
[
char
]
else
do
else
do
next
<-
readChars
handle
delims
(
nchars
-
1
)
next
<-
readChars
handle
delims
(
nchars
-
1
)
return
$
char
:
next
return
$
char
:
next
-- An error occurs at the end of the stream, so just stop reading.
-- An error occurs at the end of the stream, so just stop reading.
Left
_
->
return
[]
Left
_
->
return
[]
doLoadModule
::
String
->
String
->
Ghc
Display
doLoadModule
::
String
->
String
->
Ghc
Display
doLoadModule
name
modName
=
do
doLoadModule
name
modName
=
do
-- Remember which modules we've loaded before.
-- Remember which modules we've loaded before.
...
@@ -998,13 +1029,7 @@ doLoadModule name modName = do
...
@@ -998,13 +1029,7 @@ doLoadModule name modName = do
flip
gcatch
(
unload
importedModules
)
$
do
flip
gcatch
(
unload
importedModules
)
$
do
-- Compile loaded modules.
-- Compile loaded modules.
flags
<-
getSessionDynFlags
flags
<-
getSessionDynFlags
#
if
MIN_VERSION_ghc
(
7
,
8
,
0
)
setSessionDynFlags
flags
{
hscTarget
=
objTarget
flags
}
let
objTarget
=
defaultObjectTarget
platform
platform
=
targetPlatform
flags
#
else
let
objTarget
=
defaultObjectTarget
#
endif
setSessionDynFlags
flags
{
hscTarget
=
objTarget
}
-- Clear old targets to be sure.
-- Clear old targets to be sure.
setTargets
[]
setTargets
[]
...
@@ -1025,11 +1050,12 @@ doLoadModule name modName = do
...
@@ -1025,11 +1050,12 @@ doLoadModule name modName = do
-- Switch back to interpreted mode.
-- Switch back to interpreted mode.
flags
<-
getSessionDynFlags
flags
<-
getSessionDynFlags
setSessionDynFlags
flags
{
hscTarget
=
HscInterpreted
}
setSessionDynFlags
flags
{
hscTarget
=
HscInterpreted
}
case
result
of
case
result
of
Succeeded
->
return
mempty
Succeeded
->
return
mempty
Failed
->
return
$
displayError
$
"Failed to load module "
++
modName
Failed
->
return
$
displayError
$
"Failed to load module "
++
modName
where
where
unload
::
[
InteractiveImport
]
->
SomeException
->
Ghc
Display
unload
::
[
InteractiveImport
]
->
SomeException
->
Ghc
Display
unload
imported
exception
=
do
unload
imported
exception
=
do
...
@@ -1040,14 +1066,18 @@ doLoadModule name modName = do
...
@@ -1040,14 +1066,18 @@ doLoadModule name modName = do
-- Switch to interpreted mode!
-- Switch to interpreted mode!
flags
<-
getSessionDynFlags
flags
<-
getSessionDynFlags
setSessionDynFlags
flags
{
hscTarget
=
HscInterpreted
}
setSessionDynFlags
flags
{
hscTarget
=
HscInterpreted
}
-- Return to old context, make sure we have `it`.
-- Return to old context, make sure we have `it`.
setContext
imported
setContext
imported
initializeItVariable
initializeItVariable
return
$
displayError
$
"Failed to load module "
++
modName
++
": "
++
show
exception
return
$
displayError
$
"Failed to load module "
++
modName
++
": "
++
show
exception
#
if
MIN_VERSION_ghc
(
7
,
8
,
0
)
objTarget
flags
=
defaultObjectTarget
$
targetPlatform
flags
#
else
objTarget
flags
=
defaultObjectTarget
#
endif
keepingItVariable
::
Interpreter
a
->
Interpreter
a
keepingItVariable
::
Interpreter
a
->
Interpreter
a
keepingItVariable
act
=
do
keepingItVariable
act
=
do
-- Generate the it variable temp name
-- Generate the it variable temp name
...
@@ -1066,8 +1096,8 @@ capturedStatement :: (String -> IO ()) -- ^ Function used to publish int
...
@@ -1066,8 +1096,8 @@ capturedStatement :: (String -> IO ()) -- ^ Function used to publish int
->
String
-- ^ Statement to evaluate.
->
String
-- ^ Statement to evaluate.
->
Interpreter
(
String
,
RunResult
)
-- ^ Return the output and result.
->
Interpreter
(
String
,
RunResult
)
-- ^ Return the output and result.
capturedStatement
output
stmt
=
do
capturedStatement
output
stmt
=
do
-- Generate random variable names to use so that we cannot accidentally
-- Generate random variable names to use so that we cannot accidentally
override the variables by
--
override the variables by
using the right names in the terminal.
-- using the right names in the terminal.
gen
<-
liftIO
getStdGen
gen
<-
liftIO
getStdGen
let
let
-- Variable names generation.
-- Variable names generation.
...
@@ -1112,32 +1142,27 @@ capturedStatement output stmt = do
...
@@ -1112,32 +1142,27 @@ capturedStatement output stmt = do
-- Initialize evaluation context.
-- Initialize evaluation context.
void
$
forM
initStmts
goStmt
void
$
forM
initStmts
goStmt
-- Get the pipe to read printed output from.
-- Get the pipe to read printed output from. This is effectively the source code of dynCompileExpr
-- This is effectively the source code of dynCompileExpr from GHC API's
-- from GHC API's InteractiveEval. However, instead of using a `Dynamic` as an intermediary, it just
-- InteractiveEval. However, instead of using a `Dynamic` as an
-- directly reads the value. This is incredibly unsafe! However, for some reason the `getContext`
-- intermediary, it just directly reads the value. This is incredibly
-- and `setContext` required by dynCompileExpr (to import and clear Data.Dynamic) cause issues with
-- unsafe! However, for some reason the `getContext` and `setContext`
-- data declarations being updated (e.g. it drops newer versions of data declarations for older ones
-- required by dynCompileExpr (to import and clear Data.Dynamic) cause
-- for unknown reasons). First, compile down to an HValue.
-- issues with data declarations being updated (e.g. it drops newer
-- versions of data declarations for older ones for unknown reasons).
-- First, compile down to an HValue.
Just
(
_
,
hValues
,
_
)
<-
withSession
$
liftIO
.
flip
hscStmt
pipeExpr
Just
(
_
,
hValues
,
_
)
<-
withSession
$
liftIO
.
flip
hscStmt
pipeExpr
-- Then convert the HValue into an executable bit, and read the value.
-- Then convert the HValue into an executable bit, and read the value.
pipe
<-
liftIO
$
do
pipe
<-
liftIO
$
do
fd
<-
head
<$>
unsafeCoerce
hValues
fd
<-
head
<$>
unsafeCoerce
hValues
fdToHandle
fd
fdToHandle
fd
-- Read from a file handle until we hit a delimiter or until we've read
-- Read from a file handle until we hit a delimiter or until we've read as many characters as
-- as many characters as requested
-- requested
let
let
readChars
::
Handle
->
String
->
Int
->
IO
String
readChars
::
Handle
->
String
->
Int
->
IO
String
-- If we're done reading, return nothing.
-- If we're done reading, return nothing.
readChars
handle
delims
0
=
return
[]
readChars
handle
delims
0
=
return
[]
readChars
handle
delims
nchars
=
do
readChars
handle
delims
nchars
=
do
-- Try reading a single character. It will throw an exception if the
-- Try reading a single character. It will throw an exception if the handle is already closed.
-- handle is already closed.
tryRead
<-
gtry
$
hGetChar
handle
::
IO
(
Either
SomeException
Char
)
tryRead
<-
gtry
$
hGetChar
handle
::
IO
(
Either
SomeException
Char
)
case
tryRead
of
case
tryRead
of
Right
char
->
Right
char
->
...
@@ -1146,7 +1171,7 @@ capturedStatement output stmt = do
...
@@ -1146,7 +1171,7 @@ capturedStatement output stmt = do
then
return
[
char
]
then
return
[
char
]
else
do
else
do
next
<-
readChars
handle
delims
(
nchars
-
1
)
next
<-
readChars
handle
delims
(
nchars
-
1
)
return
$
char
:
next
return
$
char
:
next
-- An error occurs at the end of the stream, so just stop reading.
-- An error occurs at the end of the stream, so just stop reading.
Left
_
->
return
[]
Left
_
->
return
[]
...
@@ -1158,8 +1183,8 @@ capturedStatement output stmt = do
...
@@ -1158,8 +1183,8 @@ capturedStatement output stmt = do
-- Start a loop to publish intermediate results.
-- Start a loop to publish intermediate results.
let
let
-- Compute how long to wait between reading pieces of the output.
-- Compute how long to wait between reading pieces of the output. `threadDelay` takes an
-- `threadDelay` takes an
argument of microseconds.
--
argument of microseconds.
ms
=
1000
ms
=
1000
delay
=
100
*
ms
delay
=
100
*
ms
...
@@ -1200,9 +1225,8 @@ capturedStatement output stmt = do
...
@@ -1200,9 +1225,8 @@ capturedStatement output stmt = do
-- Finalize evaluation context.
-- Finalize evaluation context.
void
$
forM
postStmts
goStmt
void
$
forM
postStmts
goStmt
-- Once context is finalized, reading can finish.
-- Once context is finalized, reading can finish. Wait for reading to finish to that the output
-- Wait for reading to finish to that the output accumulator is
-- accumulator is completely filled.
-- completely filled.
liftIO
$
takeMVar
finishedReading
liftIO
$
takeMVar
finishedReading
printedOutput
<-
liftIO
$
readMVar
outputAccum
printedOutput
<-
liftIO
$
readMVar
outputAccum
...
@@ -1228,7 +1252,6 @@ formatErrorWithClass cls =
...
@@ -1228,7 +1252,6 @@ formatErrorWithClass cls =
startswith
"No instance for (Show"
err
&&
startswith
"No instance for (Show"
err
&&
isInfixOf
" arising from a use of `print'"
err
isInfixOf
" arising from a use of `print'"
err
formatParseError
::
StringLoc
->
String
->
ErrMsg
formatParseError
::
StringLoc
->
String
->
ErrMsg
formatParseError
(
Loc
line
col
)
=
formatParseError
(
Loc
line
col
)
=
printf
"Parse error (line %d, column %d): %s"
line
col
printf
"Parse error (line %d, column %d): %s"
line
col
...
...
src/IHaskell/Eval/Hoogle.hs
View file @
7ba7c4d1
{-# LANGUAGE NoImplicitPrelude, FlexibleInstances, OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude, FlexibleInstances, OverloadedStrings #-}
module
IHaskell.Eval.Hoogle
(
module
IHaskell.Eval.Hoogle
(
search
,
search
,
document
,
document
,
render
,
render
,
OutputFormat
(
..
),
OutputFormat
(
..
),
HoogleResult
HoogleResult
,
)
where
)
where
import
ClassyPrelude
hiding
(
last
,
span
,
div
)
import
ClassyPrelude
hiding
(
last
,
span
,
div
)
...
@@ -22,19 +23,13 @@ import qualified Prelude as P
...
@@ -22,19 +23,13 @@ import qualified Prelude as P
import
IHaskell.IPython
import
IHaskell.IPython
-- | Types of formats to render output to.
-- | Types of formats to render output to.
data
OutputFormat
data
OutputFormat
=
Plain
-- ^ Render to plain text.
=
Plain
-- ^ Render to plain text.
|
HTML
-- ^ Render to HTML.
|
HTML
-- ^ Render to HTML.
data
HoogleResponse
=
HoogleResponse
{
data
HoogleResponse
=
HoogleResponse
{
location
::
String
,
self
::
String
,
docs
::
String
}
location
::
String
,
self
::
String
,
docs
::
String
}
deriving
(
Eq
,
Show
)
deriving
(
Eq
,
Show
)
data
HoogleResult
data
HoogleResult
=
SearchResult
HoogleResponse
=
SearchResult
HoogleResponse
|
DocResult
HoogleResponse
|
DocResult
HoogleResponse
|
NoResult
String
|
NoResult
String
deriving
Show
deriving
Show
...
@@ -48,23 +43,21 @@ instance FromJSON [HoogleResponse] where
...
@@ -48,23 +43,21 @@ instance FromJSON [HoogleResponse] where
instance
FromJSON
HoogleResponse
where
instance
FromJSON
HoogleResponse
where
parseJSON
(
Object
obj
)
=
parseJSON
(
Object
obj
)
=
HoogleResponse
<$>
HoogleResponse
<$>
obj
.:
"location"
<*>
obj
.:
"self"
<*>
obj
.:
"docs"
obj
.:
"location"
<*>
obj
.:
"self"
<*>
obj
.:
"docs"
parseJSON
_
=
fail
"Expected object with fields: location, self, docs"
parseJSON
_
=
fail
"Expected object with fields: location, self, docs"
-- | Query Hoogle for the given string.
-- | Query Hoogle for the given string. This searches Hoogle using the internet. It returns either
-- This searches Hoogle using the internet. It returns either an error
-- an error message or the successful JSON result.
-- message or the successful JSON result.
query
::
String
->
IO
(
Either
String
String
)
query
::
String
->
IO
(
Either
String
String
)
query
str
=
do
query
str
=
do
request
<-
parseUrl
$
queryUrl
$
urlEncode
str
request
<-
parseUrl
$
queryUrl
$
urlEncode
str
response
<-
try
$
withManager
tlsManagerSettings
$
httpLbs
request
response
<-
try
$
withManager
tlsManagerSettings
$
httpLbs
request
return
$
case
response
of
return
$
case
response
of
Left
err
->
Left
$
show
(
err
::
SomeException
)
Left
err
->
Left
$
show
(
err
::
SomeException
)
Right
resp
->
Right
$
Char
.
unpack
$
responseBody
resp
Right
resp
->
Right
$
Char
.
unpack
$
responseBody
resp
where
where
queryUrl
::
String
->
String
queryUrl
::
String
->
String
queryUrl
=
printf
"https://www.haskell.org/hoogle/?hoogle=%s&mode=json"
queryUrl
=
printf
"https://www.haskell.org/hoogle/?hoogle=%s&mode=json"
...
@@ -78,27 +71,27 @@ urlEncode (ch:t)
...
@@ -78,27 +71,27 @@ urlEncode (ch:t)
|
otherwise
=
escape
(
P
.
fromEnum
ch
)
(
urlEncode
t
)
|
otherwise
=
escape
(
P
.
fromEnum
ch
)
(
urlEncode
t
)
where
where
escape
::
Int
->
String
->
String
escape
::
Int
->
String
->
String
escape
b
rs
=
'%'
:
showH
(
b
`
P
.
div
`
16
)
(
showH
(
b
`
mod
`
16
)
rs
)
escape
b
rs
=
'%'
:
showH
(
b
`
P
.
div
`
16
)
(
showH
(
b
`
mod
`
16
)
rs
)
showH
::
Int
->
String
->
String
showH
::
Int
->
String
->
String
showH
x
xs
showH
x
xs
|
x
<=
9
=
toEnum
(
o_0
+
x
)
:
xs
|
x
<=
9
=
toEnum
(
o_0
+
x
)
:
xs
|
otherwise
=
toEnum
(
o_A
+
(
x
-
10
))
:
xs
|
otherwise
=
toEnum
(
o_A
+
(
x
-
10
))
:
xs
where
where
o_0
=
P
.
fromEnum
'0'
o_0
=
P
.
fromEnum
'0'
o_A
=
P
.
fromEnum
'A'
o_A
=
P
.
fromEnum
'A'
eightBs
::
[
Int
]
->
Int
->
[
Int
]
eightBs
::
[
Int
]
->
Int
->
[
Int
]
eightBs
acc
x
eightBs
acc
x
|
x
<=
0xff
=
(
x
:
acc
)
|
x
<=
255
=
x
:
acc
|
otherwise
=
eightBs
((
x
`
mod
`
256
)
:
acc
)
(
x
`
P
.
div
`
256
)
|
otherwise
=
eightBs
((
x
`
mod
`
256
)
:
acc
)
(
x
`
P
.
div
`
256
)
-- | Search for a query on Hoogle.
-- | Search for a query on Hoogle. Return all search results.
-- Return all search results.
search
::
String
->
IO
[
HoogleResult
]
search
::
String
->
IO
[
HoogleResult
]
search
string
=
do
search
string
=
do
response
<-
query
string
response
<-
query
string
return
$
case
response
of
return
$
case
response
of
Left
err
->
[
NoResult
err
]
Left
err
->
[
NoResult
err
]
Right
json
->
Right
json
->
case
eitherDecode
$
Char
.
pack
json
of
case
eitherDecode
$
Char
.
pack
json
of
...
@@ -108,16 +101,17 @@ search string = do
...
@@ -108,16 +101,17 @@ search string = do
[]
->
[
NoResult
"no matching identifiers found."
]
[]
->
[
NoResult
"no matching identifiers found."
]
res
->
res
res
->
res
-- | Look up an identifier on Hoogle.
-- | Look up an identifier on Hoogle. Return documentation for that identifier. If there are many
-- Return documentation for that identifier. If there are many
-- identifiers, include documentation for all of them.
-- identifiers, include documentation for all of them.
document
::
String
->
IO
[
HoogleResult
]
document
::
String
->
IO
[
HoogleResult
]
document
string
=
do
document
string
=
do
matchingResults
<-
filter
matches
<$>
search
string
matchingResults
<-
filter
matches
<$>
search
string
let
results
=
map
toDocResult
matchingResults
let
results
=
map
toDocResult
matchingResults
return
$
case
results
of
return
$
case
results
of
[]
->
[
NoResult
"no matching identifiers found."
]
[]
->
[
NoResult
"no matching identifiers found."
]
res
->
res
res
->
res
where
where
matches
(
SearchResult
resp
)
=
matches
(
SearchResult
resp
)
=
case
split
" "
$
self
resp
of
case
split
" "
$
self
resp
of
...
@@ -134,21 +128,14 @@ render HTML = renderHtml
...
@@ -134,21 +128,14 @@ render HTML = renderHtml
-- | Render a Hoogle result to plain text.
-- | Render a Hoogle result to plain text.
renderPlain
::
HoogleResult
->
String
renderPlain
::
HoogleResult
->
String
renderPlain
(
NoResult
res
)
=
renderPlain
(
NoResult
res
)
=
"No response available: "
++
res
"No response available: "
++
res
renderPlain
(
SearchResult
resp
)
=
renderPlain
(
SearchResult
resp
)
=
printf
"%s
\n
URL: %s
\n
%s"
printf
"%s
\n
URL: %s
\n
%s"
(
self
resp
)
(
location
resp
)
(
docs
resp
)
(
self
resp
)
(
location
resp
)
(
docs
resp
)
renderPlain
(
DocResult
resp
)
=
renderPlain
(
DocResult
resp
)
=
printf
"%s
\n
URL: %s
\n
%s"
printf
"%s
\n
URL: %s
\n
%s"
(
self
resp
)
(
location
resp
)
(
docs
resp
)
(
self
resp
)
(
location
resp
)
(
docs
resp
)
-- | Render a Hoogle result to HTML.
-- | Render a Hoogle result to HTML.
renderHtml
::
HoogleResult
->
String
renderHtml
::
HoogleResult
->
String
...
@@ -167,37 +154,37 @@ renderHtml (SearchResult resp) =
...
@@ -167,37 +154,37 @@ renderHtml (SearchResult resp) =
renderSelf
::
String
->
String
->
String
renderSelf
::
String
->
String
->
String
renderSelf
string
loc
renderSelf
string
loc
|
startswith
"package"
string
|
startswith
"package"
string
=
=
pkg
++
" "
++
span
"hoogle-package"
(
link
loc
$
extractPackage
string
)
pkg
++
" "
++
span
"hoogle-package"
(
link
loc
$
extractPackage
string
)
|
startswith
"module"
string
|
startswith
"module"
string
=
=
let
package
=
extractPackageName
loc
in
let
package
=
extractPackageName
loc
mod
++
" "
++
in
mod
++
" "
++
span
"hoogle-module"
(
link
loc
$
extractModule
string
)
++
span
"hoogle-module"
(
link
loc
$
extractModule
string
)
++
packageSub
package
packageSub
package
|
startswith
"class"
string
|
startswith
"class"
string
=
=
let
package
=
extractPackageName
loc
in
let
package
=
extractPackageName
loc
cls
++
" "
++
in
cls
++
" "
++
span
"hoogle-class"
(
link
loc
$
extractClass
string
)
++
span
"hoogle-class"
(
link
loc
$
extractClass
string
)
++
packageSub
package
packageSub
package
|
startswith
"data"
string
|
startswith
"data"
string
=
=
let
package
=
extractPackageName
loc
in
let
package
=
extractPackageName
loc
dat
++
" "
++
in
dat
++
" "
++
span
"hoogle-class"
(
link
loc
$
extractData
string
)
++
span
"hoogle-class"
(
link
loc
$
extractData
string
)
++
packageSub
package
packageSub
package
|
otherwise
|
otherwise
=
=
let
[
name
,
args
]
=
split
"::"
string
let
[
name
,
args
]
=
split
"::"
string
package
=
extractPackageName
loc
package
=
extractPackageName
loc
modname
=
extractModuleName
loc
in
modname
=
extractModuleName
loc
span
"hoogle-name"
(
unicodeReplace
$
in
span
"hoogle-name"
(
unicodeReplace
$
link
loc
(
strip
name
)
++
link
loc
(
strip
name
)
++
" :: "
++
" :: "
++
strip
args
)
strip
args
)
++
packageAndModuleSub
package
modname
++
packageAndModuleSub
package
modname
where
where
extractPackage
=
strip
.
replace
"package"
""
extractPackage
=
strip
.
replace
"package"
""
extractModule
=
strip
.
replace
"module"
""
extractModule
=
strip
.
replace
"module"
""
...
@@ -239,8 +226,7 @@ renderDocs doc =
...
@@ -239,8 +226,7 @@ renderDocs doc =
if
isCode
lines
if
isCode
lines
then
div
"hoogle-code"
$
unlines
$
nonull
lines
then
div
"hoogle-code"
$
unlines
$
nonull
lines
else
div
"hoogle-text"
$
unlines
$
nonull
lines
else
div
"hoogle-text"
$
unlines
$
nonull
lines
in
in
div
"hoogle-doc"
$
unlines
$
map
makeBlock
groups
div
"hoogle-doc"
$
unlines
$
map
makeBlock
groups
extractPackageName
::
String
->
Maybe
String
extractPackageName
::
String
->
Maybe
String
extractPackageName
link
=
do
extractPackageName
link
=
do
...
...
src/IHaskell/Eval/Info.hs
View file @
7ba7c4d1
{-# LANGUAGE NoImplicitPrelude, OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude, OverloadedStrings #-}
{- | Description : Inspect type and function information and documentation.
-}
{- | Description : Inspect type and function information and documentation. -}
module
IHaskell.Eval.Info
(
module
IHaskell.Eval.Info
(
info
)
where
info
)
where
import
ClassyPrelude
hiding
(
liftIO
)
import
ClassyPrelude
hiding
(
liftIO
)
...
...
src/IHaskell/Eval/Lint.hs
View file @
7ba7c4d1
{-# LANGUAGE NoImplicitPrelude, QuasiQuotes, ViewPatterns #-}
{-# LANGUAGE NoImplicitPrelude, QuasiQuotes, ViewPatterns #-}
module
IHaskell.Eval.Lint
(
lint
module
IHaskell.Eval.Lint
(
lint
)
where
)
where
import
Data.String.Utils
(
replace
,
startswith
,
strip
,
split
)
import
Data.String.Utils
(
replace
,
startswith
,
strip
,
split
)
import
Prelude
(
head
,
tail
,
last
)
import
Prelude
(
head
,
tail
,
last
)
...
@@ -32,13 +31,13 @@ import IHaskell.Eval.Parser hiding (line)
...
@@ -32,13 +31,13 @@ import IHaskell.Eval.Parser hiding (line)
type
ExtsModule
=
SrcExts
.
Module
SrcSpanInfo
type
ExtsModule
=
SrcExts
.
Module
SrcSpanInfo
data
LintSuggestion
data
LintSuggestion
=
=
Suggest
{
Suggest
line
::
LineNumber
,
{
line
::
LineNumber
found
::
String
,
,
found
::
String
whyNot
::
String
,
,
whyNot
::
String
severity
::
Severity
,
,
severity
::
Severity
suggestion
::
String
,
suggestion
::
String
}
}
deriving
(
Eq
,
Show
)
deriving
(
Eq
,
Show
)
...
@@ -51,8 +50,8 @@ hlintSettings = unsafePerformIO newEmptyMVar
...
@@ -51,8 +50,8 @@ hlintSettings = unsafePerformIO newEmptyMVar
lintIdent
::
String
lintIdent
::
String
lintIdent
=
"lintIdentAEjlkQeh"
lintIdent
=
"lintIdentAEjlkQeh"
-- | Given parsed code chunks, perform linting and output a displayable
-- | Given parsed code chunks, perform linting and output a displayable
report on linting warnings
--
report on linting warnings
and errors.
-- and errors.
lint
::
[
Located
CodeBlock
]
->
IO
Display
lint
::
[
Located
CodeBlock
]
->
IO
Display
lint
blocks
=
do
lint
blocks
=
do
-- Initialize hlint settings
-- Initialize hlint settings
...
@@ -66,26 +65,25 @@ lint blocks = do
...
@@ -66,26 +65,25 @@ lint blocks = do
-- create 'suggestions'
-- create 'suggestions'
let
modules
=
mapMaybe
(
createModule
mode
)
blocks
let
modules
=
mapMaybe
(
createModule
mode
)
blocks
ideas
=
applyHints
classify
hint
(
map
(
\
m
->
(
m
,
[]
))
modules
)
ideas
=
applyHints
classify
hint
(
map
(
\
m
->
(
m
,
[]
))
modules
)
suggestions
=
mapMaybe
showIdea
ideas
suggestions
=
mapMaybe
showIdea
ideas
return
$
Display
$
return
$
Display
$
if
null
suggestions
if
null
suggestions
then
[]
then
[]
else
else
[
plain
$
concatMap
plainSuggestion
suggestions
,
html
$
htmlSuggestions
suggestions
]
[
plain
$
concatMap
plainSuggestion
suggestions
,
html
$
htmlSuggestions
suggestions
]
showIdea
::
Idea
->
Maybe
LintSuggestion
showIdea
::
Idea
->
Maybe
LintSuggestion
showIdea
idea
=
showIdea
idea
=
case
ideaTo
idea
of
case
ideaTo
idea
of
Nothing
->
Nothing
Nothing
->
Nothing
Just
whyNot
->
Just
Suggest
{
Just
whyNot
->
Just
line
=
srcSpanStartLine
$
ideaSpan
idea
,
Suggest
found
=
showSuggestion
$
ideaFrom
idea
,
{
line
=
srcSpanStartLine
$
ideaSpan
idea
whyNot
=
showSuggestion
whyNot
,
,
found
=
showSuggestion
$
ideaFrom
idea
severity
=
ideaSeverity
idea
,
,
whyNot
=
showSuggestion
whyNot
suggestion
=
ideaHint
idea
,
severity
=
ideaSeverity
idea
,
suggestion
=
ideaHint
idea
}
}
createModule
::
ParseMode
->
Located
CodeBlock
->
Maybe
ExtsModule
createModule
::
ParseMode
->
Located
CodeBlock
->
Maybe
ExtsModule
...
@@ -111,12 +109,12 @@ createModule mode (Located line block) =
...
@@ -111,12 +109,12 @@ createModule mode (Located line block) =
unparse
_
=
Nothing
unparse
_
=
Nothing
srcSpan
::
SrcSpan
srcSpan
::
SrcSpan
srcSpan
=
SrcSpan
{
srcSpan
=
SrcSpan
srcSpanFilename
=
"<interactive>"
,
{
srcSpanFilename
=
"<interactive>"
srcSpanStartLine
=
line
,
,
srcSpanStartLine
=
line
srcSpanStartColumn
=
0
,
,
srcSpanStartColumn
=
0
srcSpanEndLine
=
line
+
length
(
lines
blockStr
),
,
srcSpanEndLine
=
line
+
length
(
lines
blockStr
)
srcSpanEndColumn
=
length
$
last
$
lines
blockStr
,
srcSpanEndColumn
=
length
$
last
$
lines
blockStr
}
}
loc
::
SrcSpanInfo
loc
::
SrcSpanInfo
...
@@ -135,7 +133,8 @@ createModule mode (Located line block) =
...
@@ -135,7 +133,8 @@ createModule mode (Located line block) =
exprToModule
exp
=
moduleWithDecls
<$>
SpliceDecl
loc
<$>
parseExpWithMode
mode
exp
exprToModule
exp
=
moduleWithDecls
<$>
SpliceDecl
loc
<$>
parseExpWithMode
mode
exp
stmtToModule
::
String
->
ParseResult
ExtsModule
stmtToModule
::
String
->
ParseResult
ExtsModule
stmtToModule
stmtStr
=
case
parseStmtWithMode
mode
stmtStr
of
stmtToModule
stmtStr
=
case
parseStmtWithMode
mode
stmtStr
of
ParseOk
stmt
->
ParseOk
mod
ParseOk
stmt
->
ParseOk
mod
ParseFailed
a
b
->
ParseFailed
a
b
ParseFailed
a
b
->
ParseFailed
a
b
where
where
...
@@ -158,10 +157,7 @@ createModule mode (Located line block) =
...
@@ -158,10 +157,7 @@ createModule mode (Located line block) =
plainSuggestion
::
LintSuggestion
->
String
plainSuggestion
::
LintSuggestion
->
String
plainSuggestion
suggest
=
plainSuggestion
suggest
=
printf
"Line %d: %s
\n
Found:
\n
%s
\n
Why not:
\n
%s"
printf
"Line %d: %s
\n
Found:
\n
%s
\n
Why not:
\n
%s"
(
line
suggest
)
(
suggestion
suggest
)
(
found
suggest
)
(
line
suggest
)
(
suggestion
suggest
)
(
found
suggest
)
(
whyNot
suggest
)
(
whyNot
suggest
)
htmlSuggestions
::
[
LintSuggestion
]
->
String
htmlSuggestions
::
[
LintSuggestion
]
->
String
...
@@ -169,18 +165,17 @@ htmlSuggestions = concatMap toHtml
...
@@ -169,18 +165,17 @@ htmlSuggestions = concatMap toHtml
where
where
toHtml
::
LintSuggestion
->
String
toHtml
::
LintSuggestion
->
String
toHtml
suggest
=
concat
toHtml
suggest
=
concat
[
[
named
$
suggestion
suggest
named
$
suggestion
suggest
,
,
floating
"left"
$
style
severityClass
"Found:"
++
floating
"left"
$
style
severityClass
"Found:"
++
-- Things that look like this get highlighted.
-- Things that look like this get highlighted.
styleId
"highlight-code"
"haskell"
(
found
suggest
),
styleId
"highlight-code"
"haskell"
(
found
suggest
)
floating
"left"
$
style
severityClass
"Why Not:"
++
,
floating
"left"
$
style
severityClass
"Why Not:"
++
-- Things that look like this get highlighted.
-- Things that look like this get highlighted.
styleId
"highlight-code"
"haskell"
(
whyNot
suggest
)
styleId
"highlight-code"
"haskell"
(
whyNot
suggest
)
]
]
where
where
severityClass
=
case
severity
suggest
of
severityClass
=
case
severity
suggest
of
Error
->
"error"
Error
->
"error"
Warning
->
"warning"
Warning
->
"warning"
...
@@ -199,7 +194,6 @@ htmlSuggestions = concatMap toHtml
...
@@ -199,7 +194,6 @@ htmlSuggestions = concatMap toHtml
floating
::
String
->
String
->
String
floating
::
String
->
String
->
String
floating
dir
thing
=
[
i
|
<div class="suggestion-row" style="float: ${dir};">${thing}</div>
|]
floating
dir
thing
=
[
i
|
<div class="suggestion-row" style="float: ${dir};">${thing}</div>
|]
showSuggestion
::
String
->
String
showSuggestion
::
String
->
String
showSuggestion
=
remove
lintIdent
.
dropDo
showSuggestion
=
remove
lintIdent
.
dropDo
where
where
...
@@ -214,18 +208,14 @@ showSuggestion = remove lintIdent . dropDo
...
@@ -214,18 +208,14 @@ showSuggestion = remove lintIdent . dropDo
else
string
else
string
clean
::
[
String
]
->
[
String
]
clean
::
[
String
]
->
[
String
]
-- If the first line starts with a `do`...
-- If the first line starts with a `do`...
Note that hlint always indents by two spaces in its
--
Note that hlint always indents by two spaces in its
output.
-- output.
clean
((
stripPrefix
" do "
->
Just
a
)
:
as
)
=
clean
((
stripPrefix
" do "
->
Just
a
)
:
as
)
=
-- Take all indented lines and unindent them.
-- Take all indented lines and unindent them.
let
unindented
=
catMaybes
let
unindented
=
catMaybes
$
takeWhile
isJust
$
map
(
stripPrefix
" "
)
as
$
takeWhile
isJust
fullDo
=
a
:
unindented
$
map
(
stripPrefix
" "
)
as
fullDo
=
a
:
unindented
afterDo
=
drop
(
length
unindented
)
as
afterDo
=
drop
(
length
unindented
)
as
in
in
fullDo
++
clean
afterDo
--
fullDo
++
clean
afterDo
-- Ignore other list elements - just proceed onwards.
-- Ignore other list elements - just proceed onwards.
clean
(
x
:
xs
)
=
x
:
clean
xs
clean
(
x
:
xs
)
=
x
:
clean
xs
...
...
src/IHaskell/Eval/ParseShell.hs
View file @
7ba7c4d1
-- | This module splits a shell command line into a list of strings,
-- | This module splits a shell command line into a list of strings,
-- one for each command / filename
-- one for each command / filename
module
IHaskell.Eval.ParseShell
(
parseShell
)
where
module
IHaskell.Eval.ParseShell
(
parseShell
)
where
...
@@ -20,9 +19,10 @@ manyTill p end = scan
...
@@ -20,9 +19,10 @@ manyTill p end = scan
scan
=
end
<|>
do
scan
=
end
<|>
do
x
<-
p
x
<-
p
xs
<-
scan
xs
<-
scan
return
$
x
:
xs
return
$
x
:
xs
manyTill1
p
end
=
do
x
<-
p
manyTill1
p
end
=
do
x
<-
p
xs
<-
manyTill
p
end
xs
<-
manyTill
p
end
return
$
x
:
xs
return
$
x
:
xs
...
@@ -37,7 +37,8 @@ quotedString = do
...
@@ -37,7 +37,8 @@ quotedString = do
(
manyTill
anyChar
(
unescapedChar
quote
)
<*
quote
)
<?>
"unexpected in quoted String "
(
manyTill
anyChar
(
unescapedChar
quote
)
<*
quote
)
<?>
"unexpected in quoted String "
unquotedString
=
manyTill1
anyChar
end
unquotedString
=
manyTill1
anyChar
end
where
end
=
unescapedChar
space
where
end
=
unescapedChar
space
<|>
(
lookAhead
eol
>>
return
[]
)
<|>
(
lookAhead
eol
>>
return
[]
)
word
=
quotedString
<|>
unquotedString
<?>
"word"
word
=
quotedString
<|>
unquotedString
<?>
"word"
...
...
src/IHaskell/Eval/Parser.hs
View file @
7ba7c4d1
{-# LANGUAGE NoImplicitPrelude, OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude, OverloadedStrings #-}
module
IHaskell.Eval.Parser
(
module
IHaskell.Eval.Parser
(
parseString
,
parseString
,
CodeBlock
(
..
),
CodeBlock
(
..
),
...
@@ -26,28 +27,28 @@ import GHC hiding (Located)
...
@@ -26,28 +27,28 @@ import GHC hiding (Located)
import
Language.Haskell.GHC.Parser
import
Language.Haskell.GHC.Parser
import
IHaskell.Eval.Util
import
IHaskell.Eval.Util
-- | A block of code to be evaluated.
-- | A block of code to be evaluated. Each block contains a single element - one declaration,
-- Each block contains a single element - one declaration, statement,
-- statement, expression, etc. If parsing of the block failed, the block is instead a ParseError,
-- expression, etc. If parsing of the block failed, the block is instead
-- which has the error location and error message.
-- a ParseError, which has the error location and error message.
data
CodeBlock
=
Expression
String
-- ^ A Haskell expression.
data
CodeBlock
=
Expression
String
-- ^ A Haskell expression.
|
Declaration
String
-- ^ A data type or function declaration.
|
Declaration
String
-- ^ A data type or function declaration.
|
Statement
String
-- ^ A Haskell statement (as if in a `do` block).
|
Statement
String
-- ^ A Haskell statement (as if in a `do` block).
|
Import
String
-- ^ An import statement.
|
Import
String
-- ^ An import statement.
|
TypeSignature
String
-- ^ A lonely type signature (not above a function declaration).
|
TypeSignature
String
-- ^ A lonely type signature (not above a function
-- declaration).
|
Directive
DirectiveType
String
-- ^ An IHaskell directive.
|
Directive
DirectiveType
String
-- ^ An IHaskell directive.
|
Module
String
-- ^ A full Haskell module, to be compiled and loaded.
|
Module
String
-- ^ A full Haskell module, to be compiled and loaded.
|
ParseError
StringLoc
ErrMsg
-- ^ An error indicating that parsing the code block failed.
|
ParseError
StringLoc
ErrMsg
-- ^ An error indicating that parsing the code block
|
Pragma
PragmaType
[
String
]
-- ^ A list of GHC pragmas (from a {-# LANGUAGE ... #-} block)
-- failed.
|
Pragma
PragmaType
[
String
]
-- ^ A list of GHC pragmas (from a {-# LANGUAGE ... #-}
-- block)
deriving
(
Show
,
Eq
)
deriving
(
Show
,
Eq
)
-- | Directive types. Each directive is associated with a string in the
-- | Directive types. Each directive is associated with a string in the directive code block.
-- directive code block.
data
DirectiveType
=
GetType
-- ^ Get the type of an expression via ':type' (or unique prefixes)
data
DirectiveType
=
GetType
-- ^ Get the type of an expression via ':type' (or unique prefixes)
|
GetInfo
-- ^ Get info about the identifier via ':info' (or unique prefixes)
|
GetInfo
-- ^ Get info about the identifier via ':info' (or unique prefixes)
|
SetDynFlag
-- ^ Enable or disable an extensions, packages etc. via `:set`. Emulates GHCi's `:set`
|
SetDynFlag
-- ^ Enable or disable an extensions, packages etc. via `:set`.
-- Emulates GHCi's `:set`
|
LoadFile
-- ^ Load a Haskell module.
|
LoadFile
-- ^ Load a Haskell module.
|
SetOption
-- ^ Set IHaskell kernel option `:option`.
|
SetOption
-- ^ Set IHaskell kernel option `:option`.
|
SetExtension
-- ^ `:extension Foo` is a shortcut for `:set -XFoo`
|
SetExtension
-- ^ `:extension Foo` is a shortcut for `:set -XFoo`
...
@@ -59,10 +60,9 @@ data DirectiveType
...
@@ -59,10 +60,9 @@ data DirectiveType
|
LoadModule
-- ^ Load and unload modules via ':module'.
|
LoadModule
-- ^ Load and unload modules via ':module'.
deriving
(
Show
,
Eq
)
deriving
(
Show
,
Eq
)
-- | Pragma types. Only LANGUAGE pragmas are currently supported.
-- | Pragma types. Only LANGUAGE pragmas are currently supported. Other pragma types are kept around
-- Other pragma types are kept around as a string for error reporting.
-- as a string for error reporting.
data
PragmaType
data
PragmaType
=
PragmaLanguage
=
PragmaLanguage
|
PragmaUnsupported
String
|
PragmaUnsupported
String
deriving
(
Show
,
Eq
)
deriving
(
Show
,
Eq
)
...
@@ -73,18 +73,18 @@ parseString codeString = do
...
@@ -73,18 +73,18 @@ parseString codeString = do
flags
<-
getSessionDynFlags
flags
<-
getSessionDynFlags
let
output
=
runParser
flags
parserModule
codeString
let
output
=
runParser
flags
parserModule
codeString
case
output
of
case
output
of
Parsed
mod
|
Just
_
<-
hsmodName
(
unLoc
mod
)
->
return
[
Located
1
$
Module
codeString
]
Parsed
mod
|
Just
_
<-
hsmodName
(
unLoc
mod
)
->
return
[
Located
1
$
Module
codeString
]
_
->
do
_
->
do
-- Split input into chunks based on indentation.
-- Split input into chunks based on indentation.
let
chunks
=
layoutChunks
$
removeComments
codeString
let
chunks
=
layoutChunks
$
removeComments
codeString
result
<-
joinFunctions
<$>
processChunks
[]
chunks
result
<-
joinFunctions
<$>
processChunks
[]
chunks
-- Return to previous flags. When parsing, flags can be set to make
-- Return to previous flags. When parsing, flags can be set to make sure parsing works properly. But
-- sure parsing works properly. But we don't want those flags to be
-- we don't want those flags to be set during evaluation until the right time.
-- set during evaluation until the right time.
_
<-
setSessionDynFlags
flags
_
<-
setSessionDynFlags
flags
return
result
return
result
otherwise
->
error
"parseString failed, output was neither Parsed nor Failure"
where
where
parseChunk
::
GhcMonad
m
=>
String
->
LineNumber
->
m
(
Located
CodeBlock
)
parseChunk
::
GhcMonad
m
=>
String
->
LineNumber
->
m
(
Located
CodeBlock
)
parseChunk
chunk
line
=
Located
line
<$>
handleChunk
chunk
line
parseChunk
chunk
line
=
Located
line
<$>
handleChunk
chunk
line
...
@@ -137,14 +137,15 @@ parseCodeChunk code startLine = do
...
@@ -137,14 +137,15 @@ parseCodeChunk code startLine = do
rawResults
=
map
(
tryParser
code
)
(
parsers
flags
)
rawResults
=
map
(
tryParser
code
)
(
parsers
flags
)
-- Convert statements into expressions where we can
-- Convert statements into expressions where we can
results
=
map
(
statementToExpression
flags
)
rawResults
in
results
=
map
(
statementToExpression
flags
)
rawResults
case
successes
results
of
case
successes
results
of
-- If none of them succeeded, choose the best error message to
-- If none of them succeeded, choose the best error message to display. Only one of the error
-- display. Only one of the error
messages is actually relevant.
--
messages is actually relevant.
[]
->
return
$
bestError
$
failures
results
[]
->
return
$
bestError
$
failures
results
-- If one of the parsers succeeded
-- If one of the parsers succeeded
result
:
_
->
return
result
result
:
_
->
return
result
where
where
successes
::
[
ParseOutput
a
]
->
[
a
]
successes
::
[
ParseOutput
a
]
->
[
a
]
successes
[]
=
[]
successes
[]
=
[]
...
@@ -164,19 +165,22 @@ parseCodeChunk code startLine = do
...
@@ -164,19 +165,22 @@ parseCodeChunk code startLine = do
statementToExpression
::
DynFlags
->
ParseOutput
CodeBlock
->
ParseOutput
CodeBlock
statementToExpression
::
DynFlags
->
ParseOutput
CodeBlock
->
ParseOutput
CodeBlock
statementToExpression
flags
(
Parsed
(
Statement
stmt
))
=
Parsed
result
statementToExpression
flags
(
Parsed
(
Statement
stmt
))
=
Parsed
result
where
result
=
if
isExpr
flags
stmt
where
result
=
if
isExpr
flags
stmt
then
Expression
stmt
then
Expression
stmt
else
Statement
stmt
else
Statement
stmt
statementToExpression
_
other
=
other
statementToExpression
_
other
=
other
-- Check whether a string is a valid expression.
-- Check whether a string is a valid expression.
isExpr
::
DynFlags
->
String
->
Bool
isExpr
::
DynFlags
->
String
->
Bool
isExpr
flags
str
=
case
runParser
flags
parserExpression
str
of
isExpr
flags
str
=
Parsed
{}
->
True
case
runParser
flags
parserExpression
str
of
Parsed
{}
->
True
_
->
False
_
->
False
tryParser
::
String
->
(
String
->
CodeBlock
,
String
->
ParseOutput
String
)
->
ParseOutput
CodeBlock
tryParser
::
String
->
(
String
->
CodeBlock
,
String
->
ParseOutput
String
)
->
ParseOutput
CodeBlock
tryParser
string
(
blockType
,
parser
)
=
case
parser
string
of
tryParser
string
(
blockType
,
parser
)
=
case
parser
string
of
Parsed
res
->
Parsed
(
blockType
res
)
Parsed
res
->
Parsed
(
blockType
res
)
Failure
err
loc
->
Failure
err
loc
Failure
err
loc
->
Failure
err
loc
otherwise
->
error
"tryParser failed, output was neither Parsed nor Failure"
otherwise
->
error
"tryParser failed, output was neither Parsed nor Failure"
...
@@ -196,9 +200,9 @@ parseCodeChunk code startLine = do
...
@@ -196,9 +200,9 @@ parseCodeChunk code startLine = do
Partial
out
strs
->
Partial
code
strs
Partial
out
strs
->
Partial
code
strs
Failure
err
loc
->
Failure
err
loc
Failure
err
loc
->
Failure
err
loc
-- | Find consecutive declarations of the same function and join them into
-- | Find consecutive declarations of the same function and join them into
a single declaration.
--
a single declaration. These declarations may also include a type
--
These declarations may also include a type signature, which is also joined with the subsequent
--
signature, which is also joined with the subsequent
declarations.
-- declarations.
joinFunctions
::
[
Located
CodeBlock
]
->
[
Located
CodeBlock
]
joinFunctions
::
[
Located
CodeBlock
]
->
[
Located
CodeBlock
]
joinFunctions
[]
=
[]
joinFunctions
[]
=
[]
joinFunctions
blocks
=
joinFunctions
blocks
=
...
@@ -221,7 +225,6 @@ joinFunctions blocks =
...
@@ -221,7 +225,6 @@ joinFunctions blocks =
conjoin
::
[
CodeBlock
]
->
CodeBlock
conjoin
::
[
CodeBlock
]
->
CodeBlock
conjoin
=
Declaration
.
intercalate
"
\n
"
.
map
str
conjoin
=
Declaration
.
intercalate
"
\n
"
.
map
str
-- | Parse a pragma of the form {-# LANGUAGE ... #-}
-- | Parse a pragma of the form {-# LANGUAGE ... #-}
parsePragma
::
String
-- ^ Pragma string.
parsePragma
::
String
-- ^ Pragma string.
->
Int
-- ^ Line number at which the directive appears.
->
Int
-- ^ Line number at which the directive appears.
...
@@ -230,9 +233,10 @@ parsePragma ('{':'-':'#':pragma) line =
...
@@ -230,9 +233,10 @@ parsePragma ('{':'-':'#':pragma) line =
let
commaToSpace
::
Char
->
Char
let
commaToSpace
::
Char
->
Char
commaToSpace
','
=
' '
commaToSpace
','
=
' '
commaToSpace
x
=
x
commaToSpace
x
=
x
pragmas
=
words
$
takeWhile
(
/=
'#'
)
$
map
commaToSpace
pragma
in
pragmas
=
words
$
takeWhile
(
/=
'#'
)
$
map
commaToSpace
pragma
case
pragmas
of
in
case
pragmas
of
[]
->
Pragma
(
PragmaUnsupported
""
)
[]
--empty string pragmas are unsupported
--empty string pragmas are unsupported
[]
->
Pragma
(
PragmaUnsupported
""
)
[]
"LANGUAGE"
:
xs
->
Pragma
PragmaLanguage
xs
"LANGUAGE"
:
xs
->
Pragma
PragmaLanguage
xs
x
:
xs
->
Pragma
(
PragmaUnsupported
x
)
xs
x
:
xs
->
Pragma
(
PragmaUnsupported
x
)
xs
...
@@ -240,19 +244,21 @@ parsePragma ('{':'-':'#':pragma) line =
...
@@ -240,19 +244,21 @@ parsePragma ('{':'-':'#':pragma) line =
parseDirective
::
String
-- ^ Directive string.
parseDirective
::
String
-- ^ Directive string.
->
Int
-- ^ Line number at which the directive appears.
->
Int
-- ^ Line number at which the directive appears.
->
CodeBlock
-- ^ Directive code block or a parse error.
->
CodeBlock
-- ^ Directive code block or a parse error.
parseDirective
(
':'
:
'!'
:
directive
)
line
=
Directive
ShellCmd
$
'!'
:
directive
parseDirective
(
':'
:
'!'
:
directive
)
line
=
Directive
ShellCmd
$
'!'
:
directive
parseDirective
(
':'
:
directive
)
line
=
parseDirective
(
':'
:
directive
)
line
=
case
find
rightDirective
directives
of
case
find
rightDirective
directives
of
Just
(
directiveType
,
_
)
->
Directive
directiveType
arg
Just
(
directiveType
,
_
)
->
Directive
directiveType
arg
where
arg
=
unwords
restLine
where
arg
=
unwords
restLine
_
:
restLine
=
words
directive
_
:
restLine
=
words
directive
Nothing
->
Nothing
->
let
directiveStart
=
case
words
directive
of
let
directiveStart
=
case
words
directive
of
[]
->
""
[]
->
""
first
:
_
->
first
in
first
:
_
->
first
ParseError
(
Loc
line
1
)
$
"Unknown directive: '"
++
directiveStart
++
"'."
in
ParseError
(
Loc
line
1
)
$
"Unknown directive: '"
++
directiveStart
++
"'."
where
where
rightDirective
(
_
,
dirname
)
=
case
words
directive
of
rightDirective
(
_
,
dirname
)
=
case
words
directive
of
[]
->
False
[]
->
False
dir
:
_
->
dir
`
elem
`
tail
(
inits
dirname
)
dir
:
_
->
dir
`
elem
`
tail
(
inits
dirname
)
directives
=
directives
=
...
@@ -271,15 +277,15 @@ parseDirective (':':directive) line = case find rightDirective directives of
...
@@ -271,15 +277,15 @@ parseDirective (':':directive) line = case find rightDirective directives of
]
]
parseDirective
_
_
=
error
"Directive must start with colon!"
parseDirective
_
_
=
error
"Directive must start with colon!"
-- | Parse a module and return the name declared in the 'module X where'
-- | Parse a module and return the name declared in the 'module X where'
line. That line is
--
line. That line is required, and if it does not exist, this will error.
--
required, and if it does not exist, this will error. Names with periods in them are returned
--
Names with periods in them are returned
piece y piece.
-- piece y piece.
getModuleName
::
GhcMonad
m
=>
String
->
m
[
String
]
getModuleName
::
GhcMonad
m
=>
String
->
m
[
String
]
getModuleName
moduleSrc
=
do
getModuleName
moduleSrc
=
do
flags
<-
getSessionDynFlags
flags
<-
getSessionDynFlags
let
output
=
runParser
flags
parserModule
moduleSrc
let
output
=
runParser
flags
parserModule
moduleSrc
case
output
of
case
output
of
Failure
{}
->
error
"Module parsing failed."
Failure
{}
->
error
"Module parsing failed."
Parsed
mod
->
Parsed
mod
->
case
unLoc
<$>
hsmodName
(
unLoc
mod
)
of
case
unLoc
<$>
hsmodName
(
unLoc
mod
)
of
Nothing
->
error
"Module must have a name."
Nothing
->
error
"Module must have a name."
...
...
src/IHaskell/Eval/Util.hs
View file @
7ba7c4d1
{-# LANGUAGE CPP, NoImplicitPrelude #-}
{-# LANGUAGE CPP, NoImplicitPrelude #-}
module
IHaskell.Eval.Util
(
module
IHaskell.Eval.Util
(
-- * Initialization
-- * Initialization
initGhci
,
initGhci
,
-- * Flags and extensions
-- * Flags and extensions ** Set and unset flags.
-- ** Set and unset flags.
extensionFlag
,
extensionFlag
,
setExtension
,
setExtension
,
ExtFlag
(
..
),
ExtFlag
(
..
),
setFlags
,
setFlags
,
...
@@ -19,7 +20,7 @@ module IHaskell.Eval.Util (
...
@@ -19,7 +20,7 @@ module IHaskell.Eval.Util (
-- * Pretty printing
-- * Pretty printing
doc
,
doc
,
pprDynFlags
,
pprDynFlags
,
pprLanguages
pprLanguages
,
)
where
)
where
import
ClassyPrelude
hiding
((
<>
))
import
ClassyPrelude
hiding
((
<>
))
...
@@ -50,20 +51,17 @@ import Data.String.Utils (replace)
...
@@ -50,20 +51,17 @@ import Data.String.Utils (replace)
import
Data.List
(
nubBy
)
import
Data.List
(
nubBy
)
-- | A extension flag that can be set or unset.
-- | A extension flag that can be set or unset.
data
ExtFlag
data
ExtFlag
=
SetFlag
ExtensionFlag
=
SetFlag
ExtensionFlag
|
UnsetFlag
ExtensionFlag
|
UnsetFlag
ExtensionFlag
-- | Find the extension that corresponds to a given flag. Create the
-- | Find the extension that corresponds to a given flag. Create the corresponding 'ExtFlag' via
-- corresponding 'ExtFlag' via @SetFlag@ or @UnsetFlag@.
-- @SetFlag@ or @UnsetFlag@. If no such extension exist, yield @Nothing@.
-- If no such extension exist, yield @Nothing@.
extensionFlag
::
String
-- Extension name, such as @"DataKinds"@
extensionFlag
::
String
-- Extension name, such as @"DataKinds"@
->
Maybe
ExtFlag
->
Maybe
ExtFlag
extensionFlag
ext
=
extensionFlag
ext
=
case
find
(
flagMatches
ext
)
xFlags
of
case
find
(
flagMatches
ext
)
xFlags
of
Just
fs
->
Just
$
SetFlag
$
flagSpecFlag
fs
Just
fs
->
Just
$
SetFlag
$
flagSpecFlag
fs
-- If it doesn't match an extension name, try matching against
-- If it doesn't match an extension name, try matching against disabling an extension.
-- disabling an extension.
Nothing
->
Nothing
->
case
find
(
flagMatchesNo
ext
)
xFlags
of
case
find
(
flagMatchesNo
ext
)
xFlags
of
Just
fs
->
Just
$
UnsetFlag
$
flagSpecFlag
fs
Just
fs
->
Just
$
UnsetFlag
$
flagSpecFlag
fs
...
@@ -72,48 +70,39 @@ extensionFlag ext =
...
@@ -72,48 +70,39 @@ extensionFlag ext =
-- Check if a FlagSpec matches an extension name.
-- Check if a FlagSpec matches an extension name.
flagMatches
ext
fs
=
ext
==
flagSpecName
fs
flagMatches
ext
fs
=
ext
==
flagSpecName
fs
-- Check if a FlagSpec matches "No<ExtensionName>".
-- Check if a FlagSpec matches "No<ExtensionName>". In that case, we disable the extension.
-- In that case, we disable the extension.
flagMatchesNo
ext
fs
=
ext
==
"No"
++
flagSpecName
fs
flagMatchesNo
ext
fs
=
ext
==
"No"
++
flagSpecName
fs
#
if
!
MIN_VERSION_ghc
(
7
,
10
,
0
)
#
if
!
MIN_VERSION_ghc
(
7
,
10
,
0
)
flagSpecName
(
name
,
_
,
_
)
=
name
flagSpecName
(
name
,
_
,
_
)
=
name
flagSpecFlag
(
_
,
flag
,
_
)
=
flag
#
endif
#
if
!
MIN_VERSION_ghc
(
7
,
10
,
0
)
flagSpecFlag
(
_
,
flag
,
_
)
=
flag
flagSpecName
(
name
,
_
,
_
)
=
name
flagSpecFlag
(
_
,
flag
,
_
)
=
flag
#
endif
#
endif
-- | Pretty-print dynamic flags (taken from 'InteractiveUI' module of `ghc-bin`)
-- | Pretty-print dynamic flags (taken from 'InteractiveUI' module of `ghc-bin`)
pprDynFlags
::
Bool
-- ^ Whether to include flags which are on by default
pprDynFlags
::
Bool
-- ^ Whether to include flags which are on by default
->
DynFlags
->
DynFlags
->
SDoc
->
SDoc
pprDynFlags
show_all
dflags
=
pprDynFlags
show_all
dflags
=
vcat
[
vcat
#
if
MIN_VERSION_ghc
(
7
,
8
,
0
)
[
text
"GHCi-specific dynamic flag settings:"
$$
text
"GHCi-specific dynamic flag settings:"
$$
nest
2
(
vcat
(
map
(
setting
opt
)
ghciFlags
))
nest
2
(
vcat
(
map
(
setting
gopt
)
ghciFlags
)),
,
text
"other dynamic, non-language, flag settings:"
$$
text
"other dynamic, non-language, flag settings:"
$$
nest
2
(
vcat
(
map
(
setting
opt
)
others
))
nest
2
(
vcat
(
map
(
setting
gopt
)
others
)),
,
text
"warning settings:"
$$
text
"warning settings:"
$$
nest
2
(
vcat
(
map
(
setting
wopt
)
DynFlags
.
fWarningFlags
))
#
else
text
"GHCi-specific dynamic flag settings:"
$$
nest
2
(
vcat
(
map
(
setting
dopt
)
ghciFlags
)),
text
"other dynamic, non-language, flag settings:"
$$
nest
2
(
vcat
(
map
(
setting
dopt
)
others
)),
text
"warning settings:"
$$
nest
2
(
vcat
(
map
(
setting
wopt
)
DynFlags
.
fWarningFlags
))
nest
2
(
vcat
(
map
(
setting
wopt
)
DynFlags
.
fWarningFlags
))
#
endif
]
]
where
where
#
if
MIN_VERSION_ghc
(
7
,
8
,
0
)
opt
=
gopt
#
else
opt
=
dopt
#
endif
setting
test
flag
setting
test
flag
|
quiet
=
empty
|
quiet
=
empty
|
is_on
=
fstr
name
|
is_on
=
fstr
name
|
otherwise
=
fnostr
name
|
otherwise
=
fnostr
name
where
name
=
flagSpecName
flag
where
name
=
flagSpecName
flag
f
=
flagSpecFlag
flag
f
=
flagSpecFlag
flag
is_on
=
test
f
dflags
is_on
=
test
f
dflags
quiet
=
not
show_all
&&
test
f
default_dflags
==
is_on
quiet
=
not
show_all
&&
test
f
default_dflags
==
is_on
...
@@ -121,42 +110,43 @@ pprDynFlags show_all dflags =
...
@@ -121,42 +110,43 @@ pprDynFlags show_all dflags =
default_dflags
=
defaultDynFlags
(
settings
dflags
)
default_dflags
=
defaultDynFlags
(
settings
dflags
)
fstr
str
=
text
"-f"
<>
text
str
fstr
str
=
text
"-f"
<>
text
str
fnostr
str
=
text
"-fno-"
<>
text
str
fnostr
str
=
text
"-fno-"
<>
text
str
(
ghciFlags
,
others
)
=
partition
(
\
f
->
flagSpecFlag
f
`
elem
`
flgs
)
(
ghciFlags
,
others
)
=
partition
(
\
f
->
flagSpecFlag
f
`
elem
`
flgs
)
DynFlags
.
fFlags
DynFlags
.
fFlags
flgs
=
[
flgs
=
concat
[
flgs1
,
flgs2
,
flgs3
]
Opt_PrintExplicitForalls
flgs1
=
[
Opt_PrintExplicitForalls
]
#
if
MIN_VERSION_ghc
(
7
,
8
,
0
)
#
if
MIN_VERSION_ghc
(
7
,
8
,
0
)
,
Opt_PrintExplicitKinds
flgs2
=
[
Opt_PrintExplicitKinds
]
#
else
flgs2
=
[]
#
endif
#
endif
,
Opt_PrintBindResult
flgs3
=
[
Opt_PrintBindResult
,
Opt_BreakOnException
,
Opt_BreakOnError
,
Opt_PrintEvldWithShow
]
,
Opt_BreakOnException
,
Opt_BreakOnError
,
Opt_PrintEvldWithShow
]
-- | Pretty-print the base language and active options (taken from `InteractiveUI` module of `ghc-bin`)
-- | Pretty-print the base language and active options (taken from `InteractiveUI` module of
-- `ghc-bin`)
pprLanguages
::
Bool
-- ^ Whether to include flags which are on by default
pprLanguages
::
Bool
-- ^ Whether to include flags which are on by default
->
DynFlags
->
DynFlags
->
SDoc
->
SDoc
pprLanguages
show_all
dflags
=
pprLanguages
show_all
dflags
=
vcat
vcat
[
text
"base language is: "
<>
[
text
"base language is: "
<>
case
language
dflags
of
case
language
dflags
of
Nothing
->
text
"Haskell2010"
Nothing
->
text
"Haskell2010"
Just
Haskell98
->
text
"Haskell98"
Just
Haskell98
->
text
"Haskell98"
Just
Haskell2010
->
text
"Haskell2010"
Just
Haskell2010
->
text
"Haskell2010"
,
(
if
show_all
,
(
if
show_all
then
text
"all active language options:"
then
text
"all active language options:"
else
text
"with the following modifiers:"
)
$$
else
text
"with the following modifiers:"
)
$$
nest
2
(
vcat
(
map
(
setting
xopt
)
DynFlags
.
xFlags
))
nest
2
(
vcat
(
map
(
setting
xopt
)
DynFlags
.
xFlags
))]
]
where
where
setting
test
flag
setting
test
flag
|
quiet
=
empty
|
quiet
=
empty
|
is_on
=
text
"-X"
<>
text
name
|
is_on
=
text
"-X"
<>
text
name
|
otherwise
=
text
"-XNo"
<>
text
name
|
otherwise
=
text
"-XNo"
<>
text
name
where
name
=
flagSpecName
flag
where
name
=
flagSpecName
flag
f
=
flagSpecFlag
flag
f
=
flagSpecFlag
flag
is_on
=
test
f
dflags
is_on
=
test
f
dflags
quiet
=
not
show_all
&&
test
f
default_dflags
==
is_on
quiet
=
not
show_all
&&
test
f
default_dflags
==
is_on
...
@@ -167,8 +157,8 @@ pprLanguages show_all dflags =
...
@@ -167,8 +157,8 @@ pprLanguages show_all dflags =
Nothing
->
Just
Haskell2010
Nothing
->
Just
Haskell2010
other
->
other
other
->
other
-- | Set an extension and update flags.
-- | Set an extension and update flags.
Return @Nothing@ on success. On failure, return an error
--
Return @Nothing@ on success. On failure, return an error
message.
-- message.
setExtension
::
GhcMonad
m
=>
String
->
m
(
Maybe
String
)
setExtension
::
GhcMonad
m
=>
String
->
m
(
Maybe
String
)
setExtension
ext
=
do
setExtension
ext
=
do
flags
<-
getSessionDynFlags
flags
<-
getSessionDynFlags
...
@@ -181,9 +171,8 @@ setExtension ext = do
...
@@ -181,9 +171,8 @@ setExtension ext = do
UnsetFlag
ghcFlag
->
xopt_unset
flags
ghcFlag
UnsetFlag
ghcFlag
->
xopt_unset
flags
ghcFlag
return
Nothing
return
Nothing
-- | Set a list of flags, as per GHCi's `:set`.
-- | Set a list of flags, as per GHCi's `:set`. This was adapted from GHC's InteractiveUI.hs
-- This was adapted from GHC's InteractiveUI.hs (newDynFlags).
-- (newDynFlags). It returns a list of error messages.
-- It returns a list of error messages.
setFlags
::
GhcMonad
m
=>
[
String
]
->
m
[
String
]
setFlags
::
GhcMonad
m
=>
[
String
]
->
m
[
String
]
setFlags
ext
=
do
setFlags
ext
=
do
-- Try to parse flags.
-- Try to parse flags.
...
@@ -192,7 +181,7 @@ setFlags ext = do
...
@@ -192,7 +181,7 @@ setFlags ext = do
-- First, try to check if this flag matches any extension name.
-- First, try to check if this flag matches any extension name.
let
restorePkg
x
=
x
{
packageFlags
=
packageFlags
flags
}
let
restorePkg
x
=
x
{
packageFlags
=
packageFlags
flags
}
let
restoredPkgs
=
flags'
{
packageFlags
=
packageFlags
flags
}
let
restoredPkgs
=
flags'
{
packageFlags
=
packageFlags
flags
}
GHC
.
setProgramDynFlags
restoredPkgs
GHC
.
setProgramDynFlags
restoredPkgs
GHC
.
setInteractiveDynFlags
restoredPkgs
GHC
.
setInteractiveDynFlags
restoredPkgs
...
@@ -203,11 +192,10 @@ setFlags ext = do
...
@@ -203,11 +192,10 @@ setFlags ext = do
warnErrs
=
map
(
"Warning: "
++
)
allWarns
warnErrs
=
map
(
"Warning: "
++
)
allWarns
return
$
noParseErrs
++
warnErrs
return
$
noParseErrs
++
warnErrs
-- | Convert an 'SDoc' into a string. This is similar to the family of
-- | Convert an 'SDoc' into a string. This is similar to the family of 'showSDoc' functions, but
-- 'showSDoc' functions, but does not impose an arbitrary width limit on
-- does not impose an arbitrary width limit on the output (in terms of number of columns). Instead,
-- the output (in terms of number of columns). Instead, it respsects the
-- it respsects the 'pprCols' field in the structure returned by 'getSessionDynFlags', and thus
-- 'pprCols' field in the structure returned by 'getSessionDynFlags', and
-- gives a configurable width of output.
-- thus gives a configurable width of output.
doc
::
GhcMonad
m
=>
SDoc
->
m
String
doc
::
GhcMonad
m
=>
SDoc
->
m
String
doc
sdoc
=
do
doc
sdoc
=
do
flags
<-
getSessionDynFlags
flags
<-
getSessionDynFlags
...
@@ -216,15 +204,16 @@ doc sdoc = do
...
@@ -216,15 +204,16 @@ doc sdoc = do
let
cols
=
pprCols
flags
let
cols
=
pprCols
flags
d
=
runSDoc
sdoc
(
initSDocContext
flags
style
)
d
=
runSDoc
sdoc
(
initSDocContext
flags
style
)
return
$
Pretty
.
fullRender
Pretty
.
PageMode
cols
1.5
string_txt
""
d
return
$
Pretty
.
fullRender
Pretty
.
PageMode
cols
1.5
string_txt
""
d
where
where
string_txt
::
Pretty
.
TextDetails
->
String
->
String
string_txt
::
Pretty
.
TextDetails
->
String
->
String
string_txt
(
Pretty
.
Chr
c
)
s
=
c
:
s
string_txt
(
Pretty
.
Chr
c
)
s
=
c
:
s
string_txt
(
Pretty
.
Str
s1
)
s2
=
s1
++
s2
string_txt
(
Pretty
.
Str
s1
)
s2
=
s1
++
s2
string_txt
(
Pretty
.
PStr
s1
)
s2
=
unpackFS
s1
++
s2
string_txt
(
Pretty
.
PStr
s1
)
s2
=
unpackFS
s1
++
s2
string_txt
(
Pretty
.
LStr
s1
_
)
s2
=
unpackLitString
s1
++
s2
string_txt
(
Pretty
.
LStr
s1
_
)
s2
=
unpackLitString
s1
++
s2
-- | Initialize the GHC API. Run this as the first thing in the `runGhc`.
-- | Initialize the GHC API. Run this as the first thing in the `runGhc`.
This initializes some dyn
--
This initializes some dyn
flags (@ExtendedDefaultRules@,
-- flags (@ExtendedDefaultRules@,
-- @NoMonomorphismRestriction@), sets the target to interpreted, link in
-- @NoMonomorphismRestriction@), sets the target to interpreted, link in
-- memory, sets a reasonable output width, and potentially a few other
-- memory, sets a reasonable output width, and potentially a few other
-- things. It should be invoked before other functions from this module.
-- things. It should be invoked before other functions from this module.
...
@@ -234,27 +223,28 @@ doc sdoc = do
...
@@ -234,27 +223,28 @@ doc sdoc = do
-- (and only the first time) it is called.
-- (and only the first time) it is called.
initGhci
::
GhcMonad
m
=>
Maybe
String
->
m
()
initGhci
::
GhcMonad
m
=>
Maybe
String
->
m
()
initGhci
sandboxPackages
=
do
initGhci
sandboxPackages
=
do
-- Initialize dyn flags.
-- Initialize dyn flags. Start with -XExtendedDefaultRules and -XNoMonomorphismRestriction.
-- Start with -XExtendedDefaultRules and -XNoMonomorphismRestriction.
originalFlags
<-
getSessionDynFlags
originalFlags
<-
getSessionDynFlags
let
flag
=
flip
xopt_set
let
flag
=
flip
xopt_set
unflag
=
flip
xopt_unset
unflag
=
flip
xopt_unset
dflags
=
flag
Opt_ExtendedDefaultRules
.
unflag
Opt_MonomorphismRestriction
$
originalFlags
dflags
=
flag
Opt_ExtendedDefaultRules
.
unflag
Opt_MonomorphismRestriction
$
originalFlags
pkgConfs
=
case
sandboxPackages
of
pkgConfs
=
case
sandboxPackages
of
Nothing
->
extraPkgConfs
originalFlags
Nothing
->
extraPkgConfs
originalFlags
Just
path
->
Just
path
->
let
pkg
=
PkgConfFile
path
in
let
pkg
=
PkgConfFile
path
(
pkg
:
)
.
extraPkgConfs
originalFlags
in
(
pkg
:
)
.
extraPkgConfs
originalFlags
void
$
setSessionDynFlags
$
dflags
{
hscTarget
=
HscInterpreted
,
void
$
setSessionDynFlags
$
dflags
ghcLink
=
LinkInMemory
,
{
hscTarget
=
HscInterpreted
pprCols
=
300
,
,
ghcLink
=
LinkInMemory
extraPkgConfs
=
pkgConfs
}
,
pprCols
=
300
,
extraPkgConfs
=
pkgConfs
-- | Evaluate a single import statement.
}
-- If this import statement is importing a module which was previously
-- imported implicitly (such as `Prelude`) or if this module has a `hiding`
-- | Evaluate a single import statement. If this import statement is importing a module which was
-- annotation, the previous import is removed.
-- previously imported implicitly (such as `Prelude`) or if this module has a `hiding` annotation,
-- the previous import is removed.
evalImport
::
GhcMonad
m
=>
String
->
m
()
evalImport
::
GhcMonad
m
=>
String
->
m
()
evalImport
imports
=
do
evalImport
imports
=
do
importDecl
<-
parseImportDecl
imports
importDecl
<-
parseImportDecl
imports
...
@@ -285,7 +275,8 @@ evalImport imports = do
...
@@ -285,7 +275,8 @@ evalImport imports = do
-- Check whether an import is hidden.
-- Check whether an import is hidden.
isHiddenImport
::
ImportDecl
RdrName
->
Bool
isHiddenImport
::
ImportDecl
RdrName
->
Bool
isHiddenImport
imp
=
case
ideclHiding
imp
of
isHiddenImport
imp
=
case
ideclHiding
imp
of
Just
(
True
,
_
)
->
True
Just
(
True
,
_
)
->
True
_
->
False
_
->
False
...
@@ -301,8 +292,7 @@ removeImport moduleName = do
...
@@ -301,8 +292,7 @@ removeImport moduleName = do
isImportOf
name
(
IIModule
modName
)
=
name
==
modName
isImportOf
name
(
IIModule
modName
)
=
name
==
modName
isImportOf
name
(
IIDecl
impDecl
)
=
name
==
unLoc
(
ideclName
impDecl
)
isImportOf
name
(
IIDecl
impDecl
)
=
name
==
unLoc
(
ideclName
impDecl
)
-- | Evaluate a series of declarations.
-- | Evaluate a series of declarations. Return all names which were bound by these declarations.
-- Return all names which were bound by these declarations.
evalDeclarations
::
GhcMonad
m
=>
String
->
m
[
String
]
evalDeclarations
::
GhcMonad
m
=>
String
->
m
[
String
]
evalDeclarations
decl
=
do
evalDeclarations
decl
=
do
names
<-
runDecls
decl
names
<-
runDecls
decl
...
@@ -321,16 +311,16 @@ cleanUpDuplicateInstances = modifySession $ \hscEnv ->
...
@@ -321,16 +311,16 @@ cleanUpDuplicateInstances = modifySession $ \hscEnv ->
in
hscEnv
{
hsc_IC
=
ic
{
ic_instances
=
(
clsInsts'
,
famInsts
)
}
}
in
hscEnv
{
hsc_IC
=
ic
{
ic_instances
=
(
clsInsts'
,
famInsts
)
}
}
where
where
instEq
::
ClsInst
->
ClsInst
->
Bool
instEq
::
ClsInst
->
ClsInst
->
Bool
instEq
ClsInst
{
is_tvs
=
tpl_tvs
,
is_tys
=
tpl_tys
,
is_cls
=
cls
}
ClsInst
{
is_tys
=
tpl_tys'
,
is_cls
=
cls'
}
=
#
if
MIN_VERSION_ghc
(
7
,
8
,
0
)
#
if
MIN_VERSION_ghc
(
7
,
8
,
0
)
-- Only support replacing instances on GHC 7.8 and up
-- Only support replacing instances on GHC 7.8 and up
let
tpl_tv_set
=
mkVarSet
tpl_tvs
instEq
c1
c2
|
ClsInst
{
is_tvs
=
tpl_tvs
,
is_tys
=
tpl_tys
,
is_cls
=
cls
}
<-
c1
,
ClsInst
{
is_tys
=
tpl_tys'
,
is_cls
=
cls'
}
<-
c2
=
let
tpl_tv_set
=
mkVarSet
tpl_tvs
in
cls
==
cls'
&&
isJust
(
tcMatchTys
tpl_tv_set
tpl_tys
tpl_tys'
)
in
cls
==
cls'
&&
isJust
(
tcMatchTys
tpl_tv_set
tpl_tys
tpl_tys'
)
#
else
#
else
False
instEq
_
_
=
False
#
endif
#
endif
-- | Get the type of an expression and convert it to a string.
-- | Get the type of an expression and convert it to a string.
getType
::
GhcMonad
m
=>
String
->
m
String
getType
::
GhcMonad
m
=>
String
->
m
String
getType
expr
=
do
getType
expr
=
do
...
@@ -345,18 +335,20 @@ getDescription str = do
...
@@ -345,18 +335,20 @@ getDescription str = do
names
<-
parseName
str
names
<-
parseName
str
maybeInfos
<-
mapM
getInfo'
names
maybeInfos
<-
mapM
getInfo'
names
-- Filter out types that have parents in the same set.
-- Filter out types that have parents in the same set. GHCi also does this.
-- GHCi also does this.
let
infos
=
catMaybes
maybeInfos
let
infos
=
catMaybes
maybeInfos
allNames
=
mkNameSet
$
map
(
getName
.
getType
)
infos
allNames
=
mkNameSet
$
map
(
getName
.
getType
)
infos
hasParent
info
=
case
tyThingParent_maybe
(
getType
info
)
of
hasParent
info
=
case
tyThingParent_maybe
(
getType
info
)
of
Just
parent
->
getName
parent
`
elemNameSet
`
allNames
Just
parent
->
getName
parent
`
elemNameSet
`
allNames
Nothing
->
False
Nothing
->
False
filteredOutput
=
filter
(
not
.
hasParent
)
infos
filteredOutput
=
filter
(
not
.
hasParent
)
infos
-- Print nicely
-- Print nicely
mapM
(
doc
.
printInfo
)
filteredOutput
mapM
(
doc
.
printInfo
)
filteredOutput
where
where
#
if
MIN_VERSION_ghc
(
7
,
8
,
0
)
#
if
MIN_VERSION_ghc
(
7
,
8
,
0
)
getInfo'
=
getInfo
False
getInfo'
=
getInfo
False
#
else
#
else
...
@@ -377,7 +369,8 @@ getDescription str = do
...
@@ -377,7 +369,8 @@ getDescription str = do
vcat
(
map
GHC
.
pprFamInst
famInstances
)
vcat
(
map
GHC
.
pprFamInst
famInstances
)
#
else
#
else
printInfo
(
thing
,
fixity
,
classInstances
)
=
printInfo
(
thing
,
fixity
,
classInstances
)
=
pprTyThingInContextLoc
False
thing
$$
showFixity
thing
fixity
$$
vcat
(
map
GHC
.
pprInstance
classInstances
)
pprTyThingInContextLoc
False
thing
$$
showFixity
thing
fixity
$$
vcat
(
map
GHC
.
pprInstance
classInstances
)
#
endif
#
endif
showFixity
thing
fixity
=
showFixity
thing
fixity
=
if
fixity
==
GHC
.
defaultFixity
if
fixity
==
GHC
.
defaultFixity
...
...
src/IHaskell/Flags.hs
View file @
7ba7c4d1
{-# LANGUAGE NoImplicitPrelude, DeriveFunctor #-}
{-# LANGUAGE NoImplicitPrelude, DeriveFunctor #-}
module
IHaskell.Flags
(
module
IHaskell.Flags
(
IHaskellMode
(
..
),
IHaskellMode
(
..
),
Argument
(
..
),
Argument
(
..
),
...
@@ -16,8 +17,7 @@ import System.Console.CmdArgs.Text
...
@@ -16,8 +17,7 @@ import System.Console.CmdArgs.Text
import
Data.List
(
findIndex
)
import
Data.List
(
findIndex
)
import
IHaskell.Types
import
IHaskell.Types
-- Command line arguments to IHaskell. A set of aruments is annotated with
-- Command line arguments to IHaskell. A set of arguments is annotated with the mode being invoked.
-- the mode being invoked.
data
Args
=
Args
IHaskellMode
[
Argument
]
data
Args
=
Args
IHaskellMode
[
Argument
]
deriving
Show
deriving
Show
...
@@ -33,7 +33,9 @@ data Argument = ConfFile String -- ^ A file with commands to load at startup
...
@@ -33,7 +33,9 @@ data Argument = ConfFile String -- ^ A file with commands to load at startup
|
ConvertLhsStyle
(
LhsStyle
String
)
|
ConvertLhsStyle
(
LhsStyle
String
)
deriving
(
Eq
,
Show
)
deriving
(
Eq
,
Show
)
data
LhsStyle
string
=
LhsStyle
{
lhsCodePrefix
::
string
-- ^ @>@
data
LhsStyle
string
=
LhsStyle
{
lhsCodePrefix
::
string
-- ^ @>@
,
lhsOutputPrefix
::
string
-- ^ @<<@
,
lhsOutputPrefix
::
string
-- ^ @<<@
,
lhsBeginCode
::
string
-- ^ @\\begin{code}@
,
lhsBeginCode
::
string
-- ^ @\\begin{code}@
,
lhsEndCode
::
string
-- ^ @\\end{code}@
,
lhsEndCode
::
string
-- ^ @\\end{code}@
...
@@ -42,21 +44,18 @@ data LhsStyle string = LhsStyle { lhsCodePrefix :: string -- ^ @>@
...
@@ -42,21 +44,18 @@ data LhsStyle string = LhsStyle { lhsCodePrefix :: string -- ^ @>@
}
}
deriving
(
Eq
,
Functor
,
Show
)
deriving
(
Eq
,
Functor
,
Show
)
data
NotebookFormat
=
LhsMarkdown
data
NotebookFormat
=
LhsMarkdown
|
IpynbFile
|
IpynbFile
deriving
(
Eq
,
Show
)
deriving
(
Eq
,
Show
)
-- Which mode IHaskell is being invoked in.
-- Which mode IHaskell is being invoked in.
-- `None` means no mode was specified.
data
IHaskellMode
=
ShowHelp
String
data
IHaskellMode
=
ShowHelp
String
|
InstallKernelSpec
|
InstallKernelSpec
|
ConvertLhs
|
ConvertLhs
|
Kernel
(
Maybe
String
)
|
Kernel
(
Maybe
String
)
deriving
(
Eq
,
Show
)
deriving
(
Eq
,
Show
)
-- | Given a list of command-line arguments, return the IHaskell mode and
-- | Given a list of command-line arguments, return the IHaskell mode and arguments to process.
-- arguments to process.
parseFlags
::
[
String
]
->
Either
String
Args
parseFlags
::
[
String
]
->
Either
String
Args
parseFlags
flags
=
parseFlags
flags
=
let
modeIndex
=
findIndex
(`
elem
`
modeFlags
)
flags
let
modeIndex
=
findIndex
(`
elem
`
modeFlags
)
flags
...
@@ -111,7 +110,8 @@ installKernelSpec =
...
@@ -111,7 +110,8 @@ installKernelSpec =
[
ghcLibFlag
,
kernelDebugFlag
,
confFlag
,
helpFlag
]
[
ghcLibFlag
,
kernelDebugFlag
,
confFlag
,
helpFlag
]
kernel
::
Mode
Args
kernel
::
Mode
Args
kernel
=
mode
"kernel"
(
Args
(
Kernel
Nothing
)
[]
)
"Invoke the IHaskell kernel."
kernelArg
[
ghcLibFlag
,
kernelDebugFlag
,
confFlag
]
kernel
=
mode
"kernel"
(
Args
(
Kernel
Nothing
)
[]
)
"Invoke the IHaskell kernel."
kernelArg
[
ghcLibFlag
,
kernelDebugFlag
,
confFlag
]
where
where
kernelArg
=
flagArg
update
"<json-kernel-file>"
kernelArg
=
flagArg
update
"<json-kernel-file>"
update
filename
(
Args
_
flags
)
=
Right
$
Args
(
Kernel
$
Just
filename
)
flags
update
filename
(
Args
_
flags
)
=
Right
$
Args
(
Kernel
$
Just
filename
)
flags
...
@@ -154,6 +154,7 @@ convert = mode "convert" (Args ConvertLhs []) description unnamedArg convertFlag
...
@@ -154,6 +154,7 @@ convert = mode "convert" (Args ConvertLhs []) description unnamedArg convertFlag
lhsStyleBird
,
lhsStyleTex
::
LhsStyle
String
lhsStyleBird
,
lhsStyleTex
::
LhsStyle
String
lhsStyleBird
=
LhsStyle
"> "
"
\n
<< "
""
""
""
""
lhsStyleBird
=
LhsStyle
"> "
"
\n
<< "
""
""
""
""
lhsStyleTex
=
LhsStyle
""
""
"
\\
begin{code}"
"
\\
end{code}"
"
\\
begin{verbatim}"
"
\\
end{verbatim}"
lhsStyleTex
=
LhsStyle
""
""
"
\\
begin{code}"
"
\\
end{code}"
"
\\
begin{verbatim}"
"
\\
end{verbatim}"
ihaskellArgs
::
Mode
Args
ihaskellArgs
::
Mode
Args
...
@@ -161,8 +162,8 @@ ihaskellArgs =
...
@@ -161,8 +162,8 @@ ihaskellArgs =
let
descr
=
"Haskell for Interactive Computing."
let
descr
=
"Haskell for Interactive Computing."
helpStr
=
showText
(
Wrap
100
)
$
helpText
[]
HelpFormatAll
ihaskellArgs
helpStr
=
showText
(
Wrap
100
)
$
helpText
[]
HelpFormatAll
ihaskellArgs
onlyHelp
=
[
flagHelpSimple
(
add
Help
)]
onlyHelp
=
[
flagHelpSimple
(
add
Help
)]
noMode
=
mode
"IHaskell"
(
Args
(
ShowHelp
helpStr
)
[]
)
descr
noArgs
onlyHelp
in
noMode
=
mode
"IHaskell"
(
Args
(
ShowHelp
helpStr
)
[]
)
descr
noArgs
onlyHelp
noMode
{
modeGroupModes
=
toGroup
allModes
}
in
noMode
{
modeGroupModes
=
toGroup
allModes
}
where
where
add
flag
(
Args
mode
flags
)
=
Args
mode
$
flag
:
flags
add
flag
(
Args
mode
flags
)
=
Args
mode
$
flag
:
flags
...
...
src/IHaskell/IPython.hs
View file @
7ba7c4d1
...
@@ -40,17 +40,20 @@ import qualified GHC.Paths
...
@@ -40,17 +40,20 @@ import qualified GHC.Paths
import
IHaskell.Types
import
IHaskell.Types
import
System.Posix.Signals
import
System.Posix.Signals
data
KernelSpecOptions
=
data
KernelSpecOptions
=
KernelSpecOptions
{
kernelSpecGhcLibdir
::
String
-- ^ GHC libdir.
KernelSpecOptions
{
kernelSpecGhcLibdir
::
String
-- ^ GHC libdir.
,
kernelSpecDebug
::
Bool
-- ^ Spew debugging output?
,
kernelSpecDebug
::
Bool
-- ^ Spew debugging output?
,
kernelSpecConfFile
::
IO
(
Maybe
String
)
-- ^ Filename of profile JSON file.
,
kernelSpecConfFile
::
IO
(
Maybe
String
)
-- ^ Filename of profile JSON file.
}
}
defaultKernelSpecOptions
::
KernelSpecOptions
defaultKernelSpecOptions
::
KernelSpecOptions
defaultKernelSpecOptions
=
KernelSpecOptions
{
kernelSpecGhcLibdir
=
GHC
.
Paths
.
libdir
defaultKernelSpecOptions
=
KernelSpecOptions
{
kernelSpecGhcLibdir
=
GHC
.
Paths
.
libdir
,
kernelSpecDebug
=
False
,
kernelSpecDebug
=
False
,
kernelSpecConfFile
=
defaultConfFile
,
kernelSpecConfFile
=
defaultConfFile
}
}
-- | The IPython kernel name.
-- | The IPython kernel name.
kernelName
::
IsString
a
=>
a
kernelName
::
IsString
a
=>
a
kernelName
=
"haskell"
kernelName
=
"haskell"
...
@@ -133,6 +136,7 @@ verifyIPythonVersion = do
...
@@ -133,6 +136,7 @@ verifyIPythonVersion = do
Just
(
1
:
_
)
->
oldIPython
Just
(
1
:
_
)
->
oldIPython
Just
(
0
:
_
)
->
oldIPython
Just
(
0
:
_
)
->
oldIPython
_
->
badIPython
"Detected IPython, but could not parse version number."
_
->
badIPython
"Detected IPython, but could not parse version number."
where
where
badIPython
::
Text
->
Sh
()
badIPython
::
Text
->
Sh
()
badIPython
message
=
liftIO
$
do
badIPython
message
=
liftIO
$
do
...
@@ -140,8 +144,8 @@ verifyIPythonVersion = do
...
@@ -140,8 +144,8 @@ verifyIPythonVersion = do
exitFailure
exitFailure
oldIPython
=
badIPython
"Detected old version of IPython. IHaskell requires 3.0.0 or up."
oldIPython
=
badIPython
"Detected old version of IPython. IHaskell requires 3.0.0 or up."
-- | Install an IHaskell kernelspec into the right location.
-- | Install an IHaskell kernelspec into the right location.
The right location is determined by
--
The right location is determined by
using `ipython kernelspec install --user`.
-- using `ipython kernelspec install --user`.
installKernelspec
::
Bool
->
KernelSpecOptions
->
Sh
()
installKernelspec
::
Bool
->
KernelSpecOptions
->
Sh
()
installKernelspec
replace
opts
=
void
$
do
installKernelspec
replace
opts
=
void
$
do
ihaskellPath
<-
getIHaskellPath
ihaskellPath
<-
getIHaskellPath
...
@@ -155,13 +159,14 @@ installKernelspec replace opts = void $ do
...
@@ -155,13 +159,14 @@ installKernelspec replace opts = void $ do
Just
file
->
[
"--conf"
,
file
])
Just
file
->
[
"--conf"
,
file
])
++
[
"--ghclib"
,
kernelSpecGhcLibdir
opts
]
++
[
"--ghclib"
,
kernelSpecGhcLibdir
opts
]
let
kernelSpec
=
KernelSpec
{
kernelDisplayName
=
"Haskell"
let
kernelSpec
=
KernelSpec
{
kernelDisplayName
=
"Haskell"
,
kernelLanguage
=
kernelName
,
kernelLanguage
=
kernelName
,
kernelCommand
=
[
ihaskellPath
,
"kernel"
,
"{connection_file}"
]
++
kernelFlags
,
kernelCommand
=
[
ihaskellPath
,
"kernel"
,
"{connection_file}"
]
++
kernelFlags
}
}
-- Create a temporary directory. Use this temporary directory to make a kernelspec
-- Create a temporary directory. Use this temporary directory to make a kernelspec
directory; then,
--
directory; then,
shell out to IPython to install this kernelspec directory.
-- shell out to IPython to install this kernelspec directory.
withTmpDir
$
\
tmp
->
do
withTmpDir
$
\
tmp
->
do
let
kernelDir
=
tmp
</>
kernelName
let
kernelDir
=
tmp
</>
kernelName
let
filename
=
kernelDir
</>
"kernel.json"
let
filename
=
kernelDir
</>
"kernel.json"
...
@@ -185,16 +190,14 @@ kernelSpecCreated = do
...
@@ -185,16 +190,14 @@ kernelSpecCreated = do
let
kernelspecs
=
map
T
.
strip
$
lines
out
let
kernelspecs
=
map
T
.
strip
$
lines
out
return
$
kernelName
`
elem
`
kernelspecs
return
$
kernelName
`
elem
`
kernelspecs
-- | Replace "~" with $HOME if $HOME is defined.
-- | Replace "~" with $HOME if $HOME is defined. Otherwise, do nothing.
-- Otherwise, do nothing.
subHome
::
String
->
IO
String
subHome
::
String
->
IO
String
subHome
path
=
shelly
$
do
subHome
path
=
shelly
$
do
home
<-
unpack
<$>
fromMaybe
"~"
<$>
get_env
"HOME"
home
<-
unpack
<$>
fromMaybe
"~"
<$>
get_env
"HOME"
return
$
replace
"~"
home
path
return
$
replace
"~"
home
path
-- | Get the path to an executable. If it doensn't exist, fail with an error message complaining
-- | Get the path to an executable. If it doensn't exist, fail with an
-- about it.
-- error message complaining about it.
path
::
Text
->
Sh
FilePath
path
::
Text
->
Sh
FilePath
path
exe
=
do
path
exe
=
do
path
<-
which
$
fromText
exe
path
<-
which
$
fromText
exe
...
@@ -229,9 +232,8 @@ getIHaskellPath = do
...
@@ -229,9 +232,8 @@ getIHaskellPath = do
if
FS
.
absolute
f
if
FS
.
absolute
f
then
return
$
FS
.
encodeString
f
then
return
$
FS
.
encodeString
f
else
else
-- Check whether this is a relative path, or just 'IHaskell' with $PATH
-- Check whether this is a relative path, or just 'IHaskell' with $PATH resolution done by
-- resolution done by the shell. If it's just 'IHaskell', use the $PATH
-- the shell. If it's just 'IHaskell', use the $PATH variable to find where IHaskell lives.
-- variable to find where IHaskell lives.
if
FS
.
filename
f
==
f
if
FS
.
filename
f
==
f
then
do
then
do
ihaskellPath
<-
which
"ihaskell"
ihaskellPath
<-
which
"ihaskell"
...
...
src/IHaskell/Types.hs
View file @
7ba7c4d1
{-# LANGUAGE NoImplicitPrelude, OverloadedStrings, DeriveDataTypeable, DeriveGeneric #-}
{-# LANGUAGE NoImplicitPrelude, OverloadedStrings, DeriveDataTypeable, DeriveGeneric
, ExistentialQuantification
#-}
{-# LANGUAGE ExistentialQuantification #-}
-- | Description : All message type definitions.
-- | Description : All message type definitions.
module
IHaskell.Types
(
module
IHaskell.Types
(
Message
(
..
),
Message
(
..
),
MessageHeader
(
..
),
MessageHeader
(
..
),
MessageType
(
..
),
MessageType
(
..
),
Username
,
Username
,
Metadata
(
..
),
Metadata
(
..
),
replyType
,
replyType
,
ExecutionState
(
..
),
ExecutionState
(
..
),
StreamType
(
..
),
StreamType
(
..
),
MimeType
(
..
),
MimeType
(
..
),
DisplayData
(
..
),
DisplayData
(
..
),
...
@@ -16,7 +16,8 @@ module IHaskell.Types (
...
@@ -16,7 +16,8 @@ module IHaskell.Types (
ExecuteReplyStatus
(
..
),
ExecuteReplyStatus
(
..
),
KernelState
(
..
),
KernelState
(
..
),
LintStatus
(
..
),
LintStatus
(
..
),
Width
,
Height
,
Width
,
Height
,
Display
(
..
),
Display
(
..
),
defaultKernelState
,
defaultKernelState
,
extractPlain
,
extractPlain
,
...
@@ -40,8 +41,8 @@ import IHaskell.IPython.Kernel
...
@@ -40,8 +41,8 @@ import IHaskell.IPython.Kernel
-- | A class for displayable Haskell types.
-- | A class for displayable Haskell types.
--
--
-- IHaskell's displaying of results behaves as if these two
-- IHaskell's displaying of results behaves as if these two
overlapping/undecidable instances also
--
overlapping/undecidable instances also
existed:
-- existed:
--
--
-- > instance (Show a) => IHaskellDisplay a
-- > instance (Show a) => IHaskellDisplay a
-- > instance Show a where shows _ = id
-- > instance Show a where shows _ = id
...
@@ -50,12 +51,10 @@ class IHaskellDisplay a where
...
@@ -50,12 +51,10 @@ class IHaskellDisplay a where
-- | Display as an interactive widget.
-- | Display as an interactive widget.
class
IHaskellDisplay
a
=>
IHaskellWidget
a
where
class
IHaskellDisplay
a
=>
IHaskellWidget
a
where
-- | Output target name for this widget.
-- | Output target name for this widget. The actual input parameter should be ignored.
-- The actual input parameter should be ignored.
targetName
::
a
->
String
targetName
::
a
->
String
-- | Called when the comm is opened. Allows additional messages to be sent
-- | Called when the comm is opened. Allows additional messages to be sent after comm open.
-- after comm open.
open
::
a
-- ^ Widget to open a comm port with.
open
::
a
-- ^ Widget to open a comm port with.
->
(
Value
->
IO
()
)
-- ^ Way to respond to the message.
->
(
Value
->
IO
()
)
-- ^ Way to respond to the message.
->
IO
()
->
IO
()
...
@@ -89,12 +88,12 @@ instance IHaskellWidget Widget where
...
@@ -89,12 +88,12 @@ instance IHaskellWidget Widget where
instance
Show
Widget
where
instance
Show
Widget
where
show
_
=
"<Widget>"
show
_
=
"<Widget>"
-- | Wrapper for ipython-kernel's DisplayData which allows sending multiple results from the same
-- | Wrapper for ipython-kernel's DisplayData which allows sending multiple
-- expression.
-- results from the same expression.
data
Display
=
Display
[
DisplayData
]
data
Display
=
Display
[
DisplayData
]
|
ManyDisplay
[
Display
]
|
ManyDisplay
[
Display
]
deriving
(
Show
,
Typeable
,
Generic
)
deriving
(
Show
,
Typeable
,
Generic
)
instance
Serialize
Display
instance
Serialize
Display
instance
Monoid
Display
where
instance
Monoid
Display
where
...
@@ -102,13 +101,15 @@ instance Monoid Display where
...
@@ -102,13 +101,15 @@ instance Monoid Display where
ManyDisplay
a
`
mappend
`
ManyDisplay
b
=
ManyDisplay
(
a
++
b
)
ManyDisplay
a
`
mappend
`
ManyDisplay
b
=
ManyDisplay
(
a
++
b
)
ManyDisplay
a
`
mappend
`
b
=
ManyDisplay
(
a
++
[
b
])
ManyDisplay
a
`
mappend
`
b
=
ManyDisplay
(
a
++
[
b
])
a
`
mappend
`
ManyDisplay
b
=
ManyDisplay
(
a
:
b
)
a
`
mappend
`
ManyDisplay
b
=
ManyDisplay
(
a
:
b
)
a
`
mappend
`
b
=
ManyDisplay
[
a
,
b
]
a
`
mappend
`
b
=
ManyDisplay
[
a
,
b
]
instance
Semigroup
Display
where
instance
Semigroup
Display
where
a
<>
b
=
a
`
mappend
`
b
a
<>
b
=
a
`
mappend
`
b
-- | All state stored in the kernel between executions.
-- | All state stored in the kernel between executions.
data
KernelState
=
KernelState
{
getExecutionCounter
::
Int
data
KernelState
=
KernelState
{
getExecutionCounter
::
Int
,
getLintStatus
::
LintStatus
-- Whether to use hlint, and what arguments to pass it.
,
getLintStatus
::
LintStatus
-- Whether to use hlint, and what arguments to pass it.
,
useSvg
::
Bool
,
useSvg
::
Bool
,
useShowErrors
::
Bool
,
useShowErrors
::
Bool
...
@@ -120,7 +121,8 @@ data KernelState = KernelState { getExecutionCounter :: Int
...
@@ -120,7 +121,8 @@ data KernelState = KernelState { getExecutionCounter :: Int
deriving
Show
deriving
Show
defaultKernelState
::
KernelState
defaultKernelState
::
KernelState
defaultKernelState
=
KernelState
{
getExecutionCounter
=
1
defaultKernelState
=
KernelState
{
getExecutionCounter
=
1
,
getLintStatus
=
LintOn
,
getLintStatus
=
LintOn
,
useSvg
=
True
,
useSvg
=
True
,
useShowErrors
=
False
,
useShowErrors
=
False
...
@@ -131,10 +133,12 @@ defaultKernelState = KernelState { getExecutionCounter = 1
...
@@ -131,10 +133,12 @@ defaultKernelState = KernelState { getExecutionCounter = 1
}
}
-- | Kernel options to be set via `:set` and `:option`.
-- | Kernel options to be set via `:set` and `:option`.
data
KernelOpt
=
KernelOpt
{
data
KernelOpt
=
getOptionName
::
[
String
],
-- ^ Ways to set this option via `:option`
KernelOpt
getSetName
::
[
String
],
-- ^ Ways to set this option via `:set`
{
getOptionName
::
[
String
]
-- ^ Ways to set this option via `:option`
getUpdateKernelState
::
KernelState
->
KernelState
-- ^ Function to update the kernel state.
,
getSetName
::
[
String
]
-- ^ Ways to set this option via `:set`
,
getUpdateKernelState
::
KernelState
->
KernelState
-- ^ Function to update the kernel
-- state.
}
}
kernelOpts
::
[
KernelOpt
]
kernelOpts
::
[
KernelOpt
]
...
@@ -152,23 +156,24 @@ kernelOpts =
...
@@ -152,23 +156,24 @@ kernelOpts =
]
]
-- | Current HLint status.
-- | Current HLint status.
data
LintStatus
data
LintStatus
=
LintOn
=
LintOn
|
LintOff
|
LintOff
deriving
(
Eq
,
Show
)
deriving
(
Eq
,
Show
)
data
CommInfo
=
CommInfo
Widget
UUID
String
deriving
Show
data
CommInfo
=
CommInfo
Widget
UUID
String
deriving
Show
-- | Output of evaluation.
-- | Output of evaluation.
data
EvaluationResult
=
data
EvaluationResult
=
-- | An intermediate result which communicates what has been printed thus
-- | An intermediate result which communicates what has been printed thus
-- far.
-- far.
IntermediateResult
{
IntermediateResult
outputs
::
Display
-- ^ Display outputs.
{
outputs
::
Display
-- ^ Display outputs.
}
}
|
FinalResult
{
|
outputs
::
Display
,
-- ^ Display outputs.
FinalResult
pagerOut
::
String
,
-- ^ Text to display in the IPython pager.
{
outputs
::
Display
-- ^ Display outputs.
startComms
::
[
CommInfo
]
-- ^ Comms to start.
,
pagerOut
::
String
-- ^ Text to display in the IPython pager.
,
startComms
::
[
CommInfo
]
-- ^ Comms to start.
}
}
deriving
Show
deriving
Show
src/Main.hs
View file @
7ba7c4d1
{-# LANGUAGE NoImplicitPrelude, CPP, OverloadedStrings, ScopedTypeVariables, QuasiQuotes #-}
{-# LANGUAGE NoImplicitPrelude, CPP, OverloadedStrings, ScopedTypeVariables, QuasiQuotes #-}
-- | Description : Argument parsing and basic messaging loop, using Haskell
-- | Description : Argument parsing and basic messaging loop, using Haskell
-- Chans to communicate with the ZeroMQ sockets.
-- Chans to communicate with the ZeroMQ sockets.
module
Main
where
module
Main
(
main
)
where
-- Prelude imports.
-- Prelude imports.
import
ClassyPrelude
hiding
(
last
,
liftIO
,
readChan
,
writeChan
)
import
ClassyPrelude
hiding
(
last
,
liftIO
,
readChan
,
writeChan
)
...
@@ -71,7 +72,7 @@ ihaskell (Args (Kernel (Just filename)) args) = do
...
@@ -71,7 +72,7 @@ ihaskell (Args (Kernel (Just filename)) args) = do
showingHelp
::
IHaskellMode
->
[
Argument
]
->
IO
()
->
IO
()
showingHelp
::
IHaskellMode
->
[
Argument
]
->
IO
()
->
IO
()
showingHelp
mode
flags
act
=
showingHelp
mode
flags
act
=
case
find
(
==
Help
)
flags
of
case
find
(
==
Help
)
flags
of
Just
_
->
Just
_
->
putStrLn
$
pack
$
help
mode
putStrLn
$
pack
$
help
mode
Nothing
->
Nothing
->
...
@@ -114,13 +115,11 @@ runKernel kernelOpts profileSrc = do
...
@@ -114,13 +115,11 @@ runKernel kernelOpts profileSrc = do
-- Receive and reply to all messages on the shell socket.
-- Receive and reply to all messages on the shell socket.
interpret
libdir
True
$
do
interpret
libdir
True
$
do
-- Ignore Ctrl-C the first time. This has to go inside the
-- Ignore Ctrl-C the first time. This has to go inside the `interpret`, because GHC API resets the
-- `interpret`, because GHC API resets the signal handlers for some
-- signal handlers for some reason (completely unknown to me).
-- reason (completely unknown to me).
liftIO
ignoreCtrlC
liftIO
ignoreCtrlC
-- Initialize the context by evaluating everything we got from the
-- Initialize the context by evaluating everything we got from the command line flags.
-- command line flags.
let
noPublish
_
=
return
()
let
noPublish
_
=
return
()
evaluator
line
=
void
$
do
evaluator
line
=
void
$
do
-- Create a new state each time.
-- Create a new state each time.
...
@@ -140,9 +139,8 @@ runKernel kernelOpts profileSrc = do
...
@@ -140,9 +139,8 @@ runKernel kernelOpts profileSrc = do
-- Create a header for the reply.
-- Create a header for the reply.
replyHeader
<-
createReplyHeader
(
header
request
)
replyHeader
<-
createReplyHeader
(
header
request
)
-- We handle comm messages and normal ones separately.
-- We handle comm messages and normal ones separately. The normal ones are a standard
-- The normal ones are a standard request/response style, while comms
-- request/response style, while comms can be anything, and don't necessarily require a response.
-- can be anything, and don't necessarily require a response.
if
isCommMessage
request
if
isCommMessage
request
then
liftIO
$
do
then
liftIO
$
do
oldState
<-
takeMVar
state
oldState
<-
takeMVar
state
...
@@ -185,40 +183,39 @@ createReplyHeader parent = do
...
@@ -185,40 +183,39 @@ createReplyHeader parent = do
let
repType
=
fromMaybe
err
(
replyType
$
msgType
parent
)
let
repType
=
fromMaybe
err
(
replyType
$
msgType
parent
)
err
=
error
$
"No reply for message "
++
show
(
msgType
parent
)
err
=
error
$
"No reply for message "
++
show
(
msgType
parent
)
return
MessageHeader
{
return
identifiers
=
identifiers
parent
,
MessageHeader
parentHeader
=
Just
parent
,
{
identifiers
=
identifiers
parent
metadata
=
Map
.
fromList
[]
,
,
parentHeader
=
Just
parent
messageId
=
newMessageId
,
,
metadata
=
Map
.
fromList
[]
sessionId
=
sessionId
parent
,
,
messageId
=
newMessageId
username
=
username
parent
,
,
sessionId
=
sessionId
parent
msgType
=
repType
,
username
=
username
parent
,
msgType
=
repType
}
}
-- | Compute a reply to a message.
-- | Compute a reply to a message.
replyTo
::
ZeroMQInterface
->
Message
->
MessageHeader
->
KernelState
->
Interpreter
(
KernelState
,
Message
)
replyTo
::
ZeroMQInterface
->
Message
->
MessageHeader
->
KernelState
->
Interpreter
(
KernelState
,
Message
)
-- Reply to kernel info requests with a kernel info reply. No computation needs to be done, as a
-- Reply to kernel info requests with a kernel info reply. No computation
-- kernel info reply is a static object (all info is hard coded into the representation of that
-- needs to be done, as a kernel info reply is a static object (all info is
-- message type).
-- hard coded into the representation of that message type).
replyTo
_
KernelInfoRequest
{}
replyHeader
state
=
replyTo
_
KernelInfoRequest
{}
replyHeader
state
=
return
(
state
,
KernelInfoReply
{
return
header
=
replyHeader
,
(
state
,
KernelInfoReply
language
=
"haskell"
,
{
header
=
replyHeader
versionList
=
ghcVersionInts
,
language
=
"haskell"
,
versionList
=
ghcVersionInts
})
})
-- Reply to a shutdown request by exiting the main thread.
-- Reply to a shutdown request by exiting the main thread. Before shutdown, reply to the request to
-- Before shutdown, reply to the request to let the frontend know shutdown
-- let the frontend know shutdown is happening.
-- is happening.
replyTo
interface
ShutdownRequest
{
restartPending
=
restartPending
}
replyHeader
_
=
liftIO
$
do
replyTo
interface
ShutdownRequest
{
restartPending
=
restartPending
}
replyHeader
_
=
liftIO
$
do
writeChan
(
shellReplyChannel
interface
)
$
ShutdownReply
replyHeader
restartPending
writeChan
(
shellReplyChannel
interface
)
$
ShutdownReply
replyHeader
restartPending
exitSuccess
exitSuccess
-- Reply to an execution request. The reply itself does not require
-- Reply to an execution request. The reply itself does not require computation, but this causes
-- computation, but this causes messages to be sent to the IOPub socket
-- messages to be sent to the IOPub socket with the output of the code in the execution request.
-- with the output of the code in the execution request.
replyTo
interface
req
@
ExecuteRequest
{
getCode
=
code
}
replyHeader
state
=
do
replyTo
interface
req
@
ExecuteRequest
{
getCode
=
code
}
replyHeader
state
=
do
-- Convenience function to send a message to the IOPub socket.
-- Convenience function to send a message to the IOPub socket.
let
send
msg
=
liftIO
$
writeChan
(
iopubChannel
interface
)
msg
let
send
msg
=
liftIO
$
writeChan
(
iopubChannel
interface
)
msg
...
@@ -226,18 +223,16 @@ replyTo interface req@ExecuteRequest{ getCode = code } replyHeader state = do
...
@@ -226,18 +223,16 @@ replyTo interface req@ExecuteRequest{ getCode = code } replyHeader state = do
dir
<-
liftIO
getIHaskellDir
dir
<-
liftIO
getIHaskellDir
liftIO
$
Stdin
.
recordParentHeader
dir
$
header
req
liftIO
$
Stdin
.
recordParentHeader
dir
$
header
req
-- Notify the frontend that the kernel is busy computing.
-- Notify the frontend that the kernel is busy computing. All the headers are copies of the reply
-- All the headers are copies of the reply header with a different
-- header with a different message type, because this preserves the session ID, parent header, and
-- message type, because this preserves the session ID, parent header,
-- other important information.
-- and other important information.
busyHeader
<-
liftIO
$
dupHeader
replyHeader
StatusMessage
busyHeader
<-
liftIO
$
dupHeader
replyHeader
StatusMessage
send
$
PublishStatus
busyHeader
Busy
send
$
PublishStatus
busyHeader
Busy
-- Construct a function for publishing output as this is going.
-- Construct a function for publishing output as this is going. This function accepts a boolean
-- This function accepts a boolean indicating whether this is the final
-- indicating whether this is the final output and the thing to display. Store the final outputs in
-- output and the thing to display. Store the final outputs in a list so
-- a list so that when we receive an updated non-final output, we can clear the entire output and
-- that when we receive an updated non-final output, we can clear the
-- re-display with the updated output.
-- entire output and re-display with the updated output.
displayed
<-
liftIO
$
newMVar
[]
displayed
<-
liftIO
$
newMVar
[]
updateNeeded
<-
liftIO
$
newMVar
False
updateNeeded
<-
liftIO
$
newMVar
False
pagerOutput
<-
liftIO
$
newMVar
""
pagerOutput
<-
liftIO
$
newMVar
""
...
@@ -254,7 +249,8 @@ replyTo interface req@ExecuteRequest{ getCode = code } replyHeader state = do
...
@@ -254,7 +249,8 @@ replyTo interface req@ExecuteRequest{ getCode = code } replyHeader state = do
convertSvgToHtml
x
=
x
convertSvgToHtml
x
=
x
makeSvgImg
base64data
=
unpack
$
"<img src=
\"
data:image/svg+xml;base64,"
++
base64data
++
"
\"
/>"
makeSvgImg
base64data
=
unpack
$
"<img src=
\"
data:image/svg+xml;base64,"
++
base64data
++
"
\"
/>"
prependCss
(
DisplayData
MimeHtml
html
)
=
DisplayData
MimeHtml
$
concat
[
"<style>"
,
pack
ihaskellCSS
,
"</style>"
,
html
]
prependCss
(
DisplayData
MimeHtml
html
)
=
DisplayData
MimeHtml
$
concat
[
"<style>"
,
pack
ihaskellCSS
,
"</style>"
,
html
]
prependCss
x
=
x
prependCss
x
=
x
startComm
::
CommInfo
->
IO
()
startComm
::
CommInfo
->
IO
()
...
@@ -271,9 +267,10 @@ replyTo interface req@ExecuteRequest{ getCode = code } replyHeader state = do
...
@@ -271,9 +267,10 @@ replyTo interface req@ExecuteRequest{ getCode = code } replyHeader state = do
publish
::
EvaluationResult
->
IO
()
publish
::
EvaluationResult
->
IO
()
publish
result
=
do
publish
result
=
do
let
final
=
case
result
of
let
final
=
IntermediateResult
{}
->
False
case
result
of
FinalResult
{}
->
True
IntermediateResult
{}
->
False
FinalResult
{}
->
True
outs
=
outputs
result
outs
=
outputs
result
-- If necessary, clear all previous output and redraw.
-- If necessary, clear all previous output and redraw.
...
@@ -286,12 +283,11 @@ replyTo interface req@ExecuteRequest{ getCode = code } replyHeader state = do
...
@@ -286,12 +283,11 @@ replyTo interface req@ExecuteRequest{ getCode = code } replyHeader state = do
-- Draw this message.
-- Draw this message.
sendOutput
outs
sendOutput
outs
-- If this is the final message, add it to the list of completed
-- If this is the final message, add it to the list of completed messages. If it isn't, make sure we
-- messages. If it isn't, make sure we clear it later by marking
-- clear it later by marking update needed as true.
-- update needed as true.
modifyMVar_
updateNeeded
(
const
$
return
$
not
final
)
modifyMVar_
updateNeeded
(
const
$
return
$
not
final
)
when
final
$
do
when
final
$
do
modifyMVar_
displayed
(
return
.
(
outs
:
))
modifyMVar_
displayed
(
return
.
(
outs
:
))
-- Start all comms that need to be started.
-- Start all comms that need to be started.
mapM_
startComm
$
startComms
result
mapM_
startComm
$
startComms
result
...
@@ -319,11 +315,12 @@ replyTo interface req@ExecuteRequest{ getCode = code } replyHeader state = do
...
@@ -319,11 +315,12 @@ replyTo interface req@ExecuteRequest{ getCode = code } replyHeader state = do
pager
<-
if
usePager
state
pager
<-
if
usePager
state
then
liftIO
$
readMVar
pagerOutput
then
liftIO
$
readMVar
pagerOutput
else
return
""
else
return
""
return
(
updatedState
,
ExecuteReply
{
return
header
=
replyHeader
,
(
updatedState
,
ExecuteReply
pagerOutput
=
pager
,
{
header
=
replyHeader
executionCounter
=
execCount
,
,
pagerOutput
=
pager
status
=
Ok
,
executionCounter
=
execCount
,
status
=
Ok
})
})
...
@@ -337,24 +334,25 @@ replyTo _ req@CompleteRequest{} replyHeader state = do
...
@@ -337,24 +334,25 @@ replyTo _ req@CompleteRequest{} replyHeader state = do
reply
=
CompleteReply
replyHeader
(
map
pack
completions
)
start
end
Map
.
empty
True
reply
=
CompleteReply
replyHeader
(
map
pack
completions
)
start
end
Map
.
empty
True
return
(
state
,
reply
)
return
(
state
,
reply
)
-- Reply to the object_info_request message. Given an object name, return
-- Reply to the object_info_request message. Given an object name, return
the associated type
--
the associated type
calculated by GHC.
-- calculated by GHC.
replyTo
_
ObjectInfoRequest
{
objectName
=
oname
}
replyHeader
state
=
do
replyTo
_
ObjectInfoRequest
{
objectName
=
oname
}
replyHeader
state
=
do
docs
<-
pack
<$>
info
(
unpack
oname
)
docs
<-
pack
<$>
info
(
unpack
oname
)
let
reply
=
ObjectInfoReply
{
let
reply
=
ObjectInfoReply
header
=
replyHeader
,
{
header
=
replyHeader
objectName
=
oname
,
,
objectName
=
oname
objectFound
=
strip
docs
/=
""
,
,
objectFound
=
strip
docs
/=
""
objectTypeString
=
docs
,
,
objectTypeString
=
docs
objectDocString
=
docs
,
objectDocString
=
docs
}
}
return
(
state
,
reply
)
return
(
state
,
reply
)
-- TODO: Implement history_reply.
-- TODO: Implement history_reply.
replyTo
_
HistoryRequest
{}
replyHeader
state
=
do
replyTo
_
HistoryRequest
{}
replyHeader
state
=
do
let
reply
=
HistoryReply
{
let
reply
=
HistoryReply
header
=
replyHeader
,
{
header
=
replyHeader
historyReply
=
[]
-- FIXME
-- FIXME
,
historyReply
=
[]
}
}
return
(
state
,
reply
)
return
(
state
,
reply
)
...
...
verify_formatting.py
0 → 100755
View file @
7ba7c4d1
#!/usr/bin/env python3
from
__future__
import
print_function
import
sys
import
os
import
subprocess
def
hindent
(
contents
):
with
open
(
".tmp3"
,
"w"
)
as
f
:
f
.
write
(
contents
)
with
open
(
".tmp3"
,
"r"
)
as
f
:
output
=
subprocess
.
check_output
([
"hindent"
,
"--style"
,
"gibiansky"
],
stdin
=
f
)
return
output
.
decode
(
'utf-8'
)
def
diff
(
src1
,
src2
):
# Ignore trailing newlines
if
src1
[
-
1
]
==
"
\n
"
:
src1
=
src1
[:
-
1
]
if
src2
[
-
1
]
==
"
\n
"
:
src2
=
src2
[:
-
1
]
with
open
(
".tmp1"
,
"w"
)
as
f1
:
f1
.
write
(
src1
)
with
open
(
".tmp2"
,
"w"
)
as
f2
:
f2
.
write
(
src2
)
try
:
output
=
subprocess
.
check_output
([
"diff"
,
".tmp1"
,
".tmp2"
])
return
output
.
decode
(
'utf-8'
)
except
subprocess
.
CalledProcessError
as
e
:
return
e
.
output
.
decode
(
'utf-8'
)
# Verify that we're in the right directory
try
:
open
(
"ihaskell.cabal"
,
"r"
)
.
close
()
except
:
print
(
sys
.
argv
[
0
],
"must be run from the ihaskell directory"
,
file
=
sys
.
stderr
)
# Find all the source files
sources
=
[]
for
root
,
dirnames
,
filenames
in
os
.
walk
(
"src"
):
for
filename
in
filenames
:
if
filename
.
endswith
(
".hs"
):
sources
.
append
(
os
.
path
.
join
(
root
,
filename
))
hindent_outputs
=
{}
for
source_file
in
sources
:
print
(
"Formatting file"
,
source_file
)
with
open
(
source_file
,
"r"
)
as
f
:
original_source
=
f
.
read
()
formatted_source
=
hindent
(
original_source
)
hindent_outputs
[
source_file
]
=
(
original_source
,
formatted_source
)
diffs
=
{
filename
:
diff
(
original
,
formatted
)
for
(
filename
,
(
original
,
formatted
))
in
hindent_outputs
.
items
()}
incorrect_formatting
=
False
for
filename
,
diff
in
diffs
.
items
():
if
diff
:
incorrect_formatting
=
True
print
(
'Incorrect formatting in'
,
filename
)
print
(
'='
*
10
)
print
(
diff
)
if
incorrect_formatting
:
sys
.
exit
(
1
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment