{-# LANGUAGE RankNTypes                 #-}
{-# LANGUAGE TemplateHaskell            #-}
{-# LANGUAGE LambdaCase                 #-}
-- | Internal helpers for generating definitions
module Language.LSP.Types.Utils
  ( rdrop
  , makeSingletonFromJSON
  , makeRegHelper
  , makeExtendingDatatype
  , lspOptions
  , lspOptionsUntagged
  ) where

import Control.Monad
import Data.Aeson
import Data.List
import Language.Haskell.TH

-- ---------------------------------------------------------------------

rdrop :: Int -> [a] -> [a]
rdrop :: forall a. Int -> [a] -> [a]
rdrop Int
cnt = [a] -> [a]
forall a. [a] -> [a]
reverse ([a] -> [a]) -> ([a] -> [a]) -> [a] -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
drop Int
cnt ([a] -> [a]) -> ([a] -> [a]) -> [a] -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> [a]
forall a. [a] -> [a]
reverse

-- | Given a wrapper and a singleton GADT, construct FromJSON
-- instances for each constructor return type by invoking the
-- FromJSON instance for the wrapper and unwrapping
makeSingletonFromJSON :: Name -> Name -> Q [Dec]
makeSingletonFromJSON :: Name -> Name -> Q [Dec]
makeSingletonFromJSON Name
wrap Name
gadt = do
  TyConI (DataD Cxt
_ Name
_ [TyVarBndr BndrVis]
_ Maybe Kind
_ [Con]
cons [DerivClause]
_) <- Name -> Q Info
reify Name
gadt
  [[Dec]] -> [Dec]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[Dec]] -> [Dec]) -> Q [[Dec]] -> Q [Dec]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Con -> Q [Dec]) -> [Con] -> Q [[Dec]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM (Name -> Con -> Q [Dec]
makeInst Name
wrap) [Con]
cons

{-
instance FromJSON (SMethod $method) where
  parseJSON = parseJSON >=> \case
      SomeMethod $singleton-method -> pure $singleton-method
      _ -> mempty
-}
makeInst :: Name -> Con -> Q [Dec]
makeInst :: Name -> Con -> Q [Dec]
makeInst Name
wrap (GadtC [Name
sConstructor] [BangType]
args Kind
t) = do
  [Name]
ns <- Int -> Q Name -> Q [Name]
forall (m :: * -> *) a. Applicative m => Int -> m a -> m [a]
replicateM ([BangType] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [BangType]
args) (String -> Q Name
forall (m :: * -> *). Quote m => String -> m Name
newName String
"x")
  let wrappedPat :: Q Pat
wrappedPat = Name -> [Q Pat] -> Q Pat
forall (m :: * -> *). Quote m => Name -> [m Pat] -> m Pat
conP Name
wrap [Name -> [Q Pat] -> Q Pat
forall (m :: * -> *). Quote m => Name -> [m Pat] -> m Pat
conP Name
sConstructor ((Name -> Q Pat) -> [Name] -> [Q Pat]
forall a b. (a -> b) -> [a] -> [b]
map Name -> Q Pat
forall (m :: * -> *). Quote m => Name -> m Pat
varP [Name]
ns)]
      unwrappedE :: Q Exp
unwrappedE = Exp -> Q Exp
forall a. a -> Q a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Exp -> Q Exp) -> Exp -> Q Exp
forall a b. (a -> b) -> a -> b
$ (Exp -> Exp -> Exp) -> Exp -> [Exp] -> Exp
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' Exp -> Exp -> Exp
AppE (Name -> Exp
ConE Name
sConstructor) ((Name -> Exp) -> [Name] -> [Exp]
forall a b. (a -> b) -> [a] -> [b]
map Name -> Exp
VarE [Name]
ns)
  [d| instance FromJSON $(Kind -> Q Kind
forall a. a -> Q a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Kind
t) where
        parseJSON = parseJSON >=> \case
          $Q Pat
wrappedPat -> pure $Q Exp
unwrappedE
          _ -> mempty
    |]
makeInst Name
wrap (ForallC [TyVarBndr Specificity]
_ Cxt
_ Con
con) = Name -> Con -> Q [Dec]
makeInst Name
wrap Con
con -- Cancel and Custom requests
makeInst Name
_ Con
_ = String -> Q [Dec]
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"makeInst only defined for GADT constructors"

makeRegHelper :: Name -> DecsQ
makeRegHelper :: Name -> Q [Dec]
makeRegHelper Name
regOptTypeName = do
  Just Name
sMethodTypeName <- String -> Q (Maybe Name)
lookupTypeName String
"SMethod"
  Just Name
fromClientName <- String -> Q (Maybe Name)
lookupValueName String
"FromClient"
  TyConI (DataD Cxt
_ Name
_ [TyVarBndr BndrVis]
_ Maybe Kind
_ [Con]
allCons [DerivClause]
_) <- Name -> Q Info
reify Name
sMethodTypeName

  let isConsFromClient :: Con -> Q Bool
