diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,9 +1,22 @@
 # doctemplates
 
-This is the templating system used by pandoc.  It was formerly
-a module in pandoc. It has been split off to make it easier
-to use independently.
+This is the text templating system used by pandoc.  Its basic
+function is to fill holes in a template in a "Context" that
+provides values for variables.  Control structures are provided
+to test that a variable has a non-blank value and to iterate
+over the items of a list.  Partials---that is, subtemplates
+defined in different files---are also supported.
 
+Templates may be rendered to lazy or strict `Text`, `String`,
+or a doclayout `Doc`.  (Using a `Doc` allows rendered documents
+to wrap flexibly on breaking spaces.)  A Context can be
+constructed manually or an aeson `Value` may be used.
+
+Unlike the various HTML-centered template engines,
+doctemplates is output-format agnostic, so no automatic escaping
+is done on interpolated values.  Values are assumed to be
+escaped properly in the Context.
+
 ## Example of use
 
 ``` haskell
@@ -74,24 +87,27 @@
 ${ foo }
 ```
 
-The values of variables are determined by a JSON object that is
+The values of variables are determined by the `Context` that is
 passed as a parameter to `renderTemplate`.  So, for example,
 `title` will return the value of the `title` field, and
 `employee.salary` will return the value of the `salary` field
 of the object that is the value of the `employee` field.
 
