-- | Provides support for generating a Karabiner config via Haskell.
module Karabiner.Config where

import Prelude hiding (mod)
import Data.Aeson
import Data.Aeson.Encode.Pretty hiding (Tab)
import qualified Data.ByteString.Lazy.Char8 as LC
import Data.Maybe
import Data.String
import Data.Text (Text)
import qualified Data.Text as T
import qualified GHC.TypeLits as TL
import GHC.TypeLits (ErrorMessage((:$$:)))

import Karabiner.Config.Internal (prettyConfig, stripNulls)

-- | Builds a main function which outputs the given 'Root' as JSON.
mkMain :: Root -> IO ()
mkMain :: Root -> IO ()
mkMain = ByteString -> IO ()
LC.putStrLn (ByteString -> IO ()) -> (Root -> ByteString) -> Root -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Root -> ByteString
encodeRoot

-- | Encode a 'Root' to a JSON lazy ByteString.
encodeRoot :: Root -> LC.ByteString
encodeRoot :: Root -> ByteString
encodeRoot Root
root = Config -> Root -> ByteString
forall a. ToJSON a => Config -> a -> ByteString
encodePretty' Config
prettyConfig Root
root ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> [Char] -> ByteString
LC.pack [Char]
"\n"

-- | Converts a text literal to an application regex pattern.
litPat :: Text -> Text
litPat :: Text -> Text
litPat Text
t = Char
'^' Char -> Text -> Text
`T.cons` (Text -> Text -> Text -> Text
T.replace Text
"." Text
"\\." Text
t) Text -> Char -> Text
`T.snoc` Char
'$'

-- | Represents a key binding of @[a]@ modifiers (e.g. shift, control) and a key code.
-- The @a@ is polymorphic so we can make distinctions between PhysicalModifier
-- and MetaModifier.
data KeyBinding a = KeyBinding [a] KeyCode

-- | Key binding with no modifiers.
singleKey :: KeyCode -> KeyBinding PhysicalModifier
singleKey :: KeyCode -> KeyBinding PhysicalModifier
singleKey = [PhysicalModifier] -> KeyCode -> KeyBinding PhysicalModifier
forall a. [a] -> KeyCode -> KeyBinding a
KeyBinding []

-- | Helper for a key binding sequence, only slightly prettier than using a list.
(|->) :: KeyBinding a -> KeyBinding a -> [KeyBinding a]
KeyBinding a
x |-> :: KeyBinding a -> KeyBinding a -> [KeyBinding a]
|-> KeyBinding a
y = [KeyBinding a
x, KeyBinding a
y]

infix 5 |->

-- | Type class for constructing a single key binding from multiple keys.
class ToKeyBinding a b c | a b -> c where
  (|+|) :: a -> b -> KeyBinding c
  infix 6 |+|

instance ToKeyBinding PhysicalModifier KeyCode PhysicalModifier where
  PhysicalModifier
mod |+| :: PhysicalModifier -> KeyCode -> KeyBinding PhysicalModifier
|+| KeyCode
kc = [PhysicalModifier] -> KeyCode -> KeyBinding PhysicalModifier
forall a. [a] -> KeyCode -> KeyBinding a
KeyBinding [PhysicalModifier
mod] KeyCode
kc

instance ToKeyBinding [PhysicalModifier] KeyCode PhysicalModifier where
  [PhysicalModifier]
mods |+| :: [PhysicalModifier] -> KeyCode -> KeyBinding PhysicalModifier
|+| KeyCode
kc = [PhysicalModifier] -> KeyCode -> KeyBinding PhysicalModifier
forall a. [a] -> KeyCode -> KeyBinding a
KeyBinding [PhysicalModifier]
mods KeyCode
kc

instance ToKeyBinding MetaModifier KeyCode MetaModifier where
  MetaModifier
mod |+| :: MetaModifier -> KeyCode -> KeyBinding MetaModifier
|+| KeyCode
kc = [MetaModifier] -> KeyCode -> KeyBinding MetaModifier
forall a. [a] -> KeyCode -> KeyBinding a
KeyBinding [MetaModifier
mod] KeyCode
kc

instance ToKeyBinding [MetaModifier] KeyCode MetaModifier where
  [MetaModifier]
mods |+| :: [MetaModifier] -> KeyCode -> KeyBinding MetaModifier
|+| KeyCode
kc = [MetaModifier] -> KeyCode -> KeyBinding MetaModifier
forall a. [a] -> KeyCode -> KeyBinding a
KeyBinding [MetaModifier]
mods KeyCode
kc

