hbs2/fixme-new/lib/Fixme/Run.hs

301 lines
8.0 KiB
Haskell

{-# Language PatternSynonyms #-}
{-# Language ViewPatterns #-}
module Fixme.Run where
import Prelude hiding (init)
import Fixme.Prelude hiding (indent)
import Fixme.Types
import Fixme.Scan.Git as Git
import Fixme.Scan as Scan
import HBS2.Git.Local.CLI
import HBS2.System.Dir
import Data.Config.Suckless
import Data.Text.Fuzzy.Tokenize
import Data.ByteString.Lazy.Char8 qualified as LBS8
import Data.ByteString.Lazy (ByteString)
import Data.Either
import System.Environment
import Data.Maybe
import Data.HashSet qualified as HS
import Data.HashMap.Strict qualified as HM
import Data.Text qualified as Text
import Data.Text.Encoding qualified as Text
import Text.InterpolatedString.Perl6 (qc)
import Data.Coerce
import Control.Monad.Identity
import Lens.Micro.Platform
import Streaming.Prelude qualified as S
{- HLINT ignore "Functor law" -}
pattern Init :: forall {c}. Syntax c
pattern Init <- ListVal [SymbolVal "init"]
pattern ScanGitLocal :: forall {c}. [ScanGitArgs] -> Syntax c
pattern ScanGitLocal e <- ListVal (SymbolVal "scan-git" : (scanGitArgs -> e))
pattern ReadFixmeStdin :: forall {c}. Syntax c
pattern ReadFixmeStdin <- ListVal [SymbolVal "read-fixme-stdin"]
pattern FixmeFiles :: forall {c} . [FilePattern] -> Syntax c
pattern FixmeFiles e <- ListVal (SymbolVal "fixme-files" : (fileMasks -> e))
pattern FixmePrefix :: forall {c} . FixmeTag -> Syntax c
pattern FixmePrefix s <- ListVal [SymbolVal "fixme-prefix", fixmePrefix -> Just s]
pattern FixmeGitScanFilterDays :: forall {c}. Integer -> Syntax c
pattern FixmeGitScanFilterDays d <- ListVal [ SymbolVal "fixme-git-scan-filter-days", LitIntVal d ]
pattern StringLike :: forall {c} . String -> Syntax c
pattern StringLike e <- (stringLike -> Just e)
pattern StringLikeList :: forall {c} . [String] -> [Syntax c]
pattern StringLikeList e <- (stringLikeList -> e)
data ScanGitArgs =
PrintBlobs
deriving stock (Eq,Ord,Show,Data,Generic)
scanGitArgs :: [Syntax c] -> [ScanGitArgs]
scanGitArgs syn = [ PrintBlobs | SymbolVal "print-blobs" <- syn ]
stringLike :: Syntax c -> Maybe String
stringLike = \case
LitStrVal s -> Just $ Text.unpack s
SymbolVal (Id s) -> Just $ Text.unpack s
_ -> Nothing
stringLikeList :: [Syntax c] -> [String]
stringLikeList syn = [ stringLike s | s <- syn ] & takeWhile isJust & catMaybes
fileMasks :: [Syntax c] -> [FilePattern]
fileMasks what = [ show (pretty s) | s <- what ]
fixmePrefix :: Syntax c -> Maybe FixmeTag
fixmePrefix = \case
SymbolVal s -> Just (FixmeTag (coerce s))
_ -> Nothing
binName :: FixmePerks m => m FilePath
binName = liftIO getProgName
localConfigDir :: FixmePerks m => m FilePath
localConfigDir = do
p <- pwd
b <- binName
pure (p </> ("." <> b))
localConfig:: FixmePerks m => m FilePath
localConfig = localConfigDir <&> (</> "config")
readConfig :: FixmePerks m => FixmeM m [Syntax C]
readConfig = do
localConfig
>>= try @_ @IOException . liftIO . readFile
<&> fromRight mempty
<&> parseTop
<&> fromRight mempty
init :: FixmePerks m => FixmeM m ()
init = do
lo <- localConfigDir
let lo0 = takeFileName lo
mkdir lo
touch (lo </> "config")
let gitignore = lo </> ".gitignore"
here <- doesPathExist gitignore
unless here do
liftIO $ writeFile gitignore $ show $
vcat [ "./state.db"
]
notice $ yellow "run" <> line <> vcat [
"git add" <+> pretty (lo0 </> ".gitignore")
, "git add" <+> pretty (lo0 </> "config")
]
listCommits :: FixmePerks m => FixmeM m [GitHash]
listCommits = do
let gd = ""
days <- asks fixmeEnvGitScanDays
>>= readTVarIO
<&> fmap ( \x -> "--since" <+> squotes (pretty x <+> "days ago"))
<&> fromMaybe mempty
<&> show
let cmd = [qc|git log --all --format="%H" {days}|]
-- FIXME: git-dir
gitRunCommand cmd
<&> fromRight mempty
<&> mapMaybe (headMay . LBS8.words) . LBS8.lines
<&> mapMaybe (fromStringMay @GitHash . LBS8.unpack)
listBlobs :: FixmePerks m => GitHash -> m [(FilePath,GitHash)]
listBlobs co = do
-- FIXME: git-dir
gitRunCommand [qc|git ls-tree -r -l -t {pretty co}|]
<&> fromRight mempty
<&> fmap LBS8.words . LBS8.lines
<&> mapMaybe
(\case
[a,"blob",h,_,fn] -> (LBS8.unpack fn,) <$> fromStringMay @GitHash (LBS8.unpack h)
_ -> Nothing)
filterBlobs :: FixmePerks m
=> [(FilePath,GitHash)]
-> FixmeM m [(FilePath,GitHash)]
filterBlobs xs = do
pat <- asks fixmeEnvFileMask >>= readTVarIO <&> fmap (True,)
let src = [ ((f,h),f) | (f,h) <- xs ]
let r = [(h,f) | (_,(f,h),_) <- matchMany pat src] & HM.fromList & HM.toList
pure $ [ (b,a) | (a,b) <- r ]
scanGitLocal :: FixmePerks m => [ScanGitArgs] -> Maybe FilePath -> FixmeM m ()
scanGitLocal args p = do
debug $ yellow "scan for fixmies, wtf?"
co <- listCommits
blobs <- mconcat <$> for co (\c -> do
debug $ "commit" <+> pretty c
listBlobs c >>= filterBlobs )
when (PrintBlobs `elem` args) do
for_ blobs $ \(fp,h) -> do
liftIO $ print $ pretty h <+> pretty fp
exractFixme :: FixmePerks m => ByteString -> m [Fixme]
exractFixme bs = do
let ls = LBS8.lines bs
pure mempty
readUtf8 :: ByteString -> Text
readUtf8 bs = LBS8.toStrict bs & Text.decodeUtf8
readFixmeStdin :: FixmePerks m => FixmeM m ()
readFixmeStdin = do
what <- liftIO LBS8.getContents
fixmies <- Scan.scanBlob Nothing what
liftIO $ print $ vcat (fmap pretty fixmies)
printEnv :: FixmePerks m => FixmeM m ()
printEnv = do
g <- asks fixmeEnvGitDir
masks <- asks fixmeEnvFileMask >>= readTVarIO
tags <- asks fixmeEnvTags >>= readTVarIO
days <- asks fixmeEnvGitScanDays >>= readTVarIO
comments1 <- asks fixmeEnvDefComments >>= readTVarIO <&> HS.toList
comments2 <- asks fixmeEnvFileComments >>= readTVarIO
<&> HM.toList
<&> fmap (over _2 HS.toList)
for_ tags $ \m -> do
liftIO $ print $ "fixme-prefix" <+> pretty m
for_ masks $ \m -> do
liftIO $ print $ "fixme-files" <+> dquotes (pretty m)
for_ days $ \d -> do
liftIO $ print $ "fixme-git-scan-filter-days" <+> pretty d
for_ comments1 $ \d -> do
liftIO $ print $ "fixme-comments" <+> dquotes (pretty d)
for_ comments2 $ \(ft, comm') -> do
for_ comm' $ \comm -> do
liftIO $ print $ "fixme-file-comments"
<+> dquotes (pretty ft) <+> dquotes (pretty comm)
help :: FixmePerks m => m ()
help = do
notice "this is help message"
splitForms :: [String] -> [[String]]
splitForms s0 = runIdentity $ S.toList_ (go mempty s0)
where
go acc ( "then" : rest ) = emit acc >> go mempty rest
go acc ( x : rest ) = go ( x : acc ) rest
go acc [] = emit acc
emit = S.yield . reverse
run :: FixmePerks m => [String] -> FixmeM m ()
run what = do
sc <- readConfig
let s0 = fmap (parseTop . unwords) (splitForms what)
& rights
& mconcat
for_ (sc <> s0) $ \s -> do
debug $ pretty s
case s of
FixmeFiles xs -> do
t <- asks fixmeEnvFileMask
atomically (modifyTVar t (<> xs))
FixmePrefix tag -> do
t <- asks fixmeEnvTags
atomically (modifyTVar t (HS.insert tag))
FixmeGitScanFilterDays d -> do
t <- asks fixmeEnvGitScanDays
atomically (writeTVar t (Just d))
ListVal [SymbolVal "fixme-file-comments", StringLike ft, StringLike b] -> do
let co = Text.pack b & HS.singleton
t <- asks fixmeEnvFileComments
atomically (modifyTVar t (HM.insertWith (<>) (commentKey ft) co))
ListVal (SymbolVal "fixme-comments" : StringLikeList xs) -> do
t <- asks fixmeEnvDefComments
let co = fmap Text.pack xs & HS.fromList
atomically $ modifyTVar t (<> co)
Init -> init
ScanGitLocal args -> scanGitLocal args Nothing
ReadFixmeStdin -> readFixmeStdin
ListVal [SymbolVal "print-env"] -> do
printEnv
ListVal [SymbolVal "no-debug"] -> do
setLoggingOff @DEBUG
ListVal [SymbolVal "debug"] -> do
setLogging @DEBUG $ toStderr . logPrefix "[debug] "
w -> err (pretty w)