dev: check: refactor error messages (#1436)
This commit is contained in:
		
							parent
							
								
									6ec0e05c42
								
							
						
					
					
						commit
						8f4405e628
					
				| @ -134,7 +134,6 @@ import Data.List.NonEmpty (NonEmpty(..)) | ||||
| import Data.Maybe (catMaybes, fromMaybe, isJust, listToMaybe) | ||||
| import qualified Data.Map as M | ||||
| import qualified Data.Semigroup as Sem | ||||
| import qualified Data.Set as S | ||||
| import Data.Text (Text) | ||||
| import qualified Data.Text as T | ||||
| import Data.Time.Calendar (Day, fromGregorianValid, toGregorian) | ||||
| @ -379,28 +378,77 @@ journalCheckPayeesDeclared j = mapM_ checkpayee (jtxns j) | ||||
| journalCheckAccountsDeclared :: Journal -> Either String () | ||||
| journalCheckAccountsDeclared j = mapM_ checkacct (journalPostings j) | ||||
|   where | ||||
|     checkacct p@Posting{paccount=a,ptransaction=mt} | ||||
|     checkacct p@Posting{paccount=a} | ||||
|       | a `elem` journalAccountNamesDeclared j = Right () | ||||
|       | otherwise = Left msg | ||||
|       | otherwise = Left $  | ||||
|         printf "%s:%d:%d:\n%sundeclared account \"%s\"\n" f l (maybe 0 (+1) mc) ex a | ||||
|       where | ||||
|         msg = printf "%s:%d:%d:\n%sundeclared account \"%s\"\n" f l (c+1) excerpt a | ||||
|         (f,l,mc,ex) = makeExcerpt p finderrcol | ||||
|         finderrcol p _ _ = Just $ 4 + if isVirtual p then 1 else 0 | ||||
| 
 | ||||
| -- | Check that all the commodities used in this journal's postings have been declared | ||||
| -- by commodity directives, returning an error message otherwise. | ||||
| journalCheckCommoditiesDeclared :: Journal -> Either String () | ||||
| journalCheckCommoditiesDeclared j = mapM_ checkcommodities (journalPostings j) | ||||
|   where | ||||
|             (f,l,c,excerpt) = case mt of | ||||
|               Nothing -> ("-",0,0,"") | ||||
|               Just t  -> (tf,errabsline,errcol,txt) | ||||
|     checkcommodities p = | ||||
|       case findundeclaredcomm p of | ||||
|         Nothing   -> Right () | ||||
|         Just comm -> Left $ printf "%s:%d:\n%sundeclared commodity \"%s\"\n" f l ex comm | ||||
|           where | ||||
|                   (SourcePos tf tl _tc) = fst $ tsourcepos t | ||||
|                   mpindex = fmap fst $ find ((a==).paccount.snd) $ zip [1..] $ tpostings t | ||||
|                   tcommentlines = max 0 $ length (T.lines $ tcomment t) - 1 | ||||
|             -- we don't know the original column of amounts | ||||
|             (f,l,_,ex) = makeExcerpt p finderrcol | ||||
|       where | ||||
|         -- Find the first undeclared commodity symbol in this posting, if any. | ||||
|         findundeclaredcomm Posting{pamount=amt,pbalanceassertion} = | ||||
|           find (`M.notMember` jcommodities j) | ||||
|           . map acommodity | ||||
|           . (maybe id ((:) . baamount) pbalanceassertion) | ||||
|           . filter (not . isIgnorable) | ||||
|           $ amountsRaw amt | ||||
|           where | ||||
|             -- Ignore missing amounts and zero amounts without commodity (#1767) | ||||
|             isIgnorable a = (T.null (acommodity a) && amountIsZero a) || a == missingamt | ||||
| 
 | ||||
|         -- Find the best position for an error column marker. | ||||
|         finderrcol p t txntxt = | ||||
|           case transactionFindPostingIndex (==p) t of | ||||
|             Nothing -> Nothing | ||||
|             Just pindex -> Just $  | ||||
|               acctend + (T.length $ T.takeWhile isnotsymbol $ T.drop acctend l) | ||||
|               where | ||||
|                 l = fromMaybe "" $ T.lines txntxt `atMay` pindex | ||||
|                 acctend = 4 + T.length (paccount p) + if isVirtual p then 2 else 0 | ||||
|                 isnotsymbol c = isSpace c || isDigit c || isDigitSeparatorChar c | ||||
| 
 | ||||
| -- | Given a problem posting and a function calculating the best | ||||
| -- position for the error column marker: | ||||
| -- look up error info from the parent transaction, and render the transaction | ||||
| -- as a megaparsec-style excerpt, showing the original line number | ||||
| -- on the problem posting's line, and a column indicator. | ||||
| -- Returns the file path, line number, starting column if known, | ||||
| -- and the rendered excerpt, or as much of these as is possible. | ||||
| makeExcerpt :: Posting -> (Posting -> Transaction -> Text -> Maybe Int) -> (FilePath, Int, Maybe Int, Text) | ||||
| makeExcerpt p findErrorColumn = | ||||
|   case ptransaction p of | ||||
|     Nothing -> ("-", 0, Nothing, "") | ||||
|     Just t  -> (f, errabsline, merrcol, ex) | ||||
|       where | ||||
|         (SourcePos f tl _) = fst $ tsourcepos t | ||||
|         tcommentlines = max 0 (length (T.lines $ tcomment t) - 1) | ||||
|         mpindex = transactionFindPostingIndex (==p) t | ||||
|         errrelline = maybe 0 (tcommentlines+) mpindex | ||||
|         errabsline = unPos tl + errrelline | ||||
|                   errcol = 4 + if isVirtual p then 1 else 0 | ||||
|                   txt = formatExcerptLikeMegaparsec errabsline errrelline errcol | ||||
|                           (showTransaction t & textChomp & (<>"\n")) | ||||
|         txntxt = showTransaction t & textChomp & (<>"\n") | ||||
|         merrcol = findErrorColumn p t txntxt | ||||
|         ex = decorateExcerpt errabsline errrelline merrcol txntxt | ||||
| 
 | ||||
| formatExcerptLikeMegaparsec :: Int -> Int -> Int -> Text -> Text | ||||
| formatExcerptLikeMegaparsec absline relline col txt = | ||||
|   T.unlines $ js' <> ks' <> [colmarkerline] <> ms' | ||||
| -- | Add megaparsec-style left margin, line number, and  | ||||
| -- optional column marker to an excerpt to be used in an  | ||||
| -- error message. | ||||
| decorateExcerpt :: Int -> Int -> Maybe Int -> Text -> Text | ||||
| decorateExcerpt absline relline mcol txt = | ||||
|   T.unlines $ js' <> ks' <> colmarkerline <> ms' | ||||
|   where | ||||
|     (ls,ms) = splitAt (relline+1) $ T.lines txt | ||||
|     (js,ks) = splitAt (length ls - 1) ls | ||||
| @ -408,52 +456,15 @@ formatExcerptLikeMegaparsec absline relline col txt = | ||||
|       [k] -> (map (lineprefix<>) js, [T.pack (show absline) <> " | " <> k]) | ||||
|       _   -> ([], []) | ||||
|     ms' = map (lineprefix<>) ms | ||||
|     colmarkerline = lineprefix <> T.replicate col " " <> "^" | ||||
|     colmarkerline = [lineprefix <> T.replicate col " " <> "^" | Just col <- [mcol]] | ||||
|     lineprefix = T.replicate marginw " " <> "| " | ||||
|       where  marginw = length (show absline) + 1 | ||||
| 
 | ||||
| -- | Check that all the commodities used in this journal's postings have been declared | ||||
| -- by commodity directives, returning an error message otherwise. | ||||
| journalCheckCommoditiesDeclared :: Journal -> Either String () | ||||
| journalCheckCommoditiesDeclared j = | ||||
|   mapM_ checkcommodities (journalPostings j) | ||||
|   where | ||||
|     checkcommodities p@Posting{ptransaction=mt,paccount=acct,pamount=amt,pbalanceassertion} = | ||||
|       case mfirstundeclaredcomm of | ||||
|         Nothing   -> Right () | ||||
|         Just comm -> Left $ | ||||
|           -- we don't save the original column of amounts | ||||
|           printf "%s:%d:\n%sundeclared commodity \"%s\"\n" f l excerpt comm | ||||
|           where | ||||
|             (f,l,excerpt) = case mt of | ||||
|               Nothing -> ("-",0,"") | ||||
|               Just t  -> (tf,errabsline,ex) | ||||
|                 where | ||||
|                   (SourcePos tf tl _tc) = fst $ tsourcepos t | ||||
|                   ppredicate = S.member comm . maCommodities . pamount | ||||
|                   mpindex = fmap fst $ find (ppredicate.snd) $ zip [1..] $ tpostings t | ||||
|                   tcommentlines = max 0 $ length (T.lines $ tcomment t) - 1 | ||||
|                   errrelline = maybe 0 (tcommentlines+) mpindex | ||||
|                   errabsline = unPos tl + errrelline | ||||
|                   txn = showTransaction t & textChomp & (<>"\n") | ||||
|                   errcol = case mpindex of | ||||
|                     Nothing     -> 0 | ||||
|                     Just pindex -> acctend + (T.length $ T.takeWhile isnotsymbol $ T.drop acctend l) | ||||
|                       where | ||||
|                         acctend = 4 + T.length acct + if isVirtual p then 2 else 0 | ||||
|                         isnotsymbol c = isSpace c || isDigit c || isDigitSeparatorChar c | ||||
|                         l = fromMaybe "" $ (T.lines txn `atMay` pindex) | ||||
|                   ex = formatExcerptLikeMegaparsec errabsline errrelline errcol txn | ||||
|       where | ||||
|         mfirstundeclaredcomm = | ||||
|           find (`M.notMember` jcommodities j) | ||||
|           . map acommodity | ||||
|           . (maybe id ((:) . baamount) pbalanceassertion) | ||||
|           . filter (not . isIgnorable) | ||||
|           $ amountsRaw amt | ||||
| 
 | ||||
|     -- Ignore missing amounts and zero amounts without commodity (#1767) | ||||
|     isIgnorable a = (T.null (acommodity a) && amountIsZero a) || a == missingamt | ||||
| -- | Find the 1-based index of the first posting in this transaction | ||||
| -- satisfying the given predicate. | ||||
| transactionFindPostingIndex :: (Posting -> Bool) -> Transaction -> Maybe Int | ||||
| transactionFindPostingIndex ppredicate =  | ||||
|   fmap fst . find (ppredicate.snd) . zip [1..] . tpostings | ||||
| 
 | ||||
| setYear :: Year -> JournalParser m () | ||||
| setYear y = modify' (\j -> j{jparsedefaultyear=Just y}) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user