This commit is contained in:
Dmitry Zuikov 2024-09-09 14:11:22 +03:00
parent d9785f1930
commit e3655a8eb2
11 changed files with 216 additions and 1075 deletions

View File

@ -23,6 +23,8 @@ fixme-value-set scope mvp-0 mvp-1 backlog
fixme-files **/*.txt docs/devlog.md fixme-files **/*.txt docs/devlog.md
fixme-files **/*.hs fixme-files **/*.hs
fixme-exclude **/.**
fixme-exclude dist-newstyle
fixme-file-comments "*.scm" ";" fixme-file-comments "*.scm" ";"

View File

@ -5,7 +5,7 @@ import Fixme.Types
import HBS2.System.Dir import HBS2.System.Dir
import System.Environment import System.Environment
import System.Directory import System.Directory (getXdgDirectory, XdgDirectory(..))
binName :: FixmePerks m => m FilePath binName :: FixmePerks m => m FilePath
binName = liftIO getProgName binName = liftIO getProgName
@ -16,6 +16,9 @@ localConfigDir = do
b <- binName b <- binName
pure (p </> ("." <> b)) pure (p </> ("." <> b))
fixmeWorkDir :: FixmePerks m => m FilePath
fixmeWorkDir = localConfigDir <&> takeDirectory >>= canonicalizePath
localConfig:: FixmePerks m => m FilePath localConfig:: FixmePerks m => m FilePath
localConfig = localConfigDir <&> (</> "config") localConfig = localConfigDir <&> (</> "config")

View File

@ -10,6 +10,8 @@ import Fixme.Scan.Git.Local as Git
import Fixme.Scan as Scan import Fixme.Scan as Scan
import Fixme.Log import Fixme.Log
import Data.Config.Suckless.Script.File
import HBS2.KeyMan.Keys.Direct import HBS2.KeyMan.Keys.Direct
import HBS2.Git.Local.CLI import HBS2.Git.Local.CLI
@ -59,6 +61,8 @@ import Control.Monad.Trans.Maybe
import System.IO.Temp as Temp import System.IO.Temp as Temp
import System.IO qualified as IO import System.IO qualified as IO
import Streaming.Prelude qualified as S
{- HLINT ignore "Functor law" -} {- HLINT ignore "Functor law" -}
@ -121,8 +125,10 @@ runFixmeCLI m = do
<*> newTVarIO mempty <*> newTVarIO mempty
<*> newTVarIO mempty <*> newTVarIO mempty
<*> newTVarIO mempty <*> newTVarIO mempty
<*> newTVarIO mempty
<*> newTVarIO defCommentMap <*> newTVarIO defCommentMap
<*> newTVarIO Nothing <*> newTVarIO Nothing
<*> newTVarIO mzero
<*> newTVarIO mempty <*> newTVarIO mempty
<*> newTVarIO mempty <*> newTVarIO mempty
<*> newTVarIO defaultCatAction <*> newTVarIO defaultCatAction
@ -163,7 +169,7 @@ silence = do
setLoggingOff @TRACE setLoggingOff @TRACE
readConfig :: FixmePerks m => FixmeM m [Syntax C] readConfig :: (FixmePerks m) => FixmeM m [Syntax C]
readConfig = do readConfig = do
user <- userConfigs user <- userConfigs
@ -175,6 +181,8 @@ readConfig = do
<&> parseTop <&> parseTop
>>= either (error.show) pure >>= either (error.show) pure
updateScanMagic
pure $ mconcat w pure $ mconcat w
@ -223,16 +231,26 @@ runTop forms = do
entry $ bindMatch "fixme-attribs" $ nil_ \case entry $ bindMatch "fixme-attribs" $ nil_ \case
StringLikeList xs -> do StringLikeList xs -> do
w <- fixmeWorkDir
ta <- lift $ asks fixmeEnvAttribs ta <- lift $ asks fixmeEnvAttribs
atomically $ modifyTVar ta (<> HS.fromList (fmap fromString xs)) atomically $ modifyTVar ta (<> HS.fromList (fmap (fromString . (</> w)) xs))
_ -> throwIO $ BadFormException @C nil _ -> throwIO $ BadFormException @C nil
entry $ bindMatch "fixme-files" $ nil_ \case entry $ bindMatch "fixme-files" $ nil_ \case
StringLikeList xs -> do StringLikeList xs -> do
w <- fixmeWorkDir
t <- lift $ asks fixmeEnvFileMask t <- lift $ asks fixmeEnvFileMask
atomically (modifyTVar t (<> xs)) atomically (modifyTVar t (<> fmap (w </>) xs))
_ -> throwIO $ BadFormException @C nil
entry $ bindMatch "fixme-exclude" $ nil_ \case
StringLikeList xs -> do
w <- fixmeWorkDir
t <- lift $ asks fixmeEnvFileExclude
atomically (modifyTVar t (<> fmap (w </>) xs))
_ -> throwIO $ BadFormException @C nil _ -> throwIO $ BadFormException @C nil
@ -291,29 +309,55 @@ runTop forms = do
_ -> throwIO $ BadFormException @C nil _ -> throwIO $ BadFormException @C nil
entry $ bindMatch "dump" $ nil_ \case entry $ bindMatch "fixme:scan-magic" $ nil_ $ const do
[FixmeHashLike h] -> do magic <- lift $ asks fixmeEnvScanMagic >>= readTVarIO
lift $ dumpFixme h liftIO $ print $ pretty magic
_ -> throwIO $ BadFormException @C nil entry $ bindMatch "fixme:path" $ nil_ $ const do
path <- lift fixmeWorkDir
liftIO $ print $ pretty path
entry $ bindMatch "cat" $ nil_ \case entry $ bindMatch "fixme:files" $ nil_ $ const do
[SymbolVal "metadata", FixmeHashLike hash] -> do w <- lift fixmeWorkDir
lift $ catFixmeMetadata hash incl <- lift (asks fixmeEnvFileMask >>= readTVarIO)
excl <- lift (asks fixmeEnvFileExclude >>= readTVarIO)
glob incl excl w $ \fn -> do
liftIO $ putStrLn (makeRelative w fn)
pure True
[FixmeHashLike hash] -> do entry $ bindMatch "fixme:state:drop" $ nil_ $ const $ lift do
lift $ catFixme hash cleanupDatabase
_ -> throwIO $ BadFormException @C nil entry $ bindMatch "fixme:state:cleanup" $ nil_ $ const $ lift do
cleanupDatabase
entry $ bindMatch "report" $ nil_ \case entry $ bindMatch "fixme:scan:import" $ nil_ $ const $ lift do
[] -> lift $ list_ Nothing () fxs0 <- scanFiles
(SymbolVal "--template" : StringLike name : query) -> do fxs <- flip filterM fxs0 $ \fme -> do
lift $ list_ (Just (fromString name)) query let fn = HM.lookup "file" (fixmeAttr fme) <&> Text.unpack . coerce
seen <- maybe1 fn (pure False) selectIsAlreadyScanned
pure (not seen)
query -> do withState $ transactional do
lift $ list_ mzero query for_ fxs $ \fme -> do
notice $ "fixme" <+> pretty (fixmeKey fme)
insertFixme fme
-- TODO: remove-code-duplucation
let fn = HM.lookup "file" (fixmeAttr fme) <&> Text.unpack . coerce
for_ fn insertScanned
entry $ bindMatch "fixme:scan:list" $ nil_ $ const do
fxs <- lift scanFiles
for_ fxs $ \fme -> do
liftIO $ print $ pretty fme
-- TODO: some-shit
-- one
-- TODO: some-shit
-- two
entry $ bindMatch "env:show" $ nil_ $ const $ do entry $ bindMatch "env:show" $ nil_ $ const $ do
lift printEnv lift printEnv
@ -334,87 +378,13 @@ runTop forms = do
co <- lift listCommits <&> fmap (mkStr @C . view _1) co <- lift listCommits <&> fmap (mkStr @C . view _1)
pure $ mkList co pure $ mkList co
entry $ bindMatch "git:refs" $ const do
refs <- lift $ listRefs False
elems <- for refs $ \(h,r) -> do
pure $ mkList @C [mkStr h, mkSym ".", mkStr r]
pure $ mkList elems
-- TODO: implement-fixme:refchan:export -- TODO: implement-fixme:refchan:export
entry $ bindMatch "fixme:refchan:export" $ nil_ \case entry $ bindMatch "fixme:refchan:export" $ nil_ \case
_ -> none _ -> none
-- TODO: implement-fixme:refchan:import
entry $ bindMatch "fixme:log:export" $ nil_ \case
[StringLike fn] -> do
lift $ exportToLog fn
_ -> throwIO $ BadFormException @C nil
entry $ bindMatch "fixme:log:import" $ nil_ \case
[StringLike fn] -> lift do
env <- ask
d <- readTVarIO tvd
importFromLog fn $ \ins -> do
void $ run d ins
updateIndexes
_ -> throwIO $ BadFormException @C nil
entry $ bindMatch "fixme:list:poor" $ nil_ $ const do
fme <- lift listFixmies
pure ()
entry $ bindMatch "deleted" $ nil_ $ \case
[TimeStampLike _, FixmeHashLike hash] -> lift do
trace $ red "deleted" <+> pretty hash
deleteFixme hash
_ -> pure ()
entry $ bindMatch "modified" $ nil_ $ \case
[TimeStampLike _, FixmeHashLike hash, StringLike a, StringLike b] -> do
trace $ red "modified!" <+> pretty hash <+> pretty a <+> pretty b
lift $ updateFixme Nothing hash (fromString a) (fromString b)
_ -> pure ()
entry $ bindMatch "delete" $ nil_ \case
[FixmeHashLike hash] -> lift $ delete hash
_ -> throwIO $ BadFormException @C nil
entry $ bindMatch "modify" $ nil_ \case
[FixmeHashLike hash, StringLike a, StringLike b] -> do
lift $ modify_ hash a b
_ -> throwIO $ BadFormException @C nil
entry $ bindMatch "fixme:stage:show" $ nil_ $ const do
stage <- lift selectStage
liftIO $ print $ vcat (fmap pretty stage)
entry $ bindMatch "fixme:state:drop" $ nil_ $ const do
lift cleanupDatabase
entry $ bindMatch "fixme:state:clean" $ nil_ $ const do
lift cleanupDatabase
entry $ bindMatch "fixme:stage:drop" $ nil_ $ const do
lift cleanStage
entry $ bindMatch "fixme:stage:clean" $ nil_ $ const do
lift cleanStage
entry $ bindMatch "fixme:config:path" $ const do
co <- localConfig
pure $ mkStr @C co
entry $ bindMatch "git:import" $ nil_ $ const do entry $ bindMatch "git:import" $ nil_ $ const do
lift $ scanGitLocal mempty Nothing error "not implemented yet"
-- lift $ scanGitLocal mempty Nothing
entry $ bindMatch "git:blobs" $ \_ -> do entry $ bindMatch "git:blobs" $ \_ -> do
blobs <- lift listRelevantBlobs blobs <- lift listRelevantBlobs
@ -436,11 +406,11 @@ runTop forms = do
notice $ "1. read refchan" <+> pretty (AsBase58 rchan) notice $ "1. read refchan" <+> pretty (AsBase58 rchan)
fxs <- lift $ selectFixmeThin () -- fxs <- lift $ selectFixmeThin ()
for_ fxs $ \(FixmeThin x) -> void $ runMaybeT do -- for_ fxs $ \(FixmeThin x) -> void $ runMaybeT do
h <- HM.lookup "fixme-hash" x & toMPlus -- h <- HM.lookup "fixme-hash" x & toMPlus
notice $ pretty h -- notice $ pretty h
notice "2. read issues from state" notice "2. read issues from state"
notice "3. discover new issues" notice "3. discover new issues"

View File

@ -20,7 +20,9 @@ import HBS2.Storage
import HBS2.Storage.Compact import HBS2.Storage.Compact
import HBS2.System.Dir import HBS2.System.Dir
import DBPipe.SQLite hiding (field) import DBPipe.SQLite hiding (field)
import Data.Config.Suckless import Data.Config.Suckless
import Data.Config.Suckless.Script.File
import Data.Aeson.Encode.Pretty as Aeson import Data.Aeson.Encode.Pretty as Aeson
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
@ -46,6 +48,8 @@ import Control.Monad.Trans.Cont
import Control.Monad.Trans.Maybe import Control.Monad.Trans.Maybe
import System.IO.Temp as Temp import System.IO.Temp as Temp
import System.IO qualified as IO import System.IO qualified as IO
import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)
import System.Directory (getModificationTime)
import Streaming.Prelude qualified as S import Streaming.Prelude qualified as S
@ -53,6 +57,8 @@ import Streaming.Prelude qualified as S
pattern IsSimpleTemplate :: forall {c} . [Syntax c] -> Syntax c pattern IsSimpleTemplate :: forall {c} . [Syntax c] -> Syntax c
pattern IsSimpleTemplate xs <- ListVal (SymbolVal "simple" : xs) pattern IsSimpleTemplate xs <- ListVal (SymbolVal "simple" : xs)
{- HLINT ignore "Functor law" -}
defaultTemplate :: HashMap Id FixmeTemplate defaultTemplate :: HashMap Id FixmeTemplate
defaultTemplate = HM.fromList [ ("default", Simple (SimpleTemplate short)) ] defaultTemplate = HM.fromList [ ("default", Simple (SimpleTemplate short)) ]
where where
@ -93,6 +99,7 @@ printEnv :: FixmePerks m => FixmeM m ()
printEnv = do printEnv = do
g <- asks fixmeEnvGitDir >>= readTVarIO g <- asks fixmeEnvGitDir >>= readTVarIO
masks <- asks fixmeEnvFileMask >>= readTVarIO masks <- asks fixmeEnvFileMask >>= readTVarIO
excl <- asks fixmeEnvFileExclude >>= readTVarIO
tags <- asks fixmeEnvTags >>= readTVarIO tags <- asks fixmeEnvTags >>= readTVarIO
days <- asks fixmeEnvGitScanDays >>= readTVarIO days <- asks fixmeEnvGitScanDays >>= readTVarIO
comments1 <- asks fixmeEnvDefComments >>= readTVarIO <&> HS.toList comments1 <- asks fixmeEnvDefComments >>= readTVarIO <&> HS.toList
@ -110,6 +117,9 @@ printEnv = do
for_ masks $ \m -> do for_ masks $ \m -> do
liftIO $ print $ "fixme-files" <+> dquotes (pretty m) liftIO $ print $ "fixme-files" <+> dquotes (pretty m)
for_ excl $ \m -> do
liftIO $ print $ "fixme-exclude" <+> dquotes (pretty m)
for_ days $ \d -> do for_ days $ \d -> do
liftIO $ print $ "fixme-git-scan-filter-days" <+> pretty d liftIO $ print $ "fixme-git-scan-filter-days" <+> pretty d
@ -144,182 +154,44 @@ printEnv = do
liftIO $ print $ parens ("define-macro" <+> pretty n <+> pretty syn) liftIO $ print $ parens ("define-macro" <+> pretty n <+> pretty syn)
exportToLog :: FixmePerks m => FilePath -> FixmeM m () scanFiles :: FixmePerks m => FixmeM m [Fixme]
exportToLog fn = do scanFiles = do
e <- getEpoch w <- fixmeWorkDir
warn $ red "EXPORT-FIXMIES" <+> pretty fn incl <- asks fixmeEnvFileMask >>= readTVarIO
sto <- compactStorageOpen @HbSync mempty fn excl <- asks fixmeEnvFileExclude >>= readTVarIO
fx <- selectFixmeThin ()
for_ fx $ \(FixmeThin m) -> void $ runMaybeT do
h <- HM.lookup "fixme-hash" m & toMPlus
loaded <- lift (selectFixme (coerce h)) >>= toMPlus
let what = Added e loaded
let k = mkKey what
get sto k >>= guard . isNothing
put sto (mkKey what) (LBS.toStrict $ serialise what)
warn $ red "export" <+> pretty h
what <- selectStage keys <- newTVarIO (mempty :: HashMap Text Integer)
for_ what $ \w -> do S.toList_ do
let k = mkKey w
v0 <- get sto k <&> fmap (deserialiseOrFail @CompactAction . LBS.fromStrict)
case v0 of
Nothing -> do
put sto k (LBS.toStrict $ serialise w)
Just (Left{}) -> do glob incl excl w $ \fn -> do
put sto k (LBS.toStrict $ serialise w)
Just (Right prev) | getSequence w > getSequence prev -> do ts <- liftIO $ getModificationTime fn <&> round . utcTimeToPOSIXSeconds
put sto k (LBS.toStrict $ serialise w)
_ -> pure () let fnShort = makeRelative w fn
compactStorageClose sto lbs <- liftIO (try @_ @IOException $ LBS.readFile fn)
<&> fromRight mempty
cleanStage fxs0 <- lift $ scanBlob (Just fn) lbs
for_ fxs0 $ \fme -> do
let key = fromString (fnShort <> "#") <> coerce (fixmeTitle fme) <> ":" :: Text
atomically $ modifyTVar keys (HM.insertWith (+) key 1)
no <- readTVarIO keys <&> HM.lookup key <&> fromMaybe 0
let keyText = key <> fromString (show no)
let keyHash = FixmeKey $ fromString $ show $ pretty $ hashObject @HbSync (serialise keyText)
let f2 = mempty { fixmeTs = Just (fromIntegral ts)
, fixmeKey = Just keyHash
, fixmeAttr = HM.fromList
[ ( "fixme-key-string", FixmeAttrVal keyText)
, ( "file", FixmeAttrVal (fromString fnShort))
]
, fixmePlain = fixmePlain fme
}
let fmeNew = (fme <> f2) & fixmeDerivedFields
S.yield fmeNew
pure True
sanitizeLog :: [Syntax c] -> [Syntax c]
sanitizeLog lls = flip filter lls $ \case
ListVal (SymbolVal "deleted" : _) -> True
ListVal (SymbolVal "modified" : _) -> True
_ -> False
importFromLog :: FixmePerks m
=> FilePath
-> ([Syntax C] -> FixmeM m ())
-> FixmeM m ()
importFromLog fn runIns = do
fset <- listAllFixmeHashes
sto <- compactStorageOpen @HbSync readonly fn
ks <- keys sto
toImport <- S.toList_ do
for_ ks $ \k -> runMaybeT do
v <- get sto k & MaybeT
what <- deserialiseOrFail @CompactAction (LBS.fromStrict v) & toMPlus
case what of
Added _ fx -> do
let ha = hashObject @HbSync (serialise fx) & HashRef
unless (HS.member ha fset) do
debug $ red "import" <+> viaShow (pretty ha)
lift $ S.yield (Right fx)
w -> lift $ S.yield (Left $ fromRight mempty $ parseTop (show $ pretty w))
withState $ transactional do
for_ (rights toImport) insertFixme
let w = lefts toImport
runIns (sanitizeLog $ mconcat w)
unless (List.null toImport) do
updateIndexes
compactStorageClose sto
list_ :: (FixmePerks m, HasPredicate a) => Maybe Id -> a -> FixmeM m ()
list_ tpl a = do
tpl <- asks fixmeEnvTemplates >>= readTVarIO
<&> HM.lookup (fromMaybe "default" tpl)
fixmies <- selectFixmeThin a
case tpl of
Nothing-> do
liftIO $ LBS.putStr $ Aeson.encodePretty fixmies
Just (Simple (SimpleTemplate simple)) -> do
for_ fixmies $ \(FixmeThin attr) -> do
let subst = [ (mkId k, mkStr @C v) | (k,v) <- HM.toList attr ]
let what = render (SimpleTemplate (inject subst simple))
& fromRight "render error"
liftIO $ hPutDoc stdout what
catFixmeMetadata :: FixmePerks m => Text -> FixmeM m ()
catFixmeMetadata = cat_ True
catFixme :: FixmePerks m => Text -> FixmeM m ()
catFixme = cat_ False
dumpFixme :: FixmePerks m => Text -> FixmeM m ()
dumpFixme hash = do
flip runContT pure do
mha <- lift $ selectFixmeHash hash
ha <- ContT $ maybe1 mha (pure ())
fme' <- lift $ selectFixme ha
liftIO $ print $ pretty fme'
cat_ :: FixmePerks m => Bool -> Text -> FixmeM m ()
cat_ metaOnly hash = do
(before,after) <- asks fixmeEnvCatContext >>= readTVarIO
gd <- fixmeGetGitDirCLIOpt
CatAction action <- asks fixmeEnvCatAction >>= readTVarIO
void $ flip runContT pure do
callCC \exit -> do
mha <- lift $ selectFixmeHash hash
ha <- ContT $ maybe1 mha (pure ())
fme' <- lift $ selectFixme ha
Fixme{..} <- ContT $ maybe1 fme' (pure ())
when metaOnly do
for_ (HM.toList fixmeAttr) $ \(k,v) -> do
liftIO $ print $ (pretty k <+> pretty v)
exit ()
let gh' = HM.lookup "blob" fixmeAttr
-- FIXME: define-fallback-action
gh <- ContT $ maybe1 gh' none
let cmd = [qc|git {gd} cat-file blob {pretty gh}|] :: String
let start = fromMaybe 0 fixmeStart & fromIntegral & (\x -> x - before) & max 0
let bbefore = if start > before then before + 1 else 1
let origLen = maybe 0 fromIntegral fixmeEnd - maybe 0 fromIntegral fixmeStart & max 1
let lno = max 1 $ origLen + after + before
let dict = [ (mkId k, mkStr @C v) | (k,v) <- HM.toList fixmeAttr ]
<>
[ (mkId (FixmeAttrName "before"), mkStr @C (FixmeAttrVal $ Text.pack $ show bbefore))
]
debug (pretty cmd)
w <- gitRunCommand cmd
<&> either (LBS8.pack . show) id
<&> LBS8.lines
<&> drop start
<&> take lno
liftIO $ action dict (LBS8.unlines w)
delete :: FixmePerks m => Text -> FixmeM m ()
delete txt = do
acts <- asks fixmeEnvUpdateActions >>= readTVarIO
hashes <- selectFixmeHashes txt
for_ hashes $ \ha -> do
insertFixmeDelStaged ha
modify_ :: FixmePerks m => Text -> String -> String -> FixmeM m ()
modify_ txt a b = do
acts <- asks fixmeEnvUpdateActions >>= readTVarIO
void $ runMaybeT do
ha <- toMPlus =<< lift (selectFixmeHash txt)
lift $ insertFixmeModStaged ha (fromString a) (fromString b)

