• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..15-Sep-2021-

src/Ide/Plugin/H15-Sep-2021-2,0441,416

test/H15-Sep-2021-1,651646

LICENSEH A D15-Sep-202111.1 KiB202169

README.mdH A D15-Sep-20215.9 KiB333235

hls-eval-plugin.cabalH A D15-Sep-20212.8 KiB129115

README.md

1# Eval plugin for the [Haskell Language Server](https://github.com/haskell/haskell-language-server#readme)
2
3The Eval plugin evaluates code inserted in comments.
4
5This is mainly useful to test and document functions and to quickly evaluate small expressions.
6
7Every line of code to be evaluated is introduced by __>>>__
8
9A quick calculation:
10
11```
12-- >>> 2**4.5/pi
13-- 7.202530529256849
14```
15
16A little test for the `double` function:
17
18```
19{- |
20A doubling function.
21
22>>> double 11
2322
24-}
25double = (2*)
26```
27
28# Demo
29
30![Eval](demo.gif)
31
32# Test Structure
33
34A test is composed by a sequence of contiguous lines, the result of their evaluation is inserted after the test body:
35
36```
37>>> "AB" ++ "CD"
38>>> "CD" ++ "AB"
39"ABCD"
40"CDAB"
41```
42
43You execute a test by clicking on the _Evaluate_ code lens that appears above it (or _Refresh_, if the test has been run previously).
44
45All tests in the same comment block are executed together.
46
47
48Tests can appear in all kind of comments:
49* plain comments (both single and multi line)
50```
51{-
52>>> "ab" ++ "c"
53"abc"
54-}
55
56-- >>> "ab" ++ "c"
57-- "abc"
58```
59* Haddock commands (both single and multi line, forward and backward)
60```
61{-
62>>> "ab" ++ "c"
63"abc"
64-}
65
66-- >>> "ab" ++ "c"
67-- "abc"
68
69double a = a + a
70-- ^ A doubling function
71-- >>> double 11
72-- 22
73```
74
75Modules that use CPP and Literate Haskell (Bird-style only) modules are also supported (for GHC >= 8.8).
76
77# Test Components
78
79In general, a test is a sequence of:
80* imports
81* directives
82* statements
83* expressions
84* properties
85
86in no particular order, with every line introduced by __>>>__ (or __prop>__ in the case of properties).
87
88### Imports
89
90```
91>>> import Data.List
92>>> import GHC.TypeNats
93```
94
95From any package in scope but currently NOT from modules in the same source directory.
96
97### Language Extensions
98
99```
100>>> :set -XScopedTypeVariables -XStandaloneDeriving -XDataKinds -XTypeOperators -XExplicitNamespaces
101```
102
103### Statements and Declarations
104
105Function declarations (optionally introduced by __let__):
106
107```
108>>> let tuple x = (x,x)
109>>> let one=1;two=2
110>>> triple x = (x,x,x)
111```
112
113Any other declaration:
114
115```
116>>> data TertiumDatur = Truly | Falsely | Other deriving Show
117>>> class Display a where display :: a -> String
118>>> instance Display TertiumDatur where display = show
119```
120
121Definitions are available to following tests in the __same__ comment:
122
123```
124{-
125>>> two = 2
126
127>>> two
1282
129-}
130
131-- >>> two
132-- Variable not in scope: two
133```
134
135If you want definitions to be available to all tests in the module, define a setup section:
136
137```
138-- $setup
139-- >>> eleven = 11
140
141{-
142eleven is now available to any test:
143
144>>> eleven*2
14522
146-}
147```
148
149
150### Type and Kind directives
151
152```
153>>> :type Truly
154Truly :: TertiumDatur
155
156>>> :kind TertiumDatur
157TertiumDatur :: *
158
159>>> :type 3
1603 :: forall p. Num p => p
161
162>>> :type +d 3
1633 :: Integer
164
165>>> type N = 1
166>>> type M = 40
167>>> :kind! N + M + 1
168N + M + 1 :: Nat
169= 42
170```
171
172### Expressions
173
174```
175>>> tuple 2
176>>> triple 3
177>>> display Other
178(2,2)
179(3,3,3)
180"Other"
181```
182
183IO expressions can also be evaluated but their output to stdout/stderr is NOT captured:
184
185```
186>>> print "foo"
187()
188```
189
190### Properties
191
192```
193prop> \(l::[Int]) -> reverse (reverse l) == l
194+++ OK, passed 100 tests.
195```
196
197# Haddock vs Plain Comments
198
199There is a conceptual difference between Haddock and plain comments:
200* Haddock comments constitute the external module's documentation, they state the contract between the implementor and the module users (API)
201* Plain comments are internal documentation meant to explain how the code works (implementation).
202
203This conceptual difference is reflected in the way tests results are refreshed by the Eval plugin.
204
205Say that we have defined a `double` function as:
206
207```
208double = (*2)
209```
210
211And, in an Haddock comment, we run the test:
212
213```
214{- |
215>>> double 11
21622
217-}
218```
219
220We then change the definition to:
221
222```
223double = (*3)
224```
225
226When we refresh the test, its current result is compared with the previous one and differences are displayed (as they change the API):
227
228```
229{- |
230>>> double 11
231WAS 22
232NOW 33
233-}
234```
235
236On the contrary, if the test were into a plain comment, the result would simply be replaced:
237
238```
239{-
240>>> double 11
24133
242-}
243```
244
245# Multiline Output
246
247By default, the output of every expression is returned as a single line.
248
249This 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):
250
251```
252>>> import Text.Pretty.Simple
253>>> pShowNoColor [1..3]
254"[ 1\n, 2\n, 3\n]"
255```
256
257We could try to print the pretty-print output, but stdout is not captured so we get just a ():
258
259```
260>>> print $ pShowNoColor [1..7]
261()
262```
263
264To display it properly, we can exploit the fact that the output of an error is displayed as a multi-line text:
265
266```
267>>> import qualified Data.Text.Lazy as TL
268>>> import Text.Pretty.Simple
269>>> prettyPrint v = error (TL.unpack $ pShowNoColor v) :: IO String
270>>> prettyPrint [1..3]
271[ 1
272, 2
273, 3
274]
275```
276
277# Differences with doctest
278
279Though the Eval plugin functionality is quite similar to that of [doctest](https://hackage.haskell.org/package/doctest), some doctest's features are not supported.
280
281### Capturing Stdout
282
283Only the value of an IO expression is spliced in, not its output:
284
285```
286>>> print "foo"
287()
288```
289
290### Pattern Matching
291
292The arbitrary content matcher __...__ is unsupported.
293
294### Missing lambda abstractions in property tests
295
296Variables are not automatically introduced:
297
298```
299prop> reverse (reverse l) == (l::[Int])
300Variable not in scope: l :: [Int]
301```
302
303This works:
304
305```
306prop> \(l::[Int]) -> reverse (reverse l) == l
307+++ OK, passed 100 tests.
308```
309
310### Multiline Expressions
311
312```
313 >>> :{
314  let
315    x = 1
316    y = 2
317  in x + y + multiline
318 :}
319```
320
321# Acknowledgments
322
323Design/features derived from:
324
325* [GHCi](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html)
326
327* [Haddock's](https://www.haskell.org/haddock/doc/html/ch03s08.html#idm140354810775744) Examples and Properties
328
329* [Doctest](https://hackage.haskell.org/package/doctest)
330
331* the REPLoid feature of [Dante](https://github.com/jyp/dante)
332
333