packages feed

qm-interpolated-string 0.1.1.0 → 0.2.0.0

raw patch · 22 files changed

+2657/−307 lines, 22 filesdep ~haskell-src-meta

Dependency ranges changed: haskell-src-meta

Files

CHANGELOG.md view
@@ -1,5 +1,25 @@-## 0.1.1.0+## <a name="v0.2.0.0"></a>0.2.0.0 -- Added `qn` QuasiQuoter as alternative to `qm` but without interpolation-- Some code refactoring-- More docs and tests+* Added tab (`\t`) symbol escaping+  (breaks backward compatibility with [v0.1.1.0](#v0.1.1.0))+* Support new [LTS Haskell 9.0 (ghc-8.0.2)](https://www.stackage.org/lts-9.0)+  (updated upper version of **haskell-src-meta** from 0.7.x to 0.8.x)+* Added `qmb` QuasiQuoter,+  it's `qm` + `b` (line-<b>B</b>reaks),+  it works just as `qm` but keeps line breaks (still ignores indendation)+* Added `qnb` QuasiQuoter (`qmb` without interpolation),+  it's `qn` + `b` (line-<b>B</b>reaks),+  it works just as `qn` but keeps line breaks (still ignores indendation)+* Added `qms` QuasiQuoter,+  it's `qm` + `s` (<b>S</b>paces),+  it works just as `qmb` but kept line breaks replaced with spaces+* Added `qns` QuasiQuoter (`qms` without interpolation),+  it's `qn` + `s` (<b>S</b>paces),+  it works just as `qnb` but kept line breaks replaced with spaces+* More docs and tests++## <a name="v0.1.1.0"></a>0.1.1.0++* Added `qn` QuasiQuoter as alternative to `qm` but without interpolation+* Some code refactoring+* More docs and tests
README.md view
@@ -9,65 +9,216 @@  Actually it's modification of [interpolatedstring-perl6](https://github.com/audreyt/interpolatedstring-perl6)-package. I used it to implemenent my own strings I really like.+package. I've forked it to implemenent my own strings I really like. -This implementation looks just like `qc`-from **interpolatedstring-perl6** package but ignores any indentation,-line breaks (except explicitly written using `\n` char)+This implementation based on `qc` from **interpolatedstring-perl6** package+but ignores any indentation, line breaks+(except explicitly written using `\n` char) and trailing whitespaces. -'m' in 'qm' means 'multiline'.+* 'm' in `qm` means '**M**ultiline'.+* 'n' in `qn` means '**N**o interpolation'.+* 'b' in `qmb`/`qnb` means 'line **B**reaks'.+* 's' in `qms`/`qns` means '**S**paces'. -You could write a decoratively formatted string and your-decorative indentation and line breaks wont go to the string,+Write a decoratively formatted string and your+decorative indentation and line breaks wont go to result string, but when you really need it, you could just escape it using backslash. -## Simple usage example+## Usage example  ```haskell {-# LANGUAGE QuasiQuotes #-} -import Text.InterpolatedString.QM (qm)+import Text.InterpolatedString.QM  main :: IO ()-main = putStrLn [qm| hello-                   \ world |]+main = do+  -- Hello, world! Pi is 3.14…+  putStrLn [qms| Hello,+                 world!+                 Pi is {floor pi}.{floor $ (pi - 3) * 100}… |]++  -- Some examples with HTML below to demonstrate the difference+  -- between all of the quasi-quoters.++  let title = "Testing"+      text  = "Some testing text"++  -- <article><h1>Testing</h1><p>Some testing text</p></article>+  putStrLn [qm|+    <article>+      <h1>{title}</h1>+      <p>{text}</p>+    </article>+  |]++  -- <article><h1>{title}</h1><p>{text}</p></article>+  putStrLn [qn|+    <article>+      <h1>{title}</h1>+      <p>{text}</p>+    </article>+  |]++  -- <article> <h1>Testing</h1> <p>Some testing text</p> </article>+  putStrLn [qms|+    <article>+      <h1>{title}</h1>+      <p>{text}</p>+    </article>+  |]++  -- <article> <h1>{title}</h1> <p>{text}</p> </article>+  putStrLn [qns|+    <article>+      <h1>{title}</h1>+      <p>{text}</p>+    </article>+  |]++  -- <article>+  -- <h1>Testing</h1>+  -- <p>Some testing text</p>+  -- </article>+  putStrLn [qmb|+    <article>+      <h1>{title}</h1>+      <p>{text}</p>+    </article>+  |]++  -- <article>+  -- <h1>{title}</h1>+  -- <p>{text}</p>+  -- </article>+  putStrLn [qnb|+    <article>+      <h1>{title}</h1>+      <p>{text}</p>+    </article>+  |] ``` +## Tables++### All QuasiQuoters++```+| QuasiQuoter | Interpolation | Indentation | Line breaks          | Trailing whitespaces |+|-------------|---------------|-------------|----------------------|----------------------|+| qm          | ✓             | Removed     | Removed              | Removed              |+| qn          | ✗             | Removed     | Removed              | Removed              |+| qmb         | ✓             | Removed     | Kept                 | Removed              |+| qnb         | ✗             | Removed     | Kept                 | Removed              |+| qms         | ✓             | Removed     | Replaced with spaces | Removed              |+| qns         | ✗             | Removed     | Replaced with spaces | Removed              |+```++### About naming logic++```+| Contains in its name | What means                       | QuasiQuoters |+|----------------------|----------------------------------|--------------|+| m                    | Resolves interpolation blocks    | qm, qmb, qms |+| n                    | Without interpolation            | qn, qnb, qns |+| b                    | Keeps line breaks                | qmb, qnb     |+| s                    | Replaces line breaks with spaces | qms, qns     |+```++## About escaping++### Symbols that can be escaped++Backslash is used for escaping these:++  1. `\n` - line break+  2. `\ ` - space (space is supposed to be escaped when you're going to keep+            some part of indentation)+  3. `\↵` - backslash just before end of line cuts off line break+            (makes sense for `qmb`, `qnb`, `qms` and `qns`)+  4. `\{` - opening bracket of interpolation block+            (only for `qm`, `qmb` and `qms`, to prevent interpolation+            and interpret this block as plain text)+  5. `\t` or `\‣` (where `‣` is real tab symbol) - tab (escaping it to keep some+     part of indentation, or if you need tab symbol for some reason,+     escaping real tabs makes sense only for keeping some part of indentation)+  6. `\\` - backslash itself (for situations when you don't want to escape+            other symbols but just want backslash symbol, `\\t`, `\\n`, `\\↵`,+            `\\{`, etc., if backslash doesn't come with any of symbols from+            this list it is interpreted just as backslash symbol, keep in mind+            that `\\\` (without any of symbols from this list after)+            and `\\\\` are producing same result - `\\`)++### Escaping examples++```haskell+[qm| foo\nbar  |] -- "foo\nbar"+[qm| foo\\nbar |] -- "foo\\nbar"+[qm| foo\tbar  |] -- "foo\tbar"+[qm| foo\\tbar |] -- "foo\\tbar"+[qm| foo\‣bar  |] -- "foo\tbar"   (`‣` is real tab symbol)+[qm| foo\\‣bar |] -- "foo\\\tbar" (`‣` is real tab symbol)+[qm| foo\ bar  |] -- "foo bar"+[qm| foo\\ bar |] -- "foo\\ bar"++[qm| foo\+     bar  |] -- "foobar"+[qm| foo\\+     bar  |] -- "foo\\bar"++[qmb| foo\+      bar  |] -- "foobar"+[qmb| foo\\+      bar  |] -- "foo\\\nbar"++[qm| foo\bar    |] -- "foo\\bar"+[qm| foo\\bar   |] -- "foo\\bar"+[qm| foo\\\bar  |] -- "foo\\\\bar"+[qm| foo\\\\bar |] -- "foo\\\\bar"+```+ ## More examples  ```haskell-[qm|   hello world,-     \ what's going on here?  |]--- Result: "hello world, what's going on here?"+[qm|   you can escape spaces+     \ when you need them    |]+-- Result: "you can escape spaces when you need them" ```  ```haskell [qm|-      it's actual-      ly ignored-   |]--- Result: "it's actually ignored"+        indentation and li+  ne bre+   aks are i+       gno+     red+|]+-- Result: "indentation and line breaks are ignored" ```  ```haskell-[qm|  \  You could explicitly escape indentation or\n-         line-breaks when you really need it!  \-   |]--- Result: "  You could explicitly escape indentation or\nline-breaks when you really need it!  "+[qm|  \  You can escape indentation or\n+         line breaks when you need them! \  |]+-- Result: "  You can escape indentation or\nline breaks when you need them!  " ```  ```haskell-[qm| {1+2} \{3+4} |]--- Result: "3 {3+4}"+[qm| Interpolation blocks can be escaped too: {1+2} \{3+4} |]+-- Result: "Interpolation blocks can be escaped too: 3 {3+4}" ``` -There is also very similar to `qm` QuasiQuoter-named as `qn` that do the same except interpolation:+If you don't need interpolation - just replace `m` to `n` in quasi-quoter name:  ```haskell-[qn| foo {1+2} |]--- Result: "foo {1+2}"+[qm| foo {1+2} |] -- Result: "foo 3"+[qn| foo {1+2} |] -- Result: "foo {1+2}"++[qms| foo {1+2} |] -- Result: "foo 3"+[qns| foo {1+2} |] -- Result: "foo {1+2}"++[qmb| foo {1+2} |] -- Result: "foo 3"+[qnb| foo {1+2} |] -- Result: "foo {1+2}" ```  ## Author
qm-interpolated-string.cabal view
@@ -1,5 +1,5 @@ name:               qm-interpolated-string-version:            0.1.1.0+version:            0.2.0.0 synopsis:           Implementation of interpolated multiline strings description:        Implementation of interpolated multiline strings                     that ignores indentation and trailing whitespaces@@ -24,20 +24,42 @@   location: git://github.com/unclechu/haskell-qm-interpolated-string.git  library-  ghc-options:      -Wall-  hs-source-dirs:   src-  exposed-modules:  Text.InterpolatedString.QM-  build-depends:    base ==4.*-                  , template-haskell >=2.5 && <3-                  , haskell-src-meta >=0.3 && <0.8-                  , bytestring ==0.10.*-                  , text ==1.*+  ghc-options:     -Wall+  hs-source-dirs:  src+  exposed-modules: Text.InterpolatedString.QM+                 , Text.InterpolatedString.QM.Internal.Parsers+                 , Text.InterpolatedString.QM.Internal.Parsers.TH+                 , Text.InterpolatedString.QM.Internal.Parsers.Types+                 , Text.InterpolatedString.QM.Internal.Parsers.Helpers+                 , Text.InterpolatedString.QM.ShowQ.Class+  build-depends:   base ==4.*+                 , template-haskell >=2.5 && <3+                 , haskell-src-meta >=0.3 && <0.9+                 , bytestring ==0.10.*+                 , text ==1.*  test-suite tests-  type:            exitcode-stdio-1.0-  ghc-options:     -Wall -fno-warn-type-defaults-  hs-source-dirs:  test-  main-is:         Spec.hs-  build-depends:   base ==4.*-                 , hspec ==2.*-                 , qm-interpolated-string+  type:           exitcode-stdio-1.0++  if impl(ghc >= 8.0)+    ghc-options:  -Wall -fno-warn-type-defaults -Wno-tabs+  else+    ghc-options:  -Wall -fno-warn-type-defaults++  hs-source-dirs: test+  main-is:        Spec.hs+  other-modules:  QM.Spec+                , QN.Spec+                , QMB.Spec+                , QNB.Spec+                , QMS.Spec+                , QNS.Spec+                , LineBreaks.CRLF.QM.Spec+                , LineBreaks.CRLF.QN.Spec+                , LineBreaks.CRLF.QMB.Spec+                , LineBreaks.CRLF.QNB.Spec+                , LineBreaks.CRLF.QMS.Spec+                , LineBreaks.CRLF.QNS.Spec+  build-depends:  base ==4.*+                , hspec ==2.*+                , qm-interpolated-string
src/Text/InterpolatedString/QM.hs view
@@ -1,196 +1,120 @@ -- Fork of: https://github.com/audreyt/interpolatedstring-perl6/blob/63d91a83eb5e48740c87570a8c7fd4668afe6832/src/Text/InterpolatedString/Perl6.hs -- Author of the 'interpolatedstring-perl6' package: Audrey Tang -{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TypeSynonymInstances #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE IncoherentInstances #-}- {-# LANGUAGE PackageImports #-}-{-# LANGUAGE ViewPatterns #-} -{-# LANGUAGE CPP #-}--module Text.InterpolatedString.QM (qm, qn, ShowQ(..)) where+module Text.InterpolatedString.QM (qm, qn, qmb, qnb, qms, qns, ShowQ (..)) where -import "base" GHC.Exts (IsString (fromString))-import qualified "template-haskell" Language.Haskell.TH as TH import "template-haskell" Language.Haskell.TH.Quote (QuasiQuoter (QuasiQuoter))-import "haskell-src-meta" Language.Haskell.Meta.Parse (parseExp)-import "bytestring" Data.ByteString.Char8 as Strict (ByteString, unpack)-import "bytestring" Data.ByteString.Lazy.Char8 as Lazy (ByteString, unpack)-import "text" Data.Text as T (Text, unpack)-import "text" Data.Text.Lazy as LazyT (Text, unpack) -#if MIN_VERSION_base(4,8,0)-#else-import "base" Data.Monoid (mempty, mappend)-#endif---class ShowQ a where-  showQ :: a -> String--instance ShowQ Char where-  showQ = (:[])--instance ShowQ String where-  showQ = id--instance ShowQ Strict.ByteString where-  showQ = Strict.unpack--instance ShowQ Lazy.ByteString where-  showQ = Lazy.unpack--instance ShowQ T.Text where-  showQ = T.unpack--instance ShowQ LazyT.Text where-  showQ = LazyT.unpack--instance Show a => ShowQ a where-  showQ = show--class QQ a string where-  toQQ :: a -> string--instance IsString s => QQ s s where-  toQQ = id--instance (ShowQ a, IsString s) => QQ a s where-  toQQ = fromString . showQ--data StringPart = Literal String | AntiQuote String deriving Show---unQM :: String -> String -> [StringPart]-unQM a ""          = [Literal (reverse a)]-unQM a ('\\':x:xs) = unQM (x:a) xs-unQM a ("\\")      = unQM ('\\':a) ""-unQM a ('}':xs)    = AntiQuote (reverse a) : parseQM "" xs-unQM a (x:xs)      = unQM (x:a) xs---parseQM :: String -> String -> [StringPart]-parseQM a ""             = [Literal (reverse a)]-parseQM a ('\\':'\\':xs) = parseQM ('\\':a) xs-parseQM a ('\\':'{':xs)  = parseQM ('{':a) xs-parseQM a ('\\':' ':xs)  = parseQM (' ':a) xs-parseQM a ('\\':'\n':xs) = parseQM a ('\n':xs)-parseQM a ('\\':'n':xs)  = parseQM ('\n':a) xs-parseQM a ("\\")         = parseQM ('\\':a) ""-parseQM a ('{':xs)       = Literal (reverse a) : unQM "" xs-parseQM a (clearIndentAtSOF   -> Just clean) = parseQM a clean-parseQM a (clearIndentTillEOF -> Just clean) = parseQM a clean-parseQM a ('\n':xs)      = parseQM a xs -- cut off line breaks-parseQM a (x:xs)         = parseQM (x:a) xs---parseQN :: String -> String -> [StringPart]-parseQN a ""             = [Literal (reverse a)]-parseQN a ('\\':'\\':xs) = parseQN ('\\':a) xs-parseQN a ('\\':' ':xs)  = parseQN (' ':a) xs-parseQN a ('\\':'\n':xs) = parseQN a ('\n':xs)-parseQN a ('\\':'n':xs)  = parseQN ('\n':a) xs-parseQN a ("\\")         = parseQN ('\\':a) ""-parseQN a (clearIndentAtSOF   -> Just clean) = parseQN a clean-parseQN a (clearIndentTillEOF -> Just clean) = parseQN a clean-parseQN a ('\n':xs)      = parseQN a xs -- cut off line breaks-parseQN a (x:xs)         = parseQN (x:a) xs---clearIndentTillEOF :: String -> Maybe String-clearIndentTillEOF s | s == ""             = Nothing-                     | head s `elem` "\t " = cutOff s-                     | otherwise           = Nothing--  where cutOff x | x == ""             = Just ""-                 | head x == '\n'      = Just x-                 | head x `elem` "\t " = cutOff $ tail x-                 | otherwise           = Nothing---clearIndentAtSOF :: String -> Maybe String-clearIndentAtSOF s | s == ""                      = Nothing-                   | head s == '\n' && hasChanges = Just processed-                   | otherwise                    = Nothing--  where processed  = '\n' : cutOff (tail s)-        hasChanges = processed /= s--        cutOff x | x == ""             = ""-                 | head x `elem` "\t " = cutOff $ tail x-                 | otherwise           = x---clearIndentAtStart :: String -> String-clearIndentAtStart s | s == ""             = ""-                     | head s `elem` "\t " = clearIndentAtStart $ tail s-                     | otherwise           = s---makeExpr :: [StringPart] -> TH.ExpQ-makeExpr [] = [| mempty |]-makeExpr (Literal a : xs) =-  TH.appE [| mappend (fromString a) |]    $ makeExpr xs-makeExpr (AntiQuote a : xs) =-  TH.appE [| mappend (toQQ $(reify a)) |] $ makeExpr xs---reify :: String -> TH.Q TH.Exp-reify s = case parseExp s of-               Left  e -> TH.reportError e >> [| mempty |]-               Right e -> return e+-- local imports+import Text.InterpolatedString.QM.ShowQ.Class (ShowQ (..))+import qualified Text.InterpolatedString.QM.Internal.Parsers as Parsers   -- | QuasiQuoter for multiline interpolated string. -- -- @--- [qm| foo {'b':'a':'r':""}+-- ['qm'| foo {'b':'a':'r':""} --    \\ baz |] -- "foo bar baz" -- @ -- -- Symbols that could be escaped: -- --   * @\\@ - backslash itself (two backslashes one by one: @\\\\@)---     @[qm| foo\\\\bar |] -- "foo\\\\bar"@+--     @['qm'| foo\\\\bar |] -- "foo\\\\bar"@ -- --   * Space symbol at the edge --     (to put it to the output instead of just ignoring it)---     @[qm| foo\\ |] -- "foo "@ or @[qm|\\ foo |] -- " foo"@+--     @['qm'| foo\\ |] -- "foo "@ or @['qm'|\\ foo |] -- " foo"@ -- --   * Line break @\\n@ (actual line breaks are ignored) -- --   * Opening bracket of interpolation block @\\{@---     to prevent interpolatin and put it as it is---     @[qm| {1+2} \\{3+4} |] -- "3 {3+4}"@+--     to prevent interpolation and put it as it is+--     @['qm'| {1+2} \\{3+4} |] -- "3 {3+4}"@ -- qm :: QuasiQuoter-qm = QuasiQuoter f-  (error "Cannot use qm as a pattern")-  (error "Cannot use qm as a type")-  (error "Cannot use qm as a dec")-  where f = makeExpr . parseQM "" . clearIndentAtStart . filter (/= '\r')+qm = QuasiQuoter Parsers.qm+  (error "Cannot use 'qm' as a pattern")+  (error "Cannot use 'qm' as a type")+  (error "Cannot use 'qm' as a dec")  --- | Works just like `qm` but without interpolation+-- | Works just like 'qm' but without interpolation --   (just multiline string with decorative indentation). -- -- @--- [qn| foo {'b':'a':'r':""}+-- ['qn'| foo {'b':'a':'r':""} --    \\ baz |] -- "foo {'b':'a':'r':\\"\\"} baz" -- @ -- -- Interpolation blocks goes just as text: ----- @[qn| {1+2} \\{3+4} |] -- "{1+2} \\\\{3+4}"@+-- @['qn'| {1+2} \\{3+4} |] -- "{1+2} \\\\{3+4}"@ -- qn :: QuasiQuoter-qn = QuasiQuoter f-  (error "Cannot use qn as a pattern")-  (error "Cannot use qn as a type")-  (error "Cannot use qn as a dec")-  where f = makeExpr . parseQN "" . clearIndentAtStart . filter (/= '\r')+qn = QuasiQuoter Parsers.qn+  (error "Cannot use 'qn' as a pattern")+  (error "Cannot use 'qn' as a type")+  (error "Cannot use 'qn' as a dec")+++-- | 'qm' + `b` (line-/B/reaks)+--+-- @+-- ['qmb'| foo+--       {'b':'a':'r':""}+--       baz |] -- "foo\\nbar\\nbaz"+-- @+--+qmb :: QuasiQuoter+qmb = QuasiQuoter Parsers.qmb+  (error "Cannot use 'qmb' as a pattern")+  (error "Cannot use 'qmb' as a type")+  (error "Cannot use 'qmb' as a dec")+++-- | 'qn' + `b` (line-/B/reaks)+-- Works just like 'qmb' but without interpolation.+--+-- @+-- ['qnb'| foo+--       {'b':'a':'r':""}+--       baz |] -- "foo\\n{'b':'a':'r':\\"\\"}\\nbaz"+-- @+qnb :: QuasiQuoter+qnb = QuasiQuoter Parsers.qnb+  (error "Cannot use 'qnb' as a pattern")+  (error "Cannot use 'qnb' as a type")+  (error "Cannot use 'qnb' as a dec")+++-- | 'qm' + `s` (/S/paces)+--+-- @+-- ['qms'| foo+--       {'b':'a':'r':""}+--       baz |] -- "foo bar baz"+-- @+qms :: QuasiQuoter+qms = QuasiQuoter Parsers.qms+  (error "Cannot use 'qms' as a pattern")+  (error "Cannot use 'qms' as a type")+  (error "Cannot use 'qms' as a dec")+++-- | 'qn' + `s` (/S/paces)+--+-- Works just like 'qms' but without interpolation.+--+-- @+-- ['qns'| foo+--       {'b':'a':'r':""}+--       baz |] -- "foo {'b':'a':'r':\\"\\"} baz"+-- @+qns :: QuasiQuoter+qns = QuasiQuoter Parsers.qns+  (error "Cannot use 'qns' as a pattern")+  (error "Cannot use 'qns' as a type")+  (error "Cannot use 'qns' as a dec")
+ src/Text/InterpolatedString/QM/Internal/Parsers.hs view
@@ -0,0 +1,89 @@+-- Fork of: https://github.com/audreyt/interpolatedstring-perl6/blob/63d91a83eb5e48740c87570a8c7fd4668afe6832/src/Text/InterpolatedString/Perl6.hs+-- Author of the 'interpolatedstring-perl6' package: Audrey Tang++{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE PackageImports #-}+{-# LANGUAGE ViewPatterns #-}++module Text.InterpolatedString.QM.Internal.Parsers+  ( qm,  qn+  , qmb, qnb+  , qms, qns+  ) where++import qualified "template-haskell" Language.Haskell.TH as TH++-- local imports++import Text.InterpolatedString.QM.Internal.Parsers.TH (parserTpl)++import Text.InterpolatedString.QM.Internal.Parsers.Types ( Parser+                                                         , StringPart (..)+                                                         , LineBreaks (..)+                                                         )++import Text.InterpolatedString.QM.Internal.Parsers.Helpers+  ( unQX+  , clearIndentAtStart+  , clearIndentAtSOF+  , clearIndentTillEOF+  , clearFirstQXXLineBreak+  , clearLastQXXLineBreak+  , makeExpr+  )+++$(parserTpl "parseQM"  True  IgnoreLineBreaks)+$(parserTpl "parseQN"  False IgnoreLineBreaks)+$(parserTpl "parseQMB" True  KeepLineBreaks)+$(parserTpl "parseQNB" False KeepLineBreaks)+$(parserTpl "parseQMS" True  ReplaceLineBreaksWithSpaces)+$(parserTpl "parseQNS" False ReplaceLineBreaksWithSpaces)+++-- With interpolation blocks (line breaks and indentation are ignored)+qm :: String -> TH.ExpQ+qm = makeExpr . parseQM "" . clearIndentAtStart . filter (/= '\r')+++-- No interpolation block (line breaks and indentation are ignored)+qn :: String -> TH.ExpQ+qn = makeExpr . parseQN "" . clearIndentAtStart . filter (/= '\r')+++-- With interpolation blocks (line breaks are kept, indentation is ignored)+qmb :: String -> TH.ExpQ+qmb = makeExpr+    . parseQMB ""+    . clearFirstQXXLineBreak+    . clearIndentAtStart+    . filter (/= '\r')+++-- No interpolation block (line breaks are kept, indentation is ignored)+qnb :: String -> TH.ExpQ+qnb = makeExpr+    . parseQNB ""+    . clearFirstQXXLineBreak+    . clearIndentAtStart+    . filter (/= '\r')+++-- With interpolation blocks+-- (line breaks are replaced with spaces, indentation is ignored).+qms :: String -> TH.ExpQ+qms = makeExpr+    . parseQMS ""+    . clearFirstQXXLineBreak+    . clearIndentAtStart+    . filter (/= '\r')+++-- No interpolation block+-- (line breaks are replaced with spaces, indentation is ignored).+qns :: String -> TH.ExpQ+qns = makeExpr+    . parseQNS ""+    . clearFirstQXXLineBreak+    . clearIndentAtStart+    . filter (/= '\r')
+ src/Text/InterpolatedString/QM/Internal/Parsers/Helpers.hs view
@@ -0,0 +1,120 @@+-- Fork of: https://github.com/audreyt/interpolatedstring-perl6/blob/63d91a83eb5e48740c87570a8c7fd4668afe6832/src/Text/InterpolatedString/Perl6.hs+-- Author of the 'interpolatedstring-perl6' package: Audrey Tang++{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE PackageImports #-}+{-# LANGUAGE CPP #-}++{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE IncoherentInstances #-}+{-# LANGUAGE FlexibleInstances #-}++module Text.InterpolatedString.QM.Internal.Parsers.Helpers+  ( unQX+  , clearIndentAtStart+  , clearIndentAtSOF+  , clearIndentTillEOF+  , clearFirstQXXLineBreak+  , clearLastQXXLineBreak+  , makeExpr+  ) where++import "base" GHC.Exts (IsString (fromString))+import "haskell-src-meta" Language.Haskell.Meta.Parse (parseExp)+import qualified "template-haskell" Language.Haskell.TH as TH++#if MIN_VERSION_base(4,8,0)+#else+import "base" Data.Monoid (mempty, mappend)+#endif++-- local imports++import Text.InterpolatedString.QM.ShowQ.Class (ShowQ (..))++import Text.InterpolatedString.QM.Internal.Parsers.Types ( Parser+                                                         , StringPart (..)+                                                         )+++class QQ a string where+  toQQ :: a -> string++instance IsString s => QQ s s where+  toQQ = id++instance (ShowQ a, IsString s) => QQ a s where+  toQQ = fromString . showQ+++-- Parser for interpolation block+unQX :: Parser -> Parser+unQX _ a ""          = [Literal (reverse a)]+unQX f a ('\\':x:xs) = unQX f (x:a) xs+unQX f a ("\\")      = unQX f ('\\':a) ""+unQX f a ('}':xs)    = AntiQuote (reverse a) : f "" xs+unQX f a (x:xs)      = unQX f (x:a) xs+++clearIndentAtSOF :: String -> Maybe String+clearIndentAtSOF ""                                 = Nothing+clearIndentAtSOF s@(x:xs) | x == '\n' && hasChanges = Just processed+                          | otherwise               = Nothing++  where processed  = '\n' : cutOff xs+        hasChanges = processed /= s++        cutOff ""                        = ""+        cutOff z@(y:ys) | y `elem` "\t " = cutOff ys+                        | otherwise      = z+++clearIndentTillEOF :: String -> Maybe String+clearIndentTillEOF ""                       = Nothing+clearIndentTillEOF s@(x:_) | x `elem` "\t " = cutOff s+                           | otherwise      = Nothing++  where cutOff ""                      = Just ""+        cutOff z@('\n':_)              = Just z+        cutOff (y:ys) | y `elem` "\t " = cutOff ys+                      | otherwise      = Nothing+++clearLastQXXLineBreak :: String -> Bool+-- Cannot really be empty (matched in `parseQMB`)+clearLastQXXLineBreak ""                        = False+clearLastQXXLineBreak (x:xs) | x `elem` "\t\n " = f xs+                             | otherwise        = False++  where f ""                        = True+        f (y:ys) | y `elem` "\t\n " = f ys+                 | otherwise        = False+++clearFirstQXXLineBreak :: String -> String+clearFirstQXXLineBreak ""                          = ""+clearFirstQXXLineBreak s@(x:xs) | x `elem` "\t\n " = cutOff xs+                                | otherwise        = s+  where cutOff ""                          = ""+        cutOff c@(y:ys) | y `elem` "\t\n " = cutOff ys+                        | otherwise        = c+++clearIndentAtStart :: String -> String+clearIndentAtStart ""                        = ""+clearIndentAtStart s@(x:xs) | x `elem` "\t " = clearIndentAtStart xs+                            | otherwise      = s+++makeExpr :: [StringPart] -> TH.ExpQ+makeExpr [] = [| mempty |]+makeExpr (Literal a : xs) =+  TH.appE [| mappend (fromString a) |]    $ makeExpr xs+makeExpr (AntiQuote a : xs) =+  TH.appE [| mappend (toQQ $(reify a)) |] $ makeExpr xs+  where reify :: String -> TH.Q TH.Exp+        reify s = case parseExp s of+                       Left  e -> TH.reportError e >> [| mempty |]+                       Right e -> return e
+ src/Text/InterpolatedString/QM/Internal/Parsers/TH.hs view
@@ -0,0 +1,184 @@+-- Fork of: https://github.com/audreyt/interpolatedstring-perl6/blob/63d91a83eb5e48740c87570a8c7fd4668afE6832/src/Text/InterpolatedString/Perl6.hs+-- Author of the 'interpolatedstring-perl6' package: Audrey Tang++{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE PackageImports #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE LambdaCase #-}++module Text.InterpolatedString.QM.Internal.Parsers.TH (parserTpl) where++import           "base" Control.Arrow ((&&&))+import qualified "template-haskell" Language.Haskell.TH as TH+import           "template-haskell" Language.Haskell.TH ( Pat (ListP, ViewP)+                                                        , Exp (ListE)+                                                        )++-- local imports+import Text.InterpolatedString.QM.Internal.Parsers.Types (LineBreaks (..))+++data Decl++  =  C (Bool, TH.Pat, TH.Exp)+  -- ^ 'C' means 'Conditional'.+  --   First value of tuple is condition to add pattern or not.++  |  D (TH.Pat, TH.Exp)+  -- ^ 'D' means 'Declarative'.+  --   A pattern always will be added.++  deriving (Show, Eq)+++parserTpl :: String+          -- ^ Parser name+          -> Bool+          -- ^ Enable interpolation+          -> LineBreaks+          -> TH.DecsQ+parserTpl (TH.mkName &&& varE -> (n, fE)) withInterpolation lineBreaks = return++  [ TH.SigD n (TH.ConT $ TH.mkName "Parser") -- Type annotation for parser+  , TH.FunD n decls                          -- All patterns to match+  ]++  -- About naming variables:+  --   Suffixes (where `foo` is a variable name):+  --     * `fooP` means Pattern+  --     * `fooE` means Expression+  -- Variables:+  --   * `fE` - Quoter-parser's name (like `parseQM`), for recursive calls+  --   * `aE` - Result accumulator, defined for each pattern, like:+  --              `parseQM a …` where `…` is pattern and body+  --            For each from `decls` this prefix shown above+  --              is aproduced by `f` helper.+  --            For instance first pattern would be:+  --              `parseQM a [] = [Literal (reverse a)]`+  --   * `f` - Helper to prefix each pattern with `parseQM a`+  --           where `parseQM` is a name of parser from first argument.+  --  `apps` produces Expression,+  --    multiple function application with variate arity.+  where++    decls =++      map    (uncurry f) $+      map    (\case C (_, x, y) -> (x, y) ; D x -> x   ) $+      filter (\case C (x, _, _) -> x      ; D _ -> True)++      [ D ( ListP []+          , ListE [apps [conE "Literal", apps [varE "reverse", aE]]]+          )++      , C ( lineBreaks `elem` [KeepLineBreaks, ReplaceLineBreaksWithSpaces]+          , ViewP (varE "clearLastQXXLineBreak") $ conP "True" []+          , apps [fE, aE, strE ""]+          )++      , D ( consP [chrP '\\', chrP '\\', varP "xs"]+          , apps [fE, consE [chrE '\\', aE], varE "xs"]+          )++      , C ( withInterpolation+          , consP [chrP '\\', chrP '{', varP "xs"]+          , apps [fE, consE [chrE '{', aE], varE "xs"]+          )++      , D ( consP [chrP '\\', chrP ' ', varP "xs"]+          , apps [fE, consE [chrE ' ', aE], varE "xs"]+          )++      , C ( lineBreaks == IgnoreLineBreaks+          , consP [chrP '\\', chrP '\n', varP "xs"]+          , apps [fE, aE, consE [chrE '\n', varE "xs"]]+          )++        -- Explicitly cutting off line breaks+      , C ( lineBreaks `elem` [KeepLineBreaks, ReplaceLineBreaksWithSpaces]+          , consP [chrP '\\', chrP '\n', varP "xs"]++          , let cutOffFakeLnOrUseXS maybeVal =+                  apps [varE "maybe", varE "xs", varE "tail", maybeVal]++                clearNextLineIndentFromXS =+                  -- Fake '\n' here to make `clearIndentAtSOF` works for this+                  apps [varE "clearIndentAtSOF", consE [chrE '\n', varE "xs"]]++             in -- Recursively do stuff+                apps [fE, aE, cutOffFakeLnOrUseXS clearNextLineIndentFromXS]+          )++      , D ( consP [chrP '\\', chrP 'n', varP "xs"]+          , apps [fE, consE [chrE '\n', aE], varE "xs"]+          )++      , D ( consP [chrP '\\', chrP '\t', varP "xs"]+          , apps [fE, consE [chrE '\t', aE], varE "xs"]+          )++      , D ( consP [chrP '\\', chrP 't', varP "xs"]+          , apps [fE, consE [chrE '\t', aE], varE "xs"]+          )++      , D ( strP "\\"+          , apps [fE, consE [chrE '\\', aE], strE ""]+          )++      , C ( withInterpolation+          , consP [chrP '{', varP "xs"]+          , consE [ apps [conE "Literal", apps [varE "reverse", aE]]+                  , apps [varE "unQX", fE, strE "", varE "xs"]+                  ]+          )++      , D ( ViewP (varE "clearIndentAtSOF") $ conP "Just" [varP "clean"]+          , apps [fE, aE, varE "clean"]+          )++      , D ( ViewP (varE "clearIndentTillEOF") $ conP "Just" [varP "clean"]+          , apps [fE, aE, varE "clean"]+          )++        -- Cutting off line breaks+      , C ( lineBreaks == IgnoreLineBreaks+          , consP [chrP '\n', varP "xs"]+          , apps [fE, aE, varE "xs"]+          )++        -- Replacing line breaks with spaces+      , C ( lineBreaks == ReplaceLineBreaksWithSpaces+          , consP [chrP '\n', varP "xs"]+          , apps [fE, consE [chrE ' ', aE], varE "xs"]+          )++      , D ( consP [varP "x", varP "xs"]+          , apps [fE, consE [varE "x", aE], varE "xs"]+          )+      ]++    aE = varE "a"+    f pat body = TH.Clause [varP "a", pat] (TH.NormalB body) []++    apps [x] = x+    apps (x:y:zs) = apps $ TH.AppE x y : zs+    apps [] = error "apps []"++    consP [x] = x+    consP (x:y:zs) = consP $ TH.UInfixP x (TH.mkName ":") y : zs+    consP [] = error "consP []"++    consE [x] = x+    consE (x:y:zs) = consE $ TH.UInfixE x (conE ":") y : zs+    consE [] = error "consE []"+++varP :: String ->             TH.Pat ; varP = TH.VarP . TH.mkName+varE :: String ->             TH.Exp ; varE = TH.VarE . TH.mkName+conP :: String -> [TH.Pat] -> TH.Pat ; conP = TH.ConP . TH.mkName+conE :: String ->             TH.Exp ; conE = TH.ConE . TH.mkName++chrP :: Char   -> TH.Pat ; chrP = TH.LitP . TH.CharL+chrE :: Char   -> TH.Exp ; chrE = TH.LitE . TH.CharL+strP :: String -> TH.Pat ; strP = TH.LitP . TH.StringL+strE :: String -> TH.Exp ; strE = TH.LitE . TH.StringL
+ src/Text/InterpolatedString/QM/Internal/Parsers/Types.hs view
@@ -0,0 +1,19 @@+-- Fork of: https://github.com/audreyt/interpolatedstring-perl6/blob/63d91a83eb5e48740c87570a8c7fd4668afe6832/src/Text/InterpolatedString/Perl6.hs+-- Author of the 'interpolatedstring-perl6' package: Audrey Tang++module Text.InterpolatedString.QM.Internal.Parsers.Types+  ( Parser+  , StringPart (..)+  , LineBreaks (..)+  ) where+++type Parser = String -> String -> [StringPart]++data LineBreaks+  = IgnoreLineBreaks+  | KeepLineBreaks+  | ReplaceLineBreaksWithSpaces+  deriving (Show, Eq)++data StringPart = Literal String | AntiQuote String deriving Show
+ src/Text/InterpolatedString/QM/ShowQ/Class.hs view
@@ -0,0 +1,42 @@+-- Fork of: https://github.com/audreyt/interpolatedstring-perl6/blob/63d91a83eb5e48740c87570a8c7fd4668afe6832/src/Text/InterpolatedString/Perl6.hs+-- Author of the 'interpolatedstring-perl6' package: Audrey Tang++{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE IncoherentInstances #-}+{-# LANGUAGE FlexibleInstances #-}++{-# LANGUAGE PackageImports #-}++module Text.InterpolatedString.QM.ShowQ.Class (ShowQ (..)) where++import "bytestring" Data.ByteString.Char8 as Strict (ByteString, unpack)+import "bytestring" Data.ByteString.Lazy.Char8 as Lazy (ByteString, unpack)+import "text" Data.Text as T (Text, unpack)+import "text" Data.Text.Lazy as LazyT (Text, unpack)+++class ShowQ a where+  showQ :: a -> String++instance ShowQ Char where+  showQ = (:[])++instance ShowQ String where+  showQ = id++instance ShowQ Strict.ByteString where+  showQ = Strict.unpack++instance ShowQ Lazy.ByteString where+  showQ = Lazy.unpack++instance ShowQ T.Text where+  showQ = T.unpack++instance ShowQ LazyT.Text where+  showQ = LazyT.unpack++instance Show a => ShowQ a where+  showQ = show
+ test/LineBreaks/CRLF/QM/Spec.hs view
@@ -0,0 +1,161 @@+-- This file automatically generated by generate-cr-and-crlf-tests.sh (do not modify!)
+{-# LANGUAGE PackageImports #-}
+{-# LANGUAGE QuasiQuotes #-}
+
+module LineBreaks.CRLF.QM.Spec (spec) where
+
+import "hspec" Test.Hspec (Spec, describe, it, shouldBe)
+
+-- local imports
+import "qm-interpolated-string" Text.InterpolatedString.QM (qm)
+
+
+spec :: Spec
+spec = do
+
+  describe "Examples from README" $ do
+
+    it "First (decorative spacing and escaping space symbol)" $
+      [qm|   hello world,
+           \ what's going on here?  |]
+        `shouldBe` "hello world, what's going on here?"
+
+    it "Second (merging lines without spaces)" $
+      [qm|
+            it's actual
+            ly ignored
+         |]
+            `shouldBe` "it's actually ignored"
+
+    it "Third (explicit line breaks symbols)" $
+      [qm|  \  You could explicitly escape indentation or\n
+               line breaks when you really need it!  \
+         |] `shouldBe` "  You could explicitly escape indentation or\n\
+                       \line breaks when you really need it!  "
+
+    it "Fourth (escaping interpolation blocks to show them as text)" $
+      [qm| {1+2} \{3+4} |] `shouldBe` "3 {3+4}"
+
+  it "Type annotation in interpolation block" $
+    [qm|{10 :: Float}|] `shouldBe` "10.0"
+
+  it "Escaping interpolation symbols inside interpolation block" $ do
+    [qm|foo {"b{a{r"} baz|]   `shouldBe` "foo b{a{r baz"
+    [qm|foo {"b\}a\}r"} baz|] `shouldBe` "foo b}a}r baz"
+
+  it "Example from generated docs" $ [qm| foo {'b':'a':'r':""}
+                                        \ baz |] `shouldBe` "foo bar baz"
+
+  it "Escaping backslashes" $ do [qm| foo\bar |]    `shouldBe` "foo\\bar"
+                                 [qm| foo\\bar |]   `shouldBe` "foo\\bar"
+                                 [qm| foo\\\bar |]  `shouldBe` "foo\\\\bar"
+                                 [qm| foo\\\\bar |] `shouldBe` "foo\\\\bar"
+
+  it "Empty string" $ [qm|  |] `shouldBe` ""
+
+  it "Escaping space by slash at EOL after space" $
+    [qm| foo \
+         bar |] `shouldBe` "foo bar"
+
+  it "Escaped spaces at the edges" $ do [qm| foo\ |] `shouldBe` "foo "
+                                        [qm|\ foo |] `shouldBe` " foo"
+
+  describe "Tabs as indentation" $ do
+
+    it "Tabs is only indentation at left side" $ do
+			[qm|
+				foo  bar  baz
+			|] `shouldBe` "foo  bar  baz"
+
+			[qm|			foo bar baz|] `shouldBe` "foo bar baz"
+
+    it "Tabs also at EOL" $ do
+			[qm|
+				foo  bar  baz				
+			|] `shouldBe` "foo  bar  baz"
+
+			[qm|			foo bar baz				|] `shouldBe` "foo bar baz"
+
+    it "Escaped tabs" $ do
+      [qm|		\tfoo|]    `shouldBe` "\tfoo"
+      [qm|		\	foo	|]   `shouldBe` "\tfoo"
+      [qm|	foo		\	|]   `shouldBe` "foo\t\t\t"
+      [qm|	foo	\		|]   `shouldBe` "foo\t\t"
+      [qm|	foo\			|] `shouldBe` "foo\t"
+
+  it "Tails" $ do
+    [qm|    
+           foo   
+                 |] `shouldBe` "foo"
+    [qm|	 
+         	
+          foo	 
+               
+             	
+                 |] `shouldBe` "foo"
+    [qm|				
+            foo			
+            				
+            				|] `shouldBe` "foo"
+
+  describe "New README examples" $ do
+
+    it "Simple usage example" $ do
+      let title = "Testing"
+          text = "Some testing text"
+      [qm|
+        <article>
+          <h1>{title}</h1>
+          <p>{text}</p>
+        </article>
+      |]
+        `shouldBe`
+          "<article><h1>Testing</h1><p>Some testing text</p></article>"
+
+    it "Can escape spaces" $
+      [qm|   you can escape spaces
+           \ when you need them    |]
+        `shouldBe`
+          "you can escape spaces when you need them"
+
+    it "Indentation and line breaks are ignored" $ 
+      [qm|
+              indentation and li
+        ne bre
+         aks are i
+             gno
+           red
+      |]
+      `shouldBe` "indentation and line breaks are ignored"
+
+    it "Escaping indentation or line breaks" $
+      [qm|  \  You can escape indentation or\n
+               line breaks when you need them! \  |]
+        `shouldBe`
+          "  You can escape indentation or\nline breaks when you need them!  "
+
+    it "Interpolation blocks can be escaped too" $
+      [qm| Interpolation blocks can be escaped too: {1+2} \{3+4} |]
+        `shouldBe`
+          "Interpolation blocks can be escaped too: 3 {3+4}"
+
+    it "Interpolation" $ [qm| foo {1+2} |] `shouldBe` "foo 3"
+
+  it "Haddock example" $
+    [qm| foo {'b':'a':'r':""}
+       \ baz |] `shouldBe` "foo bar baz"
+
+  it "Escaping backslash itself when it makes sense" $ do
+    [qm| foo\nbar   |] `shouldBe` "foo\nbar"
+    [qm| foo\\nbar  |] `shouldBe` "foo\\nbar"
+    [qm| foo\tbar   |] `shouldBe` "foo\tbar"
+    [qm| foo\\tbar  |] `shouldBe` "foo\\tbar"
+    [qm| foo\	bar   |] `shouldBe` "foo\tbar"
+    [qm| foo\\	bar |] `shouldBe` "foo\\\tbar"
+    [qm| foo\ bar   |] `shouldBe` "foo bar"
+    [qm| foo\\ bar  |] `shouldBe` "foo\\ bar"
+
+    [qm| foo\
+         bar  |] `shouldBe` "foobar"
+    [qm| foo\\
+         bar  |] `shouldBe` "foo\\bar"
+ test/LineBreaks/CRLF/QMB/Spec.hs view
@@ -0,0 +1,156 @@+-- This file automatically generated by generate-cr-and-crlf-tests.sh (do not modify!)
+{-# LANGUAGE PackageImports #-}
+{-# LANGUAGE QuasiQuotes #-}
+
+module LineBreaks.CRLF.QMB.Spec (spec) where
+
+import "hspec" Test.Hspec (Spec, describe, it, shouldBe)
+
+-- local imports
+import "qm-interpolated-string" Text.InterpolatedString.QM (qmb)
+
+
+spec :: Spec
+spec = do
+
+  it "Works as expected" $
+    [qmb|
+      foo
+      {1+2}
+      bar
+    |] `shouldBe` "foo\n3\nbar"
+
+  it "Explicitly cutting off line breaks" $ do
+    [qmb|
+      foo\
+      {1+2}\
+      bar\
+      baz
+    |] `shouldBe` "foo3barbaz"
+    [qmb|
+      foo\
+      {1+2}\
+      bar\
+      baz\
+    |] `shouldBe` "foo3barbaz"
+
+  describe "Examples from README" $ do
+
+    it "First (decorative spacing and escaping space symbol)" $
+      [qmb|   hello world,
+            \ what's going on here?  |]
+        `shouldBe` "hello world,\n what's going on here?"
+
+    it "Second (breaking lines)" $
+      [qmb|
+            it's actual
+            ly NOT ignored
+         |]
+            `shouldBe` "it's actual\nly NOT ignored"
+
+    it "Third (explicit line breaks symbols)" $
+      [qmb|  \  You could explicitly escape indentation or\n
+                line breaks when you really need it!  \
+          |] `shouldBe` "  You could explicitly escape indentation or\n\n\
+                        \line breaks when you really need it!  "
+
+    it "Fourth (escaping interpolation blocks to show them as text)" $
+      [qmb| {1+2} \{3+4} |] `shouldBe` "3 {3+4}"
+
+  it "Type annotation in interpolation block" $
+    [qmb|{10 :: Float}|] `shouldBe` "10.0"
+
+  it "Escaping interpolation symbols inside interpolation block" $ do
+    [qmb|foo {"b{a{r"} baz|]   `shouldBe` "foo b{a{r baz"
+    [qmb|foo {"b\}a\}r"} baz|] `shouldBe` "foo b}a}r baz"
+
+  it "Example from generated docs" $ [qmb| foo {'b':'a':'r':""}
+                                         \ baz |] `shouldBe` "foo bar\n baz"
+
+  it "Escaping backslashes" $ do [qmb| foo\bar |]    `shouldBe` "foo\\bar"
+                                 [qmb| foo\\bar |]   `shouldBe` "foo\\bar"
+                                 [qmb| foo\\\bar |]  `shouldBe` "foo\\\\bar"
+                                 [qmb| foo\\\\bar |] `shouldBe` "foo\\\\bar"
+
+  it "Empty string" $ [qmb|  |] `shouldBe` ""
+
+  it "Escaping space by backslash at EOL after space (line break is cutted off)" $
+    [qmb| foo \
+          bar |] `shouldBe` "foo bar"
+
+  it "Escaped spaces at the edges" $ do [qmb| foo\ |] `shouldBe` "foo "
+                                        [qmb|\ foo |] `shouldBe` " foo"
+
+  describe "Tabs as indentation" $ do
+
+    it "Tabs is only indentation at left side" $ do
+			[qmb|
+				foo  bar  baz
+			|] `shouldBe` "foo  bar  baz"
+
+			[qmb|			foo bar baz|] `shouldBe` "foo bar baz"
+
+    it "Tabs also at EOL" $ do
+			[qmb|
+				foo  bar  baz				
+			|] `shouldBe` "foo  bar  baz"
+
+			[qmb|			foo bar baz				|] `shouldBe` "foo bar baz"
+
+    it "Escaped tabs" $ do
+      [qmb|		\tfoo|]    `shouldBe` "\tfoo"
+      [qmb|		\	foo	|]   `shouldBe` "\tfoo"
+      [qmb|	foo		\	|]   `shouldBe` "foo\t\t\t"
+      [qmb|	foo	\		|]   `shouldBe` "foo\t\t"
+      [qmb|	foo\			|] `shouldBe` "foo\t"
+
+  it "Tails" $ do
+    [qmb|    
+           foo   
+                 |] `shouldBe` "foo"
+    [qmb|	 
+         	
+          foo	 
+               
+             	
+                 |] `shouldBe` "foo"
+    [qmb|				
+            foo			
+            				
+            				|] `shouldBe` "foo"
+
+  describe "New README examples" $ do
+
+    it "Simple usage example" $ do
+      let title = "Testing"
+          text = "Some testing text"
+      [qmb|
+        <article>
+          <h1>{title}</h1>
+          <p>{text}</p>
+        </article>
+      |]
+        `shouldBe`
+          "<article>\n<h1>Testing</h1>\n<p>Some testing text</p>\n</article>"
+
+    it "Interpolation" $ [qmb| foo {1+2} |] `shouldBe` "foo 3"
+
+  it "Haddock example" $
+    [qmb| foo
+          {'b':'a':'r':""}
+          baz |] `shouldBe` "foo\nbar\nbaz"
+
+  it "Escaping backslash itself when it makes sense" $ do
+    [qmb| foo\nbar  |] `shouldBe` "foo\nbar"
+    [qmb| foo\\nbar |] `shouldBe` "foo\\nbar"
+    [qmb| foo\tbar  |] `shouldBe` "foo\tbar"
+    [qmb| foo\\tbar |] `shouldBe` "foo\\tbar"
+    [qmb| foo\	bar |] `shouldBe` "foo\tbar"
+    [qmb| foo\\	bar |] `shouldBe` "foo\\\tbar"
+    [qmb| foo\ bar  |] `shouldBe` "foo bar"
+    [qmb| foo\\ bar |] `shouldBe` "foo\\ bar"
+
+    [qmb| foo\
+          bar  |] `shouldBe` "foobar"
+    [qmb| foo\\
+          bar  |] `shouldBe` "foo\\\nbar"
+ test/LineBreaks/CRLF/QMS/Spec.hs view
@@ -0,0 +1,162 @@+-- This file automatically generated by generate-cr-and-crlf-tests.sh (do not modify!)
+{-# LANGUAGE PackageImports #-}
+{-# LANGUAGE QuasiQuotes #-}
+
+module LineBreaks.CRLF.QMS.Spec (spec) where
+
+import "hspec" Test.Hspec (Spec, describe, it, shouldBe)
+
+-- local imports
+import "qm-interpolated-string" Text.InterpolatedString.QM (qms)
+
+
+spec :: Spec
+spec = do
+
+  it "Works as expected" $
+    [qms|
+      foo
+      {1+2}
+      bar
+    |] `shouldBe` "foo 3 bar"
+
+  it "Explicitly cutting off line breaks" $ do
+    [qms|
+      foo\
+      {1+2}\
+      bar\
+      baz
+    |] `shouldBe` "foo3barbaz"
+    [qms|
+      foo\
+      {1+2}\
+      bar\
+      baz\
+    |] `shouldBe` "foo3barbaz"
+
+  describe "Examples from README" $ do
+
+    it "First (decorative spacing and escaping space symbol)" $
+      [qms|   hello world,
+            \ what's going on here?  |]
+        `shouldBe` "hello world,  what's going on here?"
+
+    it "Second (breaking lines)" $
+      [qms|
+            it's actual
+            ly NOT ignored
+         |]
+            `shouldBe` "it's actual ly NOT ignored"
+
+    it "Third (explicit line breaks symbols)" $
+      [qms|  \  You could explicitly escape indentation or\n
+                line breaks when you really need it!  \
+          |] `shouldBe` "  You could explicitly escape indentation or\n \
+                        \line breaks when you really need it!  "
+
+    it "Fourth (escaping interpolation blocks to show them as text)" $
+      [qms| {1+2} \{3+4} |] `shouldBe` "3 {3+4}"
+
+  it "Type annotation in interpolation block" $
+    [qms|{10 :: Float}|] `shouldBe` "10.0"
+
+  it "Escaping interpolation symbols inside interpolation block" $ do
+    [qms|foo {"b{a{r"} baz|]   `shouldBe` "foo b{a{r baz"
+    [qms|foo {"b\}a\}r"} baz|] `shouldBe` "foo b}a}r baz"
+
+  it "Example from generated docs" $ [qms| foo {'b':'a':'r':""}
+                                         \ baz |] `shouldBe` "foo bar  baz"
+
+  it "Escaping backslashes" $ do [qms| foo\bar |]    `shouldBe` "foo\\bar"
+                                 [qms| foo\\bar |]   `shouldBe` "foo\\bar"
+                                 [qms| foo\\\bar |]  `shouldBe` "foo\\\\bar"
+                                 [qms| foo\\\\bar |] `shouldBe` "foo\\\\bar"
+
+  it "Empty string" $ [qms|  |] `shouldBe` ""
+
+  it "Escaping space by backslash at EOL after space (line break is cutted off)" $
+    [qms| foo \
+          bar |] `shouldBe` "foo bar"
+
+  it "Escaped spaces at the edges" $ do [qms| foo\ |] `shouldBe` "foo "
+                                        [qms|\ foo |] `shouldBe` " foo"
+
+  describe "Tabs as indentation" $ do
+
+    it "Tabs is only indentation at left side" $ do
+			[qms|
+				foo  bar  baz
+			|] `shouldBe` "foo  bar  baz"
+
+			[qms|			foo bar baz|] `shouldBe` "foo bar baz"
+
+    it "Tabs also at EOL" $ do
+			[qms|
+				foo  bar  baz				
+			|] `shouldBe` "foo  bar  baz"
+
+			[qms|			foo bar baz				|] `shouldBe` "foo bar baz"
+
+    it "Escaped tabs" $ do
+      [qms|		\tfoo|]    `shouldBe` "\tfoo"
+      [qms|		\	foo	|]   `shouldBe` "\tfoo"
+      [qms|	foo		\	|]   `shouldBe` "foo\t\t\t"
+      [qms|	foo	\		|]   `shouldBe` "foo\t\t"
+      [qms|	foo\			|] `shouldBe` "foo\t"
+
+  it "Tails" $ do
+    [qms|    
+           foo   
+                 |] `shouldBe` "foo"
+    [qms|	 
+         	
+          foo	 
+               
+             	
+                 |] `shouldBe` "foo"
+    [qms|				
+            foo			
+            				
+            				|] `shouldBe` "foo"
+
+  describe "New README examples" $ do
+
+    it "First example" $
+      [qms| Hello,
+            world!
+            Pi is {floor pi}.{floor $ (pi - 3) * 100}… |]
+        `shouldBe` "Hello, world! Pi is 3.14…"
+
+    it "Simple usage example" $ do
+      let title = "Testing"
+          text = "Some testing text"
+      [qms|
+        <article>
+          <h1>{title}</h1>
+          <p>{text}</p>
+        </article>
+      |]
+        `shouldBe`
+          "<article> <h1>Testing</h1> <p>Some testing text</p> </article>"
+
+    it "Interpolation" $ [qms| foo {1+2} |] `shouldBe` "foo 3"
+
+  it "Haddock example" $
+    [qms| foo
+          {'b':'a':'r':""}
+          baz |] `shouldBe` "foo bar baz"
+
+  it "Escaping backslash itself when it makes sense" $ do
+    [qms| foo\nbar  |] `shouldBe` "foo\nbar"
+    [qms| foo\\nbar |] `shouldBe` "foo\\nbar"
+    [qms| foo\tbar  |] `shouldBe` "foo\tbar"
+    [qms| foo\\tbar |] `shouldBe` "foo\\tbar"
+    [qms| foo\	bar |] `shouldBe` "foo\tbar"
+    [qms| foo\\	bar |] `shouldBe` "foo\\\tbar"
+    [qms| foo\ bar  |] `shouldBe` "foo bar"
+    [qms| foo\\ bar |] `shouldBe` "foo\\ bar"
+
+    [qms| foo\
+          bar  |] `shouldBe` "foobar"
+    [qms| foo\\
+          bar  |] `shouldBe` "foo\\ bar"
+ test/LineBreaks/CRLF/QN/Spec.hs view
@@ -0,0 +1,134 @@+-- This file automatically generated by generate-cr-and-crlf-tests.sh (do not modify!)
+{-# LANGUAGE PackageImports #-}
+{-# LANGUAGE QuasiQuotes #-}
+
+module LineBreaks.CRLF.QN.Spec (spec) where
+
+import "hspec" Test.Hspec (Spec, describe, it, shouldBe)
+
+-- local imports
+import "qm-interpolated-string" Text.InterpolatedString.QM (qn)
+
+
+spec :: Spec
+spec = do
+
+  describe "Examples from README" $ do
+
+    it "First (decorative spacing and escaping space symbol)" $
+      [qn|   hello world,
+           \ what's going on here?  |]
+        `shouldBe` "hello world, what's going on here?"
+
+    it "Second (merging lines without spaces)" $
+      [qn|
+            it's actual
+            ly ignored
+         |]
+            `shouldBe` "it's actually ignored"
+
+    it "Third (explicit line breaks symbols)" $
+      [qn|  \  You could explicitly escape indentation or\n
+               line breaks when you really need it!  \
+         |] `shouldBe` "  You could explicitly escape indentation or\n\
+                       \line breaks when you really need it!  "
+
+    it "Fourth (escaping interpolation blocks to show them as text)" $
+      [qn| {1+2} \{3+4} |] `shouldBe` "{1+2} \\{3+4}"
+
+    it "Example of `qn` QuasiQuoter" $
+      [qn| foo {1+2} |] `shouldBe` "foo {1+2}"
+
+  it "Type annotation in interpolation block" $
+    [qn|{10 :: Float}|] `shouldBe` "{10 :: Float}"
+
+  it "Escaping interpolation symbols inside interpolation block" $ do
+    [qn|foo {"b{a{r"} baz|] `shouldBe` "foo {\"b{a{r\"} baz"
+    [qn|foo {"b\}a\}r"} baz|] `shouldBe` "foo {\"b\\}a\\}r\"} baz"
+
+  it "Example from generated docs" $
+    [qn| foo {'b':'a':'r':""}
+       \ baz |] `shouldBe` "foo {'b':'a':'r':\"\"} baz"
+
+  it "Escaping backslashes" $ do [qn| foo\bar |]    `shouldBe` "foo\\bar"
+                                 [qn| foo\\bar |]   `shouldBe` "foo\\bar"
+                                 [qn| foo\\\bar |]  `shouldBe` "foo\\\\bar"
+                                 [qn| foo\\\\bar |] `shouldBe` "foo\\\\bar"
+
+  it "Empty string" $ [qn|  |] `shouldBe` ""
+
+  it "Escaping space by slash at EOL after space" $
+    [qn| foo \
+         bar |] `shouldBe` "foo bar"
+
+  it "Escaped spaces at the edges" $ do [qn| foo\ |] `shouldBe` "foo "
+                                        [qn|\ foo |] `shouldBe` " foo"
+
+  describe "Tabs as indentation" $ do
+
+    it "Tabs is only indentation at left side" $ do
+			[qn|
+				foo  bar  baz
+			|] `shouldBe` "foo  bar  baz"
+
+			[qn|			foo bar baz|] `shouldBe` "foo bar baz"
+
+    it "Tabs also at EOL" $ do
+			[qn|
+				foo  bar  baz				
+			|] `shouldBe` "foo  bar  baz"
+
+			[qn|			foo bar baz				|] `shouldBe` "foo bar baz"
+
+    it "Escaped tabs" $ do
+      [qn|		\tfoo|]    `shouldBe` "\tfoo"
+      [qn|		\	foo	|]   `shouldBe` "\tfoo"
+      [qn|	foo		\	|]   `shouldBe` "foo\t\t\t"
+      [qn|	foo	\		|]   `shouldBe` "foo\t\t"
+      [qn|	foo\			|] `shouldBe` "foo\t"
+
+  it "Tails" $ do
+    [qn|    
+           foo   
+                 |] `shouldBe` "foo"
+    [qn|	 
+         	
+          foo	 
+               
+             	
+                 |] `shouldBe` "foo"
+    [qn|				
+            foo			
+            				
+            				|] `shouldBe` "foo"
+
+  describe "New README examples" $ do
+
+    it "Simple usage example" $ do
+      [qn|
+        <article>
+          <h1>{title}</h1>
+          <p>{text}</p>
+        </article>
+      |] `shouldBe` "<article><h1>{title}</h1><p>{text}</p></article>"
+
+    it "Interpolation" $ [qn| foo {1+2} |] `shouldBe` "foo {1+2}"
+
+  it "Haddock example" $
+    [qn| foo {'b':'a':'r':""}
+       \ baz |] `shouldBe` "foo {'b':'a':'r':\"\"} baz"
+
+  it "Escaping backslash itself when it makes sense" $ do
+    [qn| foo\nbar   |] `shouldBe` "foo\nbar"
+    [qn| foo\\nbar  |] `shouldBe` "foo\\nbar"
+    [qn| foo\tbar   |] `shouldBe` "foo\tbar"
+    [qn| foo\\tbar  |] `shouldBe` "foo\\tbar"
+    [qn| foo\	bar   |] `shouldBe` "foo\tbar"
+    [qn| foo\\	bar |] `shouldBe` "foo\\\tbar"
+    [qn| foo\ bar   |] `shouldBe` "foo bar"
+    [qn| foo\\ bar  |] `shouldBe` "foo\\ bar"
+
+    [qn| foo\
+         bar  |] `shouldBe` "foobar"
+    [qn| foo\\
+         bar  |] `shouldBe` "foo\\bar"
+ test/LineBreaks/CRLF/QNB/Spec.hs view
@@ -0,0 +1,158 @@+-- This file automatically generated by generate-cr-and-crlf-tests.sh (do not modify!)
+{-# LANGUAGE PackageImports #-}
+{-# LANGUAGE QuasiQuotes #-}
+
+module LineBreaks.CRLF.QNB.Spec (spec) where
+
+import "hspec" Test.Hspec (Spec, describe, it, shouldBe)
+
+-- local imports
+import "qm-interpolated-string" Text.InterpolatedString.QM (qnb)
+
+
+spec :: Spec
+spec = do
+
+  it "Works as expected" $
+    [qnb|
+      foo
+      {1+2}
+      bar
+    |] `shouldBe` "foo\n{1+2}\nbar"
+
+  it "Explicitly cutting off line breaks" $ do
+    [qnb|
+      foo\
+      {1+2}\
+      bar\
+      baz
+    |] `shouldBe` "foo{1+2}barbaz"
+    [qnb|
+      foo\
+      {1+2}\
+      bar\
+      baz\
+    |] `shouldBe` "foo{1+2}barbaz"
+
+  describe "Examples from README" $ do
+
+    it "First (decorative spacing and escaping space symbol)" $
+      [qnb|   hello world,
+            \ what's going on here?  |]
+        `shouldBe` "hello world,\n what's going on here?"
+
+    it "Second (breaking lines)" $
+      [qnb|
+            it's actual
+            ly NOT ignored
+         |]
+            `shouldBe` "it's actual\nly NOT ignored"
+
+    it "Third (explicit line breaks symbols, line break plus space)" $
+      [qnb|  \  You could explicitly escape indentation or\n
+                line breaks when you really need it!  \
+          |] `shouldBe` "  You could explicitly escape indentation or\n\n\
+                        \line breaks when you really need it!  "
+
+    it "Fourth (escaping interpolation blocks to show them as text)" $
+      [qnb| {1+2} \{3+4} |] `shouldBe` "{1+2} \\{3+4}"
+
+    it "Example of `qnb` QuasiQuoter" $
+      [qnb| foo {1+2} |] `shouldBe` "foo {1+2}"
+
+  it "Type annotation in interpolation block" $
+    [qnb|{10 :: Float}|] `shouldBe` "{10 :: Float}"
+
+  it "Escaping interpolation symbols inside interpolation block" $ do
+    [qnb|foo {"b{a{r"} baz|] `shouldBe` "foo {\"b{a{r\"} baz"
+    [qnb|foo {"b\}a\}r"} baz|] `shouldBe` "foo {\"b\\}a\\}r\"} baz"
+
+  it "Example from generated docs (double-space)" $
+    [qnb| foo {'b':'a':'r':""}
+        \ baz |] `shouldBe` "foo {'b':'a':'r':\"\"}\n baz"
+
+  it "Escaping backslashes" $ do [qnb| foo\bar |]    `shouldBe` "foo\\bar"
+                                 [qnb| foo\\bar |]   `shouldBe` "foo\\bar"
+                                 [qnb| foo\\\bar |]  `shouldBe` "foo\\\\bar"
+                                 [qnb| foo\\\\bar |] `shouldBe` "foo\\\\bar"
+
+  it "Empty string" $ [qnb|  |] `shouldBe` ""
+
+  it "Escaping space by backslash at EOL after space (line break is cutted off)" $
+    [qnb| foo \
+          bar |] `shouldBe` "foo bar"
+
+  it "Escaped spaces at the edges" $ do [qnb| foo\ |] `shouldBe` "foo "
+                                        [qnb|\ foo |] `shouldBe` " foo"
+
+  describe "Tabs as indentation" $ do
+
+    it "Tabs is only indentation at left side" $ do
+			[qnb|
+				foo  bar  baz
+			|] `shouldBe` "foo  bar  baz"
+
+			[qnb|			foo bar baz|] `shouldBe` "foo bar baz"
+
+    it "Tabs also at EOL" $ do
+			[qnb|
+				foo  bar  baz				
+			|] `shouldBe` "foo  bar  baz"
+
+			[qnb|			foo bar baz				|] `shouldBe` "foo bar baz"
+
+    it "Escaped tabs" $ do
+      [qnb|		\tfoo|]    `shouldBe` "\tfoo"
+      [qnb|		\	foo	|]   `shouldBe` "\tfoo"
+      [qnb|	foo		\	|]   `shouldBe` "foo\t\t\t"
+      [qnb|	foo	\		|]   `shouldBe` "foo\t\t"
+      [qnb|	foo\			|] `shouldBe` "foo\t"
+
+  it "Tails" $ do
+    [qnb|    
+           foo   
+                 |] `shouldBe` "foo"
+    [qnb|	 
+         	
+          foo	 
+               
+             	
+                 |] `shouldBe` "foo"
+    [qnb|				
+            foo			
+            				
+            				|] `shouldBe` "foo"
+
+  describe "New README examples" $ do
+
+    it "Simple usage example" $ do
+      [qnb|
+        <article>
+          <h1>{title}</h1>
+          <p>{text}</p>
+        </article>
+      |]
+        `shouldBe`
+          "<article>\n<h1>{title}</h1>\n<p>{text}</p>\n</article>"
+
+    it "Interpolation" $ [qnb| foo {1+2} |] `shouldBe` "foo {1+2}"
+
+  it "Haddock example" $
+    [qnb| foo
+          {'b':'a':'r':""}
+          baz |] `shouldBe` "foo\n{'b':'a':'r':\"\"}\nbaz"
+
+  it "Escaping backslash itself when it makes sense" $ do
+    [qnb| foo\nbar  |] `shouldBe` "foo\nbar"
+    [qnb| foo\\nbar |] `shouldBe` "foo\\nbar"
+    [qnb| foo\tbar  |] `shouldBe` "foo\tbar"
+    [qnb| foo\\tbar |] `shouldBe` "foo\\tbar"
+    [qnb| foo\	bar |] `shouldBe` "foo\tbar"
+    [qnb| foo\\	bar |] `shouldBe` "foo\\\tbar"
+    [qnb| foo\ bar  |] `shouldBe` "foo bar"
+    [qnb| foo\\ bar |] `shouldBe` "foo\\ bar"
+
+    [qnb| foo\
+          bar  |] `shouldBe` "foobar"
+    [qnb| foo\\
+          bar  |] `shouldBe` "foo\\\nbar"
+ test/LineBreaks/CRLF/QNS/Spec.hs view
@@ -0,0 +1,158 @@+-- This file automatically generated by generate-cr-and-crlf-tests.sh (do not modify!)
+{-# LANGUAGE PackageImports #-}
+{-# LANGUAGE QuasiQuotes #-}
+
+module LineBreaks.CRLF.QNS.Spec (spec) where
+
+import "hspec" Test.Hspec (Spec, describe, it, shouldBe)
+
+-- local imports
+import "qm-interpolated-string" Text.InterpolatedString.QM (qns)
+
+
+spec :: Spec
+spec = do
+
+  it "Works as expected" $
+    [qns|
+      foo
+      {1+2}
+      bar
+    |] `shouldBe` "foo {1+2} bar"
+
+  it "Explicitly cutting off line breaks" $ do
+    [qns|
+      foo\
+      {1+2}\
+      bar\
+      baz
+    |] `shouldBe` "foo{1+2}barbaz"
+    [qns|
+      foo\
+      {1+2}\
+      bar\
+      baz\
+    |] `shouldBe` "foo{1+2}barbaz"
+
+  describe "Examples from README" $ do
+
+    it "First (decorative spacing and escaping space symbol)" $
+      [qns|   hello world,
+            \ what's going on here?  |]
+        `shouldBe` "hello world,  what's going on here?"
+
+    it "Second (breaking lines)" $
+      [qns|
+            it's actual
+            ly NOT ignored
+         |]
+            `shouldBe` "it's actual ly NOT ignored"
+
+    it "Third (explicit line breaks symbols, line break plus space)" $
+      [qns|  \  You could explicitly escape indentation or\n
+                line breaks when you really need it!  \
+          |] `shouldBe` "  You could explicitly escape indentation or\n \
+                        \line breaks when you really need it!  "
+
+    it "Fourth (escaping interpolation blocks to show them as text)" $
+      [qns| {1+2} \{3+4} |] `shouldBe` "{1+2} \\{3+4}"
+
+    it "Example of `qns` QuasiQuoter" $
+      [qns| foo {1+2} |] `shouldBe` "foo {1+2}"
+
+  it "Type annotation in interpolation block" $
+    [qns|{10 :: Float}|] `shouldBe` "{10 :: Float}"
+
+  it "Escaping interpolation symbols inside interpolation block" $ do
+    [qns|foo {"b{a{r"} baz|] `shouldBe` "foo {\"b{a{r\"} baz"
+    [qns|foo {"b\}a\}r"} baz|] `shouldBe` "foo {\"b\\}a\\}r\"} baz"
+
+  it "Example from generated docs (double-space)" $
+    [qns| foo {'b':'a':'r':""}
+        \ baz |] `shouldBe` "foo {'b':'a':'r':\"\"}  baz"
+
+  it "Escaping backslashes" $ do [qns| foo\bar |]    `shouldBe` "foo\\bar"
+                                 [qns| foo\\bar |]   `shouldBe` "foo\\bar"
+                                 [qns| foo\\\bar |]  `shouldBe` "foo\\\\bar"
+                                 [qns| foo\\\\bar |] `shouldBe` "foo\\\\bar"
+
+  it "Empty string" $ [qns|  |] `shouldBe` ""
+
+  it "Escaping space by backslash at EOL after space (line break is cutted off)" $
+    [qns| foo \
+          bar |] `shouldBe` "foo bar"
+
+  it "Escaped spaces at the edges" $ do [qns| foo\ |] `shouldBe` "foo "
+                                        [qns|\ foo |] `shouldBe` " foo"
+
+  describe "Tabs as indentation" $ do
+
+    it "Tabs is only indentation at left side" $ do
+			[qns|
+				foo  bar  baz
+			|] `shouldBe` "foo  bar  baz"
+
+			[qns|			foo bar baz|] `shouldBe` "foo bar baz"
+
+    it "Tabs also at EOL" $ do
+			[qns|
+				foo  bar  baz				
+			|] `shouldBe` "foo  bar  baz"
+
+			[qns|			foo bar baz				|] `shouldBe` "foo bar baz"
+
+    it "Escaped tabs" $ do
+      [qns|		\tfoo|]    `shouldBe` "\tfoo"
+      [qns|		\	foo	|]   `shouldBe` "\tfoo"
+      [qns|	foo		\	|]   `shouldBe` "foo\t\t\t"
+      [qns|	foo	\		|]   `shouldBe` "foo\t\t"
+      [qns|	foo\			|] `shouldBe` "foo\t"
+
+  it "Tails" $ do
+    [qns|    
+           foo   
+                 |] `shouldBe` "foo"
+    [qns|	 
+         	
+          foo	 
+               
+             	
+                 |] `shouldBe` "foo"
+    [qns|				
+            foo			
+            				
+            				|] `shouldBe` "foo"
+
+  describe "New README examples" $ do
+
+    it "Simple usage example" $ do
+      [qns|
+        <article>
+          <h1>{title}</h1>
+          <p>{text}</p>
+        </article>
+      |]
+        `shouldBe`
+          "<article> <h1>{title}</h1> <p>{text}</p> </article>"
+
+    it "Interpolation" $ [qns| foo {1+2} |] `shouldBe` "foo {1+2}"
+
+  it "Haddock example" $
+    [qns| foo
+          {'b':'a':'r':""}
+          baz |] `shouldBe` "foo {'b':'a':'r':\"\"} baz"
+
+  it "Escaping backslash itself when it makes sense" $ do
+    [qns| foo\nbar  |] `shouldBe` "foo\nbar"
+    [qns| foo\\nbar |] `shouldBe` "foo\\nbar"
+    [qns| foo\tbar  |] `shouldBe` "foo\tbar"
+    [qns| foo\\tbar |] `shouldBe` "foo\\tbar"
+    [qns| foo\	bar |] `shouldBe` "foo\tbar"
+    [qns| foo\\	bar |] `shouldBe` "foo\\\tbar"
+    [qns| foo\ bar  |] `shouldBe` "foo bar"
+    [qns| foo\\ bar |] `shouldBe` "foo\\ bar"
+
+    [qns| foo\
+          bar  |] `shouldBe` "foobar"
+    [qns| foo\\
+          bar  |] `shouldBe` "foo\\ bar"
+ test/QM/Spec.hs view
@@ -0,0 +1,160 @@+{-# LANGUAGE PackageImports #-}+{-# LANGUAGE QuasiQuotes #-}++module QM.Spec (spec) where++import "hspec" Test.Hspec (Spec, describe, it, shouldBe)++-- local imports+import "qm-interpolated-string" Text.InterpolatedString.QM (qm)+++spec :: Spec+spec = do++  describe "Examples from README" $ do++    it "First (decorative spacing and escaping space symbol)" $+      [qm|   hello world,+           \ what's going on here?  |]+        `shouldBe` "hello world, what's going on here?"++    it "Second (merging lines without spaces)" $+      [qm|+            it's actual+            ly ignored+         |]+            `shouldBe` "it's actually ignored"++    it "Third (explicit line breaks symbols)" $+      [qm|  \  You could explicitly escape indentation or\n+               line breaks when you really need it!  \+         |] `shouldBe` "  You could explicitly escape indentation or\n\+                       \line breaks when you really need it!  "++    it "Fourth (escaping interpolation blocks to show them as text)" $+      [qm| {1+2} \{3+4} |] `shouldBe` "3 {3+4}"++  it "Type annotation in interpolation block" $+    [qm|{10 :: Float}|] `shouldBe` "10.0"++  it "Escaping interpolation symbols inside interpolation block" $ do+    [qm|foo {"b{a{r"} baz|]   `shouldBe` "foo b{a{r baz"+    [qm|foo {"b\}a\}r"} baz|] `shouldBe` "foo b}a}r baz"++  it "Example from generated docs" $ [qm| foo {'b':'a':'r':""}+                                        \ baz |] `shouldBe` "foo bar baz"++  it "Escaping backslashes" $ do [qm| foo\bar |]    `shouldBe` "foo\\bar"+                                 [qm| foo\\bar |]   `shouldBe` "foo\\bar"+                                 [qm| foo\\\bar |]  `shouldBe` "foo\\\\bar"+                                 [qm| foo\\\\bar |] `shouldBe` "foo\\\\bar"++  it "Empty string" $ [qm|  |] `shouldBe` ""++  it "Escaping space by slash at EOL after space" $+    [qm| foo \+         bar |] `shouldBe` "foo bar"++  it "Escaped spaces at the edges" $ do [qm| foo\ |] `shouldBe` "foo "+                                        [qm|\ foo |] `shouldBe` " foo"++  describe "Tabs as indentation" $ do++    it "Tabs is only indentation at left side" $ do+			[qm|+				foo  bar  baz+			|] `shouldBe` "foo  bar  baz"++			[qm|			foo bar baz|] `shouldBe` "foo bar baz"++    it "Tabs also at EOL" $ do+			[qm|+				foo  bar  baz				+			|] `shouldBe` "foo  bar  baz"++			[qm|			foo bar baz				|] `shouldBe` "foo bar baz"++    it "Escaped tabs" $ do+      [qm|		\tfoo|]    `shouldBe` "\tfoo"+      [qm|		\	foo	|]   `shouldBe` "\tfoo"+      [qm|	foo		\	|]   `shouldBe` "foo\t\t\t"+      [qm|	foo	\		|]   `shouldBe` "foo\t\t"+      [qm|	foo\			|] `shouldBe` "foo\t"++  it "Tails" $ do+    [qm|    +           foo   +                 |] `shouldBe` "foo"+    [qm|	 +         	+          foo	 +               +             	+                 |] `shouldBe` "foo"+    [qm|				+            foo			+            				+            				|] `shouldBe` "foo"++  describe "New README examples" $ do++    it "Simple usage example" $ do+      let title = "Testing"+          text = "Some testing text"+      [qm|+        <article>+          <h1>{title}</h1>+          <p>{text}</p>+        </article>+      |]+        `shouldBe`+          "<article><h1>Testing</h1><p>Some testing text</p></article>"++    it "Can escape spaces" $+      [qm|   you can escape spaces+           \ when you need them    |]+        `shouldBe`+          "you can escape spaces when you need them"++    it "Indentation and line breaks are ignored" $ +      [qm|+              indentation and li+        ne bre+         aks are i+             gno+           red+      |]+      `shouldBe` "indentation and line breaks are ignored"++    it "Escaping indentation or line breaks" $+      [qm|  \  You can escape indentation or\n+               line breaks when you need them! \  |]+        `shouldBe`+          "  You can escape indentation or\nline breaks when you need them!  "++    it "Interpolation blocks can be escaped too" $+      [qm| Interpolation blocks can be escaped too: {1+2} \{3+4} |]+        `shouldBe`+          "Interpolation blocks can be escaped too: 3 {3+4}"++    it "Interpolation" $ [qm| foo {1+2} |] `shouldBe` "foo 3"++  it "Haddock example" $+    [qm| foo {'b':'a':'r':""}+       \ baz |] `shouldBe` "foo bar baz"++  it "Escaping backslash itself when it makes sense" $ do+    [qm| foo\nbar   |] `shouldBe` "foo\nbar"+    [qm| foo\\nbar  |] `shouldBe` "foo\\nbar"+    [qm| foo\tbar   |] `shouldBe` "foo\tbar"+    [qm| foo\\tbar  |] `shouldBe` "foo\\tbar"+    [qm| foo\	bar   |] `shouldBe` "foo\tbar"+    [qm| foo\\	bar |] `shouldBe` "foo\\\tbar"+    [qm| foo\ bar   |] `shouldBe` "foo bar"+    [qm| foo\\ bar  |] `shouldBe` "foo\\ bar"++    [qm| foo\+         bar  |] `shouldBe` "foobar"+    [qm| foo\\+         bar  |] `shouldBe` "foo\\bar"
+ test/QMB/Spec.hs view
@@ -0,0 +1,155 @@+{-# LANGUAGE PackageImports #-}+{-# LANGUAGE QuasiQuotes #-}++module QMB.Spec (spec) where++import "hspec" Test.Hspec (Spec, describe, it, shouldBe)++-- local imports+import "qm-interpolated-string" Text.InterpolatedString.QM (qmb)+++spec :: Spec+spec = do++  it "Works as expected" $+    [qmb|+      foo+      {1+2}+      bar+    |] `shouldBe` "foo\n3\nbar"++  it "Explicitly cutting off line breaks" $ do+    [qmb|+      foo\+      {1+2}\+      bar\+      baz+    |] `shouldBe` "foo3barbaz"+    [qmb|+      foo\+      {1+2}\+      bar\+      baz\+    |] `shouldBe` "foo3barbaz"++  describe "Examples from README" $ do++    it "First (decorative spacing and escaping space symbol)" $+      [qmb|   hello world,+            \ what's going on here?  |]+        `shouldBe` "hello world,\n what's going on here?"++    it "Second (breaking lines)" $+      [qmb|+            it's actual+            ly NOT ignored+         |]+            `shouldBe` "it's actual\nly NOT ignored"++    it "Third (explicit line breaks symbols)" $+      [qmb|  \  You could explicitly escape indentation or\n+                line breaks when you really need it!  \+          |] `shouldBe` "  You could explicitly escape indentation or\n\n\+                        \line breaks when you really need it!  "++    it "Fourth (escaping interpolation blocks to show them as text)" $+      [qmb| {1+2} \{3+4} |] `shouldBe` "3 {3+4}"++  it "Type annotation in interpolation block" $+    [qmb|{10 :: Float}|] `shouldBe` "10.0"++  it "Escaping interpolation symbols inside interpolation block" $ do+    [qmb|foo {"b{a{r"} baz|]   `shouldBe` "foo b{a{r baz"+    [qmb|foo {"b\}a\}r"} baz|] `shouldBe` "foo b}a}r baz"++  it "Example from generated docs" $ [qmb| foo {'b':'a':'r':""}+                                         \ baz |] `shouldBe` "foo bar\n baz"++  it "Escaping backslashes" $ do [qmb| foo\bar |]    `shouldBe` "foo\\bar"+                                 [qmb| foo\\bar |]   `shouldBe` "foo\\bar"+                                 [qmb| foo\\\bar |]  `shouldBe` "foo\\\\bar"+                                 [qmb| foo\\\\bar |] `shouldBe` "foo\\\\bar"++  it "Empty string" $ [qmb|  |] `shouldBe` ""++  it "Escaping space by backslash at EOL after space (line break is cutted off)" $+    [qmb| foo \+          bar |] `shouldBe` "foo bar"++  it "Escaped spaces at the edges" $ do [qmb| foo\ |] `shouldBe` "foo "+                                        [qmb|\ foo |] `shouldBe` " foo"++  describe "Tabs as indentation" $ do++    it "Tabs is only indentation at left side" $ do+			[qmb|+				foo  bar  baz+			|] `shouldBe` "foo  bar  baz"++			[qmb|			foo bar baz|] `shouldBe` "foo bar baz"++    it "Tabs also at EOL" $ do+			[qmb|+				foo  bar  baz				+			|] `shouldBe` "foo  bar  baz"++			[qmb|			foo bar baz				|] `shouldBe` "foo bar baz"++    it "Escaped tabs" $ do+      [qmb|		\tfoo|]    `shouldBe` "\tfoo"+      [qmb|		\	foo	|]   `shouldBe` "\tfoo"+      [qmb|	foo		\	|]   `shouldBe` "foo\t\t\t"+      [qmb|	foo	\		|]   `shouldBe` "foo\t\t"+      [qmb|	foo\			|] `shouldBe` "foo\t"++  it "Tails" $ do+    [qmb|    +           foo   +                 |] `shouldBe` "foo"+    [qmb|	 +         	+          foo	 +               +             	+                 |] `shouldBe` "foo"+    [qmb|				+            foo			+            				+            				|] `shouldBe` "foo"++  describe "New README examples" $ do++    it "Simple usage example" $ do+      let title = "Testing"+          text = "Some testing text"+      [qmb|+        <article>+          <h1>{title}</h1>+          <p>{text}</p>+        </article>+      |]+        `shouldBe`+          "<article>\n<h1>Testing</h1>\n<p>Some testing text</p>\n</article>"++    it "Interpolation" $ [qmb| foo {1+2} |] `shouldBe` "foo 3"++  it "Haddock example" $+    [qmb| foo+          {'b':'a':'r':""}+          baz |] `shouldBe` "foo\nbar\nbaz"++  it "Escaping backslash itself when it makes sense" $ do+    [qmb| foo\nbar  |] `shouldBe` "foo\nbar"+    [qmb| foo\\nbar |] `shouldBe` "foo\\nbar"+    [qmb| foo\tbar  |] `shouldBe` "foo\tbar"+    [qmb| foo\\tbar |] `shouldBe` "foo\\tbar"+    [qmb| foo\	bar |] `shouldBe` "foo\tbar"+    [qmb| foo\\	bar |] `shouldBe` "foo\\\tbar"+    [qmb| foo\ bar  |] `shouldBe` "foo bar"+    [qmb| foo\\ bar |] `shouldBe` "foo\\ bar"++    [qmb| foo\+          bar  |] `shouldBe` "foobar"+    [qmb| foo\\+          bar  |] `shouldBe` "foo\\\nbar"
+ test/QMS/Spec.hs view
@@ -0,0 +1,161 @@+{-# LANGUAGE PackageImports #-}+{-# LANGUAGE QuasiQuotes #-}++module QMS.Spec (spec) where++import "hspec" Test.Hspec (Spec, describe, it, shouldBe)++-- local imports+import "qm-interpolated-string" Text.InterpolatedString.QM (qms)+++spec :: Spec+spec = do++  it "Works as expected" $+    [qms|+      foo+      {1+2}+      bar+    |] `shouldBe` "foo 3 bar"++  it "Explicitly cutting off line breaks" $ do+    [qms|+      foo\+      {1+2}\+      bar\+      baz+    |] `shouldBe` "foo3barbaz"+    [qms|+      foo\+      {1+2}\+      bar\+      baz\+    |] `shouldBe` "foo3barbaz"++  describe "Examples from README" $ do++    it "First (decorative spacing and escaping space symbol)" $+      [qms|   hello world,+            \ what's going on here?  |]+        `shouldBe` "hello world,  what's going on here?"++    it "Second (breaking lines)" $+      [qms|+            it's actual+            ly NOT ignored+         |]+            `shouldBe` "it's actual ly NOT ignored"++    it "Third (explicit line breaks symbols)" $+      [qms|  \  You could explicitly escape indentation or\n+                line breaks when you really need it!  \+          |] `shouldBe` "  You could explicitly escape indentation or\n \+                        \line breaks when you really need it!  "++    it "Fourth (escaping interpolation blocks to show them as text)" $+      [qms| {1+2} \{3+4} |] `shouldBe` "3 {3+4}"++  it "Type annotation in interpolation block" $+    [qms|{10 :: Float}|] `shouldBe` "10.0"++  it "Escaping interpolation symbols inside interpolation block" $ do+    [qms|foo {"b{a{r"} baz|]   `shouldBe` "foo b{a{r baz"+    [qms|foo {"b\}a\}r"} baz|] `shouldBe` "foo b}a}r baz"++  it "Example from generated docs" $ [qms| foo {'b':'a':'r':""}+                                         \ baz |] `shouldBe` "foo bar  baz"++  it "Escaping backslashes" $ do [qms| foo\bar |]    `shouldBe` "foo\\bar"+                                 [qms| foo\\bar |]   `shouldBe` "foo\\bar"+                                 [qms| foo\\\bar |]  `shouldBe` "foo\\\\bar"+                                 [qms| foo\\\\bar |] `shouldBe` "foo\\\\bar"++  it "Empty string" $ [qms|  |] `shouldBe` ""++  it "Escaping space by backslash at EOL after space (line break is cutted off)" $+    [qms| foo \+          bar |] `shouldBe` "foo bar"++  it "Escaped spaces at the edges" $ do [qms| foo\ |] `shouldBe` "foo "+                                        [qms|\ foo |] `shouldBe` " foo"++  describe "Tabs as indentation" $ do++    it "Tabs is only indentation at left side" $ do+			[qms|+				foo  bar  baz+			|] `shouldBe` "foo  bar  baz"++			[qms|			foo bar baz|] `shouldBe` "foo bar baz"++    it "Tabs also at EOL" $ do+			[qms|+				foo  bar  baz				+			|] `shouldBe` "foo  bar  baz"++			[qms|			foo bar baz				|] `shouldBe` "foo bar baz"++    it "Escaped tabs" $ do+      [qms|		\tfoo|]    `shouldBe` "\tfoo"+      [qms|		\	foo	|]   `shouldBe` "\tfoo"+      [qms|	foo		\	|]   `shouldBe` "foo\t\t\t"+      [qms|	foo	\		|]   `shouldBe` "foo\t\t"+      [qms|	foo\			|] `shouldBe` "foo\t"++  it "Tails" $ do+    [qms|    +           foo   +                 |] `shouldBe` "foo"+    [qms|	 +         	+          foo	 +               +             	+                 |] `shouldBe` "foo"+    [qms|				+            foo			+            				+            				|] `shouldBe` "foo"++  describe "New README examples" $ do++    it "First example" $+      [qms| Hello,+            world!+            Pi is {floor pi}.{floor $ (pi - 3) * 100}… |]+        `shouldBe` "Hello, world! Pi is 3.14…"++    it "Simple usage example" $ do+      let title = "Testing"+          text = "Some testing text"+      [qms|+        <article>+          <h1>{title}</h1>+          <p>{text}</p>+        </article>+      |]+        `shouldBe`+          "<article> <h1>Testing</h1> <p>Some testing text</p> </article>"++    it "Interpolation" $ [qms| foo {1+2} |] `shouldBe` "foo 3"++  it "Haddock example" $+    [qms| foo+          {'b':'a':'r':""}+          baz |] `shouldBe` "foo bar baz"++  it "Escaping backslash itself when it makes sense" $ do+    [qms| foo\nbar  |] `shouldBe` "foo\nbar"+    [qms| foo\\nbar |] `shouldBe` "foo\\nbar"+    [qms| foo\tbar  |] `shouldBe` "foo\tbar"+    [qms| foo\\tbar |] `shouldBe` "foo\\tbar"+    [qms| foo\	bar |] `shouldBe` "foo\tbar"+    [qms| foo\\	bar |] `shouldBe` "foo\\\tbar"+    [qms| foo\ bar  |] `shouldBe` "foo bar"+    [qms| foo\\ bar |] `shouldBe` "foo\\ bar"++    [qms| foo\+          bar  |] `shouldBe` "foobar"+    [qms| foo\\+          bar  |] `shouldBe` "foo\\ bar"
+ test/QN/Spec.hs view
@@ -0,0 +1,133 @@+{-# LANGUAGE PackageImports #-}+{-# LANGUAGE QuasiQuotes #-}++module QN.Spec (spec) where++import "hspec" Test.Hspec (Spec, describe, it, shouldBe)++-- local imports+import "qm-interpolated-string" Text.InterpolatedString.QM (qn)+++spec :: Spec+spec = do++  describe "Examples from README" $ do++    it "First (decorative spacing and escaping space symbol)" $+      [qn|   hello world,+           \ what's going on here?  |]+        `shouldBe` "hello world, what's going on here?"++    it "Second (merging lines without spaces)" $+      [qn|+            it's actual+            ly ignored+         |]+            `shouldBe` "it's actually ignored"++    it "Third (explicit line breaks symbols)" $+      [qn|  \  You could explicitly escape indentation or\n+               line breaks when you really need it!  \+         |] `shouldBe` "  You could explicitly escape indentation or\n\+                       \line breaks when you really need it!  "++    it "Fourth (escaping interpolation blocks to show them as text)" $+      [qn| {1+2} \{3+4} |] `shouldBe` "{1+2} \\{3+4}"++    it "Example of `qn` QuasiQuoter" $+      [qn| foo {1+2} |] `shouldBe` "foo {1+2}"++  it "Type annotation in interpolation block" $+    [qn|{10 :: Float}|] `shouldBe` "{10 :: Float}"++  it "Escaping interpolation symbols inside interpolation block" $ do+    [qn|foo {"b{a{r"} baz|] `shouldBe` "foo {\"b{a{r\"} baz"+    [qn|foo {"b\}a\}r"} baz|] `shouldBe` "foo {\"b\\}a\\}r\"} baz"++  it "Example from generated docs" $+    [qn| foo {'b':'a':'r':""}+       \ baz |] `shouldBe` "foo {'b':'a':'r':\"\"} baz"++  it "Escaping backslashes" $ do [qn| foo\bar |]    `shouldBe` "foo\\bar"+                                 [qn| foo\\bar |]   `shouldBe` "foo\\bar"+                                 [qn| foo\\\bar |]  `shouldBe` "foo\\\\bar"+                                 [qn| foo\\\\bar |] `shouldBe` "foo\\\\bar"++  it "Empty string" $ [qn|  |] `shouldBe` ""++  it "Escaping space by slash at EOL after space" $+    [qn| foo \+         bar |] `shouldBe` "foo bar"++  it "Escaped spaces at the edges" $ do [qn| foo\ |] `shouldBe` "foo "+                                        [qn|\ foo |] `shouldBe` " foo"++  describe "Tabs as indentation" $ do++    it "Tabs is only indentation at left side" $ do+			[qn|+				foo  bar  baz+			|] `shouldBe` "foo  bar  baz"++			[qn|			foo bar baz|] `shouldBe` "foo bar baz"++    it "Tabs also at EOL" $ do+			[qn|+				foo  bar  baz				+			|] `shouldBe` "foo  bar  baz"++			[qn|			foo bar baz				|] `shouldBe` "foo bar baz"++    it "Escaped tabs" $ do+      [qn|		\tfoo|]    `shouldBe` "\tfoo"+      [qn|		\	foo	|]   `shouldBe` "\tfoo"+      [qn|	foo		\	|]   `shouldBe` "foo\t\t\t"+      [qn|	foo	\		|]   `shouldBe` "foo\t\t"+      [qn|	foo\			|] `shouldBe` "foo\t"++  it "Tails" $ do+    [qn|    +           foo   +                 |] `shouldBe` "foo"+    [qn|	 +         	+          foo	 +               +             	+                 |] `shouldBe` "foo"+    [qn|				+            foo			+            				+            				|] `shouldBe` "foo"++  describe "New README examples" $ do++    it "Simple usage example" $ do+      [qn|+        <article>+          <h1>{title}</h1>+          <p>{text}</p>+        </article>+      |] `shouldBe` "<article><h1>{title}</h1><p>{text}</p></article>"++    it "Interpolation" $ [qn| foo {1+2} |] `shouldBe` "foo {1+2}"++  it "Haddock example" $+    [qn| foo {'b':'a':'r':""}+       \ baz |] `shouldBe` "foo {'b':'a':'r':\"\"} baz"++  it "Escaping backslash itself when it makes sense" $ do+    [qn| foo\nbar   |] `shouldBe` "foo\nbar"+    [qn| foo\\nbar  |] `shouldBe` "foo\\nbar"+    [qn| foo\tbar   |] `shouldBe` "foo\tbar"+    [qn| foo\\tbar  |] `shouldBe` "foo\\tbar"+    [qn| foo\	bar   |] `shouldBe` "foo\tbar"+    [qn| foo\\	bar |] `shouldBe` "foo\\\tbar"+    [qn| foo\ bar   |] `shouldBe` "foo bar"+    [qn| foo\\ bar  |] `shouldBe` "foo\\ bar"++    [qn| foo\+         bar  |] `shouldBe` "foobar"+    [qn| foo\\+         bar  |] `shouldBe` "foo\\bar"
+ test/QNB/Spec.hs view
@@ -0,0 +1,157 @@+{-# LANGUAGE PackageImports #-}+{-# LANGUAGE QuasiQuotes #-}++module QNB.Spec (spec) where++import "hspec" Test.Hspec (Spec, describe, it, shouldBe)++-- local imports+import "qm-interpolated-string" Text.InterpolatedString.QM (qnb)+++spec :: Spec+spec = do++  it "Works as expected" $+    [qnb|+      foo+      {1+2}+      bar+    |] `shouldBe` "foo\n{1+2}\nbar"++  it "Explicitly cutting off line breaks" $ do+    [qnb|+      foo\+      {1+2}\+      bar\+      baz+    |] `shouldBe` "foo{1+2}barbaz"+    [qnb|+      foo\+      {1+2}\+      bar\+      baz\+    |] `shouldBe` "foo{1+2}barbaz"++  describe "Examples from README" $ do++    it "First (decorative spacing and escaping space symbol)" $+      [qnb|   hello world,+            \ what's going on here?  |]+        `shouldBe` "hello world,\n what's going on here?"++    it "Second (breaking lines)" $+      [qnb|+            it's actual+            ly NOT ignored+         |]+            `shouldBe` "it's actual\nly NOT ignored"++    it "Third (explicit line breaks symbols, line break plus space)" $+      [qnb|  \  You could explicitly escape indentation or\n+                line breaks when you really need it!  \+          |] `shouldBe` "  You could explicitly escape indentation or\n\n\+                        \line breaks when you really need it!  "++    it "Fourth (escaping interpolation blocks to show them as text)" $+      [qnb| {1+2} \{3+4} |] `shouldBe` "{1+2} \\{3+4}"++    it "Example of `qnb` QuasiQuoter" $+      [qnb| foo {1+2} |] `shouldBe` "foo {1+2}"++  it "Type annotation in interpolation block" $+    [qnb|{10 :: Float}|] `shouldBe` "{10 :: Float}"++  it "Escaping interpolation symbols inside interpolation block" $ do+    [qnb|foo {"b{a{r"} baz|] `shouldBe` "foo {\"b{a{r\"} baz"+    [qnb|foo {"b\}a\}r"} baz|] `shouldBe` "foo {\"b\\}a\\}r\"} baz"++  it "Example from generated docs (double-space)" $+    [qnb| foo {'b':'a':'r':""}+        \ baz |] `shouldBe` "foo {'b':'a':'r':\"\"}\n baz"++  it "Escaping backslashes" $ do [qnb| foo\bar |]    `shouldBe` "foo\\bar"+                                 [qnb| foo\\bar |]   `shouldBe` "foo\\bar"+                                 [qnb| foo\\\bar |]  `shouldBe` "foo\\\\bar"+                                 [qnb| foo\\\\bar |] `shouldBe` "foo\\\\bar"++  it "Empty string" $ [qnb|  |] `shouldBe` ""++  it "Escaping space by backslash at EOL after space (line break is cutted off)" $+    [qnb| foo \+          bar |] `shouldBe` "foo bar"++  it "Escaped spaces at the edges" $ do [qnb| foo\ |] `shouldBe` "foo "+                                        [qnb|\ foo |] `shouldBe` " foo"++  describe "Tabs as indentation" $ do++    it "Tabs is only indentation at left side" $ do+			[qnb|+				foo  bar  baz+			|] `shouldBe` "foo  bar  baz"++			[qnb|			foo bar baz|] `shouldBe` "foo bar baz"++    it "Tabs also at EOL" $ do+			[qnb|+				foo  bar  baz				+			|] `shouldBe` "foo  bar  baz"++			[qnb|			foo bar baz				|] `shouldBe` "foo bar baz"++    it "Escaped tabs" $ do+      [qnb|		\tfoo|]    `shouldBe` "\tfoo"+      [qnb|		\	foo	|]   `shouldBe` "\tfoo"+      [qnb|	foo		\	|]   `shouldBe` "foo\t\t\t"+      [qnb|	foo	\		|]   `shouldBe` "foo\t\t"+      [qnb|	foo\			|] `shouldBe` "foo\t"++  it "Tails" $ do+    [qnb|    +           foo   +                 |] `shouldBe` "foo"+    [qnb|	 +         	+          foo	 +               +             	+                 |] `shouldBe` "foo"+    [qnb|				+            foo			+            				+            				|] `shouldBe` "foo"++  describe "New README examples" $ do++    it "Simple usage example" $ do+      [qnb|+        <article>+          <h1>{title}</h1>+          <p>{text}</p>+        </article>+      |]+        `shouldBe`+          "<article>\n<h1>{title}</h1>\n<p>{text}</p>\n</article>"++    it "Interpolation" $ [qnb| foo {1+2} |] `shouldBe` "foo {1+2}"++  it "Haddock example" $+    [qnb| foo+          {'b':'a':'r':""}+          baz |] `shouldBe` "foo\n{'b':'a':'r':\"\"}\nbaz"++  it "Escaping backslash itself when it makes sense" $ do+    [qnb| foo\nbar  |] `shouldBe` "foo\nbar"+    [qnb| foo\\nbar |] `shouldBe` "foo\\nbar"+    [qnb| foo\tbar  |] `shouldBe` "foo\tbar"+    [qnb| foo\\tbar |] `shouldBe` "foo\\tbar"+    [qnb| foo\	bar |] `shouldBe` "foo\tbar"+    [qnb| foo\\	bar |] `shouldBe` "foo\\\tbar"+    [qnb| foo\ bar  |] `shouldBe` "foo bar"+    [qnb| foo\\ bar |] `shouldBe` "foo\\ bar"++    [qnb| foo\+          bar  |] `shouldBe` "foobar"+    [qnb| foo\\+          bar  |] `shouldBe` "foo\\\nbar"
+ test/QNS/Spec.hs view
@@ -0,0 +1,157 @@+{-# LANGUAGE PackageImports #-}+{-# LANGUAGE QuasiQuotes #-}++module QNS.Spec (spec) where++import "hspec" Test.Hspec (Spec, describe, it, shouldBe)++-- local imports+import "qm-interpolated-string" Text.InterpolatedString.QM (qns)+++spec :: Spec+spec = do++  it "Works as expected" $+    [qns|+      foo+      {1+2}+      bar+    |] `shouldBe` "foo {1+2} bar"++  it "Explicitly cutting off line breaks" $ do+    [qns|+      foo\+      {1+2}\+      bar\+      baz+    |] `shouldBe` "foo{1+2}barbaz"+    [qns|+      foo\+      {1+2}\+      bar\+      baz\+    |] `shouldBe` "foo{1+2}barbaz"++  describe "Examples from README" $ do++    it "First (decorative spacing and escaping space symbol)" $+      [qns|   hello world,+            \ what's going on here?  |]+        `shouldBe` "hello world,  what's going on here?"++    it "Second (breaking lines)" $+      [qns|+            it's actual+            ly NOT ignored+         |]+            `shouldBe` "it's actual ly NOT ignored"++    it "Third (explicit line breaks symbols, line break plus space)" $+      [qns|  \  You could explicitly escape indentation or\n+                line breaks when you really need it!  \+          |] `shouldBe` "  You could explicitly escape indentation or\n \+                        \line breaks when you really need it!  "++    it "Fourth (escaping interpolation blocks to show them as text)" $+      [qns| {1+2} \{3+4} |] `shouldBe` "{1+2} \\{3+4}"++    it "Example of `qns` QuasiQuoter" $+      [qns| foo {1+2} |] `shouldBe` "foo {1+2}"++  it "Type annotation in interpolation block" $+    [qns|{10 :: Float}|] `shouldBe` "{10 :: Float}"++  it "Escaping interpolation symbols inside interpolation block" $ do+    [qns|foo {"b{a{r"} baz|] `shouldBe` "foo {\"b{a{r\"} baz"+    [qns|foo {"b\}a\}r"} baz|] `shouldBe` "foo {\"b\\}a\\}r\"} baz"++  it "Example from generated docs (double-space)" $+    [qns| foo {'b':'a':'r':""}+        \ baz |] `shouldBe` "foo {'b':'a':'r':\"\"}  baz"++  it "Escaping backslashes" $ do [qns| foo\bar |]    `shouldBe` "foo\\bar"+                                 [qns| foo\\bar |]   `shouldBe` "foo\\bar"+                                 [qns| foo\\\bar |]  `shouldBe` "foo\\\\bar"+                                 [qns| foo\\\\bar |] `shouldBe` "foo\\\\bar"++  it "Empty string" $ [qns|  |] `shouldBe` ""++  it "Escaping space by backslash at EOL after space (line break is cutted off)" $+    [qns| foo \+          bar |] `shouldBe` "foo bar"++  it "Escaped spaces at the edges" $ do [qns| foo\ |] `shouldBe` "foo "+                                        [qns|\ foo |] `shouldBe` " foo"++  describe "Tabs as indentation" $ do++    it "Tabs is only indentation at left side" $ do+			[qns|+				foo  bar  baz+			|] `shouldBe` "foo  bar  baz"++			[qns|			foo bar baz|] `shouldBe` "foo bar baz"++    it "Tabs also at EOL" $ do+			[qns|+				foo  bar  baz				+			|] `shouldBe` "foo  bar  baz"++			[qns|			foo bar baz				|] `shouldBe` "foo bar baz"++    it "Escaped tabs" $ do+      [qns|		\tfoo|]    `shouldBe` "\tfoo"+      [qns|		\	foo	|]   `shouldBe` "\tfoo"+      [qns|	foo		\	|]   `shouldBe` "foo\t\t\t"+      [qns|	foo	\		|]   `shouldBe` "foo\t\t"+      [qns|	foo\			|] `shouldBe` "foo\t"++  it "Tails" $ do+    [qns|    +           foo   +                 |] `shouldBe` "foo"+    [qns|	 +         	+          foo	 +               +             	+                 |] `shouldBe` "foo"+    [qns|				+            foo			+            				+            				|] `shouldBe` "foo"++  describe "New README examples" $ do++    it "Simple usage example" $ do+      [qns|+        <article>+          <h1>{title}</h1>+          <p>{text}</p>+        </article>+      |]+        `shouldBe`+          "<article> <h1>{title}</h1> <p>{text}</p> </article>"++    it "Interpolation" $ [qns| foo {1+2} |] `shouldBe` "foo {1+2}"++  it "Haddock example" $+    [qns| foo+          {'b':'a':'r':""}+          baz |] `shouldBe` "foo {'b':'a':'r':\"\"} baz"++  it "Escaping backslash itself when it makes sense" $ do+    [qns| foo\nbar  |] `shouldBe` "foo\nbar"+    [qns| foo\\nbar |] `shouldBe` "foo\\nbar"+    [qns| foo\tbar  |] `shouldBe` "foo\tbar"+    [qns| foo\\tbar |] `shouldBe` "foo\\tbar"+    [qns| foo\	bar |] `shouldBe` "foo\tbar"+    [qns| foo\\	bar |] `shouldBe` "foo\\\tbar"+    [qns| foo\ bar  |] `shouldBe` "foo bar"+    [qns| foo\\ bar |] `shouldBe` "foo\\ bar"++    [qns| foo\+          bar  |] `shouldBe` "foobar"+    [qns| foo\\+          bar  |] `shouldBe` "foo\\ bar"
test/Spec.hs view
@@ -3,111 +3,38 @@  module Main (main) where -import "hspec" Test.Hspec (hspec, describe, it, shouldBe)-import "qm-interpolated-string" Text.InterpolatedString.QM (qm, qn)+import "hspec" Test.Hspec (hspec, describe) +-- local imports+import qualified QM.Spec+import qualified QN.Spec+import qualified QMB.Spec+import qualified QNB.Spec+import qualified QMS.Spec+import qualified QNS.Spec -main :: IO ()-main = hspec $ do+-- Generated tests for CRLF line breaks+import qualified LineBreaks.CRLF.QM.Spec+import qualified LineBreaks.CRLF.QN.Spec+import qualified LineBreaks.CRLF.QMB.Spec+import qualified LineBreaks.CRLF.QNB.Spec+import qualified LineBreaks.CRLF.QMS.Spec+import qualified LineBreaks.CRLF.QNS.Spec -  describe "QM" $ do -    describe "Examples from README" $ do--      it "First (decorative spacing and escaping space symbol)" $-        [qm|   hello world,-             \ what's going on here?  |]-          `shouldBe` "hello world, what's going on here?"--      it "Second (merging lines without spaces)" $-        [qm|-              it's actual-              ly ignored-           |]-              `shouldBe` "it's actually ignored"--      it "Third (explicit line-breaks symbols)" $-        [qm|  \  You could explicitly escape indentation or\n-                 line-breaks when you really need it!  \-           |] `shouldBe` "  You could explicitly escape indentation or\n\-                         \line-breaks when you really need it!  "--      it "Fourth (escaping interpolation blocks to show them as text)" $-        [qm| {1+2} \{3+4} |] `shouldBe` "3 {3+4}"--    it "Type annotation in interpolation block" $-      [qm|{10 :: Float}|] `shouldBe` "10.0"--    it "Escaping interpolation symbols inside interpolation block" $ do-      [qm|foo {"b{a{r"} baz|]   `shouldBe` "foo b{a{r baz"-      [qm|foo {"b\}a\}r"} baz|] `shouldBe` "foo b}a}r baz"--    it "Example from generated docs" $ [qm| foo {'b':'a':'r':""}-                                          \ baz |] `shouldBe` "foo bar baz"--    it "Escaping backslashes" $ do [qm| foo\bar |]    `shouldBe` "foo\\bar"-                                   [qm| foo\\bar |]   `shouldBe` "foo\\bar"-                                   [qm| foo\\\bar |]  `shouldBe` "foo\\\\bar"-                                   [qm| foo\\\\bar |] `shouldBe` "foo\\\\bar"--    it "Empty string" $ [qm|  |] `shouldBe` ""--    it "Escaping space by slash at EOL after space" $-      [qm| foo \-           bar |] `shouldBe` "foo bar"--    it "Escaped spaces at the edges" $ do [qm| foo\ |] `shouldBe` "foo "-                                          [qm|\ foo |] `shouldBe` " foo"--  describe "QN (QM but without interpolation)" $ do--    describe "Examples from README" $ do--      it "First (decorative spacing and escaping space symbol)" $-        [qn|   hello world,-             \ what's going on here?  |]-          `shouldBe` "hello world, what's going on here?"--      it "Second (merging lines without spaces)" $-        [qn|-              it's actual-              ly ignored-           |]-              `shouldBe` "it's actually ignored"--      it "Third (explicit line-breaks symbols)" $-        [qn|  \  You could explicitly escape indentation or\n-                 line-breaks when you really need it!  \-           |] `shouldBe` "  You could explicitly escape indentation or\n\-                         \line-breaks when you really need it!  "--      it "Fourth (escaping interpolation blocks to show them as text)" $-        [qn| {1+2} \{3+4} |] `shouldBe` "{1+2} \\{3+4}"--      it "Example of `qn` QuasiQuoter" $-        [qn| foo {1+2} |] `shouldBe` "foo {1+2}"--    it "Type annotation in interpolation block" $-      [qn|{10 :: Float}|] `shouldBe` "{10 :: Float}"--    it "Escaping interpolation symbols inside interpolation block" $ do-      [qn|foo {"b{a{r"} baz|] `shouldBe` "foo {\"b{a{r\"} baz"-      [qn|foo {"b\}a\}r"} baz|] `shouldBe` "foo {\"b\\}a\\}r\"} baz"--    it "Example from generated docs" $-      [qn| foo {'b':'a':'r':""}-         \ baz |] `shouldBe` "foo {'b':'a':'r':\"\"} baz"--    it "Escaping backslashes" $ do [qn| foo\bar |]    `shouldBe` "foo\\bar"-                                   [qn| foo\\bar |]   `shouldBe` "foo\\bar"-                                   [qn| foo\\\bar |]  `shouldBe` "foo\\\\bar"-                                   [qn| foo\\\\bar |] `shouldBe` "foo\\\\bar"--    it "Empty string" $ [qn|  |] `shouldBe` ""--    it "Escaping space by slash at EOL after space" $-      [qn| foo \-           bar |] `shouldBe` "foo bar"+main :: IO ()+main = hspec $ do+  describe "QM" QM.Spec.spec+  describe "QN (QM but without interpolation)" QN.Spec.spec+  describe "QMB (interpolated string with line-*B*reaks)" QMB.Spec.spec+  describe "QNB (QMB but without interpolation)" QNB.Spec.spec+  describe "QMS (interpolated string with line breaks replaced with *S*paces)"+            QMS.Spec.spec+  describe "QNS (QMS but without interpolation)" QNS.Spec.spec -    it "Escaped spaces at the edges" $ do [qn| foo\ |] `shouldBe` "foo "-                                          [qn|\ foo |] `shouldBe` " foo"+  describe "QM (CRLF line breaks)"  LineBreaks.CRLF.QM.Spec.spec+  describe "QN (CRLF line breaks)"  LineBreaks.CRLF.QN.Spec.spec+  describe "QMB (CRLF line breaks)" LineBreaks.CRLF.QMB.Spec.spec+  describe "QNB (CRLF line breaks)" LineBreaks.CRLF.QNB.Spec.spec+  describe "QMS (CRLF line breaks)" LineBreaks.CRLF.QMS.Spec.spec+  describe "QNS (CRLF line breaks)" LineBreaks.CRLF.QNS.Spec.spec