-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Automate keyboard\/mouse\/clipboard\/application interaction. -- -- a platform-independent interface for scripting your manual computer -- workflows. -- -- documention at Workflow.Core. -- -- see the source of Workflow.Example for an example. -- -- transitive dependencies: -- @package workflow-types @version 0.0.1 module Workflow.Reexports -- | A class for monads in which exceptions may be thrown. -- -- Instances should obey the following law: -- --
-- throwM e >> x = throwM e ---- -- In other words, throwing an exception short-circuits the rest of the -- monadic computation. class Monad m => MonadThrow (m :: * -> *) -- | Monads provide substitution (fmap) and renormalization -- (join): -- --
-- m >>= f = join (fmap f m) ---- -- A free Monad is one that does no work during the normalization -- step beyond simply grafting the two monadic values together. -- -- [] is not a free Monad (in this sense) because -- join [[a]] smashes the lists flat. -- -- On the other hand, consider: -- --
-- data Tree a = Bin (Tree a) (Tree a) | Tip a ---- --
-- instance Monad Tree where -- return = Tip -- Tip a >>= f = f a -- Bin l r >>= f = Bin (l >>= f) (r >>= f) ---- -- This Monad is the free Monad of Pair: -- --
-- data Pair a = Pair a a ---- -- And we could make an instance of MonadFree for it directly: -- --
-- instance MonadFree Pair Tree where -- wrap (Pair l r) = Bin l r ---- -- Or we could choose to program with Free Pair instead -- of Tree and thereby avoid having to define our own -- Monad instance. -- -- Moreover, Control.Monad.Free.Church provides a MonadFree -- instance that can improve the asymptotic complexity of code -- that constructs free monads by effectively reassociating the use of -- (>>=). You may also want to take a look at the -- kan-extensions package -- (http://hackage.haskell.org/package/kan-extensions). -- -- See Free for a more formal definition of the free Monad -- for a Functor. class Monad m => MonadFree (f :: * -> *) (m :: * -> *) | m -> f module Workflow.Extra failed :: (MonadThrow m) => String -> m a module Workflow.Types -- | platform-agnostic workflows, which can be interpreted by -- platform-specific bindings. -- -- Naming: WorkflowF for "Workflow Functor". -- -- NOTE: currently, no error codes are returned (only ())). this -- (1) simplifies bindings and (2) saves the user from explicitly -- ignoring action results (e.g. _ <- getClipboard). later, -- they can be supported, alongside wrappers that return () and -- throw SomeException and provide the same simple API. since -- the intented usage of workflows are as user-facing (often -- user-written) scripts, and the monad that satisifes MonadWorkflow will -- often satisify MonadIO too, convenient partial functions that throw a -- helpful error message to stdout (the error codes should be converted -- to their error messages) should suffice. and either way, is strictly -- better for the user than ignoring, as the exceptions can always be -- caught, or not displayed. data WorkflowF k -- | press the Key while the Modifiers are held down. sent to -- the current application. TODO | SendKeyChordTo Application [Modifier] -- Key k -- ^ TODO | SendKeyChordTo Window [Modifier] Key k -- ^ versus -- unary: ([Modifier], Key) rn SendChord SendKeyChord :: [Modifier] -> Key -> k -> WorkflowF k -- | a logical grouping for: (1) unicode support (2) efficiency and (3) -- debugging. sent to the current application. SendText :: String -> k -> WorkflowF k -- | click the button, some number of times, holding down the modifiers -- derived, make method, not constructor. sent to the current -- application. SendMouseClick :: [Modifier] -> Natural -> MouseButton -> k -> WorkflowF k -- | spin the wheel, some number of units*, holding down the modifiers SendMouseScroll :: [Modifier] -> MouseScroll -> Natural -> k -> WorkflowF k GetClipboard :: (Clipboard -> k) -> WorkflowF k SetClipboard :: Clipboard -> k -> WorkflowF k -- | like a getter. CurrentApplication :: (Application -> k) -> WorkflowF k -- | idempotent. like a setter. OpenApplication :: Application -> k -> WorkflowF k OpenURL :: URL -> k -> WorkflowF k -- | interpreted as threadDelay on all platforms; included for -- convenience Delay :: MilliSeconds -> k -> WorkflowF k newtype Application' Application :: ApplicationName' -> Application' [getApplication] :: Application' -> ApplicationName' type ApplicationName' = Text type ApplicationExecutable' = Text type ApplicationName = String type ApplicationExecutable = FilePath -- | the non-monadic subset of WorkflowF. i.e. all cases that return -- (), preserving the previous continuation. -- -- Naming: "unit workflow", like "traverse_". data Workflow_ SendKeyChord_ :: [Modifier] -> Key -> Workflow_ SendText_ :: String -> Workflow_ SendMouseClick_ :: [Modifier] -> Natural -> MouseButton -> Workflow_ SendMouseScroll_ :: [Modifier] -> MouseScroll -> Natural -> Workflow_ SetClipboard_ :: Clipboard -> Workflow_ OpenApplication_ :: Application -> Workflow_ OpenURL_ :: URL -> Workflow_ Delay_ :: MilliSeconds -> Workflow_ -- | abstract interface. -- -- a monad constraint for "workflow effects" (just like -- MonadState is for "state effects"). Can be used in any monad -- transformer stack that handles them. -- -- WorkflowF holds the effects. -- -- MonadThrow supports: -- --
-- pattern KeyChord ms k = (ms,k) ---- | modifier keys are keys that can be "held". -- -- NOTE the escape key tends to be "pressed", not "held", it seems. -- (possibly explains its behavior in your terminal emulator?) -- -- alt is OptionModifier. data Modifier -- | fake modifier: Alt on Linux/Windows, Command on OSX MetaModifier :: Modifier -- | fake modifier: Control on Linux/Windows, Command on OSX HyperModifier :: Modifier ControlModifier :: Modifier OptionModifier :: Modifier ShiftModifier :: Modifier FunctionModifier :: Modifier -- | (really, a Set) type Modifiers = [Modifier] -- | a "cross-platform" keyboard, that has: -- --
-- = traverse_ sendKeyChord' --sendKeySequence :: (MonadWorkflow m) => KeySequence -> m () -- | uncurried sendKeyChord sendKeyChord' :: (MonadWorkflow m) => KeyChord -> m () fromWorkflows_ :: (MonadWorkflow m) => [Workflow_] -> m () fromWorkflow_ :: Workflow_ -> WorkflowF () module Workflow.Keys -- | Parses and executes a keyboard shortcut. (via readKeySequence -- and sendKeyChord). -- -- -- -- e.g. compare: -- --
-- press "H-S-t H-l" ---- -- to: -- --
-- traverse_ sendKeyChord' -- [ KeyChord [HyperModifier, ShiftModifier] TKey -- , KeyChord [HyperModifier) LKey -- ] ---- -- (a keyboard shortcut to "re-open tab, then jump to url bar") -- -- throwMs on a "syntax error" -- -- The default syntax is inspired by Emacs: -- --
-- press = press' defaultKeyChordSyntax --press :: (MonadWorkflow m, MonadThrow m) => String -> m () -- |
-- >>> readEmacsKeySequence "H-S-t H-l" -- Just [([HyperModifier,ShiftModifier],TKey),([HyperModifier],LKey)] ---- --
-- =readEmacsKeySequence emacsKeyChordSyntax --readEmacsKeySequence :: String -> Maybe KeySequence -- |
-- >>> readEmacsKeyChord "H-S-t" -- Just ([HyperModifier,ShiftModifier],TKey) ---- --
-- =readEmacsKeyChord emacsKeyChordSyntax --readEmacsKeyChord :: String -> Maybe KeySequence -- |
-- >>> readEmacsModifier "H" -- Just HyperModifier ---- --
-- =readModifier emacsModifierSyntax --readEmacsModifier :: String -> Maybe Modifier -- |
-- >>> readEmacsKey "<tab>" -- Just TabKey ---- --
-- =readKey emacsKeySyntax --readEmacsKey :: String -> Maybe KeyChord -- | Build your own press. e.g. -- --
-- import Workflow.Core hiding (press)
--
-- press = press' KeyChordSyntax{..}
--
-- modifierSyntax :: ModifierSyntax
-- modifierSyntax = defaultModifierSyntax -- defaulting
--
-- keySyntax :: KeySyntax -- overriding
-- keySyntax = Map.fromList
-- [ ...
-- ]
--
press' :: (MonadWorkflow m, MonadThrow m) => KeyChordSyntax -> String -> m ()
readKeySequence :: KeyChordSyntax -> String -> Maybe KeySequence
readKeyChord :: KeyChordSyntax -> [String] -> Maybe KeyChord
-- | surjective, non-injective.
readModifier :: ModifierSyntax -> String -> Maybe Modifier
readKey :: KeySyntax -> String -> Maybe KeyChord
-- | A table for parsing strings of modifiers and keys.
data KeyChordSyntax
KeyChordSyntax :: ModifierSyntax -> KeySyntax -> KeyChordSyntax
[modifierSyntax] :: KeyChordSyntax -> ModifierSyntax
[keySyntax] :: KeyChordSyntax -> KeySyntax
-- | <> overrides (i.e. right-biased i.e. pick the last).
type ModifierSyntax = Map String Modifier
type KeySyntax = Map String KeyChord
-- | -- = emacsKeyChordSyntax --defaultKeyChordSyntax :: KeyChordSyntax -- |
-- = emacsModifierSyntax --defaultModifierSyntax :: ModifierSyntax -- |
-- = emacsKeySyntax --defaultKeySyntax :: KeySyntax -- |
-- = KeyChordSyntax defaultModifierSyntax defaultKeySyntax --emacsKeyChordSyntax :: KeyChordSyntax -- | (see source) emacsModifierSyntax :: ModifierSyntax -- | follows Emacs keybinding syntax, with some differences: -- --
-- WorkflowD IO ---- -- template: -- --
-- myDictionary :: (MonadIO m) => WorkflowD m
-- myDictionary = WorkflowD{..}
-- where
-- _sendKeyChord =
-- _sendText =
--
-- _sendMouseClick =
-- _sendMouseScroll =
--
-- _getClipboard =
-- _setClipboard =
--
-- _currentApplication =
-- _openApplication =
-- _openURL =
--
-- runWorkflowByMy :: (MonadIO m) => WorkflowT m a -> m a
-- runWorkflowByMy = runWorkflowByT myDictionary
--
--
-- Delay is elided, as its implementation can use cross-platform
-- IO (threadDelay).
--
-- see runWorkflowByT
data WorkflowD m
WorkflowD :: ([Modifier] -> Key -> m ()) -> (String -> m ()) -> ([Modifier] -> Natural -> MouseButton -> m ()) -> ([Modifier] -> MouseScroll -> Natural -> m ()) -> m Clipboard -> (Clipboard -> m ()) -> m Application -> (Application -> m ()) -> (URL -> m ()) -> WorkflowD m
[_sendKeyChord] :: WorkflowD m -> [Modifier] -> Key -> m ()
[_sendText] :: WorkflowD m -> String -> m ()
[_sendMouseClick] :: WorkflowD m -> [Modifier] -> Natural -> MouseButton -> m ()
[_sendMouseScroll] :: WorkflowD m -> [Modifier] -> MouseScroll -> Natural -> m ()
[_getClipboard] :: WorkflowD m -> m Clipboard
[_setClipboard] :: WorkflowD m -> (Clipboard -> m ())
[_currentApplication] :: WorkflowD m -> m Application
[_openApplication] :: WorkflowD m -> Application -> m ()
[_openURL] :: WorkflowD m -> URL -> m ()
-- | e.g.
--
--
-- shellDictionary :: WorkflowD IO
-- shellDictionary = WorkflowD{..}
-- where
-- _getClipboard = shell $ "pbpaste"
-- _setClipboard s = shell $ "echo "++(shellEscape s)++"| pbcopy" >> return ()
-- ...
--
-- runWorkflowByShell :: (MonadIO m) => WorkflowT m a -> m a
-- runWorkflowByShell = runWorkflowByT shellDictionary
--
-- -- specializeable:
-- -- runWorkflowByShell :: Workflow a -> IO a
--
runWorkflowByT :: forall m a. (MonadIO m) => WorkflowD m -> WorkflowT m a -> m a
delayMilliseconds :: (MonadIO m) => Int -> m ()
delaySeconds :: (MonadIO m) => Int -> m ()
-- | Automate keyboard/mouse/clipboard/application interaction.
--
-- the core types that define platform-diagnostic workflows, as well as
-- helpers for parsing keychords and implementing backends.
--
-- (see WorkflowF, MonadWorkflow, Key, press,
-- WorkflowD)
module Workflow.Core
module Workflow.Example
main :: IO ()