glualint-1.26.0: app/GLuaFixer/Effects/AnalyseGlobals.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module GLuaFixer.Effects.AnalyseGlobals where
import Data.Char (toLower)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Effectful (Eff, (:>))
import qualified Effectful.State.Static.Local as State
import GHC.Exts (sortWith)
import GLua.AG.AST (AST)
import GLua.AG.PrettyPrint (renderRegion)
import GLua.AG.Token (Region)
import GLuaFixer.AG.ASTLint (globalDefinitions)
import GLuaFixer.Effects.Logging (Logging, putStrLnStdOut)
import GLuaFixer.LintSettings (LintSettings)
import Prelude hiding (readFile, writeFile)
data VariableLocation = VariableLocation
{ file :: !FilePath
, regions :: ![Region]
}
newtype AnalysisState = AnalysisState {state :: Map String [VariableLocation]}
emptyState :: AnalysisState
emptyState = AnalysisState Map.empty
-- | Run the analysis and get the finished analysis state
execAnalysis :: Eff (State.State AnalysisState : es) () -> Eff es AnalysisState
execAnalysis = State.execState emptyState
-- | Analyse a single file
analyseFile :: State.State AnalysisState :> es => LintSettings -> FilePath -> AST -> Eff es ()
analyseFile lintSettings filepath ast = do
let
definitionsInFile :: Map String [Region] = globalDefinitions lintSettings ast
variableLocations :: Map String [VariableLocation] =
Map.map ((: []) . VariableLocation filepath) definitionsInFile
State.modify $ \analysisState ->
AnalysisState $ Map.unionWith (++) analysisState.state variableLocations
-- | Print the analysis state to the terminal.
-- TODO: make the rendering a pure function
reportAnalysis :: forall es. Logging :> es => AnalysisState -> Eff es ()
reportAnalysis analysis =
mapM_ reportGlobal globals
where
globals :: [(String, [VariableLocation])]
globals = sortWith (map toLower . fst) $ Map.toList $ Map.map (sortWith file) analysis.state
reportRegions :: [Region] -> Eff es ()
reportRegions rgs =
mapM_ (\r -> putStrLnStdOut $ " " ++ renderRegion r) $ reverse rgs
reportVariableLocation :: VariableLocation -> Eff es ()
reportVariableLocation location =
do
putStrLnStdOut $ " " ++ file location ++ ":"
reportRegions $ regions location
putStrLnStdOut ""
reportGlobal :: (String, [VariableLocation]) -> Eff es ()
reportGlobal (global, analyses) =
do
putStrLnStdOut $ "- " ++ global
mapM_ reportVariableLocation analyses