ascii-art-to-unicode (empty) → 0.1.0.0
raw patch · 8 files changed
+812/−0 lines, 8 filesdep +ascii-art-to-unicodedep +basedep +comonadsetup-changed
Dependencies added: ascii-art-to-unicode, base, comonad, doctest, strict
Files
- LICENSE +30/−0
- README.md +68/−0
- Setup.hs +2/−0
- app/Main.hs +46/−0
- ascii-art-to-unicode.cabal +56/−0
- src/Text/AsciiArt.hs +532/−0
- stack.yaml +66/−0
- test/Spec.hs +12/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Franz Thoma (c) 2017++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Franz Thoma nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,68 @@+ASCII art to Unicode converter+==============================++Small CLI program to convert ASCII box drawings to unicode. Inspired+by [svgbob](https://github.com/ivanceras/svgbobrus)+and+[The Monads Hidden Behind Every Zipper](http://blog.sigfpe.com/2007/01/monads-hidden-behind-every-zipper.html).+++```+> aa2u++-------------+ |+| Hello World | | -----+++-------------+ | | +--++ | +-----+ |+----------------------------+ |+ | +------++ +-----+---. | |+ | +---+ | +--+ |+ .---| |---. | | | |+ | +---------+ |# | | | |+ | |# | | +--++ '-----------------'# | +---------++ ################## |+++------+-----+---------++| This | is | a table |++------+-----+---------++| It | has | some |++------+-----+---------++| values | |++------+-----+---------++| 0 | 1 | 2 | 3 |++------+-----+----+----++^D+┌─────────────┐ │+│ Hello World │ │ ─────┐+└─────────────┘ │ │ ┌──┐+ │ └─────┘ │+────────────────────────────┤ │+ │ ┌──────┘+ ┌─────┬───╮ │ │+ │ └───┤ │ ┌──┐ │+ ╭───│ │───╮ │ │ │ │+ │ └─────────┘ │█ │ │ │ │+ │ │█ │ │ └──┘+ ╰─────────────────╯█ │ └──────────+ ██████████████████ │++┌──────┬─────┬─────────┐+│ This │ is │ a table │+├──────┼─────┼─────────┤+│ It │ has │ some │+├──────┴─────┼─────────┤+│ values │ │+├──────┬─────┼─────────┤+│ 0 │ 1 │ 2 │ 3 │+└──────┴─────┴────┴────┘+```++### Usage:++```bash+aa2u > outfile # Reads from stdin+aa2u < infile # Prints to stdout+aa2u infile # Input file as argument works as well+aa2u < infile > outfile # Convert a file+```
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/Main.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE LambdaCase #-}+module Main where++import System.Environment+import qualified System.IO.Strict as Strict+import Text.AsciiArt++data Mode+ = Inplace FilePath+ | Read FilePath+ | Pipe++main :: IO ()+main = do+ mode <- getArgs <&> \case+ [] -> Pipe+ ["-i", file] -> Inplace file+ [file] -> Read file+ _ -> error "Usage: \n\+ \ aa2u # reads from stdin, prints to stdout\n\+ \ aa2u FILE # reads FILE, prints to stdout\n\+ \ aa2u -i FILE # reads from and writes to FILE\n"+ input <- case mode of+ Inplace file -> Strict.readFile file+ Read file -> readFile file+ Pipe -> getContents+ let inputLines = lines input+ plane = planeFromList ' ' inputLines+ width = maximum (fmap length inputLines)+ height = length inputLines+ let unicodeArt+ = unlines+ . fmap trimRight+ . planeToList height width+ . renderAsciiToUnicode+ $ plane+ case mode of+ Inplace file -> writeFile file unicodeArt+ Read _ -> putStr unicodeArt+ Pipe -> putStr unicodeArt++trimRight :: String -> String+trimRight = reverse . dropWhile (== ' ') . reverse++(<&>) :: Functor f => f a -> (a -> b) -> f b+(<&>) = flip (<$>)
+ ascii-art-to-unicode.cabal view
@@ -0,0 +1,56 @@+name: ascii-art-to-unicode+version: 0.1.0.0+synopsis: ASCII Art to Unicode Box Drawing converter+description:+ @aa2u@ converts ASCII Art box drawings to Unicode.+ .+ > > aa2u+ > +-------------++ > | Hello World |+ > +-------------++ > ^D+ > ┌─────────────┐+ > │ Hello World │+ > └─────────────┘+ .+ For more examples see the @doctest@ suite in "Text.AsciiArt".+homepage: https://github.com/fmthoma/ascii-art-to-unicode#readme+license: BSD3+license-file: LICENSE+author: Franz Thoma+maintainer: f.m.thoma@googlemail.com+copyright: 2017 Franz Thoma+build-type: Simple+extra-source-files: README.md+ , stack.yaml+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: Text.AsciiArt+ build-depends: base >= 4.7 && < 5+ , comonad+ default-language: Haskell2010++executable aa2u+ hs-source-dirs: app+ main-is: Main.hs+ ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N+ build-depends: base+ , ascii-art-to-unicode+ , strict+ default-language: Haskell2010++test-suite ascii-art-to-unicode-test+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ build-depends: base+ , ascii-art-to-unicode+ , doctest+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/githubuser/ascii-art-to-unicode
+ src/Text/AsciiArt.hs view
@@ -0,0 +1,532 @@+{- |+=Basic Elements++==Simple Boxes++>>> :{+aa2u "++ +-----+ +--+--+-----+ \n\+ \++ +--+ | | | | | \n\+ \ | | +--+--+--+--+ \n\+ \+---+ | | | | | | \n\+ \+---+ +--+ +-----+--+--+ "+:}+┌┐ ┌─────┐ ┌──┬──┬─────┐+└┘ └──┐ │ │ │ │ │+ │ │ ├──┴──┼──┬──┤+┌───┐ │ │ │ │ │ │+└───┘ └──┘ └─────┴──┴──┘++==Rounded Boxes++>>> :{+aa2u ".. .-----. .--+--+-----. \n\+ \'' '--. | | | | | \n\+ \ | | +--+--+--+--+ \n\+ \.---. | | | | | | \n\+ \'---' '--' '-----+--+--' "+:}+╭╮ ╭─────╮ ╭──┬──┬─────╮+╰╯ ╰──╮ │ │ │ │ │+ │ │ ├──┴──┼──┬──┤+╭───╮ │ │ │ │ │ │+╰───╯ ╰──╯ ╰─────┴──┴──╯++==Dotted and double strokes++>>> :{+aa2u "++ .-----. +==+==+=====+ \n\+ \++ +==+ : | : | | \n\+ \ : : +==+==+==+==+ \n\+ \+===+ : : | | : | \n\+ \+---+ '--' +=====+==+==+ "+:}+┌┐ ╭─────╮ ╒══╤══╤═════╕+└┘ ╘══╕ ┆ │ ┆ │ │+ ┆ ┆ ╞══╧══╪══╤══╡+╒═══╕ ┆ ┆ │ │ ┆ │+└───┘ ╰──╯ ╘═════╧══╧══╛++==Cast shadows++>>> :{+aa2u "+-------------+ \n\+ \| | \n\+ \+---+ +---+# \n\+ \ ##| |##### \n\+ \ | |# \n\+ \ +-----+# \n\+ \ ###### "+:}+┌─────────────┐+│ │+└───┐ ┌───┘█+ ██│ │█████+ │ │█+ └─────┘█+ ██████++=Properties++==Idempotent++Already rendered portions are not affected:++>>> :{+aa2u "┌┐ ╭─────╮ ╒══╤══╤═════╕ ┌───┐ \n\+ \└┘ ╘══╕ ┆ │ ┆ │ │ │ │ \n\+ \ ┆ ┆ ╞══╧══╪══╤══╡ │ │█ \n\+ \╒═══╕ ┆ ┆ │ │ ┆ │ └───┘█ \n\+ \└───┘ ╰──╯ ╘═════╧══╧══╛ ████ "+:}+┌┐ ╭─────╮ ╒══╤══╤═════╕ ┌───┐+└┘ ╘══╕ ┆ │ ┆ │ │ │ │+ ┆ ┆ ╞══╧══╪══╤══╡ │ │█+╒═══╕ ┆ ┆ │ │ ┆ │ └───┘█+└───┘ ╰──╯ ╘═════╧══╧══╛ ████++==Incremental++Existing characters can be removed:++>>> :{+aa2u "┌──┬ ┬──┐\n\+ \ │ │ │\n\+ \╞══╪ ╪══╡\n\+ \│ \n\+ \├──┼ ┼──┤\n\+ \ │ │ │\n\+ \└──┴ ┴──┘"+:}+───┐ ┌──┐+ │ │ │+╒══╛ ╘══╛+│+└──┐ ┌──┐+ │ │ │+───┘ └──┘++New connections can be added:++>>> :{+aa2u "┌──┐-┌──┐\n\+ \│ │ │ │\n\+ \╘══╛=╘══╛\n\+ \| : : |\n\+ \┌──┐-┌──┐\n\+ \│ │ │ │\n\+ \└──┘-└──┘"+:}+┌──┬─┬──┐+│ │ │ │+╞══╪═╪══╡+│ ┆ ┆ │+├──┼─┼──┤+│ │ │ │+└──┴─┴──┘++Existing connections can be altered by replacing/adding characters:++>>> :{+aa2u "┌──+──┐ .─────+─────. ┌────┐ \n\+ \│ +==+ │ | │ │####│ \n\+ \+==+ | │ | │ │# │█ \n\+ \└──+─-┘ │ +-----+ └────┘█# \n\+ \ +=====+ │ █████# \n\+ \╭──+─-╮ │ | │ ##### \n\+ \│ | | │ | │ \n\+ \╰──+──╯ '───────────' "+:}+┌──┬──┐ ╭─────┬─────╮ ┌────┐+│ ╞══╡ │ │ │ │████│+╞══╡ │ │ │ │ │█ │█+└──┴──┘ │ ├─────┤ └────┘██+ ╞═════╡ │ ██████+╭──┬──╮ │ │ │ █████+│ │ │ │ │ │+╰──┴──╯ ╰───────────╯++=Limitations++Some connections do not work as expected (mostly because the corresponding+Unicode characters do not exist), e.g. rounded corners with double-stroke lines,+or connection pieces connecting horizontal single- and double-stroke lines:++>>> :{+aa2u "--+== .==. .--.--. \n\+ \ | | | | | | \n\+ \==+-- '==' .--+--' \n\+ \ | | | | \n\+ \--+== --== '--'--' "+:}+──┐══ ╒══╕ ╭──╮──╮+ │ │ │ │ │ │+══├── ╘══╛ ╰──┼──╯+ │ │ │ │+──┘══ ──══ ╰──╰──╯++-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE RecordWildCards #-}++module Text.AsciiArt where++import Control.Comonad+import Data.Maybe++++--------------------------------------------------------------------------------+-- * Zipper+--------------------------------------------------------------------------------++-- | The 'Zipper' is assumed to be infinite, i.e. filled with empty values+-- outside the defined area.+data Zipper a = Zipper+ { before :: [a]+ , current :: a+ , after :: [a] }+ deriving (Functor)++moveBefore, moveAfter :: Zipper a -> Zipper a+moveBefore zipper@Zipper { before = a : as, current = b, after = cs }+ = zipper { before = as, current = a, after = b : cs }+moveAfter zipper@Zipper { before = as, current = b, after = c : cs }+ = zipper { before = b : as, current = c, after = cs }++-- | Renders the 'current' and the @n - 1@ elements 'after' as list.+zipperToList :: Int -> Zipper a -> [a]+zipperToList n Zipper{..} = current : take (n - 1) after++-- | An infinite 'Zipper' filled with @a@s.+zipperOf :: a -> Zipper a+zipperOf a = Zipper { before = repeat a, current = a, after = repeat a }++-- | Takes a list and creates a 'Zipper' from it. The 'current' element will be+-- the 'head' of the list, and 'after' that 'tail'. The rest will be filled with+-- @a@s to an infinite 'Zipper'.+zipperFromList :: a -> [a] -> Zipper a+zipperFromList a = \case+ [] -> zipperOf a+ b : bs -> (zipperOf a) { current = b, after = bs ++ repeat a }+++instance Comonad Zipper where+ extract = current+ extend f zipper = fmap f $ Zipper+ { before = iterate1 moveBefore zipper+ , current = zipper+ , after = iterate1 moveAfter zipper }+ where+ iterate1 f x = tail (iterate f x)++++--------------------------------------------------------------------------------+-- * Plane (two-dimensional 'Zipper')+--------------------------------------------------------------------------------++-- | A plane is a 'Zipper' of 'Zipper's. The outer layer zips through lines+-- (up\/down), the inner layer through columns (left\/right).+-- Like the 'Zipper', the 'Plane' is assumed to be infinite in all directions.+newtype Plane a = Plane { unPlane :: Zipper (Zipper a) }+ deriving (Functor)++moveLeft, moveRight, moveUp, moveDown :: Plane a -> Plane a+moveLeft = Plane . fmap moveBefore . unPlane+moveRight = Plane . fmap moveAfter . unPlane+moveUp = Plane . moveBefore . unPlane+moveDown = Plane . moveAfter . unPlane+++-- | Renders @m@ lines and @n@ columns as nested list.+planeToList :: Int -> Int -> Plane a -> [[a]]+planeToList m n (Plane Zipper{..}) = fmap (zipperToList n) $+ current : take (m - 1) after++-- | An infinite 'Plane' filled with @a@s.+planeOf :: a -> Plane a+planeOf a = Plane $ Zipper+ { before = repeat (zipperOf a)+ , current = zipperOf a+ , after = repeat (zipperOf a) }++-- | Create a 'Plane' from a list of lists, filling the rest with @a@s in all+-- directions.+planeFromList :: a -> [[a]] -> Plane a+planeFromList a = \case+ [] -> planeOf a+ as : ass -> Plane $ (zipperOf (zipperOf a))+ { current = zipperFromList a as+ , after = fmap (zipperFromList a) ass ++ repeat (zipperOf a) }+++instance Comonad Plane where+ extract = current . current . unPlane+ extend f plane = fmap f $ Plane $ Zipper+ { before = fmap foo (iterate1 moveUp plane)+ , current = foo plane+ , after = fmap foo (iterate1 moveDown plane) }+ where+ foo p = Zipper+ { before = iterate1 moveLeft p+ , current = p+ , after = iterate1 moveRight p }+ iterate1 f x = tail (iterate f x)++++--------------------------------------------------------------------------------+-- * Patterns+--------------------------------------------------------------------------------++newtype Pattern = Pattern ((Char, Char, Char), (Char, Char, Char), (Char, Char, Char))+++patternFromString :: String -> Pattern+patternFromString [a, b, c, d, e, f, g, h, i] = Pattern ((a, b, c), (d, e, f), (g, h, i))+patternFromString _ = undefined++patternToString :: Pattern -> String+patternToString (Pattern ((a, b, c), (d, e, f), (g, h, i))) = [a, b, c, d, e, f, g, h, i]++-- | Find the 'Char' to replace the center of a 'Pattern'.+lookupPattern :: Pattern -> Maybe Char+lookupPattern pattern = case filter (satisfies pattern) patterns of+ [] -> Nothing+ a : _ -> Just (snd a)+ where+ satisfies :: Pattern -> (Pattern, Char) -> Bool+ satisfies diagram (pattern, _)+ = and (zipWith connectsLike (patternToString diagram) (patternToString pattern))++-- | Whether a character can connect to another character. For example, @+@+-- connects both horizontally (like @-@) and vertically (like @|@), so it+-- 'connectsLike' @-@, @|@, and of course like itself.+connectsLike :: Char -> Char -> Bool+char `connectsLike` pattern = case pattern of+ '-' -> char `elem` ['-', '>', '<', '─'] || char `connectsLike` '+'+ '=' -> char `elem` ['=', '>', '<', '═'] || char `connectsLike` '+'+ '|' -> char `elem` ['|', '^', 'v', '│'] || char `connectsLike` ':' || char `connectsLike` '+'+ ':' -> char `elem` [':', '┆']+ '+' -> char `elem` [ '+'+ , '└', '┘', '┌', '┐'+ , '╘', '╛', '╒', '╕'+ , '├', '┤', '┬', '┴', '┼'+ , '╞', '╡', '╤', '╧', '╪' ]+ || char `connectsLike` '.'+ '.' -> char `elem` [ '\'', '.'+ , '╭', '╮', '╯', '╰' ]+ '\'' -> char `connectsLike` '.'+ ' ' -> True+ other -> char == other++-- | The actual pattern definitions. For convenience, the simple patterns are at+-- the top, and more complex ones at the bottom. 'lookupPattern' will first try+-- the most complex pattern and work its way to the simpler patterns, thus+-- avoiding to choose a simpler pattern and forgetting some connection.+patterns :: [(Pattern, Char)]+patterns = reverse $ fmap (\(a, b) -> (patternFromString a, b))+ [ ( " \+ \ --\+ \ ", '─' )++ , ( " \+ \-- \+ \ ", '─' )++ , ( " \+ \ ==\+ \ ", '═' )++ , ( " \+ \== \+ \ ", '═' )++ , ( " \+ \ | \+ \ | ", '│' )++ , ( " | \+ \ | \+ \ ", '│' )++ , ( " \+ \ : \+ \ | ", '┆' )++ , ( " | \+ \ : \+ \ ", '┆' )++ , ( " \+ \ : \+ \ : ", '┆' )++ , ( " : \+ \ : \+ \ ", '┆' )++ , ( " | \+ \=+ \+ \ ", '╛' )++ , ( " | \+ \ +=\+ \ ", '╘' )++ , ( " \+ \ +=\+ \ | ", '╒' )++ , ( " \+ \=+ \+ \ | ", '╕' )++ , ( " | \+ \ +=\+ \ | ", '╞' )++ , ( " | \+ \=+ \+ \ | ", '╡' )++ , ( " \+ \=+=\+ \ | ", '╤' )++ , ( " | \+ \=+=\+ \ ", '╧' )++ , ( " | \+ \=+=\+ \ | ", '╪' )++ , ( " | \+ \-+ \+ \ ", '┘' )++ , ( " | \+ \ +-\+ \ ", '└' )++ , ( " \+ \ +-\+ \ | ", '┌' )++ , ( " \+ \-+ \+ \ | ", '┐' )++ , ( " | \+ \ +-\+ \ | ", '├' )++ , ( " | \+ \-+ \+ \ | ", '┤' )++ , ( " \+ \-+-\+ \ | ", '┬' )++ , ( " | \+ \-+-\+ \ ", '┴' )++ , ( " | \+ \-+-\+ \ | ", '┼' )++ , ( " \+ \ .-\+ \ | ", '╭' )++ , ( " \+ \-. \+ \ | ", '╮' )++ , ( " | \+ \-' \+ \ ", '╯' )++ , ( " | \+ \ '-\+ \ ", '╰' )++ , ( " \+ \ # \+ \ # ", '█' )++ , ( " \+ \## \+ \ ", '█' )++ , ( " \+ \ ##\+ \ ", '█' )++ , ( " # \+ \ # \+ \ ", '█' )++ , ( " \+ \-> \+ \ ", '▷' )++ , ( " \+ \ <-\+ \ ", '◁' )++ , ( " \+ \ ^ \+ \ | ", '△' )++ , ( " | \+ \ v \+ \ ", '▽' )+ ]++++--------------------------------------------------------------------------------+-- * Transforming ASCII to Unicode+--------------------------------------------------------------------------------++-- | Match the 'current' element and its eight neighbours against the defined+-- 'patterns' and choose the 'Char' from the matching 'Pattern'.+substituteChar :: Plane Char -> Char+substituteChar = \case+ Plane ( Zipper ((Zipper (a : as) b (c : cs)) : _)+ ( Zipper (d : ds) e (f : fs))+ ((Zipper (g : gs) h (i : is)) : _) )+ -> fromMaybe e (lookupPattern (patternFromString [a, b, c, d, e, f, g, h, i]))+ _ -> undefined -- We assume an infinite Zipper!++-- | Transform a 'Plane' of ASCII characters to an equivalent plane where the+-- ASCII box drawings have been replaced by their Unicode counterpart.+--+-- This function is a convolution with 'substituteChar' using the 'Comonad'ic+-- 'extend'.+renderAsciiToUnicode :: Plane Char -> Plane Char+renderAsciiToUnicode = extend substituteChar++++{- $setup+>>> :{+aa2u :: String -> IO ()+aa2u input =+ let inputLines = lines input+ plane = planeFromList ' ' inputLines+ width = maximum (fmap length inputLines)+ height = length inputLines+ in putStr+ . unlines+ . fmap (reverse . dropWhile (== ' ') . reverse)+ . planeToList height width+ . renderAsciiToUnicode+ $ plane+:}+-}
+ stack.yaml view
@@ -0,0 +1,66 @@+# This file was automatically generated by 'stack init'+#+# Some commonly used options have been documented as comments in this file.+# For advanced use and comprehensive documentation of the format, please see:+# http://docs.haskellstack.org/en/stable/yaml_configuration/++# Resolver to choose a 'specific' stackage snapshot or a compiler version.+# A snapshot resolver dictates the compiler version and the set of packages+# to be used for project dependencies. For example:+#+# resolver: lts-3.5+# resolver: nightly-2015-09-21+# resolver: ghc-7.10.2+# resolver: ghcjs-0.1.0_ghc-7.10.2+# resolver:+# name: custom-snapshot+# location: "./custom-snapshot.yaml"+resolver: lts-8.5++# User packages to be built.+# Various formats can be used as shown in the example below.+#+# packages:+# - some-directory+# - https://example.com/foo/bar/baz-0.0.2.tar.gz+# - location:+# git: https://github.com/commercialhaskell/stack.git+# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a+# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a+# extra-dep: true+# subdirs:+# - auto-update+# - wai+#+# A package marked 'extra-dep: true' will only be built if demanded by a+# non-dependency (i.e. a user package), and its test suites and benchmarks+# will not be run. This is useful for tweaking upstream packages.+packages:+- '.'+# Dependency packages to be pulled from upstream that are not in the resolver+# (e.g., acme-missiles-0.3)+extra-deps: []++# Override default flag values for local packages and extra-deps+flags: {}++# Extra package databases containing global packages+extra-package-dbs: []++# Control whether we use the GHC we find on the path+# system-ghc: true+#+# Require a specific version of stack, using version ranges+# require-stack-version: -any # Default+# require-stack-version: ">=1.3"+#+# Override the architecture used by stack, especially useful on Windows+# arch: i386+# arch: x86_64+#+# Extra directories used by stack for building+# extra-include-dirs: [/path/to/dir]+# extra-lib-dirs: [/path/to/dir]+#+# Allow a newer minor version of GHC than the snapshot specifies+# compiler-check: newer-minor
+ test/Spec.hs view
@@ -0,0 +1,12 @@+import Test.DocTest++main :: IO ()+main =+ let extensions =+ [ "-XLambdaCase"+ , "-XMultiWayIf"+ , "-XOverloadedStrings" ]+ sourceFolders =+ [ "src"+ , "app" ]+ in doctest (extensions ++ sourceFolders)