-- | Maps first 'KeyBinding' to second 'KeyBinding'
-- We can map physical modifiers to physical modifiers or meta modifiers
-- to physical modifiers, but we can't (or rather, shouldn't) map
-- any modifiers to meta modifiers; these type class instances enforce this.
-- We provide an instance for this via 'TL.TypeError' to provide nice error
-- messages.
class ManipulatorBuilder a b where
  (!>) :: a -> b -> Manipulator
  infix 4 !>

instance AsAnyModifier a
  => ManipulatorBuilder (KeyBinding a) (KeyBinding PhysicalModifier) where
  (KeyBinding [a]
fromMods KeyCode
fromK) !> :: KeyBinding a -> KeyBinding PhysicalModifier -> Manipulator
!> (KeyBinding [PhysicalModifier]
toMods KeyCode
toK) =
    ManipulatorType
-> ManipulatorFrom
-> [ManipulatorTo]
-> Maybe [ManipulatorCondition]
-> Manipulator
Manipulator ManipulatorType
Basic ManipulatorFrom
mf [ManipulatorTo
mt] Maybe [ManipulatorCondition]
forall a. Maybe a
Nothing
    where
    mf :: ManipulatorFrom
mf = KeyCode -> FromModifiers -> ManipulatorFrom
ManipulatorFrom KeyCode
fromK ([AnyModifier] -> FromModifiers
FromModifiers ([AnyModifier] -> FromModifiers) -> [AnyModifier] -> FromModifiers
forall a b. (a -> b) -> a -> b
$ (a -> AnyModifier) -> [a] -> [AnyModifier]
forall a b. (a -> b) -> [a] -> [b]
map a -> AnyModifier
forall a. AsAnyModifier a => a -> AnyModifier
asAnyModifier [a]
fromMods)
    mt :: ManipulatorTo
mt = KeyCode -> [PhysicalModifier] -> ManipulatorTo
ManipulatorTo   KeyCode
toK   [PhysicalModifier]
toMods

instance AsAnyModifier a
  => ManipulatorBuilder (KeyBinding a) [KeyBinding PhysicalModifier] where
  (KeyBinding [a]
fromMods KeyCode
fromK) !> :: KeyBinding a -> [KeyBinding PhysicalModifier] -> Manipulator
!> [KeyBinding PhysicalModifier]
toBindingSeq =
    ManipulatorType
-> ManipulatorFrom
-> [ManipulatorTo]
-> Maybe [ManipulatorCondition]
-> Manipulator
Manipulator ManipulatorType
Basic ManipulatorFrom
mf [ManipulatorTo]
mts Maybe [ManipulatorCondition]
forall a. Maybe a
Nothing
    where
    mf :: ManipulatorFrom
mf = KeyCode -> FromModifiers -> ManipulatorFrom
ManipulatorFrom KeyCode
fromK ([AnyModifier] -> FromModifiers
FromModifiers ([AnyModifier] -> FromModifiers) -> [AnyModifier] -> FromModifiers
forall a b. (a -> b) -> a -> b
$ (a -> AnyModifier) -> [a] -> [AnyModifier]
forall a b. (a -> b) -> [a] -> [b]
map a -> AnyModifier
forall a. AsAnyModifier a => a -> AnyModifier
asAnyModifier [a]
fromMods)
    mts :: [ManipulatorTo]
mts = ((KeyBinding PhysicalModifier -> ManipulatorTo)
 -> [KeyBinding PhysicalModifier] -> [ManipulatorTo])
-> [KeyBinding PhysicalModifier]
-> (KeyBinding PhysicalModifier -> ManipulatorTo)
-> [ManipulatorTo]
forall a b c. (a -> b -> c) -> b -> a -> c
flip (KeyBinding PhysicalModifier -> ManipulatorTo)
-> [KeyBinding PhysicalModifier] -> [ManipulatorTo]
forall a b. (a -> b) -> [a] -> [b]
map [KeyBinding PhysicalModifier]
toBindingSeq ((KeyBinding PhysicalModifier -> ManipulatorTo) -> [ManipulatorTo])
-> (KeyBinding PhysicalModifier -> ManipulatorTo)
-> [ManipulatorTo]
forall a b. (a -> b) -> a -> b
$ \(KeyBinding [PhysicalModifier]
toMods KeyCode
toK) ->
            KeyCode -> [PhysicalModifier] -> ManipulatorTo