isConsFromClient (GadtC [Name]
_ [BangType]
_ (AppT Kind
_ Kind
method)) = Kind -> Q Bool
isMethodFromClient Kind
method
      isConsFromClient Con
_ = Bool -> Q Bool
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
      isMethodFromClient :: Type -> Q Bool
      isMethodFromClient :: Kind -> Q Bool
isMethodFromClient (PromotedT Name
method) = do
        DataConI Name
_ Kind
typ Name
_ <- Name -> Q Info
reify Name
method
        case Kind
typ of
          AppT (AppT Kind
_ (PromotedT Name
n)) Kind
_ -> Bool -> Q Bool
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Q Bool) -> Bool -> Q Bool
forall a b. (a -> b) -> a -> b
$ Name
n Name -> Name -> Bool
forall a. Eq a => a -> a -> Bool
== Name
fromClientName
          Kind
_ -> Bool -> Q Bool
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
      isMethodFromClient Kind
_ = String -> Q Bool
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Didn't expect this type of Method!"

  [Con]
cons <- (Con -> Q Bool) -> [Con] -> Q [Con]
forall (m :: * -> *) a.
Applicative m =>
(a -> m Bool) -> [a] -> m [a]
filterM Con -> Q Bool
isConsFromClient [Con]
allCons

  let conNames :: [Name]
conNames = (Con -> Name) -> [Con] -> [Name]
forall a b. (a -> b) -> [a] -> [b]
map (\(GadtC [Name
name] [BangType]
_ Kind
_) -> Name
name) [Con]
cons
      helperName :: Name
helperName = String -> Name
mkName String
"regHelper"
      mkClause :: Name -> m Clause
mkClause Name
name = do
        Name
x <- String -> m Name
forall (m :: * -> *). Quote m => String -> m Name
newName String
"x"
        [m Pat] -> m Body -> [m Dec] -> m Clause
forall (m :: * -> *).
Quote m =>
[m Pat] -> m Body -> [m Dec] -> m Clause
clause [ Name -> [m Pat] -> m Pat
forall (m :: * -> *). Quote m => Name -> [m Pat] -> m Pat
conP Name
name [], Name -> m Pat
forall (m :: * -> *). Quote m => Name -> m Pat
varP Name
x ]
               (m Exp -> m Body
forall (m :: * -> *). Quote m => m Exp -> m Body
normalB (Name -> m Exp
forall (m :: * -> *). Quote m => Name -> m Exp
varE Name
x))
               []
      regOptTcon :: Q Kind
regOptTcon = Name -> Q Kind
forall (m :: * -> *). Quote m => Name -> m Kind
conT Name
regOptTypeName
  Dec
fun <- Name -> [Q Clause] -> Q Dec
forall (m :: * -> *). Quote m => Name -> [m Clause] -> m Dec
funD Name
helperName ((Name -> Q Clause) -> [Name] -> [Q Clause]
forall a b. (a -> b) -> [a] -> [b]
map Name -> Q Clause
forall {m :: * -> *}. Quote m => Name -> m Clause
mkClause [Name]
conNames)

  Dec
typSig <- Name -> Q Kind -> Q Dec
forall (m :: * -> *). Quote m => Name -> m Kind -> m Dec
sigD Name
helperName (Q Kind -> Q Dec) -> Q Kind -> Q Dec
forall a b. (a -> b) -> a -> b
$
    [t| forall m x. $(Name -> Q Kind
forall (m :: * -> *). Quote m => Name -> m Kind
conT Name
sMethodTypeName) m
        -> (Show ($Q Kind
regOptTcon m) => ToJSON ($Q Kind
regOptTcon m) => FromJSON ($Q Kind
regOptTcon m) => x)
        -> x |]
  [Dec] -> Q [Dec]
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return [Dec
typSig, Dec
fun]

-- | @makeExtendingDatatype name extends fields@ generates a record datatype
-- that contains all the fields of @extends@, plus the additional fields in
-- @fields@.
-- e.g.
-- data Foo = { a :: Int }
-- makeExtendingDatatype "bar" [''Foo] [("b", [t| String |])]
-- Will generate
-- data Bar = { a :: Int, b :: String }
makeExtendingDatatype :: String -> [Name] -> [(String, TypeQ)] -> DecsQ
makeExtendingDatatype :: String -> [Name] -> [(String, Q Kind)] -> Q [Dec]
makeExtendingDatatype String
datatypeNameStr [Name]
extends [(String, Q Kind)]
fields = do
  [VarBangType]
