packages feed

qm-interpolated-string 0.1.0.0 → 0.1.1.0

raw patch · 5 files changed

+209/−67 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Text.InterpolatedString.QM: qn :: QuasiQuoter

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+## 0.1.1.0++- Added `qn` QuasiQuoter as alternative to `qm` but without interpolation+- Some code refactoring+- More docs and tests
README.md view
@@ -3,10 +3,9 @@ [![Hackage](https://img.shields.io/hackage/v/qm-interpolated-string.svg)](https://hackage.haskell.org/package/qm-interpolated-string) [![Build Status](https://travis-ci.org/unclechu/haskell-qm-interpolated-string.svg?branch=master)](https://travis-ci.org/unclechu/haskell-qm-interpolated-string) -Implementation of interpolated multiline strings that ignores indentation-and trailing whitespaces.--It's [QuasiQuoter](https://wiki.haskell.org/Quasiquotation).+Implementation of interpolated multiline string+[QuasiQuoter](https://wiki.haskell.org/Quasiquotation)+that ignores indentation and trailing whitespaces.  Actually it's modification of [interpolatedstring-perl6](https://github.com/audreyt/interpolatedstring-perl6)@@ -14,13 +13,13 @@  This implementation looks just like `qc` from **interpolatedstring-perl6** package but ignores any indentation,-line-breaks (except explicitly written using `\n` char)+line breaks (except explicitly written using `\n` char) and trailing whitespaces.  'm' in 'qm' means 'multiline'.  You could write a decoratively formatted string and your-decorative indentation and line-breaks wont go to the string,+decorative indentation and line breaks wont go to the string, but when you really need it, you could just escape it using backslash.  ## Simple usage example@@ -61,6 +60,14 @@ ```haskell [qm| {1+2} \{3+4} |] -- Result: "3 {3+4}"+```++There is also very similar to `qm` QuasiQuoter+named as `qn` that do the same except interpolation:++```haskell+[qn| foo {1+2} |]+-- Result: "foo {1+2}" ```  ## Author
qm-interpolated-string.cabal view
@@ -1,5 +1,5 @@ name:               qm-interpolated-string-version:            0.1.0.0+version:            0.1.1.0 synopsis:           Implementation of interpolated multiline strings description:        Implementation of interpolated multiline strings                     that ignores indentation and trailing whitespaces@@ -11,7 +11,7 @@ maintainer:         Viacheslav Lotsmanov <lotsmanov89@gmail.com> category:           Data build-type:         Simple-extra-source-files: README.md+extra-source-files: README.md CHANGELOG.md cabal-version:      >=1.8 tested-with:        GHC == 7.6.3                   , GHC == 7.8.4
src/Text/InterpolatedString/QM.hs view
@@ -13,12 +13,12 @@  {-# LANGUAGE CPP #-} -module Text.InterpolatedString.QM (qm, ShowQ(..)) where+module Text.InterpolatedString.QM (qm, qn, ShowQ(..)) where -import "base" GHC.Exts (IsString(..))+import "base" GHC.Exts (IsString (fromString)) import qualified "template-haskell" Language.Haskell.TH as TH-import "template-haskell" Language.Haskell.TH.Quote-import "haskell-src-meta" Language.Haskell.Meta.Parse+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)@@ -67,49 +67,69 @@   unQM :: String -> String -> [StringPart]-unQM a []          = [Literal (reverse a)]+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 ("\\")      = 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 ""             = [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 ("\\")         = 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 str@((ifMaybe (`elem` "\t ") -> Just _) : _) = cutOff str-  where cutOff :: String -> Maybe String-        cutOff ""            = Just ""-        cutOff eof@('\n':_) = Just eof-        cutOff ((ifMaybe (`elem` "\t ") -> Just _) : xs) = cutOff xs-        cutOff _ = Nothing-clearIndentTillEOF _ = Nothing+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 ('\n' : xs) = if result /= xs-                                  then Just $ '\n' : cutOff xs-                                  else Nothing-  where cutOff :: String -> String-        cutOff ((ifMaybe (`elem` "\t ") -> Just _) : ys) = cutOff ys-        cutOff s = s-        result = cutOff xs-clearIndentAtSOF _ = Nothing+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 ((ifMaybe (`elem` "\t ") -> Just _) : xs) =-  clearIndentAtStart xs-clearIndentAtStart s = s+clearIndentAtStart s | s == ""             = ""+                     | head s `elem` "\t " = clearIndentAtStart $ tail s+                     | otherwise           = s   makeExpr :: [StringPart] -> TH.ExpQ@@ -119,20 +139,58 @@ 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+reify s = case parseExp s of+               Left  e -> TH.reportError e >> [| mempty |]+               Right e -> return e  +-- | QuasiQuoter for multiline interpolated string.+--+-- @+-- [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"@+--+--   * Space symbol at the edge+--     (to put it to the output instead of just ignoring it)+--     @[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}"@+-- 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')+  where f = makeExpr . parseQM "" . clearIndentAtStart . filter (/= '\r')  -ifMaybe :: (a -> Bool) -> a -> Maybe a-ifMaybe f x = if f x then Just x else Nothing+-- | Works just like `qm` but without interpolation+--   (just multiline string with decorative indentation).+--+-- @+-- [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 :: 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')
test/Spec.hs view
@@ -4,38 +4,110 @@ module Main (main) where  import "hspec" Test.Hspec (hspec, describe, it, shouldBe)-import "qm-interpolated-string" Text.InterpolatedString.QM (qm)+import "qm-interpolated-string" Text.InterpolatedString.QM (qm, qn)   main :: IO () main = hspec $ do -  describe "Examples from README" $ do+  describe "QM" $ 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?"+    describe "Examples from README" $ do -    it "Second (merging lines without spaces)" $-      [qm|-            it's actual-            ly ignored-         |]-            `shouldBe` "it's actually ignored"+      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 "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 "Second (merging lines without spaces)" $+        [qm|+              it's actual+              ly ignored+           |]+              `shouldBe` "it's actually ignored" -    it "Fourth (escaping interpolation blocks to show them as text)" $-      [qm| {1+2} \{3+4} |] `shouldBe` "3 {3+4}"+      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 "Type annotation in interpolation block" $-    [qm|{10 :: Float}|] `shouldBe` "10.0"+      it "Fourth (escaping interpolation blocks to show them as text)" $+        [qm| {1+2} \{3+4} |] `shouldBe` "3 {3+4}" -  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 "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"++    it "Escaped spaces at the edges" $ do [qn| foo\ |] `shouldBe` "foo "+                                          [qn|\ foo |] `shouldBe` " foo"