ManipulatorTo KeyCode
toK [PhysicalModifier]
toMods

instance AsAnyModifier a
  => ManipulatorBuilder (KeyBinding a) KeyCode where
  KeyBinding a
fromKeys !> :: KeyBinding a -> KeyCode -> Manipulator
!> KeyCode
toKey = KeyBinding a
fromKeys KeyBinding a -> KeyBinding PhysicalModifier -> Manipulator
forall a b. ManipulatorBuilder a b => a -> b -> Manipulator
!> ([] :: [PhysicalModifier]) [PhysicalModifier] -> KeyCode -> KeyBinding PhysicalModifier
forall a b c. ToKeyBinding a b c => a -> b -> KeyBinding c
|+| KeyCode
toKey

instance
  TL.TypeError
    (     'TL.Text "Unsupported ManipulatorBuilder (!>) usage;"
    ':$$: 'TL.Text "'to' binding must use a PhysicalModifier (e.g. RightControl)"
    ':$$: 'TL.Text "not a MetaModifier (e.g. Control)"
    )
  => ManipulatorBuilder a (KeyBinding MetaModifier) where
  !> :: a -> KeyBinding MetaModifier -> Manipulator
(!>) = a -> KeyBinding MetaModifier -> Manipulator
forall a. HasCallStack => a
undefined

-- | Adds @frontmost_application_if@ condition to 'Manipulator'
frontmostApplicationIf :: Manipulator -> [Text] -> Manipulator
Manipulator
m frontmostApplicationIf :: Manipulator -> [Text] -> Manipulator
`frontmostApplicationIf` [Text]
ts = Manipulator
m { manipulatorConditions :: Maybe [ManipulatorCondition]
manipulatorConditions = Maybe [ManipulatorCondition]
cs }
  where
  c :: ManipulatorCondition
c = ManipulatorConditionType -> [Text] -> ManipulatorCondition
ManipulatorCondition ManipulatorConditionType
FrontmostApplicationIf [Text]
ts
  cs :: Maybe [ManipulatorCondition]
cs = [ManipulatorCondition] -> Maybe [ManipulatorCondition]
forall a. a -> Maybe a
Just ([ManipulatorCondition] -> Maybe [ManipulatorCondition])
-> [ManipulatorCondition] -> Maybe [ManipulatorCondition]
forall a b. (a -> b) -> a -> b
$ ManipulatorCondition
c ManipulatorCondition
-> [ManipulatorCondition] -> [ManipulatorCondition]
forall a. a -> [a] -> [a]
: [ManipulatorCondition]
-> Maybe [ManipulatorCondition] -> [ManipulatorCondition]
forall a. a -> Maybe a -> a
fromMaybe [] (Manipulator -> Maybe [ManipulatorCondition]
manipulatorConditions Manipulator
m)

-- | Alias for 'frontmostApplicationIf'
(?) :: Manipulator -> [Text] -> Manipulator
? :: Manipulator -> [Text] -> Manipulator
(?) = Manipulator -> [Text] -> Manipulator
frontmostApplicationIf

-- | Same as 'frontmostApplicationIf' except updates a list of 'Manipulator'
(??) :: [Manipulator] -> [Text] -> [Manipulator]
[Manipulator]
ms ?? :: [Manipulator] -> [Text] -> [Manipulator]
?? [Text]
ts = (Manipulator -> Manipulator) -> [Manipulator] -> [Manipulator]
forall a b. (a -> b) -> [a] -> [b]
map (Manipulator -> [Text] -> Manipulator
? [Text]
ts) [Manipulator]
ms

-- | Adds @frontmost_application_unless@ condition to 'Manipulator'
frontmostApplicationUnless :: Manipulator -> [Text] -> Manipulator
Manipulator
m frontmostApplicationUnless :: Manipulator -> [Text] -> Manipulator
`frontmostApplicationUnless` [Text]
ts = Manipulator
m { manipulatorConditions :: Maybe [ManipulatorCondition]
manipulatorConditions = Maybe [ManipulatorCondition]
cs }
  where
  c :: ManipulatorCondition
c = ManipulatorConditionType -> [Text] -> ManipulatorCondition
ManipulatorCondition ManipulatorConditionType
FrontmostApplicationUnless [Text]
ts
  cs :: Maybe [ManipulatorCondition]
