diff --git a/Cabal/Distribution/FieldGrammar/Parsec.hs b/Cabal/Distribution/FieldGrammar/Parsec.hs
index 13231f5d33baebdcd6db18828bb1391981a7a170..916403f0741c6aa6c80c80f540ebeb8c6ac2b1f5 100644
--- a/Cabal/Distribution/FieldGrammar/Parsec.hs
+++ b/Cabal/Distribution/FieldGrammar/Parsec.hs
@@ -262,31 +262,33 @@ instance FieldGrammar ParsecFieldGrammar where
 -- Parsec
 -------------------------------------------------------------------------------
 
-runFieldParser' :: Position -> ParsecParser a -> CabalSpecVersion -> FieldLineStream -> ParseResult a
-runFieldParser' (Position row col) p v str = case P.runParser p' [] "<field>" str of
+runFieldParser' :: [Position] -> ParsecParser a -> CabalSpecVersion -> FieldLineStream -> ParseResult a
+runFieldParser' inputPoss p v str = case P.runParser p' [] "<field>" str of
     Right (pok, ws) -> do
-        -- TODO: map pos
-        traverse_ (\(PWarning t pos w) -> parseWarning pos t w) ws
+        traverse_ (\(PWarning t pos w) -> parseWarning (mapPosition pos) t w) ws
         pure pok
     Left err        -> do
         let ppos = P.errorPos err
-        -- Positions start from 1:1, not 0:0
-        let epos = Position (row - 1 + P.sourceLine ppos) (col - 1 + P.sourceColumn ppos)
+        let epos = mapPosition $ Position (P.sourceLine ppos) (P.sourceColumn ppos)
+
         let msg = P.showErrorMessages
                 "or" "unknown parse error" "expecting" "unexpected" "end of input"
                 (P.errorMessages err)
         parseFatalFailure epos $ msg ++ "\n"
-
   where
     p' = (,) <$ P.spaces <*> unPP p v <* P.spaces <* P.eof <*> P.getState
 
+    -- Positions start from 1:1, not 0:0
+    mapPosition (Position prow pcol) = go (prow - 1) inputPoss where
+        go _ []                            = zeroPos
+        go _ [Position row col]            = Position row (col + pcol - 1)
+        go n (Position row col:_) | n <= 0 = Position row (col + pcol - 1)
+        go n (_:ps)                        = go (n - 1) ps
+
 runFieldParser :: Position -> ParsecParser a -> CabalSpecVersion -> [FieldLine Position] -> ParseResult a
-runFieldParser pp p v ls = runFieldParser' pos p v (fieldLinesToStream ls)
+runFieldParser pp p v ls = runFieldParser' poss p v (fieldLinesToStream ls)
   where
-    -- TODO: make per line lookup
-    pos = case ls of
-        []                     -> pp
-        (FieldLine pos' _ : _) -> pos'
+    poss = map (\(FieldLine pos _) -> pos) ls ++ [pp] -- add "default" position
 
 fieldlinesToBS :: [FieldLine ann] -> BS.ByteString
 fieldlinesToBS = BS.intercalate "\n" . map (\(FieldLine _ bs) -> bs)
diff --git a/Cabal/Distribution/PackageDescription/Parsec.hs b/Cabal/Distribution/PackageDescription/Parsec.hs
index 3ea4f7975e2dc2e3caaccc5ced8fb49c08f49eed..a3387eb9e5fea1ecef4e23271a9c83af6b10ba6c 100644
--- a/Cabal/Distribution/PackageDescription/Parsec.hs
+++ b/Cabal/Distribution/PackageDescription/Parsec.hs
@@ -343,7 +343,7 @@ goSections specVer = traverse_ process
 
         | name == "flag" = do
             name'  <- parseNameBS pos args
-            name'' <- lift $ runFieldParser' pos parsec specVer (fieldLineStreamFromBS name') `recoverWith` mkFlagName ""
+            name'' <- lift $ runFieldParser' [pos] parsec specVer (fieldLineStreamFromBS name') `recoverWith` mkFlagName ""
             flag   <- lift $ parseFields specVer fields (flagFieldGrammar name'')
             -- Check default flag
             stateGpd . L.genPackageFlags %= snoc flag
@@ -355,7 +355,7 @@ goSections specVer = traverse_ process
         | name == "source-repository" = do
             kind <- lift $ case args of
                 [SecArgName spos secName] ->
-                    runFieldParser' spos parsec specVer (fieldLineStreamFromBS secName) `recoverWith` RepoHead
+                    runFieldParser' [spos] parsec specVer (fieldLineStreamFromBS secName) `recoverWith` RepoHead
                 [] -> do
                     parseFailure pos "'source-repository' requires exactly one argument"
                     pure RepoHead
diff --git a/Cabal/Distribution/Parsec/Class.hs b/Cabal/Distribution/Parsec/Class.hs
index d65ea54eeccdee3e6271edd9da7e4e6771f675b0..b08658c8d09d94115ba0e1a17e860fe46fb4155c 100644
--- a/Cabal/Distribution/Parsec/Class.hs
+++ b/Cabal/Distribution/Parsec/Class.hs
@@ -142,7 +142,10 @@ instance P.CharParsing ParsecParser where
     string    = liftParsec . P.string
 
 instance CabalParsing ParsecParser where
-    parsecWarning t w = liftParsec $ Parsec.modifyState (PWarning t (Position 0 0) w :)
+    parsecWarning t w = liftParsec $ do
+        spos <- Parsec.getPosition
+        Parsec.modifyState
+            (PWarning t (Position (Parsec.sourceLine spos) (Parsec.sourceColumn spos)) w :)
     askCabalSpecVersion = PP pure
 
 -- | Parse a 'String' with 'lexemeParsec'.
