11{-# LANGUAGE DataKinds #-}
22{-# LANGUAGE DeriveAnyClass #-}
33{-# LANGUAGE DeriveGeneric #-}
4+ {-# LANGUAGE LambdaCase #-}
45{-# LANGUAGE OverloadedStrings #-}
56{-# LANGUAGE TupleSections #-}
67
78module Main (main ) where
89
9- import Control.Monad (unless )
10+ import Control.Monad (unless , (>=>) )
11+ import Control.Monad.Error.Class (throwError )
1012import Control.Monad.IO.Class (liftIO )
1113import Control.Monad.Logger (runLogger' )
14+ import Control.Monad.State (State )
15+ import qualified Control.Monad.State as State
1216import Control.Monad.Trans (lift )
13- import Control.Monad.Error.Class (throwError )
1417import Control.Monad.Trans.Except (ExceptT (.. ), runExceptT )
1518import Control.Monad.Trans.Reader (runReaderT )
1619import qualified Data.Aeson as A
1720import Data.Aeson ((.=) )
1821import qualified Data.ByteString.Lazy as BL
19- import Data.List (foldl' )
22+ import Data.Function (on )
23+ import Data.List (foldl' , nubBy )
24+ import qualified Data.Map as M
2025import Data.String (fromString )
2126import Data.Text (Text )
2227import qualified Data.Text as T
@@ -30,12 +35,14 @@ import qualified Language.PureScript.CodeGen.JS as J
3035import qualified Language.PureScript.CoreFn as CF
3136import qualified Language.PureScript.Errors.JSON as P
3237import qualified Language.PureScript.Interactive as I
38+ import qualified Language.PureScript.TypeChecker.TypeSearch as TS
3339import System.Environment (getArgs )
3440import System.Exit (exitFailure )
3541import System.FilePath ((</>) )
3642import System.FilePath.Glob (glob )
3743import qualified System.IO as IO
3844import System.IO.UTF8 (readUTF8File )
45+ import qualified Text.Parsec.Combinator as Parsec
3946import Web.Scotty
4047import qualified Web.Scotty as Scotty
4148
@@ -90,6 +97,58 @@ server bundled externs initEnv port = do
9097 Scotty. json $ A. object [ " error" .= err ]
9198 Right comp ->
9299 Scotty. json $ A. object [ " js" .= comp ]
100+ get " /search" $ do
101+ query <- param " q"
102+ Scotty. setHeader " Access-Control-Allow-Origin" " *"
103+ Scotty. setHeader " Content-Type" " application/json"
104+ case tryParseType query of
105+ Nothing -> Scotty. json $ A. object [ " error" .= (" Cannot parse type" :: Text ) ]
106+ Just ty -> do
107+ let elabs = lookupAllConstructors initEnv ty
108+ search = M. toList . TS. typeSearch (Just [] ) initEnv (P. emptyCheckState initEnv)
109+ results = nubBy ((==) `on` fst ) $ do
110+ elab <- elabs
111+ let strictMatches = search (replaceTypeVariablesAndDesugar (\ nm s -> P. Skolem nm s (P. SkolemScope 0 ) Nothing ) elab)
112+ flexMatches = search (replaceTypeVariablesAndDesugar (const P. TUnknown ) elab)
113+ take 50 (strictMatches ++ flexMatches)
114+ Scotty. json $ A. object [ " results" .= [ P. showQualified P. runIdent k
115+ | (k, _) <- take 50 results
116+ ]
117+ ]
118+
119+ lookupAllConstructors :: P. Environment -> P. Type -> [P. Type ]
120+ lookupAllConstructors env = P. everywhereOnTypesM $ \ case
121+ P. TypeConstructor (P. Qualified Nothing tyCon) -> P. TypeConstructor <$> lookupConstructor env tyCon
122+ other -> pure other
123+ where
124+ lookupConstructor :: P. Environment -> P. ProperName 'P.TypeName -> [P. Qualified (P. ProperName 'P.TypeName )]
125+ lookupConstructor env nm =
126+ [ q
127+ | (q@ (P. Qualified (Just mn) thisNm), _) <- M. toList (P. types env)
128+ , thisNm == nm
129+ ]
130+
131+ -- | (Consistently) replace unqualified type constructors and type variables with unknowns.
132+ --
133+ -- Also remove the @ParensInType@ Constructor (we need to deal with type operators later at some point).
134+ replaceTypeVariablesAndDesugar :: (Text -> Int -> P. Type ) -> P. Type -> P. Type
135+ replaceTypeVariablesAndDesugar f ty = State. evalState (P. everywhereOnTypesM go ty) (0 , M. empty) where
136+ go = \ case
137+ P. ParensInType ty -> pure ty
138+ P. TypeVar s -> do
139+ (next, m) <- State. get
140+ case M. lookup s m of
141+ Nothing -> do
142+ let ty = f s next
143+ State. put (next + 1 , M. insert s ty m)
144+ pure ty
145+ Just ty -> pure ty
146+ other -> pure other
147+
148+ tryParseType :: Text -> Maybe P. Type
149+ tryParseType = hush (P. lex " " ) >=> hush (P. runTokenParser " " (P. parsePolyType <* Parsec. eof))
150+ where
151+ hush f = either (const Nothing ) Just . f
93152
94153bundle :: IO (Either Bundle. ErrorMessage String )
95154bundle = runExceptT $ do
0 commit comments