cs = [ManipulatorCondition] -> Maybe [ManipulatorCondition]
forall a. a -> Maybe a
Just ([ManipulatorCondition] -> Maybe [ManipulatorCondition])
-> [ManipulatorCondition] -> Maybe [ManipulatorCondition]
forall a b. (a -> b) -> a -> b
$ ManipulatorCondition
c ManipulatorCondition
-> [ManipulatorCondition] -> [ManipulatorCondition]
forall a. a -> [a] -> [a]
: [ManipulatorCondition]
-> Maybe [ManipulatorCondition] -> [ManipulatorCondition]
forall a. a -> Maybe a -> a
fromMaybe [] (Manipulator -> Maybe [ManipulatorCondition]
manipulatorConditions Manipulator
m)

-- | Alias for 'frontmostApplicationUnless'
(?!) :: Manipulator -> [Text] -> Manipulator
?! :: Manipulator -> [Text] -> Manipulator
(?!) = Manipulator -> [Text] -> Manipulator
frontmostApplicationUnless

infix 3 ?!

-- | Same as '?!' except updates a list of 'Manipulator'
(??!) :: [Manipulator] -> [Text] -> [Manipulator]
[Manipulator]
ms ??! :: [Manipulator] -> [Text] -> [Manipulator]
??! [Text]
ts = (Manipulator -> Manipulator) -> [Manipulator] -> [Manipulator]
forall a b. (a -> b) -> [a] -> [b]
map (Manipulator -> [Text] -> Manipulator
?! [Text]
ts) [Manipulator]
ms

-- | Most top-level node of the karabiner config
data Root = Root
  { Root -> Text
rootTitle :: Text
  , Root -> [Rule]
rootRules :: [Rule]
  }

instance ToJSON Root where
  toJSON :: Root -> Value
toJSON (Root Text
title [Rule]
rules) =
    [Pair] -> Value
object [Text
"title" Text -> Text -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= Text
title, Text
"rules" Text -> [Rule] -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= [Rule]
rules]

-- |  A set of manupulations that can be enabled or disabled via the
-- @Complex modifications@ section in the Karabiner UI.
data Rule = Rule
  { Rule -> Text
ruleDescription :: Text
  , Rule -> [Manipulator]
ruleManipulators :: [Manipulator]
  }

instance ToJSON Rule where
  toJSON :: Rule -> Value
toJSON (Rule Text
d [Manipulator]
ms) =
    [Pair] -> Value
object [Text
"description" Text -> Text -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= Text
d, Text
"manipulators" Text -> [Manipulator] -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= [Manipulator]
ms]

-- | A configured key rebinding
data Manipulator = Manipulator
  { Manipulator -> ManipulatorType
manipulatorType :: ManipulatorType
  , Manipulator -> ManipulatorFrom
manipulatorFrom :: ManipulatorFrom
  , Manipulator -> [ManipulatorTo]
manipulatorTo :: [ManipulatorTo]
  , Manipulator -> Maybe [ManipulatorCondition]
manipulatorConditions :: Maybe [ManipulatorCondition]
  }

instance ToJSON Manipulator where
  toJSON :: Manipulator -> Value
toJSON (Manipulator ManipulatorType
typ ManipulatorFrom
from [ManipulatorTo]
to Maybe [ManipulatorCondition]
conds) = [Pair] -> Value
object ([Pair] -> Value) -> [Pair] -> Value
forall a b. (a -> b) -> a -> b
$ [Pair] -> [Pair]
stripNulls
    [ Text
"type" Text -> ManipulatorType -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= ManipulatorType
typ
    , Text
"from" Text -> ManipulatorFrom -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= ManipulatorFrom
from
    , Text
"to" Text -> [ManipulatorTo] -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= [ManipulatorTo]
to
    , Text
"conditions" Text -> Maybe [ManipulatorCondition] -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= Maybe [ManipulatorCondition]
conds
    ]

data ManipulatorType = Basic

manipulatorTypeToText :: ManipulatorType -> Text
manipulatorTypeToText :: ManipulatorType -> Text
manipulatorTypeToText = \case
  ManipulatorType
Basic -> Text
"basic"

instance ToJSON ManipulatorType where
  toJSON :: ManipulatorType -> Value
toJSON = Text -> Value
forall a. ToJSON a => a -> Value
toJSON (Text -> Value)
-> (ManipulatorType -> Text) -> ManipulatorType -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ManipulatorType -> Text
manipulatorTypeToText

data ManipulatorFrom = ManipulatorFrom
  { ManipulatorFrom -> KeyCode
fromKeyCode :: KeyCode
  , ManipulatorFrom -> FromModifiers
fromModifiers :: FromModifiers
  }

instance ToJSON ManipulatorFrom where
  toJSON :: ManipulatorFrom -> Value
toJSON (ManipulatorFrom KeyCode
k FromModifiers
ms) =
    [Pair] -> Value
object [Text
"key_code" Text -> KeyCode -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= KeyCode
k, Text
"modifiers" Text -> FromModifiers -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= FromModifiers
ms]

data ManipulatorTo = ManipulatorTo
  { ManipulatorTo -> KeyCode
toKeyCode :: KeyCode
  , ManipulatorTo -> [PhysicalModifier]
toModifiers :: [PhysicalModifier]
  }

instance ToJSON ManipulatorTo where
  toJSON :: ManipulatorTo -> Value
toJSON (ManipulatorTo KeyCode
k [PhysicalModifier]
ms) =
    [Pair] -> Value
object [Text
"key_code" Text -> KeyCode -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= KeyCode
k, Text
"modifiers" Text -> [PhysicalModifier] -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= [PhysicalModifier]
ms]

data ManipulatorCondition = ManipulatorCondition
  { ManipulatorCondition -> ManipulatorConditionType
conditionType :: ManipulatorConditionType
  , ManipulatorCondition -> [Text]
conditionBundleIdentifiers :: [Text]
  }

instance ToJSON ManipulatorCondition where
  toJSON :: ManipulatorCondition -> Value
toJSON (ManipulatorCondition ManipulatorConditionType
t [Text]
bis) =
    [Pair] -> Value
object [Text
"type" Text -> ManipulatorConditionType -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= ManipulatorConditionType
t, Text
"bundle_identifiers" Text -> [Text] -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= [Text]
bis]

data ManipulatorConditionType
  = FrontmostApplicationUnless
  | FrontmostApplicationIf

manipulatorConditionTypeToText :: ManipulatorConditionType -> Text
manipulatorConditionTypeToText :: ManipulatorConditionType -> Text
manipulatorConditionTypeToText = \case
  ManipulatorConditionType
FrontmostApplicationUnless -> Text
"frontmost_application_unless"
  ManipulatorConditionType
FrontmostApplicationIf -> Text
"frontmost_application_if"

instance ToJSON ManipulatorConditionType where
  toJSON :: ManipulatorConditionType -> Value
toJSON = Text -> Value
forall a. ToJSON a => a -> Value
toJSON (Text -> Value)
-> (ManipulatorConditionType -> Text)
-> ManipulatorConditionType
-> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ManipulatorConditionType -> Text
manipulatorConditionTypeToText

data FromModifiers = FromModifiers
  { FromModifiers -> [AnyModifier]
modifiersMandatory :: [AnyModifier]
  }

instance ToJSON FromModifiers where
  toJSON :: FromModifiers -> Value
toJSON (FromModifiers [AnyModifier]
m) =
    [Pair] -> Value
object [Text
"mandatory" Text -> [AnyModifier] -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= [AnyModifier]
m]

data PhysicalModifier
  = LeftShift | RightShift
  | LeftControl | RightControl
  | LeftOption | RightOption
  | LeftCommand | RightCommand

instance ToJSON PhysicalModifier where
  toJSON :: PhysicalModifier -> Value
toJSON = \case
    PhysicalModifier
LeftShift -> Value
"left_shift"
    PhysicalModifier
RightShift -> Value
"right_shift"
    PhysicalModifier
LeftControl -> Value
"left_control"
    PhysicalModifier
RightControl -> Value
"right_control"
    PhysicalModifier
LeftOption -> Value
"left_option"
    PhysicalModifier
RightOption -> Value
"right_option"
    PhysicalModifier
LeftCommand -> Value
"left_command"
    PhysicalModifier
RightCommand -> Value
"right_command"

data MetaModifier
  = Shift
  | Control
  | Option
  | Command

instance ToJSON MetaModifier where
  toJSON :: MetaModifier -> Value
toJSON = \case
    MetaModifier
Shift -> Value
"shift"
    MetaModifier
Control -> Value
"control"
    MetaModifier
Option -> Value
"option"
    MetaModifier
Command -> Value
"command"

data AnyModifier
  = ModifierFromPhysical PhysicalModifier
  | ModifierFromMeta MetaModifier

