diff --git a/library/NeatInterpolation.hs b/library/NeatInterpolation.hs
--- a/library/NeatInterpolation.hs
+++ b/library/NeatInterpolation.hs
@@ -1,127 +1,136 @@
--- |
--- NeatInterpolation provides a quasiquoter for producing strings
--- with a simple interpolation of input values.
--- It removes the excessive indentation from the input and
--- accurately manages the indentation of all lines of interpolated variables.
--- But enough words, the code shows it better.
---
--- Consider the following declaration:
---
--- > {-# LANGUAGE QuasiQuotes #-}
--- >
--- > import NeatInterpolation
--- > import Data.Text (Text)
--- >
--- > f :: Text -> Text -> Text
--- > f a b =
--- >   [text|
--- >     function(){
--- >       function(){
--- >         $a
--- >       }
--- >       return $b
--- >     }
--- >   |]
---
--- Executing the following:
---
--- > main = T.putStrLn $ f "1" "2"
---
--- will produce this (notice the reduced indentation compared to how it was
--- declared):
---
--- > function(){
--- >   function(){
--- >     1
--- >   }
--- >   return 2
--- > }
---
--- Now let's test it with multiline string parameters:
---
--- > main = T.putStrLn $ f
--- >   "{\n  indented line\n  indented line\n}"
--- >   "{\n  indented line\n  indented line\n}"
---
--- We get
---
--- > function(){
--- >   function(){
--- >     {
--- >       indented line
--- >       indented line
--- >     }
--- >   }
--- >   return {
--- >     indented line
--- >     indented line
--- >   }
--- > }
---
--- See how it neatly preserved the indentation levels of lines the
--- variable placeholders were at?
---
--- If you need to separate variable placeholder from the following text to
--- prevent treating the rest of line as variable name, use escaped variable:
---
--- > f name = [text|this_could_be_${name}_long_identifier|]
---
--- So
---
--- > f "one" == "this_could_be_one_long_identifier"
---
--- If you want to write something that looks like a variable but should be
--- inserted as-is, escape it with another @$@:
---
--- > f word = [text|$$my ${word} $${string}|]
---
--- results in
---
--- > f "funny" == "$my funny ${string}|]
-module NeatInterpolation (text) where
+{-|
+NeatInterpolation provides a quasiquoter for producing strings
+with a simple interpolation of input values.
+It removes the excessive indentation from the input and
+accurately manages the indentation of all lines of interpolated variables.
+But enough words, the code shows it better.
 
-import NeatInterpolation.Prelude
+Consider the following declaration:
 
+> {-# LANGUAGE QuasiQuotes #-}
+>
+> import NeatInterpolation
+> import Data.Text (Text)
+>
+> f :: Text -> Text -> Text
+> f a b =
+>   [trimming|
+>     function(){
+>       function(){
+>         $a
+>       }
+>       return $b
+>     }
+>   |]
+
+Executing the following:
+
+> main = Text.putStrLn $ f "1" "2"
+
+will produce this (notice the reduced indentation compared to how it was
+declared):
+
+> function(){
+>   function(){
+>     1
+>   }
+>   return 2
+> }
+
+Now let's test it with multiline string parameters:
+
+> main = Text.putStrLn $ f
+>   "{\n  indented line\n  indented line\n}"
+>   "{\n  indented line\n  indented line\n}"
+
+We get
+
+> function(){
+>   function(){
+>     {
+>       indented line
+>       indented line
+>     }
+>   }
+>   return {
+>     indented line
+>     indented line
+>   }
+> }
+
+See how it neatly preserved the indentation levels of lines the
+variable placeholders were at?
+
+If you need to separate variable placeholder from the following text to
+prevent treating the rest of line as variable name, use escaped variable:
+
+> f name = [trimming|this_could_be_${name}_long_identifier|]
+
+So
+
+> f "one" == "this_could_be_one_long_identifier"
+
+If you want to write something that looks like a variable but should be
+inserted as-is, escape it with another @$@:
+
+> f word = [trimming|$$my ${word} $${string}|]
+
+results in
+
+> f "funny" == "$my funny ${string}"
+-}
+module NeatInterpolation (trimming, untrimming) where
+
+import NeatInterpolation.Prelude
 import Language.Haskell.TH
 import Language.Haskell.TH.Quote hiding (quoteExp)
+import qualified Data.Text as Text
+import qualified NeatInterpolation.String as String
+import qualified NeatInterpolation.Parsing as Parsing
 
-import NeatInterpolation.String
-import NeatInterpolation.Parsing
 
-import Data.Text (Text)
-import qualified Data.Text as T
+expQQ quoteExp = QuasiQuoter quoteExp notSupported notSupported notSupported where
+  notSupported _ = fail "Quotation in this context is not supported"
 
+{-|
+Trimmed quasiquoter variation.
+Same as `untrimming`, but also
+removes the leading and trailing whitespace.
+-}
+trimming :: QuasiQuoter
+trimming = expQQ (quoteExp . String.trim . String.unindent . String.tabsToSpaces)
 
--- |
--- The quasiquoter.
-text :: QuasiQuoter
-text = QuasiQuoter quoteExp notSupported notSupported notSupported where
-  notSupported _ = fail "Quotation in this context is not supported"
+{-|
+Untrimmed quasiquoter variation.
+Unindents the quoted template and converts tabs to spaces.
+-}
+untrimming :: QuasiQuoter
+untrimming = expQQ (quoteExp . String.unindent . String.tabsToSpaces)
 
 indentQQPlaceholder :: Int -> Text -> Text
-indentQQPlaceholder indent text = case T.lines text of
-  head:tail -> T.intercalate (T.singleton '\n') $
-               head : map (T.replicate indent (T.singleton ' ') <>) tail
+indentQQPlaceholder indent text = case Text.lines text of
+  head:tail -> Text.intercalate (Text.singleton '\n') $
+               head : map (Text.replicate indent (Text.singleton ' ') <>) tail
   [] -> text
 
 quoteExp :: String -> Q Exp
 quoteExp input =
-  case parseLines $ normalizeQQInput input of
+  case Parsing.parseLines input of
     Left e -> fail $ show e
-    Right lines -> sigE (appE [|T.intercalate "\n"|] $ listE $ map lineExp lines)
+    Right lines -> sigE (appE [|Text.intercalate "\n"|] $ listE $ map lineExp lines)
                         [t|Text|]
 
-lineExp :: Line -> Q Exp
-lineExp (Line indent contents) =
+lineExp :: Parsing.Line -> Q Exp
+lineExp (Parsing.Line indent contents) =
   case contents of
-    []  -> [| T.empty |]
+    []  -> [| Text.empty |]
     [x] -> toExp x
-    xs  -> appE [|T.concat|] $ listE $ map toExp xs
+    xs  -> appE [|Text.concat|] $ listE $ map toExp xs
   where toExp = contentExp (fromIntegral indent)
 
-contentExp :: Integer -> LineContent -> Q Exp
-contentExp _ (LineContentText text) = appE [|T.pack|] (stringE text)
-contentExp indent (LineContentIdentifier name) = do
+contentExp :: Integer -> Parsing.LineContent -> Q Exp
+contentExp _ (Parsing.LineContentText text) = appE [|Text.pack|] (stringE text)
+contentExp indent (Parsing.LineContentIdentifier name) = do
   valueName <- lookupValueName name
   case valueName of
     Just valueName -> do
diff --git a/library/NeatInterpolation/Prelude.hs b/library/NeatInterpolation/Prelude.hs
--- a/library/NeatInterpolation/Prelude.hs
+++ b/library/NeatInterpolation/Prelude.hs
@@ -29,7 +29,6 @@
 import Data.Foldable as Exports hiding (toList)
 import Data.Function as Exports hiding (id, (.))
 import Data.Functor as Exports
-import Data.Functor.Contravariant as Exports
 import Data.Functor.Identity as Exports
 import Data.Int as Exports
 import Data.IORef as Exports
diff --git a/library/NeatInterpolation/String.hs b/library/NeatInterpolation/String.hs
--- a/library/NeatInterpolation/String.hs
+++ b/library/NeatInterpolation/String.hs
@@ -3,21 +3,18 @@
 import NeatInterpolation.Prelude
 
 
-normalizeQQInput :: [Char] -> [Char]
-normalizeQQInput = trim . unindent' . tabsToSpaces
-  where
-    unindent' :: [Char] -> [Char]
-    unindent' s =
-      case lines s of
-        head:tail -> 
-          let 
-            unindentedHead = dropWhile (== ' ') head 
-            minimumTailIndent = minimumIndent . unlines $ tail
-            unindentedTail = case minimumTailIndent of
-              Just indent -> map (drop indent) tail
-              Nothing -> tail
-          in unlines $ unindentedHead : unindentedTail
-        [] -> []
+unindent :: [Char] -> [Char]
+unindent s =
+  case lines s of
+    head : tail -> 
+      let 
+        unindentedHead = dropWhile (== ' ') head 
+        minimumTailIndent = minimumIndent . unlines $ tail
+        unindentedTail = case minimumTailIndent of
+          Just indent -> map (drop indent) tail
+          Nothing -> tail
+      in unlines $ unindentedHead : unindentedTail
+    [] -> []
 
 trim :: [Char] -> [Char]
 trim = dropWhileRev isSpace . dropWhile isSpace
@@ -25,15 +22,9 @@
 dropWhileRev :: (a -> Bool) -> [a] -> [a]
 dropWhileRev p = foldr (\x xs -> if p x && null xs then [] else x:xs) []
 
-unindent :: [Char] -> [Char]
-unindent s =
-  case minimumIndent s of
-    Just indent -> unlines . map (drop indent) . lines $ s
-    Nothing -> s
-
 tabsToSpaces :: [Char] -> [Char]
-tabsToSpaces ('\t':tail) = "    " ++ tabsToSpaces tail
-tabsToSpaces (head:tail) = head : tabsToSpaces tail
+tabsToSpaces ('\t' : tail) = "    " ++ tabsToSpaces tail
+tabsToSpaces (head : tail) = head : tabsToSpaces tail
 tabsToSpaces [] = []
 
 minimumIndent :: [Char] -> Maybe Int
diff --git a/neat-interpolation.cabal b/neat-interpolation.cabal
--- a/neat-interpolation.cabal
+++ b/neat-interpolation.cabal
@@ -1,5 +1,5 @@
 name: neat-interpolation
-version: 0.4
+version: 0.5
 synopsis: A quasiquoter for neat and simple multiline text interpolation
 description:
   A quasiquoter for producing Text values with support for
@@ -24,7 +24,7 @@
 
 library
   hs-source-dirs: library
-  default-extensions: BangPatterns, BinaryLiterals, BlockArguments, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFoldable, DeriveFunctor, DeriveGeneric, DeriveTraversable, DuplicateRecordFields, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, HexFloatLiterals, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, NumericUnderscores, OverloadedLists, OverloadedStrings, PatternGuards, PatternSynonyms, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, StrictData, TemplateHaskell, TupleSections, TypeApplications, TypeFamilies, TypeOperators, UnboxedTuples
+  default-extensions: BangPatterns, BinaryLiterals, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFoldable, DeriveFunctor, DeriveGeneric, DeriveTraversable, DuplicateRecordFields, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedLists, OverloadedStrings, PatternGuards, PatternSynonyms, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, StrictData, TemplateHaskell, TupleSections, TypeApplications, TypeFamilies, TypeOperators, UnboxedTuples
   default-language: Haskell2010
   exposed-modules:
     NeatInterpolation
@@ -41,7 +41,7 @@
 test-suite test
   type: exitcode-stdio-1.0
   hs-source-dirs: test
-  default-extensions: BangPatterns, BinaryLiterals, BlockArguments, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFoldable, DeriveFunctor, DeriveGeneric, DeriveTraversable, DuplicateRecordFields, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, HexFloatLiterals, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, NumericUnderscores, OverloadedLists, OverloadedStrings, PatternGuards, PatternSynonyms, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, StrictData, TemplateHaskell, TupleSections, TypeApplications, TypeFamilies, TypeOperators, UnboxedTuples
+  default-extensions: BangPatterns, BinaryLiterals, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFoldable, DeriveFunctor, DeriveGeneric, DeriveTraversable, DuplicateRecordFields, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedLists, OverloadedStrings, PatternGuards, PatternSynonyms, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, StrictData, TemplateHaskell, TupleSections, TypeApplications, TypeFamilies, TypeOperators, UnboxedTuples
   default-language: Haskell2010
   main-is: Main.hs
   build-depends:
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -15,7 +15,7 @@
   [
     testCase "Demo" $ let
       template a b = 
-        [text|
+        [trimming|
           function(){
             function(){
               $a
@@ -29,14 +29,14 @@
           (template a a)
     ,
     testCase "Isolation" $ let
-      isolated name = [text|this_could_be_${name}_long_identifier|]
+      isolated name = [trimming|this_could_be_${name}_long_identifier|]
       in assertEqual ""
           "this_could_be_one_long_identifier"
           (isolated "one")
     ,
     testCase "Escaping" $ let
       template a b = 
-        [text|
+        [trimming|
           function(){
             function(){
               $a
@@ -44,7 +44,7 @@
             return "$$b"
           }
         |]
-      escaped name = [text|this_could_be_$$${name}$$_long_identifier|]
+      escaped name = [trimming|this_could_be_$$${name}$$_long_identifier|]
       a = "{\n  indented line\n  indented line\n}"
       in do
         assertEqual ""
