-- | A parser that takes a maximum line length as input, and emits warnings for any line that -- exceeds it. module GLua.LineLimitParser where import Control.Applicative ((<|>)) import Control.Monad (void) import GLua.Lexer (pPos) import GLua.Position (Region (..)) import GLuaFixer.LintMessage (Issue (LineTooLong), LintMessage (..), Severity (LintWarning)) import Text.Parsec (endOfLine, eof, parse, satisfy, skipMany1) import Text.Parsec.String (Parser) -- | The maximum line length newtype LineLimit = LineLimit Int execParseLineLimits :: FilePath -> LineLimit -> String -> [LintMessage] execParseLineLimits filePath lineLimit@(LineLimit limit) contents = -- No point in parsing anything if the limit is 0 if limit <= 0 then [] else case parse (lineLimitParser filePath lineLimit) "input" contents of Left parseError -> error $ "Parse error while checking line limit: " ++ show parseError Right lintWarnings -> lintWarnings -- | Parser that produces warnings about lines being too long lineLimitParser :: FilePath -> LineLimit -> Parser [LintMessage] lineLimitParser filePath lineLimit = [] <$ eof <|> recurse filePath lineLimit -- | Recursive case of 'lineLimitParser', recurse :: FilePath -> LineLimit -> Parser [LintMessage] recurse filePath lineLimit@(LineLimit limit) = do countAtMost limit notNewline -- If it finds line endings, it recurses without producing warnings. Otherwise, if it finds -- anything else, produce a warning and still recurse. endOfLines *> lineLimitParser filePath lineLimit <|> (:) <$> lineLimitWarning filePath <*> lineLimitParser filePath lineLimit -- | Succeeds when a character is not a line notNewline :: Parser Char notNewline = satisfy $ \c -> c /= '\n' && c /= '\r' -- | end of lines. Consumes all \r and \n characters it finds endOfLines :: Parser () endOfLines = skipMany1 endOfLine <|> void eof -- | Produces a line limit warning lineLimitWarning :: FilePath -> Parser LintMessage lineLimitWarning filePath = do startPos <- pPos skipMany1 notNewline endPos <- pPos let warnRegion = Region startPos endPos pure $ LintMessage LintWarning warnRegion LineTooLong filePath -- | Consume the parser until either it has been parsed the Int amount of times, or the parser fails countAtMost :: Int -> Parser a -> Parser () countAtMost 0 _ = pure () countAtMost i p = p *> countAtMost (i - 1) p <|> pure ()