{-# LANGUAGE MultiWayIf        #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE RecordWildCards   #-}
{-# LANGUAGE ViewPatterns      #-}

{-|
Module      : Headroom.FileSupport
Description : Support for handling various source code file types
Copyright   : (c) 2019-2021 Vaclav Svejcar
License     : BSD-3-Clause
Maintainer  : vaclav.svejcar@gmail.com
Stability   : experimental
Portability : POSIX

/Headroom/ currently supports working with file types defined in 'FileType'
type, and because every type of source code file requires different handling of
some aspects, this file type specific support is implemented for every supported
file type and exposed as instance of 'FileSupport' data type.
-}

module Headroom.FileSupport
  ( fileSupport
  , analyzeSourceCode
  )
where

import           Control.Monad.State                 ( get
                                                     , put
                                                     )
import qualified Headroom.FileSupport.C             as C
import qualified Headroom.FileSupport.CPP           as CPP
import qualified Headroom.FileSupport.CSS           as CSS
import qualified Headroom.FileSupport.Haskell       as Haskell
import qualified Headroom.FileSupport.HTML          as HTML
import qualified Headroom.FileSupport.Java          as Java
import qualified Headroom.FileSupport.JS            as JS
import qualified Headroom.FileSupport.PureScript    as PureScript
import qualified Headroom.FileSupport.Rust          as Rust
import qualified Headroom.FileSupport.Scala         as Scala
import qualified Headroom.FileSupport.Shell         as Shell
import           Headroom.FileSupport.Types          ( FileSupport(..)
                                                     , SyntaxAnalysis(..)
                                                     )
import           Headroom.FileType.Types             ( FileType(..) )
import           Headroom.SourceCode                 ( LineType(..)
                                                     , SourceCode
                                                     , fromText
                                                     )
import           RIO
import qualified RIO.Text                           as T


------------------------------  PUBLIC FUNCTIONS  ------------------------------

-- | Returns 'FileSupport' for corresponding 'FileType'.
fileSupport :: FileType -> FileSupport
fileSupport :: FileType -> FileSupport
fileSupport FileType
C          = FileSupport
C.fileSupport
fileSupport FileType
CPP        = FileSupport
CPP.fileSupport
fileSupport FileType
CSS        = FileSupport
CSS.fileSupport
fileSupport FileType
Haskell    = FileSupport
Haskell.fileSupport
fileSupport FileType
HTML       = FileSupport
HTML.fileSupport
fileSupport FileType
Java       = FileSupport
Java.fileSupport
fileSupport FileType
JS         = FileSupport
JS.fileSupport
fileSupport FileType
PureScript = FileSupport
PureScript.fileSupport
fileSupport FileType
Rust       = FileSupport
Rust.fileSupport
fileSupport FileType
Scala      = FileSupport
Scala.fileSupport
fileSupport FileType
Shell      = FileSupport
Shell.fileSupport


-- | Analyzes the raw source code of given type using provided 'FileSupport'.
analyzeSourceCode :: FileSupport
                  -- ^ 'FileSupport' implementation used for analysis
                  -> Text
                  -- ^ raw source code to analyze
                  -> SourceCode
                  -- ^ analyzed source code
analyzeSourceCode :: FileSupport -> Text -> SourceCode
analyzeSourceCode FileSupport
fs = Int -> (Text -> State Int LineType) -> Text -> SourceCode
forall a. a -> (Text -> State a LineType) -> Text -> SourceCode
fromText Int
state0 Text -> State Int LineType
forall (m :: * -> *) a.
(MonadState a m, Num a, Ord a) =>
Text -> m LineType
process
 where
  SyntaxAnalysis {Text -> Bool
saIsCommentEnd :: SyntaxAnalysis -> Text -> Bool
saIsCommentStart :: SyntaxAnalysis -> Text -> Bool
saIsCommentEnd :: Text -> Bool
saIsCommentStart :: Text -> Bool
..} = FileSupport -> SyntaxAnalysis
fsSyntaxAnalysis FileSupport
fs
  state0 :: Int
state0              = Int
0 :: Int
  process :: Text -> m LineType
process (Text -> Text
T.strip -> Text
l) = do
    a
cs <- m a
forall s (m :: * -> *). MonadState s m => m s
get
    let isStart :: Text -> Bool
isStart   = Text -> Bool
saIsCommentStart
        isEnd :: Text -> Bool
isEnd     = Text -> Bool
saIsCommentEnd
        tpe :: a -> LineType
tpe       = \a
c -> if a
c a -> a -> Bool
forall a. Ord a => a -> a -> Bool
> a
0 then LineType
Comment else LineType
Code
        (a
ns, LineType
res) = if
          | Text -> Bool
isStart Text
l Bool -> Bool -> Bool
&& Text -> Bool
isEnd Text
l -> (a
cs, LineType
Comment)
          | Text -> Bool
isStart Text
l            -> (a
cs a -> a -> a
forall a. Num a => a -> a -> a
+ a
1, LineType
Comment)
          | Text -> Bool
isEnd Text
l              -> (a
cs a -> a -> a
forall a. Num a => a -> a -> a
- a
1, a -> LineType
tpe a
cs)
          | a
cs a -> a -> Bool
forall a. Ord a => a -> a -> Bool
> a
0               -> (a
cs, LineType
Comment)
          | Bool
otherwise            -> (a
0, LineType
Code)
    a -> m ()
forall s (m :: * -> *). MonadState s m => s -> m ()
put a
ns
    LineType -> m LineType
forall (f :: * -> *) a. Applicative f => a -> f a
pure LineType
res