-- If the value of the variable is a JSON string, the string will
-  be rendered verbatim.  (Note that no escaping is done on the
-  string; the assumption is that the calling program will escape
+- If the value of the variable is simple value, it will be
+  rendered verbatim.  (Note that no escaping is done;
+  the assumption is that the calling program will escape
   the strings appropriately for the output format.)
-- If the value is a JSON array, the values will be concatenated.
-- If the value is a JSON object, the string `true` will be
-  rendered.
-- If the value is a JSON number, it will be rendered as an
+- If the value is a list, the values will be concatenated.
+- If the value is a map, the string `true` will be rendered.
+- Every other value will be rendered as the empty string.
+
+When a `Context` is derived from an aeson (JSON) `Value`,
+the following conversions are done:
+
+- If the value is a number, it will be rendered as an
   integer if possible, otherwise as a floating-point number.
 - If the value is a JSON boolean, it will be rendered as `true`
   if true, and as the empty string if false.
-- Every other value will be rendered as the empty string.
 
 The value of a variable that occurs by itself on a line
 will be indented to the same level as the opening delimiter of
@@ -133,9 +149,33 @@
 ${endif}
 ```
 
-Conditional keywords should not be indented, or unexpected spacing
-problems may occur.
+The keyword `elseif` may be used to simplify complex nested
+conditionals.  Thus
 
+```
+$if(foo)$
+XXX
+$elseif(bar)$
+YYY
+$else$
+ZZZ
+$endif$
+```
+
+is equivalent to
+
+```
+$if(foo)$
+XXX
+$else$
+$if(bar)$
+YYY
+$else$
+ZZZ
+$endif$
+$endif$
+```
+
 ## For loops
 
 A for loop begins with `for(variable)` (enclosed in
@@ -243,4 +283,40 @@
 The separator in this case is literal and (unlike with `sep`
 in an explicit `for` loop) cannot contain interpolated
 variables or other template directives.
+
+## Breakable spaces
+
+When rendering to a `Doc`, a distinction can be made between
+breakable and unbreakable spaces.  Normally, spaces in the
+template itself (as opposed to values of the interpolated
+variables) are not breakable, but they can be made breakable
+in part of the template by using the `+reflow` keyword (ended
+with `-reflow`).
+
+```
+${ +reflow }This long line may break if the document is rendered
+with a short line length.${ -reflow }
+```
+
+The `+` keyword has no effect when rendering to `Text`
+or `String`.
+
+## Nesting
+
+As noted above, the value of a variable that occurs by itself on
+a line will be indented to the same level as the opening
+delimiter of the variable.
+
+In addition, any part of a template can be marked explicitly for
+indented rendering, using the `+nest` keyword (o start nesting at
+the column where it appears) and `-nest` to stop nesting.
+
+Example:
+
+```
+$for(article)$
+- $+nest$$article.author$, "$article.title$," in $article.book$
+  ($article.year$).$-nest$
+$endfor$
+```
 
diff --git a/bench/bench.hs b/bench/bench.hs
--- a/bench/bench.hs
+++ b/bench/bench.hs
@@ -1,10 +1,12 @@
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE OverloadedStrings #-}
 import Text.DocTemplates
+import qualified Data.Text as T
 import Data.Text (Text)
 import Criterion.Main
 import Criterion.Types (Config (..))
 import Control.Monad.Identity
+import Data.Semigroup ((<>))
 import Data.Aeson (object, (.=), Value)
 
 main :: IO ()
@@ -21,7 +23,9 @@
    ]
 
 bigtext :: Text
-bigtext = "Hello there $foo$. This is a big text.\n$for(bar)$$bar.baz$$endfor$"
+bigtext = T.replicate 150 $
+  "Hello there $foo$. This is a big text.\n$for(bar)$$bar.baz$$endfor$\n"
+  <> "$if(foo)$Hi $foo$.$endif$\n"
 
 val :: Value
 val = object [ "foo" .= (22 :: Int)
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,25 @@
 # doctemplates
 
+## 0.6
+
+  * Add `+nest`/`-nest` keywords.
+
+  * Add `+reflow`/`-reflow` keywords.
+
+  * Add Nested constructor to Template, remove Indented
+    and Indented parameter for Interpolate.
+
+  * More expansive description of library.
+
+## 0.5.1
+
+  * Add elseif keyword.
+
+  * Improve compile error source locations with partials.
+
+  * Handle templates that don't end in newlines.  Previously
+    this caused problems in some cases.
+
 ## 0.5
 
   * Add toText method to TemplateTarget class.
diff --git a/doctemplates.cabal b/doctemplates.cabal
--- a/doctemplates.cabal
+++ b/doctemplates.cabal
@@ -1,7 +1,13 @@
 name:                doctemplates
-version:             0.5
+version:             0.6
 synopsis:            Pandoc-style document templates
-description:         A simple text templating system used by pandoc.
+description:         This is the text templating system used by pandoc.
+                     It supports variable interpolation, iteration,
+                     tests for non-blank values, and partials.  Templates
+                     may be rendered to a variety of formats, including
+                     doclayout Docs, and variable values may come from
+                     a variety of different sources, including aeson
+                     Values.
 homepage:            https://github.com/jgm/doctemplates#readme
 license:             BSD3
 license-file:        LICENSE
@@ -46,6 +52,7 @@
   main-is:             test.hs
   build-depends:       base,
                        doctemplates,
+                       doclayout >= 0.1 && < 0.2,
                        aeson,
                        Glob,
                        tasty,
diff --git a/src/Text/DocTemplates.hs b/src/Text/DocTemplates.hs
--- a/src/Text/DocTemplates.hs
+++ b/src/Text/DocTemplates.hs
@@ -7,8 +7,21 @@
    Stability   : alpha
    Portability : portable
 
-The text templating system used by pandoc.
+This is the text templating system used by pandoc. Its basic function is
+to fill holes in a template in a 'Context' that provides values for
+variables. Control structures are provided to test that a variable has a
+non-blank value and to iterate over the items of a list. Partials—that
+is, subtemplates defined in different files—are also supported.
 
+Templates may be rendered to lazy or strict 'Text', 'String', or a
+doclayout 'Doc'. (Using a 'Doc' allows rendered documents to wrap
+flexibly on breaking spaces.) A 'Context' can be constructed manually or
+an aeson 'Value' may be used.
+
+Unlike the various HTML-centered template engines, doctemplates is
+output-format agnostic, so no automatic escaping is done on interpolated
+values. Values are assumed to be escaped properly in the Context.
+
 == Example of use
 
 > {-# LANGUAGE OverloadedStrings #-}
@@ -73,27 +86,30 @@
 > ${foo_bar.baz-bim}
 > ${ foo }
 
-The values of variables are determined by a JSON object that is passed
-as a parameter to @renderTemplate@. So, for example, @title@ will return
+The values of variables are determined by the 'Context' that is passed
+as a parameter to 'renderTemplate'. So, for example, @title@ will return
 the value of the @title@ field, and @employee.salary@ will return the
 value of the @salary@ field of the object that is the value of the
 @employee@ field.
 
--   If the value of the variable is a JSON string, the string will be
-    rendered verbatim. (Note that no escaping is done on the string; the
-    assumption is that the calling program will escape the strings
-    appropriately for the output format.)
--   If the value is a JSON array, the values will be concatenated.
--   If the value is a JSON object, the string @true@ will be rendered.
--   If the value is a JSON number, it will be rendered as an integer if
+-   If the value of the variable is simple value, it will be rendered
+    verbatim. (Note that no escaping is done; the assumption is that the
+    calling program will escape the strings appropriately for the output
+    format.)
+-   If the value is a list, the values will be concatenated.
+-   If the value is a map, the string @true@ will be rendered.
+-   Every other value will be rendered as the empty string.
+
+When a 'Context' is derived from an aeson (JSON) 'Value', the following
+conversions are done:
+
+-   If the value is a number, it will be rendered as an integer if
     possible, otherwise as a floating-point number.
 -   If the value is a JSON boolean, it will be rendered as @true@ if
     true, and as the empty string if false.
--   Every other value will be rendered as the empty string.
 
-The value of a variable that occurs by itself on a line
-will be indented to the same level as the opening delimiter of
-the variable.
+The value of a variable that occurs by itself on a line will be indented
+to the same level as the opening delimiter of the variable.
 
 == Conditionals
 
@@ -128,9 +144,29 @@
 > no foo!
 > ${endif}
 
-Conditional keywords should not be indented, or unexpected spacing
-problems may occur.
+The keyword @elseif@ may be used to simplify complex nested
+conditionals. Thus
 
+> $if(foo)$
+> XXX
+> $elseif(bar)$
+> YYY
+> $else$
+> ZZZ
+> $endif$
+
+is equivalent to
+
+> $if(foo)$
+> XXX
+> $else$
+> $if(bar)$
+> YYY
+> $else$
+> ZZZ
+> $endif$
+> $endif$
+
 == For loops
 
 A for loop begins with @for(variable)@ (enclosed in matched delimiters)
@@ -197,10 +233,9 @@
 > ${ it:bibentry() }
 > ${ endfor }
 
-Note that the anaphoric keyword @it@ must be used when
-iterating over partials.  In the above examples,
-the @bibentry@ partial should contain @it.title@
-(and so on) instead of @articles.title@.
+Note that the anaphoric keyword @it@ must be used when iterating over
+partials. In the above examples, the @bibentry@ partial should contain
+@it.title@ (and so on) instead of @articles.title@.
 
 Final newlines are omitted from included partials.
 
@@ -219,6 +254,36 @@
 explicit @for@ loop) cannot contain interpolated variables or other
 template directives.
 
+== Breakable spaces
+
+When rendering to a @Doc@, a distinction can be made between breakable
+and unbreakable spaces. Normally, spaces in the template itself (as
+opposed to values of the interpolated variables) are not breakable, but
+they can be made breakable in part of the template by using the
+@+reflow@ keyword (ended with @-reflow@).
+
+> ${ +reflow }This long line may break if the document is rendered
+> with a short line length.${ -reflow }
+
+The @+@ keyword has no effect when rendering to @Text@ or @String@.
+
+== Nesting
+
+As noted above, the value of a variable that occurs by itself on a line
+will be indented to the same level as the opening delimiter of the
+variable.
+
+In addition, any part of a template can be marked explicitly for
+indented rendering, using the @+nest@ keyword (o start nesting at the
+column where it appears) and @-nest@ to stop nesting.
+
+Example:
+
+> $for(article)$
+> - $+nest$$article.author$, "$article.title$," in $article.book$
+>   ($article.year$).$-nest$
+> $endfor$
+
 -}
 
 module Text.DocTemplates ( renderTemplate
@@ -238,8 +303,8 @@
 import Data.Text (Text)
 import Text.DocTemplates.Parser (compileTemplate)
 import Text.DocTemplates.Internal ( TemplateMonad(..), Context(..),
-            Val(..), ToContext(..), FromContext(..), TemplateTarget(..), Template,
-            renderTemplate )
+            Val(..), ToContext(..), FromContext(..), TemplateTarget(..),
+            Template, renderTemplate )
 
 -- | Compile a template from a file.  IO errors will be
 -- raised as exceptions; template parsing errors result in
diff --git a/src/Text/DocTemplates/Internal.hs b/src/Text/DocTemplates/Internal.hs
--- a/src/Text/DocTemplates/Internal.hs
+++ b/src/Text/DocTemplates/Internal.hs
@@ -32,7 +32,6 @@
       , TemplateTarget(..)
       , Template(..)
       , Variable(..)
-      , Indented(..)
       ) where
 
 import Safe (lastMay, initDef)
@@ -57,19 +56,16 @@
 import Data.Semigroup
 #endif
 
--- | Determines whether an interpolated variable is rendered with
--- indentation.
-data Indented = Indented !Int | Unindented
-     deriving (Show, Read, Data, Typeable, Generic, Eq, Ord)
-
 -- | A template.
 data Template =
-       Interpolate Indented Variable
+       Interpolate Variable
      | Conditional Variable Template Template
      | Iterate Variable Template Template
+     | Nested Int Template
      | Partial Template
      | Literal Text
      | Concat Template Template
+     | BreakingSpace
      | Empty
      deriving (Show, Read, Data, Typeable, Generic, Eq, Ord)
 
@@ -100,6 +96,7 @@
   removeFinalNewline :: a -> a
   isEmpty            :: a -> Bool
   indent             :: Int -> a -> a
+  breakingSpace      :: a
 
 instance TemplateTarget Text where
   fromText   = id
@@ -111,6 +108,7 @@
   isEmpty    = T.null
   indent 0   = id
   indent ind = T.intercalate ("\n" <> T.replicate ind " ") . T.lines
+  breakingSpace = " "
 
 instance TemplateTarget TL.Text where
   fromText   = TL.fromStrict
@@ -123,6 +121,7 @@
   indent 0   = id
   indent ind = TL.intercalate ("\n" <> TL.replicate (fromIntegral ind) " ")
                . TL.lines
+  breakingSpace = " "
 
 instance TemplateTarget String where
   fromText   = T.unpack
@@ -134,8 +133,10 @@
   isEmpty    = null
   indent 0   = id
   indent ind = intercalate ("\n" <> replicate ind ' ') . lines
+  breakingSpace = " "
 
-instance (DL.HasChars a, IsString a) => TemplateTarget (DL.Doc a) where
+instance (DL.HasChars a, IsString a, Eq a)
+    => TemplateTarget (DL.Doc a) where
   fromText = DL.text . T.unpack
   toText   = T.pack . DL.foldrChar (:) [] . DL.render Nothing
   removeFinalNewline = DL.chomp
@@ -144,7 +145,7 @@
   isEmpty (DL.Text 0 _)   = True
   isEmpty (DL.Concat x y) = isEmpty x && isEmpty y
   isEmpty _               = False
-
+  breakingSpace  = DL.space
 
 -- | A 'Context' defines values for template's variables.
 newtype Context a = Context { unContext :: M.Map Text (Val a) }
@@ -258,8 +259,8 @@
 
 multiLookup :: [Text] -> Val a -> Val a
 multiLookup [] x = x
-multiLookup (v:vs) (MapVal (Context o)) =
-  case M.lookup v o of
+multiLookup (t:vs) (MapVal (Context o)) =
+  case M.lookup t o of
     Nothing -> NullVal
     Just v' -> multiLookup vs v'
 multiLookup _ _ = NullVal
@@ -295,13 +296,8 @@
 renderTemp :: forall a . TemplateTarget a
            => Template -> Context a -> a
 renderTemp (Literal t) _ = fromText t
-renderTemp (Interpolate indented v) ctx =
-  let vals = resolveVariable v ctx
-   in if null vals
-         then mempty
-         else case indented of
-                Indented ind -> indent ind $ mconcat vals
-                _            -> mconcat vals
+renderTemp BreakingSpace _ = breakingSpace
+renderTemp (Interpolate v) ctx = mconcat $ resolveVariable v ctx
 renderTemp (Conditional v ift elset) ctx =
   let res = resolveVariable v ctx
    in case res of
@@ -310,6 +306,7 @@
 renderTemp (Iterate v t sep) ctx =
   let sep' = renderTemp sep ctx
    in mconcat . intersperse sep' $ withVariable v ctx (renderTemp t)
+renderTemp (Nested n t) ctx = indent n $ renderTemp t ctx
 renderTemp (Partial t) ctx = renderTemp t ctx
 renderTemp (Concat t1 t2) ctx =
   mappend (renderTemp t1 ctx) (renderTemp t2 ctx)
diff --git a/src/Text/DocTemplates/Parser.hs b/src/Text/DocTemplates/Parser.hs
--- a/src/Text/DocTemplates/Parser.hs
+++ b/src/Text/DocTemplates/Parser.hs
@@ -38,7 +38,9 @@
   res <- P.runParserT (pTemplate <* P.eof)
            PState{ templatePath   = templPath
                  , partialNesting = 1
-                 , beginsLine = True } "template" template
+                 , indentLevel = 0
+                 , beginsLine = True
+                 , breakingSpaces = False } templPath template
   case res of
        Left e   -> return $ Left $ show e
        Right x  -> return $ Right x
@@ -47,28 +49,58 @@
 data PState =
   PState { templatePath   :: FilePath
          , partialNesting :: Int
-         , beginsLine     :: Bool }
+         , indentLevel    :: Int
+         , beginsLine     :: Bool
+         , breakingSpaces :: Bool }
 
 type Parser = P.ParsecT Text PState
 
 pTemplate :: TemplateMonad m => Parser m Template
 pTemplate = do
-  ts <- many $ P.try
-         (P.skipMany pComment *> (pLit <|> pDirective <|> pEscape))
   P.skipMany pComment
-  return $ mconcat ts
+  mconcat <$> many
+    ((pLit <|> pNewline <|> pDirective <|> pEscape) <* P.skipMany pComment)
 
+pNewline :: Monad m => Parser m Template
+pNewline = do
+  nls <- P.string "\n" <|> P.string "\r" <|> P.string "\r\n"
+  breakspaces <- breakingSpaces <$> P.getState
+  return $
+   if breakspaces
+      then BreakingSpace
+      else Literal $ fromString nls
+
 pLit :: Monad m => Parser m Template
 pLit = do
-  cs <- mconcat <$> P.many1 (P.many1 (P.satisfy (/= '$')))
-  P.updateState $ \st ->
-    st{ beginsLine =
-          case dropWhile (\c -> c == ' ' || c == '\t') $ reverse cs of
-            ('\n':_) -> True
-            []       -> beginsLine st
-            _        -> False }
-  return $ Literal $ fromString cs
+  col <- P.sourceColumn <$> P.getPosition
+  ind <- indentLevel <$> P.getState
+  -- eat up indentlevel spaces
+  P.skipMany $ do
+    P.getPosition >>= guard . (< (ind - 1)) . P.sourceColumn
+    P.satisfy (\c -> c == ' ' || c == '\t')
+  cs <- P.many1 (P.satisfy (\c -> c /= '$' && c /= '\n' && c /= '\r'))
+  P.updateState $ \st -> st{ beginsLine = col == 1 && all (==' ') cs }
+  breakspaces <- breakingSpaces <$> P.getState
+  if breakspaces
+     then return $ toBreakable cs
+     else return $ Literal $ fromString cs
 
+toBreakable :: String -> Template
+toBreakable [] = Empty
+toBreakable xs =
+  case break isSpacy xs of
+    ([], []) -> Empty
+    ([], zs) -> BreakingSpace <> toBreakable (dropWhile isSpacy zs)
+    (ys, []) -> Literal (fromString ys)
+    (ys, zs) -> Literal (fromString ys) <> toBreakable zs
+
+isSpacy :: Char -> Bool
+isSpacy ' '  = True
+isSpacy '\n' = True
+isSpacy '\r' = True
+isSpacy '\t' = True
+isSpacy _    = False
+
 backupSourcePos :: Monad m => Int -> Parser m ()
 backupSourcePos n = do
   pos <- P.getPosition
@@ -80,7 +112,8 @@
 pDirective :: TemplateMonad m
            => Parser m Template
 pDirective = do
-  res <- pConditional <|> pForLoop <|> pInterpolate <|> pBarePartial
+  res <- pConditional <|> pForLoop <|> pReflow <|> pNest <|>
+         pInterpolate <|> pBarePartial
   col <- P.sourceColumn <$> P.getPosition
   P.updateState $ \st -> st{ beginsLine = col == 1 }
   return res
@@ -108,19 +141,50 @@
   -- if newline after the "if", then a newline after "endif" will be swallowed
   multiline <- P.option False (True <$ skipEndline)
   ifContents <- pTemplate
-  elseContents <- P.option mempty $
-                    do pEnclosed (P.string "else")
-                       when multiline $ P.option () skipEndline
-                       pTemplate
+  elseContents <- P.option mempty (pElse multiline <|> pElseIf)
   pEnclosed (P.string "endif")
   when multiline $ P.option () skipEndline
   return $ Conditional v ifContents elseContents
 
+pElse :: TemplateMonad m => Bool -> Parser m Template
+pElse multiline = do
+  pEnclosed (P.string "else")
+  when multiline $ P.option () skipEndline
+  pTemplate
+
+pElseIf :: TemplateMonad m => Parser m Template
+pElseIf = do
+  v <- pEnclosed $ P.try $ P.string "elseif" *> pParens pVar
+  multiline <- P.option False (True <$ skipEndline)
+  ifContents <- pTemplate
+  elseContents <- P.option mempty (pElse multiline <|> pElseIf)
+  return $ Conditional v ifContents elseContents
+
 skipEndline :: Monad m => Parser m ()
-skipEndline = P.try $ P.skipMany pSpaceOrTab <* P.char '\n'
+skipEndline = P.try $
+      (P.skipMany pSpaceOrTab <* P.char '\n')
+  <|> (P.skipMany1 pSpaceOrTab <* P.eof)
 
-pForLoop :: TemplateMonad m
-         => Parser m Template
+pNest :: TemplateMonad m => Parser m Template
+pNest = do
+  col <- P.sourceColumn <$> P.getPosition
+  pEnclosed $ P.string "+nest"
+  P.updateState $ \st -> st{ indentLevel = col - 1 }
+  t <- pTemplate
+  P.optional $ pEnclosed $ P.string "-nest"
+  return $ Nested (col - 1) t
+
+pReflow :: TemplateMonad m => Parser m Template
+pReflow = mempty <$ (pReflowOn <|> pReflowOff)
+ where
+  pReflowOn = do
+    pEnclosed $ P.string "+reflow"
+    P.modifyState $ \st -> st{ breakingSpaces = True }
+  pReflowOff = do
+    pEnclosed $ P.string "-reflow"
+    P.modifyState $ \st -> st{ breakingSpaces = False }
+
+pForLoop :: TemplateMonad m => Parser m Template
 pForLoop = do
   v <- pEnclosed $ P.try $ P.string "for" *> pParens pVar
   -- if newline after the "for", then a newline after "endfor" will be swallowed
@@ -137,15 +201,15 @@
 changeToIt :: Variable -> Template -> Template
 changeToIt v = go
  where
-  go (Interpolate i w) = Interpolate i (reletter v w)
+  go (Interpolate w) = Interpolate (reletter v w)
   go (Conditional w t1 t2) = Conditional (reletter v w)
         (changeToIt v t1) (changeToIt v t2)
   go (Iterate w t1 t2) = Iterate (reletter v w)
         (changeToIt v t1) (changeToIt v t2)
-  go (Partial t) = Partial t  -- don't reletter inside partial
-  go (Literal x) = Literal x
   go (Concat t1 t2) = changeToIt v t1 <> changeToIt v t2
-  go Empty = mempty
+  go (Partial t) = Partial t  -- don't reletter inside partial
+  go (Nested n t) = Nested n (go t)
+  go x = x
   reletter (Variable vs) (Variable ws) =
     if vs `isPrefixOf` ws
        then Variable ("it" : drop (length vs) ws)
@@ -156,30 +220,56 @@
 pInterpolate = do
   begins <- beginsLine <$> P.getState
   pos <- P.getPosition
-  res <- pEnclosed $ do
-    var <- pVar
-    (P.char ':' *> pPartial (Just var))
-      <|> Iterate var (Interpolate Unindented (Variable ["it"])) <$> pSep
-      <|> return (Interpolate Unindented var)
+  -- we don't used pEnclosed here, to get better error messages:
+  (closer, var) <- P.try $ do
+    cl <- pOpen
+    P.skipMany pSpaceOrTab
+    v <- pVar
+    P.notFollowedBy (P.char '(') -- bare partial
+    return (cl, v)
+  res <- (P.char ':' *> (pPartialName >>= pPartial (Just var)))
+      <|> Iterate var (Interpolate (Variable ["it"])) <$> pSep
+      <|> return (Interpolate var)
+  P.skipMany pSpaceOrTab
+  closer
   ends <- P.lookAhead $ P.option False $
-             True <$ P.try (P.skipMany pSpaceOrTab *> P.newline)
+             True <$ P.try (P.skipMany pSpaceOrTab *> pNewlineOrEof)
+  let toNested = case P.sourceColumn pos - 1 of
+                   0 -> id
+                   i -> Nested i
   case (begins && ends, res) of
-    (True, Interpolate _ v)
-               -> return $ Interpolate (Indented (P.sourceColumn pos - 1)) v
-    (True, Iterate v (Interpolate _ v') s)
-               -> return $ Iterate v
-                    (Interpolate (Indented (P.sourceColumn pos - 1)) v') s
+    (True, Interpolate v)
+               -> return $ toNested $ Interpolate v
+    (True, Iterate v (Interpolate v') s)
+               -> return $ toNested $ Iterate v (Interpolate v') s
     _ -> return res
 
+pNewlineOrEof :: Monad m => Parser m ()
+pNewlineOrEof = () <$ P.newline <|> P.eof
+
 pBarePartial :: TemplateMonad m
              => Parser m Template
-pBarePartial = pEnclosed $ pPartial Nothing
+pBarePartial = do
+  (closer, fp) <- P.try $ do
+    closer <- pOpen
+    P.skipMany pSpaceOrTab
+    fp <- pPartialName
+    return (closer, fp)
+  res <- pPartial Nothing fp
+  P.skipMany pSpaceOrTab
+  closer
+  return res
 
-pPartial :: TemplateMonad m
-         => Maybe Variable -> Parser m Template
-pPartial mbvar = do
+pPartialName :: TemplateMonad m
+             => Parser m FilePath
+pPartialName = P.try $ do
   fp <- P.many1 (P.alphaNum <|> P.oneOf ['_','-','.','/','\\'])
   P.string "()"
+  return fp
+
+pPartial :: TemplateMonad m
+         => Maybe Variable -> FilePath -> Parser m Template
+pPartial mbvar fp = do
   separ <- P.option mempty pSep
   tp <- templatePath <$> P.getState
   let fp' = case takeExtension fp of
@@ -192,7 +282,7 @@
           else do
             oldInput <- P.getInput
             oldPos <- P.getPosition
-            P.setPosition $ P.initialPos fp
+            P.setPosition $ P.initialPos fp'
             P.setInput partial
             P.updateState $ \st -> st{ partialNesting = nesting + 1 }
             res' <- pTemplate <* P.eof
@@ -222,7 +312,7 @@
   -- If the comment begins in the first column, the line ending
   -- will be consumed; otherwise not.
   when (P.sourceColumn pos == 1) $ () <$ do
-    P.char '\n'
+    pNewlineOrEof
     P.updateState $ \st -> st{ beginsLine = True }
 
 pOpenDollar :: Monad m => Parser m (Parser m ())
@@ -243,10 +333,13 @@
 
 pVar :: Monad m => Parser m Variable
 pVar = do
-  first <- pIdentPart <|> "it" <$ P.try (P.string "it")
-  rest <- P.many $ P.char '.' *> pIdentPart
+  first <- pIdentPart <|> pIt
+  rest <- P.many $ (P.char '.' *> pIdentPart)
   return $ Variable (first:rest)
 
+pIt :: Monad m => Parser m Text
+pIt = fromString <$> P.try (P.string "it")
+
 pIdentPart :: Monad m => Parser m Text
 pIdentPart = P.try $ do
   first <- P.letter
@@ -256,4 +349,4 @@
   return $ fromString part
 
 reservedWords :: [String]
-reservedWords = ["else","endif","for","endfor","sep","it"]
+reservedWords = ["if","else","endif","elseif","for","endfor","sep","it"]
diff --git a/test/bad.txt b/test/bad.txt
new file mode 100644
--- /dev/null
+++ b/test/bad.txt
@@ -0,0 +1,2 @@
+partial
+$with syntax error
diff --git a/test/elseif.test b/test/elseif.test
new file mode 100644
--- /dev/null
+++ b/test/elseif.test
@@ -0,0 +1,30 @@
+{ "foo": 1,
+  "bar": null,
+  "baz": ["a", "b"],
+  "bim": { "zub": "sim" },
+  "sup": [ { "biz": "qux" }
+         , { "sax": "" }
+         ]
+}
+.
+$if(sup.sax)$
+XXX
+$elseif(baz)$
+YYY
+$else$
+ZZZ
+$endif$
+
+$if(sup.sax)$
+XXX
+$elseif(baz.nonexist)$
+YYY
+$elseif(sup.sax)$
+ZZZ
+$else$
+WWW
+$endif$
+.
+YYY
+
+WWW
diff --git a/test/partials.test b/test/partials.test
--- a/test/partials.test
+++ b/test/partials.test
@@ -13,6 +13,8 @@
 
 $employee:name()[, ]$
 
+$employee:name()$
+
 $employee:name.tex()[; ]$
 
 $boilerplate()$
@@ -22,6 +24,8 @@
 (Sara) Chen
 
 (John) Doe, (Omar) Smith, (Sara) Chen
+
+(John) Doe(Omar) Smith(Sara) Chen
 
 \name{John}{Doe}; \name{Omar}{Smith}; \name{Sara}{Chen}
 
diff --git a/test/test.hs b/test/test.hs
--- a/test/test.hs
+++ b/test/test.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 
+import Text.DocLayout (render)
 import Text.DocTemplates
 import Test.Tasty.Golden
 import Test.Tasty
@@ -28,12 +29,36 @@
 unitTests :: [TestTree]
 unitTests = [
     testCase "compile failure" $ do
-      (res :: Either String Template)
-        <- compileTemplate "" "$if(x$and$endif$"
-      res @?= Left "\"template\" (line 1, column 6):\nunexpected \"$\"\nexpecting \".\" or \")\""
+      res <- compileTemplate "" "$if(x$and$endif$"
+      res @?= Left "(line 1, column 6):\nunexpected \"$\"\nexpecting \".\" or \")\""
   , testCase "compile failure (keyword as variable)" $ do
-      (res :: Either String Template) <- compileTemplate "" "$sep$"
-      res @?= Left "\"template\" (line 1, column 5):\nunexpected \"$\"\nexpecting letter or digit or \"()\""
+      res <- compileTemplate "foobar.txt" "$sep$"
+      res @?= Left "\"foobar.txt\" (line 1, column 5):\nunexpected \"$\"\nexpecting letter or digit or \"()\""
+  , testCase "compile failure (error in partial)" $ do
+      res <- compileTemplate "test/foobar.txt" "$bad()$"
+      res @?= Left "\"test/bad.txt\" (line 2, column 7):\nunexpected \"s\"\nexpecting \"$\""
+  , testCase "comment with no newline" $ do
+      res <- compileTemplate "foo" "$-- hi"
+      res @?= Right mempty
+  , testCase "reflow" $ do
+      templ <- compileTemplate "foo" "not breakable and $+reflow$ this is breakable\nok? $foo$$-reflow$"
+      let res :: T.Text
+          res = case templ of
+                  Right t -> render (Just 10)
+                   (renderTemplate t (object ["foo" .= ("42" :: T.Text)]))
+                  Left e  -> T.pack e
+      res @?= "not breakable and \nthis is\nbreakable\nok? 42"
+  , testCase "nest" $ do
+      res <- applyTemplate "foo" "- $+nest$aaa\nbbb$-nest$" (object [])
+      res @?= Right ("- aaa\n  bbb" :: T.Text)
+  , testCase "implicitly closed nest" $ do
+      templ <- compileTemplate "foo" "$if(foo)$\n- $+nest$a\nb$endif$"
+      let res :: T.Text
+          res = case templ of
+                  Right t -> render (Just 10)
+                   (renderTemplate t (object ["foo" .= ("42" :: T.Text)]))
+                  Left e  -> T.pack e
+      res @?= "- a\n  b"
   ]
 
 {- The test "golden" files are structured as follows:
