module Rasa.Internal.Action ( Action(..) , dispatchActionAsync , asyncActionProvider , bufferDo , addBuffer , getBufRefs , getExt , setExt , overExt , exit , shouldExit , getBuffer , getEditor ) where import Rasa.Internal.ActionMonads import Rasa.Internal.Editor import Rasa.Internal.Buffer import Rasa.Internal.Extensions import qualified Yi.Rope as Y -- | dispatchActionAsync allows you to perform a task asynchronously and then apply the -- result. In @dispatchActionAsync asyncAction@, @asyncAction@ is an IO which resolves to -- an Action, note that the context in which the second action is executed is -- NOT the same context in which dispatchActionAsync is called; it is likely that text and -- other state have changed while the IO executed, so it's a good idea to check -- (inside the applying Action) that things are in a good state before making -- changes. Here's an example: -- -- > asyncCapitalize :: Action () -- > asyncCapitalize = do -- > txt <- focusDo getText -- > -- We give dispatchActionAsync an IO which resolves in an action -- > dispatchActionAsync $ ioPart txt -- > -- > ioPart :: Text -> IO (Action ()) -- > ioPart txt = do -- > result <- longAsyncronousCapitalizationProgram txt -- > -- Note that this returns an Action, but it's still wrapped in IO -- > return $ maybeApplyResult txt result -- > -- > maybeApplyResult :: Text -> Text -> Action () -- > maybeApplyResult oldTxt capitalized = do -- > -- We get the current buffer's text, which may have changed since we started -- > newTxt <- focusDo getText -- > if newTxt == oldTxt -- > -- If the text is the same as it was, we can apply the transformation -- > then focusDo (setText capitalized) -- > -- Otherwise we can choose to re-queue the whole action and try again -- > -- Or we could just give up. -- > else asyncCapitalize dispatchActionAsync :: IO (Action ()) -> Action () dispatchActionAsync ioAction = liftActionF $ DispatchActionAsync ioAction () -- | Don't let the type signature confuse you; it's much simpler than it seems. -- The first argument is a function which takes an action provider; the action provider -- will be passed a dispatch function which can be called as often as you like with @Action ()@s. -- When it is passed an 'Action' it forks off an IO to dispatch that 'Action' to the main event loop. -- Note that the dispatch function calls forkIO on its own; so there's no need for you to do so. -- -- Use this function when you have some long-running process which dispatches multiple 'Action's. -- -- Here's an example which fires a @Timer@ event every second. -- -- > data Timer = TimerFired -- > dispatchTimer :: Action () -- > dispatchTimer = mkDispatcher Timer -- > myTimer :: (Action () -> IO ()) -> IO () -- > myTimer dispatch = forever $ dispatch dispatchTimer >> threadDelay 1000000 -- > -- > myAction :: Action () -- > myAction = onInit $ asyncActionProvider myTimer asyncActionProvider :: ((Action () -> IO ()) -> IO ()) -> Action () asyncActionProvider asyncActionProv = liftActionF $ AsyncActionProvider asyncActionProv () -- | Runs a BufAction over the given BufRefs, returning any results. -- -- Result list is not guaranteed to be the same length or positioning as input BufRef list; some buffers may no -- longer exist. bufferDo :: [BufRef] -> BufAction r -> Action [r] bufferDo bufRefs bufAct = liftActionF $ BufferDo bufRefs bufAct id -- | Adds a new buffer and returns the BufRef addBuffer :: Y.YiString -> Action BufRef addBuffer txt = liftActionF $ AddBuffer txt id -- | Returns an up-to-date list of all 'BufRef's getBufRefs :: Action [BufRef] getBufRefs = liftActionF $ GetBufRefs id -- | Retrieve the entire editor state. This is read-only for logging/rendering/debugging purposes only. getEditor :: Action Editor getEditor = liftActionF $ GetEditor id -- | Retrieve a buffer. This is read-only for logging/rendering/debugging purposes only. getBuffer :: BufRef -> Action (Maybe Buffer) getBuffer bufRef = liftActionF $ GetBuffer bufRef id -- | This signals to the editor that you'd like to shutdown. The current events -- will finish processing, then the 'Rasa.Internal.Listeners.onExit' event will be dispatched, -- then the editor will exit. exit :: Action () exit = liftActionF $ Exit () shouldExit :: Action Bool shouldExit = liftActionF $ ShouldExit id