PageRenderTime 26ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/server/Scion/Server/Commands.hs

http://github.com/JPMoresmau/scion
Haskell | 833 lines | 620 code | 112 blank | 101 comment | 15 complexity | ba8ddf6ddb5432443fd990ac9cbb9688 MD5 | raw file
  1. {-# LANGUAGE ScopedTypeVariables, CPP, PatternGuards, FlexibleContexts,
  2. ExistentialQuantification #-} -- for 'Cmd'
  3. {-# OPTIONS_GHC -fno-warn-orphans #-}
  4. -- |
  5. -- Module : Scion.Server.Commands
  6. -- Copyright : (c) Thomas Schilling 2008
  7. -- License : BSD-style
  8. --
  9. -- Maintainer : nominolo@gmail.com
  10. -- Stability : experimental
  11. -- Portability : portable
  12. --
  13. -- Commands provided by the server.
  14. --
  15. -- TODO: Need some way to document the wire protocol. Autogenerate?
  16. --
  17. module Scion.Server.Commands (
  18. handleRequest, malformedRequest, -- allCommands, allCommands',
  19. -- these are reused in the vim interface
  20. supportedPragmas, allExposedModules
  21. ) where
  22. import Prelude as P
  23. import Scion.Types
  24. import Scion.Types.Notes
  25. import Scion.Types.Outline
  26. import qualified Scion.Types.JSONDictionary as Dic
  27. import Scion.Utils
  28. import Scion.Session
  29. import Scion.Server.Protocol
  30. import Scion.Inspect
  31. import Scion.Inspect.DefinitionSite
  32. import Scion.Inspect.PackageDB
  33. import Scion.Inspect.Completions
  34. import Scion.Cabal
  35. import Scion.Ghc hiding ( (<+>) )
  36. #if __GLASGOW_HASKELL__ < 700
  37. import DynFlags ( supportedLanguages, allFlags )
  38. #else
  39. import DynFlags ( supportedLanguagesAndExtensions, allFlags )
  40. #endif
  41. import Exception
  42. import FastString
  43. import PprTyThing ( pprTypeForUser )
  44. import qualified Outputable as O ( (<+>),alwaysQualify,neverQualify,text )
  45. import Control.Applicative
  46. import Control.Monad
  47. import Data.List ( nub, isInfixOf )
  48. import Data.Time.Clock ( NominalDiffTime )
  49. import System.Exit ( ExitCode(..) )
  50. import Text.JSON.AttoJSON (JSON,JSValue(..),fromJSON,toJSON)
  51. import qualified Data.ByteString.UTF8 as S
  52. import qualified Data.Map as M
  53. import qualified Data.MultiSet as MS
  54. import GHC.SYB.Utils
  55. #ifndef HAVE_PACKAGE_DB_MODULES
  56. import UniqFM ( eltsUFM )
  57. import Packages ( pkgIdMap )
  58. import Distribution.InstalledPackageInfo
  59. #endif
  60. #if __GLASGOW_HASKELL__ >= 700
  61. supportedLanguages :: [String]
  62. supportedLanguages = supportedLanguagesAndExtensions
  63. #endif
  64. type KeepGoing = Bool
  65. -- a scion request is JS object with 3 keys:
  66. -- method: the method to be called
  67. -- params: arguments to be passed
  68. -- id : this value will be passed back to the client
  69. -- to identify a reply to a specific request
  70. -- asynchronous requests will be implemented in the future
  71. handleRequest :: JSValue -> ScionM (JSValue, KeepGoing)
  72. handleRequest req@(JSObject _) =
  73. let request = do JSString method <- Dic.lookupKey req (Dic.method)
  74. params <- Dic.lookupKey req (Dic.params)
  75. seq_id <- Dic.lookupKey req (Dic.id)
  76. return (method, params, seq_id)
  77. decode_params JSNull arg_parser seq_id = decode_params (Dic.makeObject []) arg_parser seq_id
  78. decode_params args@(JSObject _) arg_parser seq_id =
  79. case unPa arg_parser args of
  80. Left err -> return (paramParseError seq_id err, True)
  81. Right act -> do
  82. r <- handleScionException act
  83. case r of
  84. Error msg -> return (commandExecError seq_id msg, True)
  85. Ok a ->
  86. return (Dic.makeObject
  87. [(Dic.version, str "0.1")
  88. ,(Dic.id, seq_id)
  89. ,(Dic.result, toJSON a)], True)
  90. decode_params _ _ seq_id = return (paramParseError seq_id "Params not an object", True)
  91. -- The quit command's reply
  92. quitReply seq_id = Dic.makeObject [ (Dic.version
  93. , JSString Dic.version01)
  94. , (Dic.result, JSNull)
  95. , (Dic.id, seq_id)
  96. ]
  97. -- The default command dispatcher:
  98. dispatch method params seq_id =
  99. if method /= Dic.quit
  100. then case M.lookup (S.toString method) allCmds of
  101. Nothing -> return (unknownCommand seq_id method, True)
  102. Just (Cmd _ arg_parser) -> decode_params params arg_parser seq_id
  103. else return (quitReply seq_id, False)
  104. in case request of
  105. Nothing -> return (malformedRequest, True)
  106. Just (method, params, seq_id) -> dispatch method params seq_id
  107. handleRequest _ = return(malformedRequest, True)
  108. malformedRequest :: JSValue
  109. malformedRequest = Dic.makeObject
  110. [(Dic.version, JSString Dic.version01)
  111. ,(Dic.error, Dic.makeObject
  112. [(Dic.name, str "MalformedRequest")
  113. ,(Dic.message, str "Request was not a proper request object.")])]
  114. unknownCommand :: JSValue -> S.ByteString -> JSValue
  115. unknownCommand seq_id method= Dic.makeObject
  116. [(Dic.version, JSString Dic.version01)
  117. ,(Dic.id, seq_id)
  118. ,(Dic.error, Dic.makeObject
  119. [(Dic.name, str "UnknownCommand")
  120. ,(Dic.message, str $ "The requested method '"++ (S.toString method) ++"' is not supported.")])]
  121. paramParseError :: JSValue -> String -> JSValue
  122. paramParseError seq_id msg = Dic.makeObject
  123. [(Dic.version, JSString Dic.version01)
  124. ,(Dic.id, seq_id)
  125. ,(Dic.error, Dic.makeObject
  126. [(Dic.name, str "ParamParseError")
  127. ,(Dic.message, str msg)])]
  128. commandExecError :: JSValue -> String -> JSValue
  129. commandExecError seq_id msg = Dic.makeObject
  130. [(Dic.version, JSString Dic.version01)
  131. ,(Dic.id, seq_id)
  132. ,(Dic.error, Dic.makeObject
  133. [(Dic.name, str "CommandFailed")
  134. ,(Dic.message, str msg)])]
  135. allCmds :: M.Map String Cmd
  136. allCmds = M.fromList [ (cmdName c, c) | c <- allCommands ]
  137. ------------------------------------------------------------------------
  138. -- | All Commands supported by this Server.
  139. allCommands :: [Cmd]
  140. allCommands =
  141. [ cmdConnectionInfo
  142. , cmdListsupportedLanguages
  143. , cmdListSupportedPragmas
  144. , cmdListSupportedFlags
  145. , cmdListCabalComponents
  146. , cmdListCabalConfigurations
  147. , cmdWriteSampleConfig
  148. , cmdListRdrNamesInScope
  149. , cmdListExposedModules
  150. , cmdCurrentComponent
  151. , cmdSetVerbosity
  152. , cmdGetVerbosity
  153. , cmdLoad
  154. , cmdDumpSources
  155. , cmdThingAtPoint
  156. , cmdSetGHCVerbosity
  157. , cmdBackgroundTypecheckFile
  158. , cmdBackgroundTypecheckArbitrary
  159. , cmdAddCmdLineFlag
  160. , cmdForceUnload
  161. , cmdDumpDefinedNames
  162. , cmdDefinedNames
  163. , cmdNameDefinitions
  164. , cmdIdentify
  165. , cmdDumpModuleGraph
  166. , cmdDumpNameDB
  167. , cmdToplevelNames
  168. , cmdOutline
  169. , cmdTokens
  170. , cmdTokenAtPoint
  171. , cmdTokenPreceding
  172. , cmdTokenTypes
  173. -- , cmdParseCabal
  174. -- , cmdParseCabalArbitrary
  175. , cmdCabalDependencies
  176. , cmdModuleGraph
  177. , cmdCompletionTypes
  178. , cmdCompletionVarIds
  179. , cmdCompletionClassTypeNames
  180. , cmdOccurrences
  181. , cmdSetUserFlags
  182. ]
  183. ------------------------------------------------------------------------------
  184. data OkErr a = Error String | Ok a
  185. -- encode expected errors as proper return values
  186. handleScionException :: ScionM a -> ScionM (OkErr a)
  187. handleScionException m = ((((do
  188. r <- m
  189. return (Ok r)
  190. `gcatch` \(e :: SomeScionException) -> return (Error ("Scion:" ++ (show e))))
  191. `gcatch` \(e' :: GhcException) ->
  192. case e' of
  193. Panic _ -> throw e'
  194. InstallationError _ -> throw e'
  195. #if __GLASGOW_HASKELL__ < 700
  196. Interrupted -> throw e'
  197. #endif
  198. _ -> return (Error ("GHC:" ++ (show e'))))
  199. `gcatch` \(e :: ExitCode) ->
  200. -- client code may not exit the server!
  201. return (Error (show e)))
  202. `gcatch` \(e :: IOError) ->
  203. return (Error ("IO:" ++ (show e))))
  204. -- `gcatch` \(e :: SomeException) ->
  205. -- liftIO (print e) >> liftIO (throwIO e)
  206. ------------------------------------------------------------------------------
  207. -- | Parsed argument ("Pa") type
  208. newtype Pa a = Pa {
  209. unPa :: JSValue
  210. -> Either String a
  211. }
  212. instance Monad Pa where
  213. return x = Pa $ \_ -> Right x
  214. m >>= k = Pa $ \req ->
  215. case unPa m req of
  216. Left err -> Left err
  217. Right a -> unPa (k a) req
  218. fail msg = Pa $ \_ -> Left msg
  219. withReq :: (JSValue -> Pa a) -> Pa a
  220. withReq f = Pa $ \req -> unPa (f req) req
  221. reqArg' :: JSON a => String -> (a -> b) -> (b -> r) -> Pa r
  222. reqArg' name trans f = withReq $ \req ->
  223. case Dic.lookupKey req (S.fromString name) of
  224. Nothing -> fail $ "required arg missing: " ++ name
  225. Just x ->
  226. case fromJSON x of
  227. Nothing -> fail $ "could not decode: '" ++ name ++ "'"
  228. Just a -> return (f (trans a))
  229. optArg' :: JSON a => String -> b -> (a -> b) -> (b -> r) -> Pa r
  230. optArg' name dflt trans f = withReq $ \req ->
  231. case Dic.lookupKey req (S.fromString name) of
  232. Nothing -> return (f dflt)
  233. Just x ->
  234. case fromJSON x of
  235. Nothing -> fail $ "could not decode: " ++ name -- ++ " - " ++ n
  236. Just a -> return (f (trans a))
  237. reqArg :: JSON a => String -> (a -> r) -> Pa r
  238. reqArg name f = reqArg' name id f
  239. optArg :: JSON a => String -> a -> (a -> r) -> Pa r
  240. optArg name dflt f = optArg' name dflt id f
  241. -- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  242. -- Commonly used arguments:
  243. -- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  244. -- | Command takes no arguments
  245. noArgs :: r -> Pa r
  246. noArgs = return
  247. -- | Command takes a file name argument
  248. fileNameArg :: (String -> r) -> Pa r
  249. fileNameArg = reqArg' "file" S.toString
  250. -- | Command takes a document argument
  251. docContentsArg :: (String -> r) -> Pa r
  252. docContentsArg = reqArg' "contents" S.toString
  253. -- | Command takes an optional literate Haskell flag
  254. literateFlagOpt :: (Bool -> r) -> Pa r
  255. literateFlagOpt = optArg' "literate" False decodeBool
  256. -- | Command takes required line and column arguments
  257. lineColumnArgs :: (Int -> Int -> r) -> Pa r
  258. lineColumnArgs = reqArg "line" <&> reqArg "column"
  259. -- | Combine two arguments.
  260. --
  261. -- TODO: explain type
  262. infixr 1 <&>
  263. (<&>) :: (a -> Pa b)
  264. -> (b -> Pa c)
  265. -> a -> Pa c
  266. a1 <&> a2 = \f -> do f' <- a1 f; a2 f'
  267. data Cmd = forall a. JSON a => Cmd String (Pa (ScionM a))
  268. cmdName :: Cmd -> String
  269. cmdName (Cmd n _) = n
  270. ------------------------------------------------------------------------
  271. -- | Used by the client to initialise the connection.
  272. cmdConnectionInfo :: Cmd
  273. cmdConnectionInfo = Cmd "connection-info" $ noArgs worker
  274. where
  275. worker = let pid = 0 :: Int in -- TODO for linux: System.Posix.Internals (c_getpid)
  276. return $ Dic.makeObject
  277. [(Dic.version, toJSON scionVersion)
  278. ,(Dic.pid, toJSON pid)]
  279. decodeBool :: JSValue -> Bool
  280. decodeBool (JSBool b) = b
  281. decodeBool _ = error "no bool"
  282. {- Unused at the moment
  283. decodeExtraArgs :: JSValue -> [String]
  284. decodeExtraArgs JSNull = []
  285. decodeExtraArgs (JSString s) = words (S.toString s) -- TODO: check shell-escaping
  286. decodeExtraArgs (JSArray arr) = [ S.toString s | JSString s <- arr ]
  287. decodeExtraArgs (JSBool b) = [ (show b) ]
  288. decodeExtraArgs (JSNumber b) = [ (show b) ]
  289. decodeExtraArgs (JSObject _) = undefined -}
  290. instance JSON Component where
  291. fromJSON obj = do
  292. case fromJSON obj of
  293. Just (c :: CabalComponent) -> return $ Component c
  294. Nothing -> case fromJSON obj of
  295. Just (c :: FileComp) -> return $ Component c
  296. Nothing -> fail $ "Unknown component" ++ show obj
  297. toJSON (Component c) = toJSON c
  298. instance JSON CompilationResult where
  299. toJSON (CompilationResult suc notes time) =
  300. Dic.makeObject [(Dic.succeeded, JSBool suc)
  301. ,(Dic.notes, toJSON notes)
  302. ,(Dic.duration, toJSON time)]
  303. fromJSON obj@(JSObject _) = do
  304. JSBool suc <- Dic.lookupKey obj Dic.succeeded
  305. notes <- fromJSON =<< Dic.lookupKey obj Dic.notes
  306. dur <- fromJSON =<< Dic.lookupKey obj Dic.duration
  307. return (CompilationResult suc notes dur)
  308. fromJSON _ = fail "compilation-result"
  309. instance JSON (MS.MultiSet Note) where
  310. toJSON ms = toJSON (MS.toList ms)
  311. fromJSON o = MS.fromList <$> fromJSON o
  312. instance JSON Note where
  313. toJSON (Note note_kind loc msg) =
  314. Dic.makeObject [(Dic.kind, toJSON note_kind)
  315. ,(Dic.location, toJSON loc)
  316. ,(Dic.message, JSString (S.fromString msg))]
  317. fromJSON obj@(JSObject _) = do
  318. note_kind <- fromJSON =<< Dic.lookupKey obj Dic.kind
  319. loc <- fromJSON =<< Dic.lookupKey obj Dic.location
  320. JSString s <- Dic.lookupKey obj Dic.message
  321. return (Note note_kind loc (S.toString s))
  322. fromJSON _ = fail "note"
  323. instance (JSON a, JSON b)=> JSON (Either a b) where
  324. toJSON (Left a)=Dic.makeObject [(Dic.leftC,toJSON a)]
  325. toJSON (Right a)=Dic.makeObject [(Dic.rightC,toJSON a)]
  326. fromJSON _ = fail "Either"
  327. --instance (JSON a)=>JSON (Maybe a) where
  328. -- toJSON (Nothing)=Dic.makeObject [(Dic.nothingC,JSNull)]
  329. -- toJSON (Just a)=Dic.makeObject [(Dic.justC,toJSON a)]
  330. -- fromJSON _ = fail "Maybe"
  331. str :: String -> JSValue
  332. str = JSString . S.fromString
  333. instance JSON NoteKind where
  334. toJSON ErrorNote = JSString Dic.error
  335. toJSON WarningNote = JSString Dic.warning
  336. toJSON InfoNote = JSString Dic.info
  337. toJSON OtherNote = JSString Dic.other
  338. fromJSON (JSString s) =
  339. case lookup s
  340. [(Dic.error, ErrorNote), (Dic.warning, WarningNote)
  341. ,(Dic.info, InfoNote), (Dic.other, OtherNote)]
  342. of Just x -> return x
  343. Nothing -> fail "note-kind"
  344. fromJSON _ = fail "note-kind"
  345. instance JSON Location where
  346. toJSON loc | not (isValidLoc loc) =
  347. Dic.makeObject [(Dic.noLocation, str (noLocText loc))]
  348. toJSON loc | (src, l0, c0, l1, c1) <- viewLoc loc =
  349. Dic.makeObject [case src of
  350. FileSrc f -> (Dic.file, str (toFilePath f))
  351. OtherSrc s -> (Dic.other, str s)
  352. ,(Dic.region, JSArray (map toJSON [l0,c0,l1,c1]))]
  353. fromJSON obj@(JSObject _) = do
  354. src <- (do JSString f <- Dic.lookupKey obj Dic.file
  355. return (FileSrc (mkAbsFilePath "/" (S.toString f))))
  356. <|>
  357. (do JSString s <- Dic.lookupKey obj Dic.other
  358. return (OtherSrc (S.toString s)))
  359. JSArray ls <- Dic.lookupKey obj Dic.region
  360. case mapM fromJSON ls of
  361. Just [l0,c0,l1,c1] -> return (mkLocation src l0 c0 l1 c1)
  362. _ -> fail "region"
  363. fromJSON _ = fail "location"
  364. instance JSON NominalDiffTime where
  365. toJSON t = JSNumber (fromRational (toRational t))
  366. fromJSON (JSNumber n) = return $ fromRational (toRational n)
  367. fromJSON _ = fail "diff-time"
  368. instance JSON OutlineDef where
  369. toJSON t =
  370. Dic.makeObject $
  371. [(Dic.name, str $ case od_name t of
  372. Left n -> showSDocUnqual n
  373. Right s -> s)
  374. ,(Dic.location, toJSON $ od_loc t)
  375. ,(Dic.block, toJSON $ od_block t)
  376. ,(Dic.typ, str $ od_type t)]
  377. ++
  378. (case od_parentName t of
  379. Just (n,typ) ->
  380. [(Dic.parent, Dic.makeObject [(Dic.name, str $ showSDocUnqual $ n)
  381. ,(Dic.typ, str typ)])]
  382. Nothing -> [])
  383. fromJSON _ = fail "OutlineDef"
  384. cmdListsupportedLanguages :: Cmd
  385. cmdListsupportedLanguages = Cmd "list-supported-languages" $ noArgs cmd
  386. where cmd = return (map S.fromString supportedLanguages)
  387. cmdListSupportedPragmas :: Cmd
  388. cmdListSupportedPragmas =
  389. Cmd "list-supported-pragmas" $ noArgs $ return supportedPragmas
  390. supportedPragmas :: [String]
  391. supportedPragmas =
  392. [ "OPTIONS_GHC", "LANGUAGE", "INCLUDE", "WARNING", "DEPRECATED"
  393. , "INLINE", "NOINLINE", "RULES", "SPECIALIZE", "UNPACK", "SOURCE"
  394. , "SCC"
  395. , "LINE" -- XXX: only used by code generators, still include?
  396. ]
  397. cmdListSupportedFlags :: Cmd
  398. cmdListSupportedFlags =
  399. Cmd "list-supported-flags" $ noArgs $ return (nub allFlags)
  400. cmdListRdrNamesInScope :: Cmd
  401. cmdListRdrNamesInScope =
  402. Cmd "list-rdr-names-in-scope" $ noArgs $ cmd
  403. where cmd = do
  404. rdr_names <- getNamesInScope
  405. return (map (showSDoc . ppr) rdr_names)
  406. cmdListCabalComponents :: Cmd
  407. cmdListCabalComponents =
  408. Cmd "list-cabal-components" $ reqArg' "cabal-file" S.toString $ cmd
  409. where cmd cabal_file = cabalProjectComponents cabal_file
  410. --cmdParseCabal :: Cmd
  411. --cmdParseCabal =
  412. -- Cmd "parse-cabal" $ reqArg' "cabal-file" S.toString $ cmd
  413. -- where cmd _cabal_file = return (JSObject M.empty) --liftM toJSON $ cabalParse cabal_file
  414. --
  415. --cmdParseCabalArbitrary :: Cmd
  416. --cmdParseCabalArbitrary =
  417. -- Cmd "parse-cabal-arbitrary" $ docContentsArg $ cmd
  418. -- where cmd cabal_contents = cabalParseArbitrary cabal_contents
  419. cmdCabalDependencies :: Cmd
  420. cmdCabalDependencies =
  421. Cmd "cabal-dependencies" $ reqArg' "cabal-file" S.toString $ cmd
  422. where cmd cabal_file = do
  423. dep<- cabalDependencies cabal_file
  424. case dep of
  425. Left err->return $ Left err
  426. Right depArr -> return $ Right $
  427. (JSArray $ map (\(x,y)->Dic.makeObject [(S.fromString x,JSArray $ map toJSON y)]) depArr)
  428. -- return all cabal configurations.
  429. -- currently this just globs for * /setup-config
  430. -- in the future you may write a config file describing the most common configuration settings
  431. cmdListCabalConfigurations :: Cmd
  432. cmdListCabalConfigurations =
  433. Cmd "list-cabal-configurations" $
  434. reqArg' "cabal-file" S.toString <&>
  435. optArg' "type" "uniq" id <&>
  436. optArg' "scion-default" False decodeBool $ cmd
  437. where cmd _cabal_file _type' _scionDefault = return (JSArray []) -- liftM toJSON $ cabalConfigurations cabal_file type' scionDefault
  438. cmdWriteSampleConfig :: Cmd
  439. cmdWriteSampleConfig =
  440. Cmd "write-sample-config" $ fileNameArg cmd
  441. where cmd fp = liftIO $ writeSampleConfig fp
  442. allExposedModules :: ScionM [ModuleName]
  443. #ifdef HAVE_PACKAGE_DB_MODULES
  444. allExposedModules = map moduleName `fmap` packageDbModules True
  445. #else
  446. -- This implementation requires our Cabal to be the same as GHC's.
  447. allExposedModules = do
  448. dflags <- getSessionDynFlags
  449. let pkg_db = pkgIdMap (pkgState dflags)
  450. return $ P.concat (map exposedModules (filter exposed (eltsUFM pkg_db)))
  451. #endif
  452. cmdListExposedModules :: Cmd
  453. cmdListExposedModules = Cmd "list-exposed-modules" $ noArgs $ cmd
  454. where cmd = do
  455. mod_names <- allExposedModules
  456. return $ map (showSDoc . ppr) mod_names
  457. cmdSetGHCVerbosity :: Cmd
  458. cmdSetGHCVerbosity =
  459. Cmd "set-ghc-verbosity" $ reqArg "level" $ setGHCVerbosity
  460. cmdBackgroundTypecheckFile :: Cmd
  461. cmdBackgroundTypecheckFile =
  462. Cmd "background-typecheck-file" $ fileNameArg $ cmd
  463. where cmd fname = backgroundTypecheckFile fname
  464. cmdBackgroundTypecheckArbitrary :: Cmd
  465. cmdBackgroundTypecheckArbitrary =
  466. Cmd "background-typecheck-arbitrary" $
  467. fileNameArg <&>
  468. docContentsArg $ cmd
  469. where cmd fname contents = backgroundTypecheckArbitrary fname contents
  470. cmdForceUnload :: Cmd
  471. cmdForceUnload = Cmd "force-unload" $ noArgs $ unload
  472. cmdAddCmdLineFlag :: Cmd
  473. cmdAddCmdLineFlag =
  474. Cmd "add-command-line-flag" $
  475. optArg' "flag" "" S.toString <&>
  476. optArg' "flags" [] (map S.toString) $ cmd
  477. where cmd flag flags' = do
  478. addCmdLineFlags $ (if flag == "" then [] else [flag]) ++ flags'
  479. return JSNull
  480. cmdThingAtPoint :: Cmd
  481. cmdThingAtPoint =
  482. Cmd "thing-at-point" $
  483. fileNameArg <&> lineColumnArgs <&> optArg' "qualify" False decodeBool <&> optArg' "typed" True decodeBool $ cmd
  484. where
  485. cmd fname line col qual typed= do
  486. let loc = srcLocSpan $ mkSrcLoc (fsLit fname) line (scionColToGhcCol col)
  487. tc_res <- getSessionSelector bgTcCache
  488. case tc_res of
  489. Just (Typechecked tcm) -> do
  490. let f=(if typed then (doThingAtPointTyped $ typecheckedSource tcm) else (doThingAtPointUntyped $ renamedSource tcm))
  491. --tap<- doThingAtPoint loc qual typed tcm (if typed then (typecheckedSource tcm) else (renamedSource tcm))
  492. tap<-f loc qual tcm
  493. --(if typed then (doThingAtPointTyped $ typecheckedSource tcm)
  494. -- else doThingAtPointTyped (renamedSource tcm) loc qual tcm
  495. return $ Just tap
  496. _ -> return Nothing
  497. doThingAtPointTyped :: TypecheckedSource -> SrcSpan -> Bool -> TypecheckedModule -> ScionM String
  498. doThingAtPointTyped src loc qual tcm=do
  499. let in_range = overlaps loc
  500. let r = searchBindBag in_range noSrcSpan src
  501. unqual <- if qual
  502. then return $ O.alwaysQualify
  503. else unqualifiedForModule tcm
  504. --liftIO $ putStrLn $ showData TypeChecker 2 src
  505. return $ case pathToDeepest r of
  506. Nothing -> "no info"
  507. Just (x,xs) ->
  508. case typeOf (x,xs) of
  509. Just t ->
  510. showSDocForUser unqual
  511. (prettyResult x O.<+> dcolon O.<+>
  512. pprTypeForUser True t)
  513. _ -> showSDocForUser unqual (prettyResult x) --(Just (showSDocDebug (ppr x O.$$ ppr xs )))
  514. doThingAtPointUntyped :: (Search id a, OutputableBndr id) => a -> SrcSpan -> Bool -> TypecheckedModule -> ScionM String
  515. doThingAtPointUntyped src loc qual tcm =do
  516. let in_range = overlaps loc
  517. let r = findHsThing in_range src
  518. unqual <- if qual
  519. then return $ O.neverQualify
  520. else unqualifiedForModule tcm
  521. return $ case pathToDeepest r of
  522. Nothing -> "no info"
  523. Just (x,_) ->
  524. if qual
  525. then showSDocForUser unqual ((qualifiedResult x) O.<+> (O.text $ haddockType x))
  526. else showSDocForUser unqual ((prettyResult x) O.<+> (O.text $ haddockType x))
  527. cmdToplevelNames :: Cmd
  528. cmdToplevelNames=
  529. Cmd "top-level-names" $ noArgs $ cmd
  530. where
  531. cmd =do
  532. tc_res <- getSessionSelector bgTcCache
  533. case tc_res of
  534. Just m -> do
  535. return $ map showSDocDump $ toplevelNames m
  536. _ -> return []
  537. cmdOutline :: Cmd
  538. cmdOutline =
  539. Cmd "outline" $ optArg' "trimFile" True decodeBool $ cmd
  540. where
  541. cmd trim = do
  542. root_dir <- projectRootDir
  543. tc_res <- getSessionSelector bgTcCache
  544. case tc_res of
  545. Just m -> do
  546. let f = if trim then trimLocationFile else id
  547. return $ f $ outline root_dir m
  548. _ -> return []
  549. cmdTokens :: Cmd
  550. cmdTokens =
  551. Cmd "tokens" $ docContentsArg cmd
  552. where cmd contents = do
  553. root_dir <- projectRootDir
  554. tokensArbitrary root_dir contents
  555. cmdTokenAtPoint :: Cmd
  556. cmdTokenAtPoint =
  557. Cmd "token-at-point" $ cmdArgs tokenAtPoint
  558. where cmdArgs = docContentsArg <&> lineColumnArgs <&> literateFlagOpt
  559. tokenAtPoint contents line column literate =
  560. projectRootDir
  561. >>= (\rootDir -> tokenArbitraryAtPoint rootDir contents line column literate)
  562. cmdTokenPreceding :: Cmd
  563. cmdTokenPreceding =
  564. Cmd "token-preceding" $ cmdArgs tokenPreceding
  565. where cmdArgs = docContentsArg <&> optArg "numTokens" (1 :: Int) <&> lineColumnArgs <&> literateFlagOpt
  566. tokenPreceding contents numToks line column literate =
  567. projectRootDir
  568. >>= (\rootDir -> tokensArbitraryPreceding rootDir contents numToks line column literate)
  569. cmdOccurrences :: Cmd
  570. cmdOccurrences =
  571. Cmd "occurrences" $ cmdArgs tokenPreceding
  572. where cmdArgs = docContentsArg <&> reqArg "query" <&> literateFlagOpt
  573. tokenPreceding contents query literate =
  574. projectRootDir
  575. >>= (\rootDir -> occurrences rootDir contents query literate)
  576. cmdSetUserFlags :: Cmd
  577. cmdSetUserFlags =
  578. Cmd "set-user-flags" $ reqArg "user-flags" <&> reqArg' "cabal-file" S.toString $ cmd
  579. where cmd user_flags cabal_file= do
  580. modifySessionState $ \sess ->
  581. sess { userFlags = user_flags }
  582. cabalClean cabal_file
  583. cmdTokenTypes :: Cmd
  584. cmdTokenTypes =
  585. Cmd "token-types" $ docContentsArg <&> literateFlagOpt $ cmd
  586. where cmd contents literate= do
  587. root_dir <- projectRootDir
  588. tokenTypesArbitrary root_dir contents literate
  589. {--mb_modsum <- filePathToProjectModule fname
  590. case mb_modsum of
  591. Nothing -> do
  592. return $ Left "Could not find file in module graph."
  593. Just modsum -> do
  594. ts<-tokens root_dir $ ms_mod modsum
  595. return $ Right ts--}
  596. cmdDumpSources :: Cmd
  597. cmdDumpSources = Cmd "dump-sources" $ noArgs $ cmd
  598. where
  599. cmd = do
  600. tc_res <- getSessionSelector bgTcCache
  601. case tc_res of
  602. Just (Typechecked tcm)
  603. | Just rn <- renamedSourceGroup `fmap` renamedSource tcm ->
  604. do let tc = typecheckedSource tcm
  605. liftIO $ putStrLn $ showSDocDump $ ppr rn
  606. liftIO $ putStrLn $ showData TypeChecker 2 tc
  607. return ()
  608. _ -> return ()
  609. cmdLoad :: Cmd
  610. cmdLoad = Cmd "load" $ reqArg "component" <&> optArg "options" defaultLoadOptions $ cmd
  611. where
  612. cmd comp options = do
  613. loadComponent' comp options
  614. `gcatch` \(e' :: GhcException) ->
  615. case e' of
  616. CmdLineError s | isInfixOf "cannot satisfy -package-id" s,
  617. Component c <- comp -> do
  618. componentClean c
  619. loadComponent' comp options
  620. _ -> throw e'
  621. cmdSetVerbosity :: Cmd
  622. cmdSetVerbosity =
  623. Cmd "set-verbosity" $ reqArg "level" $ cmd
  624. where cmd v = setVerbosity (intToVerbosity v)
  625. cmdGetVerbosity :: Cmd
  626. cmdGetVerbosity = Cmd "get-verbosity" $ noArgs $ verbosityToInt <$> getVerbosity
  627. -- rename to GetCurrentComponent?
  628. cmdCurrentComponent :: Cmd
  629. cmdCurrentComponent = Cmd "current-component" $ noArgs $ getActiveComponent
  630. cmdDumpDefinedNames :: Cmd
  631. cmdDumpDefinedNames = Cmd "dump-defined-names" $ noArgs $ cmd
  632. where
  633. cmd = getSessionSelector defSiteDB
  634. >>= (\db -> liftIO $ putStrLn $ dumpDefSiteDB db)
  635. cmdDefinedNames :: Cmd
  636. cmdDefinedNames = Cmd "defined-names" $ noArgs $ definedNames <$> getSessionSelector defSiteDB
  637. cmdNameDefinitions :: Cmd
  638. cmdNameDefinitions =
  639. Cmd "name-definitions" $ reqArg' "name" S.toString $ cmd
  640. where cmd nm = do
  641. -- mc<-getSessionSelector moduleCache
  642. -- liftIO $ putStrLn $ ("moduleCache values:" ++ (show $ moduleCacheSize mc))
  643. db <- getSessionSelector defSiteDB
  644. let nms=comps nm
  645. --liftIO $ putStrLn $ last nms
  646. --liftIO $ putStrLn $ show $ map (\(_,b)->showSDocForUser alwaysQualify $ ppr $ getName b) $ lookupDefSite db (last nms)
  647. return $ map fst
  648. $ filter (\(_,b)->nm == showSDocForUser alwaysQualify (ppr $ getName b))
  649. $ lookupDefSite db (last nms)
  650. comps :: String -> [String]
  651. comps s = case dropWhile ('.'==) s of
  652. "" -> []
  653. s' -> w : comps s''
  654. where (w, s'') =
  655. break ('.'==) s'
  656. cmdIdentify :: Cmd
  657. cmdIdentify =
  658. Cmd "client-identify" $ reqArg' "name" S.toString $ cmd
  659. where cmd c = modifySessionState $ \s -> s { client = c }
  660. cmdDumpModuleGraph :: Cmd
  661. cmdDumpModuleGraph =
  662. Cmd "dump-module-graph" $ noArgs $ cmd
  663. where
  664. cmd = do
  665. mg <- getModuleGraph
  666. liftIO $ printDump (ppr mg)
  667. return ()
  668. cmdModuleGraph :: Cmd
  669. cmdModuleGraph =
  670. Cmd "module-graph" $ noArgs $ cmd
  671. where
  672. cmd = do
  673. mg <- getModuleGraph
  674. return $ map (showSDoc . ppr . moduleName . ms_mod) mg
  675. cmdDumpNameDB :: Cmd
  676. cmdDumpNameDB =
  677. Cmd "dump-name-db" $ noArgs $ buildNameDB >>= dumpNameDB >> return ()
  678. -- | Type name completions: generate the list of type names currently visible within the
  679. -- current module. The IDE is responsible for prefix or name filtering.
  680. -- FIXME: Use focused_mod here, when available, just like what bgTypeCheck does.
  681. cmdCompletionTypes :: Cmd
  682. cmdCompletionTypes = Cmd "completion-types" $ fileNameArg $ cmd
  683. where
  684. currentModTyCons (Just modSum) =
  685. getSessionSelector moduleCache
  686. >>= (\mCache -> case M.lookup (ms_mod modSum) mCache of
  687. Just mcd -> return $ tyCons mcd
  688. Nothing -> return [])
  689. currentModTyCons Nothing = return []
  690. allTyCons projMod = liftM2 (++) (getTypeCompletions projMod) (currentModTyCons projMod)
  691. cmd fname = filePathToProjectModule fname
  692. >>= allTyCons
  693. -- | Variable id completions: generate the list of variable identifiers currently visible within
  694. -- the current module. The IDE is responsible for prefix or name filtering.
  695. -- FIXME: Use focused_mod here, when available, mimicing bgTypeCheck.
  696. cmdCompletionVarIds :: Cmd
  697. cmdCompletionVarIds = Cmd "completion-varIds" $ fileNameArg $ generateCompletions getVarIdCompletions
  698. -- | Class type name completions: generate the list of class names currently visible within the
  699. -- current module. The IDE is repsonsible for prefix or name filering.
  700. -- FIXME: Use focused_mod here, when available, mimicking bgTypeCheck.
  701. cmdCompletionClassTypeNames :: Cmd
  702. cmdCompletionClassTypeNames = Cmd "completion-classTypeNames" $ fileNameArg $ generateCompletions getClassTypeNameCompletions
  703. -- | Generate the completion tuple list using a completion function and file name
  704. generateCompletions :: forall a.
  705. (Maybe ModSummary -> ScionM a) -- ^ The completion function (see Completions.hs)
  706. -> FilePath -- ^ File name, if there is no currently focused module.
  707. -> ScionM a
  708. generateCompletions completionFunc fpath = withSessionState $ getCompletions fpath
  709. where
  710. getCompletions fname scion =
  711. case focusedModule scion of
  712. modsum@(Just ms) -> validFocusedModuleSource fname (ml_hs_file (ms_location ms)) modsum
  713. Nothing -> getCompletionsFromProjectModule fname
  714. validFocusedModuleSource fname (Just f) modsum
  715. | f == fname
  716. = completionFunc modsum
  717. | otherwise
  718. = getCompletionsFromProjectModule fname
  719. validFocusedModuleSource fname Nothing _ = getCompletionsFromProjectModule fname
  720. -- Default: If we can't use focusedModule, find the module summary in the module graph
  721. getCompletionsFromProjectModule fname = filePathToProjectModule fname >>= completionFunc