class AsAnyModifier a where
  asAnyModifier :: a -> AnyModifier

instance AsAnyModifier PhysicalModifier where
  asAnyModifier :: PhysicalModifier -> AnyModifier
asAnyModifier = PhysicalModifier -> AnyModifier
ModifierFromPhysical

instance AsAnyModifier MetaModifier where
  asAnyModifier :: MetaModifier -> AnyModifier
asAnyModifier = MetaModifier -> AnyModifier
ModifierFromMeta

instance ToJSON AnyModifier where
  toJSON :: AnyModifier -> Value
toJSON AnyModifier
mf = case AnyModifier
mf of
    ModifierFromPhysical PhysicalModifier
m -> PhysicalModifier -> Value
forall a. ToJSON a => a -> Value
toJSON PhysicalModifier
m
    ModifierFromMeta MetaModifier
m -> MetaModifier -> Value
forall a. ToJSON a => a -> Value
toJSON MetaModifier
m

-- | Key codes available for binding
-- The full list can be viewed in the Karabiner-Elements source
-- https://github.com/tekezo/Karabiner-Elements/blob/master/src/share/types.hpp
data KeyCode
  = A | B | C | D | E | F | G | H | I | J | K | L | M
  | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
  | One | Two | Three | Four | Five | Six | Seven | Eight | Nine | Zero
  | Spacebar | Backspace
  | OpenBracket | CloseBracket
  | RightArrow | LeftArrow | UpArrow | DownArrow
  | ReturnOrEnter
  | Tab
  | Comma | Semicolon

-- Number keys on the keyboard, useful for generating key maps via loops.
numbers :: [KeyCode]
numbers :: [KeyCode]
numbers = [KeyCode
One, KeyCode
Two, KeyCode
Three, KeyCode
Four, KeyCode
Five, KeyCode
Six, KeyCode
Seven, KeyCode
Eight, KeyCode
Nine, KeyCode
Zero]

keyCodeToString :: IsString a => KeyCode -> a
keyCodeToString :: KeyCode -> a
keyCodeToString = \case
  KeyCode
A -> a
"a"
  KeyCode
B -> a
"b"
  KeyCode
C -> a
"c"
  KeyCode
D -> a
"d"
  KeyCode
E -> a
"e"
  KeyCode
F -> a
"f"
  KeyCode
G -> a
"g"
  KeyCode
H -> a
"h"
  KeyCode
I -> a
"i"
  KeyCode
J -> a
"j"
  KeyCode
K -> a
"k"
  KeyCode
L -> a
"l"
  KeyCode
M -> a
"m"
  KeyCode
N -> a
"n"
  KeyCode
O -> a
"o"
  KeyCode
P -> a
"p"
  KeyCode
Q -> a
"q"
  KeyCode
R -> a
"r"
  KeyCode
S -> a
"s"
  KeyCode
T -> a
"t"
  KeyCode
U -> a
"u"
  KeyCode
V -> a
"v"
  KeyCode
W -> a
"w"
  KeyCode
X -> a
"x"
  KeyCode
Y -> a
"y"
  KeyCode
Z -> a
"z"
  KeyCode
One -> a
"1"
  KeyCode
Two -> a
"2"
  KeyCode
Three -> a
"3"
  KeyCode
Four -> a
"4"
  KeyCode
Five -> a
"5"
  KeyCode
Six -> a
"6"
  KeyCode
Seven -> a
"7"
  KeyCode
Eight -> a
"8"
  KeyCode
Nine -> a
"9"
  KeyCode
Zero -> a
"0"
  KeyCode
Spacebar -> a
"spacebar"
  KeyCode
Backspace -> a
"delete_or_backspace"
  KeyCode
OpenBracket -> a
"open_bracket"
  KeyCode
CloseBracket -> a
"close_bracket"
  KeyCode
RightArrow -> a
"right_arrow"
  KeyCode
LeftArrow -> a
"left_arrow"
  KeyCode
DownArrow -> a
"down_arrow"
  KeyCode
UpArrow -> a
"up_arrow"
  KeyCode
ReturnOrEnter -> a
"return_or_enter"
  KeyCode
Tab -> a
"tab"
  KeyCode
Comma -> a
"comma"
  KeyCode
Semicolon -> a
"semicolon"

instance ToJSON KeyCode where
  toJSON :: KeyCode -> Value
toJSON = KeyCode -> Value
forall a. IsString a => KeyCode -> a
keyCodeToString