diff --git a/Cabal/tests/ParserTests/regressions/MiniAgda.check b/Cabal/tests/ParserTests/regressions/MiniAgda.check
index b482da3202e152d019eaa314640f27e9051a7691..37b642163e1494d99c8060216dc25d45a41a7219 100644
--- a/Cabal/tests/ParserTests/regressions/MiniAgda.check
+++ b/Cabal/tests/ParserTests/regressions/MiniAgda.check
@@ -1 +1 @@
-MiniAgda.cabal:0:0: Version digit with leading zero. Use cabal-version: 2.0 or later to write such versions. For more information see https://github.com/haskell/cabal/issues/5092
+MiniAgda.cabal:2:27: Version digit with leading zero. Use cabal-version: 2.0 or later to write such versions. For more information see https://github.com/haskell/cabal/issues/5092
diff --git a/cabal-install/Distribution/Client/Utils/Parsec.hs b/cabal-install/Distribution/Client/Utils/Parsec.hs
index 8d866d328397d77838a40338839dd6c8ea78e52a..13851efe6c4c9d1711c80042ac891d45d6d1efb3 100644
--- a/cabal-install/Distribution/Client/Utils/Parsec.hs
+++ b/cabal-install/Distribution/Client/Utils/Parsec.hs
@@ -25,41 +25,75 @@ renderParseError filepath contents errors warnings = unlines $
     ++ renderedErrors
     ++ renderedWarnings
   where
-    -- lines of the input file.
-    ls = BS8.lines contents
+    -- lines of the input file. 'lines' is taken, so they are called rows
+    -- contents, line number, whether it's empty line
+    rows :: [(String, Int, Bool)]
+    rows = zipWith f (BS8.lines contents) [1..] where
+        f bs i = let s = fromUTF8BS bs in (s, i, isEmptyOrComment s)
 
-    nths :: Int -> [a] -> [a]
-    nths n | n <= 0 = take 2
-    nths n = take 3 . drop (n - 1)
+    rowsZipper = listToZipper rows
 
-    -- empty line before each error and warning
-    renderedErrors   = concatMap (prepend . renderError) errors
-    renderedWarnings = concatMap (prepend . renderWarning) warnings
+    isEmptyOrComment :: String -> Bool
+    isEmptyOrComment s = case dropWhile (== ' ') s of
+        ""          -> True   -- empty
+        ('-':'-':_) -> True   -- comment
+        _           -> False
 
-    prepend = ("" :)
+    -- empty line before each error and warning
+    renderedErrors   = concatMap (("" :) . renderError) errors
+    renderedWarnings = concatMap (("" :) . renderWarning) warnings
 
     renderError :: PError -> [String]
-    renderError e@(PError pos@(Position row _col) _)
+    renderError e@(PError pos@(Position row col) _)
         -- if position is 0:0, then it doesn't make sense to show input
         -- looks like, Parsec errors have line-feed in them
-        | pos == zeroPos = [trim $ showPError filepath e]
-        | otherwise      = [trim $ showPError filepath e, ""] ++
-            zipWith formatInputLine (nths (row - 1) ls) [row - 1 ..]
+        | pos == zeroPos = [trimLF $ showPError filepath e]
+        | otherwise      = [trimLF $ showPError filepath e, ""] ++
+            formatInput row col
 
     -- sometimes there are (especially trailing) newlines.
-    trim = dropWhile (== '\n') . reverse . dropWhile (== '\n') . reverse
+    trimLF :: String -> String
+    trimLF = dropWhile (== '\n') . reverse . dropWhile (== '\n') . reverse
 
     renderWarning :: PWarning -> [String]
-    renderWarning w@(PWarning _ pos@(Position row _col) _)
+    renderWarning w@(PWarning _ pos@(Position row col) _)
         | pos == zeroPos = [showPWarning filepath w]
         | otherwise      = [showPWarning filepath w, ""] ++
-            zipWith formatInputLine (nths (row - 1) ls) [row - 1 ..]
+            formatInput row col
 
     -- format line: prepend the given line number
-    formatInputLine :: BS.ByteString -> Int -> String
-    formatInputLine bs l =
-        showN l ++ " | " ++ fromUTF8BS bs
+    formatInput :: Int -> Int -> [String]
+    formatInput row col = case advance (row - 1) rowsZipper of
+        Zipper xs ys -> before ++ after where
+            before = case span (\(_, _, b) -> b) xs of
+                (_, [])     -> []
+                (zs, z : _) -> map formatInputLine $ z : reverse zs
+
+            after  = case ys of
+                []        -> []
+                (z : _zs) ->
+                    [ formatInputLine z                             -- error line
+                    , "      | " ++ replicate (col - 1) ' ' ++ "^"  -- pointer: ^
+                    ]
+                    -- do we need rows after?
+                    -- ++ map formatInputLine (take 1 zs)           -- one row after
+
+    formatInputLine :: (String, Int, Bool) -> String
+    formatInputLine (str, row, _) = leftPadShow row ++ " | " ++ str
 
     -- hopefully we don't need to work with over 99999 lines .cabal files
     -- at that point small glitches in error messages are hopefully fine.
-    showN n = let s = show n in replicate (5 - length s) ' ' ++ s
+    leftPadShow :: Int -> String
+    leftPadShow n = let s = show n in replicate (5 - length s) ' ' ++ s
+
+data Zipper a = Zipper [a] [a]
+
+listToZipper :: [a] -> Zipper a
+listToZipper = Zipper []
+
+advance :: Int -> Zipper a -> Zipper a
+advance n z@(Zipper xs ys)
+    | n <= 0 = z
+    | otherwise = case ys of
+        []      -> z
+        (y:ys') -> advance (n - 1) $ Zipper (y:xs) ys'