packages feed

ariadne-0.1.2.2: Ariadne/ModuleDB.hs

{-# LANGUAGE FlexibleContexts, MultiParamTypeClasses #-}

module Ariadne.ModuleDB
  ( sendRequestAsync
  , sendRequestSync
  , Request(..)
  , answer
  , withModuleDB
  )
  where

import Ariadne.Types
import qualified Ariadne.SrcMap as SrcMap
import Ariadne.ModuleDB.Types
import Ariadne.ModuleDB.ParseResolve

import Language.Haskell.Names
import Language.Haskell.Exts.Annotated hiding (parse)

import Control.Monad.Trans
import Control.Monad
import Control.Exception
import Control.Monad.State
import Text.Printf
import System.Directory
import qualified Data.Set as Set
import qualified Data.Map as Map
import qualified System.Log.Logger as L
import Data.Maybe
import Data.Proxy

import Control.Concurrent
import Control.Concurrent.Async
import Control.Concurrent.STM

import Data.Lens

-- | Send a request and wait for its completion
sendRequestSync :: ModuleDB -> Request -> IO ()
sendRequestSync (ModuleDB { requestChan = chan }) req = do
  mvar <- newEmptyMVar
  let cb = putMVar mvar ()
  writeChan chan (req, cb)
  takeMVar mvar

-- | Send a request and return immediately
sendRequestAsync :: ModuleDB -> Request -> IO ()
sendRequestAsync (ModuleDB { requestChan = chan }) req =
  writeChan chan (req, return ())

-- | A ModuleDB create/destroy bracket
withModuleDB :: (ModuleDB -> IO a) -> IO a
withModuleDB act = do
  chan <- newChan
  storageV <- atomically $ newTVar emptyStorage
  withAsync (respond chan storageV) $ \asy ->
    act $ ModuleDB { requestChan = chan, storage = storageV }

respond :: Chan (Request, Callback) -> TVar Storage -> IO ()
respond chan storageV = forever $ do
  storage <- atomically $ readTVar storageV
  (req, cb) <- readChan chan
  storage' <- execStateT (actOn req) storage
  atomically $ writeTVar storageV storage'
  cb

-- | Handle a request, operating with the Storage in the State monad
-- (a helper for respond)
actOn :: Request -> StateT Storage IO ()
actOn (Include path) = include path
actOn (Update path) = update path

-- | Get the current state of ModuleDB's location -> origin map
getSrcMap :: ModuleDB -> FilePath -> IO (Maybe (SrcMap.SrcMap Origin))
getSrcMap (ModuleDB { storage = storageV }) path = do
  storage <- atomically $ readTVar storageV
  return $ Map.lookup path $ storage ^. moduleSrcMaps

-- | Compute a result for the query
answer :: ModuleDB -> String -> Int -> Int -> IO (Maybe Origin)
answer moduleDB path line col = do
  mbSrcMap <- getSrcMap moduleDB path
  return $
    maybe
      (Just $ ResolveError "No information about this module")
      (SrcMap.lookup noLoc { srcLine = line, srcColumn = col })
      mbSrcMap