diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,332 @@
+# Eval plugin for the [Haskell Language Server](https://github.com/haskell/haskell-language-server#readme)
+
+The Eval plugin evaluates code inserted in comments.
+
+This is mainly useful to test and document functions and to quickly evaluate small expressions.
+
+Every line of code to be evaluated is introduced by __>>>__
+
+A quick calculation:
+
+```
+-- >>> 2**4.5/pi
+-- 7.202530529256849
+```
+
+A little test for the `double` function:
+
+```
+{- |
+A doubling function.
+
+>>> double 11
+22
+-}
+double = (2*)
+```
+
+# Demo
+
+![Eval](demo.gif)
+
+# Test Structure
+
+A test is composed by a sequence of contiguous lines, the result of their evaluation is inserted after the test body:
+
+```
+>>> "AB" ++ "CD"
+>>> "CD" ++ "AB"
+"ABCD"
+"CDAB"
+```
+
+You execute a test by clicking on the _Evaluate_ code lens that appears above it (or _Refresh_, if the test has been run previously).
+
+All tests in the same comment block are executed together.
+
+
+Tests can appear in all kind of comments:
+* plain comments (both single and multi line)
+```
+{-
+>>> "ab" ++ "c"
+"abc"
+-}
+
+-- >>> "ab" ++ "c"
+-- "abc"
+```
+* Haddock commands (both single and multi line, forward and backward)
+```
+{-
+>>> "ab" ++ "c"
+"abc"
+-}
+
+-- >>> "ab" ++ "c"
+-- "abc"
+
+double a = a + a
+-- ^ A doubling function
+-- >>> double 11
+-- 22
+```
+
+Both plain Haskell and Literate Haskell (Bird-style only) source files are supported.
+
+# Test Components
+
+In general, a test is a sequence of:
+* imports
+* directives
+* statements
+* expressions
+* properties
+
+in no particular order, with every line introduced by __>>>__ (or __prop>__ in the case of properties).
+
+### Imports
+
+```
+>>> import Data.List
+>>> import GHC.TypeNats
+```
+
+From any package in scope but currently NOT from modules in the same source directory.
+
+### Language Extensions
+
+```
+>>> :set -XScopedTypeVariables -XStandaloneDeriving -XDataKinds -XTypeOperators -XExplicitNamespaces
+```
+
+### Statements and Declarations
+
+Function declarations (optionally introduced by __let__):
+
+```
+>>> let tuple x = (x,x)
+>>> let one=1;two=2
+>>> triple x = (x,x,x)
+```
+
+Any other declaration:
+
+```
+>>> data TertiumDatur = Truly | Falsely | Other deriving Show
+>>> class Display a where display :: a -> String
+>>> instance Display TertiumDatur where display = show
+```
+
+Definitions are available to following tests in the __same__ comment:
+
+```
+{-
+>>> two = 2
+
+>>> two
+2
+-}
+
+-- >>> two
+-- Variable not in scope: two
+```
+
+If you want definitions to be available to all tests in the module, define a setup section:
+
+```
+-- $setup
+-- >>> eleven = 11
+
+{-
+eleven is now available to any test:
+
+>>> eleven*2
+22
+-}
+```
+
+
+### Type and Kind directives
+
+```
+>>> :type Truly
+Truly :: TertiumDatur
+
+>>> :kind TertiumDatur
+TertiumDatur :: *
+
+>>> :type 3
+3 :: forall p. Num p => p
+
+>>> :type +d 3
+3 :: Integer
+
+>>> type N = 1
+>>> type M = 40
+>>> :kind! N + M + 1
+N + M + 1 :: Nat
+= 42
+```
+
+### Expressions
+
+```
+>>> tuple 2
+>>> triple 3
+>>> display Other
+(2,2)
+(3,3,3)
+"Other"
+```
+
+IO expressions can also be evaluated but their output to stdout/stderr is NOT captured:
+
+```
+>>> print "foo"
+()
+```
+
+### Properties
+
+```
+prop> \(l::[Int]) -> reverse (reverse l) == l
++++ OK, passed 100 tests.
+```
+
+# Haddock vs Plain Comments
+
+There is a conceptual difference between Haddock and plain comments:
+* Haddock comments constitute the external module's documentation, they state the contract between the implementor and the module users (API)
+* Plain comments are internal documentation meant to explain how the code works (implementation).
+
+This conceptual difference is reflected in the way tests results are refreshed by the Eval plugin.
+
+Say that we have defined a `double` function as:
+
+```
+double = (*2)
+```
+
+And, in an Haddock comment, we run the test:
+
+```
+{- |
+>>> double 11
+22
+-}
+```
+
+We then change the definition to:
+
+```
+double = (*3)
+```
+
+When we refresh the test, its current result is compared with the previous one and differences are displayed (as they change the API):
+
+```
+{- |
+>>> double 11
+WAS 22
+NOW 33
+-}
+```
+
+On the contrary, if the test were into a plain comment, the result would simply be replaced:
+
+```
+{-
+>>> double 11
+33
+-}
+```
+
+# Multiline Output
+
+By default, the output of every expression is returned as a single line.
+
+This is a problem if you want, for example, to pretty print a value (in this case using the [pretty-simple](https://hackage.haskell.org/package/pretty-simple) package):
+
+```
+>>> import Text.Pretty.Simple
+>>> pShowNoColor [1..3]
+"[ 1\n, 2\n, 3\n]"
+```
+
+We could try to print the pretty-print output, but stdout is not captured so we get just a ():
+
+```
+>>> print $ pShowNoColor [1..7]
+()
+```
+
+To display it properly, we can exploit the fact that the output of an error is displayed as a multi-line text:
+
+```
+>>> import qualified Data.Text.Lazy as TL
+>>> import Text.Pretty.Simple
+>>> prettyPrint v = error (TL.unpack $ pShowNoColor v) :: IO String
+>>> prettyPrint [1..3]
+[ 1
+, 2
+, 3
+]
+```
+
+# Differences with doctest
+
+Though the Eval plugin functionality is quite similar to that of [doctest](https://hackage.haskell.org/package/doctest), some doctest's features are not supported.
+
+### Capturing Stdout
+
+Only the value of an IO expression is spliced in, not its output:
+
+```
+>>> print "foo"
+()
+```
+
+### Pattern Matching
+
+The arbitrary content matcher __...__ is unsupported.
+
+### Missing lambda abstractions in property tests
+
+Variables are not automatically introduced:
+
+```
+prop> reverse (reverse l) == (l::[Int])
+Variable not in scope: l :: [Int]
+```
+
+This works:
+
+```
+prop> \(l::[Int]) -> reverse (reverse l) == l
++++ OK, passed 100 tests.
+```
+
+### Multiline Expressions
+
+```
+ >>> :{
+  let
+    x = 1
+    y = 2
+  in x + y + multiline
+ :}
+```
+
+# Acknowledgments
+
+Design/features derived from:
+
+* [GHCi](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html)
+
+* [Haddock's](https://www.haskell.org/haddock/doc/html/ch03s08.html#idm140354810775744) Examples and Properties
+
+* [Doctest](https://hackage.haskell.org/package/doctest)
+
+* the REPLoid feature of [Dante](https://github.com/jyp/dante)
+
diff --git a/hls-eval-plugin.cabal b/hls-eval-plugin.cabal
--- a/hls-eval-plugin.cabal
+++ b/hls-eval-plugin.cabal
@@ -1,16 +1,24 @@
 cabal-version:      2.2
 name:               hls-eval-plugin
-version:            0.1.0.1
+version:            0.1.0.2
 synopsis:           Eval plugin for Haskell Language Server
-description:     Please see the README on GitHub at <https://github.com/haskell/haskell-language-server#readme>
-category: Development
-bug-reports:   https://github.com/haskell/haskell-language-server/issues
+description:
+  Please see the README on GitHub at <https://github.com/haskell/haskell-language-server#readme>
+
+category:           Development
+bug-reports:        https://github.com/haskell/haskell-language-server/issues
 license:            Apache-2.0
 license-file:       LICENSE
-author: https://github.com/haskell/haskell-language-server/contributors
-maintainer: https://github.com/haskell/haskell-language-server/contributors
+author:
+  https://github.com/haskell/haskell-language-server/contributors
+
+maintainer:
+  https://github.com/haskell/haskell-language-server/contributors
+
 build-type:         Simple
-extra-source-files: LICENSE
+extra-source-files:
+  LICENSE
+  README.md
 
 flag pedantic
   description: Enable -Werror
@@ -37,7 +45,7 @@
 
   build-depends:
     , aeson
-    , base       >=4.12 && <5
+    , base                  >=4.12 && <5
     , containers
     , deepseq
     , Diff
@@ -63,7 +71,7 @@
     , transformers
     , unordered-containers
 
-  ghc-options: -Wall -Wno-name-shadowing
+  ghc-options:      -Wall -Wno-name-shadowing
 
   if flag(pedantic)
     ghc-options: -Werror
diff --git a/src/Ide/Plugin/Eval.hs b/src/Ide/Plugin/Eval.hs
--- a/src/Ide/Plugin/Eval.hs
+++ b/src/Ide/Plugin/Eval.hs
@@ -3,15 +3,7 @@
 {-# OPTIONS_GHC -Wwarn #-}
 
 {- |
-A plugin inspired by:
-
-* the REPLoid feature of <https://github.com/jyp/dante Dante>
-
-* <https://www.haskell.org/haddock/doc/html/ch03s08.html#idm140354810775744 Haddock>'s Examples and Properties
-
-* <https://hackage.haskell.org/package/doctest Doctest>
-
-See the "Ide.Plugin.Eval.Tutorial" module for a full introduction to the plugin functionality.
+Eval Plugin entry point.
 -}
 module Ide.Plugin.Eval (
     descriptor,
diff --git a/src/Ide/Plugin/Eval/Parse/Token.hs b/src/Ide/Plugin/Eval/Parse/Token.hs
--- a/src/Ide/Plugin/Eval/Parse/Token.hs
+++ b/src/Ide/Plugin/Eval/Parse/Token.hs
@@ -2,7 +2,7 @@
 
 -- | Parse source code into a list of line Tokens.
 module Ide.Plugin.Eval.Parse.Token (
-    Token(..),
+    Token (..),
     TokenS,
     tokensFrom,
     unsafeContent,
@@ -11,77 +11,93 @@
     isPropLine,
     isCodeLine,
     isBlockOpen,
-    isBlockClose
+    isBlockClose,
 ) where
 
-import           Control.Monad.Combinators    (many, optional, skipManyTill,
-                                               (<|>))
-import           Data.Functor                 (($>))
-import           Data.List                    (foldl')
-import           Ide.Plugin.Eval.Parse.Parser (Parser, alphaNumChar, char,
-                                               letterChar, runParser, satisfy,
-                                               space, string, tillEnd)
-import           Ide.Plugin.Eval.Types        (Format (..), Language (..), Loc,
-                                               Located (Located))
-import           Maybes                       (fromJust, fromMaybe)
+import Control.Monad.Combinators (
+    many,
+    optional,
+    skipManyTill,
+    (<|>),
+ )
+import Data.Functor (($>))
+import Data.List (foldl')
+import Ide.Plugin.Eval.Parse.Parser (
+    Parser,
+    alphaNumChar,
+    char,
+    letterChar,
+    runParser,
+    satisfy,
+    space,
+    string,
+    tillEnd,
+ )
+import Ide.Plugin.Eval.Types (
+    Format (..),
+    Language (..),
+    Loc,
+    Located (Located),
+ )
+import Maybes (fromJust, fromMaybe)
 
 type TParser = Parser Char (State, [TokenS])
 
 data State = InCode | InSingleComment | InMultiComment deriving (Eq, Show)
 
 commentState :: Bool -> State
-commentState True  = InMultiComment
+commentState True = InMultiComment
 commentState False = InSingleComment
 
 type TokenS = Token String
 
 data Token s
-  = -- | Text, without prefix "(--)? >>>"
-    Statement s
-  | -- | Text, without prefix "(--)? prop>"
-    PropLine s
-  | -- | Text inside a comment
-    TextLine s
-  | -- | Line of code (outside comments)
-    CodeLine
-  | -- | Open of comment
-    BlockOpen {blockName :: Maybe s, blockLanguage :: Language, blockFormat :: Format}
-  | -- | Close of multi-line comment
-    BlockClose
-  deriving (Eq, Show)
+    = -- | Text, without prefix "(--)? >>>"
+      Statement s
+    | -- | Text, without prefix "(--)? prop>"
+      PropLine s
+    | -- | Text inside a comment
+      TextLine s
+    | -- | Line of code (outside comments)
+      CodeLine
+    | -- | Open of comment
+      BlockOpen {blockName :: Maybe s, blockLanguage :: Language, blockFormat :: Format}
+    | -- | Close of multi-line comment
+      BlockClose
+    deriving (Eq, Show)
 
 isStatement :: Token s -> Bool
 isStatement (Statement _) = True
-isStatement _             = False
+isStatement _ = False
 
 isTextLine :: Token s -> Bool
 isTextLine (TextLine _) = True
-isTextLine _            = False
+isTextLine _ = False
 
 isPropLine :: Token s -> Bool
 isPropLine (PropLine _) = True
-isPropLine _            = False
+isPropLine _ = False
 
 isCodeLine :: Token s -> Bool
 isCodeLine CodeLine = True
-isCodeLine _        = False
+isCodeLine _ = False
 
 isBlockOpen :: Token s -> Bool
 isBlockOpen (BlockOpen _ _ _) = True
-isBlockOpen _                 = False
+isBlockOpen _ = False
 
 isBlockClose :: Token s -> Bool
 isBlockClose BlockClose = True
-isBlockClose _          = False
+isBlockClose _ = False
 
 unsafeContent :: Token a -> a
 unsafeContent = fromJust . contentOf
 
 contentOf :: Token a -> Maybe a
 contentOf (Statement c) = Just c
-contentOf (PropLine c)  = Just c
-contentOf (TextLine c)  = Just c
-contentOf _             = Nothing
+contentOf (PropLine c) = Just c
+contentOf (TextLine c) = Just c
+contentOf _ = Nothing
 
 {- | Parse source code and return a list of located Tokens
 >>> import           Ide.Plugin.Eval.Types        (unLoc)
@@ -92,7 +108,6 @@
 
 >>> tks "test/testdata/eval/TLanguageOptions.hs"
 [BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = SingleLine},TextLine "Support for language options",CodeLine,CodeLine,CodeLine,CodeLine,BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = SingleLine},TextLine "Language options set in the module source (ScopedTypeVariables)",TextLine "also apply to tests so this works fine",Statement " f = (\\(c::Char) -> [c])",CodeLine,BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = MultiLine},TextLine "Multiple options can be set with a single `:set`",TextLine "",Statement " :set -XMultiParamTypeClasses -XFlexibleInstances",Statement " class Z a b c",BlockClose,CodeLine,BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = MultiLine},TextLine "",TextLine "",TextLine "Options apply only in the section where they are defined (unless they are in the setup section), so this will fail:",TextLine "",Statement " class L a b c",BlockClose,CodeLine,CodeLine,BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = MultiLine},TextLine "",TextLine "Options apply to all tests in the same section after their declaration.",TextLine "",TextLine "Not set yet:",TextLine "",Statement " class D",TextLine "",TextLine "Now it works:",TextLine "",Statement ":set -XMultiParamTypeClasses",Statement " class C",TextLine "",TextLine "It still works",TextLine "",Statement " class F",BlockClose,CodeLine,BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = MultiLine},TextLine "Wrong option names are reported.",Statement " :set -XWrong",BlockClose]
-
 -}
 tokensFrom :: String -> [Loc (Token String)]
 tokensFrom = tokens . lines . filter (/= '\r')
@@ -126,13 +141,13 @@
 
 Multi lines, closed on the same line:
 
->>> tokens $ ["{--}"]
+>>> tokens $ ["{","--"++"}"]
 [Located {location = 0, located = CodeLine}]
 
->>> tokens $ ["  {- IGNORED -}  "]
+>>> tokens $ ["  {","- IGNORED -","}  "]
 [Located {location = 0, located = CodeLine}]
 
->>> tokens ["{-# LANGUAGE TupleSections","#-}"]
+>>> tokens ["{","-# LANGUAGE TupleSections","#-","}"]
 [Located {location = 0, located = CodeLine},Located {location = 1, located = CodeLine}]
 
 >>> tokens []
@@ -142,14 +157,14 @@
 tokens = concatMap (\(l, vs) -> map (Located l) vs) . zip [0 ..] . reverse . snd . foldl' next (InCode, [])
   where
     next (st, tokens) ln = case runParser (aline st) ln of
-      Right (st', tokens') -> (st', tokens' : tokens)
-      Left err -> error $ unwords ["Tokens.next failed to parse", ln, err]
+        Right (st', tokens') -> (st', tokens' : tokens)
+        Left err -> error $ unwords ["Tokens.next failed to parse", ln, err]
 
 -- | Parse a line of input
 aline :: State -> TParser
-aline InCode          = optionStart <|> multi <|> singleOpen <|> codeLine
+aline InCode = optionStart <|> multi <|> singleOpen <|> codeLine
 aline InSingleComment = optionStart <|> multi <|> commentLine False <|> codeLine
-aline InMultiComment  = multiClose <|> commentLine True
+aline InMultiComment = multiClose <|> commentLine True
 
 multi :: TParser
 multi = multiOpenClose <|> multiOpen
@@ -168,7 +183,7 @@
 multiOpenClose :: TParser
 multiOpenClose = (multiStart >> multiClose) $> (InCode, [CodeLine])
 
-{-| Parses the opening of a multi line comment.
+{- | Parses the opening of a multi line comment.
 >>> runParser multiOpen $ "{"++"- $longSection this is also parsed"
 Right (InMultiComment,[BlockOpen {blockName = Just "longSection", blockLanguage = Plain, blockFormat = MultiLine},TextLine "this is also parsed"])
 
@@ -177,12 +192,12 @@
 -}
 multiOpen :: TParser
 multiOpen =
-  ( \() (maybeLanguage, maybeName) tk ->
-      (InMultiComment, [BlockOpen maybeName (defLang maybeLanguage) MultiLine, tk])
-  )
-    <$> multiStart
-    <*> languageAndName
-    <*> commentRest
+    ( \() (maybeLanguage, maybeName) tk ->
+        (InMultiComment, [BlockOpen maybeName (defLang maybeLanguage) MultiLine, tk])
+    )
+        <$> multiStart
+        <*> languageAndName
+        <*> commentRest
 
 {- | Parse the first line of a sequence of single line comments
 >>> runParser singleOpen "-- |$doc >>>11"
@@ -190,12 +205,12 @@
 -}
 singleOpen :: TParser
 singleOpen =
-  ( \() (maybeLanguage, maybeName) tk ->
-      (InSingleComment, [BlockOpen maybeName (defLang maybeLanguage) SingleLine, tk])
-  )
-    <$> singleStart
-    <*> languageAndName
-    <*> commentRest
+    ( \() (maybeLanguage, maybeName) tk ->
+        (InSingleComment, [BlockOpen maybeName (defLang maybeLanguage) SingleLine, tk])
+    )
+        <$> singleStart
+        <*> languageAndName
+        <*> commentRest
 
 {- | Parse a line in a comment
 >>> runParser (commentLine False) "x=11"
@@ -209,7 +224,7 @@
 -}
 commentLine :: Bool -> TParser
 commentLine noPrefix =
-  (\tk -> (commentState noPrefix, [tk])) <$> (optLineStart noPrefix *> commentBody)
+    (\tk -> (commentState noPrefix, [tk])) <$> (optLineStart noPrefix *> commentBody)
 
 commentRest :: Parser Char (Token [Char])
 commentRest = many space *> commentBody
@@ -224,8 +239,8 @@
 -- | Remove comment line prefix, if needed
 optLineStart :: Bool -> Parser Char ()
 optLineStart noPrefix
-  | noPrefix = pure ()
-  | otherwise = singleStart
+    | noPrefix = pure ()
+    | otherwise = singleStart
 
 singleStart :: Parser Char ()
 singleStart = (string "--" *> optional space) $> ()
@@ -275,9 +290,9 @@
 -}
 languageAndName :: Parser Char (Maybe Language, Maybe String)
 languageAndName =
-  (,) <$> optional ((char '|' <|> char '^') >> pure Haddock)
-    <*> optional
-      (char '$' *> (fromMaybe "" <$> optional name))
+    (,) <$> optional ((char '|' <|> char '^') >> pure Haddock)
+        <*> optional
+            (char '$' *> (fromMaybe "" <$> optional name))
 
 defLang :: Maybe Language -> Language
 defLang = fromMaybe Plain