View File

@ -1,5 +1,5 @@
{-# Language MultiWayIf #-} {-# Language MultiWayIf #-}
module Fixme.Scan (scanBlob,scanMagic) where module Fixme.Scan (scanBlob,scanMagic,updateScanMagic) where
import Fixme.Prelude hiding (indent) import Fixme.Prelude hiding (indent)
import Fixme.Types import Fixme.Types
@ -57,16 +57,23 @@ scanMagic :: FixmePerks m => FixmeM m HashRef
scanMagic = do scanMagic = do
env <- ask env <- ask
w <- atomically do w <- atomically do
tagz <- fixmeEnvTags env & readTVar tagz <- fixmeEnvTags env & readTVar
co <- fixmeEnvDefComments env & readTVar co <- fixmeEnvDefComments env & readTVar
fco <- fixmeEnvFileComments env & readTVar fco <- fixmeEnvFileComments env & readTVar
m <- fixmeEnvFileMask env & readTVar m <- fixmeEnvFileMask env & readTVar
e <- fixmeEnvFileExclude env & readTVar
a <- fixmeEnvAttribs env & readTVar a <- fixmeEnvAttribs env & readTVar
v <- fixmeEnvAttribValues env & readTVar v <- fixmeEnvAttribValues env & readTVar
pure $ serialise (tagz, co, fco, m, a, v) pure $ serialise (tagz, co, fco, m, e, a, v)
pure $ HashRef $ hashObject w pure $ HashRef $ hashObject w
updateScanMagic :: (FixmePerks m) => FixmeM m ()
updateScanMagic = do
t <- asks fixmeEnvScanMagic
magic <- scanMagic
atomically $ writeTVar t (Just magic)
scanBlob :: forall m . FixmePerks m scanBlob :: forall m . FixmePerks m
=> Maybe FilePath -- ^ filename to detect type => Maybe FilePath -- ^ filename to detect type
-> ByteString -- ^ content -> ByteString -- ^ content

View File

@ -115,27 +115,6 @@ listCommits = do
spec = sq <> delims " \t" spec = sq <> delims " \t"
listRefs :: FixmePerks m => Bool -> FixmeM m [(GitHash, GitRef)]
listRefs every = do
gd <- fixmeGetGitDirCLIOpt
gitRunCommand [qc|git {gd} show-ref --dereference|]
<&> fromRight mempty
<&> fmap LBS8.words . LBS8.lines
<&> mapMaybe
(\case
[h,b] -> (,) <$> fromStringMay @GitHash (LBS8.unpack h) <*> pure (GitRef (LBS8.toStrict b))
_ -> Nothing
)
>>= filterM filt
where
filt _ | every = pure True
filt (h,_) = do
done <- withState $ isProcessed $ ViaSerialise h
pure (not done)
listBlobs :: (FixmePerks m, MonadReader FixmeEnv m) => GitHash -> m [(FilePath,GitHash)] listBlobs :: (FixmePerks m, MonadReader FixmeEnv m) => GitHash -> m [(FilePath,GitHash)]
listBlobs co = do listBlobs co = do
gd <- fixmeGetGitDirCLIOpt gd <- fixmeGetGitDirCLIOpt
@ -166,60 +145,6 @@ filterBlobs xs = do
pat <- asks fixmeEnvFileMask >>= readTVarIO <&> fmap (True,) pat <- asks fixmeEnvFileMask >>= readTVarIO <&> fmap (True,)
filterBlobs0 pat xs filterBlobs0 pat xs
scanGitLogLocal :: FixmePerks m
=> FilePath
-> ( CompactStorage HbSync -> FixmeM m () )
-> FixmeM m ()
scanGitLogLocal refMask play = do
warn $ red "scanGitLogLocal" <+> pretty refMask
(t,refs) <- timeItT $ listRefs False
let hashes = fmap fst refs
warn $ yellow "listRefs in" <+> pretty (realToFrac t :: Fixed E6)
let pat = [(True, refMask)]
-- FIXME: use-cache-to-skip-already-processed-tips
logz <- withState do
S.toList_ $ for_ hashes $ \h -> do
done <- lift $ isProcessed (ViaSerialise h)
unless done do
blobs <- lift $ lift $ (listBlobs h >>= filterBlobs0 pat)
when (List.null blobs) do
lift $ insertProcessed (ViaSerialise h)
for_ blobs $ \(_,b) -> do
S.yield (h,b)
warn $ yellow "STEP 3" <+> "for each tree --- find log"
warn $ vcat (fmap pretty logz)
warn $ yellow "STEP 4" <+> "for each log --- scan log"
withState $ transactional do
flip runContT pure do
for_ logz $ \(commitHash, h) -> callCC \shit -> do
warn $ blue "SCAN BLOB" <+> pretty h
tmp <- ContT $ bracket (liftIO (emptySystemTempFile "fixme-log")) rm
blob <- lift $ lift $ gitCatBlob h
liftIO (LBS8.writeFile tmp blob)
esto <- lift $ try @_ @CompactStorageOpenError $ compactStorageOpen @HbSync readonly tmp
-- skip even problematic commit
lift $ insertProcessed (ViaSerialise commitHash)
either (const $ warn $ "skip malformed/unknown log" <+> pretty h) (const none) esto
sto <- either (const $ shit ()) pure esto
lift $ lift $ play sto
compactStorageClose sto
listRelevantBlobs :: FixmePerks m listRelevantBlobs :: FixmePerks m
=> FixmeM m [(FilePath, GitHash)] => FixmeM m [(FilePath, GitHash)]
listRelevantBlobs = do listRelevantBlobs = do
@ -265,219 +190,6 @@ listFixmies = do
pure mempty pure mempty
scanGitLocal :: FixmePerks m
=> [ScanGitArgs]
-> Maybe FilePath
-> FixmeM m ()
scanGitLocal args p = do
env <- ask
flip runContT pure do
(dbFn, _) <- ContT $ withSystemTempFile "fixme-db" . curry
tempDb <- newDBPipeEnv dbPipeOptsDef dbFn
withDB tempDb do
ddl [qc| create table co
( cohash text not null
, ts int null
, primary key (cohash)
)
|]
ddl [qc| create table coattr
( cohash text not null
, name text not null
, value text not null
, primary key (cohash,name)
)
|]
ddl [qc| create table blob
( hash text not null
, cohash text not null
, path text not null
, primary key (hash,cohash,path)
)
|]
-- update_ [qc|ATTACH DATABASE '{dbpath}' as fixme|]
let onlyNewCommits xs
| ScanAllCommits `elem` args = pure xs
| otherwise = lift $ filterM (newCommit . view _1) xs
co <- lift listCommits >>= onlyNewCommits
lift do
withDB tempDb $ transactional do
for_ co $ \(commit, attr) -> do
debug $ "commit" <+> pretty commit
blobs <- lift $ listBlobs commit >>= withFixmeEnv env . filterBlobs
let ts = HM.lookup "commit-time" attr
>>= readMay @Word64 . Text.unpack . coerce
insert [qc|
insert into co (cohash,ts) values (?,?) on conflict (cohash) do nothing
|] (commit,ts)
for_ (HM.toList attr) $ \(a,b) -> do
insert [qc|
insert into coattr(cohash,name,value) values(?,?,?)
on conflict (cohash,name) do nothing
|] (commit,a,b)
for_ blobs $ \(fp,h) -> do
insert [qc| insert into blob (hash,cohash,path)
values (?,?,?)
on conflict (hash,cohash,path) do nothing
|] (h,commit,fp)
blobs <- withDB tempDb do
select_ @_ @(GitHash, FilePath) [qc|select distinct hash, path from blob order by path|]
when ( PrintBlobs `elem` args ) do
for_ blobs $ \(h,fp) -> do
notice $ pretty h <+> pretty fp
callCC \fucked -> do
gitCat <- ContT $ bracket startGitCatFile (hClose . getStdin)
let ssin = getStdin gitCat
let ssout = getStdout gitCat
liftIO $ IO.hSetBuffering ssin LineBuffering
for_ blobs $ \(h,fp) -> callCC \next -> do
seen <- lift (withState $ selectObjectHash h) <&> isJust
when seen do
trace $ red "ALREADY SEEN BLOB" <+> pretty h
next ()
liftIO $ IO.hPrint ssin (pretty h) >> IO.hFlush ssin
prefix <- liftIO (BS.hGetLine ssout) <&> BS.words
case prefix of
[bh, "blob", ssize] -> do
let mslen = readMay @Int (BS.unpack ssize)
len <- ContT $ maybe1 mslen (pure ())
blob <- liftIO $ LBS8.hGet ssout len
void $ liftIO $ BS.hGetLine ssout
poor <- lift (Scan.scanBlob (Just fp) blob)
rich <- withDB tempDb do
let q = [qc|
WITH CommitAttributes AS (
SELECT co.cohash, co.ts, coattr.name, coattr.value
FROM co
JOIN coattr ON co.cohash = coattr.cohash
),
MinCommitTimes AS (
SELECT blob.hash, MIN(co.ts) as mintime
FROM blob
JOIN co ON blob.cohash = co.cohash
WHERE co.ts IS NOT NULL
GROUP BY blob.hash
),
RelevantCommits AS (
SELECT blob.hash, blob.cohash, blob.path
FROM blob
JOIN MinCommitTimes ON blob.hash = MinCommitTimes.hash
JOIN co ON blob.cohash = co.cohash AND co.ts = MinCommitTimes.mintime
)
SELECT CommitAttributes.name, CommitAttributes.value
FROM RelevantCommits
JOIN CommitAttributes ON RelevantCommits.cohash = CommitAttributes.cohash
WHERE RelevantCommits.hash = ?
|]
what <- select @(FixmeAttrName,FixmeAttrVal) q (Only h)
<&> HM.fromList
<&> (<> HM.fromList [ ("blob",fromString $ show (pretty h))
, ("file",fromString fp)
])
for poor $ \f -> do
let lno = maybe mempty ( HM.singleton "line"
. FixmeAttrVal
. Text.pack
. show
)
(fixmeStart f)
let ts = HM.lookup "commit-time" what
<&> Text.unpack . coerce
>>= readMay
<&> FixmeTimestamp
pure $ set (field @"fixmeTs") ts $ over (field @"fixmeAttr") (<> (what <> lno)) f
let fxpos1 = [ (fixmeTitle fx, [i :: Int])
| (i,fx) <- zip [0..] rich
-- , fixmeTitle fx /= mempty
] & Map.fromListWith (flip (<>))
let mt e = do
let seed = [ (fst e, i) | i <- snd e ]
flip fix (0,[],seed) $ \next (num,acc,rest) ->
case rest of
[] -> acc
(x:xs) -> next (succ num, (x,num) : acc, xs)
let fxpos2 = [ mt e
| e <- Map.toList fxpos1
] & mconcat
& Map.fromList
fixmies <- for (zip [0..] rich) $ \(i,fx) -> do
let title = fixmeTitle fx
let kb = Map.lookup (title,i) fxpos2
let ka = HM.lookup "file" (fixmeAttr fx)
let kk = (,,) <$> ka <*> pure title <*> kb
case kk of
Nothing -> pure fx
Just (a,b,c) -> do
let ks = [qc|{show (pretty a)}#{show (pretty b)}:{show c}|] :: Text
let ksh = hashObject @HbSync (serialise ks) & pretty & show & Text.pack & FixmeAttrVal
let kh = HM.singleton "fixme-key" ksh
let kv = HM.singleton "fixme-key-string" (FixmeAttrVal ks) <> kh
pure $ over (field @"fixmeAttr") (<> kv) fx
when ( PrintFixme `elem` args ) do
for_ fixmies $ \fixme -> do
notice $ pretty fixme
when ( ScanRunDry `elem` args ) $ fucked ()
debug $ "actually-import-fixmies" <+> pretty h
lift $ withFixmeEnv env $ withState $ transactional do
insertBlob h
for_ fixmies insertFixme
_ -> fucked ()
unless ( ScanRunDry `elem` args ) do
lift runLogActions
lift $ withFixmeEnv env $ withState $ transactional do
for_ co $ \w -> do
insertCommit (view _1 w)
gitListStage :: (FixmePerks m) gitListStage :: (FixmePerks m)
=> FixmeM m [Either (FilePath, GitHash) (FilePath, GitHash)] => FixmeM m [Either (FilePath, GitHash) (FilePath, GitHash)]
@ -602,16 +314,6 @@ gitExtractFileMetaData fns = do
pure $ HM.fromList [ (view _1 w, view _3 w) | w <- rich ] pure $ HM.fromList [ (view _1 w, view _3 w) | w <- rich ]
-- TODO: move-outta-here
runLogActions :: FixmePerks m => FixmeM m ()
runLogActions = do
debug $ yellow "runLogActions"
actions <- asks fixmeEnvReadLogActions >>= readTVarIO
for_ actions $ \(ReadLogAction a) -> do
liftIO (a (List noContext []))
updateIndexes
data GitBlobInfo = GitBlobInfo FilePath GitHash data GitBlobInfo = GitBlobInfo FilePath GitHash
deriving stock (Eq,Ord,Data,Generic,Show) deriving stock (Eq,Ord,Data,Generic,Show)

View File

@ -3,30 +3,10 @@
module Fixme.State module Fixme.State
( evolve ( evolve
, withState , withState
, insertFixme
, selectFixmeThin
, selectFixmeHash
, selectFixmeHashes
, selectFixme
, deleteFixme
, updateFixme
, insertCommit
, insertBlob
, selectObjectHash
, newCommit
, cleanupDatabase , cleanupDatabase
, updateIndexes , insertFixme
, insertFixmeDelStaged , insertScanned
, insertFixmeModStaged , selectIsAlreadyScanned
, selectStageModified
, selectStageDeleted
, selectStage
, cleanStage
, insertProcessed
, isProcessed
, selectProcessed
, checkFixmeExists
, listAllFixmeHashes
, HasPredicate(..) , HasPredicate(..)
, SelectPredicate(..) , SelectPredicate(..)
) where ) where
@ -57,6 +37,8 @@ import Control.Monad.Trans.Maybe
import Data.Coerce import Data.Coerce
import Data.Fixed import Data.Fixed
import Data.Word (Word64) import Data.Word (Word64)
import System.Directory (getModificationTime)
import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)
import System.TimeIt import System.TimeIt
-- TODO: runPipe-omitted -- TODO: runPipe-omitted
@ -123,235 +105,20 @@ withState what = do
createTables :: FixmePerks m => DBPipeM m () createTables :: FixmePerks m => DBPipeM m ()
createTables = do createTables = do
-- тут все таблицы будут называться с префиксом
-- fixme, что бы может быть можно было встроить
-- в другую бд, если вдруг понадобится
ddl [qc| ddl [qc| create table if not exists scanned
create table if not exists fixmegitobject ( hash text not null primary key )
( hash text not null
, type text null
, primary key (hash)
)
|]
ddl [qc|
create table if not exists fixme
( id text not null
, ts integer
, fixme blob not null
, primary key (id)
)
|]
ddl [qc|
create table if not exists fixmedeleted
( id text not null
, ts integer not null
, deleted bool not null
, primary key (id,ts)
)
|]
ddl [qc|
create table if not exists fixmerel
( origin text not null
, related text not null
, ts integer not null
, reason text not null
, primary key (origin,related,ts)
)
|]
ddl [qc|
create table if not exists fixmeattr
( fixme text not null
, ts integer null
, name text not null
, value text
, primary key (fixme,ts,name)
)
|]
ddl [qc| drop view if exists fixmeattrview |]
let commits = [qc|name in ('commit','committer','committer-name','committer-email','commit-time')|] :: Text
ddl [qc|
create view fixmeattrview as
with ranked1 as (
select
fixme,
name,
value,
row_number() over (partition by fixme, name order by ts desc nulls first) as rn
from fixmeattr
where not ({commits})
)
, ranked2 as (
select
fixme,
name,
value,
row_number() over (partition by fixme, name order by ts asc nulls last) as rn
from fixmeattr
where ({commits})
)
select distinct fixme,name,value
from
(
select
fixme,
name,
value
from ranked1
where rn = 1
union
select
fixme,
name,
value
from ranked2
where rn = 1
)
|]
ddl [qc|drop view if exists fixmeactualview|]
ddl [qc|
create view fixmeactualview as
with a1 as (
select
a.fixme,
f.ts,
a.name,
a.value
from
fixmeattrview a
join fixme f on a.fixme = f.id
where
a.name = 'fixme-key'
and not exists (select null from fixmedeleted d where d.id = f.id)
),
rn AS (
select
f.id,
f.ts,
a.value AS fixmekey,
row_number() over (partition by a.value order by f.ts desc) as rn
from
fixme f
join a1 a on f.id = a.fixme and a.name = 'fixme-key'
)
select id as fixme, fixmekey, ts from rn
where rn = 1
and not exists (
select null
from fixmeattr a
join fixmedeleted d on d.id = a.fixme
where a.name = 'fixme-key'
and a.value = rn.fixmekey
)
|]
ddl [qc|
create table if not exists fixmeactual
( fixme text not null
, primary key (fixme)
)
|]
ddl [qc|
create table if not exists fixmejson
( fixme text not null
, fixmekey text
, json blob
, primary key (fixme)
)
|]
ddl [qc|
create index if not exists idx_fixmekey ON fixmejson(fixmekey)
|]
ddl [qc| create table if not exists fixmestagedel
( hash text not null primary key
, ts integer not null
)
|] |]
ddl [qc| create table if not exists fixmestagemod ddl [qc| create table if not exists object
( hash text not null ( o text not null
, ts integer not null , w integer not null
, attr text not null , k text not null
, value text , v blob not null
, primary key (hash,attr) , primary key (o,k)
) )
|] |]
ddl [qc| create table if not exists fixmeprocessed
( hash text not null
, primary key (hash)
)
|]
-- .fixme-new/state.db
-- and not exists (select null from fixmedeleted d where a.fixme = id limit 1)
insertCommit :: FixmePerks m => GitHash -> DBPipeM m ()
insertCommit gh = do
insert [qc|
insert into fixmegitobject (hash,type) values(?,'commit')
on conflict (hash) do nothing
|] (Only gh)
insertBlob :: FixmePerks m => GitHash -> DBPipeM m ()
insertBlob gh = do
insert [qc|
insert into fixmegitobject (hash,type) values(?,'blob')
on conflict (hash) do nothing
|] (Only gh)
selectObjectHash :: FixmePerks m => GitHash -> DBPipeM m (Maybe GitHash)
selectObjectHash gh = do
select [qc|select hash from fixmegitobject where hash = ?|] (Only gh)
<&> fmap fromOnly . listToMaybe
newCommit :: (FixmePerks m, MonadReader FixmeEnv m) => GitHash -> m Bool
newCommit gh = isNothing <$> withState (selectObjectHash gh)
insertFixme :: FixmePerks m => Fixme -> DBPipeM m ()
insertFixme fx@Fixme{..} = do
let fixme = serialise fx
let fxId = hashObject @HbSync fixme & HashRef
insert [qc|insert into fixme (id, ts, fixme) values (?,?,?)
on conflict(id) do nothing
|] (fxId, fixmeTs, fixme)
for_ (HM.toList fixmeAttr) $ \(n,v) -> do
insert [qc|
insert into fixmeattr(fixme,ts,name,value)
values (?,?,?,?)
on conflict (fixme,ts,name) do update set value = excluded.value
|] (fxId, fixmeTs, n, v)
insert [qc|
insert into fixmeattr(fixme,ts,name,value)
values (?,?,?,?)
on conflict (fixme,ts,name) do update set value = excluded.value
|] (fxId, fixmeTs, "fixme-tag", fixmeTag)
insert [qc|
insert into fixmeattr(fixme,ts,name,value)
values (?,?,?,?)
on conflict (fixme,ts,name) do update set value = excluded.value
|] (fxId, fixmeTs, "fixme-title", fixmeTitle)
data SelectPredicate = data SelectPredicate =
All All
@ -410,50 +177,6 @@ instance IsContext c => HasPredicate [Syntax c] where
{- HLINT ignore "Functor law" -} {- HLINT ignore "Functor law" -}
{- HLINT ignore "Eta reduce" -} {- HLINT ignore "Eta reduce" -}
selectFixmeHash :: (FixmePerks m) => Text -> FixmeM m (Maybe Text)
selectFixmeHash what = listToMaybe <$> selectFixmeHashes what
selectFixmeHashes :: (FixmePerks m) => Text -> FixmeM m [Text]
selectFixmeHashes what = withState do
let w = what <> "%"
select @(Only Text)
[qc| select fixme
from fixmejson
where json_extract(json,'$."fixme-key"') like ?
union
select id
from fixme
where id like ?
|] (w,w)
<&> fmap fromOnly
selectFixme :: FixmePerks m => Text -> FixmeM m (Maybe Fixme)
selectFixme txt = do
attrs <- selectFixmeThin (FixmeHashExactly txt)
<&> fmap coerce . headMay
<&> fromMaybe mempty
runMaybeT do
lift (withState $ select [qc|select fixme from fixme where id = ? limit 1|] (Only txt))
<&> listToMaybe . fmap fromOnly
>>= toMPlus
<&> (deserialiseOrFail @Fixme)
>>= toMPlus
<&> over (field @"fixmeAttr") (<> attrs)
listAllFixmeHashes :: (FixmePerks m, MonadReader FixmeEnv m) => m (HashSet HashRef)
listAllFixmeHashes = withState do
select_ @_ @(Only HashRef) [qc|select id from fixme|]
<&> HS.fromList . fmap fromOnly
checkFixmeExists :: FixmePerks m => HashRef -> FixmeM m Bool
checkFixmeExists what = withState do
select @(Only (Maybe Int)) [qc|select 1 from fixme where id = ? limit 1|] (Only what)
<&> not . List.null
data Bound = forall a . (ToField a, Show a) => Bound a data Bound = forall a . (ToField a, Show a) => Bound a
instance ToField Bound where instance ToField Bound where
@ -497,215 +220,70 @@ genPredQ tbl what = go what
Ignored -> ("false", mempty) Ignored -> ("false", mempty)
updateFixmeJson :: FixmePerks m => DBPipeM m ()
updateFixmeJson = do
update_ [qc|
insert into fixmejson (fixme,fixmekey,json)
with json as (
select
a.fixme as fixme,
cast(json_set(json_group_object(a.name,a.value), '$."fixme-hash"', f.fixme) as blob) as json
from
fixmeattrview a join fixmeactual f on f.fixme = a.fixme
group by a.fixme
)
select
fixme
, json_extract(json, '$."fixme-key"') as fixmekey
, json
from json where true
on conflict (fixme) do update set json = excluded.json, fixmekey = excluded.fixmekey
|]
-- TODO: predicate-for-stage-toggle
selectFixmeThin :: (FixmePerks m, HasPredicate a) => a -> FixmeM m [FixmeThin]
selectFixmeThin a = withState do
let predic = genPredQ "blob" (predicate a)
let emptyObect = [q|'{}'|] :: String
let sql = [qc|
with s1 as (
select m.hash as hash
, cast(json_group_object(m.attr,m.value) as blob) as json
from fixmestagemod m
where not exists (select null from fixmestagedel d where d.hash = m.hash)
),
s2 as
( select cast(json_patch(j.json, coalesce(s.json,{emptyObect})) as blob) as blob, j.fixme as fixme
from
fixmejson j join fixmeactual f on f.fixme = j.fixme
join fixme f0 on f0.id = f.fixme
left join s1 s on s.hash = j.fixme
)
select s2.blob from s2
where
(
{fst predic}
)
order by json_extract(blob, '$.commit-time'), json_extract(blob, '$.title')
|]
trace $ red "selectFixmeThin" <> line <> pretty sql
(t,r) <- timeItT $ select sql (snd predic) <&> mapMaybe (Aeson.decode @FixmeThin . fromOnly)
trace $ yellow "selectFixmeThin" <> line
<> pretty sql <> line
<> pretty (length r) <+> "rows" <> line
<> pretty "elapsed" <+> pretty (realToFrac t :: Fixed E6)
pure r
cleanupDatabase :: (FixmePerks m, MonadReader FixmeEnv m) => m () cleanupDatabase :: (FixmePerks m, MonadReader FixmeEnv m) => m ()
cleanupDatabase = do cleanupDatabase = do
warn $ red "cleanupDatabase" warn $ red "cleanupDatabase"
withState $ transactional do withState $ transactional do
update_ [qc|delete from fixme|] update_ [qc|delete from object|]
update_ [qc|delete from fixmeattr|] update_ [qc|delete from scanned|]
update_ [qc|delete from fixmegitobject|]
update_ [qc|delete from fixmedeleted|] scannedKey :: (FixmePerks m, MonadReader FixmeEnv m) => Fixme -> m HashRef
update_ [qc|delete from fixmerel|] scannedKey fme = do
update_ [qc|delete from fixmeactual|] magic <- asks fixmeEnvScanMagic >>= readTVarIO
update_ [qc|delete from fixmejson|] let file = fixmeAttr fme & HM.lookup "file"
update_ [qc|delete from fixmestagedel|] let w = fixmeTs fme
update_ [qc|delete from fixmestagemod|] pure $ hashObject @HbSync ( serialise (magic,w,file) ) & HashRef
scannedKeyForFile :: (FixmePerks m, MonadReader FixmeEnv m) => FilePath-> m HashRef
insertFixmeModStaged :: (FixmePerks m,MonadReader FixmeEnv m) scannedKeyForFile file = do
=> Text dir <- fixmeWorkDir
-> FixmeAttrName magic <- asks fixmeEnvScanMagic >>= readTVarIO
-> FixmeAttrVal let fn = dir </> file
-> m () w <- liftIO $ getModificationTime fn <&> round . utcTimeToPOSIXSeconds
insertFixmeModStaged hash k v = withState do pure $ hashObject @HbSync ( serialise (magic,w,file) ) & HashRef
ts <- getEpoch
insert [qc| insert into fixmestagemod (hash,ts,attr,value) values(?,?,?,?) selectIsAlreadyScanned :: (FixmePerks m, MonadReader FixmeEnv m) => FilePath -> m Bool
on conflict (hash,attr) selectIsAlreadyScanned file = withState do
do update set hash = excluded.hash k <- lift $ scannedKeyForFile file
, ts = excluded.ts what <- select @(Only Int) [qc|select 1 from scanned where hash = ? limit 1|] (Only k)
, attr = excluded.attr pure $ not $ List.null what
, value = excluded.value
|] (hash,ts,k,v) insertScanned :: (FixmePerks m, MonadReader FixmeEnv m) => FilePath -> DBPipeM m ()
insertScanned file = do
k <- lift $ scannedKeyForFile file
insertFixmeDelStaged :: (FixmePerks m,MonadReader FixmeEnv m) => Text -> m () insert [qc| insert into scanned (hash)
insertFixmeDelStaged hash = withState do values(?)
ts <- getEpoch on conflict (hash) do nothing|]
insert [qc| insert into fixmestagedel (hash,ts) values(?,?) (Only k)
on conflict (hash)
do update set hash = excluded.hash insertFixme :: (FixmePerks m, MonadReader FixmeEnv m) => Fixme -> DBPipeM m ()
, ts = excluded.ts insertFixme fme = do
|] (hash,ts)
void $ runMaybeT do
type StageModRow = (HashRef,Word64,Text,Text) o <- fixmeKey fme & toMPlus
w <- fixmeTs fme & toMPlus
selectStageModified :: (FixmePerks m,MonadReader FixmeEnv m) => m [CompactAction] let attrs = fixmeAttr fme
selectStageModified = withState do let txt = fixmePlain fme & Text.unlines . fmap coerce
what <- select_ @_ @StageModRow [qc|select hash,ts,attr,value from fixmestagemod|]
for what $ \(h,t,k,v) -> do let sql = [qc|
pure $ Modified t h (FixmeAttrName k) (FixmeAttrVal v) insert into object (o, w, k, v)
values (?, ?, ?, ?)
selectStageDeleted :: (FixmePerks m,MonadReader FixmeEnv m) => m [CompactAction] on conflict (o, k)
selectStageDeleted = withState do do update set
what <- select_ @_ @(HashRef,Word64) [qc|select hash,ts from fixmestagedel|] v = case
for what $ \(h,t) -> do when excluded.w > object.w and (excluded.v <> object.v) then excluded.v
pure $ Deleted t h else object.v
end,
selectStage :: (FixmePerks m,MonadReader FixmeEnv m) => m [CompactAction] w = case
selectStage = do when excluded.w > object.w and (excluded.v <> object.v) then excluded.v
a <- selectStageModified else object.w
b <- selectStageDeleted end
pure (a<>b) |]
cleanStage :: (FixmePerks m,MonadReader FixmeEnv m) => m () for_ (HM.toList attrs) $ \(k,v) -> do
cleanStage = withState do lift $ insert sql (o,w,k,v)
transactional do
update_ [qc|delete from fixmestagedel|] lift $ insert sql (o,w,"fixme-text",txt)
update_ [qc|delete from fixmestagemod|]
deleteFixme :: (FixmePerks m,MonadReader FixmeEnv m) => Text -> m ()
deleteFixme hash = withState do
trace $ red "deleteFixme" <+> pretty hash
here <- select [qc| select true
from fixmedeleted
where deleted and id = ?
order by ts desc
limit 1
|] (Only hash) <&> isJust . listToMaybe . fmap (fromOnly @Bool)
unless here do
insert [qc| insert into fixmedeleted (id,ts,deleted)
values (?,(strftime('%s', 'now')),true)
on conflict(id,ts) do nothing
|] (Only hash)
updateFixme :: (FixmePerks m,MonadReader FixmeEnv m)
=> Maybe FixmeTimestamp
-> Text
-> FixmeAttrName
-> FixmeAttrVal
-> m ()
updateFixme ts hash a b = withState do
warn $ red "updateFixme" <+> pretty hash
insert [qc| insert into fixmeattr (fixme,ts,name,value)
values (?,coalesce(?,strftime('%s', 'now')),?,?)
on conflict(fixme,ts,name) do update set value = excluded.value
|] (hash,ts,a,b)
updateIndexes :: (FixmePerks m, MonadReader FixmeEnv m) => m ()
updateIndexes = withState $ transactional do
update_ [qc|delete from fixmeactual|]
update_ [qc|
insert into fixmeactual
select distinct fixme from fixmeactualview
|]
updateFixmeJson
-- FIXME: delete-table-grows
-- надо добавлять статус в fixmedeleted
-- только если он отличается от последнего
-- известного статуса
update_ [qc|delete from fixmejson where fixme in (select distinct id from fixmedeleted)|]
insertProcessed :: (FixmePerks m, MonadReader FixmeEnv m, Hashed HbSync w)
=> w
-> DBPipeM m ()
insertProcessed what = do
insert [qc| insert into fixmeprocessed (hash) values(?)
on conflict (hash) do nothing
|] (Only (show $ pretty $ hashObject @HbSync what))
isProcessed :: (FixmePerks m, MonadReader FixmeEnv m, Hashed HbSync w)
=> w
-> DBPipeM m Bool
isProcessed what = do
let k = show $ pretty $ hashObject @HbSync what
select @(Only (Maybe Int)) [qc| select null from fixmeprocessed where hash = ? limit 1 |] (Only k)
<&> isJust . listToMaybe
selectProcessed :: (FixmePerks m, MonadReader FixmeEnv m)
=> m [HashRef]
selectProcessed = withState do
select_ [qc|select hash from fixmeprocessed|]
<&> fmap fromOnly

View File

@ -126,7 +126,7 @@ newtype FixmeTimestamp = FixmeTimestamp Word64
newtype FixmeKey = FixmeKey Text newtype FixmeKey = FixmeKey Text
deriving newtype (Eq,Ord,Show,ToField,FromField) deriving newtype (Eq,Ord,Show,ToField,FromField,Pretty)
deriving stock (Data,Generic) deriving stock (Data,Generic)
newtype FixmeOffset = FixmeOffset Word32 newtype FixmeOffset = FixmeOffset Word32
@ -281,12 +281,14 @@ data FixmeEnv =
, fixmeEnvDb :: TVar (Maybe DBPipeEnv) , fixmeEnvDb :: TVar (Maybe DBPipeEnv)
, fixmeEnvGitDir :: TVar (Maybe FilePath) , fixmeEnvGitDir :: TVar (Maybe FilePath)
, fixmeEnvFileMask :: TVar [FilePattern] , fixmeEnvFileMask :: TVar [FilePattern]
, fixmeEnvFileExclude :: TVar [FilePattern]
, fixmeEnvTags :: TVar (HashSet FixmeTag) , fixmeEnvTags :: TVar (HashSet FixmeTag)
, fixmeEnvAttribs :: TVar (HashSet FixmeAttrName) , fixmeEnvAttribs :: TVar (HashSet FixmeAttrName)
, fixmeEnvAttribValues :: TVar (HashMap FixmeAttrName (HashSet FixmeAttrVal)) , fixmeEnvAttribValues :: TVar (HashMap FixmeAttrName (HashSet FixmeAttrVal))
, fixmeEnvDefComments :: TVar (HashSet Text) , fixmeEnvDefComments :: TVar (HashSet Text)
, fixmeEnvFileComments :: TVar (HashMap FilePath (HashSet Text)) , fixmeEnvFileComments :: TVar (HashMap FilePath (HashSet Text))
, fixmeEnvGitScanDays :: TVar (Maybe Integer) , fixmeEnvGitScanDays :: TVar (Maybe Integer)
, fixmeEnvScanMagic :: TVar (Maybe HashRef)
, fixmeEnvUpdateActions :: TVar [UpdateAction] , fixmeEnvUpdateActions :: TVar [UpdateAction]
, fixmeEnvReadLogActions :: TVar [ReadLogAction] , fixmeEnvReadLogActions :: TVar [ReadLogAction]
, fixmeEnvCatAction :: TVar CatAction , fixmeEnvCatAction :: TVar CatAction
@ -345,8 +347,10 @@ fixmeEnvBare =
<*> newTVarIO mempty <*> newTVarIO mempty
<*> newTVarIO mempty <*> newTVarIO mempty
<*> newTVarIO mempty <*> newTVarIO mempty
<*> newTVarIO mempty
<*> newTVarIO defCommentMap <*> newTVarIO defCommentMap
<*> newTVarIO Nothing <*> newTVarIO Nothing
<*> newTVarIO mzero
<*> newTVarIO mempty <*> newTVarIO mempty
<*> newTVarIO mempty <*> newTVarIO mempty
<*> newTVarIO (CatAction $ \_ _ -> pure ()) <*> newTVarIO (CatAction $ \_ _ -> pure ())
@ -631,7 +635,7 @@ fixmeAttrNonEmpty a b = case (coerce a :: Text, coerce b :: Text) of
(_,_) -> b (_,_) -> b
fixmeDerivedFields :: Fixme -> Fixme fixmeDerivedFields :: Fixme -> Fixme
fixmeDerivedFields fx = fx <> fxCo <> tag <> fxLno <> fxMisc fixmeDerivedFields fx = fx <> fxKey <> fxCo <> tag <> fxLno <> fxMisc
where where
email = HM.lookup "commiter-email" (fixmeAttr fx) email = HM.lookup "commiter-email" (fixmeAttr fx)
& maybe mempty (\x -> " <" <> x <> ">") & maybe mempty (\x -> " <" <> x <> ">")
@ -641,6 +645,10 @@ fixmeDerivedFields fx = fx <> fxCo <> tag <> fxLno <> fxMisc
tag = mempty { fixmeAttr = HM.singleton "fixme-tag" (FixmeAttrVal (coerce $ fixmeTag fx)) } tag = mempty { fixmeAttr = HM.singleton "fixme-tag" (FixmeAttrVal (coerce $ fixmeTag fx)) }
key = maybe mempty ( HM.singleton "fixme-key" . FixmeAttrVal . coerce) (fixmeKey fx)
fxKey = mempty { fixmeAttr = key }
lno = succ <$> fixmeStart fx <&> FixmeAttrVal . fromString . show lno = succ <$> fixmeStart fx <&> FixmeAttrVal . fromString . show
fxLno = mempty { fixmeAttr = maybe mempty (HM.singleton "line") lno } fxLno = mempty { fixmeAttr = maybe mempty (HM.singleton "line") lno }

View File

@ -193,11 +193,11 @@
"rev": "5a55c22750589b357e50b759d2a754df058446d6", "rev": "5a55c22750589b357e50b759d2a754df058446d6",
"revCount": 40, "revCount": 40,
"type": "git", "type": "git",
"url": "http://git.hbs2.net/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA" "url": "https://git.hbs2.net/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA"
}, },
"original": { "original": {
"type": "git", "type": "git",
"url": "http://git.hbs2.net/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA" "url": "https://git.hbs2.net/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA"
} }
}, },
"fuzzy_2": { "fuzzy_2": {

View File

@ -24,7 +24,7 @@ inputs = {
lsm.url = "git+https://git.hbs2.net/5BCaH95cWsVKBmWaDNLWQr2umxzzT5kqRRKNTm2J15Ls"; lsm.url = "git+https://git.hbs2.net/5BCaH95cWsVKBmWaDNLWQr2umxzzT5kqRRKNTm2J15Ls";
lsm.inputs.nixpkgs.follows = "nixpkgs"; lsm.inputs.nixpkgs.follows = "nixpkgs";
fuzzy.url = "git+http://git.hbs2.net/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA"; fuzzy.url = "git+https://git.hbs2.net/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA";
fuzzy.inputs.nixpkgs.follows = "nixpkgs"; fuzzy.inputs.nixpkgs.follows = "nixpkgs";
saltine = { saltine = {
@ -105,8 +105,6 @@ outputs = { self, nixpkgs, haskell-flake-utils, ... }@inputs:
]; ];
shellWithHoogle = true;
shell = {pkgs, ...}: shell = {pkgs, ...}:
pkgs.haskellPackages.shellFor { pkgs.haskellPackages.shellFor {
packages = _: pkgs.lib.attrsets.attrVals packageNames pkgs.haskellPackages; packages = _: pkgs.lib.attrsets.attrVals packageNames pkgs.haskellPackages;

View File

@ -57,6 +57,7 @@ touch what = do
pwd :: MonadIO m => m FilePath pwd :: MonadIO m => m FilePath
pwd = liftIO D.getCurrentDirectory pwd = liftIO D.getCurrentDirectory
doesPathExist :: MonadIO m => FilePath -> m Bool doesPathExist :: MonadIO m => FilePath -> m Bool
doesPathExist = liftIO . D.doesPathExist doesPathExist = liftIO . D.doesPathExist