summaryrefslogtreecommitdiff
path: root/lib/LintWriter.hs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/LintWriter.hs')
-rw-r--r--lib/LintWriter.hs180
1 files changed, 104 insertions, 76 deletions
diff --git a/lib/LintWriter.hs b/lib/LintWriter.hs
index e235fca..12c4311 100644
--- a/lib/LintWriter.hs
+++ b/lib/LintWriter.hs
@@ -7,11 +7,39 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TupleSections #-}
+{-# OPTIONS_GHC -Wno-missing-signatures #-}
-- | a monad that collects warnings, outputs, etc,
-module LintWriter where
+module LintWriter
+ ( runLintWriter
+ , LintWriter
+ , LintWriter'
+ , LintResult
+ , invertLintResult
+ -- * working with lint results
+ , resultToDeps
+ , resultToOffers
+ , resultToBadges
+ , resultToLints
+ , resultToAdjusted
+ -- * Add lints to a linter
+ , info
+ , suggest
+ , warn
+ , forbid
+ , complain
+ -- * add other information to the linter
+ , offersEntrypoint
+ , offersBadge
+ , dependsOn
+ -- * get information about the linter's context
+ , askContext
+ , askFileDepth
+ , lintConfig
+ -- * adjust the linter's context
+ , adjust
+ ) where
-import Data.Aeson (ToJSON (toJSON))
import Data.Text (Text)
import Control.Monad.State (StateT, modify)
@@ -21,123 +49,123 @@ import Control.Monad.Writer.Lazy (lift)
import Data.Bifunctor (Bifunctor (second))
import Data.Map (Map, fromListWith)
import Data.Maybe (mapMaybe)
-import qualified Data.Text as T
-import Util (PrettyPrint (..))
-
import Badges (Badge)
import LintConfig (LintConfig')
-import Tiled2 (HasName)
-import Types
-
--- | for now, all context we have is how "deep" in the directory tree
--- we currently are
-type Context = Int
-
-newtype LinterState ctxt = LinterState
- { fromLinterState :: ([Lint], ctxt)}
+import TiledAbstract (HasName)
+import Types (Dep, Hint, Level (..), Lint (..),
+ hint, lintsToHints)
--- | a monad to collect hints, with some context (usually the containing layer/etc.)
+-- | A monad modelling the main linter features
type LintWriter ctxt = LintWriter' ctxt ()
+-- | A linter that can use pure / return things monadically
type LintWriter' ctxt res =
StateT (LinterState ctxt) (Reader (Context, ctxt, LintConfig')) res
--- wrapped to allow for manual writing of Aeson instances
-type LintResult' ctxt = (ctxt, [Lint]) -- Either Lint (a, [Lint])
-newtype LintResult ctxt = LintResult (LintResult' ctxt)
+-- | A Linter's state: some context (which it may adjust), and a list of lints
+-- | it already collected.
+newtype LinterState ctxt = LinterState
+ { fromLinterState :: ([Lint], ctxt)}
+
+-- | The result of running a linter: an adjusted context, and a list of lints.
+-- | This is actually just a type synonym of LinterState, but kept seperately
+-- | for largely historic reasons since I don't think I'll change it again
+type LintResult ctxt = LinterState ctxt
+-- | for now, all context we have is how "deep" in the directory tree
+-- we currently are
+type Context = Int
+-- | run a linter. Returns the adjusted context, and a list of lints
+runLintWriter
+ :: LintConfig' -> ctxt -> Context -> LintWriter ctxt -> LintResult ctxt
+runLintWriter config context depth linter = LinterState
+ . fromLinterState
+ . snd
+ . runReader runstate
+ $ (depth, context, config)
+ where runstate = runStateT linter (LinterState ([], context))
+
+-- | "invert" a linter's result, grouping lints by their messages
invertLintResult :: HasName ctxt => LintResult ctxt -> Map Hint [ctxt]
-invertLintResult (LintResult (ctxt, lints)) =
+invertLintResult (LinterState (lints, ctxt)) =
fromListWith (<>) $ (, [ctxt]) <$> lintsToHints lints
--- better, less confusing serialisation of an Either Hint (a, [Hint]).
--- Note that Left hint is also serialised as a list to make the resulting
--- json schema more regular.
-instance ToJSON (LintResult a) where
- toJSON (LintResult res) = toJSON $ snd res
-
-instance PrettyPrint ctxt => PrettyPrint (Level, LintResult ctxt) where
- prettyprint (level, LintResult (ctxt, res)) =
- T.concat $ map ((<> context) . prettyprint) (filterLintLevel level res)
- where context = " (" <> prettyprint ctxt <> ")\n"
-
-lintToDep :: Lint -> Maybe Dep
-lintToDep = \case
- Depends dep -> Just dep
- _ -> Nothing
-
-lintToOffer :: Lint -> Maybe Text
-lintToOffer = \case
- Offers frag -> Just frag
- _ -> Nothing
-
-filterLintLevel :: Level -> [Lint] -> [Lint]
-filterLintLevel level = mapMaybe $ \l -> if level <= lintLevel l
- then Just l
- else Nothing
-
resultToDeps :: LintResult a -> [Dep]
-resultToDeps (LintResult a) = mapMaybe lintToDep $ snd a
+resultToDeps (LinterState (lints,_)) = mapMaybe lintToDep lints
+ where lintToDep = \case
+ Depends dep -> Just dep
+ _ -> Nothing
resultToOffers :: LintResult a -> [Text]
-resultToOffers (LintResult a) = mapMaybe lintToOffer $ snd a
-
--- | convert a lint result into a flat list of lints
--- (throwing away information on if a single error was fatal)
-resultToLints :: LintResult a -> [Lint]
-resultToLints (LintResult res) = snd res
+resultToOffers (LinterState a) = mapMaybe lintToOffer $ fst a
+ where lintToOffer = \case
+ Offers frag -> Just frag
+ _ -> Nothing
resultToBadges :: LintResult a -> [Badge]
-resultToBadges (LintResult a) = mapMaybe lintToBadge $ snd a
+resultToBadges (LinterState a) = mapMaybe lintToBadge $ fst a
where lintToBadge (Badge badge) = Just badge
lintToBadge _ = Nothing
+-- | convert a lint result into a flat list of lints
+resultToLints :: LintResult a -> [Lint]
+resultToLints (LinterState res) = fst res
+
+-- | extract the adjusted context from a lint result
resultToAdjusted :: LintResult a -> a
-resultToAdjusted (LintResult res) = fst res
+resultToAdjusted (LinterState res) = snd res
--- | run a linter. Returns the adjusted context, and a list of lints
-runLintWriter :: LintConfig' -> ctxt -> Context -> LintWriter ctxt -> LintResult ctxt
-runLintWriter config c c' linter = LintResult (snd $ fromLinterState lints,fst $ fromLinterState lints)
- where lints = snd $ runReader ranstate (c',c, config)
- ranstate = runStateT linter (LinterState ([], c))
-tell' :: Lint -> LintWriter ctxt
-tell' l = modify $ \(LinterState (lints, ctxt)) -> LinterState (l:lints, ctxt)
--- | write a hint into the LintWriter monad
-lint :: Level -> Text -> LintWriter a
-lint level text = tell' $ hint level text
+-- | fundamental linter operations: add a lint of some severity
+info = lint Info
+suggest = lint Suggestion
+warn = lint Warning
+forbid = lint Forbidden
+complain = lint Error
+-- | add a dependency to the linter
dependsOn :: Dep -> LintWriter a
dependsOn dep = tell' $ Depends dep
+-- | add an offer for an entrypoint to the linter
offersEntrypoint :: Text -> LintWriter a
offersEntrypoint text = tell' $ Offers text
+-- | add an offer for a badge to the linter
offersBadge :: Badge -> LintWriter a
offersBadge badge = tell' $ Badge badge
--- | adjusts the context. Gets a copy of the /current/ context, i.e. one which might
--- have already been changed by other lints
-adjust :: (a -> a) -> LintWriter a
-adjust f = modify $ LinterState . second f . fromLinterState
-info = lint Info
-suggest = lint Suggestion
-warn = lint Warning
-forbid = lint Forbidden
-complain = lint Error
-
-
--- | get the context as it was originally, without any modifications
+-- | get the context as it was initially, without any modifications
askContext :: LintWriter' a a
askContext = lift $ asks (\(_,a,_) -> a)
+-- | ask for the file depth within the repository tree of the current map.
+-- | This function brings in a lot more conceptual baggage than I'd like, but
+-- | it's needed to check if relative paths lie outside the repository
askFileDepth :: LintWriter' a Int
askFileDepth = lift $ asks (\(a,_,_) -> a)
+-- | ask for a specific part of the linter's global config
lintConfig :: (LintConfig' -> a) -> LintWriter' ctxt a
lintConfig get = lift $ asks (\(_,_,config) -> get config)
+
+
+
+
+-- | tell, but for a singular lint. Leaves the context unchanged
+tell' :: Lint -> LintWriter ctxt
+tell' l = modify $ \(LinterState (lints, ctxt)) -> LinterState (l:lints, ctxt)
+
+-- | small helper to tell a singlular proper lint
+lint :: Level -> Text -> LintWriter a
+lint level text = tell' $ hint level text
+
+-- | adjusts the context. Gets a copy of the /current/ context,
+-- | i.e. one which might have already been changed by other adjustments
+adjust :: (a -> a) -> LintWriter a
+adjust f = modify $ LinterState . second f . fromLinterState