{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE LambdaCase #-}
module Language.LSP.Types.Utils
( rdrop
, makeSingletonFromJSON
, makeRegHelper
, makeExtendingDatatype
, lspOptions
) where
import Control.Monad
import Data.Aeson
import Data.List
import Language.Haskell.TH
rdrop :: Int -> [a] -> [a]
rdrop cnt = reverse . drop cnt . reverse
makeSingletonFromJSON :: Name -> Name -> Q [Dec]
makeSingletonFromJSON wrap gadt = do
TyConI (DataD _ _ _ _ cons _) <- reify gadt
concat <$> mapM (makeInst wrap) cons
makeInst :: Name -> Con -> Q [Dec]
makeInst wrap (GadtC [sConstructor] args t) = do
ns <- replicateM (length args) (newName "x")
let wrappedPat = pure $ ConP wrap [ConP sConstructor (map VarP ns)]
unwrappedE = pure $ foldl' AppE (ConE sConstructor) (map VarE ns)
[d| instance FromJSON $(pure t) where
parseJSON = parseJSON >=> \case
$wrappedPat -> pure $unwrappedE
_ -> mempty
|]
makeInst wrap (ForallC _ _ con) = makeInst wrap con
makeInst _ _ = fail "makeInst only defined for GADT constructors"
makeRegHelper :: Name -> DecsQ
makeRegHelper regOptTypeName = do
Just sMethodTypeName <- lookupTypeName "SMethod"
Just fromClientName <- lookupValueName "FromClient"
TyConI (DataD _ _ _ _ allCons _) <- reify sMethodTypeName
let isConsFromClient (GadtC _ _ (AppT _ method)) = isMethodFromClient method
isConsFromClient _ = return False
isMethodFromClient :: Type -> Q Bool
isMethodFromClient (PromotedT method) = do
DataConI _ typ _ <- reify method
case typ of
AppT (AppT _ (PromotedT n)) _ -> return $ n == fromClientName
_ -> return False
isMethodFromClient _ = fail "Didn't expect this type of Method!"
cons <- filterM isConsFromClient allCons
let conNames = map (\(GadtC [name] _ _) -> name) cons
helperName = mkName "regHelper"
mkClause name = do
x <- newName "x"
clause [ conP name [], varP x ]
(normalB (varE x))
[]
regOptTcon = conT regOptTypeName
fun <- funD helperName (map mkClause conNames)
typSig <- sigD helperName $
[t| forall m x. $(conT sMethodTypeName) m
-> (Show ($regOptTcon m) => ToJSON ($regOptTcon m) => FromJSON ($regOptTcon m) => x)
-> x |]
return [typSig, fun]
makeExtendingDatatype :: String -> [Name] -> [(String, TypeQ)] -> DecsQ
makeExtendingDatatype datatypeNameStr extends fields = do
extendFields <- fmap concat $ forM extends $ \e -> do
TyConI (DataD _ _ _ _ [RecC _ eFields] _) <- reify e
return eFields
let datatypeName = mkName datatypeNameStr
insts = [[t| Read |], [t| Show |], [t| Eq |]]
constructor = recC datatypeName combinedFields
userFields = flip map fields $ \(s, typ) -> do
varBangType (mkName s) (bangType (bang noSourceUnpackedness noSourceStrictness) typ)
combinedFields = (map pure extendFields) <> userFields
derivs = [derivClause Nothing insts]
(\a -> [a]) <$> dataD (cxt []) datatypeName [] Nothing [constructor] derivs
lspOptions :: Options
lspOptions = defaultOptions { omitNothingFields = True, fieldLabelModifier = modifier }
where
modifier :: String -> String
modifier "_xdata" = "data"
modifier "_xtype" = "type"
modifier xs = drop 1 xs