extendFields <- ([[VarBangType]] -> [VarBangType])
-> Q [[VarBangType]] -> Q [VarBangType]
forall a b. (a -> b) -> Q a -> Q b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [[VarBangType]] -> [VarBangType]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (Q [[VarBangType]] -> Q [VarBangType])
-> Q [[VarBangType]] -> Q [VarBangType]
forall a b. (a -> b) -> a -> b
$ [Name] -> (Name -> Q [VarBangType]) -> Q [[VarBangType]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [Name]
extends ((Name -> Q [VarBangType]) -> Q [[VarBangType]])
-> (Name -> Q [VarBangType]) -> Q [[VarBangType]]
forall a b. (a -> b) -> a -> b
$ \Name
e -> do
    TyConI (DataD Cxt
_ Name
_ [TyVarBndr BndrVis]
_ Maybe Kind
_ [RecC Name
_ [VarBangType]
eFields] [DerivClause]
_) <- Name -> Q Info
reify Name
e
    [VarBangType] -> Q [VarBangType]
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return [VarBangType]
eFields
  let datatypeName :: Name
datatypeName = String -> Name
mkName String
datatypeNameStr
      insts :: [Q Kind]
insts = [[t| Read |], [t| Show |], [t| Eq |]]
      constructor :: Q Con
constructor = Name -> [Q VarBangType] -> Q Con
forall (m :: * -> *). Quote m => Name -> [m VarBangType] -> m Con
recC Name
datatypeName [Q VarBangType]
combinedFields
      userFields :: [Q VarBangType]
userFields = (((String, Q Kind) -> Q VarBangType)
 -> [(String, Q Kind)] -> [Q VarBangType])
-> [(String, Q Kind)]
-> ((String, Q Kind) -> Q VarBangType)
-> [Q VarBangType]
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((String, Q Kind) -> Q VarBangType)
-> [(String, Q Kind)] -> [Q VarBangType]
forall a b. (a -> b) -> [a] -> [b]
map [(String, Q Kind)]
fields (((String, Q Kind) -> Q VarBangType) -> [Q VarBangType])
-> ((String, Q Kind) -> Q VarBangType) -> [Q VarBangType]
forall a b. (a -> b) -> a -> b
$ \(String
s, Q Kind
typ) -> do
        Name -> Q BangType -> Q VarBangType
forall (m :: * -> *).
Quote m =>
Name -> m BangType -> m VarBangType
varBangType (String -> Name
mkName String
s) (Q Bang -> Q Kind -> Q BangType
forall (m :: * -> *). Quote m => m Bang -> m Kind -> m BangType
bangType (Q SourceUnpackedness -> Q SourceStrictness -> Q Bang
forall (m :: * -> *).
Quote m =>
m SourceUnpackedness -> m SourceStrictness -> m Bang
bang Q SourceUnpackedness
forall (m :: * -> *). Quote m => m SourceUnpackedness
noSourceUnpackedness Q SourceStrictness
forall (m :: * -> *). Quote m => m SourceStrictness
noSourceStrictness) Q Kind
typ)
      combinedFields :: [Q VarBangType]
combinedFields = ((VarBangType -> Q VarBangType) -> [VarBangType] -> [Q VarBangType]
forall a b. (a -> b) -> [a] -> [b]
map VarBangType -> Q VarBangType
forall a. a -> Q a
forall (f :: * -> *) a. Applicative f => a -> f a
pure [VarBangType]
extendFields) [Q VarBangType] -> [Q VarBangType] -> [Q VarBangType]
forall a. Semigroup a => a -> a -> a
<> [Q VarBangType]
userFields
      derivs :: [Q DerivClause]
derivs = [Maybe DerivStrategy -> [Q Kind] -> Q DerivClause
forall (m :: * -> *).
Quote m =>
Maybe DerivStrategy -> [m Kind] -> m DerivClause
derivClause Maybe DerivStrategy
forall a. Maybe a
Nothing [Q Kind]
insts]
  (\Dec
a -> [Dec
a]) (Dec -> [Dec]) -> Q Dec -> Q [Dec]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Q Cxt
-> Name
-> [TyVarBndr BndrVis]
-> Maybe Kind
-> [Q Con]
-> [Q DerivClause]
-> Q Dec
forall (m :: * -> *).
Quote m =>
m Cxt
-> Name
-> [TyVarBndr BndrVis]
-> Maybe Kind
-> [m Con]
-> [m DerivClause]
-> m Dec
dataD ([Q Kind] -> Q Cxt
forall (m :: * -> *). Quote m => [m Kind] -> m Cxt
cxt []) Name
datatypeName [] Maybe Kind
forall a. Maybe a
Nothing [Q Con
constructor] [Q DerivClause]
derivs

-- | Standard options for use when generating JSON instances
-- NOTE: This needs to be in a separate file because of the TH stage restriction
lspOptions :: Options
lspOptions :: Options
lspOptions = Options
defaultOptions { omitNothingFields = True, fieldLabelModifier = modifier }
  where
  modifier :: String -> String
  -- For fields called data and type in the spec, we call them xdata and xtype
  -- in haskell-lsp-types to avoid it clashing with the Haskell keywords. This
  -- fixes up the json derivation
  modifier :: String -> String
modifier String
"_xdata" = String
"data"
  modifier String
"_xtype" = String
"type"
  modifier String
xs = Int -> String -> String
forall a. Int -> [a] -> [a]
drop Int
1 String
xs

-- | Standard options for use when generating JSON instances for an untagged union
lspOptionsUntagged :: Options
lspOptionsUntagged :: Options
lspOptionsUntagged = Options
lspOptions { sumEncoding = UntaggedValue }