diff --git a/cli/GingerCLI.hs b/cli/GingerCLI.hs
--- a/cli/GingerCLI.hs
+++ b/cli/GingerCLI.hs
@@ -74,29 +74,20 @@
 
     case tpl of
         Left err -> do
-            printParserError err
             tplSource <- case src of
-                            Just s -> return s
+                            Just s -> return (Just s)
                             Nothing -> do
                                 let s = peSourceName err
                                 case s of
-                                    Nothing -> return ""
-                                    Just sn -> loadFile sn
-            displayParserError tplSource err
+                                    Nothing -> return Nothing
+                                    Just sn -> Just <$> loadFile sn
+            printParserError tplSource err
         Right t -> do
             let context = makeContextHtmlM contextLookup (putStr . Text.unpack . htmlSource)
             runGingerT context t >>= hPutStrLn stderr . show
 
-printParserError :: ParserError -> IO ()
-printParserError = putStrLn . formatParserError
-
-formatParserError :: ParserError -> String
-formatParserError pe = Prelude.concat
-    [ fromMaybe "<<unknown source>>" . peSourceName $ pe
-    , ":"
-    , fromMaybe "" $ (++ ":") . show <$> peSourceLine pe
-    , fromMaybe "" $ (++ ":") . show <$> peSourceColumn pe
-    , peErrorMessage pe ]
+printParserError :: Maybe String -> ParserError -> IO ()
+printParserError srcMay = putStrLn . formatParserError srcMay
 
 displayParserError :: String -> ParserError -> IO ()
 displayParserError src pe = do
diff --git a/ginger.cabal b/ginger.cabal
--- a/ginger.cabal
+++ b/ginger.cabal
@@ -2,7 +2,7 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                ginger
-version:             0.5.0.0
+version:             0.5.1.0
 synopsis:            An implementation of the Jinja2 template language in Haskell
 description:         Ginger is Jinja, minus the most blatant pythonisms. Wants
                      to be feature complete, but isn't quite there yet.
diff --git a/src/Text/Ginger/Parse.hs b/src/Text/Ginger/Parse.hs
--- a/src/Text/Ginger/Parse.hs
+++ b/src/Text/Ginger/Parse.hs
@@ -7,6 +7,7 @@
 ( parseGinger
 , parseGingerFile
 , ParserError (..)
+, formatParserError
 , IncludeResolver
 , Source, SourceName
 )
@@ -46,7 +47,7 @@
 import Safe ( readMay )
 
 import Data.Text (Text)
-import Data.Maybe ( fromMaybe )
+import Data.Maybe ( fromMaybe, catMaybes, listToMaybe )
 import Data.Scientific ( Scientific )
 import qualified Data.Text as Text
 import Data.List ( foldr, nub, sort )
@@ -57,6 +58,7 @@
 import Data.Char (isSpace)
 
 import System.FilePath ( takeDirectory, (</>) )
+import Text.Printf ( printf )
 
 -- | Input type for the parser (source code).
 type Source = String
@@ -82,6 +84,30 @@
         deriving (Show, Generic)
 
 instance Exception ParserError where
+
+-- | 
+formatParserError :: Maybe String -> ParserError -> String
+formatParserError tplSrc e =
+    let sourceLocation = do
+            printf "%s:%i:%i\n"
+                <$> peSourceName e
+                <*> peSourceLine e
+                <*> peSourceColumn e
+        markerLines = do
+            sourceLines <- lines <$> tplSrc
+            lineNum <- peSourceLine e
+            offendingLine <- listToMaybe . drop (pred lineNum) $ sourceLines
+            offendingColumn <- peSourceColumn e <|> Just 1
+            return . unlines $
+                [ offendingLine
+                , (replicate (pred offendingColumn) ' ') <> "^"
+                ]
+
+    in unlines . catMaybes $
+        [ sourceLocation
+        , markerLines
+        , Just (peErrorMessage e)
+        ]
 
 -- | Helper function to create a Ginger parser error from a Parsec error.
 fromParsecError :: ParseError -> ParserError
