mirror of https://github.com/voidlizard/hbs2
wip
This commit is contained in:
parent
54443b4189
commit
60fde0e948
|
|
@ -7,7 +7,7 @@ import System.Environment
|
||||||
-- TODO: fixme-new
|
-- TODO: fixme-new
|
||||||
-- после майских:
|
-- после майских:
|
||||||
-- 1. fixme переезжает в дерево hbs2, конкретно в hbs2-git
|
-- 1. fixme переезжает в дерево hbs2, конкретно в hbs2-git
|
||||||
--
|
-- ??
|
||||||
-- 2. fixme преобразуется в утилиту для генерации отчётов по репозиторию git
|
-- 2. fixme преобразуется в утилиту для генерации отчётов по репозиторию git
|
||||||
--
|
--
|
||||||
-- 3. fixme генерирует поток фактов про репозиторий git, включая записи todo/fixme
|
-- 3. fixme генерирует поток фактов про репозиторий git, включая записи todo/fixme
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ common shared-properties
|
||||||
, exceptions
|
, exceptions
|
||||||
, filepath
|
, filepath
|
||||||
, filepattern
|
, filepattern
|
||||||
|
, generic-lens
|
||||||
, interpolatedstring-perl6
|
, interpolatedstring-perl6
|
||||||
, memory
|
, memory
|
||||||
, microlens-platform
|
, microlens-platform
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import Prelude hiding (init)
|
||||||
import Fixme.Prelude hiding (indent)
|
import Fixme.Prelude hiding (indent)
|
||||||
import Fixme.Types
|
import Fixme.Types
|
||||||
import Fixme.Scan.Git as Git
|
import Fixme.Scan.Git as Git
|
||||||
|
import Fixme.Scan as Scan
|
||||||
|
|
||||||
import HBS2.Git.Local.CLI
|
import HBS2.Git.Local.CLI
|
||||||
|
|
||||||
|
|
@ -192,8 +193,9 @@ readUtf8 bs = LBS8.toStrict bs & Text.decodeUtf8
|
||||||
|
|
||||||
readFixmeStdin :: FixmePerks m => FixmeM m ()
|
readFixmeStdin :: FixmePerks m => FixmeM m ()
|
||||||
readFixmeStdin = do
|
readFixmeStdin = do
|
||||||
what <- liftIO LBS8.getContents <&> LBS8.lines
|
what <- liftIO LBS8.getContents
|
||||||
pure ()
|
fixmies <- Scan.scanBlob Nothing what
|
||||||
|
liftIO $ print $ vcat (fmap pretty fixmies)
|
||||||
|
|
||||||
printEnv :: FixmePerks m => FixmeM m ()
|
printEnv :: FixmePerks m => FixmeM m ()
|
||||||
printEnv = do
|
printEnv = do
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
{-# LANGUAGE PatternSynonyms #-}
|
{-# Language MultiWayIf #-}
|
||||||
{-# LANGUAGE ViewPatterns #-}
|
|
||||||
module Fixme.Scan (scanBlob) where
|
module Fixme.Scan (scanBlob) where
|
||||||
|
|
||||||
import Fixme.Prelude hiding (indent)
|
import Fixme.Prelude hiding (indent)
|
||||||
|
|
@ -8,166 +7,157 @@ import Fixme.Types
|
||||||
import Data.ByteString.Lazy.Char8 (ByteString)
|
import Data.ByteString.Lazy.Char8 (ByteString)
|
||||||
import Data.ByteString.Lazy.Char8 qualified as LBS8
|
import Data.ByteString.Lazy.Char8 qualified as LBS8
|
||||||
import Data.Text qualified as Text
|
import Data.Text qualified as Text
|
||||||
|
|
||||||
import Data.Text.Encoding (decodeUtf8With)
|
import Data.Text.Encoding (decodeUtf8With)
|
||||||
import Data.Text.Encoding.Error (ignore)
|
import Data.Text.Encoding.Error (ignore)
|
||||||
|
|
||||||
import Data.Int
|
|
||||||
import Data.Map (Map)
|
|
||||||
import Data.Map qualified as Map
|
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.HashSet qualified as HS
|
import Data.HashSet qualified as HS
|
||||||
import Data.Coerce
|
import Data.Coerce
|
||||||
|
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
import Data.Generics.Product.Fields (field)
|
||||||
|
import Lens.Micro.Platform
|
||||||
|
|
||||||
|
import Streaming.Prelude qualified as S
|
||||||
|
|
||||||
{- HLINT ignore "Functor law" -}
|
{- HLINT ignore "Functor law" -}
|
||||||
|
|
||||||
data Sx = S0 -- initial state
|
|
||||||
| Sc -- in-comment state
|
|
||||||
| Scc -- in-comment-after-space
|
|
||||||
| Sf Int ByteString ByteString -- fixme-found-at-indent
|
|
||||||
deriving stock (Show)
|
|
||||||
|
|
||||||
data E =
|
data SfEnv =
|
||||||
E Int Int64 [ByteString]
|
SfEnv { lno :: Int
|
||||||
deriving stock (Show)
|
, l0 :: Int
|
||||||
|
, eln :: Int
|
||||||
|
}
|
||||||
|
|
||||||
data S = S Sx E
|
sfEnv0 = SfEnv 0 0 0
|
||||||
deriving stock (Show)
|
|
||||||
|
|
||||||
pattern CurrentChar :: Char -> S -> S
|
data Sx = S0 | Sf SfEnv
|
||||||
pattern CurrentChar c s <- s@(S _ ( currentChar -> Just c ))
|
|
||||||
|
|
||||||
pattern EndOfLine :: S -> S
|
data S = S Sx [(Int,ByteString)]
|
||||||
pattern EndOfLine s <- s@(S _ ( currentChar -> Nothing ))
|
|
||||||
|
|
||||||
pattern EndOfInput :: S -> S
|
|
||||||
pattern EndOfInput s <- s@(S _ (E _ _ []))
|
|
||||||
|
|
||||||
currentChar :: E -> Maybe Char
|
data FixmePart = FixmePart Int FixmeWhat
|
||||||
currentChar = \case
|
deriving stock (Show,Data,Generic)
|
||||||
(E _ n (x:_)) -> LBS8.indexMaybe x n
|
|
||||||
(E _ _ []) -> Nothing
|
|
||||||
|
|
||||||
currentChunk :: E -> ByteString
|
data FixmeWhat = FixmeHead Int Text Text
|
||||||
currentChunk (E _ off (x:_)) = LBS8.drop off x
|
| FixmeLine Text
|
||||||
currentChunk _ = mempty
|
deriving stock (Show,Data,Generic)
|
||||||
|
|
||||||
indent :: E -> Int
|
data P = P0 [FixmePart] | P1 Fixme [FixmePart]
|
||||||
indent (E n _ _) = n
|
|
||||||
|
|
||||||
-- increase current indent level by i
|
scanBlob :: forall m . FixmePerks m
|
||||||
shiftI :: Int -> E -> E
|
|
||||||
shiftI i (E j o s) = E (i+j) o s
|
|
||||||
|
|
||||||
setI :: Int -> E -> E
|
|
||||||
setI i (E _ o s) = E i o s
|
|
||||||
|
|
||||||
move :: Int64 -> E -> E
|
|
||||||
move i (E x p s) = E x (p+i) s
|
|
||||||
|
|
||||||
dropLine :: E -> E
|
|
||||||
dropLine (E x _ s) = E x 0 (drop 1 s)
|
|
||||||
|
|
||||||
scanBlob :: FixmePerks m
|
|
||||||
=> Maybe FilePath -- ^ filename to detect type
|
=> Maybe FilePath -- ^ filename to detect type
|
||||||
-> ByteString -- ^ content
|
-> ByteString -- ^ content
|
||||||
-> FixmeM m [Fixme]
|
-> FixmeM m [Fixme]
|
||||||
|
|
||||||
scanBlob fpath lbs = do
|
scanBlob fpath lbs = do
|
||||||
|
|
||||||
tagz' <- asks fixmeEnvTags
|
tagz <- asks fixmeEnvTags
|
||||||
>>= readTVarIO
|
>>= readTVarIO
|
||||||
<&> HS.toList
|
<&> HS.toList
|
||||||
<&> fmap (Text.unpack . coerce)
|
<&> fmap (Text.unpack . coerce)
|
||||||
<&> filter (not . null)
|
<&> filter (not . null)
|
||||||
|
<&> fmap LBS8.pack
|
||||||
let tagz = [ (head t, LBS8.pack t) | t <- tagz' ] & Map.fromList
|
|
||||||
|
|
||||||
comments <- fixmeGetCommentsFor fpath
|
comments <- fixmeGetCommentsFor fpath
|
||||||
<&> filter (not . null) . fmap Text.unpack
|
<&> filter (not . LBS8.null) . fmap (LBS8.pack . Text.unpack)
|
||||||
|
|
||||||
let co = Map.fromList [ (head s, LBS8.pack s) | s <- comments ]
|
let ls = LBS8.lines lbs & zip [0..]
|
||||||
|
|
||||||
let ls = LBS8.lines lbs
|
parts <- S.toList_ do
|
||||||
|
|
||||||
-- TODO: ASAP-no-comment-case
|
flip fix (S S0 ls) $ \next -> \case
|
||||||
-- для текстовых файлов нет комментариев,
|
S S0 ((lno,x):xs) -> do
|
||||||
-- этот кейс тоже надо обработать (из S0!)
|
|
||||||
|
|
||||||
flip fix (S S0 (E 0 0 ls)) $ \next -> \case
|
(l,bs) <- eatPrefix comments x
|
||||||
|
|
||||||
EndOfInput{} -> pure ()
|
let mtag = headMay [ t | t <- tagz, LBS8.isPrefixOf t bs ]
|
||||||
|
|
||||||
-- S0
|
case mtag of
|
||||||
CurrentChar ' ' (S S0 e) -> do
|
Nothing ->
|
||||||
next (S S0 (shiftI 1 $ move 1 $ e))
|
next (S S0 xs)
|
||||||
|
|
||||||
CurrentChar '\t' (S S0 e) -> do
|
Just tag -> do
|
||||||
next (S S0 (shiftI 8 $ move 1 $ e))
|
emitFixmeStart lno l tag (LBS8.drop (LBS8.length tag) bs)
|
||||||
|
next (S (Sf (SfEnv lno l 0)) xs)
|
||||||
|
|
||||||
-- maybe-start-of-comment
|
S sf@(Sf (SfEnv{..})) (x : xs) -> do
|
||||||
CurrentChar c (S S0 e) | Map.member c co -> do
|
|
||||||
let comm = Map.lookup c co & fromMaybe (LBS8.singleton c)
|
|
||||||
|
|
||||||
if LBS8.isPrefixOf comm (currentChunk e) then
|
(li,bs) <- eatPrefix comments (snd x)
|
||||||
next (S Sc (shiftI 1 $ move (max 1 (LBS8.length comm)) $ e))
|
|
||||||
|
if li <= l0 && not (LBS8.null bs) then do
|
||||||
|
next (S S0 (x:xs))
|
||||||
else do
|
else do
|
||||||
-- scanning-further
|
emitFixmeLine lno l0 bs -- (snd x)
|
||||||
next (S S0 (move 1 $ e))
|
next (S sf xs)
|
||||||
|
|
||||||
CurrentChar _ (S S0 e) -> do
|
S _ [] -> pure ()
|
||||||
next (S S0 (move 1 $ e))
|
|
||||||
|
|
||||||
-- Sc
|
debug $ vcat (fmap viaShow parts)
|
||||||
|
|
||||||
CurrentChar ' ' (S Sc e) -> do
|
S.toList_ do
|
||||||
next (S Scc (shiftI 1 $ move 1 $ e))
|
flip fix (P0 parts) $ \next -> \case
|
||||||
|
|
||||||
CurrentChar '\t' (S Sc e) -> do
|
(P0 (FixmePart _ h@FixmeHead{} : rs)) -> do
|
||||||
next (S Scc (shiftI 8 $ move 1 $ e))
|
next (P1 (fromHead h) rs)
|
||||||
|
|
||||||
-- Scc
|
(P1 fx (FixmePart _ h@FixmeHead{} : rs)) -> do
|
||||||
|
emitFixme fx
|
||||||
|
next (P1 (fromHead h) rs)
|
||||||
|
|
||||||
CurrentChar ' ' (S Scc e) -> do
|
(P1 fx (FixmePart _ (FixmeLine what) : rs)) -> do
|
||||||
next (S Scc (shiftI 1 $ move 1 $ e))
|
next (P1 (over (field @"fixmePlain") (<> [FixmePlainLine what]) fx) rs)
|
||||||
|
|
||||||
CurrentChar '\t' (S Scc e) -> do
|
(P1 fx []) -> emitFixme fx
|
||||||
next (S Scc (shiftI 8 $ move 1 $ e))
|
(P0 ( _ : rs ) ) -> next (P0 rs)
|
||||||
|
(P0 []) -> pure ()
|
||||||
|
|
||||||
CurrentChar c (S Scc e) | Map.member c tagz -> do
|
where
|
||||||
|
|
||||||
let tag = Map.lookup c tagz & fromJust -- works, cause Map.member
|
emitFixme e = do
|
||||||
|
S.yield $ over (field @"fixmePlain") dropEmpty e
|
||||||
|
where
|
||||||
|
dropEmpty = dropWhile $ \case
|
||||||
|
FixmePlainLine "" -> True
|
||||||
|
_ -> False
|
||||||
|
|
||||||
if LBS8.isPrefixOf tag (currentChunk e) then
|
-- FIXME: jopakita
|
||||||
next (S (Sf (indent e) tag mempty) (move (max 1 (LBS8.length tag)) $ e))
|
fromHead = \case
|
||||||
-- это тег
|
FixmeHead _ tag title -> Fixme (FixmeTag tag) (FixmeTitle title) Nothing mempty mempty Nothing
|
||||||
-- переходим в обработку fixme
|
_ -> Fixme mempty mempty Nothing mempty mempty Nothing
|
||||||
-- запоминаем indent
|
|
||||||
else
|
|
||||||
next (S Scc (shiftI 1 $ move 1 $ e))
|
|
||||||
-- это не тег, но и не пробел
|
|
||||||
-- едем дальше по коменту
|
|
||||||
-- двигаем ли indent? ну чо нет-то
|
|
||||||
|
|
||||||
-- Sf
|
emitFixmeStart lno lvl tagbs restbs = do
|
||||||
-- жрём до
|
debug $ red "emitFixmeStart"
|
||||||
-- 1. Пока indent > indent (Sf ..)
|
let tag = decodeUtf8With ignore (LBS8.toStrict tagbs) & Text.strip
|
||||||
|
let rest = decodeUtf8With ignore (LBS8.toStrict restbs) & Text.strip
|
||||||
|
S.yield (FixmePart lno (FixmeHead lvl tag rest))
|
||||||
|
|
||||||
CurrentChar ' ' (S (Sf{}) e) -> do
|
emitFixmeLine lno l0 restbs = do
|
||||||
next (S Scc (shiftI 1 $ move 1 $ e))
|
debug $ red "emitFixmeLine"
|
||||||
|
let rest = decodeUtf8With ignore (LBS8.toStrict restbs) & Text.stripEnd
|
||||||
|
S.yield (FixmePart lno (FixmeLine rest))
|
||||||
|
|
||||||
CurrentChar '\t' (S (Sf{}) e) -> do
|
eatPrefix comments x = do
|
||||||
next (S Scc (shiftI 8 $ move 1 $ e))
|
-- дропаем пробелы или табы
|
||||||
|
let (pre1,s1) = LBS8.span (`elem` " \t") x
|
||||||
|
|
||||||
CurrentChar c (S (Sf{}) e) -> do
|
-- дропаем токен коммента
|
||||||
error "WTF?"
|
-- перебираем все коменты, пока не найдем первый
|
||||||
|
let comm = headMay [ co | co <- comments, LBS8.isPrefixOf co s1 ]
|
||||||
|
|
||||||
-- в любом случае жрём строку, что бы это закончилось
|
let pre2 = pre1 <> fromMaybe mempty comm
|
||||||
EndOfLine (S _ e) -> do
|
let rest2 = LBS8.drop (maybe 0 LBS8.length comm) s1
|
||||||
next (S S0 (setI 0 $ dropLine $ e))
|
|
||||||
|
|
||||||
w -> error (show w)
|
let (pre3,s2) = LBS8.span (`elem` " \t") rest2
|
||||||
|
|
||||||
pure mempty
|
let pre = pre1 <> pre2 <> pre3
|
||||||
|
|
||||||
|
l <- for (LBS8.unpack pre) $ \case
|
||||||
|
' ' -> pure 1
|
||||||
|
'\t' -> pure 8
|
||||||
|
_ -> pure 0
|
||||||
|
|
||||||
|
let level = sum l + maybe 0 (const 1) comm
|
||||||
|
|
||||||
|
pure (level, s2)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@ import System.FilePath
|
||||||
data GitLocation =
|
data GitLocation =
|
||||||
GitLocation
|
GitLocation
|
||||||
{ gitLocationHash :: GitHash
|
{ gitLocationHash :: GitHash
|
||||||
, gitLocationOffset :: Integer
|
, gitLocationLine :: Integer
|
||||||
, gitLocationLength :: Integer
|
|
||||||
}
|
}
|
||||||
deriving stock (Eq,Ord,Show,Data,Generic)
|
deriving stock (Eq,Ord,Show,Data,Generic)
|
||||||
|
|
||||||
|
|
@ -25,15 +24,15 @@ data FixmeSource =
|
||||||
deriving stock (Show,Data,Generic)
|
deriving stock (Show,Data,Generic)
|
||||||
|
|
||||||
newtype FixmeTag = FixmeTag { fromFixmeTag :: Text }
|
newtype FixmeTag = FixmeTag { fromFixmeTag :: Text }
|
||||||
deriving newtype (Eq,Ord,Show,IsString,Hashable)
|
deriving newtype (Eq,Ord,Show,IsString,Hashable,Semigroup,Monoid)
|
||||||
deriving stock (Data,Generic)
|
deriving stock (Data,Generic)
|
||||||
|
|
||||||
newtype FixmeTitle = FixmeTitle { fromFixmeTitle :: Text }
|
newtype FixmeTitle = FixmeTitle { fromFixmeTitle :: Text }
|
||||||
deriving newtype (Eq,Ord,Show,IsString)
|
deriving newtype (Eq,Ord,Show,IsString,Semigroup,Monoid)
|
||||||
deriving stock (Data,Generic)
|
deriving stock (Data,Generic)
|
||||||
|
|
||||||
newtype FixmePlainLine = FixmePlainLine { fromFixmeText :: Text }
|
newtype FixmePlainLine = FixmePlainLine { fromFixmeText :: Text }
|
||||||
deriving newtype (Eq,Ord,Show,IsString)
|
deriving newtype (Eq,Ord,Show,IsString,Semigroup,Monoid)
|
||||||
deriving stock (Data,Generic)
|
deriving stock (Data,Generic)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -48,14 +47,14 @@ newtype FixmeAttrVal = FixmeAttrVal { fromFixmeAttrVal :: Text }
|
||||||
|
|
||||||
|
|
||||||
newtype FixmeTimestamp = FixmeTimestamp Word64
|
newtype FixmeTimestamp = FixmeTimestamp Word64
|
||||||
deriving newtype (Eq,Ord,Show)
|
deriving newtype (Eq,Ord,Show,Num)
|
||||||
deriving stock (Data,Generic)
|
deriving stock (Data,Generic)
|
||||||
|
|
||||||
data Fixme =
|
data Fixme =
|
||||||
Fixme
|
Fixme
|
||||||
{ fixmeTag :: FixmeTag
|
{ fixmeTag :: FixmeTag
|
||||||
, fixmeTitle :: FixmeTitle
|
, fixmeTitle :: FixmeTitle
|
||||||
, fixmeTs :: FixmeTimestamp
|
, fixmeTs :: Maybe FixmeTimestamp
|
||||||
, fixmePlain :: [FixmePlainLine]
|
, fixmePlain :: [FixmePlainLine]
|
||||||
, fixmeAttr :: HashMap FixmeAttrName FixmeAttrVal
|
, fixmeAttr :: HashMap FixmeAttrName FixmeAttrVal
|
||||||
, fixmeSource :: Maybe FixmeSource
|
, fixmeSource :: Maybe FixmeSource
|
||||||
|
|
@ -144,6 +143,16 @@ instance Pretty FixmeTitle where
|
||||||
instance Pretty FixmeTag where
|
instance Pretty FixmeTag where
|
||||||
pretty = pretty . coerce @_ @Text
|
pretty = pretty . coerce @_ @Text
|
||||||
|
|
||||||
|
instance Pretty FixmePlainLine where
|
||||||
|
pretty = pretty . coerce @_ @Text
|
||||||
|
|
||||||
|
instance Pretty Fixme where
|
||||||
|
pretty Fixme{..} =
|
||||||
|
pretty fixmeTag <+> pretty fixmeTitle
|
||||||
|
<> lls
|
||||||
|
where
|
||||||
|
lls | not (null fixmePlain) = line <> vcat (fmap pretty fixmePlain)
|
||||||
|
| otherwise = mempty
|
||||||
|
|
||||||
|
|
||||||
defCommentMap :: HashMap FilePath (HashSet Text)
|
defCommentMap :: HashMap FilePath (HashSet Text)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue