module Proteome.Grep.Parse where

import Chiasma.Data.Ident (generateIdent, identText)
import Data.Attoparsec.Text (parseOnly)
import qualified Data.Text as Text (strip)
import Exon (exon)
import qualified Log
import Path (Abs, Dir, Path, parseAbsFile, parseRelFile, stripProperPrefix, (</>))
import Ribosome (pathText)
import Ribosome.Menu.Data.MenuItem (MenuItem (MenuItem))
import Text.Parser.Char (anyChar, char, noneOf)
import Text.Parser.Combinators (manyTill)
import Text.Parser.Token (TokenParsing, natural)

import Proteome.Data.GrepOutputLine (GrepOutputLine (GrepOutputLine))
import Proteome.Grep.Syntax (lineNumber)

grepParser ::
  MonadFail m =>
  TokenParsing m =>
  Path Abs Dir ->
  m GrepOutputLine
grepParser :: forall (m :: * -> *).
(MonadFail m, TokenParsing m) =>
Path Abs Dir -> m GrepOutputLine
grepParser Path Abs Dir
cwd =
  Path Abs File -> Int -> Maybe Int -> Text -> GrepOutputLine
GrepOutputLine (Path Abs File -> Int -> Maybe Int -> Text -> GrepOutputLine)
-> m (Path Abs File)
-> m (Int -> Maybe Int -> Text -> GrepOutputLine)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m (Path Abs File)
path m (Int -> Maybe Int -> Text -> GrepOutputLine)
-> m Int -> m (Maybe Int -> Text -> GrepOutputLine)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (Int -> Int -> Int
forall a. Num a => a -> a -> a
subtract Int
1 (Int -> Int) -> m Int -> m Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m Int
number) m (Maybe Int -> Text -> GrepOutputLine)
-> m (Maybe Int) -> m (Text -> GrepOutputLine)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> m Int -> m (Maybe Int)
forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
optional m Int
number m (Text -> GrepOutputLine) -> m Text -> m GrepOutputLine
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> ([Char] -> Text
forall a. ToText a => a -> Text
toText ([Char] -> Text) -> m [Char] -> m Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m Char -> m [Char]
forall (f :: * -> *) a. Alternative f => f a -> f [a]
many m Char
forall (m :: * -> *). CharParsing m => m Char
anyChar)
  where
    path :: m (Path Abs File)
path = do
      [Char]
s <- m Char -> m Char -> m [Char]
forall (m :: * -> *) a end. Alternative m => m a -> m end -> m [a]
manyTill ([Char] -> m Char
forall (m :: * -> *). CharParsing m => [Char] -> m Char
noneOf [Char]
":") (Char -> m Char
forall (m :: * -> *). CharParsing m => Char -> m Char
char Char
':')
      m (Path Abs File)
-> (Path Abs File -> m (Path Abs File))
-> Maybe (Path Abs File)
-> m (Path Abs File)
forall b a. b -> (a -> b) -> Maybe a -> b
maybe ([Char] -> m (Path Abs File)
forall (m :: * -> *) a. MonadFail m => [Char] -> m a
fail [Char]
"not a path") Path Abs File -> m (Path Abs File)
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Char] -> Maybe (Path Abs File)
forall (m :: * -> *). MonadThrow m => [Char] -> m (Path Abs File)
parseAbsFile [Char]
s Maybe (Path Abs File)
-> Maybe (Path Abs File) -> Maybe (Path Abs File)
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> ((Path Abs Dir
cwd Path Abs Dir -> Path Rel File -> Path Abs File
forall b t. Path b Dir -> Path Rel t -> Path b t
</>) (Path Rel File -> Path Abs File)
-> Maybe (Path Rel File) -> Maybe (Path Abs File)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Char] -> Maybe (Path Rel File)
forall (m :: * -> *). MonadThrow m => [Char] -> m (Path Rel File)
parseRelFile [Char]
s))
    number :: m Int
number =
      (Integer -> Int
forall a. Num a => Integer -> a
fromInteger (Integer -> Int) -> m Integer -> m Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m Integer
forall (m :: * -> *). TokenParsing m => m Integer
natural) m Int -> m Char -> m Int
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Char -> m Char
forall (m :: * -> *). CharParsing m => Char -> m Char
char Char
':'

formatGrepLine :: Path Abs Dir -> GrepOutputLine -> Text
formatGrepLine :: Path Abs Dir -> GrepOutputLine -> Text
formatGrepLine Path Abs Dir
cwd (GrepOutputLine Path Abs File
path Int
line Maybe Int
col Text
text) =
  [exon|#{relativePath} #{lineNumber} #{show line}:#{show (fromMaybe 1 col)} #{Text.strip text}|]
  where
    relativePath :: Text
relativePath =
      Text -> (Path Rel File -> Text) -> Maybe (Path Rel File) -> Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Path Abs File -> Text
forall b t. Path b t -> Text
pathText Path Abs File
path) Path Rel File -> Text
forall b t. Path b t -> Text
pathText (Path Abs Dir -> Path Abs File -> Maybe (Path Rel File)
forall (m :: * -> *) b t.
MonadThrow m =>
Path b Dir -> Path b t -> m (Path Rel t)
stripProperPrefix Path Abs Dir
cwd Path Abs File
path)

parseGrepOutput ::
  Members [Log, Embed IO] r =>
  Path Abs Dir ->
  Text ->
  Sem r (Maybe (MenuItem GrepOutputLine))
parseGrepOutput :: forall (r :: EffectRow).
Members '[Log, Embed IO] r =>
Path Abs Dir -> Text -> Sem r (Maybe (MenuItem GrepOutputLine))
parseGrepOutput Path Abs Dir
cwd =
  Either [Char] GrepOutputLine
-> Sem r (Maybe (MenuItem GrepOutputLine))
item (Either [Char] GrepOutputLine
 -> Sem r (Maybe (MenuItem GrepOutputLine)))
-> (Text -> Either [Char] GrepOutputLine)
-> Text
-> Sem r (Maybe (MenuItem GrepOutputLine))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Parser GrepOutputLine -> Text -> Either [Char] GrepOutputLine
forall a. Parser a -> Text -> Either [Char] a
parseOnly (Path Abs Dir -> Parser GrepOutputLine
forall (m :: * -> *).
(MonadFail m, TokenParsing m) =>
Path Abs Dir -> m GrepOutputLine
grepParser Path Abs Dir
cwd)
  where
    item :: Either [Char] GrepOutputLine
-> Sem r (Maybe (MenuItem GrepOutputLine))
item (Right GrepOutputLine
a) = do
      Text
ident <- Ident -> Text
identText (Ident -> Text) -> Sem r Ident -> Sem r Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Sem r Ident
forall (m :: * -> *). MonadIO m => m Ident
generateIdent
      pure (MenuItem GrepOutputLine -> Maybe (MenuItem GrepOutputLine)
forall a. a -> Maybe a
Just (Text -> GrepOutputLine -> MenuItem GrepOutputLine
convert Text
ident GrepOutputLine
a))
    item (Left [Char]
err) =
      Maybe (MenuItem GrepOutputLine)
forall a. Maybe a
Nothing Maybe (MenuItem GrepOutputLine)
-> Sem r () -> Sem r (Maybe (MenuItem GrepOutputLine))
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Text -> Sem r ()
forall (r :: EffectRow).
(HasCallStack, Member Log r) =>
Text -> Sem r ()
Log.debug [exon|parsing grep output failed: #{toText err}|]
    convert :: Text -> GrepOutputLine -> MenuItem GrepOutputLine
convert Text
_ GrepOutputLine
file =
      GrepOutputLine -> Text -> Text -> MenuItem GrepOutputLine
forall a. a -> Text -> Text -> MenuItem a
MenuItem GrepOutputLine
file Text
text [exon| * #{text}|]
      where
        text :: Text
text =
          Path Abs Dir -> GrepOutputLine -> Text
formatGrepLine Path Abs Dir
cwd GrepOutputLine
file