glirc 2.22 → 2.23
raw patch · 40 files changed
+2797/−2130 lines, 40 filesdep +freedep +template-haskelldep ~vtyPVP ok
version bump matches the API change (PVP)
Dependencies added: free, template-haskell
Dependency ranges changed: vty
API changes (from Hackage documentation)
- Client.Commands.Arguments: [NoArg] :: ArgumentSpec ()
- Client.Commands.Arguments: [OptTokenArg] :: String -> ArgumentSpec rest -> ArgumentSpec (Maybe (String, rest))
- Client.Commands.Arguments: [RemainingArg] :: String -> ArgumentSpec String
- Client.Commands.Arguments: [ReqTokenArg] :: String -> ArgumentSpec rest -> ArgumentSpec (String, rest)
- Client.Commands.Arguments: data ArgumentSpec :: * -> *
- Client.Commands.Arguments: instance GHC.Show.Show (Client.Commands.Arguments.ArgumentSpec s)
- Client.Commands.Arguments: parseArguments :: ArgumentSpec a -> String -> Maybe a
- Client.Configuration: [_configConfigPath] :: Configuration -> Maybe FilePath
- Client.Configuration: configConfigPath :: Lens' Configuration (Maybe FilePath)
- Client.Configuration: resolveConfigurationPath :: FilePath -> IO FilePath
- Client.Image.Arguments: argumentsImage :: Palette -> ArgumentSpec a -> String -> Image'
- Client.Image.MircFormatting: parseIrcTextExplicit :: Text -> Image'
- Client.State: [_clientNextConnectionId] :: ClientState -> !NetworkId
- Client.State: clientNextConnectionId :: Lens' ClientState NetworkId
+ Client.CApi: chatExtension :: Ptr () -> Text -> Text -> Text -> [ActiveExtension] -> IO Bool
+ Client.CApi.Exports: glirc_clear_window :: Glirc_clear_window
+ Client.CApi.Exports: glirc_current_focus :: Glirc_current_focus
+ Client.CApi.Exports: glirc_free_string :: Glirc_free_string
+ Client.CApi.Exports: glirc_free_strings :: Glirc_free_strings
+ Client.CApi.Exports: glirc_identifier_cmp :: Glirc_identifier_cmp
+ Client.CApi.Exports: glirc_inject_chat :: Glirc_inject_chat
+ Client.CApi.Exports: glirc_is_channel :: Glirc_is_channel
+ Client.CApi.Exports: glirc_is_logged_on :: Glirc_is_channel
+ Client.CApi.Exports: glirc_list_channel_users :: Glirc_list_channel_users
+ Client.CApi.Exports: glirc_list_channels :: Glirc_list_channels
+ Client.CApi.Exports: glirc_list_networks :: Glirc_list_networks
+ Client.CApi.Exports: glirc_mark_seen :: Glirc_mark_seen
+ Client.CApi.Exports: glirc_my_nick :: Glirc_my_nick
+ Client.CApi.Exports: glirc_print :: Glirc_print
+ Client.CApi.Exports: glirc_send_message :: Glirc_send_message
+ Client.CApi.Exports: type Glirc_current_focus = Ptr () api token -> Ptr CString newly allocated network string -> Ptr CSize network length -> Ptr CString newly allocated target string -> Ptr CSize target length -> IO ()
+ Client.CApi.Exports: type Glirc_inject_chat = Ptr () api token -> CString network -> CSize network length -> CString source -> CSize source length -> CString target -> CSize target length -> CString message -> CSize message length -> IO CInt 0 on success
+ Client.CApi.Exports: type Glirc_is_channel = Ptr () api token -> CString network name -> CSize network length -> CString target name -> CSize target length -> IO CInt boolean
+ Client.CApi.Exports: type Glirc_is_logged_on = Ptr () api token -> CString network name -> CSize network length -> CString target name -> CSize target length -> IO CInt boolean
+ Client.CApi.Types: FgnChat :: FgnStringLen -> FgnStringLen -> FgnStringLen -> FgnChat
+ Client.CApi.Types: [fgnChat] :: FgnExtension -> FunPtr ProcessChat
+ Client.CApi.Types: [fhMessage] :: FgnChat -> FgnStringLen
+ Client.CApi.Types: [fhNetwork] :: FgnChat -> FgnStringLen
+ Client.CApi.Types: [fhTarget] :: FgnChat -> FgnStringLen
+ Client.CApi.Types: data FgnChat
+ Client.CApi.Types: exportText :: Ptr CString -> Ptr CSize -> Text -> IO ()
+ Client.CApi.Types: instance Foreign.Storable.Storable Client.CApi.Types.FgnChat
+ Client.CApi.Types: poke' :: Storable a => Ptr a -> a -> IO ()
+ Client.CApi.Types: runProcessChat :: Dynamic ProcessChat
+ Client.CApi.Types: withText0 :: Text -> (CStringLen -> IO a) -> IO a
+ Client.Commands.Arguments.Parser: parse :: r -> Args r a -> String -> Maybe a
+ Client.Commands.Arguments.Renderer: render :: Palette -> r -> Bool -> Args r a -> String -> Image'
+ Client.Commands.Arguments.Spec: RemainingArgument :: ArgumentShape
+ Client.Commands.Arguments.Spec: TokenArgument :: ArgumentShape
+ Client.Commands.Arguments.Spec: [Argument] :: ArgumentShape -> String -> (r -> String -> Maybe a) -> Arg r a
+ Client.Commands.Arguments.Spec: [Extension] :: String -> (r -> String -> Maybe (Args r a)) -> Arg r a
+ Client.Commands.Arguments.Spec: [Optional] :: Args r a -> Arg r (Maybe a)
+ Client.Commands.Arguments.Spec: data Arg :: * -> * -> *
+ Client.Commands.Arguments.Spec: data ArgumentShape
+ Client.Commands.Arguments.Spec: extensionArg :: String -> (r -> String -> Maybe (Args r a)) -> Args r a
+ Client.Commands.Arguments.Spec: numberArg :: Args r Int
+ Client.Commands.Arguments.Spec: optionalArg :: Args r a -> Args r (Maybe a)
+ Client.Commands.Arguments.Spec: remainingArg :: String -> Args r String
+ Client.Commands.Arguments.Spec: simpleToken :: String -> Args r String
+ Client.Commands.Arguments.Spec: tokenList :: [String] -> [String] -> Args r [String]
+ Client.Commands.Arguments.Spec: type Args r = Ap (Arg r)
+ Client.Image.Message: drawWindowLine :: Palette -> Int -> PaddingMode -> WindowLine -> [Image']
+ Client.Image.Message: parseIrcTextWithNicks :: Palette -> HashSet Identifier -> HashSet Identifier -> Bool -> Text -> Image'
+ Client.Image.MircFormatting: mircColor :: Int -> Maybe Color
+ Client.Image.MircFormatting: parseIrcText' :: Bool -> Text -> Image'
+ Client.State: [_clientConfigPath] :: ClientState -> !(Maybe FilePath)
+ Client.State: channelUserList :: Text -> Identifier -> ClientState -> [Identifier]
+ Client.State: clientConfigPath :: Lens' ClientState (Maybe FilePath)
+ Client.State: clientHighlightsNetwork :: Text -> ClientState -> HashSet Identifier
- Client.CApi.Types: FgnExtension :: FunPtr StartExtension -> FunPtr StopExtension -> FunPtr ProcessMessage -> FunPtr ProcessCommand -> CString -> CInt -> FgnExtension
+ Client.CApi.Types: FgnExtension :: FunPtr StartExtension -> FunPtr StopExtension -> FunPtr ProcessMessage -> FunPtr ProcessChat -> FunPtr ProcessCommand -> CString -> CInt -> FgnExtension
- Client.Commands: Command :: NonEmpty Text -> ArgumentSpec a -> Text -> CommandImpl a -> Command
+ Client.Commands: Command :: NonEmpty Text -> Args ClientState a -> Text -> CommandImpl a -> Command
- Client.Commands: [cmdArgumentSpec] :: Command -> ArgumentSpec a
+ Client.Commands: [cmdArgumentSpec] :: Command -> Args ClientState a
- Client.Commands.Interpolation: [MacroSpec] :: ArgumentSpec s -> MacroSpec
+ Client.Commands.Interpolation: [MacroSpec] :: (forall r. Args r [String]) -> MacroSpec
- Client.Configuration: Configuration :: ServerSettings -> (HashMap Text ServerSettings) -> Palette -> Text -> HashSet Identifier -> PaddingMode -> Maybe FilePath -> Recognizer Macro -> [FilePath] -> Maybe FilePath -> HashSet Identifier -> Bool -> Bool -> Bool -> KeyMap -> LayoutMode -> [Modifier] -> Configuration
+ Client.Configuration: Configuration :: ServerSettings -> (HashMap Text ServerSettings) -> Palette -> Text -> HashSet Identifier -> PaddingMode -> Recognizer Macro -> [FilePath] -> Maybe FilePath -> HashSet Identifier -> Bool -> Bool -> Bool -> KeyMap -> LayoutMode -> [Modifier] -> Configuration
- Client.Configuration: configurationSpec :: ValueSpecs (Maybe FilePath -> ServerSettings -> Configuration)
+ Client.Configuration: configurationSpec :: ValueSpecs (ServerSettings -> Configuration)
- Client.State: ClientState :: !(Map Focus Window) -> !Focus -> !Focus -> !Focus -> !Subfocus -> ![Focus] -> !(IntMap NetworkState) -> !NetworkId -> !(TQueue NetworkEvent) -> !(HashMap Text NetworkId) -> !Configuration -> !EditBox -> !Int -> !Int -> !Int -> !Int -> !Bool -> !Bool -> Maybe Regex -> !LayoutMode -> !Bool -> !(HashSet Identifier) -> !ExtensionState -> ![LogLine] -> Maybe Text -> Maybe Stats -> ClientState
+ Client.State: ClientState :: !(Map Focus Window) -> !Focus -> !Focus -> !Focus -> !Subfocus -> ![Focus] -> !(IntMap NetworkState) -> !(TQueue NetworkEvent) -> !(HashMap Text NetworkId) -> !Configuration -> !(Maybe FilePath) -> !EditBox -> !Int -> !Int -> !Int -> !Int -> !Bool -> !Bool -> Maybe Regex -> !LayoutMode -> !Bool -> !(HashSet Identifier) -> !ExtensionState -> ![LogLine] -> Maybe Text -> Maybe Stats -> ClientState
- Client.State: withClientState :: Configuration -> (ClientState -> IO a) -> IO a
+ Client.State: withClientState :: Maybe FilePath -> Configuration -> (ClientState -> IO a) -> IO a
- Client.State.EditBox: class HasLine c_aIxW where pos = (.) line pos text = (.) line text
+ Client.State.EditBox: class HasLine c_az5Q where pos = (.) line pos text = (.) line text
- Client.State.EditBox: line :: HasLine c_aIxW => Lens' c_aIxW Line
+ Client.State.EditBox: line :: HasLine c_az5Q => Lens' c_az5Q Line
- Client.State.EditBox: pos :: HasLine c_aIxW => Lens' c_aIxW Int
+ Client.State.EditBox: pos :: HasLine c_az5Q => Lens' c_az5Q Int
- Client.State.EditBox: text :: HasLine c_aIxW => Lens' c_aIxW String
+ Client.State.EditBox: text :: HasLine c_az5Q => Lens' c_az5Q String
- Client.State.EditBox.Content: class HasLine c_aIxW where pos = (.) line pos text = (.) line text
+ Client.State.EditBox.Content: class HasLine c_az5Q where pos = (.) line pos text = (.) line text
- Client.State.EditBox.Content: line :: HasLine c_aIxW => Lens' c_aIxW Line
+ Client.State.EditBox.Content: line :: HasLine c_az5Q => Lens' c_az5Q Line
- Client.State.EditBox.Content: pos :: HasLine c_aIxW => Lens' c_aIxW Int
+ Client.State.EditBox.Content: pos :: HasLine c_az5Q => Lens' c_az5Q Int
- Client.State.EditBox.Content: text :: HasLine c_aIxW => Lens' c_aIxW String
+ Client.State.EditBox.Content: text :: HasLine c_az5Q => Lens' c_az5Q String
- Client.View.Help: helpImageLines :: Maybe Text -> Palette -> [Image']
+ Client.View.Help: helpImageLines :: ClientState -> Maybe Text -> Palette -> [Image']
Files
- ChangeLog.md +21/−0
- README.md +16/−0
- exec/Exports.hs +30/−0
- exec/Main.hs +10/−11
- exec/linux_exported_symbols.txt +4/−0
- exec/macos_exported_symbols.txt +4/−0
- glirc.cabal +20/−24
- include/glirc-api.h +20/−2
- src/Client/Authentication/Ecdsa.hs +4/−6
- src/Client/CApi.hs +54/−6
- src/Client/CApi/Exports.hs +152/−45
- src/Client/CApi/Types.hsc +77/−4
- src/Client/Commands.hs +268/−186
- src/Client/Commands/Arguments.hs +0/−75
- src/Client/Commands/Arguments/Parser.hs +65/−0
- src/Client/Commands/Arguments/Renderer.hs +106/−0
- src/Client/Commands/Arguments/Spec.hs +64/−0
- src/Client/Commands/Interpolation.hs +12/−11
- src/Client/Configuration.hs +43/−25
- src/Client/Configuration/ServerSettings.hs +9/−5
- src/Client/EventLoop/Actions.hs +3/−1
- src/Client/Image/Arguments.hs +0/−66
- src/Client/Image/Message.hs +44/−31
- src/Client/Image/MircFormatting.hs +39/−51
- src/Client/Image/StatusLine.hs +14/−10
- src/Client/Image/Textbox.hs +43/−19
- src/Client/Log.hs +3/−5
- src/Client/Network/Async.hs +5/−3
- src/Client/Network/Connect.hs +22/−27
- src/Client/State.hs +37/−17
- src/Client/State/Network.hs +4/−1
- src/Client/View.hs +1/−1
- src/Client/View/Digraphs.hs +5/−3
- src/Client/View/Help.hs +32/−26
- src/Client/View/Mentions.hs +25/−18
- src/Client/View/Messages.hs +1/−7
- src/Client/View/Palette.hs +56/−15
- src/DigraphQuote.hs +53/−0
- src/Digraphs.hs +1356/−1359
- src/RtsStats.hs +75/−70
ChangeLog.md view
@@ -1,5 +1,26 @@ # Revision history for glirc2 +## 2.23++* CONFIGURATION FILE CHANGE: Relative paths are now resolved+ from the directory of the configuration file. The "~" alias+ for the home directory works for resolving paths relative+ to $HOME. Absolute paths are unmodified.++* Support network:#channel in /query++* Add an optional OTR extension. See the otr-extension/ directory+ and the wiki for more information.++* Extended the C API with `glirc_inject_chat` and `chat_entrypoint`++* Smarter WYSIWYG highlighting in text edit box. Nicknames are+ highlighted and `/mode` command parameters get correct placeholders.++* Added more information to `/palette`++* Added "human readable" labels to `/rtsstats`+ ## 2.22 * Added dynanic indentation of wrapped lines. Lines now
README.md view
@@ -95,6 +95,11 @@ Learn more about this file format at [config-value](http://hackage.haskell.org/package/config-value) ```+-- vim: filetype=config-value+-- Grab the Vim syntax highlighting file from the config-value package++-- Learn more about these settings with `glirc2 --config-format`+ -- Defaults used when not specified on command line defaults: nick: "yournick"@@ -143,7 +148,18 @@ extra-highlights: ["glirc", "lens"] +nick-padding:+ side: left -- try right if you don't like left padding+ width: 13++url-opener: "open" -- This works on macOS, "gnome-open" for GNOME++key-bindings:+ * bind: "C-M-b"+ command: "masks b"+ palette:+ line-marker: yellow time: fg: [10,10,10] -- RGB values for color for timestamps bg: blue
+ exec/Exports.hs view
@@ -0,0 +1,30 @@+{-|+Module : Exports+Description : C API function exports+Copyright : (c) Eric Mertens, 2017+License : ISC+Maintainer : emertens@gmail.com++Entry point into glirc2 from the C API+-}+module Exports () where++import Client.CApi.Exports+import Client.CApi.Types+import Foreign.C++foreign export ccall glirc_send_message :: Glirc_send_message+foreign export ccall glirc_print :: Glirc_print+foreign export ccall glirc_inject_chat :: Glirc_inject_chat+foreign export ccall glirc_list_networks :: Glirc_list_networks+foreign export ccall glirc_identifier_cmp :: Glirc_identifier_cmp+foreign export ccall glirc_is_channel :: Glirc_is_channel+foreign export ccall glirc_is_logged_on :: Glirc_is_channel+foreign export ccall glirc_list_channels :: Glirc_list_channels+foreign export ccall glirc_list_channel_users :: Glirc_list_channel_users+foreign export ccall glirc_my_nick :: Glirc_my_nick+foreign export ccall glirc_mark_seen :: Glirc_mark_seen+foreign export ccall glirc_clear_window :: Glirc_clear_window+foreign export ccall glirc_free_string :: Glirc_free_string+foreign export ccall glirc_free_strings :: Glirc_free_strings+foreign export ccall glirc_current_focus :: Glirc_current_focus
exec/Main.hs view
@@ -1,5 +1,4 @@ {-|-Module : Main Description : Entry-point of executable Copyright : (c) Eric Mertens, 2016 License : ISC@@ -7,10 +6,8 @@ Entry point into glirc2. This module sets up VTY and launches the client. -}- module Main where -import Control.Concurrent import Control.Exception import Control.Lens import Control.Monad@@ -26,14 +23,16 @@ import Client.State import Client.State.Focus +import Exports ()+ -- | Main action for IRC client main :: IO () main = do opts <- getOptions- cfg <- loadConfiguration' (view optConfigFile opts)- runInUnboundThread $- withClientState cfg $ \st0 ->- withVty $ \vty ->+ let mbPath = view optConfigFile opts+ cfg <- loadConfiguration' mbPath+ withClientState mbPath cfg $ \st0 ->+ withVty $ \vty -> do st1 <- clientStartExtensions st0 st2 <- initialNetworkLogic opts st1 st3 <- updateTerminalSize vty st2@@ -54,10 +53,10 @@ Right cfg -> return cfg Left (ConfigurationReadFailed e) -> report "Failed to open configuration:" e- Left (ConfigurationParseFailed path e) ->- report ("Failed to parse configuration file: " ++ path) e- Left (ConfigurationMalformed path e) ->- report ("Malformed configuration file: " ++ path ++ "\n(try --config-format)") e+ Left (ConfigurationParseFailed p e) ->+ report ("Failed to parse configuration file: " ++ p) e+ Left (ConfigurationMalformed p e) ->+ report ("Malformed configuration file: " ++ p ++ "\n(try --config-format)") e where report problem msg = do hPutStrLn stderr problem
exec/linux_exported_symbols.txt view
@@ -1,13 +1,17 @@ { glirc_send_message; glirc_print;+glirc_inject_chat; glirc_identifier_cmp; glirc_list_networks; glirc_list_channels; glirc_list_channel_users; glirc_my_nick; glirc_mark_seen;+glirc_is_channel;+glirc_is_logged_on; glirc_clear_window;+glirc_current_focus; glirc_free_string; glirc_free_strings; };
exec/macos_exported_symbols.txt view
@@ -1,11 +1,15 @@ _glirc_send_message _glirc_print+_glirc_inject_chat _glirc_identifier_cmp _glirc_list_networks _glirc_list_channels _glirc_list_channel_users _glirc_my_nick _glirc_mark_seen+_glirc_is_channel+_glirc_is_logged_on _glirc_clear_window+_glirc_current_focus _glirc_free_string _glirc_free_strings
glirc.cabal view
@@ -1,5 +1,5 @@ name: glirc-version: 2.22+version: 2.23 synopsis: Console IRC client description: Console IRC client .@@ -25,34 +25,29 @@ custom-setup setup-depends: base >=4.9 && <4.11, filepath >=1.4 && <1.5,- Cabal >=1.24 && <3+ Cabal >=1.24 && <2.1 source-repository head type: git location: git://github.com/glguy/irc-core.git branch: v2 -flag ExportCApi- description: Export C functions used by the extension API (incompatible with GHCi)- default: True- manual: True- executable glirc2 main-is: Main.hs+ other-modules: Exports ghc-options: -threaded -rtsopts hs-source-dirs: exec default-language: Haskell2010 - if flag(ExportCApi)- if os(Linux)- ld-options: -Wl,--dynamic-list=exec/linux_exported_symbols.txt- if os(Darwin)- ld-options: -Wl,-exported_symbols_list,exec/macos_exported_symbols.txt- -- Constraints can be found on the library itself build-depends: base, glirc, lens, text, vty + if os(Linux)+ ld-options: -Wl,--dynamic-list=exec/linux_exported_symbols.txt+ if os(Darwin)+ ld-options: -Wl,-exported_symbols_list,exec/macos_exported_symbols.txt+ library ghc-options: -Wall -O2 hs-source-dirs: src@@ -67,7 +62,9 @@ Client.CApi.Exports Client.CApi.Types Client.Commands- Client.Commands.Arguments+ Client.Commands.Arguments.Spec+ Client.Commands.Arguments.Parser+ Client.Commands.Arguments.Renderer Client.Commands.Exec Client.Commands.Interpolation Client.Commands.Recognizer@@ -83,7 +80,6 @@ Client.Hook.Znc.Buffextras Client.Hooks Client.Image- Client.Image.Arguments Client.Image.Layout Client.Image.LineWrap Client.Image.Message@@ -121,26 +117,29 @@ other-modules: LensUtils StrictUnit Digraphs+ DigraphQuote RtsStats Paths_glirc Build_glirc build-depends: base >=4.9 && <4.11,+ HsOpenSSL >=0.11 && <0.12, async >=2.1 && <2.2, attoparsec >=0.13 && <0.14,+ base64-bytestring >=1.0.0.1&& <1.1, bytestring >=0.10.8 && <0.11,- base64-bytestring >=1.0.0.1 && <1.1,- config-value >=0.6 && <0.7, config-schema >=0.4 && <0.6,+ config-value >=0.6 && <0.7, containers >=0.5.7 && <0.6, directory >=1.2.6 && <1.4, filepath >=1.4.1 && <1.5,+ free >=4.12 && <4.13, gitrev >=1.2 && <1.4, hashable >=1.2.4 && <1.3,- HsOpenSSL >=0.11 && <0.12,+ hookup >=0.1 && <0.2, irc-core >=2.3 && <2.4,- lens >=4.14 && <4.16, kan-extensions >=5.0 && <5.1,+ lens >=4.14 && <4.16, network >=2.6.2 && <2.7, process >=1.4.2 && <1.7, regex-tdfa >=1.2 && <1.3,@@ -148,17 +147,14 @@ socks >=0.5.5 && <0.6, split >=0.2 && <0.3, stm >=2.4 && <2.5,+ template-haskell >=2.11 && <2.13, text >=1.2.2 && <1.3, time >=1.6 && <1.9, transformers >=0.5.2 && <0.6, unix >=2.7 && <2.8, unordered-containers >=0.2.7 && <0.3, vector >=0.11 && <0.13,- vty >=5.11.1 && <5.16,- hookup >=0.1 && <0.2-- if flag(ExportCApi)- cpp-options: -DEXPORT_GLIRC_CAPI+ vty >=5.11.1 && <5.16 test-suite test type: exitcode-stdio-1.0
include/glirc-api.h view
@@ -33,6 +33,12 @@ size_t tags_n; }; +struct glirc_chat {+ struct glirc_string network;+ struct glirc_string target;+ struct glirc_string message;+};+ struct glirc_command { const struct glirc_string *params; size_t params_n;@@ -41,6 +47,7 @@ typedef void *start_type (struct glirc *G, const char *path); typedef void stop_type (struct glirc *G, void *S); typedef enum process_result process_message_type(struct glirc *G, void *S, const struct glirc_message *);+typedef enum process_result process_chat_type(struct glirc *G, void *S, const struct glirc_chat *); typedef void process_command_type(struct glirc *G, void *S, const struct glirc_command *); struct glirc_extension {@@ -50,17 +57,28 @@ stop_type *stop; process_message_type *process_message; process_command_type *process_command;+ process_chat_type *process_chat; }; int glirc_send_message(struct glirc *G, const struct glirc_message *);-int glirc_print(struct glirc *G, enum message_code, struct glirc_string msg);+int glirc_print(struct glirc *G, enum message_code, const char *msg, size_t msglen);+int glirc_inject_chat(struct glirc *G,+ const char* net, size_t netLen,+ const char* src, size_t srcLen,+ const char* tgt, size_t tgtLen,+ const char* msg, size_t msgLen); char ** glirc_list_networks(struct glirc *G); char ** glirc_list_channels(struct glirc *G, struct glirc_string network); char ** glirc_list_channel_users(struct glirc *G, struct glirc_string network, struct glirc_string channel);-char * glirc_my_nick(struct glirc *G, struct glirc_string network);+void glirc_current_focus(struct glirc *G, char **net, size_t *netlen, char **tgt , size_t *tgtlen);+char * glirc_my_nick(struct glirc *G, const char *net, size_t netlen); void glirc_mark_seen(struct glirc *G, struct glirc_string network, struct glirc_string channel); void glirc_clear_window(struct glirc *G, struct glirc_string network, struct glirc_string channel); int glirc_identifier_cmp(struct glirc_string s, struct glirc_string t);+int glirc_is_channel(struct glirc *G, const char *net, size_t netlen,+ const char *tgt, size_t tgtlen);+int glirc_is_logged_on(struct glirc *G, const char *net, size_t netlen,+ const char *tgt, size_t tgtlen); void glirc_free_string(char *); void glirc_free_strings(char **);
src/Client/Authentication/Ecdsa.hs view
@@ -20,7 +20,6 @@ , computeResponse ) where -import Client.Configuration (resolveConfigurationPath) import Control.Exception (displayException, try) import Data.ByteString.Base64 as Enc import Data.Text (Text)@@ -52,11 +51,10 @@ Text {- ^ challenge string -} -> IO (Either String Text) {- ^ error message or response string -} computeResponse privateKeyFile challenge =- do path <- resolveConfigurationPath privateKeyFile- res <- try $ readProcess- "ecdsatool"- ["sign", path, Text.unpack challenge]- "" -- stdin+ do res <- try $ readProcess+ "ecdsatool"+ ["sign", privateKeyFile, Text.unpack challenge]+ "" -- stdin return $! case words <$> res of Right [resp] -> Right $! Text.pack resp Right _ -> Left "bad sasl ecdsa response message"
src/Client/CApi.hs view
@@ -21,6 +21,7 @@ , deactivateExtension , notifyExtensions , commandExtension+ , chatExtension ) where import Client.CApi.Types@@ -29,7 +30,6 @@ import Control.Monad.Codensity import Data.Text (Text) import qualified Data.Text as Text-import qualified Data.Text.Foreign as Text import Foreign.C import Foreign.Marshal import Foreign.Ptr@@ -100,16 +100,19 @@ -- | Call all of the process message callbacks in the list of extensions. -- This operation marshals the IRC message once and shares that across -- all of the callbacks.+--+-- Returns 'True' to pass message to client. Returns 'False to drop message. notifyExtensions :: Ptr () {- ^ clientstate stable pointer -} ->- Text {- ^ network -} ->- RawIrcMsg {- ^ current message -} ->- [ActiveExtension] ->- IO Bool {- ^ Return 'True' to pass message -}+ Text {- ^ network -} ->+ RawIrcMsg {- ^ current message -} ->+ [ActiveExtension] {- ^ all active extensions -} ->+ IO Bool {- ^ should pass message -} notifyExtensions stab network msg aes | null aes' = return True | otherwise = doNotifications where+ -- only the extensions that have a incoming message callback aes' = [ (f,s) | ae <- aes , let f = fgnMessage (aeFgn ae) s = aeSession ae@@ -127,6 +130,40 @@ then go rest msgPtr else return False +-- | Call all of the process chat callbacks in the list of extensions.+-- This operation marshals the IRC message once and shares that across+-- all of the callbacks.+--+-- Returns 'True' to pass message to client. Returns 'False to drop message.+chatExtension ::+ Ptr () {- ^ clientstate stable pointer -} ->+ Text {- ^ network -} ->+ Text {- ^ target (channel or user) -} ->+ Text {- ^ message body -} ->+ [ActiveExtension] {- ^ all active extensions -} ->+ IO Bool {- ^ should pass message -}+chatExtension stab net tgt msg aes+ | null aes' = return True+ | otherwise = doNotifications+ where+ -- only the extensions that have a chat callback+ aes' = [ (f, aeSession ae)+ | ae <- aes+ , let f = fgnChat (aeFgn ae)+ , f /= nullFunPtr ]++ doNotifications = evalNestedIO $+ do chat <- withChat net tgt msg+ liftIO (go aes' chat)++ -- run handlers until one of them drops the message+ go [] _ = return True+ go ((f,s):rest) ptr =+ do res <- runProcessChat f stab s ptr+ if res == passMessage+ then go rest ptr+ else return False+ -- | Notify an extension of a client command with the given parameters. commandExtension :: Ptr () {- ^ client state stableptr -} ->@@ -160,6 +197,17 @@ nest1 $ with $ FgnMsg net pfxN pfxU pfxH cmd prmPtr (fromIntegral prmN) keysPtr valsPtr (fromIntegral tagN) +withChat ::+ Text {- ^ network -} ->+ Text {- ^ target -} ->+ Text {- ^ message -} ->+ NestedIO (Ptr FgnChat)+withChat net tgt msg =+ do net' <- withText net+ tgt' <- withText tgt+ msg' <- withText msg+ nest1 $ with $ FgnChat net' tgt' msg'+ withCommand :: [Text] {- ^ parameters -} -> NestedIO (Ptr FgnCmd)@@ -176,7 +224,7 @@ withText :: Text -> NestedIO FgnStringLen withText txt =- do (ptr,len) <- nest1 $ Text.withCStringLen txt+ do (ptr,len) <- nest1 $ withText0 txt return $ FgnStringLen ptr $ fromIntegral len ------------------------------------------------------------------------
src/Client/CApi/Exports.hs view
@@ -1,4 +1,4 @@-{-# Language CPP, RecordWildCards #-}+{-# Language RecordWildCards #-} {-| Module : Client.CApi.Exports Description : Foreign exports which expose functionality for extensions@@ -12,16 +12,49 @@ module Client.CApi.Exports ( -- * Extension API types Glirc_send_message+ , glirc_send_message+ , Glirc_print+ , glirc_print+ , Glirc_list_networks+ , glirc_list_networks+ , Glirc_list_channels+ , glirc_list_channels+ , Glirc_list_channel_users+ , glirc_list_channel_users+ , Glirc_my_nick+ , glirc_my_nick+ , Glirc_identifier_cmp+ , glirc_identifier_cmp++ , Glirc_is_channel+ , glirc_is_channel++ , Glirc_is_logged_on+ , glirc_is_logged_on+ , Glirc_mark_seen+ , glirc_mark_seen+ , Glirc_clear_window+ , glirc_clear_window++ , Glirc_current_focus+ , glirc_current_focus+ , Glirc_free_string+ , glirc_free_string+ , Glirc_free_strings+ , glirc_free_strings++ , Glirc_inject_chat+ , glirc_inject_chat ) where import Client.CApi.Types@@ -49,6 +82,7 @@ import Irc.Identifier import Irc.RawIrcMsg import Irc.UserInfo+import Irc.Message import LensUtils ------------------------------------------------------------------------@@ -98,10 +132,6 @@ Ptr FgnMsg {- ^ pointer to message -} -> IO CInt {- ^ 0 on success -} -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall "glirc_send_message" glirc_send_message :: Glirc_send_message-#endif- glirc_send_message :: Glirc_send_message glirc_send_message token msgPtr = do mvar <- derefToken token@@ -125,10 +155,6 @@ CSize {- ^ message length -} -> IO CInt {- ^ 0 on success -} -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_print :: Glirc_print-#endif- glirc_print :: Glirc_print glirc_print stab code msgPtr msgLen = do mvar <- derefToken stab@@ -149,16 +175,46 @@ ------------------------------------------------------------------------ +-- | Print a message or error to the client window+type Glirc_inject_chat =+ Ptr () {- ^ api token -} ->+ CString {- ^ network -} ->+ CSize {- ^ network length -} ->+ CString {- ^ source -} ->+ CSize {- ^ source length -} ->+ CString {- ^ target -} ->+ CSize {- ^ target length -} ->+ CString {- ^ message -} ->+ CSize {- ^ message length -} ->+ IO CInt {- ^ 0 on success -}++glirc_inject_chat :: Glirc_inject_chat+glirc_inject_chat stab netPtr netLen srcPtr srcLen tgtPtr tgtLen msgPtr msgLen =+ do mvar <- derefToken stab+ net <- peekFgnStringLen (FgnStringLen netPtr netLen)+ src <- peekFgnStringLen (FgnStringLen srcPtr srcLen)+ tgt <- mkId <$> peekFgnStringLen (FgnStringLen tgtPtr tgtLen)+ txt <- peekFgnStringLen (FgnStringLen msgPtr msgLen)+ now <- getZonedTime++ let msg = ClientMessage+ { _msgBody = IrcBody (Privmsg (parseUserInfo src) tgt txt)+ , _msgTime = now+ , _msgNetwork = net+ }+ modifyMVar_ mvar $ \st ->+ do return (recordChannelMessage net tgt msg st)+ return 0+ `catch` \SomeException{} -> return 1++------------------------------------------------------------------------+ -- | The resulting strings and array of strings are malloc'd and the -- caller must free them. NULL returned on failure. type Glirc_list_networks = Ptr () {- ^ api token -} -> IO (Ptr CString) {- ^ null terminated array of null terminated strings -} -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_list_networks :: Glirc_list_networks-#endif- glirc_list_networks :: Glirc_list_networks glirc_list_networks stab = do mvar <- derefToken stab@@ -180,10 +236,6 @@ CSize {- ^ identifier 2 len -} -> IO CInt -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_identifier_cmp :: Glirc_identifier_cmp-#endif- glirc_identifier_cmp :: Glirc_identifier_cmp glirc_identifier_cmp p1 n1 p2 n2 = do txt1 <- peekFgnStringLen (FgnStringLen p1 n1)@@ -203,10 +255,6 @@ CSize {- ^ network len -} -> IO (Ptr CString) {- ^ null terminated array of null terminated strings -} -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_list_channels :: Glirc_list_channels-#endif- glirc_list_channels :: Glirc_list_channels glirc_list_channels stab networkPtr networkLen = do mvar <- derefToken stab@@ -230,10 +278,6 @@ CSize {- ^ channel len -} -> IO (Ptr CString) {- ^ null terminated array of null terminated strings -} -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_list_channel_users :: Glirc_list_channel_users-#endif- glirc_list_channel_users :: Glirc_list_channel_users glirc_list_channel_users stab networkPtr networkLen channelPtr channelLen = do mvar <- derefToken stab@@ -260,10 +304,6 @@ CSize {- ^ network name length -} -> IO CString -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_my_nick :: Glirc_my_nick-#endif- glirc_my_nick :: Glirc_my_nick glirc_my_nick stab networkPtr networkLen = do mvar <- derefToken stab@@ -287,10 +327,6 @@ CSize {- ^ channel name length -} -> IO () -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_mark_seen :: Glirc_mark_seen-#endif- glirc_mark_seen :: Glirc_mark_seen glirc_mark_seen stab networkPtr networkLen channelPtr channelLen = do network <- peekFgnStringLen (FgnStringLen networkPtr networkLen)@@ -318,10 +354,6 @@ CSize {- ^ channel name length -} -> IO () -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_clear_window :: Glirc_clear_window-#endif- glirc_clear_window :: Glirc_clear_window glirc_clear_window stab networkPtr networkLen channelPtr channelLen = do network <- peekFgnStringLen (FgnStringLen networkPtr networkLen)@@ -344,10 +376,6 @@ CString {- ^ glirc allocated string -} -> IO () -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_free_string :: Glirc_free_string-#endif- glirc_free_string :: Glirc_free_string glirc_free_string = free @@ -359,12 +387,91 @@ Ptr CString {- ^ glirc allocated strings, null-terminated -} -> IO () -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_free_strings :: Glirc_free_strings-#endif- glirc_free_strings :: Glirc_free_strings glirc_free_strings p = unless (p == nullPtr) $ do traverse_ free =<< peekArray0 nullPtr p free p++------------------------------------------------------------------------++-- | Free an array of heap allocated strings found as a return value+-- from the extension API. If argument is NULL, nothing happens.+--+-- Free the allocated strings with @glirc_free_string@+--+-- Strings set to NULL if there is no current network or no+-- current target.+type Glirc_current_focus =+ Ptr () {- ^ api token -} ->+ Ptr CString {- ^ newly allocated network string -} ->+ Ptr CSize {- ^ network length -} ->+ Ptr CString {- ^ newly allocated target string -} ->+ Ptr CSize {- ^ target length -} ->+ IO ()++glirc_current_focus :: Glirc_current_focus+glirc_current_focus stab netP netL tgtP tgtL =+ do mvar <- derefToken stab+ st <- readMVar mvar+ case view clientFocus st of+ Unfocused -> do poke' netP nullPtr+ poke' netL 0+ poke' tgtP nullPtr+ poke' tgtL 0++ NetworkFocus n -> do exportText netP netL n+ poke' tgtP nullPtr+ poke' tgtL 0++ ChannelFocus n t -> do exportText netP netL n+ exportText tgtP tgtL (idText t)++------------------------------------------------------------------------++-- | Returns @1@ when the given target on the given network is a channel+-- name, otherwise returns @0@.+--+-- If the given network is not currently active this returns @0@.+type Glirc_is_channel =+ Ptr () {- ^ api token -} ->+ CString {- ^ network name -} ->+ CSize {- ^ network length -} ->+ CString {- ^ target name -} ->+ CSize {- ^ target length -} ->+ IO CInt {- ^ boolean -}++glirc_is_channel :: Glirc_is_channel+glirc_is_channel stab net netL tgt tgtL =+ do mvar <- derefToken stab+ st <- readMVar mvar+ network <- peekFgnStringLen (FgnStringLen net netL)+ target <- peekFgnStringLen (FgnStringLen tgt tgtL)++ case preview (clientConnection network) st of+ Just cs | isChannelIdentifier cs (mkId target) -> return 1+ _ -> return 0++------------------------------------------------------------------------++-- | Returns @1@ when the given target on the given network shares a+-- channel with the user, @0@ otherwise.+--+-- If the given network is not currently active this returns @0@.+type Glirc_is_logged_on =+ Ptr () {- ^ api token -} ->+ CString {- ^ network name -} ->+ CSize {- ^ network length -} ->+ CString {- ^ target name -} ->+ CSize {- ^ target length -} ->+ IO CInt {- ^ boolean -}++glirc_is_logged_on :: Glirc_is_channel+glirc_is_logged_on stab net netL tgt tgtL =+ do mvar <- derefToken stab+ st <- readMVar mvar+ network <- peekFgnStringLen (FgnStringLen net netL)+ target <- peekFgnStringLen (FgnStringLen tgt tgtL)++ let online = has (clientConnection network . csUsers . ix (mkId target)) st+ return $! if online then 1 else 0
src/Client/CApi/Types.hsc view
@@ -1,4 +1,4 @@-{-# Language ForeignFunctionInterface, RecordWildCards #-}+{-# Language RecordWildCards #-} {-| Module : Client.CApi.Types@@ -30,23 +30,36 @@ -- * Commands , FgnCmd(..) + -- * Chat+ , FgnChat(..)+ -- * Function pointer calling , Dynamic , runStartExtension , runStopExtension , runProcessMessage , runProcessCommand+ , runProcessChat -- * report message codes , MessageCode(..), normalMessage, errorMessage -- * process message results , MessageResult(..), passMessage, dropMessage++ -- * Marshaling helpers+ , withText0+ , exportText+ , poke' ) where -import Foreign.C-import Foreign.Ptr-import Foreign.Storable+import Control.Monad+import Data.Text (Text)+import qualified Data.Text.Foreign as Text+import Foreign.C+import Foreign.Marshal.Array+import Foreign.Ptr+import Foreign.Storable -- | Tag for describing the kind of message to display in the client -- as used in `glirc_print`.@@ -90,6 +103,13 @@ Ptr FgnCmd {- ^ command -} -> IO () +-- | @typedef void process_chat(void *glirc, void *S, const struct glirc_chat *);@+type ProcessChat =+ Ptr () {- ^ api token -} ->+ Ptr () {- ^ extension state -} ->+ Ptr FgnChat {- ^ chat info -} ->+ IO MessageResult+ -- | Type of dynamic function pointer wrappers. type Dynamic a = FunPtr a -> a @@ -97,6 +117,7 @@ foreign import ccall "dynamic" runStopExtension :: Dynamic StopExtension foreign import ccall "dynamic" runProcessMessage :: Dynamic ProcessMessage foreign import ccall "dynamic" runProcessCommand :: Dynamic ProcessCommand+foreign import ccall "dynamic" runProcessChat :: Dynamic ProcessChat ------------------------------------------------------------------------ @@ -105,6 +126,7 @@ { fgnStart :: FunPtr StartExtension -- ^ Optional startup callback , fgnStop :: FunPtr StopExtension -- ^ Optional shutdown callback , fgnMessage :: FunPtr ProcessMessage -- ^ Optional message received callback+ , fgnChat :: FunPtr ProcessChat -- ^ Optional message send callback , fgnCommand :: FunPtr ProcessCommand -- ^ Optional client command callback , fgnName :: CString -- ^ Null-terminated name , fgnMajorVersion, fgnMinorVersion :: CInt -- ^ extension version@@ -117,6 +139,7 @@ <$> (#peek struct glirc_extension, start ) p <*> (#peek struct glirc_extension, stop ) p <*> (#peek struct glirc_extension, process_message) p+ <*> (#peek struct glirc_extension, process_chat ) p <*> (#peek struct glirc_extension, process_command) p <*> (#peek struct glirc_extension, name ) p <*> (#peek struct glirc_extension, major_version ) p@@ -125,6 +148,7 @@ do (#poke struct glirc_extension, start ) p fgnStart (#poke struct glirc_extension, stop ) p fgnStop (#poke struct glirc_extension, process_message) p fgnMessage+ (#poke struct glirc_extension, process_chat ) p fgnChat (#poke struct glirc_extension, process_command) p fgnCommand (#poke struct glirc_extension, name ) p fgnName (#poke struct glirc_extension, major_version ) p fgnMajorVersion@@ -175,6 +199,28 @@ ------------------------------------------------------------------------ +-- | @struct glirc_message@+data FgnChat = FgnChat+ { fhNetwork :: FgnStringLen+ , fhTarget :: FgnStringLen+ , fhMessage :: FgnStringLen+ }++instance Storable FgnChat where+ alignment _ = #alignment struct glirc_chat+ sizeOf _ = #size struct glirc_chat+ peek p = FgnChat+ <$> (#peek struct glirc_chat, network) p+ <*> (#peek struct glirc_chat, target ) p+ <*> (#peek struct glirc_chat, message) p++ poke p FgnChat{..} =+ do (#poke struct glirc_chat, network) p fhNetwork+ (#poke struct glirc_chat, target ) p fhTarget+ (#poke struct glirc_chat, message) p fhMessage++------------------------------------------------------------------------+ -- | @struct glirc_command@ data FgnCmd = FgnCmd { fcParams :: Ptr FgnStringLen -- ^ array@@ -206,3 +252,30 @@ poke p (FgnStringLen x y) = do (#poke struct glirc_string, str) p x (#poke struct glirc_string, len) p y++------------------------------------------------------------------------++-- | Like 'poke' except it doesn't write to NULL+poke' :: Storable a => Ptr a -> a -> IO ()+poke' ptr x = unless (nullPtr == ptr) (poke ptr x)++-- | Marshal a text as a malloced null-terminated CStringLen+exportText :: Ptr CString -> Ptr CSize -> Text -> IO ()+exportText dstP dstL txt =++ Text.withCStringLen txt $ \(srcP, srcL) ->+ do poke' dstL (fromIntegral srcL)+ unless (dstP == nullPtr) $+ do a <- mallocArray0 srcL+ copyArray a srcP srcL+ pokeElemOff a srcL 0+ poke dstP a++-- | Marshal a text as a temporary null-terminated CStringLen+withText0 :: Text -> (CStringLen -> IO a) -> IO a+withText0 txt k =+ Text.withCStringLen txt $ \(p,l) ->+ allocaArray0 l $ \p' ->+ do copyArray p' p l+ pokeElemOff p' l 0+ k (p', l)
src/Client/Commands.hs view
@@ -26,7 +26,8 @@ ) where import Client.CApi-import Client.Commands.Arguments+import Client.Commands.Arguments.Spec+import Client.Commands.Arguments.Parser import Client.Commands.Exec import Client.Commands.Interpolation import Client.Commands.Recognizer@@ -50,9 +51,10 @@ import Data.List.Split import qualified Data.HashMap.Strict as HashMap import qualified Data.Map as Map+import Data.Maybe (fromMaybe)+import Data.Monoid ((<>)) import Data.Text (Text) import qualified Data.Text as Text-import Data.Monoid ((<>)) import Data.Time import Irc.Commands import Irc.Identifier@@ -63,7 +65,6 @@ import LensUtils import RtsStats (getStats) import System.Process-import Text.Read import Text.Regex.TDFA import Text.Regex.TDFA.String (compile) @@ -108,7 +109,7 @@ { -- | Names of this command, first in the list is the "primary" name cmdNames :: NonEmpty Text -- | Specification of the arguments of the command- , cmdArgumentSpec :: ArgumentSpec a+ , cmdArgumentSpec :: Args ClientState a -- | Multi-line IRC-formatted documentation text used for @/help@ , cmdDocumentation :: Text -- | Implementation of the command for both execution and tab completion@@ -150,23 +151,27 @@ -- | Execute command provided by user, resolve aliases if necessary. executeUserCommand :: Maybe Text -> String -> ClientState -> IO CommandResult executeUserCommand discoTime command st = do- let key = Text.takeWhile (/=' ') tcmd- rest = dropWhile (==' ') . dropWhile (/=' ') $ command+ let key = Text.takeWhile (/=' ') (Text.pack command)+ rest = dropWhile (==' ') (dropWhile (/=' ') command) case views (clientConfig . configMacros) (recognize key) st of Exact (Macro (MacroSpec spec) cmdExs) ->- case parseArguments spec rest *> traverse resolveMacro cmdExs of+ case doExpansion spec cmdExs rest of Nothing -> commandFailureMsg "macro expansions failed" st Just cmds -> process cmds st _ -> executeCommand Nothing command st where- resolveMacro = resolveMacroExpansions (commandExpansion discoTime st) expandInt+ doExpansion spec cmdExs rest =+ do args <- parse st spec rest+ traverse (resolveMacro (map Text.pack args)) cmdExs - tcmd = Text.pack command- args = Text.words tcmd+ resolveMacro args = resolveMacroExpansions (commandExpansion discoTime st) (expandInt args) - expandInt i = preview (ix (fromInteger i)) args+ expandInt :: [a] -> Integer -> Maybe a+ expandInt args i = preview (ix (fromInteger i)) args ++ process [] st0 = commandSuccess st0 process (c:cs) st0 = do res <- executeCommand Nothing (Text.unpack c) st0@@ -209,15 +214,20 @@ | Just !cs <- preview (clientConnection network) st -> do now <- getZonedTime let msgTxt = Text.pack $ takeWhile (/='\n') msg- ircMsg = ircPrivmsg (idText channel) msgTxt- myNick = UserInfo (view csNick cs) "" ""+ tgtTxt = idText channel++ (st1,allow) <- clientPark st $ \ptr ->+ do let aes = view (clientExtensions . esActive) st+ chatExtension ptr network tgtTxt msgTxt aes++ when allow (sendMsg cs (ircPrivmsg tgtTxt msgTxt))++ let myNick = UserInfo (view csNick cs) "" "" entry = ClientMessage- { _msgTime = now- , _msgNetwork = network- , _msgBody = IrcBody (Privmsg myNick channel msgTxt)- }- sendMsg cs ircMsg- commandSuccess $! recordChannelMessage network channel entry st+ { _msgTime = now+ , _msgNetwork = network+ , _msgBody = IrcBody (Privmsg myNick channel msgTxt) }+ commandSuccess $! recordChannelMessage network channel entry st1 _ -> commandFailureMsg "cannot send chat messages to this window" st @@ -238,7 +248,7 @@ case tabCompleteReversed of Just isReversed -> tab isReversed st rest Nothing ->- case parseArguments spec rest of+ case parse st spec rest of Nothing -> commandFailureMsg "bad command arguments" st Just arg -> exec st arg in@@ -295,13 +305,13 @@ [ Command (pure "exit")- NoArg+ (pure ()) "Exit the client immediately.\n" $ ClientCommand cmdExit noClientTab , Command (pure "reload")- (OptTokenArg "filename" NoArg)+ (optionalArg (simpleToken "filename")) "Reload the client configuration file.\n\ \\n\ \If \^Bfilename\^B is provided it will be used to reload.\n\@@ -310,7 +320,7 @@ , Command (pure "extension")- (ReqTokenArg "extension" (RemainingArg "arguments"))+ (liftA2 (,) (simpleToken "extension") (remainingArg "arguments")) "Calls the process_command callback of the given extension.\n\ \\n\ \\^Bextension\^B should be the name of the loaded extension.\n"@@ -318,21 +328,28 @@ , Command (pure "palette")- NoArg+ (pure ()) "Show the current palette settings and a color chart to help pick new colors.\n" $ ClientCommand cmdPalette noClientTab , Command (pure "digraphs")- NoArg- "Show the table of digraphs.\n\+ (pure ())+ "\^BDescription:\^B\n\ \\n\- \To enter a digraph type the two-character entry and press M-k.\n"+ \ Show the table of digraphs. A digraph is a pair of characters\n\+ \ can be used together to represent an uncommon character. Type\n\+ \ the two-character digraph corresponding to the desired output\n\+ \ character and then press M-k (default binding).\n\+ \\n\+ \ Note that the digraphs list is searchable with /grep.\n\+ \\n\+ \\^BSee also:\^B grep\n" $ ClientCommand cmdDigraphs noClientTab , Command (pure "keymap")- NoArg+ (pure ()) "Show the key binding map.\n\ \\n\ \Key bindings can be changed in configuration file. See `glirc2 --config-format`.\n"@@ -340,13 +357,13 @@ , Command (pure "rtsstats")- NoArg+ (pure ()) "Show the GHC RTS statistics.\n" $ ClientCommand cmdRtsStats noClientTab , Command (pure "exec")- (RemainingArg "arguments")+ (remainingArg "arguments") "Execute a command synchnonously sending the to a configuration destination.\n\ \\n\ \\^Barguments\^B: [-n network] [-c channel] [-i input] command [command arguments...]\n\@@ -366,7 +383,7 @@ , Command (pure "url")- (OptTokenArg "number" NoArg)+ (optionalArg numberArg) "Open a URL seen in chat.\n\ \\n\ \The URL is opened using the executable configured under \^Burl-opener\^B.\n\@@ -378,7 +395,7 @@ , Command (pure "help")- (OptTokenArg "command" NoArg)+ (optionalArg (simpleToken "command")) "Show command documentation.\n\ \\n\ \When \^Bcommand\^B is omitted a list of all commands is displayed.\n\@@ -391,25 +408,25 @@ [ Command (pure "toggle-detail")- NoArg+ (pure ()) "Toggle detailed message view.\n" $ ClientCommand cmdToggleDetail noClientTab , Command (pure "toggle-activity-bar")- NoArg+ (pure ()) "Toggle detailed detailed activity information in status bar.\n" $ ClientCommand cmdToggleActivityBar noClientTab , Command (pure "toggle-metadata")- NoArg+ (pure ()) "Toggle visibility of metadata in chat windows.\n" $ ClientCommand cmdToggleMetadata noClientTab , Command (pure "toggle-layout")- NoArg+ (pure ()) "Toggle multi-window layout mode.\n" $ ClientCommand cmdToggleLayout noClientTab @@ -419,7 +436,7 @@ [ Command (pure "connect")- (ReqTokenArg "network" NoArg)+ (simpleToken "network") "Connect to \^Bnetwork\^B by name.\n\ \\n\ \If no name is configured the hostname is the 'name'.\n"@@ -427,13 +444,13 @@ , Command (pure "reconnect")- NoArg+ (pure ()) "Reconnect to the current network.\n" $ ClientCommand cmdReconnect noClientTab , Command (pure "disconnect")- NoArg+ (pure ()) "Immediately terminate the current network connection.\n\ \\n\ \See also: /quit /exit\n"@@ -441,7 +458,7 @@ , Command (pure "quit")- (RemainingArg "reason")+ (remainingArg "reason") "Gracefully disconnect the current network connection.\n\ \\n\ \\^Breason\^B: optional quit reason\n\@@ -455,7 +472,7 @@ [ Command (pure "focus")- (ReqTokenArg "network" (OptTokenArg "target" NoArg))+ (liftA2 (,) (simpleToken "network") (optionalArg (simpleToken "target"))) "Change the focused window.\n\ \\n\ \When only \^Bnetwork\^B is specified this switches to the network status window.\n\@@ -467,18 +484,37 @@ , Command ("query" :| ["c", "channel"])- (ReqTokenArg "target" NoArg)- "Change the focused window.\n\+ (simpleToken "focus")+ "\^BParameters:\^B\n\ \\n\- \Changes the focus to the \^Btarget\^B chat window on the current network.\n\+ \ focuses: Focus name\n\ \\n\- \Nicknames and channels can be specified in the \^Btarget\^B parameter.\n\- \See also: /focus to switch to a target on a different network.\n"- $ NetworkCommand cmdQuery tabQuery+ \\^BDescription:\^B\n\+ \\n\+ \ This command sets the current window focus. When\n\+ \ no network is specified, the current network will\n\+ \ be used.\n\+ \\n\+ \ Client: *\n\+ \ Network: \^_network\^_:\n\+ \ Channel: \^_#channel\^_\n\+ \ Channel: \^_network\^_:\^_#channel\^_\n\+ \ User: \^_nick\^_\n\+ \ User: \^_network\^_:\^_nick\^_\n\+ \\n\+ \\^BExamples:\^B\n\+ \\n\+ \ /c fn:#haskell\n\+ \ /c #haskell\n\+ \ /c fn:\n\+ \ /c *:\n\+ \\n\+ \\^BSee also:\^B focus\n"+ $ ClientCommand cmdQuery tabQuery , Command (pure "clear")- (OptTokenArg "network" (OptTokenArg "channel" NoArg))+ (optionalArg (liftA2 (,) (simpleToken "network") (optionalArg (simpleToken "channel")))) "Clear a window.\n\ \\n\ \If no arguments are provided the current window is cleared.\n\@@ -491,7 +527,7 @@ , Command (pure "windows")- (OptTokenArg "kind" NoArg)+ (optionalArg (simpleToken "kind")) "Show a list of all windows with an optional argument to limit the kinds of windows listed.\n\ \\n\ \\^Bkind\^O: one of \^Bnetworks\^O, \^Bchannels\^O, \^Busers\^O\n\@@ -500,7 +536,7 @@ , Command (pure "splits")- (RemainingArg "focuses")+ (remainingArg "focuses") "\^BParameters:\^B\n\ \\n\ \ focuses: List of focus names\n\@@ -531,7 +567,7 @@ , Command (pure "splits+")- (RemainingArg "focuses")+ (remainingArg "focuses") "Add windows to the splits list. Omit the list of focuses to add the\ \ current window.\n\ \\n\@@ -547,7 +583,7 @@ , Command (pure "splits-")- (RemainingArg "focuses")+ (remainingArg "focuses") "Remove windows from the splits list. Omit the list of focuses to\ \ remove the current window.\n\ \\n\@@ -563,13 +599,13 @@ , Command (pure "ignore")- (RemainingArg "nicks")+ (remainingArg "nicks") "Toggle the soft-ignore on each of the space-delimited given nicknames.\n" $ ClientCommand cmdIgnore simpleClientTab , Command (pure "grep")- (RemainingArg "regular-expression")+ (remainingArg "regular-expression") "Set the persistent regular expression.\n\ \\n\ \Clear the regular expression by calling this without an argument.\n\@@ -580,7 +616,7 @@ , Command (pure "grepi")- (RemainingArg "regular-expression")+ (remainingArg "regular-expression") "Set the persistent regular expression.\n\ \\n\ \Clear the regular expression by calling this without an argument.\n\@@ -591,7 +627,7 @@ , Command (pure "mentions")- NoArg+ (pure ()) "Show a list of all message that were highlighted as important.\n" $ ClientCommand cmdMentions noClientTab @@ -601,7 +637,7 @@ [ Command ("join" :| ["j"])- (ReqTokenArg "channels" (OptTokenArg "keys" NoArg))+ (liftA2 (,) (simpleToken "channels") (optionalArg (simpleToken "keys"))) "\^BParameters:\^B\n\ \\n\ \ channels: Comma-separated list of channels\n\@@ -623,7 +659,7 @@ , Command (pure "part")- (RemainingArg "reason")+ (remainingArg "reason") "\^BParameters:\^B\n\ \\n\ \ reason: Optional message sent to channel as part reason\@@ -642,7 +678,7 @@ , Command (pure "msg")- (ReqTokenArg "target" (RemainingArg "message"))+ (liftA2 (,) (simpleToken "target") (remainingArg "message")) "\^BParameters:\^B\n\ \\n\ \ target: Comma-separated list of nicknames and channels\n\@@ -667,7 +703,7 @@ , Command (pure "me")- (RemainingArg "message")+ (remainingArg "message") "\^BParameters:\^B\n\ \\n\ \ message: Body of action message\n\@@ -687,7 +723,7 @@ , Command (pure "say")- (RemainingArg "message")+ (remainingArg "message") "\^BParameters:\^B\n\ \\n\ \ message: Body of message\n\@@ -707,7 +743,7 @@ , Command (pure "notice")- (ReqTokenArg "target" (RemainingArg "message"))+ (liftA2 (,) (simpleToken "target") (remainingArg "message")) "\^BParameters:\^B\n\ \\n\ \ target: Comma-separated list of nicknames and channels\n\@@ -734,7 +770,7 @@ , Command (pure "ctcp")- (ReqTokenArg "target" (ReqTokenArg "command" (RemainingArg "arguments")))+ (liftA3 (,,) (simpleToken "target") (simpleToken "command") (remainingArg "arguments")) "\^BParameters:\^B\n\ \\n\ \ target: Comma-separated list of nicknames and channels\n\@@ -757,7 +793,7 @@ , Command (pure "nick")- (ReqTokenArg "nick" NoArg)+ (simpleToken "nick") "\^BParameters:\^B\n\ \\n\ \ nick: New nickname\n\@@ -774,7 +810,7 @@ , Command (pure "away")- (RemainingArg "message")+ (remainingArg "message") "\^BParameters:\^B\n\ \\n\ \ message: Optional away message\n\@@ -795,7 +831,7 @@ , Command ("users" :| ["names"])- NoArg+ (pure ()) "\^BDescription:\^B\n\ \\n\ \ Show the user list for the current channel.\n\@@ -808,7 +844,7 @@ , Command (pure "channelinfo")- NoArg+ (pure ()) "\^BDescription:\^B\n\ \\n\ \ Show information about the current channel.\n\@@ -819,9 +855,9 @@ , Command (pure "quote")- (RemainingArg "raw IRC command")+ (remainingArg "raw IRC command") "Send a raw IRC command.\n"- $ NetworkCommand cmdQuote simpleNetworkTab+ $ NetworkCommand cmdQuote simpleNetworkTab ------------------------------------------------------------------------ ] , CommandSection "IRC queries"@@ -829,79 +865,79 @@ [ Command (pure "who")- (RemainingArg "arguments")+ (remainingArg "arguments") "Send WHO query to server with given arguments.\n" $ NetworkCommand cmdWho simpleNetworkTab , Command (pure "whois")- (RemainingArg "arguments")+ (remainingArg "arguments") "Send WHOIS query to server with given arguments.\n" $ NetworkCommand cmdWhois simpleNetworkTab , Command (pure "whowas")- (RemainingArg "arguments")+ (remainingArg "arguments") "Send WHOWAS query to server with given arguments.\n" $ NetworkCommand cmdWhowas simpleNetworkTab , Command (pure "ison")- (RemainingArg "arguments")+ (remainingArg "arguments") "Send ISON query to server with given arguments.\n" $ NetworkCommand cmdIson simpleNetworkTab , Command (pure "userhost")- (RemainingArg "arguments")+ (remainingArg "arguments") "Send USERHOST query to server with given arguments.\n" $ NetworkCommand cmdUserhost simpleNetworkTab , Command (pure "time")- (OptTokenArg "servername" NoArg)+ (optionalArg (simpleToken "servername")) "Send TIME query to server with given arguments.\n" $ NetworkCommand cmdTime simpleNetworkTab , Command (pure "stats")- (RemainingArg "arguments")+ (remainingArg "arguments") "Send STATS query to server with given arguments.\n" $ NetworkCommand cmdStats simpleNetworkTab , Command (pure "lusers")- (OptTokenArg "mask" (OptTokenArg "servername" NoArg))+ (optionalArg (liftA2 (,) (simpleToken "mask") (optionalArg (simpleToken "servername")))) "Send LUSERS query to server with given arguments.\n" $ NetworkCommand cmdLusers simpleNetworkTab , Command- (pure "motd") (OptTokenArg "servername" NoArg)+ (pure "motd") (optionalArg (simpleToken "servername")) "Send MOTD query to server.\n" $ NetworkCommand cmdMotd simpleNetworkTab , Command- (pure "admin") (OptTokenArg "servername" NoArg)+ (pure "admin") (optionalArg (simpleToken "servername")) "Send ADMIN query to server.\n" $ NetworkCommand cmdAdmin simpleNetworkTab , Command- (pure "rules") (OptTokenArg "servername" NoArg)+ (pure "rules") (optionalArg (simpleToken "servername")) "Send RULES query to server.\n" $ NetworkCommand cmdRules simpleNetworkTab , Command- (pure "info") NoArg+ (pure "info") (pure ()) "Send INFO query to server.\n" $ NetworkCommand cmdInfo noNetworkTab , Command- (pure "list") (RemainingArg "arguments")+ (pure "list") (remainingArg "arguments") "Send LIST query to server.\n" $ NetworkCommand cmdList simpleNetworkTab , Command- (pure "version") (OptTokenArg "servername" NoArg)+ (pure "version") (optionalArg (simpleToken "servername")) "Send VERSION query to server.\n" $ NetworkCommand cmdVersion simpleNetworkTab @@ -911,7 +947,7 @@ [ Command (pure "mode")- (RemainingArg "modes and parameters")+ (fromMaybe [] <$> optionalArg (extensionArg "modes" modeParamArgs)) "Sets IRC modes.\n\ \\n\ \Examples:\n\@@ -924,11 +960,11 @@ \When executed in a channel window, mode changes are applied to the channel.\n\ \\n\ \This command has parameter sensitive tab-completion.\n"- $ NetworkCommand cmdMode tabMode+ $ NetworkCommand cmdMode tabMode , Command (pure "masks")- (ReqTokenArg "mode" NoArg)+ (simpleToken "mode") "Show mask lists for current channel.\n\ \\n\ \Common \^Bmode\^B values:\n\@@ -942,13 +978,13 @@ , Command (pure "invite")- (ReqTokenArg "nick" NoArg)+ (simpleToken "nick") "Invite a user to the current channel.\n" $ ChannelCommand cmdInvite simpleChannelTab , Command (pure "topic")- (RemainingArg "message")+ (remainingArg "message") "Set the topic on the current channel.\n\ \\n\ \Tab-completion with no \^Bmessage\^B specified will load the current topic for editing.\n"@@ -956,15 +992,15 @@ , Command (pure "kick")- (ReqTokenArg "nick" (RemainingArg "reason"))+ (liftA2 (,) (simpleToken "nick") (remainingArg "reason")) "Kick a user from the current channel.\n\ \\n\ \See also: /kickban /remove\n"- $ ChannelCommand cmdKick simpleChannelTab+ $ ChannelCommand cmdKick simpleChannelTab , Command (pure "kickban")- (ReqTokenArg "nick" (RemainingArg "reason"))+ (liftA2 (,) (simpleToken "nick") (remainingArg "reason")) "Ban and kick a user from the current channel.\n\ \\n\ \Users are banned by hostname match.\n\@@ -973,7 +1009,7 @@ , Command (pure "remove")- (ReqTokenArg "nick" (RemainingArg "reason"))+ (liftA2 (,) (simpleToken "nick") (remainingArg "reason")) "Remove a user from the current channel.\n\ \\n\ \Remove works like /kick except it results in a PART.\n\@@ -982,7 +1018,7 @@ , Command (pure "knock")- (ReqTokenArg "channel" (RemainingArg "message"))+ (liftA2 (,) (simpleToken "channel") (remainingArg "message")) "Request entry to an invite-only channel.\n" $ NetworkCommand cmdKnock simpleNetworkTab @@ -992,7 +1028,7 @@ [ Command (pure "znc")- (RemainingArg "arguments")+ (remainingArg "arguments") "Send command directly to ZNC.\n\ \\n\ \The advantage of this over /msg is that responses are not broadcast to call clients.\n"@@ -1000,7 +1036,7 @@ , Command (pure "znc-playback")- (OptTokenArg "time" (OptTokenArg "date" NoArg))+ (optionalArg (liftA2 (,) (simpleToken "time") (optionalArg (simpleToken "date")))) "Request playback from the ZNC 'playback' module.\n\ \\n\ \\^Btime\^B determines the time to playback since.\n\@@ -1019,25 +1055,25 @@ [ Command (pure "oper")- (ReqTokenArg "user" (ReqTokenArg "password" NoArg))+ (liftA2 (,) (simpleToken "user") (simpleToken "password")) "Authenticate as a server operator.\n" $ NetworkCommand cmdOper noNetworkTab , Command (pure "kill")- (ReqTokenArg "client" (RemainingArg "reason"))+ (liftA2 (,) (simpleToken "client") (remainingArg "reason")) "Kill a client connection to the server.\n" $ NetworkCommand cmdKill simpleNetworkTab , Command (pure "map")- NoArg+ (pure ()) "Display network map.\n" $ NetworkCommand cmdMap simpleNetworkTab , Command (pure "links")- (RemainingArg "arguments")+ (remainingArg "arguments") "Send LINKS query to server with given arguments.\n" $ NetworkCommand cmdLinks simpleNetworkTab @@ -1094,14 +1130,14 @@ -- joined to this command will clear the messages but -- preserve the window. When used on a window that the -- user is not joined to this command will delete the window.-cmdClear :: ClientCommand (Maybe (String, Maybe (String, ())))+cmdClear :: ClientCommand (Maybe (String, Maybe String)) cmdClear st args = case args of Nothing -> clearFocus (view clientFocus st) Just ("*", Nothing) -> clearFocus Unfocused Just (network, Nothing) -> clearFocus (NetworkFocus (Text.pack network))- Just (network, Just ("*", _)) -> clearNetworkWindows network- Just (network, Just (channel, _)) ->+ Just (network, Just "*") -> clearNetworkWindows network+ Just (network, Just channel) -> clearFocus (ChannelFocus (Text.pack network) (mkId (Text.pack channel))) where clearNetworkWindows network@@ -1140,7 +1176,7 @@ -- sends to the current network. cmdQuote :: NetworkCommand String cmdQuote cs st rest =- case parseRawIrcMsg (Text.pack rest) of+ case parseRawIrcMsg (Text.pack (dropWhile (' '==) rest)) of Nothing -> commandFailureMsg "failed to parse raw IRC command" st Just raw -> do sendMsg cs raw@@ -1163,8 +1199,8 @@ $! recordChannelMessage network channelId entry st -- | Implementation of @/ctcp@-cmdCtcp :: NetworkCommand (String, (String, String))-cmdCtcp cs st (target, (cmd, args)) =+cmdCtcp :: NetworkCommand (String, String, String)+cmdCtcp cs st (target, cmd, args) = do let cmdTxt = Text.toUpper (Text.pack cmd) argTxt = Text.pack args tgtTxt = Text.pack target@@ -1237,15 +1273,15 @@ entries -cmdConnect :: ClientCommand (String, ())-cmdConnect st (networkStr, _) =+cmdConnect :: ClientCommand String+cmdConnect st networkStr = do -- abort any existing connection before connecting let network = Text.pack networkStr st' <- addConnection 0 Nothing network =<< abortNetwork network st commandSuccess $ changeFocus (NetworkFocus network) st' -cmdFocus :: ClientCommand (String, Maybe (String, ()))+cmdFocus :: ClientCommand (String, Maybe String) cmdFocus st (network, mbChannel) | network == "*" = commandSuccess (changeFocus Unfocused st) | otherwise =@@ -1253,7 +1289,7 @@ Nothing -> let focus = NetworkFocus (Text.pack network) in commandSuccess (changeFocus focus st)- Just (channel,_) ->+ Just channel -> let focus = ChannelFocus (Text.pack network) (mkId (Text.pack channel)) in commandSuccess $ changeFocus focus st@@ -1267,14 +1303,14 @@ -- | Implementation of @/windows@ command. Set subfocus to Windows.-cmdWindows :: ClientCommand (Maybe (String, ()))+cmdWindows :: ClientCommand (Maybe String) cmdWindows st arg = case arg of- Nothing -> success AllWindows- Just ("networks",_) -> success NetworkWindows- Just ("channels",_) -> success ChannelWindows- Just ("users" ,_) -> success UserWindows- _ -> commandFailureMsg errmsg st+ Nothing -> success AllWindows+ Just "networks" -> success NetworkWindows+ Just "channels" -> success ChannelWindows+ Just "users" -> success UserWindows+ _ -> commandFailureMsg errmsg st where errmsg = "/windows expected networks, channels, or users" success x =@@ -1307,10 +1343,10 @@ $ changeSubfocus FocusRtsStats st -- | Implementation of @/help@ command. Set subfocus to Help.-cmdHelp :: ClientCommand (Maybe (String, ()))+cmdHelp :: ClientCommand (Maybe String) cmdHelp st mb = commandSuccess (changeSubfocus focus st) where- focus = FocusHelp (fmap (Text.pack . fst) mb)+ focus = FocusHelp (fmap Text.pack mb) -- | Tab completion for @/splits[+]@. When given no arguments this -- populates the current list of splits, otherwise it tab completes@@ -1364,19 +1400,26 @@ Just args -> k args where mb = traverse- (parseSplitFocus (views clientFocus focusNetwork st))+ (parseFocus (views clientFocus focusNetwork st)) (words str) --- | Parses a single entry in the format used by @/splits[+-]@ to specify--- windows.-parseSplitFocus :: Maybe Text -> String -> Maybe Focus-parseSplitFocus mbNet x =+-- | Parses a single focus name given a default network.+--+-- The default is parameterized over an arbitrary 'Applicative'+-- instance so that if you know the network you can use 'Identity'+-- and if you might not, you can use 'Maybe'+parseFocus ::+ Applicative f =>+ f Text {- ^ default network -} ->+ String {- ^ @[network:]target@ -} ->+ f Focus+parseFocus mbNet x = case break (==':') x of- ("*","") -> Just Unfocused- (net,_:"") -> Just (NetworkFocus (Text.pack net))- (net,_:chan) -> Just (ChannelFocus (Text.pack net) (mkId (Text.pack chan)))- (chan,"") -> do net <- mbNet- Just (ChannelFocus net (mkId (Text.pack chan)))+ ("*","") -> pure Unfocused+ (net,_:"") -> pure (NetworkFocus (Text.pack net))+ (net,_:chan) -> pure (ChannelFocus (Text.pack net) (mkId (Text.pack chan)))+ (chan,"") -> mbNet <&> \net ->+ ChannelFocus net (mkId (Text.pack chan)) -- | Render a entry from splits back to the textual format. renderSplitFocus :: Focus -> Text@@ -1492,35 +1535,35 @@ do sendMsg cs (ircStats (Text.pack <$> words rest)) commandSuccess st -cmdLusers :: NetworkCommand (Maybe (String, Maybe (String, ())))+cmdLusers :: NetworkCommand (Maybe (String, Maybe String)) cmdLusers cs st arg = do sendMsg cs $ ircLusers $ fmap Text.pack $ case arg of- Nothing -> []- Just (x, Nothing) -> [x]- Just (x, (Just (y,_))) -> [x,y]+ Nothing -> []+ Just (x, Nothing) -> [x]+ Just (x, Just y) -> [x,y] commandSuccess st -cmdMotd :: NetworkCommand (Maybe (String, ()))+cmdMotd :: NetworkCommand (Maybe String) cmdMotd cs st mbservername = do sendMsg cs $ ircMotd $ case mbservername of- Just (s,_) -> Text.pack s- Nothing -> ""+ Just s -> Text.pack s+ Nothing -> "" commandSuccess st -cmdAdmin :: NetworkCommand (Maybe (String, ()))+cmdAdmin :: NetworkCommand (Maybe String) cmdAdmin cs st mbservername = do sendMsg cs $ ircAdmin $ case mbservername of- Just (s,_) -> Text.pack s- Nothing -> ""+ Just s -> Text.pack s+ Nothing -> "" commandSuccess st -cmdRules :: NetworkCommand (Maybe (String, ()))+cmdRules :: NetworkCommand (Maybe String) cmdRules cs st mbservername = do sendMsg cs $ ircRules $ case mbservername of- Just (s,_) -> Text.pack s- Nothing -> ""+ Just s -> Text.pack s+ Nothing -> "" commandSuccess st cmdMap :: NetworkCommand ()@@ -1533,11 +1576,11 @@ do sendMsg cs ircInfo commandSuccess st -cmdVersion :: NetworkCommand (Maybe (String, ()))+cmdVersion :: NetworkCommand (Maybe String) cmdVersion cs st mbservername = do sendMsg cs $ ircVersion $ case mbservername of- Just (s,_) -> Text.pack s- Nothing -> ""+ Just s -> Text.pack s+ Nothing -> "" commandSuccess st cmdList :: NetworkCommand String@@ -1560,12 +1603,12 @@ do sendMsg cs (ircLinks (Text.pack <$> words rest)) commandSuccess st -cmdTime :: NetworkCommand (Maybe (String, ()))+cmdTime :: NetworkCommand (Maybe String) cmdTime cs st arg = do sendMsg cs $ ircTime $ case arg of- Nothing -> ""- Just (x,_) -> Text.pack x+ Nothing -> ""+ Just x -> Text.pack x commandSuccess st cmdZnc :: NetworkCommand String@@ -1573,7 +1616,7 @@ do sendMsg cs (ircZnc (Text.words (Text.pack rest))) commandSuccess st -cmdZncPlayback :: NetworkCommand (Maybe (String, Maybe (String, ())))+cmdZncPlayback :: NetworkCommand (Maybe (String, Maybe String)) cmdZncPlayback cs st args = case args of @@ -1582,7 +1625,7 @@ -- current date explicit time Just (timeStr, Nothing)- | Just tod <- parse timeFormats timeStr ->+ | Just tod <- parseFormats timeFormats timeStr -> do now <- getZonedTime let (nowTod,t) = (zonedTimeLocalTime . localTimeTimeOfDay <<.~ tod) now yesterday = over (zonedTimeLocalTime . localTimeDay) (addDays (-1))@@ -1592,9 +1635,9 @@ successZoned (fixDay t) -- explicit date and time- Just (dateStr, Just (timeStr, _))- | Just day <- parse dateFormats dateStr- , Just tod <- parse timeFormats timeStr ->+ Just (dateStr, Just timeStr)+ | Just day <- parseFormats dateFormats dateStr+ , Just tod <- parseFormats timeFormats timeStr -> do tz <- getCurrentTimeZone successZoned ZonedTime { zonedTimeZone = tz@@ -1608,7 +1651,7 @@ -- %k doesn't require a leading 0 for times before 10AM timeFormats = ["%k:%M:%S","%k:%M"] dateFormats = ["%F"]- parse formats str =+ parseFormats formats str = asum (map (parseTimeM False defaultTimeLocale ?? str) formats) successZoned = success . formatTime defaultTimeLocale "%s"@@ -1617,11 +1660,11 @@ do sendMsg cs (ircZnc ["*playback", "play", "*", Text.pack start]) commandSuccess st -cmdMode :: NetworkCommand String-cmdMode cs st rest = modeCommand (Text.pack <$> words rest) cs st+cmdMode :: NetworkCommand [String]+cmdMode cs st xs = modeCommand (Text.pack <$> xs) cs st -cmdNick :: NetworkCommand (String, ())-cmdNick cs st (nick,_) =+cmdNick :: NetworkCommand String+cmdNick cs st nick = do sendMsg cs (ircNick (Text.pack nick)) commandSuccess st @@ -1636,8 +1679,8 @@ cmdSay :: ChannelCommand String cmdSay _ _ st rest = executeChat rest st -cmdInvite :: ChannelCommand (String, ())-cmdInvite channelId cs st (nick,_) =+cmdInvite :: ChannelCommand String+cmdInvite channelId cs st nick = do let freeTarget = has (csChannels . ix channelId . chanModes . ix 'g') cs cmd = ircInvite (Text.pack nick) channelId cs' <- if freeTarget@@ -1675,8 +1718,8 @@ cmdChannelInfo :: ChannelCommand () cmdChannelInfo _ _ st _ = commandSuccess (changeSubfocus FocusInfo st) -cmdMasks :: ChannelCommand (String,())-cmdMasks channel cs st (rest,_) =+cmdMasks :: ChannelCommand String+cmdMasks channel cs st rest = case rest of [mode] | mode `elem` view (csModeTypes . modesLists) cs -> @@ -1728,30 +1771,35 @@ do sendMsg cs (ircKnock (Text.pack chan) (Text.pack message)) commandSuccess st -cmdJoin :: NetworkCommand (String, Maybe (String, ()))+cmdJoin :: NetworkCommand (String, Maybe String) cmdJoin cs st (channels, mbKeys) = do let network = view csNetwork cs let channelId = mkId (Text.pack (takeWhile (/=',') channels))- sendMsg cs (ircJoin (Text.pack channels) (Text.pack . fst <$> mbKeys))+ sendMsg cs (ircJoin (Text.pack channels) (Text.pack <$> mbKeys)) commandSuccess $ changeFocus (ChannelFocus network channelId) st -- | @/query@ command. Takes a channel or nickname and switches -- focus to that target on the current network.-cmdQuery :: NetworkCommand (String, ())-cmdQuery cs st (channel, _) =- commandSuccess- $ changeFocus (ChannelFocus (view csNetwork cs) (mkId (Text.pack channel))) st+cmdQuery :: ClientCommand String+cmdQuery st channel =+ case parseFocus (views clientFocus focusNetwork st) channel of+ Just focus -> commandSuccess (changeFocus focus st)+ Nothing -> commandFailureMsg "No current network" st -- | Tab completion for @/query@ tabQuery :: Bool {- ^ reversed order -} ->- NetworkCommand String-tabQuery isReversed cs st _ =+ ClientCommand String+tabQuery isReversed st _ = simpleTabCompletion plainWordCompleteMode [] completions isReversed st where- completions = channelWindowsOnNetwork (view csNetwork cs) st+ completions = currentNet <> allWindows+ allWindows = renderSplitFocus <$> views clientWindows Map.keys st+ currentNet = case views clientFocus focusNetwork st of+ Just net -> idText <$> channelWindowsOnNetwork net st+ Nothing -> [] -- | Return the list of identifiers for open channel windows on -- the given network name.@@ -1803,15 +1851,16 @@ -- | Implementation of @/reload@ -- -- Attempt to reload the configuration file-cmdReload :: ClientCommand (Maybe (String, ()))+cmdReload :: ClientCommand (Maybe String) cmdReload st mbPath =- do let path = fst <$> mbPath- <|> view (clientConfig . configConfigPath) st+ do let path = mbPath <|> view clientConfigPath st res <- loadConfiguration path case res of- Left e -> commandFailureMsg (describeProblem e) st+ Left e -> commandFailureMsg (describeProblem e) st Right cfg ->- do st1 <- clientStartExtensions (set clientConfig cfg st)+ do st1 <- clientStartExtensions+ $ set clientConfig cfg+ $ set clientConfigPath mbPath st commandSuccess st1 where@@ -1829,6 +1878,45 @@ tabReload :: Bool {- ^ reversed -} -> ClientCommand String tabReload _ st _ = commandFailure st ++modeParamArgs :: ClientState -> String -> Maybe (Args ClientState [String])+modeParamArgs st str =+ case view clientFocus st of+ Unfocused -> Nothing+ NetworkFocus _ -> Just (pure [str])+ ChannelFocus net _ ->++ -- determine current mode types+ do cs <- preview (clientConnection net) st+ let types = view csModeTypes cs++ -- parse the list of modes being set+ flags <- splitModes types (Text.pack str) []++ -- generate the argument specification+ let (req,opt) = foldr (countFlags types) ([],[]) flags+ return ((str:) <$> tokenList req (map (++"?") opt))+++-- | This function computes the list of required and optional parameters+-- corresponding to the flags that have been entered.+countFlags ::+ ModeTypes {- ^ network's mode behaviors -} ->+ (Bool, Char, Text) {- ^ polarity mode-letter unused-parameter -} ->+ ([String],[String]) {- ^ required-names optional-names -} ->+ ([String],[String]) {- ^ required-names optional-names -}+countFlags types (pol, flag, _)+ | flag `elem` view modesLists types = addOpt+ | pol && flag `elem` view modesSetArg types = addReq+ | flag `elem` view modesAlwaysArg types = addReq+ | elemOf (modesPrefixModes . folded . _1) flag types = addReq+ | otherwise = id+ where+ addReq (req,opt) = ((flag:" param"):req,opt)+ addOpt ([] ,opt) = ([], (flag:" param"):opt)+ addOpt (req,opt) = ((flag:" param"):req,opt)++ modeCommand :: [Text] {- mode parameters -} -> NetworkState ->@@ -2062,17 +2150,11 @@ } ste -cmdUrl :: ClientCommand (Maybe (String, ()))-cmdUrl st mbArg =+cmdUrl :: ClientCommand (Maybe Int)+cmdUrl st arg = case view (clientConfig . configUrlOpener) st of- Nothing -> commandFailureMsg "url-opener not configured" st- Just opener ->- case mbArg of- Nothing -> doUrlOpen opener 0- Just (arg,_) ->- case readMaybe arg of- Just n | n > 0 -> doUrlOpen opener (n-1)- _ -> commandFailureMsg "bad url number" st+ Nothing -> commandFailureMsg "url-opener not configured" st+ Just opener -> doUrlOpen opener (maybe 0 (subtract 1) arg) where focus = view clientFocus st @@ -2102,7 +2184,7 @@ Left e -> commandFailureMsg (Text.pack e) st Right r -> commandSuccess (set clientRegex (Just r) st) -cmdOper :: NetworkCommand (String, (String, ()))-cmdOper cs st (user, (pass, ())) =+cmdOper :: NetworkCommand (String, String)+cmdOper cs st (user, pass) = do sendMsg cs (ircOper (Text.pack user) (Text.pack pass)) commandSuccess st
− src/Client/Commands/Arguments.hs
@@ -1,75 +0,0 @@-{-# Language GADTs, KindSignatures #-}--{-|-Module : Client.Commands.Arguments-Description : Command argument description and parsing-Copyright : (c) Eric Mertens, 2016-License : ISC-Maintainer : emertens@gmail.com--This module provides a description for the arguments expected-by command commands as well as a way to parse those arguments.--}--module Client.Commands.Arguments- ( ArgumentSpec(..)- , parseArguments- ) where--import Control.Monad---- | Description of a command's arguments indexed by the result of parsing--- those arguments. Arguments are annotated with a 'String' describing the--- argument.-data ArgumentSpec :: * -> * where-- -- | A required space-delimited token- ReqTokenArg :: String -> ArgumentSpec rest -> ArgumentSpec (String, rest)-- -- | An optional space-delimited token- OptTokenArg :: String -> ArgumentSpec rest -> ArgumentSpec (Maybe (String, rest))-- -- | Take all the remaining text in free-form- RemainingArg :: String -> ArgumentSpec String-- -- | No arguments- NoArg :: ArgumentSpec ()--instance Show (ArgumentSpec s) where- showsPrec p spec =- case spec of- ReqTokenArg s rest -> showParen (p >= 11)- $ showString "ReqTokenArg "- . showsPrec 11 s . showChar ' ' . showsPrec 11 rest- OptTokenArg s rest -> showParen (p >= 11)- $ showString "OptTokenArg "- . showsPrec 11 s . showChar ' ' . showsPrec 11 rest- RemainingArg s -> showParen (p >= 11)- $ showString "RemainingArg " . showsPrec 11 s- NoArg -> showString "NoArg"---- | Parse the given input string using an argument specification.--- The arguments should start with a space but might have more.-parseArguments ::- ArgumentSpec a {- ^ specification -} ->- String {- ^ input string -} ->- Maybe a {- ^ parse results -}-parseArguments arg xs =- case arg of- NoArg -> guard (all (==' ') xs)- RemainingArg _ -> Just (drop 1 xs) -- drop the leading space- OptTokenArg _ rest ->- do let (tok, xs') = nextToken xs- if null tok- then Just Nothing- else do rest' <- parseArguments rest xs'- return (Just (tok, rest'))- ReqTokenArg _ rest ->- do let (tok, xs') = nextToken xs- guard (not (null tok))- rest' <- parseArguments rest xs'- return (tok, rest')---- | Return the next space delimited token. Leading space is dropped.-nextToken :: String -> (String, String)-nextToken = break (==' ') . dropWhile (==' ')
+ src/Client/Commands/Arguments/Parser.hs view
@@ -0,0 +1,65 @@+{-# Language GADTs #-}++{-|+Module : Client.Commands.Arguments.Parser+Description : Interpret argument specifications as a parser+Copyright : (c) Eric Mertens, 2017+License : ISC+Maintainer : emertens@gmail.com++-}++module Client.Commands.Arguments.Parser (parse) where++import Client.Commands.Arguments.Spec+import Control.Applicative+import Control.Monad+import Control.Monad.Trans.State+import Control.Monad.Trans.Class+import Control.Applicative.Free++------------------------------------------------------------------------+-- Parser++parse :: r -> Args r a -> String -> Maybe a+parse env spec str =+ do (a,rest) <- runStateT (parseArgs env spec) str+ guard (all (' '==) rest)+ return a++type Parser = StateT String Maybe++parseArgs :: r -> Args r a -> Parser a+parseArgs env spec = runAp (parseArg env) spec++parseArg :: r -> Arg r a -> Parser a+parseArg env spec =+ case spec of+ Argument shape _ f ->+ do t <- argumentString shape+ lift (f env t)+ Optional subspec -> optional (parseArgs env subspec)+ Extension _ parseFormat ->+ do t <- token+ subspec <- lift (parseFormat env t)+ parseArgs env subspec++argumentString :: ArgumentShape -> Parser String+argumentString TokenArgument = token+argumentString RemainingArgument = remaining++remaining :: Parser String+remaining =+ do xs <- get+ put ""+ return $! case xs of+ ' ':xs' -> xs'+ _ -> xs++token :: Parser String+token =+ do xs <- get+ let (t, xs') = break (' '==) (dropWhile (' '==) xs)+ guard (not (null t))+ put (drop 1 xs')+ return t
+ src/Client/Commands/Arguments/Renderer.hs view
@@ -0,0 +1,106 @@+{-# Language OverloadedStrings, GADTs #-}++{-|+Module : Client.Commands.Arguments.Renderer+Description : Interpretation of argument specification as a renderer+Copyright : (c) Eric Mertens, 2017+License : ISC+Maintainer : emertens@gmail.com++-}++module Client.Commands.Arguments.Renderer (render) where++import Client.Commands.Arguments.Spec+import Client.Image.MircFormatting+import Client.Image.PackedImage+import Client.Image.Palette+import Control.Applicative.Free+import Control.Lens+import Control.Monad.Trans.State+import Data.Functor.Compose+import Data.Semigroup ((<>))+import qualified Data.Text as Text+import Graphics.Vty (wcswidth)+import Graphics.Vty.Attributes++render ::+ Palette {- ^ palette -} ->+ r {- ^ environment -} ->+ Bool {- ^ render placeholders -} ->+ Args r a {- ^ specification -} ->+ String {- ^ user input -} ->+ Image'+render pal env placeholders spec str = extend (addExcess img)+ where+ (img, excess) = flip runState str . getState+ $ renderArgs pal env placeholders spec++ addExcess+ | any (' '/=) excess = (<> string defAttr excess)+ | otherwise = id++ extend i+ | imageWidth i < minLen = resizeImage minLen i+ | otherwise = i+ where minLen = wcswidth str++++renderArgs :: Palette -> r -> Bool -> Args r a -> Renderer a+renderArgs pal r placeholders = runAp (renderArg pal r placeholders)++------------------------------------------------------------------------++type Renderer = Compose (State String) (Const Image')++getState :: Renderer a -> State String Image'+getState = fmap getConst . getCompose++putState :: State String Image' -> Renderer a+putState = Compose . fmap Const++------------------------------------------------------------------------++renderArg :: Palette -> r -> Bool -> Arg r a -> Renderer b+renderArg pal r placeholders spec = putState $++ let placeholder name+ | placeholders = return (" " <> string (view palCommandPlaceholder pal) name)+ | otherwise = return mempty++ draw = parseIrcText' True . Text.pack+ in++ case spec of+ Optional subspec -> getState (renderArgs pal r placeholders subspec)++ Extension name ext ->+ do (lead,tok) <- state token+ if null tok then+ placeholder name+ else do+ rest <- case ext r tok of+ Nothing -> return mempty+ Just subspec -> getState (renderArgs pal r placeholders subspec)+ return (draw (lead++tok) <> rest)++ Argument TokenArgument name _ ->+ do (lead,tok) <- state token+ if null tok then+ placeholder name+ else+ return (draw (lead++tok))++ Argument RemainingArgument name _ ->+ do rest <- state (\x -> (x,""))+ if all (' '==) rest then+ placeholder name+ else+ return (draw rest)++token :: String -> ((String, String), String)+token xs =+ let (lead, xs1) = span (' '==) xs+ (tok , xs2) = break (' '==) xs1+ in ((lead, tok), xs2)
+ src/Client/Commands/Arguments/Spec.hs view
@@ -0,0 +1,64 @@+{-# Language KindSignatures, GADTs #-}++{-|+Module : Client.Commands.Arguments.Spec+Description : Argument specifications used within client commands+Copyright : (c) Eric Mertens, 2017+License : ISC+Maintainer : emertens@gmail.com++-}++module Client.Commands.Arguments.Spec+ ( Args+ , simpleToken+ , remainingArg+ , optionalArg+ , tokenList+ , numberArg+ , extensionArg++ , ArgumentShape(..)+ , Arg(..)+ ) where++import Control.Applicative+import Control.Applicative.Free+import Data.Maybe (fromMaybe)+import Text.Read (readMaybe)++type Args r = Ap (Arg r)++data ArgumentShape = TokenArgument | RemainingArgument++data Arg :: * -> * -> * where+ Argument :: ArgumentShape -> String -> (r -> String -> Maybe a) -> Arg r a+ Optional :: Args r a -> Arg r (Maybe a)+ Extension :: String -> (r -> String -> Maybe (Args r a)) -> Arg r a++tokenArg :: String -> (r -> String -> Maybe a) -> Args r a+tokenArg name parser = liftAp (Argument TokenArgument name parser)++remainingArg :: String -> Args r String+remainingArg name = liftAp (Argument RemainingArgument name (\_ -> Just))++optionalArg :: Args r a -> Args r (Maybe a)+optionalArg = liftAp . Optional++extensionArg :: String -> (r -> String -> Maybe (Args r a)) -> Args r a+extensionArg name parser = liftAp (Extension name parser)++simpleToken :: String -> Args r String+simpleToken name = tokenArg name (\_ -> Just)++numberArg :: Args r Int+numberArg = tokenArg "number" (\_ -> readMaybe)++tokenList ::+ [String] {- ^ required names -} ->+ [String] {- ^ optional names -} ->+ Args r [String]+tokenList req opt = foldr addReq (foldr addOpt (pure []) opt) req+ where+ addReq name = liftA2 (:) (simpleToken name)+ addOpt name rest = fromMaybe [] <$> optionalArg (addReq name rest)
src/Client/Commands/Interpolation.hs view
@@ -1,4 +1,4 @@-{-# Language OverloadedStrings, GADTs #-}+{-# Language OverloadedStrings, GADTs, RankNTypes #-} {-| Module : Client.Commands.Interpolation@@ -24,10 +24,11 @@ import Control.Applicative import Data.Attoparsec.Text as P import Data.Char+import Data.Maybe import qualified Data.Text as Text import Data.Text (Text) -import Client.Commands.Arguments+import Client.Commands.Arguments.Spec -- | Parsed chunk of an expandable command data ExpansionChunk@@ -48,16 +49,14 @@ } deriving Show data MacroSpec where- MacroSpec :: ArgumentSpec s -> MacroSpec+ MacroSpec :: (forall r. Args r [String]) -> MacroSpec instance Show MacroSpec where- showsPrec p (MacroSpec as)- = showParen (p >= 11)- $ showString "MacroSpec " . showsPrec 11 as+ show MacroSpec{} = "MacroSpec" -- | Specification used when unspecified, no arguments. noMacroArguments :: MacroSpec-noMacroArguments = MacroSpec NoArg+noMacroArguments = MacroSpec (pure []) parseMacroSpecs :: Text -> Maybe MacroSpec parseMacroSpecs txt =@@ -71,10 +70,12 @@ <*> optional (char '?') <* P.skipSpace <*> macroSpecs- <|> pure (MacroSpec NoArg)- where- cons desc (Just _) (MacroSpec rest) = MacroSpec (OptTokenArg (Text.unpack desc) rest)- cons desc Nothing (MacroSpec rest) = MacroSpec (ReqTokenArg (Text.unpack desc) rest)+ <|> pure (MacroSpec (pure []))+ where+ add1 desc = liftA2 (:) (simpleToken (Text.unpack desc))++ cons desc (Just _) (MacroSpec rest) = MacroSpec (fromMaybe [] <$> optionalArg (add1 desc rest))+ cons desc Nothing (MacroSpec rest) = MacroSpec (add1 desc rest) -- | Parse a 'Text' searching for the expansions as specified in -- 'ExpansionChunk'. @$$@ is used to escape a single @$@.
src/Client/Configuration.hs view
@@ -28,7 +28,6 @@ , configPalette , configWindowNames , configNickPadding- , configConfigPath , configMacros , configExtensions , configExtraHighlights@@ -45,7 +44,6 @@ , loadConfiguration -- * Resolving paths- , resolveConfigurationPath , getNewConfigPath -- * Specification@@ -92,8 +90,6 @@ , _configWindowNames :: Text -- ^ Names of windows, used when alt-jumping) , _configExtraHighlights :: HashSet Identifier -- ^ Extra highlight nicks/terms , _configNickPadding :: PaddingMode -- ^ Padding of nicks in messages- , _configConfigPath :: Maybe FilePath- -- ^ manually specified configuration path, used for reloading , _configMacros :: Recognizer Macro -- ^ command macros , _configExtensions :: [FilePath] -- ^ paths to shared library , _configUrlOpener :: Maybe FilePath -- ^ paths to url opening executable@@ -214,7 +210,7 @@ $ Text.unpack $ Text.unlines $ map explainLoadError (toList es)- Right cfg -> return (cfg mbPath def)+ Right cfg -> resolvePaths path (cfg def) explainLoadError :: LoadError Position -> Text@@ -237,7 +233,21 @@ SpecMismatch t -> "expected " <> t -configurationSpec :: ValueSpecs (Maybe FilePath -> ServerSettings -> Configuration)+-- | Resolve all the potentially relative file paths in the configuration file+resolvePaths :: FilePath -> Configuration -> IO Configuration+resolvePaths file cfg =+ do res <- resolveFilePath <$> newFilePathContext file+ let resolveServerFilePaths = over (ssTlsClientCert . mapped) res+ . over (ssTlsClientKey . mapped) res+ . over (ssTlsServerCert . mapped) res+ . over (ssSaslEcdsaFile . mapped) res+ . over (ssLogDir . mapped) res+ return $! over (configExtensions . mapped) res+ . over (configServers . mapped) resolveServerFilePaths+ $ cfg++configurationSpec ::+ ValueSpecs (ServerSettings -> Configuration) configurationSpec = sectionsSpec "" $ do let sec' def name spec info = fromMaybe def <$> optSection' name spec info@@ -276,7 +286,7 @@ "Extra key bindings" _configLayout <- sec' OneColumn "layout" layoutSpec "Initial setting for window layout"- return (\_configConfigPath def ->+ return (\def -> let _configDefaults = ssDefUpdate def _configServers = buildServerMap _configDefaults ssUpdates _configKeyMap = foldl (\acc f -> f acc) initialKeyMap bindings@@ -330,24 +340,24 @@ actBindingSpec :: ValueSpecs (KeyMap -> KeyMap) actBindingSpec = sectionsSpec "action-binding" $- do (m,k) <- reqSection' "bind" keySpec- "Key to be bound (e.g. a, C-b, M-c C-M-d)"- a <- reqSection "action"- "Action name (see `/keymap`)"+ do ~(m,k) <- reqSection' "bind" keySpec+ "Key to be bound (e.g. a, C-b, M-c C-M-d)"+ a <- reqSection "action"+ "Action name (see `/keymap`)" return (addKeyBinding m k a) cmdBindingSpec :: ValueSpecs (KeyMap -> KeyMap) cmdBindingSpec = sectionsSpec "command-binding" $- do (m,k) <- reqSection' "bind" keySpec- "Key to be bound (e.g. a, C-b, M-c C-M-d)"- cmd <- reqSection "command"- "Client command to execute (exclude leading `/`)"+ do ~(m,k) <- reqSection' "bind" keySpec+ "Key to be bound (e.g. a, C-b, M-c C-M-d)"+ cmd <- reqSection "command"+ "Client command to execute (exclude leading `/`)" return (addKeyBinding m k (ActCommand cmd)) unbindingSpec :: ValueSpecs (KeyMap -> KeyMap) unbindingSpec = sectionsSpec "remove-binding" $- do (m,k) <- reqSection' "unbind" keySpec- "Key to be unbound (e.g. a, C-b, M-c C-M-d)"+ do ~(m,k) <- reqSection' "unbind" keySpec+ "Key to be unbound (e.g. a, C-b, M-c C-M-d)" return (removeKeyBinding m k) @@ -373,7 +383,10 @@ : [ optSection' lbl (set l <$> attrSpec) "" | (lbl, Lens l) <- paletteMap ] -buildServerMap :: ServerSettings -> [ServerSettings -> ServerSettings] -> HashMap Text ServerSettings+buildServerMap ::+ ServerSettings {- ^ defaults -} ->+ [ServerSettings -> ServerSettings] ->+ HashMap Text ServerSettings buildServerMap def ups = HashMap.fromList [ (serverSettingName ss, ss) | up <- ups, let ss = up def ] where@@ -382,10 +395,15 @@ (view ssName ss) --- | Resolve relative paths starting at the home directory rather than--- the current directory of the client.-resolveConfigurationPath :: FilePath -> IO FilePath-resolveConfigurationPath path- | isAbsolute path = return path- | otherwise = do home <- getHomeDirectory- return (home </> path)+data FilePathContext = FilePathContext { fpBase, fpHome :: FilePath }++newFilePathContext ::+ FilePath {- ^ configuration file path -} ->+ IO FilePathContext+newFilePathContext base = FilePathContext (takeDirectory base) <$> getHomeDirectory++resolveFilePath :: FilePathContext -> FilePath -> FilePath+resolveFilePath fpc path+ | isAbsolute path = path+ | "~":rest <- splitDirectories path = joinPath (fpHome fpc : rest)+ | otherwise = fpBase fpc </> path
src/Client/Configuration/ServerSettings.hs view
@@ -154,16 +154,20 @@ serverSpec = sectionsSpec "server-settings" $ composeMaybe <$> sequenceA settings where+ composeMaybe :: [Maybe (a -> a)] -> a -> a composeMaybe = ala Endo (foldMap . foldMap) - req name l s info = optSection' name ?? info- $ set l <$> s+ req name l s info+ = optSection' name ?? info+ $ set l <$> s - opt name l s info = optSection' name ?? info- $ set l . Just <$> s- <!> set l Nothing <$ atomSpec "clear"+ opt name l s info+ = optSection' name ?? info+ $ set l . Just <$> s <!>+ set l Nothing <$ atomSpec "clear" + settings :: [SectionSpecs (Maybe (ServerSettings -> ServerSettings))] settings = [ opt "name" ssName valuesSpec "The name used to identify this server in the client"
src/Client/EventLoop/Actions.hs view
@@ -118,7 +118,7 @@ HashMap.fromList - [("delete" , (ActDelete , [meta (KChar 'd'), norm KDel]))+ [("delete" , (ActDelete , [ctrl (KChar 'd'), norm KDel])) ,("backspace" , (ActBackspace , [norm KBS])) ,("home" , (ActHome , [norm KHome, ctrl (KChar 'a')])) ,("end" , (ActEnd , [norm KEnd , ctrl (KChar 'e')]))@@ -244,6 +244,7 @@ liftMaybe mb = Compose ((,)[] <$> mb) go str = case str of+ "Space" -> pure (KChar ' ') "Tab" -> pure (KChar '\t') "BackTab" -> pure KBackTab "Enter" -> pure KEnter@@ -276,6 +277,7 @@ prettyModifier MAlt = showString "A-" prettyKey :: Key -> String+prettyKey (KChar ' ') = "Space" prettyKey (KChar '\t') = "Tab" prettyKey (KChar c) = showLitChar c "" -- escapes anything non-ascii prettyKey (KFun n) = 'F' : show n
− src/Client/Image/Arguments.hs
@@ -1,66 +0,0 @@-{-# Language GADTs #-}--{-|-Module : Client.Image.Arguments-Description : Rendering logic for commanad arguments-Copyright : (c) Eric Mertens, 2016-License : ISC-Maintainer : emertens@gmail.com--This module provides image rendering for the textbox in the-context of command argument processing.---}--module Client.Image.Arguments- ( argumentsImage- ) where--import Client.Commands.Arguments-import Client.Image.MircFormatting-import Client.Image.PackedImage-import Client.Image.Palette-import Control.Lens-import Data.Semigroup-import qualified Data.Text as Text-import Graphics.Vty.Attributes---- | Parse command arguments against a given 'ArgumentSpec'.--- The given text will be rendered and then any missing arguments--- will be indicated by extra placeholder values appended onto the--- image. Parameters are rendered with 'plainText' except for--- the case of 'RemainingArg' which supports WYSIWYG.-argumentsImage :: Palette -> ArgumentSpec a -> String -> Image'-argumentsImage pal spec xs- | all (==' ') xs = placeholders- <> string defAttr (drop (imageWidth placeholders) xs)- | otherwise =- case spec of- NoArg -> plainText xs- ReqTokenArg _ a -> plainText token <> argumentsImage pal a xs'- OptTokenArg _ a -> plainText token <> argumentsImage pal a xs'- RemainingArg _ -> parseIrcTextExplicit (Text.pack xs)-- where- token = token1 ++ token2- (token1,(token2,xs')) =- break (==' ') <$> span (==' ') xs-- placeholders = mkPlaceholders pal spec---- | Construct an 'Image' containing placeholders for each--- of the remaining arguments.-mkPlaceholders :: Palette -> ArgumentSpec a -> Image'-mkPlaceholders pal arg =- case arg of- NoArg -> mempty- ReqTokenArg n a -> leader- <> string (view palCommandPlaceholder pal) n- <> mkPlaceholders pal a- OptTokenArg n a -> leader- <> string (view palCommandPlaceholder pal) (n ++ "?")- <> mkPlaceholders pal a- RemainingArg n -> leader- <> string (view palCommandPlaceholder pal) (n ++ "…")- where- leader = char defAttr ' '
src/Client/Image/Message.hs view
@@ -24,13 +24,17 @@ , cleanChar , nickPad , timeImage+ , drawWindowLine+ , parseIrcTextWithNicks ) where import Client.Configuration (PaddingMode(..))+import Client.Image.LineWrap import Client.Image.MircFormatting import Client.Image.PackedImage import Client.Image.Palette import Client.Message+import Client.State.Window import Control.Lens import Data.Char import Data.Hashable (hash)@@ -233,9 +237,8 @@ string (withForeColor defAttr blue) "* " <> who src Ctcp {} -> mempty - CtcpNotice src _dst "ACTION" _txt ->- string (withForeColor defAttr red) "* " <> who src- CtcpNotice {} -> mempty+ CtcpNotice src _dst _cmd _txt ->+ string (withForeColor defAttr red) "! " <> who src Error {} -> string (view palError pal) "ERROR" <> ":" @@ -278,17 +281,17 @@ Authenticate{} -> "***" Error txt -> parseIrcText txt- Topic _ _ txt -> parseIrcTextWithNicks pal myNicks nicks txt- Kick _ _ _ txt -> parseIrcTextWithNicks pal myNicks nicks txt- Notice _ _ txt -> parseIrcTextWithNicks pal myNicks nicks txt- Privmsg _ _ txt -> parseIrcTextWithNicks pal myNicks nicks txt- Ctcp _ _ "ACTION" txt -> parseIrcTextWithNicks pal myNicks nicks txt+ Topic _ _ txt -> parseIrcTextWithNicks pal myNicks nicks False txt+ Kick _ _ _ txt -> parseIrcTextWithNicks pal myNicks nicks False txt+ Notice _ _ txt -> parseIrcTextWithNicks pal myNicks nicks False txt+ Privmsg _ _ txt -> parseIrcTextWithNicks pal myNicks nicks False txt+ Ctcp _ _ "ACTION" txt -> parseIrcTextWithNicks pal myNicks nicks False txt Ctcp {} -> mempty- CtcpNotice _ _ "ACTION" txt -> parseIrcTextWithNicks pal myNicks nicks txt- CtcpNotice {} -> mempty+ CtcpNotice _ _ cmd txt -> parseIrcText cmd <> " " <>+ parseIrcTextWithNicks pal myNicks nicks False txt Reply code params -> renderReplyCode NormalRender code params- UnknownMsg irc ->+ UnknownMsg irc -> text' defAttr (view msgCommand irc) <> char defAttr ' ' <> separatedParams (view msgParams irc)@@ -354,40 +357,32 @@ string quietAttr "note " <> who src <> string (withForeColor defAttr red) ": " <>- parseIrcTextWithNicks pal myNicks nicks txt+ parseIrcTextWithNicks pal myNicks nicks False txt Privmsg src _dst txt -> string quietAttr "chat " <> who src <> ": " <>- parseIrcTextWithNicks pal myNicks nicks txt+ parseIrcTextWithNicks pal myNicks nicks False txt Ctcp src _dst "ACTION" txt -> string quietAttr "actp " <> string (withForeColor defAttr blue) "* " <> who src <> " " <>- parseIrcTextWithNicks pal myNicks nicks txt-- CtcpNotice src _dst "ACTION" txt ->- string quietAttr "actn " <>- string (withForeColor defAttr red) "* " <>- who src <> " " <>- parseIrcTextWithNicks pal myNicks nicks txt+ parseIrcTextWithNicks pal myNicks nicks False txt Ctcp src _dst cmd txt -> string quietAttr "ctcp " <> string (withForeColor defAttr blue) "! " <> who src <> " " <> parseIrcText cmd <>- separatorImage <>- parseIrcText txt+ if Text.null txt then mempty else separatorImage <> parseIrcText txt CtcpNotice src _dst cmd txt -> string quietAttr "ctcp " <> string (withForeColor defAttr red) "! " <> who src <> " " <> parseIrcText cmd <>- separatorImage <>- parseIrcText txt+ if Text.null txt then mempty else separatorImage <> parseIrcText txt Ping params -> "PING " <> separatedParams params@@ -578,13 +573,15 @@ -- the formatting codes. Otherwise the nicknames in the message are -- highlighted. parseIrcTextWithNicks ::- Palette ->- HashSet Identifier {- ^ my nicks -} ->- HashSet Identifier {- ^ other nicks -} ->- Text -> Image'-parseIrcTextWithNicks palette myNicks nicks txt- | Text.any isControl txt = parseIrcText txt- | otherwise = highlightNicks palette myNicks nicks txt+ Palette {- ^ palette -} ->+ HashSet Identifier {- ^ my nicks -} ->+ HashSet Identifier {- ^ other nicks -} ->+ Bool {- ^ explicit controls rendering -} ->+ Text {- ^ input text -} ->+ Image' {- ^ colored text -}+parseIrcTextWithNicks palette myNick nicks explicit txt+ | Text.any isControl txt = parseIrcText' explicit txt+ | otherwise = highlightNicks palette myNick nicks txt -- | Given a list of nicknames and a chat message, this will generate -- an image where all of the occurrences of those nicknames are colored.@@ -618,3 +615,19 @@ -- | Image used when treating ignored chat messages as metadata ignoreImage :: Image' ignoreImage = char (withForeColor defAttr yellow) 'I'+++-- | Render the normal view of a chat message line padded and wrapped.+drawWindowLine ::+ Palette {- ^ palette -} ->+ Int {- ^ draw columns -} ->+ PaddingMode {- ^ nick padding -} ->+ WindowLine {- ^ window line -} ->+ [Image'] {- ^ wrapped lines -}+drawWindowLine palette w padAmt wl = wrap (drawPrefix wl) (view wlImage wl)+ where+ drawTime = timeImage palette . unpackTimeOfDay+ padNick = nickPad padAmt+ wrap pfx body = reverse (lineWrapPrefix w pfx body)+ drawPrefix = views wlTimestamp drawTime <>+ views wlPrefix padNick
src/Client/Image/MircFormatting.hs view
@@ -12,9 +12,10 @@ -} module Client.Image.MircFormatting ( parseIrcText- , parseIrcTextExplicit+ , parseIrcText' , plainText , controlImage+ , mircColor ) where import Client.Image.PackedImage as I@@ -28,32 +29,11 @@ import Data.Text (Text) import Graphics.Vty.Attributes -data FormatState = FormatState- { _fmtFore :: Maybe Color- , _fmtBack :: Maybe Color- , _fmtBold, _fmtItalic, _fmtUnderline, _fmtReverse :: !Bool- }--makeLenses '' FormatState--formatAttr :: FormatState -> Attr-formatAttr fmt- = doStyle (view fmtBold fmt) bold- $ doStyle (view fmtUnderline fmt) underline- $ doStyle (view fmtReverse fmt) reverseVideo- $ doColor withForeColor (view fmtFore fmt)- $ doColor withBackColor (view fmtBack fmt)- $ defAttr-- where- doStyle False _ attr = attr- doStyle True style attr = withStyle attr style-- doColor _ Nothing attr = attr- doColor with (Just x) attr = with attr x--defaultFormatState :: FormatState-defaultFormatState = FormatState Nothing Nothing False False False False+makeLensesFor+ [ ("attrForeColor", "foreColorLens")+ , ("attrBackColor", "backColorLens")+ , ("attrStyle" , "styleLens" )]+ ''Attr -- | Parse mIRC encoded format characters and hide the control characters. parseIrcText :: Text -> Image'@@ -62,12 +42,9 @@ -- | Parse mIRC encoded format characters and render the control characters -- explicitly. This view is useful when inputting control characters to make -- it clear where they are in the text.-parseIrcTextExplicit :: Text -> Image'-parseIrcTextExplicit = parseIrcText' True- parseIrcText' :: Bool -> Text -> Image' parseIrcText' explicit = either plainText id- . parseOnly (pIrcLine explicit defaultFormatState)+ . parseOnly (pIrcLine explicit defAttr) data Segment = TextSegment Text | ControlSegment Char @@ -75,14 +52,14 @@ pSegment = TextSegment <$> takeWhile1 (not . isControl) <|> ControlSegment <$> satisfy isControl -pIrcLine :: Bool -> FormatState -> Parser Image'+pIrcLine :: Bool -> Attr -> Parser Image' pIrcLine explicit fmt = do seg <- option Nothing (Just <$> pSegment) case seg of Nothing -> return mempty Just (TextSegment txt) -> do rest <- pIrcLine explicit fmt- return (text' (formatAttr fmt) txt <> rest)+ return (text' fmt txt <> rest) Just (ControlSegment '\^C') -> do (numberText, colorNumbers) <- match pColorNumbers rest <- pIrcLine explicit (applyColors colorNumbers fmt)@@ -99,13 +76,17 @@ | otherwise -> next where mbFmt' = applyControlEffect c fmt- next = pIrcLine explicit (fromMaybe fmt mbFmt')+ next = pIrcLine explicit (fromMaybe fmt mbFmt') -pColorNumbers :: Parser (Maybe Int, Maybe Int)-pColorNumbers = option (Nothing,Nothing) $- do n <- pNumber- m <- optional (Parse.char ',' *> pNumber)- return (Just n,m)+pColorNumbers :: Parser (Maybe (Color, Maybe Color))+pColorNumbers = option Nothing $+ do n <- pNumber+ Just fc <- pure (mircColor n)+ bc <- optional $+ do m <- Parse.char ',' *> pNumber+ Just bc <- pure (mircColor m)+ pure bc+ return (Just (fc,bc)) where pNumber = do d1 <- digit@@ -115,11 +96,12 @@ optional :: Parser a -> Parser (Maybe a) optional p = option Nothing (Just <$> p) -applyColors :: (Maybe Int, Maybe Int) -> FormatState -> FormatState-applyColors (fore, back) = aux fmtFore fore . aux fmtBack back- where- aux _ Nothing = id- aux l (Just x) = set l (mircColor x)+applyColors :: (Maybe (Color, Maybe Color)) -> Attr -> Attr+applyColors Nothing = set foreColorLens Default+ . set backColorLens Default+applyColors (Just (c1, Nothing)) = set foreColorLens (SetTo c1) -- preserve background+applyColors (Just (c1, Just c2)) = set foreColorLens (SetTo c1)+ . set backColorLens (SetTo c2) mircColor :: Int -> Maybe Color mircColor 0 = Just (white ) -- white@@ -143,13 +125,19 @@ rgbColor' :: Int -> Int -> Int -> Color rgbColor' = rgbColor -- fix the type to Int -applyControlEffect :: Char -> FormatState -> Maybe FormatState-applyControlEffect '\^B' = Just . over fmtBold not-applyControlEffect '\^O' = Just . const defaultFormatState-applyControlEffect '\^V' = Just . over fmtReverse not-applyControlEffect '\^]' = Just . over fmtItalic not-applyControlEffect '\^_' = Just . over fmtUnderline not-applyControlEffect _ = const Nothing+applyControlEffect :: Char -> Attr -> Maybe Attr+applyControlEffect '\^B' attr = Just $! toggleStyle bold attr+applyControlEffect '\^V' attr = Just $! toggleStyle reverseVideo attr+applyControlEffect '\^_' attr = Just $! toggleStyle underline attr+applyControlEffect '\^O' _ = Just defAttr+applyControlEffect '\^]' attr = Just attr -- italic not supported+applyControlEffect _ _ = Nothing++toggleStyle :: Style -> Attr -> Attr+toggleStyle s1 = over styleLens $ \old ->+ case old of+ SetTo s2 -> SetTo (xor s1 s2)+ _ -> SetTo s1 -- | Safely render a control character. controlImage :: Char -> Image'
src/Client/Image/StatusLine.hs view
@@ -35,9 +35,10 @@ import Irc.Identifier (Identifier, idText) import Numeric -bar :: Char-bar = '━'+bar :: Image'+bar = char (withStyle defAttr bold) '─' + -- | Renders the status line between messages and the textbox. statusLineImage :: Int {- ^ draw width -} ->@@ -86,7 +87,7 @@ ClientState {- ^ client state -} -> Image' minorStatusLineImage focus w showHideMeta st =- content <> string defAttr (replicate fillSize bar)+ content <> mconcat (replicate fillSize bar) where content = focusImage focus st <> if showHideMeta then nometaImage focus st else mempty@@ -146,8 +147,7 @@ -- | Wrap some text in parentheses to make it suitable for inclusion in the -- status line. infoBubble :: Image' -> Image'-infoBubble img =- string defAttr (bar:"(") <> img <> ")"+infoBubble img = bar <> "(" <> img <> ")" -- | Indicate that the client is in the /detailed/ view.@@ -177,7 +177,8 @@ activitySummary :: ClientState -> Vty.Image activitySummary st | null indicators = Vty.emptyImage- | otherwise = Vty.string defAttr (bar:"[") Vty.<|>+ | otherwise = unpackImage bar Vty.<|>+ Vty.string defAttr "[" Vty.<|> Vty.horizCat indicators Vty.<|> Vty.string defAttr "]" where@@ -209,13 +210,14 @@ baraux i (focus,w) | n == 0 = Nothing -- todo: make configurable | otherwise = Just- $ Vty.string defAttr (bar:"[") Vty.<|>+ $ unpackImage bar Vty.<|>+ Vty.char defAttr '[' Vty.<|> Vty.char (view palWindowName pal) i Vty.<|> Vty.char defAttr ':' Vty.<|> Vty.text' (view palLabel pal) focusText Vty.<|> Vty.char defAttr ':' Vty.<|> Vty.string attr (show n) Vty.<|>- Vty.string defAttr "]"+ Vty.char defAttr ']' where n = view winUnread w pal = clientPalette st@@ -241,14 +243,16 @@ makeLines _ [] = Vty.emptyImage makeLines w (x:xs) = go x xs where- go acc (y:ys) | let acc' = acc Vty.<|> y , Vty.imageWidth acc' <= w = go acc' ys go acc ys = makeLines w ys- Vty.<-> acc Vty.<|> Vty.charFill defAttr bar (max 0 (w - Vty.imageWidth acc)) 1+ Vty.<-> Vty.cropRight w acc+ Vty.<|> unpackImage (mconcat (replicate fillsize bar))+ where+ fillsize = max 0 (w - Vty.imageWidth acc) myNickImage :: ClientState -> Vty.Image
src/Client/Image/Textbox.hs view
@@ -17,22 +17,27 @@ import Client.Configuration import Client.Commands-import Client.Commands.Arguments+import Client.Commands.Arguments.Renderer+import Client.Commands.Arguments.Parser import Client.Commands.Interpolation import Client.Commands.Recognizer-import Client.Image.Arguments+import Client.Image.Message import Client.Image.MircFormatting import Client.Image.PackedImage import Client.Image.Palette import Client.State+import Client.State.Focus import qualified Client.State.EditBox as Edit import Control.Lens import Data.Char+import qualified Data.HashSet as HashSet+import Data.HashSet (HashSet) import Data.List import Data.Semigroup import qualified Data.Text as Text import Graphics.Vty.Attributes import qualified Graphics.Vty.Image as Vty+import Irc.Identifier -- | Compute the UI image for the text input box. This computes -- the logical cursor position on the screen to compensate for@@ -43,7 +48,7 @@ where macros = views (clientConfig . configMacros) (fmap macroSpec) st (txt, content) =- views (clientTextBox . Edit.content) (renderContent macros pal) st+ views (clientTextBox . Edit.content) (renderContent st myNick nicks macros pal) st lineImage = unpackImage (beginning <> content <> ending) @@ -72,15 +77,28 @@ beginning = char attr '^' ending = char attr '$' ++ (myNick,nicks) =+ case view clientFocus st of+ ChannelFocus network channel ->+ (clientHighlightsNetwork network st,+ HashSet.fromList (channelUserList network channel st)+ )+ _ -> (HashSet.empty, HashSet.empty)++ -- | Renders the whole, uncropped text box as well as the 'String' -- corresponding to the rendered image which can be used for computing -- the logical cursor position of the cropped version of the text box. renderContent ::+ ClientState {- ^ client state -} ->+ HashSet Identifier {- ^ my nicknames -} ->+ HashSet Identifier {- ^ other nicknames -} -> Recognizer MacroSpec {- ^ macro completions -} -> Palette {- ^ palette -} -> Edit.Content {- ^ content -} -> (String, Image') {- ^ plain text rendering, image rendering -}-renderContent macros pal c = (txt, wholeImg)+renderContent st myNick nicks macros pal c = (txt, wholeImg) where as = reverse (view Edit.above c) bs = view Edit.below c@@ -92,11 +110,13 @@ -- ["one","two"] "three" --> "two one three" txt = foldl (\acc x -> x ++ ' ' : acc) leftCur as + rndr = renderLine st pal myNick nicks macros+ wholeImg = mconcat $ intersperse (plainText "\n")- $ map renderOtherLine as- ++ renderLine macros pal curTxt- : map renderOtherLine bs+ $ map (rndr False) as+ ++ rndr True curTxt+ : map (rndr False) bs -- | Version of 'wcwidth' that accounts for how control characters are@@ -112,18 +132,22 @@ myWcswidth = sum . map myWcwidth --- | Render an unfocused line-renderOtherLine :: String -> Image'-renderOtherLine = parseIrcTextExplicit . Text.pack- -- | Render the active text box line using command highlighting and -- placeholders, and WYSIWYG mIRC formatting control characters.-renderLine :: Recognizer MacroSpec -> Palette -> String -> Image'-renderLine macros pal ('/':xs) =+renderLine ::+ ClientState ->+ Palette ->+ HashSet Identifier ->+ HashSet Identifier ->+ Recognizer MacroSpec {- ^ commands -} ->+ Bool {- ^ focused -} ->+ String {- ^ input text -} ->+ Image' {- ^ output image -}+renderLine st pal myNick nicks macros focused ('/':xs) = char defAttr '/' <> string attr cmd <> continue rest where specAttr spec =- case parseArguments spec rest of+ case parse st spec rest of Nothing -> view palCommand pal Just{} -> view palCommandReady pal @@ -133,19 +157,19 @@ = case recognize (Text.pack cmd) allCommands of Exact (Right Command{cmdArgumentSpec = spec}) -> ( specAttr spec- , argumentsImage pal spec+ , render pal st focused spec ) Exact (Left (MacroSpec spec)) -> ( specAttr spec- , argumentsImage pal spec+ , render pal st focused spec ) Prefix _ -> ( view palCommandPrefix pal- , renderOtherLine+ , parseIrcTextWithNicks pal myNick nicks focused . Text.pack ) Invalid -> ( view palCommandError pal- , renderOtherLine+ , parseIrcTextWithNicks pal myNick nicks focused . Text.pack ) -renderLine _ _ xs = parseIrcTextExplicit (Text.pack xs)+renderLine _ pal myNick nicks _ focused xs = parseIrcTextWithNicks pal myNick nicks focused (Text.pack xs)
src/Client/Log.hs view
@@ -11,7 +11,6 @@ -} module Client.Log where -import Client.Configuration import Client.Image.Message (cleanText) import Client.Message import Control.Exception@@ -43,12 +42,11 @@ LogLine {- ^ log line -} -> IO () writeLogLine ll = ignoreProblems $- do dir' <- resolveConfigurationPath- (logBaseDir ll </> Text.unpack (logTarget ll))- let file = dir' </> formatTime defaultTimeLocale "%F" (logDay ll) <.> "log"+ do let dir = logBaseDir ll </> Text.unpack (logTarget ll)+ let file = dir </> formatTime defaultTimeLocale "%F" (logDay ll) <.> "log" let recursiveFlag = True- createDirectoryIfMissing recursiveFlag dir'+ createDirectoryIfMissing recursiveFlag dir L.appendFile file (logLine ll)
src/Client/Network/Async.hs view
@@ -43,6 +43,7 @@ import Control.Lens import Control.Monad import Data.ByteString (ByteString)+import qualified Data.ByteString as B import Data.Foldable import Data.Time import Irc.RateLimit@@ -176,7 +177,8 @@ receiveLoop network h inQueue = do mb <- recvLine h (2*ircMaxMessageLength) for_ mb $ \msg ->- do now <- getZonedTime- atomically $ writeTQueue inQueue- $ NetworkLine network now msg+ do unless (B.null msg) $ -- RFC says to ignore empty messages+ do now <- getZonedTime+ atomically $ writeTQueue inQueue+ $ NetworkLine network now msg receiveLoop network h inQueue
src/Client/Network/Connect.hs view
@@ -18,7 +18,6 @@ ( withConnection ) where -import Client.Configuration import Client.Configuration.ServerSettings import Control.Applicative import Control.Exception (bracket)@@ -26,34 +25,31 @@ import Network.Socket (PortNumber) import Hookup -buildConnectionParams :: ServerSettings -> IO ConnectionParams+buildConnectionParams :: ServerSettings -> ConnectionParams buildConnectionParams args =- do let resPath = traverse resolveConfigurationPath-- tlsParams <- TlsParams- <$> resPath (view ssTlsClientCert args)- <*> resPath (view ssTlsClientKey args <|>- view ssTlsClientCert args)- <*> resPath (view ssTlsServerCert args)- <*> pure (view ssTlsCiphers args)+ let tlsParams = TlsParams+ (view ssTlsClientCert args)+ (view ssTlsClientKey args <|> view ssTlsClientCert args)+ (view ssTlsServerCert args)+ (view ssTlsCiphers args) - let useSecure =- case view ssTls args of- UseInsecure -> Nothing- UseInsecureTls -> Just (tlsParams True)- UseTls -> Just (tlsParams False)+ useSecure =+ case view ssTls args of+ UseInsecure -> Nothing+ UseInsecureTls -> Just (tlsParams True)+ UseTls -> Just (tlsParams False) - proxySettings = view ssSocksHost args <&> \host ->- SocksParams- host- (view ssSocksPort args)+ proxySettings = view ssSocksHost args <&> \host ->+ SocksParams+ host+ (view ssSocksPort args) - return ConnectionParams- { cpHost = view ssHostName args- , cpPort = ircPort args- , cpTls = useSecure- , cpSocks = proxySettings- }+ in ConnectionParams+ { cpHost = view ssHostName args+ , cpPort = ircPort args+ , cpTls = useSecure+ , cpSocks = proxySettings+ } ircPort :: ServerSettings -> PortNumber@@ -70,5 +66,4 @@ -- finishes. withConnection :: ServerSettings -> (Connection -> IO a) -> IO a withConnection settings k =- do params <- buildConnectionParams settings- bracket (connect params) close k+ bracket (connect (buildConnectionParams settings)) close k
src/Client/State.hs view
@@ -31,7 +31,6 @@ , clientDetailView , clientActivityBar , clientSubfocus- , clientNextConnectionId , clientNetworkMap , clientIgnores , clientConnection@@ -43,6 +42,7 @@ , clientErrorMsg , clientLayout , clientRtsStats+ , clientConfigPath -- * Client operations , withClientState@@ -53,6 +53,8 @@ , clientMatcher' , clientActiveRegex , clientToggleHideMeta+ , clientHighlightsNetwork+ , channelUserList , consumeInput , currentCompletionList@@ -129,6 +131,7 @@ import Data.HashSet (HashSet) import qualified Data.HashSet as HashSet import Data.IntMap (IntMap)+import qualified Data.IntMap as IntMap import Data.List import Data.Maybe import Data.Map (Map)@@ -159,11 +162,11 @@ , _clientExtraFocus :: ![Focus] -- ^ extra messages windows to view , _clientConnections :: !(IntMap NetworkState) -- ^ state of active connections- , _clientNextConnectionId :: !NetworkId -- ^ next available 'NetworkId' , _clientEvents :: !(TQueue NetworkEvent) -- ^ incoming network event queue , _clientNetworkMap :: !(HashMap Text NetworkId) -- ^ network name to connection ID , _clientConfig :: !Configuration -- ^ client configuration+ , _clientConfigPath :: !(Maybe FilePath) -- ^ client configuration file path , _clientTextBox :: !Edit.EditBox -- ^ primary text box , _clientTextBoxOffset :: !Int -- ^ size to crop from left of text box@@ -232,8 +235,8 @@ clientLine = views (clientTextBox . Edit.line) (\(Edit.Line n t) -> (n, t)) -- | Construct an initial 'ClientState' using default values.-withClientState :: Configuration -> (ClientState -> IO a) -> IO a-withClientState cfg k =+withClientState :: Maybe FilePath -> Configuration -> (ClientState -> IO a) -> IO a+withClientState cfgPath cfg k = withExtensionState $ \exts -> @@ -254,12 +257,12 @@ , _clientSubfocus = FocusMessages , _clientExtraFocus = [] , _clientConfig = cfg+ , _clientConfigPath = cfgPath , _clientScroll = 0 , _clientDetailView = False , _clientRegex = Nothing , _clientLayout = view configLayout cfg , _clientActivityBar = view configActivityBar cfg- , _clientNextConnectionId = 0 , _clientBell = False , _clientExtensions = exts , _clientLogQueue = []@@ -285,9 +288,12 @@ abortNetwork network st = case preview (clientConnection network) st of Nothing -> return st- Just cs -> do abortConnection ForcedDisconnect (view csSocket cs)+ Just cs -> do -- cancel the network thread+ abortConnection ForcedDisconnect (view csSocket cs)+ -- unassociate this network name from this network id return $! over clientNetworkMap (sans network) st + -- | Add a message to the window associated with a given channel recordChannelMessage :: Text {- ^ network -} ->@@ -418,7 +424,7 @@ ClientMessage -> ClientState -> ClientState recordIrcMessage network target msg st =- updateTransientError msg $+ updateTransientError (NetworkFocus network) msg $ case target of TargetHidden -> st TargetNetwork -> recordNetworkMessage msg st@@ -463,11 +469,15 @@ -- | Detect /error/ messages and add the message text to the transient--- error display.-updateTransientError :: ClientMessage -> ClientState -> ClientState-updateTransientError msg =+-- error display. The transient message will not be generated if the+-- user is focused on the window where the message is going to be+-- rendered, anyway.+updateTransientError :: Focus -> ClientMessage -> ClientState -> ClientState+updateTransientError destination msg st+ | view clientFocus st == destination = st+ | otherwise = - let err = set clientErrorMsg . Just in+ let err e = set clientErrorMsg (Just e) st in case view msgBody msg of ErrorBody txt -> err txt@@ -476,12 +486,12 @@ | let info = replyCodeInfo code , ErrorReply <- replyCodeType info -> err (Text.intercalate " " (replyCodeText info : drop 1 args))- _ -> id+ _ -> st -- | Record a message on a network window recordNetworkMessage :: ClientMessage -> ClientState -> ClientState-recordNetworkMessage msg st = updateTransientError msg+recordNetworkMessage msg st = updateTransientError focus msg $ recordWindowLine focus wl st where network = view msgNetwork msg@@ -696,21 +706,31 @@ settings = fromMaybe defSettings $ preview (clientConfig . configServers . ix network) st - let (i,st') = st & clientNextConnectionId <+~ 1+ i = nextAvailableKey (view clientConnections st)+ -- don't bother delaying on the first reconnect delay = 15 * max 0 (attempts - 1)+ c <- createConnection delay i settings- (view clientEvents st')+ (view clientEvents st) let cs = newNetworkState i network settings c (PingConnecting attempts lastTime) traverse_ (sendMsg cs) (initialMessages cs) return $ set (clientNetworkMap . at network) (Just i)- $ set (clientConnections . at i) (Just cs) st'+ $ set (clientConnections . at i) (Just cs) st +-- | Find the first unused key in the intmap starting at 0.+nextAvailableKey :: IntMap a -> Int+nextAvailableKey m = foldr aux id (IntMap.keys m) 0+ where+ aux k next i+ | k == i = next (i+1)+ | otherwise = i+ applyMessageToClientState :: ZonedTime {- ^ timestamp -} -> IrcMsg {- ^ message received -} ->@@ -776,7 +796,7 @@ do let cfg = view clientConfig st st1 <- clientStopExtensions st (st2, res) <- clientPark st1 $ \ptr ->- traverse (try . activateExtension ptr <=< resolveConfigurationPath)+ traverse (try . activateExtension ptr) (view configExtensions cfg) let (errors, exts) = partitionEithers res
src/Client/State/Network.hs view
@@ -161,6 +161,9 @@ makePrisms ''Transaction makePrisms ''PingStatus +defaultChannelTypes :: String+defaultChannelTypes = "#&"+ csNick :: Lens' NetworkState Identifier csNick = csUserInfo . uiNick @@ -223,7 +226,7 @@ , _csUserInfo = UserInfo "*" "" "" , _csChannels = HashMap.empty , _csSocket = sock- , _csChannelTypes = "#&"+ , _csChannelTypes = defaultChannelTypes , _csModeTypes = defaultModeTypes , _csTransaction = NoTransaction , _csModes = ""
src/Client/View.hs view
@@ -47,7 +47,7 @@ (_, FocusPalette) -> paletteViewLines pal (_, FocusDigraphs) -> digraphLines w st (_, FocusKeyMap) -> keyMapLines st- (_, FocusHelp mb) -> helpImageLines mb pal+ (_, FocusHelp mb) -> helpImageLines st mb pal (_, FocusRtsStats) -> rtsStatsLines (view clientRtsStats st) pal _ -> chatMessageImages focus w st where
src/Client/View/Digraphs.hs view
@@ -17,6 +17,7 @@ import Data.List import Data.List.Split import qualified Data.Text as Text+import Data.Text (Text) import Digraphs import Graphics.Vty.Attributes import Graphics.Vty.Image (wcwidth, wcswidth)@@ -32,7 +33,7 @@ $ map (text' defAttr) $ matcher $ map (Text.pack . drawEntry)- $ digraphListToList digraphs+ $ Text.chunksOf 3 digraphs where matcher = maybe id filter (clientMatcher' st) entriesPerLine = max 1 -- just in case?@@ -47,9 +48,10 @@ sep :: Image' sep = text' defAttr " " -drawEntry :: (Char,Char,Char) -> String-drawEntry (x,y,z) = output ++ replicate (entryWidth - wcswidth output) ' '+drawEntry :: Text {- ^ 3-character entry -} -> String+drawEntry entry = output ++ replicate (entryWidth - wcswidth output) ' ' where+ [x,y,z] = Text.unpack entry output = x:y:z2 dottedCircle = '\x25cc' z1 = cleanChar z
src/Client/View/Help.hs view
@@ -14,13 +14,14 @@ ( helpImageLines ) where +import Client.State (ClientState) import Client.Commands-import Client.Commands.Arguments-import Client.Image.Arguments+import Client.Commands.Arguments.Spec+import Client.Commands.Arguments.Renderer+import Client.Commands.Recognizer import Client.Image.MircFormatting import Client.Image.PackedImage import Client.Image.Palette-import Client.Commands.Recognizer import Control.Lens import Data.Foldable (toList) import Data.List (delete, intercalate)@@ -34,20 +35,22 @@ -- or when given a command name generate the detailed help text -- for that command. helpImageLines ::- Maybe Text {- ^ optional command name -} ->- Palette {- ^ palette -} ->- [Image'] {- ^ help lines -}-helpImageLines mbCmd pal =+ ClientState {- ^ client state -} ->+ Maybe Text {- ^ optional command name -} ->+ Palette {- ^ palette -} ->+ [Image'] {- ^ help lines -}+helpImageLines st mbCmd pal = case mbCmd of- Nothing -> listAllCommands pal- Just cmd -> commandHelpLines cmd pal+ Nothing -> listAllCommands st pal+ Just cmd -> commandHelpLines st cmd pal -- | Generate detailed help lines for the command with the given name. commandHelpLines ::- Text {- ^ command name -} ->- Palette {- ^ palette -} ->- [Image'] {- ^ lines -}-commandHelpLines cmdName pal =+ ClientState {- ^ client state -} ->+ Text {- ^ command name -} ->+ Palette {- ^ palette -} ->+ [Image'] {- ^ lines -}+commandHelpLines st cmdName pal = case recognize cmdName commands of Invalid -> [string (view palError pal) "Unknown command, try /help"] Prefix sfxs ->@@ -56,7 +59,7 @@ suggestions = Text.unpack $ Text.intercalate " " ((cmdName <>) <$> sfxs) Exact Command{cmdNames = names, cmdImplementation = impl, cmdArgumentSpec = spec, cmdDocumentation = doc} ->- reverse $ heading "Syntax: " <> commandSummary pal (pure cmdName) spec+ reverse $ heading "Syntax: " <> commandSummary st pal (pure cmdName) spec : emptyLine : aliasLines ++ explainContext impl@@ -89,20 +92,22 @@ -- | Generate the lines for the help window showing all commands. listAllCommands ::- Palette {- ^ palette -} ->- [Image'] {- ^ help lines -}-listAllCommands pal+ ClientState {- ^ client state -} ->+ Palette {- ^ palette -} ->+ [Image'] {- ^ help lines -}+listAllCommands st pal = intercalate [emptyLine] $ map reverse- $ listCommandSection pal <$> commandsList+ $ listCommandSection st pal <$> commandsList listCommandSection ::+ ClientState {- ^ client state -} -> Palette {- ^ palette -} -> CommandSection {- ^ command section -} -> [Image'] {- ^ help lines -}-listCommandSection pal sec+listCommandSection st pal sec = text' (withStyle defAttr bold) (cmdSectionName sec)- : [ commandSummary pal names spec+ : [ commandSummary st pal names spec | -- pattern needed due to existential quantification Command { cmdNames = names , cmdArgumentSpec = spec@@ -112,14 +117,15 @@ -- | Generate the help line for the given command and its -- specification for use in the list of commands. commandSummary ::- Palette {- ^ palette -} ->- NonEmpty Text {- ^ command name and aliases -} ->- ArgumentSpec a {- ^ argument specification -} ->- Image' {- ^ summary help line -}-commandSummary pal (cmd :| _) args =+ r {- ^ client state -} ->+ Palette {- ^ palette -} ->+ NonEmpty Text {- ^ command name and aliases -} ->+ Args r a {- ^ argument specification -} ->+ Image' {- ^ summary help line -}+commandSummary st pal (cmd :| _) args = char defAttr '/' <> text' (view palCommandReady pal) cmd <>- argumentsImage pal' args ""+ render pal' st True args "" where pal' = set palCommandPlaceholder defAttr pal
src/Client/View/Mentions.hs view
@@ -14,14 +14,16 @@ ( mentionsViewLines ) where +import Client.Configuration (PaddingMode, configNickPadding)+import Client.Image.Message import Client.Image.PackedImage+import Client.Image.Palette (Palette) import Client.Image.StatusLine import Client.State import Client.State.Focus import Client.State.Window-import qualified Data.Map as Map import Control.Lens-import Data.Semigroup+import qualified Data.Map as Map import Data.Time (UTCTime) -- | Generate the list of message lines marked important ordered by@@ -35,18 +37,22 @@ detail = view clientDetailView st + padAmt = view (clientConfig . configNickPadding) st+ palette = clientPalette st+ entries = merge- [windowEntries detail n focus v+ [windowEntries palette w padAmt detail n focus v | (n,(focus, v)) <- names `zip` Map.toList (view clientWindows st) ] data MentionLine = MentionLine- { mlTimestamp :: UTCTime- , mlWindowName :: Char- , mlFocus :: Focus- , mlImage :: Image'+ { mlTimestamp :: UTCTime -- ^ message timestamp for sorting+ , mlWindowName :: Char -- ^ window names shortcut+ , mlFocus :: Focus -- ^ associated window+ , mlImage :: [Image'] -- ^ wrapped rendered lines } +-- | Insert channel name markers between messages from different channels addMarkers :: Int {- ^ draw width -} -> ClientState {- ^ client state -} ->@@ -54,7 +60,7 @@ [Image'] {- ^ mention images and channel labels -} addMarkers _ _ [] = [] addMarkers w !st (!ml : xs)- = map mlImage (ml:same)+ = concatMap mlImage (ml:same) ++ minorStatusLineImage (mlFocus ml) w False st : addMarkers w st rest where@@ -63,24 +69,25 @@ (same,rest) = span isSame xs windowEntries ::- Bool {- ^ detailed view -} ->- Char {- ^ window name -} ->- Focus {- ^ window focus -} ->- Window {- ^ window -} ->+ Palette {- ^ palette -} ->+ Int {- ^ draw columns -} ->+ PaddingMode {- ^ nick padding -} ->+ Bool {- ^ detailed view -} ->+ Char {- ^ window name -} ->+ Focus {- ^ window focus -} ->+ Window {- ^ window -} -> [MentionLine]-windowEntries !detailed name focus w =+windowEntries palette w padAmt detailed name focus win = [ MentionLine { mlTimestamp = views wlTimestamp unpackUTCTime l , mlWindowName = name , mlFocus = focus , mlImage = if detailed- then view wlFullImage l- else view wlPrefix l <>- char mempty ' ' <>- view wlImage l+ then [view wlFullImage l]+ else drawWindowLine palette w padAmt l } | let p x = WLImportant == view wlImportance x- , l <- toListOf (winMessages . each . filtered p) w+ , l <- toListOf (winMessages . each . filtered p) win ] -- | Merge a list of sorted lists of mention lines into a single sorted list
src/Client/View/Messages.hs view
@@ -87,7 +87,7 @@ windowLinesToImages st w hideMeta wwls = case gatherMetadataLines st wwls of ([], []) -> []- ([], wl:wls) -> drawLine wl+ ([], wl:wls) -> drawWindowLine palette w padAmt wl ++ windowLinesToImages st w hideMeta wls ((img,who,mbnext):mds, wls) @@ -106,16 +106,10 @@ config = view clientConfig st padAmt = view configNickPadding config - drawTime = timeImage palette . unpackTimeOfDay padNick = nickPad padAmt metaPad = " " <> padNick "" - drawPrefix = views wlTimestamp drawTime <>- views wlPrefix padNick- wrap pfx body = reverse (lineWrapPrefix w pfx body)-- drawLine wl = wrap (drawPrefix wl) (view wlImage wl) ------------------------------------------------------------------------
src/Client/View/Palette.hs view
@@ -1,3 +1,4 @@+{-# Language OverloadedStrings #-} {-| Module : Client.View.Palette Description : View current palette and to see all terminal colors@@ -15,6 +16,7 @@ ) where import Client.Image.Palette+import Client.Image.MircFormatting import Client.Image.PackedImage import Control.Lens import Data.List@@ -26,6 +28,11 @@ digitImage :: Char -> Image' digitImage d = string defAttr [' ',d,' '] +decimalImage :: Int -> Image'+decimalImage n+ | n < 10 = string defAttr (' ':'0':show n)+ | otherwise = string defAttr ( ' ':show n)+ columns :: [Image'] -> Image' columns = mconcat . intersperse (char defAttr ' ') @@ -33,30 +40,64 @@ -- all the colors used in the current palette as well as -- the colors available for use in palettes. paletteViewLines :: Palette -> [Image']-paletteViewLines pal =+paletteViewLines pal = reverse $++ [ "Current client palette"+ , ""+ , columns (paletteEntries pal)+ , ""++ , "Current client palette nick highlight colors"+ , ""+ , columns (nickHighlights pal)+ , ""++ , "Chat formatting colors: ^C[foreground[,background]]"+ , ""+ , columns (" " : map decimalImage [0..15])+ , columns mircColors+ , ""++ , "Available palette colors: 0x<row><col>"+ , ""+ , columns (map digitImage (' ':digits))+ , columns isoColors ]++ +++ [ columns $ digitImage digit : [ string (withBackColor defAttr c) " " | col <- [0 .. 15] , let c = Color240 (row * 16 + col) ]- | (digit,row) <- reverse $ take 15 $ zip (drop 1 digits) [0 ..]- ] ++- [ columns- $ digitImage '0'+ | (digit,row) <- zip (drop 1 digits) [0 ..]+ ]+++isoColors :: [Image']+isoColors =+ digitImage '0' : [ string (withBackColor defAttr (ISOColor c)) " " | c <- [0..15] ] - , columns (map digitImage (' ':digits))- , mempty- , columns- [ text' (view l pal) name- | (name, Lens l) <- paletteMap- ]- , mempty- , columns- [ string attr "nicks"- | attr <- toListOf (palNicks . folded) pal+mircColors :: [Image']+mircColors =+ " "+ : [ string (withBackColor defAttr c) " "+ | i <- [0..15]+ , let Just c = mircColor i ]++paletteEntries :: Palette -> [Image']+paletteEntries pal =+ [ text' (view l pal) name+ | (name, Lens l) <- paletteMap+ ]++nickHighlights :: Palette -> [Image']+nickHighlights pal =+ [ string attr "nicks"+ | attr <- toListOf (palNicks . folded) pal ]
+ src/DigraphQuote.hs view
@@ -0,0 +1,53 @@+{-|+Module : DigraphQuote+Description : Template Haskell quasi-quoter for digraph table+Copyright : (c) Eric Mertens, 2017+License : ISC+Maintainer : emertens@gmail.com++-}+module DigraphQuote (digraphTable) where++import Data.Char+import Control.Monad+import Language.Haskell.TH+import Language.Haskell.TH.Quote+import Numeric (readHex)++digraphTable :: QuasiQuoter+digraphTable = QuasiQuoter+ { quoteExp = digraphTableExp+ , quotePat = const (fail "Digraph table must be an expression")+ , quoteType = const (fail "Digraph table must be an expression")+ , quoteDec = const (fail "Digraph table must be an expression")+ }++digraphTableExp :: String -> ExpQ+digraphTableExp = stringE . concat <=< traverse parseEntry . lines++-- Parse entries, empty lines are ignored, -- comments are allowed+-- Entries are a two-character digraph followed by a hexadecimal+-- representation of the replacement character's unicode value.+--+-- Examples+--+-- > "' 14+-- > AB 0123+-- > CD 0FDE -- with a comment+-- >+-- > -- with a comment+parseEntry :: String -> Q String+parseEntry line =+ case words line of+ [x,y] : ('U':'+':hex) : rest+ | [(n,"")] <- readHex hex+ , isAllowedTerminator rest -> pure [x,y,chr n]++ rest | isAllowedTerminator rest -> pure "" -- skip empty lines+ | otherwise -> fail ("Bad digraph entry: " ++ line)++-- Optionally tolerate a comment+isAllowedTerminator :: [String] -> Bool+isAllowedTerminator (('-':'-':_):_) = True+isAllowedTerminator [] = True+isAllowedTerminator _ = False
src/Digraphs.hs view
@@ -1,1359 +1,1356 @@-{-# Language OverloadedStrings #-}-{-|-Module : Digraphs-Description : Character mnemonics-Copyright : (c) Eric Mertens, 2016-License : ISC-Maintainer : emertens@gmail.com--This module provides an implementation of /digraphs/ as implemented-in Vim and as specified in RFC 1345 (2-character only).--<https://tools.ietf.org/html/rfc1345>-<http://vimdoc.sourceforge.net/htmldoc/digraph.html>---}-module Digraphs- ( lookupDigraph- , digraphs- , digraphListToList- ) where--import Data.IntMap (IntMap)-import qualified Data.IntMap as IntMap-import Data.Char----- | Look up 2-character digraph.-lookupDigraph :: Char -> Char -> Maybe Char-lookupDigraph x y- | isAscii x, isAscii y = digraphToInt x y `IntMap.lookup` digraphMap- | otherwise = Nothing---digraphMap :: IntMap Char-digraphMap = IntMap.fromList- [ (digraphToInt x y, z) | (x,y,z) <- digraphListToList digraphs ]----- | Injective mapping from 2-character ASCII digraph to 'Int'-digraphToInt :: Char -> Char -> Int-digraphToInt x y = ord x * 127 + ord y----------------------------------------------------------------------------digraphListToList :: DigraphList -> [(Char,Char,Char)]-digraphListToList (Entry x y z xs) = (x,y,z) : digraphListToList xs-digraphListToList Nil = []----- | Packed list of digraph entries-data DigraphList- = Entry {-# UNPACK #-}!Char {-# UNPACK #-}!Char {-# UNPACK #-}!Char DigraphList- | Nil---digraphs :: DigraphList-digraphs- = Entry 'N' 'U' '\x00' -- NULL (NUL)- $ Entry 'S' 'H' '\x01' -- START OF HEADING (SOH)- $ Entry 'S' 'X' '\x02' -- START OF TEXT (STX)- $ Entry 'E' 'X' '\x03' -- END OF TEXT (ETX)- $ Entry 'E' 'T' '\x04' -- END OF TRANSMISSION (EOT)- $ Entry 'E' 'Q' '\x05' -- ENQUIRY (ENQ)- $ Entry 'A' 'K' '\x06' -- ACKNOWLEDGE (ACK)- $ Entry 'B' 'L' '\x07' -- BELL (BEL)- $ Entry 'B' 'S' '\x08' -- BACKSPACE (BS)- $ Entry 'H' 'T' '\x09' -- CHARACTER TABULATION (HT)--- $ Entry 'L' 'F' '\x0a' -- LINE FEED (LF)- $ Entry 'V' 'T' '\x0b' -- LINE TABULATION (VT)- $ Entry 'F' 'F' '\x0c' -- FORM FEED (FF)--- $ Entry 'C' 'R' '\x0d' -- CARRIAGE RETURN (CR)- $ Entry 'S' 'O' '\x0e' -- SHIFT OUT (SO)- $ Entry 'S' 'I' '\x0f' -- SHIFT IN (SI)- $ Entry 'D' 'L' '\x10' -- DATALINK ESCAPE (DLE)- $ Entry 'D' '1' '\x11' -- DEVICE CONTROL ONE (DC1)- $ Entry 'D' '2' '\x12' -- DEVICE CONTROL TWO (DC2)- $ Entry 'D' '3' '\x13' -- DEVICE CONTROL THREE (DC3)- $ Entry 'D' '4' '\x14' -- DEVICE CONTROL FOUR (DC4)- $ Entry 'N' 'K' '\x15' -- NEGATIVE ACKNOWLEDGE (NAK)- $ Entry 'S' 'Y' '\x16' -- SYNCHRONOUS IDLE (SYN)- $ Entry 'E' 'B' '\x17' -- END OF TRANSMISSION BLOCK (ETB)- $ Entry 'C' 'N' '\x18' -- CANCEL (CAN)- $ Entry 'E' 'M' '\x19' -- END OF MEDIUM (EM)- $ Entry 'S' 'B' '\x1a' -- SUBSTITUTE (SUB)- $ Entry 'E' 'C' '\x1b' -- ESCAPE (ESC)- $ Entry 'F' 'S' '\x1c' -- FILE SEPARATOR (IS4)- $ Entry 'G' 'S' '\x1d' -- GROUP SEPARATOR (IS3)- $ Entry 'R' 'S' '\x1e' -- RECORD SEPARATOR (IS2)- $ Entry 'U' 'S' '\x1f' -- UNIT SEPARATOR (IS1)- $ Entry 'S' 'P' '\x20' -- SPACE- $ Entry 'N' 'b' '\x23' -- NUMBER SIGN- $ Entry 'D' 'O' '\x24' -- DOLLAR SIGN- $ Entry 'A' 't' '\x40' -- COMMERCIAL AT- $ Entry '<' '(' '\x5b' -- LEFT SQUARE BRACKET- $ Entry '/' '/' '\x5c' -- REVERSE SOLIDUS- $ Entry ')' '>' '\x5d' -- RIGHT SQUARE BRACKET- $ Entry '\'' '>' '\x5e' -- CIRCUMFLEX ACCENT- $ Entry '\'' '!' '\x60' -- GRAVE ACCENT- $ Entry '(' '!' '\x7b' -- LEFT CURLY BRACKET- $ Entry '!' '!' '\x7c' -- VERTICAL LINE- $ Entry '!' ')' '\x7d' -- RIGHT CURLY BRACKET- $ Entry '\'' '?' '\x7e' -- TILDE- $ Entry 'D' 'T' '\x7f' -- DELETE (DEL)- $ Entry 'P' 'A' '\x80' -- PADDING CHARACTER (PAD)- $ Entry 'H' 'O' '\x81' -- HIGH OCTET PRESET (HOP)- $ Entry 'B' 'H' '\x82' -- BREAK PERMITTED HERE (BPH)- $ Entry 'N' 'H' '\x83' -- NO BREAK HERE (NBH)- $ Entry 'I' 'N' '\x84' -- INDEX (IND)- $ Entry 'N' 'L' '\x85' -- NEXT LINE (NEL)- $ Entry 'S' 'A' '\x86' -- START OF SELECTED AREA (SSA)- $ Entry 'E' 'S' '\x87' -- END OF SELECTED AREA (ESA)- $ Entry 'H' 'S' '\x88' -- CHARACTER TABULATION SET (HTS)- $ Entry 'H' 'J' '\x89' -- CHARACTER TABULATION WITH JUSTIFICATION (HTJ)- $ Entry 'V' 'S' '\x8a' -- LINE TABULATION SET (VTS)- $ Entry 'P' 'D' '\x8b' -- PARTIAL LINE FORWARD (PLD)- $ Entry 'P' 'U' '\x8c' -- PARTIAL LINE BACKWARD (PLU)- $ Entry 'R' 'I' '\x8d' -- REVERSE LINE FEED (RI)- $ Entry 'S' '2' '\x8e' -- SINGLE-SHIFT TWO (SS2)- $ Entry 'S' '3' '\x8f' -- SINGLE-SHIFT THREE (SS3)- $ Entry 'D' 'C' '\x90' -- DEVICE CONTROL STRING (DCS)- $ Entry 'P' '1' '\x91' -- PRIVATE USE ONE (PU1)- $ Entry 'P' '2' '\x92' -- PRIVATE USE TWO (PU2)- $ Entry 'T' 'S' '\x93' -- SET TRANSMIT STATE (STS)- $ Entry 'C' 'C' '\x94' -- CANCEL CHARACTER (CCH)- $ Entry 'M' 'W' '\x95' -- MESSAGE WAITING (MW)- $ Entry 'S' 'G' '\x96' -- START OF GUARDED AREA (SPA)- $ Entry 'E' 'G' '\x97' -- END OF GUARDED AREA (EPA)- $ Entry 'S' 'S' '\x98' -- START OF STRING (SOS)- $ Entry 'G' 'C' '\x99' -- SINGLE GRAPHIC CHARACTER INTRODUCER (SGCI)- $ Entry 'S' 'C' '\x9a' -- SINGLE CHARACTER INTRODUCER (SCI)- $ Entry 'C' 'I' '\x9b' -- CONTROL SEQUENCE INTRODUCER (CSI)- $ Entry 'S' 'T' '\x9c' -- STRING TERMINATOR (ST)- $ Entry 'O' 'C' '\x9d' -- OPERATING SYSTEM COMMAND (OSC)- $ Entry 'P' 'M' '\x9e' -- PRIVACY MESSAGE (PM)- $ Entry 'A' 'C' '\x9f' -- APPLICATION PROGRAM COMMAND (APC)- $ Entry 'N' 'S' '\xa0' -- NO-BREAK SPACE- $ Entry '!' 'I' '\xa1' -- INVERTED EXCLAMATION MARK- $ Entry 'C' 't' '\xa2' -- CENT SIGN- $ Entry 'P' 'd' '\xa3' -- POUND SIGN- $ Entry 'C' 'u' '\xa4' -- CURRENCY SIGN- $ Entry 'Y' 'e' '\xa5' -- YEN SIGN- $ Entry 'B' 'B' '\xa6' -- BROKEN BAR- $ Entry 'S' 'E' '\xa7' -- SECTION SIGN- $ Entry '\'' ':' '\xa8' -- DIAERESIS- $ Entry 'C' 'o' '\xa9' -- COPYRIGHT SIGN- $ Entry '-' 'a' '\xaa' -- FEMININE ORDINAL INDICATOR- $ Entry '<' '<' '\xab' -- LEFT-POINTING DOUBLE ANGLE QUOTATION MARK- $ Entry 'N' 'O' '\xac' -- NOT SIGN- $ Entry '-' '-' '\xad' -- SOFT HYPHEN- $ Entry 'R' 'g' '\xae' -- REGISTERED SIGN- $ Entry '\'' 'm' '\xaf' -- MACRON- $ Entry 'D' 'G' '\xb0' -- DEGREE SIGN- $ Entry '+' '-' '\xb1' -- PLUS-MINUS SIGN- $ Entry '2' 'S' '\xb2' -- SUPERSCRIPT TWO- $ Entry '3' 'S' '\xb3' -- SUPERSCRIPT THREE- $ Entry '\'' '\'' '\xb4' -- ACUTE ACCENT- $ Entry 'M' 'y' '\xb5' -- MICRO SIGN- $ Entry 'P' 'I' '\xb6' -- PILCROW SIGN- $ Entry '.' 'M' '\xb7' -- MIDDLE DOT- $ Entry '\'' ',' '\xb8' -- CEDILLA- $ Entry '1' 'S' '\xb9' -- SUPERSCRIPT ONE- $ Entry '-' 'o' '\xba' -- MASCULINE ORDINAL INDICATOR- $ Entry '>' '>' '\xbb' -- RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK- $ Entry '1' '4' '\xbc' -- VULGAR FRACTION ONE QUARTER- $ Entry '1' '2' '\xbd' -- VULGAR FRACTION ONE HALF- $ Entry '3' '4' '\xbe' -- VULGAR FRACTION THREE QUARTERS- $ Entry '?' 'I' '\xbf' -- INVERTED QUESTION MARK- $ Entry 'A' '!' '\xc0' -- LATIN CAPITAL LETTER A WITH GRAVE- $ Entry 'A' '\'' '\xc1' -- LATIN CAPITAL LETTER A WITH ACUTE- $ Entry 'A' '>' '\xc2' -- LATIN CAPITAL LETTER A WITH CIRCUMFLEX- $ Entry 'A' '?' '\xc3' -- LATIN CAPITAL LETTER A WITH TILDE- $ Entry 'A' ':' '\xc4' -- LATIN CAPITAL LETTER A WITH DIAERESIS- $ Entry 'A' 'A' '\xc5' -- LATIN CAPITAL LETTER A WITH RING ABOVE- $ Entry 'A' 'E' '\xc6' -- LATIN CAPITAL LETTER AE- $ Entry 'C' ',' '\xc7' -- LATIN CAPITAL LETTER C WITH CEDILLA- $ Entry 'E' '!' '\xc8' -- LATIN CAPITAL LETTER E WITH GRAVE- $ Entry 'E' '\'' '\xc9' -- LATIN CAPITAL LETTER E WITH ACUTE- $ Entry 'E' '>' '\xca' -- LATIN CAPITAL LETTER E WITH CIRCUMFLEX- $ Entry 'E' ':' '\xcb' -- LATIN CAPITAL LETTER E WITH DIAERESIS- $ Entry 'I' '!' '\xcc' -- LATIN CAPITAL LETTER I WITH GRAVE- $ Entry 'I' '\'' '\xcd' -- LATIN CAPITAL LETTER I WITH ACUTE- $ Entry 'I' '>' '\xce' -- LATIN CAPITAL LETTER I WITH CIRCUMFLEX- $ Entry 'I' ':' '\xcf' -- LATIN CAPITAL LETTER I WITH DIAERESIS- $ Entry 'D' '-' '\xd0' -- LATIN CAPITAL LETTER ETH (Icelandic)- $ Entry 'N' '?' '\xd1' -- LATIN CAPITAL LETTER N WITH TILDE- $ Entry 'O' '!' '\xd2' -- LATIN CAPITAL LETTER O WITH GRAVE- $ Entry 'O' '\'' '\xd3' -- LATIN CAPITAL LETTER O WITH ACUTE- $ Entry 'O' '>' '\xd4' -- LATIN CAPITAL LETTER O WITH CIRCUMFLEX- $ Entry 'O' '?' '\xd5' -- LATIN CAPITAL LETTER O WITH TILDE- $ Entry 'O' ':' '\xd6' -- LATIN CAPITAL LETTER O WITH DIAERESIS- $ Entry '*' 'X' '\xd7' -- MULTIPLICATION SIGN- $ Entry 'O' '/' '\xd8' -- LATIN CAPITAL LETTER O WITH STROKE- $ Entry 'U' '!' '\xd9' -- LATIN CAPITAL LETTER U WITH GRAVE- $ Entry 'U' '\'' '\xda' -- LATIN CAPITAL LETTER U WITH ACUTE- $ Entry 'U' '>' '\xdb' -- LATIN CAPITAL LETTER U WITH CIRCUMFLEX- $ Entry 'U' ':' '\xdc' -- LATIN CAPITAL LETTER U WITH DIAERESIS- $ Entry 'Y' '\'' '\xdd' -- LATIN CAPITAL LETTER Y WITH ACUTE- $ Entry 'T' 'H' '\xde' -- LATIN CAPITAL LETTER THORN (Icelandic)- $ Entry 's' 's' '\xdf' -- LATIN SMALL LETTER SHARP S (German)- $ Entry 'a' '!' '\xe0' -- LATIN SMALL LETTER A WITH GRAVE- $ Entry 'a' '\'' '\xe1' -- LATIN SMALL LETTER A WITH ACUTE- $ Entry 'a' '>' '\xe2' -- LATIN SMALL LETTER A WITH CIRCUMFLEX- $ Entry 'a' '?' '\xe3' -- LATIN SMALL LETTER A WITH TILDE- $ Entry 'a' ':' '\xe4' -- LATIN SMALL LETTER A WITH DIAERESIS- $ Entry 'a' 'a' '\xe5' -- LATIN SMALL LETTER A WITH RING ABOVE- $ Entry 'a' 'e' '\xe6' -- LATIN SMALL LETTER AE- $ Entry 'c' ',' '\xe7' -- LATIN SMALL LETTER C WITH CEDILLA- $ Entry 'e' '!' '\xe8' -- LATIN SMALL LETTER E WITH GRAVE- $ Entry 'e' '\'' '\xe9' -- LATIN SMALL LETTER E WITH ACUTE- $ Entry 'e' '>' '\xea' -- LATIN SMALL LETTER E WITH CIRCUMFLEX- $ Entry 'e' ':' '\xeb' -- LATIN SMALL LETTER E WITH DIAERESIS- $ Entry 'i' '!' '\xec' -- LATIN SMALL LETTER I WITH GRAVE- $ Entry 'i' '\'' '\xed' -- LATIN SMALL LETTER I WITH ACUTE- $ Entry 'i' '>' '\xee' -- LATIN SMALL LETTER I WITH CIRCUMFLEX- $ Entry 'i' ':' '\xef' -- LATIN SMALL LETTER I WITH DIAERESIS- $ Entry 'd' '-' '\xf0' -- LATIN SMALL LETTER ETH (Icelandic)- $ Entry 'n' '?' '\xf1' -- LATIN SMALL LETTER N WITH TILDE- $ Entry 'o' '!' '\xf2' -- LATIN SMALL LETTER O WITH GRAVE- $ Entry 'o' '\'' '\xf3' -- LATIN SMALL LETTER O WITH ACUTE- $ Entry 'o' '>' '\xf4' -- LATIN SMALL LETTER O WITH CIRCUMFLEX- $ Entry 'o' '?' '\xf5' -- LATIN SMALL LETTER O WITH TILDE- $ Entry 'o' ':' '\xf6' -- LATIN SMALL LETTER O WITH DIAERESIS- $ Entry '-' ':' '\xf7' -- DIVISION SIGN- $ Entry 'o' '/' '\xf8' -- LATIN SMALL LETTER O WITH STROKE- $ Entry 'u' '!' '\xf9' -- LATIN SMALL LETTER U WITH GRAVE- $ Entry 'u' '\'' '\xfa' -- LATIN SMALL LETTER U WITH ACUTE- $ Entry 'u' '>' '\xfb' -- LATIN SMALL LETTER U WITH CIRCUMFLEX- $ Entry 'u' ':' '\xfc' -- LATIN SMALL LETTER U WITH DIAERESIS- $ Entry 'y' '\'' '\xfd' -- LATIN SMALL LETTER Y WITH ACUTE- $ Entry 't' 'h' '\xfe' -- LATIN SMALL LETTER THORN (Icelandic)- $ Entry 'y' ':' '\xff' -- LATIN SMALL LETTER Y WITH DIAERESIS- $ Entry 'A' '-' '\x0100' -- LATIN CAPITAL LETTER A WITH MACRON- $ Entry 'a' '-' '\x0101' -- LATIN SMALL LETTER A WITH MACRON- $ Entry 'A' '(' '\x0102' -- LATIN CAPITAL LETTER A WITH BREVE- $ Entry 'a' '(' '\x0103' -- LATIN SMALL LETTER A WITH BREVE- $ Entry 'A' ';' '\x0104' -- LATIN CAPITAL LETTER A WITH OGONEK- $ Entry 'a' ';' '\x0105' -- LATIN SMALL LETTER A WITH OGONEK- $ Entry 'C' '\'' '\x0106' -- LATIN CAPITAL LETTER C WITH ACUTE- $ Entry 'c' '\'' '\x0107' -- LATIN SMALL LETTER C WITH ACUTE- $ Entry 'C' '>' '\x0108' -- LATIN CAPITAL LETTER C WITH CIRCUMFLEX- $ Entry 'c' '>' '\x0109' -- LATIN SMALL LETTER C WITH CIRCUMFLEX- $ Entry 'C' '.' '\x010A' -- LATIN CAPITAL LETTER C WITH DOT ABOVE- $ Entry 'c' '.' '\x010B' -- LATIN SMALL LETTER C WITH DOT ABOVE- $ Entry 'C' '<' '\x010C' -- LATIN CAPITAL LETTER C WITH CARON- $ Entry 'c' '<' '\x010D' -- LATIN SMALL LETTER C WITH CARON- $ Entry 'D' '<' '\x010E' -- LATIN CAPITAL LETTER D WITH CARON- $ Entry 'd' '<' '\x010F' -- LATIN SMALL LETTER D WITH CARON- $ Entry 'D' '/' '\x0110' -- LATIN CAPITAL LETTER D WITH STROKE- $ Entry 'd' '/' '\x0111' -- LATIN SMALL LETTER D WITH STROKE- $ Entry 'E' '-' '\x0112' -- LATIN CAPITAL LETTER E WITH MACRON- $ Entry 'e' '-' '\x0113' -- LATIN SMALL LETTER E WITH MACRON- $ Entry 'E' '(' '\x0114' -- LATIN CAPITAL LETTER E WITH BREVE- $ Entry 'e' '(' '\x0115' -- LATIN SMALL LETTER E WITH BREVE- $ Entry 'E' '.' '\x0116' -- LATIN CAPITAL LETTER E WITH DOT ABOVE- $ Entry 'e' '.' '\x0117' -- LATIN SMALL LETTER E WITH DOT ABOVE- $ Entry 'E' ';' '\x0118' -- LATIN CAPITAL LETTER E WITH OGONEK- $ Entry 'e' ';' '\x0119' -- LATIN SMALL LETTER E WITH OGONEK- $ Entry 'E' '<' '\x011A' -- LATIN CAPITAL LETTER E WITH CARON- $ Entry 'e' '<' '\x011B' -- LATIN SMALL LETTER E WITH CARON- $ Entry 'G' '>' '\x011C' -- LATIN CAPITAL LETTER G WITH CIRCUMFLEX- $ Entry 'g' '>' '\x011D' -- LATIN SMALL LETTER G WITH CIRCUMFLEX- $ Entry 'G' '(' '\x011E' -- LATIN CAPITAL LETTER G WITH BREVE- $ Entry 'g' '(' '\x011F' -- LATIN SMALL LETTER G WITH BREVE- $ Entry 'G' '.' '\x0120' -- LATIN CAPITAL LETTER G WITH DOT ABOVE- $ Entry 'g' '.' '\x0121' -- LATIN SMALL LETTER G WITH DOT ABOVE- $ Entry 'G' ',' '\x0122' -- LATIN CAPITAL LETTER G WITH CEDILLA- $ Entry 'g' ',' '\x0123' -- LATIN SMALL LETTER G WITH CEDILLA- $ Entry 'H' '>' '\x0124' -- LATIN CAPITAL LETTER H WITH CIRCUMFLEX- $ Entry 'h' '>' '\x0125' -- LATIN SMALL LETTER H WITH CIRCUMFLEX- $ Entry 'H' '/' '\x0126' -- LATIN CAPITAL LETTER H WITH STROKE- $ Entry 'h' '/' '\x0127' -- LATIN SMALL LETTER H WITH STROKE- $ Entry 'I' '?' '\x0128' -- LATIN CAPITAL LETTER I WITH TILDE- $ Entry 'i' '?' '\x0129' -- LATIN SMALL LETTER I WITH TILDE- $ Entry 'I' '-' '\x012A' -- LATIN CAPITAL LETTER I WITH MACRON- $ Entry 'i' '-' '\x012B' -- LATIN SMALL LETTER I WITH MACRON- $ Entry 'I' '(' '\x012C' -- LATIN CAPITAL LETTER I WITH BREVE- $ Entry 'i' '(' '\x012D' -- LATIN SMALL LETTER I WITH BREVE- $ Entry 'I' ';' '\x012E' -- LATIN CAPITAL LETTER I WITH OGONEK- $ Entry 'i' ';' '\x012F' -- LATIN SMALL LETTER I WITH OGONEK- $ Entry 'I' '.' '\x0130' -- LATIN CAPITAL LETTER I WITH DOT ABOVE- $ Entry 'i' '.' '\x0131' -- LATIN SMALL LETTER DOTLESS I- $ Entry 'I' 'J' '\x0132' -- LATIN CAPITAL LIGATURE IJ- $ Entry 'i' 'j' '\x0133' -- LATIN SMALL LIGATURE IJ- $ Entry 'J' '>' '\x0134' -- LATIN CAPITAL LETTER J WITH CIRCUMFLEX- $ Entry 'j' '>' '\x0135' -- LATIN SMALL LETTER J WITH CIRCUMFLEX- $ Entry 'K' ',' '\x0136' -- LATIN CAPITAL LETTER K WITH CEDILLA- $ Entry 'k' ',' '\x0137' -- LATIN SMALL LETTER K WITH CEDILLA- $ Entry 'k' 'k' '\x0138' -- LATIN SMALL LETTER KRA- $ Entry 'L' '\'' '\x0139' -- LATIN CAPITAL LETTER L WITH ACUTE- $ Entry 'l' '\'' '\x013A' -- LATIN SMALL LETTER L WITH ACUTE- $ Entry 'L' ',' '\x013B' -- LATIN CAPITAL LETTER L WITH CEDILLA- $ Entry 'l' ',' '\x013C' -- LATIN SMALL LETTER L WITH CEDILLA- $ Entry 'L' '<' '\x013D' -- LATIN CAPITAL LETTER L WITH CARON- $ Entry 'l' '<' '\x013E' -- LATIN SMALL LETTER L WITH CARON- $ Entry 'L' '.' '\x013F' -- LATIN CAPITAL LETTER L WITH MIDDLE DOT- $ Entry 'l' '.' '\x0140' -- LATIN SMALL LETTER L WITH MIDDLE DOT- $ Entry 'L' '/' '\x0141' -- LATIN CAPITAL LETTER L WITH STROKE- $ Entry 'l' '/' '\x0142' -- LATIN SMALL LETTER L WITH STROKE- $ Entry 'N' '\'' '\x0143' -- LATIN CAPITAL LETTER N WITH ACUTE `- $ Entry 'n' '\'' '\x0144' -- LATIN SMALL LETTER N WITH ACUTE `- $ Entry 'N' ',' '\x0145' -- LATIN CAPITAL LETTER N WITH CEDILLA `- $ Entry 'n' ',' '\x0146' -- LATIN SMALL LETTER N WITH CEDILLA `- $ Entry 'N' '<' '\x0147' -- LATIN CAPITAL LETTER N WITH CARON `- $ Entry 'n' '<' '\x0148' -- LATIN SMALL LETTER N WITH CARON `- $ Entry '\'' 'n' '\x0149' -- LATIN SMALL LETTER N PRECEDED BY APOSTROPHE `- $ Entry 'N' 'G' '\x014A' -- LATIN CAPITAL LETTER ENG- $ Entry 'n' 'g' '\x014B' -- LATIN SMALL LETTER ENG- $ Entry 'O' '-' '\x014C' -- LATIN CAPITAL LETTER O WITH MACRON- $ Entry 'o' '-' '\x014D' -- LATIN SMALL LETTER O WITH MACRON- $ Entry 'O' '(' '\x014E' -- LATIN CAPITAL LETTER O WITH BREVE- $ Entry 'o' '(' '\x014F' -- LATIN SMALL LETTER O WITH BREVE- $ Entry 'O' '"' '\x0150' -- LATIN CAPITAL LETTER O WITH DOUBLE ACUTE- $ Entry 'o' '"' '\x0151' -- LATIN SMALL LETTER O WITH DOUBLE ACUTE- $ Entry 'O' 'E' '\x0152' -- LATIN CAPITAL LIGATURE OE- $ Entry 'o' 'e' '\x0153' -- LATIN SMALL LIGATURE OE- $ Entry 'R' '\'' '\x0154' -- LATIN CAPITAL LETTER R WITH ACUTE- $ Entry 'r' '\'' '\x0155' -- LATIN SMALL LETTER R WITH ACUTE- $ Entry 'R' ',' '\x0156' -- LATIN CAPITAL LETTER R WITH CEDILLA- $ Entry 'r' ',' '\x0157' -- LATIN SMALL LETTER R WITH CEDILLA- $ Entry 'R' '<' '\x0158' -- LATIN CAPITAL LETTER R WITH CARON- $ Entry 'r' '<' '\x0159' -- LATIN SMALL LETTER R WITH CARON- $ Entry 'S' '\'' '\x015A' -- LATIN CAPITAL LETTER S WITH ACUTE- $ Entry 's' '\'' '\x015B' -- LATIN SMALL LETTER S WITH ACUTE- $ Entry 'S' '>' '\x015C' -- LATIN CAPITAL LETTER S WITH CIRCUMFLEX- $ Entry 's' '>' '\x015D' -- LATIN SMALL LETTER S WITH CIRCUMFLEX- $ Entry 'S' ',' '\x015E' -- LATIN CAPITAL LETTER S WITH CEDILLA- $ Entry 's' ',' '\x015F' -- LATIN SMALL LETTER S WITH CEDILLA- $ Entry 'S' '<' '\x0160' -- LATIN CAPITAL LETTER S WITH CARON- $ Entry 's' '<' '\x0161' -- LATIN SMALL LETTER S WITH CARON- $ Entry 'T' ',' '\x0162' -- LATIN CAPITAL LETTER T WITH CEDILLA- $ Entry 't' ',' '\x0163' -- LATIN SMALL LETTER T WITH CEDILLA- $ Entry 'T' '<' '\x0164' -- LATIN CAPITAL LETTER T WITH CARON- $ Entry 't' '<' '\x0165' -- LATIN SMALL LETTER T WITH CARON- $ Entry 'T' '/' '\x0166' -- LATIN CAPITAL LETTER T WITH STROKE- $ Entry 't' '/' '\x0167' -- LATIN SMALL LETTER T WITH STROKE- $ Entry 'U' '?' '\x0168' -- LATIN CAPITAL LETTER U WITH TILDE- $ Entry 'u' '?' '\x0169' -- LATIN SMALL LETTER U WITH TILDE- $ Entry 'U' '-' '\x016A' -- LATIN CAPITAL LETTER U WITH MACRON- $ Entry 'u' '-' '\x016B' -- LATIN SMALL LETTER U WITH MACRON- $ Entry 'U' '(' '\x016C' -- LATIN CAPITAL LETTER U WITH BREVE- $ Entry 'u' '(' '\x016D' -- LATIN SMALL LETTER U WITH BREVE- $ Entry 'U' '0' '\x016E' -- LATIN CAPITAL LETTER U WITH RING ABOVE- $ Entry 'u' '0' '\x016F' -- LATIN SMALL LETTER U WITH RING ABOVE- $ Entry 'U' '"' '\x0170' -- LATIN CAPITAL LETTER U WITH DOUBLE ACUTE- $ Entry 'u' '"' '\x0171' -- LATIN SMALL LETTER U WITH DOUBLE ACUTE- $ Entry 'U' ';' '\x0172' -- LATIN CAPITAL LETTER U WITH OGONEK- $ Entry 'u' ';' '\x0173' -- LATIN SMALL LETTER U WITH OGONEK- $ Entry 'W' '>' '\x0174' -- LATIN CAPITAL LETTER W WITH CIRCUMFLEX- $ Entry 'w' '>' '\x0175' -- LATIN SMALL LETTER W WITH CIRCUMFLEX- $ Entry 'Y' '>' '\x0176' -- LATIN CAPITAL LETTER Y WITH CIRCUMFLEX- $ Entry 'y' '>' '\x0177' -- LATIN SMALL LETTER Y WITH CIRCUMFLEX- $ Entry 'Y' ':' '\x0178' -- LATIN CAPITAL LETTER Y WITH DIAERESIS- $ Entry 'Z' '\'' '\x0179' -- LATIN CAPITAL LETTER Z WITH ACUTE- $ Entry 'z' '\'' '\x017A' -- LATIN SMALL LETTER Z WITH ACUTE- $ Entry 'Z' '.' '\x017B' -- LATIN CAPITAL LETTER Z WITH DOT ABOVE- $ Entry 'z' '.' '\x017C' -- LATIN SMALL LETTER Z WITH DOT ABOVE- $ Entry 'Z' '<' '\x017D' -- LATIN CAPITAL LETTER Z WITH CARON- $ Entry 'z' '<' '\x017E' -- LATIN SMALL LETTER Z WITH CARON- $ Entry 'O' '9' '\x01A0' -- LATIN CAPITAL LETTER O WITH HORN- $ Entry 'o' '9' '\x01A1' -- LATIN SMALL LETTER O WITH HORN- $ Entry 'O' 'I' '\x01A2' -- LATIN CAPITAL LETTER OI- $ Entry 'o' 'i' '\x01A3' -- LATIN SMALL LETTER OI- $ Entry 'y' 'r' '\x01A6' -- LATIN LETTER YR- $ Entry 'U' '9' '\x01AF' -- LATIN CAPITAL LETTER U WITH HORN- $ Entry 'u' '9' '\x01B0' -- LATIN SMALL LETTER U WITH HORN- $ Entry 'Z' '/' '\x01B5' -- LATIN CAPITAL LETTER Z WITH STROKE- $ Entry 'z' '/' '\x01B6' -- LATIN SMALL LETTER Z WITH STROKE- $ Entry 'E' 'D' '\x01B7' -- LATIN CAPITAL LETTER EZH- $ Entry 'A' '<' '\x01CD' -- LATIN CAPITAL LETTER A WITH CARON- $ Entry 'a' '<' '\x01CE' -- LATIN SMALL LETTER A WITH CARON- $ Entry 'I' '<' '\x01CF' -- LATIN CAPITAL LETTER I WITH CARON- $ Entry 'i' '<' '\x01D0' -- LATIN SMALL LETTER I WITH CARON- $ Entry 'O' '<' '\x01D1' -- LATIN CAPITAL LETTER O WITH CARON- $ Entry 'o' '<' '\x01D2' -- LATIN SMALL LETTER O WITH CARON- $ Entry 'U' '<' '\x01D3' -- LATIN CAPITAL LETTER U WITH CARON- $ Entry 'u' '<' '\x01D4' -- LATIN SMALL LETTER U WITH CARON- $ Entry 'A' '1' '\x01DE' -- LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON- $ Entry 'a' '1' '\x01DF' -- LATIN SMALL LETTER A WITH DIAERESIS AND MACRON- $ Entry 'A' '7' '\x01E0' -- LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON- $ Entry 'a' '7' '\x01E1' -- LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON- $ Entry 'A' '3' '\x01E2' -- LATIN CAPITAL LETTER AE WITH MACRON- $ Entry 'a' '3' '\x01E3' -- LATIN SMALL LETTER AE WITH MACRON- $ Entry 'G' '/' '\x01E4' -- LATIN CAPITAL LETTER G WITH STROKE- $ Entry 'g' '/' '\x01E5' -- LATIN SMALL LETTER G WITH STROKE- $ Entry 'G' '<' '\x01E6' -- LATIN CAPITAL LETTER G WITH CARON- $ Entry 'g' '<' '\x01E7' -- LATIN SMALL LETTER G WITH CARON- $ Entry 'K' '<' '\x01E8' -- LATIN CAPITAL LETTER K WITH CARON- $ Entry 'k' '<' '\x01E9' -- LATIN SMALL LETTER K WITH CARON- $ Entry 'O' ';' '\x01EA' -- LATIN CAPITAL LETTER O WITH OGONEK- $ Entry 'o' ';' '\x01EB' -- LATIN SMALL LETTER O WITH OGONEK- $ Entry 'O' '1' '\x01EC' -- LATIN CAPITAL LETTER O WITH OGONEK AND MACRON- $ Entry 'o' '1' '\x01ED' -- LATIN SMALL LETTER O WITH OGONEK AND MACRON- $ Entry 'E' 'Z' '\x01EE' -- LATIN CAPITAL LETTER EZH WITH CARON- $ Entry 'e' 'z' '\x01EF' -- LATIN SMALL LETTER EZH WITH CARON- $ Entry 'j' '<' '\x01F0' -- LATIN SMALL LETTER J WITH CARON- $ Entry 'G' '\'' '\x01F4' -- LATIN CAPITAL LETTER G WITH ACUTE- $ Entry 'g' '\'' '\x01F5' -- LATIN SMALL LETTER G WITH ACUTE- $ Entry ';' 'S' '\x02BF' -- MODIFIER LETTER LEFT HALF RING- $ Entry '\'' '<' '\x02C7' -- CARON- $ Entry '\'' '(' '\x02D8' -- BREVE- $ Entry '\'' '.' '\x02D9' -- DOT ABOVE- $ Entry '\'' '0' '\x02DA' -- RING ABOVE- $ Entry '\'' ';' '\x02DB' -- OGONEK- $ Entry '\'' '"' '\x02DD' -- DOUBLE ACUTE ACCENT- $ Entry 'A' '%' '\x0386' -- GREEK CAPITAL LETTER ALPHA WITH TONOS- $ Entry 'E' '%' '\x0388' -- GREEK CAPITAL LETTER EPSILON WITH TONOS- $ Entry 'Y' '%' '\x0389' -- GREEK CAPITAL LETTER ETA WITH TONOS- $ Entry 'I' '%' '\x038A' -- GREEK CAPITAL LETTER IOTA WITH TONOS- $ Entry 'O' '%' '\x038C' -- GREEK CAPITAL LETTER OMICRON WITH TONOS- $ Entry 'U' '%' '\x038E' -- GREEK CAPITAL LETTER UPSILON WITH TONOS- $ Entry 'W' '%' '\x038F' -- GREEK CAPITAL LETTER OMEGA WITH TONOS- $ Entry 'i' '3' '\x0390' -- GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS- $ Entry 'A' '*' '\x0391' -- GREEK CAPITAL LETTER ALPHA- $ Entry 'B' '*' '\x0392' -- GREEK CAPITAL LETTER BETA- $ Entry 'G' '*' '\x0393' -- GREEK CAPITAL LETTER GAMMA- $ Entry 'D' '*' '\x0394' -- GREEK CAPITAL LETTER DELTA- $ Entry 'E' '*' '\x0395' -- GREEK CAPITAL LETTER EPSILON- $ Entry 'Z' '*' '\x0396' -- GREEK CAPITAL LETTER ZETA- $ Entry 'Y' '*' '\x0397' -- GREEK CAPITAL LETTER ETA- $ Entry 'H' '*' '\x0398' -- GREEK CAPITAL LETTER THETA- $ Entry 'I' '*' '\x0399' -- GREEK CAPITAL LETTER IOTA- $ Entry 'K' '*' '\x039A' -- GREEK CAPITAL LETTER KAPPA- $ Entry 'L' '*' '\x039B' -- GREEK CAPITAL LETTER LAMDA- $ Entry 'M' '*' '\x039C' -- GREEK CAPITAL LETTER MU- $ Entry 'N' '*' '\x039D' -- GREEK CAPITAL LETTER NU- $ Entry 'C' '*' '\x039E' -- GREEK CAPITAL LETTER XI- $ Entry 'O' '*' '\x039F' -- GREEK CAPITAL LETTER OMICRON- $ Entry 'P' '*' '\x03A0' -- GREEK CAPITAL LETTER PI- $ Entry 'R' '*' '\x03A1' -- GREEK CAPITAL LETTER RHO- $ Entry 'S' '*' '\x03A3' -- GREEK CAPITAL LETTER SIGMA- $ Entry 'T' '*' '\x03A4' -- GREEK CAPITAL LETTER TAU- $ Entry 'U' '*' '\x03A5' -- GREEK CAPITAL LETTER UPSILON- $ Entry 'F' '*' '\x03A6' -- GREEK CAPITAL LETTER PHI- $ Entry 'X' '*' '\x03A7' -- GREEK CAPITAL LETTER CHI- $ Entry 'Q' '*' '\x03A8' -- GREEK CAPITAL LETTER PSI- $ Entry 'W' '*' '\x03A9' -- GREEK CAPITAL LETTER OMEGA- $ Entry 'J' '*' '\x03AA' -- GREEK CAPITAL LETTER IOTA WITH DIALYTIKA- $ Entry 'V' '*' '\x03AB' -- GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA- $ Entry 'a' '%' '\x03AC' -- GREEK SMALL LETTER ALPHA WITH TONOS- $ Entry 'e' '%' '\x03AD' -- GREEK SMALL LETTER EPSILON WITH TONOS- $ Entry 'y' '%' '\x03AE' -- GREEK SMALL LETTER ETA WITH TONOS- $ Entry 'i' '%' '\x03AF' -- GREEK SMALL LETTER IOTA WITH TONOS- $ Entry 'u' '3' '\x03B0' -- GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS- $ Entry 'a' '*' '\x03B1' -- GREEK SMALL LETTER ALPHA- $ Entry 'b' '*' '\x03B2' -- GREEK SMALL LETTER BETA- $ Entry 'g' '*' '\x03B3' -- GREEK SMALL LETTER GAMMA- $ Entry 'd' '*' '\x03B4' -- GREEK SMALL LETTER DELTA- $ Entry 'e' '*' '\x03B5' -- GREEK SMALL LETTER EPSILON- $ Entry 'z' '*' '\x03B6' -- GREEK SMALL LETTER ZETA- $ Entry 'y' '*' '\x03B7' -- GREEK SMALL LETTER ETA- $ Entry 'h' '*' '\x03B8' -- GREEK SMALL LETTER THETA- $ Entry 'i' '*' '\x03B9' -- GREEK SMALL LETTER IOTA- $ Entry 'k' '*' '\x03BA' -- GREEK SMALL LETTER KAPPA- $ Entry 'l' '*' '\x03BB' -- GREEK SMALL LETTER LAMDA- $ Entry 'm' '*' '\x03BC' -- GREEK SMALL LETTER MU- $ Entry 'n' '*' '\x03BD' -- GREEK SMALL LETTER NU- $ Entry 'c' '*' '\x03BE' -- GREEK SMALL LETTER XI- $ Entry 'o' '*' '\x03BF' -- GREEK SMALL LETTER OMICRON- $ Entry 'p' '*' '\x03C0' -- GREEK SMALL LETTER PI- $ Entry 'r' '*' '\x03C1' -- GREEK SMALL LETTER RHO- $ Entry '*' 's' '\x03C2' -- GREEK SMALL LETTER FINAL SIGMA- $ Entry 's' '*' '\x03C3' -- GREEK SMALL LETTER SIGMA- $ Entry 't' '*' '\x03C4' -- GREEK SMALL LETTER TAU- $ Entry 'u' '*' '\x03C5' -- GREEK SMALL LETTER UPSILON- $ Entry 'f' '*' '\x03C6' -- GREEK SMALL LETTER PHI- $ Entry 'x' '*' '\x03C7' -- GREEK SMALL LETTER CHI- $ Entry 'q' '*' '\x03C8' -- GREEK SMALL LETTER PSI- $ Entry 'w' '*' '\x03C9' -- GREEK SMALL LETTER OMEGA- $ Entry 'j' '*' '\x03CA' -- GREEK SMALL LETTER IOTA WITH DIALYTIKA- $ Entry 'v' '*' '\x03CB' -- GREEK SMALL LETTER UPSILON WITH DIALYTIKA- $ Entry 'o' '%' '\x03CC' -- GREEK SMALL LETTER OMICRON WITH TONOS- $ Entry 'u' '%' '\x03CD' -- GREEK SMALL LETTER UPSILON WITH TONOS- $ Entry 'w' '%' '\x03CE' -- GREEK SMALL LETTER OMEGA WITH TONOS- $ Entry '\'' 'G' '\x03D8' -- GREEK LETTER ARCHAIC KOPPA- $ Entry ',' 'G' '\x03D9' -- GREEK SMALL LETTER ARCHAIC KOPPA- $ Entry 'T' '3' '\x03DA' -- GREEK LETTER STIGMA- $ Entry 't' '3' '\x03DB' -- GREEK SMALL LETTER STIGMA- $ Entry 'M' '3' '\x03DC' -- GREEK LETTER DIGAMMA- $ Entry 'm' '3' '\x03DD' -- GREEK SMALL LETTER DIGAMMA- $ Entry 'K' '3' '\x03DE' -- GREEK LETTER KOPPA- $ Entry 'k' '3' '\x03DF' -- GREEK SMALL LETTER KOPPA- $ Entry 'P' '3' '\x03E0' -- GREEK LETTER SAMPI- $ Entry 'p' '3' '\x03E1' -- GREEK SMALL LETTER SAMPI- $ Entry '\'' '%' '\x03F4' -- GREEK CAPITAL THETA SYMBOL- $ Entry 'j' '3' '\x03F5' -- GREEK LUNATE EPSILON SYMBOL- $ Entry 'I' 'O' '\x0401' -- CYRILLIC CAPITAL LETTER IO- $ Entry 'D' '%' '\x0402' -- CYRILLIC CAPITAL LETTER DJE- $ Entry 'G' '%' '\x0403' -- CYRILLIC CAPITAL LETTER GJE- $ Entry 'I' 'E' '\x0404' -- CYRILLIC CAPITAL LETTER UKRAINIAN IE- $ Entry 'D' 'S' '\x0405' -- CYRILLIC CAPITAL LETTER DZE- $ Entry 'I' 'I' '\x0406' -- CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I- $ Entry 'Y' 'I' '\x0407' -- CYRILLIC CAPITAL LETTER YI- $ Entry 'J' '%' '\x0408' -- CYRILLIC CAPITAL LETTER JE- $ Entry 'L' 'J' '\x0409' -- CYRILLIC CAPITAL LETTER LJE- $ Entry 'N' 'J' '\x040A' -- CYRILLIC CAPITAL LETTER NJE- $ Entry 'T' 's' '\x040B' -- CYRILLIC CAPITAL LETTER TSHE- $ Entry 'K' 'J' '\x040C' -- CYRILLIC CAPITAL LETTER KJE- $ Entry 'V' '%' '\x040E' -- CYRILLIC CAPITAL LETTER SHORT U- $ Entry 'D' 'Z' '\x040F' -- CYRILLIC CAPITAL LETTER DZHE- $ Entry 'A' '=' '\x0410' -- CYRILLIC CAPITAL LETTER A- $ Entry 'B' '=' '\x0411' -- CYRILLIC CAPITAL LETTER BE- $ Entry 'V' '=' '\x0412' -- CYRILLIC CAPITAL LETTER VE- $ Entry 'G' '=' '\x0413' -- CYRILLIC CAPITAL LETTER GHE- $ Entry 'D' '=' '\x0414' -- CYRILLIC CAPITAL LETTER DE- $ Entry 'E' '=' '\x0415' -- CYRILLIC CAPITAL LETTER IE- $ Entry 'Z' '%' '\x0416' -- CYRILLIC CAPITAL LETTER ZHE- $ Entry 'Z' '=' '\x0417' -- CYRILLIC CAPITAL LETTER ZE- $ Entry 'I' '=' '\x0418' -- CYRILLIC CAPITAL LETTER I- $ Entry 'J' '=' '\x0419' -- CYRILLIC CAPITAL LETTER SHORT I- $ Entry 'K' '=' '\x041A' -- CYRILLIC CAPITAL LETTER KA- $ Entry 'L' '=' '\x041B' -- CYRILLIC CAPITAL LETTER EL- $ Entry 'M' '=' '\x041C' -- CYRILLIC CAPITAL LETTER EM- $ Entry 'N' '=' '\x041D' -- CYRILLIC CAPITAL LETTER EN- $ Entry 'O' '=' '\x041E' -- CYRILLIC CAPITAL LETTER O- $ Entry 'P' '=' '\x041F' -- CYRILLIC CAPITAL LETTER PE- $ Entry 'R' '=' '\x0420' -- CYRILLIC CAPITAL LETTER ER- $ Entry 'S' '=' '\x0421' -- CYRILLIC CAPITAL LETTER ES- $ Entry 'T' '=' '\x0422' -- CYRILLIC CAPITAL LETTER TE- $ Entry 'U' '=' '\x0423' -- CYRILLIC CAPITAL LETTER U- $ Entry 'F' '=' '\x0424' -- CYRILLIC CAPITAL LETTER EF- $ Entry 'H' '=' '\x0425' -- CYRILLIC CAPITAL LETTER HA- $ Entry 'C' '=' '\x0426' -- CYRILLIC CAPITAL LETTER TSE- $ Entry 'C' '%' '\x0427' -- CYRILLIC CAPITAL LETTER CHE- $ Entry 'S' '%' '\x0428' -- CYRILLIC CAPITAL LETTER SHA- $ Entry 'S' 'c' '\x0429' -- CYRILLIC CAPITAL LETTER SHCHA- $ Entry '=' '\'' '\x042A' -- CYRILLIC CAPITAL LETTER HARD SIGN- $ Entry 'Y' '=' '\x042B' -- CYRILLIC CAPITAL LETTER YERU- $ Entry '%' '\'' '\x042C' -- CYRILLIC CAPITAL LETTER SOFT SIGN- $ Entry 'J' 'E' '\x042D' -- CYRILLIC CAPITAL LETTER E- $ Entry 'J' 'U' '\x042E' -- CYRILLIC CAPITAL LETTER YU- $ Entry 'J' 'A' '\x042F' -- CYRILLIC CAPITAL LETTER YA- $ Entry 'a' '=' '\x0430' -- CYRILLIC SMALL LETTER A- $ Entry 'b' '=' '\x0431' -- CYRILLIC SMALL LETTER BE- $ Entry 'v' '=' '\x0432' -- CYRILLIC SMALL LETTER VE- $ Entry 'g' '=' '\x0433' -- CYRILLIC SMALL LETTER GHE- $ Entry 'd' '=' '\x0434' -- CYRILLIC SMALL LETTER DE- $ Entry 'e' '=' '\x0435' -- CYRILLIC SMALL LETTER IE- $ Entry 'z' '%' '\x0436' -- CYRILLIC SMALL LETTER ZHE- $ Entry 'z' '=' '\x0437' -- CYRILLIC SMALL LETTER ZE- $ Entry 'i' '=' '\x0438' -- CYRILLIC SMALL LETTER I- $ Entry 'j' '=' '\x0439' -- CYRILLIC SMALL LETTER SHORT I- $ Entry 'k' '=' '\x043A' -- CYRILLIC SMALL LETTER KA- $ Entry 'l' '=' '\x043B' -- CYRILLIC SMALL LETTER EL- $ Entry 'm' '=' '\x043C' -- CYRILLIC SMALL LETTER EM- $ Entry 'n' '=' '\x043D' -- CYRILLIC SMALL LETTER EN- $ Entry 'o' '=' '\x043E' -- CYRILLIC SMALL LETTER O- $ Entry 'p' '=' '\x043F' -- CYRILLIC SMALL LETTER PE- $ Entry 'r' '=' '\x0440' -- CYRILLIC SMALL LETTER ER- $ Entry 's' '=' '\x0441' -- CYRILLIC SMALL LETTER ES- $ Entry 't' '=' '\x0442' -- CYRILLIC SMALL LETTER TE- $ Entry 'u' '=' '\x0443' -- CYRILLIC SMALL LETTER U- $ Entry 'f' '=' '\x0444' -- CYRILLIC SMALL LETTER EF- $ Entry 'h' '=' '\x0445' -- CYRILLIC SMALL LETTER HA- $ Entry 'c' '=' '\x0446' -- CYRILLIC SMALL LETTER TSE- $ Entry 'c' '%' '\x0447' -- CYRILLIC SMALL LETTER CHE- $ Entry 's' '%' '\x0448' -- CYRILLIC SMALL LETTER SHA- $ Entry 's' 'c' '\x0449' -- CYRILLIC SMALL LETTER SHCHA- $ Entry '=' '\'' '\x044A' -- CYRILLIC SMALL LETTER HARD SIGN- $ Entry 'y' '=' '\x044B' -- CYRILLIC SMALL LETTER YERU- $ Entry '%' '\'' '\x044C' -- CYRILLIC SMALL LETTER SOFT SIGN- $ Entry 'j' 'e' '\x044D' -- CYRILLIC SMALL LETTER E- $ Entry 'j' 'u' '\x044E' -- CYRILLIC SMALL LETTER YU- $ Entry 'j' 'a' '\x044F' -- CYRILLIC SMALL LETTER YA- $ Entry 'i' 'o' '\x0451' -- CYRILLIC SMALL LETTER IO- $ Entry 'd' '%' '\x0452' -- CYRILLIC SMALL LETTER DJE- $ Entry 'g' '%' '\x0453' -- CYRILLIC SMALL LETTER GJE- $ Entry 'i' 'e' '\x0454' -- CYRILLIC SMALL LETTER UKRAINIAN IE- $ Entry 'd' 's' '\x0455' -- CYRILLIC SMALL LETTER DZE- $ Entry 'i' 'i' '\x0456' -- CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I- $ Entry 'y' 'i' '\x0457' -- CYRILLIC SMALL LETTER YI- $ Entry 'j' '%' '\x0458' -- CYRILLIC SMALL LETTER JE- $ Entry 'l' 'j' '\x0459' -- CYRILLIC SMALL LETTER LJE- $ Entry 'n' 'j' '\x045A' -- CYRILLIC SMALL LETTER NJE- $ Entry 't' 's' '\x045B' -- CYRILLIC SMALL LETTER TSHE- $ Entry 'k' 'j' '\x045C' -- CYRILLIC SMALL LETTER KJE- $ Entry 'v' '%' '\x045E' -- CYRILLIC SMALL LETTER SHORT U- $ Entry 'd' 'z' '\x045F' -- CYRILLIC SMALL LETTER DZHE- $ Entry 'Y' '3' '\x0462' -- CYRILLIC CAPITAL LETTER YAT- $ Entry 'y' '3' '\x0463' -- CYRILLIC SMALL LETTER YAT- $ Entry 'O' '3' '\x046A' -- CYRILLIC CAPITAL LETTER BIG YUS- $ Entry 'o' '3' '\x046B' -- CYRILLIC SMALL LETTER BIG YUS- $ Entry 'F' '3' '\x0472' -- CYRILLIC CAPITAL LETTER FITA- $ Entry 'f' '3' '\x0473' -- CYRILLIC SMALL LETTER FITA- $ Entry 'V' '3' '\x0474' -- CYRILLIC CAPITAL LETTER IZHITSA- $ Entry 'v' '3' '\x0475' -- CYRILLIC SMALL LETTER IZHITSA- $ Entry 'C' '3' '\x0480' -- CYRILLIC CAPITAL LETTER KOPPA- $ Entry 'c' '3' '\x0481' -- CYRILLIC SMALL LETTER KOPPA- $ Entry 'G' '3' '\x0490' -- CYRILLIC CAPITAL LETTER GHE WITH UPTURN- $ Entry 'g' '3' '\x0491' -- CYRILLIC SMALL LETTER GHE WITH UPTURN- $ Entry 'A' '+' '\x05D0' -- HEBREW LETTER ALEF- $ Entry 'B' '+' '\x05D1' -- HEBREW LETTER BET- $ Entry 'G' '+' '\x05D2' -- HEBREW LETTER GIMEL- $ Entry 'D' '+' '\x05D3' -- HEBREW LETTER DALET- $ Entry 'H' '+' '\x05D4' -- HEBREW LETTER HE- $ Entry 'W' '+' '\x05D5' -- HEBREW LETTER VAV- $ Entry 'Z' '+' '\x05D6' -- HEBREW LETTER ZAYIN- $ Entry 'X' '+' '\x05D7' -- HEBREW LETTER HET- $ Entry 'T' 'j' '\x05D8' -- HEBREW LETTER TET- $ Entry 'J' '+' '\x05D9' -- HEBREW LETTER YOD- $ Entry 'K' '%' '\x05DA' -- HEBREW LETTER FINAL KAF- $ Entry 'K' '+' '\x05DB' -- HEBREW LETTER KAF- $ Entry 'L' '+' '\x05DC' -- HEBREW LETTER LAMED- $ Entry 'M' '%' '\x05DD' -- HEBREW LETTER FINAL MEM- $ Entry 'M' '+' '\x05DE' -- HEBREW LETTER MEM- $ Entry 'N' '%' '\x05DF' -- HEBREW LETTER FINAL NUN `- $ Entry 'N' '+' '\x05E0' -- HEBREW LETTER NUN `- $ Entry 'S' '+' '\x05E1' -- HEBREW LETTER SAMEKH- $ Entry 'E' '+' '\x05E2' -- HEBREW LETTER AYIN- $ Entry 'P' '%' '\x05E3' -- HEBREW LETTER FINAL PE- $ Entry 'P' '+' '\x05E4' -- HEBREW LETTER PE- $ Entry 'Z' 'j' '\x05E5' -- HEBREW LETTER FINAL TSADI- $ Entry 'Z' 'J' '\x05E6' -- HEBREW LETTER TSADI- $ Entry 'Q' '+' '\x05E7' -- HEBREW LETTER QOF- $ Entry 'R' '+' '\x05E8' -- HEBREW LETTER RESH- $ Entry 'S' 'h' '\x05E9' -- HEBREW LETTER SHIN- $ Entry 'T' '+' '\x05EA' -- HEBREW LETTER TAV- $ Entry ',' '+' '\x060C' -- ARABIC COMMA- $ Entry ';' '+' '\x061B' -- ARABIC SEMICOLON- $ Entry '?' '+' '\x061F' -- ARABIC QUESTION MARK- $ Entry 'H' '\'' '\x0621' -- ARABIC LETTER HAMZA- $ Entry 'a' 'M' '\x0622' -- ARABIC LETTER ALEF WITH MADDA ABOVE- $ Entry 'a' 'H' '\x0623' -- ARABIC LETTER ALEF WITH HAMZA ABOVE- $ Entry 'w' 'H' '\x0624' -- ARABIC LETTER WAW WITH HAMZA ABOVE- $ Entry 'a' 'h' '\x0625' -- ARABIC LETTER ALEF WITH HAMZA BELOW- $ Entry 'y' 'H' '\x0626' -- ARABIC LETTER YEH WITH HAMZA ABOVE- $ Entry 'a' '+' '\x0627' -- ARABIC LETTER ALEF- $ Entry 'b' '+' '\x0628' -- ARABIC LETTER BEH- $ Entry 't' 'm' '\x0629' -- ARABIC LETTER TEH MARBUTA- $ Entry 't' '+' '\x062A' -- ARABIC LETTER TEH- $ Entry 't' 'k' '\x062B' -- ARABIC LETTER THEH- $ Entry 'g' '+' '\x062C' -- ARABIC LETTER JEEM- $ Entry 'h' 'k' '\x062D' -- ARABIC LETTER HAH- $ Entry 'x' '+' '\x062E' -- ARABIC LETTER KHAH- $ Entry 'd' '+' '\x062F' -- ARABIC LETTER DAL- $ Entry 'd' 'k' '\x0630' -- ARABIC LETTER THAL- $ Entry 'r' '+' '\x0631' -- ARABIC LETTER REH- $ Entry 'z' '+' '\x0632' -- ARABIC LETTER ZAIN- $ Entry 's' '+' '\x0633' -- ARABIC LETTER SEEN- $ Entry 's' 'n' '\x0634' -- ARABIC LETTER SHEEN- $ Entry 'c' '+' '\x0635' -- ARABIC LETTER SAD- $ Entry 'd' 'd' '\x0636' -- ARABIC LETTER DAD- $ Entry 't' 'j' '\x0637' -- ARABIC LETTER TAH- $ Entry 'z' 'H' '\x0638' -- ARABIC LETTER ZAH- $ Entry 'e' '+' '\x0639' -- ARABIC LETTER AIN- $ Entry 'i' '+' '\x063A' -- ARABIC LETTER GHAIN- $ Entry '+' '+' '\x0640' -- ARABIC TATWEEL- $ Entry 'f' '+' '\x0641' -- ARABIC LETTER FEH- $ Entry 'q' '+' '\x0642' -- ARABIC LETTER QAF- $ Entry 'k' '+' '\x0643' -- ARABIC LETTER KAF- $ Entry 'l' '+' '\x0644' -- ARABIC LETTER LAM- $ Entry 'm' '+' '\x0645' -- ARABIC LETTER MEEM- $ Entry 'n' '+' '\x0646' -- ARABIC LETTER NOON- $ Entry 'h' '+' '\x0647' -- ARABIC LETTER HEH- $ Entry 'w' '+' '\x0648' -- ARABIC LETTER WAW- $ Entry 'j' '+' '\x0649' -- ARABIC LETTER ALEF MAKSURA- $ Entry 'y' '+' '\x064A' -- ARABIC LETTER YEH- $ Entry ':' '+' '\x064B' -- ARABIC FATHATAN- $ Entry '"' '+' '\x064C' -- ARABIC DAMMATAN- $ Entry '=' '+' '\x064D' -- ARABIC KASRATAN- $ Entry '/' '+' '\x064E' -- ARABIC FATHA- $ Entry '\'' '+' '\x064F' -- ARABIC DAMMA- $ Entry '1' '+' '\x0650' -- ARABIC KASRA- $ Entry '3' '+' '\x0651' -- ARABIC SHADDA- $ Entry '0' '+' '\x0652' -- ARABIC SUKUN- $ Entry 'a' 'S' '\x0670' -- ARABIC LETTER SUPERSCRIPT ALEF- $ Entry 'p' '+' '\x067E' -- ARABIC LETTER PEH- $ Entry 'v' '+' '\x06A4' -- ARABIC LETTER VEH- $ Entry 'g' 'f' '\x06AF' -- ARABIC LETTER GAF- $ Entry '0' 'a' '\x06F0' -- EXTENDED ARABIC-INDIC DIGIT ZERO- $ Entry '1' 'a' '\x06F1' -- EXTENDED ARABIC-INDIC DIGIT ONE- $ Entry '2' 'a' '\x06F2' -- EXTENDED ARABIC-INDIC DIGIT TWO- $ Entry '3' 'a' '\x06F3' -- EXTENDED ARABIC-INDIC DIGIT THREE- $ Entry '4' 'a' '\x06F4' -- EXTENDED ARABIC-INDIC DIGIT FOUR- $ Entry '5' 'a' '\x06F5' -- EXTENDED ARABIC-INDIC DIGIT FIVE- $ Entry '6' 'a' '\x06F6' -- EXTENDED ARABIC-INDIC DIGIT SIX- $ Entry '7' 'a' '\x06F7' -- EXTENDED ARABIC-INDIC DIGIT SEVEN- $ Entry '8' 'a' '\x06F8' -- EXTENDED ARABIC-INDIC DIGIT EIGHT- $ Entry '9' 'a' '\x06F9' -- EXTENDED ARABIC-INDIC DIGIT NINE- $ Entry 'B' '.' '\x1E02' -- LATIN CAPITAL LETTER B WITH DOT ABOVE- $ Entry 'b' '.' '\x1E03' -- LATIN SMALL LETTER B WITH DOT ABOVE- $ Entry 'B' '_' '\x1E06' -- LATIN CAPITAL LETTER B WITH LINE BELOW- $ Entry 'b' '_' '\x1E07' -- LATIN SMALL LETTER B WITH LINE BELOW- $ Entry 'D' '.' '\x1E0A' -- LATIN CAPITAL LETTER D WITH DOT ABOVE- $ Entry 'd' '.' '\x1E0B' -- LATIN SMALL LETTER D WITH DOT ABOVE- $ Entry 'D' '_' '\x1E0E' -- LATIN CAPITAL LETTER D WITH LINE BELOW- $ Entry 'd' '_' '\x1E0F' -- LATIN SMALL LETTER D WITH LINE BELOW- $ Entry 'D' ',' '\x1E10' -- LATIN CAPITAL LETTER D WITH CEDILLA- $ Entry 'd' ',' '\x1E11' -- LATIN SMALL LETTER D WITH CEDILLA- $ Entry 'F' '.' '\x1E1E' -- LATIN CAPITAL LETTER F WITH DOT ABOVE- $ Entry 'f' '.' '\x1E1F' -- LATIN SMALL LETTER F WITH DOT ABOVE- $ Entry 'G' '-' '\x1E20' -- LATIN CAPITAL LETTER G WITH MACRON- $ Entry 'g' '-' '\x1E21' -- LATIN SMALL LETTER G WITH MACRON- $ Entry 'H' '.' '\x1E22' -- LATIN CAPITAL LETTER H WITH DOT ABOVE- $ Entry 'h' '.' '\x1E23' -- LATIN SMALL LETTER H WITH DOT ABOVE- $ Entry 'H' ':' '\x1E26' -- LATIN CAPITAL LETTER H WITH DIAERESIS- $ Entry 'h' ':' '\x1E27' -- LATIN SMALL LETTER H WITH DIAERESIS- $ Entry 'H' ',' '\x1E28' -- LATIN CAPITAL LETTER H WITH CEDILLA- $ Entry 'h' ',' '\x1E29' -- LATIN SMALL LETTER H WITH CEDILLA- $ Entry 'K' '\'' '\x1E30' -- LATIN CAPITAL LETTER K WITH ACUTE- $ Entry 'k' '\'' '\x1E31' -- LATIN SMALL LETTER K WITH ACUTE- $ Entry 'K' '_' '\x1E34' -- LATIN CAPITAL LETTER K WITH LINE BELOW- $ Entry 'k' '_' '\x1E35' -- LATIN SMALL LETTER K WITH LINE BELOW- $ Entry 'L' '_' '\x1E3A' -- LATIN CAPITAL LETTER L WITH LINE BELOW- $ Entry 'l' '_' '\x1E3B' -- LATIN SMALL LETTER L WITH LINE BELOW- $ Entry 'M' '\'' '\x1E3E' -- LATIN CAPITAL LETTER M WITH ACUTE- $ Entry 'm' '\'' '\x1E3F' -- LATIN SMALL LETTER M WITH ACUTE- $ Entry 'M' '.' '\x1E40' -- LATIN CAPITAL LETTER M WITH DOT ABOVE- $ Entry 'm' '.' '\x1E41' -- LATIN SMALL LETTER M WITH DOT ABOVE- $ Entry 'N' '.' '\x1E44' -- LATIN CAPITAL LETTER N WITH DOT ABOVE `- $ Entry 'n' '.' '\x1E45' -- LATIN SMALL LETTER N WITH DOT ABOVE `- $ Entry 'N' '_' '\x1E48' -- LATIN CAPITAL LETTER N WITH LINE BELOW `- $ Entry 'n' '_' '\x1E49' -- LATIN SMALL LETTER N WITH LINE BELOW `- $ Entry 'P' '\'' '\x1E54' -- LATIN CAPITAL LETTER P WITH ACUTE- $ Entry 'p' '\'' '\x1E55' -- LATIN SMALL LETTER P WITH ACUTE- $ Entry 'P' '.' '\x1E56' -- LATIN CAPITAL LETTER P WITH DOT ABOVE- $ Entry 'p' '.' '\x1E57' -- LATIN SMALL LETTER P WITH DOT ABOVE- $ Entry 'R' '.' '\x1E58' -- LATIN CAPITAL LETTER R WITH DOT ABOVE- $ Entry 'r' '.' '\x1E59' -- LATIN SMALL LETTER R WITH DOT ABOVE- $ Entry 'R' '_' '\x1E5E' -- LATIN CAPITAL LETTER R WITH LINE BELOW- $ Entry 'r' '_' '\x1E5F' -- LATIN SMALL LETTER R WITH LINE BELOW- $ Entry 'S' '.' '\x1E60' -- LATIN CAPITAL LETTER S WITH DOT ABOVE- $ Entry 's' '.' '\x1E61' -- LATIN SMALL LETTER S WITH DOT ABOVE- $ Entry 'T' '.' '\x1E6A' -- LATIN CAPITAL LETTER T WITH DOT ABOVE- $ Entry 't' '.' '\x1E6B' -- LATIN SMALL LETTER T WITH DOT ABOVE- $ Entry 'T' '_' '\x1E6E' -- LATIN CAPITAL LETTER T WITH LINE BELOW- $ Entry 't' '_' '\x1E6F' -- LATIN SMALL LETTER T WITH LINE BELOW- $ Entry 'V' '?' '\x1E7C' -- LATIN CAPITAL LETTER V WITH TILDE- $ Entry 'v' '?' '\x1E7D' -- LATIN SMALL LETTER V WITH TILDE- $ Entry 'W' '!' '\x1E80' -- LATIN CAPITAL LETTER W WITH GRAVE- $ Entry 'w' '!' '\x1E81' -- LATIN SMALL LETTER W WITH GRAVE- $ Entry 'W' '\'' '\x1E82' -- LATIN CAPITAL LETTER W WITH ACUTE- $ Entry 'w' '\'' '\x1E83' -- LATIN SMALL LETTER W WITH ACUTE- $ Entry 'W' ':' '\x1E84' -- LATIN CAPITAL LETTER W WITH DIAERESIS- $ Entry 'w' ':' '\x1E85' -- LATIN SMALL LETTER W WITH DIAERESIS- $ Entry 'W' '.' '\x1E86' -- LATIN CAPITAL LETTER W WITH DOT ABOVE- $ Entry 'w' '.' '\x1E87' -- LATIN SMALL LETTER W WITH DOT ABOVE- $ Entry 'X' '.' '\x1E8A' -- LATIN CAPITAL LETTER X WITH DOT ABOVE- $ Entry 'x' '.' '\x1E8B' -- LATIN SMALL LETTER X WITH DOT ABOVE- $ Entry 'X' ':' '\x1E8C' -- LATIN CAPITAL LETTER X WITH DIAERESIS- $ Entry 'x' ':' '\x1E8D' -- LATIN SMALL LETTER X WITH DIAERESIS- $ Entry 'Y' '.' '\x1E8E' -- LATIN CAPITAL LETTER Y WITH DOT ABOVE- $ Entry 'y' '.' '\x1E8F' -- LATIN SMALL LETTER Y WITH DOT ABOVE- $ Entry 'Z' '>' '\x1E90' -- LATIN CAPITAL LETTER Z WITH CIRCUMFLEX- $ Entry 'z' '>' '\x1E91' -- LATIN SMALL LETTER Z WITH CIRCUMFLEX- $ Entry 'Z' '_' '\x1E94' -- LATIN CAPITAL LETTER Z WITH LINE BELOW- $ Entry 'z' '_' '\x1E95' -- LATIN SMALL LETTER Z WITH LINE BELOW- $ Entry 'h' '_' '\x1E96' -- LATIN SMALL LETTER H WITH LINE BELOW- $ Entry 't' ':' '\x1E97' -- LATIN SMALL LETTER T WITH DIAERESIS- $ Entry 'w' '0' '\x1E98' -- LATIN SMALL LETTER W WITH RING ABOVE- $ Entry 'y' '0' '\x1E99' -- LATIN SMALL LETTER Y WITH RING ABOVE- $ Entry 'A' '2' '\x1EA2' -- LATIN CAPITAL LETTER A WITH HOOK ABOVE- $ Entry 'a' '2' '\x1EA3' -- LATIN SMALL LETTER A WITH HOOK ABOVE- $ Entry 'E' '2' '\x1EBA' -- LATIN CAPITAL LETTER E WITH HOOK ABOVE- $ Entry 'e' '2' '\x1EBB' -- LATIN SMALL LETTER E WITH HOOK ABOVE- $ Entry 'E' '?' '\x1EBC' -- LATIN CAPITAL LETTER E WITH TILDE- $ Entry 'e' '?' '\x1EBD' -- LATIN SMALL LETTER E WITH TILDE- $ Entry 'I' '2' '\x1EC8' -- LATIN CAPITAL LETTER I WITH HOOK ABOVE- $ Entry 'i' '2' '\x1EC9' -- LATIN SMALL LETTER I WITH HOOK ABOVE- $ Entry 'O' '2' '\x1ECE' -- LATIN CAPITAL LETTER O WITH HOOK ABOVE- $ Entry 'o' '2' '\x1ECF' -- LATIN SMALL LETTER O WITH HOOK ABOVE- $ Entry 'U' '2' '\x1EE6' -- LATIN CAPITAL LETTER U WITH HOOK ABOVE- $ Entry 'u' '2' '\x1EE7' -- LATIN SMALL LETTER U WITH HOOK ABOVE- $ Entry 'Y' '!' '\x1EF2' -- LATIN CAPITAL LETTER Y WITH GRAVE- $ Entry 'y' '!' '\x1EF3' -- LATIN SMALL LETTER Y WITH GRAVE- $ Entry 'Y' '2' '\x1EF6' -- LATIN CAPITAL LETTER Y WITH HOOK ABOVE- $ Entry 'y' '2' '\x1EF7' -- LATIN SMALL LETTER Y WITH HOOK ABOVE- $ Entry 'Y' '?' '\x1EF8' -- LATIN CAPITAL LETTER Y WITH TILDE- $ Entry 'y' '?' '\x1EF9' -- LATIN SMALL LETTER Y WITH TILDE- $ Entry ';' '\'' '\x1F00' -- GREEK SMALL LETTER ALPHA WITH PSILI- $ Entry ',' '\'' '\x1F01' -- GREEK SMALL LETTER ALPHA WITH DASIA- $ Entry ';' '!' '\x1F02' -- GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA- $ Entry ',' '!' '\x1F03' -- GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA- $ Entry '?' ';' '\x1F04' -- GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA- $ Entry '?' ',' '\x1F05' -- GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA- $ Entry '!' ':' '\x1F06' -- GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI- $ Entry '?' ':' '\x1F07' -- GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI- $ Entry '1' 'N' '\x2002' -- EN SPACE- $ Entry '1' 'M' '\x2003' -- EM SPACE- $ Entry '3' 'M' '\x2004' -- THREE-PER-EM SPACE- $ Entry '4' 'M' '\x2005' -- FOUR-PER-EM SPACE- $ Entry '6' 'M' '\x2006' -- SIX-PER-EM SPACE- $ Entry '1' 'T' '\x2009' -- THIN SPACE- $ Entry '1' 'H' '\x200A' -- HAIR SPACE- $ Entry '-' '1' '\x2010' -- HYPHEN- $ Entry '-' 'N' '\x2013' -- EN DASH `- $ Entry '-' 'M' '\x2014' -- EM DASH- $ Entry '-' '3' '\x2015' -- HORIZONTAL BAR- $ Entry '!' '2' '\x2016' -- DOUBLE VERTICAL LINE- $ Entry '=' '2' '\x2017' -- DOUBLE LOW LINE- $ Entry '\'' '6' '\x2018' -- LEFT SINGLE QUOTATION MARK- $ Entry '\'' '9' '\x2019' -- RIGHT SINGLE QUOTATION MARK- $ Entry '.' '9' '\x201A' -- SINGLE LOW-9 QUOTATION MARK- $ Entry '9' '\'' '\x201B' -- SINGLE HIGH-REVERSED-9 QUOTATION MARK- $ Entry '"' '6' '\x201C' -- LEFT DOUBLE QUOTATION MARK- $ Entry '"' '9' '\x201D' -- RIGHT DOUBLE QUOTATION MARK- $ Entry ':' '9' '\x201E' -- DOUBLE LOW-9 QUOTATION MARK- $ Entry '9' '"' '\x201F' -- DOUBLE HIGH-REVERSED-9 QUOTATION MARK- $ Entry '/' '-' '\x2020' -- DAGGER- $ Entry '/' '=' '\x2021' -- DOUBLE DAGGER- $ Entry '.' '.' '\x2025' -- TWO DOT LEADER- $ Entry '%' '0' '\x2030' -- PER MILLE SIGN- $ Entry '1' '\'' '\x2032' -- PRIME- $ Entry '2' '\'' '\x2033' -- DOUBLE PRIME- $ Entry '3' '\'' '\x2034' -- TRIPLE PRIME- $ Entry '1' '"' '\x2035' -- REVERSED PRIME- $ Entry '2' '"' '\x2036' -- REVERSED DOUBLE PRIME- $ Entry '3' '"' '\x2037' -- REVERSED TRIPLE PRIME- $ Entry 'C' 'a' '\x2038' -- CARET- $ Entry '<' '1' '\x2039' -- SINGLE LEFT-POINTING ANGLE QUOTATION MARK- $ Entry '>' '1' '\x203A' -- SINGLE RIGHT-POINTING ANGLE QUOTATION MARK- $ Entry ':' 'X' '\x203B' -- REFERENCE MARK- $ Entry '\'' '-' '\x203E' -- OVERLINE- $ Entry '/' 'f' '\x2044' -- FRACTION SLASH- $ Entry '0' 'S' '\x2070' -- SUPERSCRIPT ZERO- $ Entry '4' 'S' '\x2074' -- SUPERSCRIPT FOUR- $ Entry '5' 'S' '\x2075' -- SUPERSCRIPT FIVE- $ Entry '6' 'S' '\x2076' -- SUPERSCRIPT SIX- $ Entry '7' 'S' '\x2077' -- SUPERSCRIPT SEVEN- $ Entry '8' 'S' '\x2078' -- SUPERSCRIPT EIGHT- $ Entry '9' 'S' '\x2079' -- SUPERSCRIPT NINE- $ Entry '+' 'S' '\x207A' -- SUPERSCRIPT PLUS SIGN- $ Entry '-' 'S' '\x207B' -- SUPERSCRIPT MINUS- $ Entry '=' 'S' '\x207C' -- SUPERSCRIPT EQUALS SIGN- $ Entry '(' 'S' '\x207D' -- SUPERSCRIPT LEFT PARENTHESIS- $ Entry ')' 'S' '\x207E' -- SUPERSCRIPT RIGHT PARENTHESIS- $ Entry 'n' 'S' '\x207F' -- SUPERSCRIPT LATIN SMALL LETTER N `- $ Entry '0' 's' '\x2080' -- SUBSCRIPT ZERO- $ Entry '1' 's' '\x2081' -- SUBSCRIPT ONE- $ Entry '2' 's' '\x2082' -- SUBSCRIPT TWO- $ Entry '3' 's' '\x2083' -- SUBSCRIPT THREE- $ Entry '4' 's' '\x2084' -- SUBSCRIPT FOUR- $ Entry '5' 's' '\x2085' -- SUBSCRIPT FIVE- $ Entry '6' 's' '\x2086' -- SUBSCRIPT SIX- $ Entry '7' 's' '\x2087' -- SUBSCRIPT SEVEN- $ Entry '8' 's' '\x2088' -- SUBSCRIPT EIGHT- $ Entry '9' 's' '\x2089' -- SUBSCRIPT NINE- $ Entry '+' 's' '\x208A' -- SUBSCRIPT PLUS SIGN- $ Entry '-' 's' '\x208B' -- SUBSCRIPT MINUS- $ Entry '=' 's' '\x208C' -- SUBSCRIPT EQUALS SIGN- $ Entry '(' 's' '\x208D' -- SUBSCRIPT LEFT PARENTHESIS- $ Entry ')' 's' '\x208E' -- SUBSCRIPT RIGHT PARENTHESIS- $ Entry 'L' 'i' '\x20A4' -- LIRA SIGN- $ Entry 'P' 't' '\x20A7' -- PESETA SIGN- $ Entry 'W' '=' '\x20A9' -- WON SIGN- $ Entry 'E' 'u' '\x20AC' -- EURO SIGN- $ Entry 'o' 'C' '\x2103' -- DEGREE CELSIUS- $ Entry 'c' 'o' '\x2105' -- CARE OF- $ Entry 'o' 'F' '\x2109' -- DEGREE FAHRENHEIT- $ Entry 'N' '0' '\x2116' -- NUMERO SIGN- $ Entry 'P' 'O' '\x2117' -- SOUND RECORDING COPYRIGHT- $ Entry 'R' 'x' '\x211E' -- PRESCRIPTION TAKE- $ Entry 'S' 'M' '\x2120' -- SERVICE MARK- $ Entry 'T' 'M' '\x2122' -- TRADE MARK SIGN- $ Entry 'O' 'm' '\x2126' -- OHM SIGN- $ Entry 'A' 'O' '\x212B' -- ANGSTROM SIGN- $ Entry '1' '3' '\x2153' -- VULGAR FRACTION ONE THIRD- $ Entry '2' '3' '\x2154' -- VULGAR FRACTION TWO THIRDS- $ Entry '1' '5' '\x2155' -- VULGAR FRACTION ONE FIFTH- $ Entry '2' '5' '\x2156' -- VULGAR FRACTION TWO FIFTHS- $ Entry '3' '5' '\x2157' -- VULGAR FRACTION THREE FIFTHS- $ Entry '4' '5' '\x2158' -- VULGAR FRACTION FOUR FIFTHS- $ Entry '1' '6' '\x2159' -- VULGAR FRACTION ONE SIXTH- $ Entry '5' '6' '\x215A' -- VULGAR FRACTION FIVE SIXTHS- $ Entry '1' '8' '\x215B' -- VULGAR FRACTION ONE EIGHTH- $ Entry '3' '8' '\x215C' -- VULGAR FRACTION THREE EIGHTHS- $ Entry '5' '8' '\x215D' -- VULGAR FRACTION FIVE EIGHTHS- $ Entry '7' '8' '\x215E' -- VULGAR FRACTION SEVEN EIGHTHS- $ Entry '1' 'R' '\x2160' -- ROMAN NUMERAL ONE- $ Entry '2' 'R' '\x2161' -- ROMAN NUMERAL TWO- $ Entry '3' 'R' '\x2162' -- ROMAN NUMERAL THREE- $ Entry '4' 'R' '\x2163' -- ROMAN NUMERAL FOUR- $ Entry '5' 'R' '\x2164' -- ROMAN NUMERAL FIVE- $ Entry '6' 'R' '\x2165' -- ROMAN NUMERAL SIX- $ Entry '7' 'R' '\x2166' -- ROMAN NUMERAL SEVEN- $ Entry '8' 'R' '\x2167' -- ROMAN NUMERAL EIGHT- $ Entry '9' 'R' '\x2168' -- ROMAN NUMERAL NINE- $ Entry 'a' 'R' '\x2169' -- ROMAN NUMERAL TEN- $ Entry 'b' 'R' '\x216A' -- ROMAN NUMERAL ELEVEN- $ Entry 'c' 'R' '\x216B' -- ROMAN NUMERAL TWELVE- $ Entry '1' 'r' '\x2170' -- SMALL ROMAN NUMERAL ONE- $ Entry '2' 'r' '\x2171' -- SMALL ROMAN NUMERAL TWO- $ Entry '3' 'r' '\x2172' -- SMALL ROMAN NUMERAL THREE- $ Entry '4' 'r' '\x2173' -- SMALL ROMAN NUMERAL FOUR- $ Entry '5' 'r' '\x2174' -- SMALL ROMAN NUMERAL FIVE- $ Entry '6' 'r' '\x2175' -- SMALL ROMAN NUMERAL SIX- $ Entry '7' 'r' '\x2176' -- SMALL ROMAN NUMERAL SEVEN- $ Entry '8' 'r' '\x2177' -- SMALL ROMAN NUMERAL EIGHT- $ Entry '9' 'r' '\x2178' -- SMALL ROMAN NUMERAL NINE- $ Entry 'a' 'r' '\x2179' -- SMALL ROMAN NUMERAL TEN- $ Entry 'b' 'r' '\x217A' -- SMALL ROMAN NUMERAL ELEVEN- $ Entry 'c' 'r' '\x217B' -- SMALL ROMAN NUMERAL TWELVE- $ Entry '<' '-' '\x2190' -- LEFTWARDS ARROW- $ Entry '-' '!' '\x2191' -- UPWARDS ARROW- $ Entry '-' '>' '\x2192' -- RIGHTWARDS ARROW- $ Entry '-' 'v' '\x2193' -- DOWNWARDS ARROW- $ Entry '<' '>' '\x2194' -- LEFT RIGHT ARROW- $ Entry 'U' 'D' '\x2195' -- UP DOWN ARROW- $ Entry '<' '=' '\x21D0' -- LEFTWARDS DOUBLE ARROW- $ Entry '=' '>' '\x21D2' -- RIGHTWARDS DOUBLE ARROW- $ Entry '=' '=' '\x21D4' -- LEFT RIGHT DOUBLE ARROW- $ Entry 'F' 'A' '\x2200' -- FOR ALL- $ Entry 'd' 'P' '\x2202' -- PARTIAL DIFFERENTIAL- $ Entry 'T' 'E' '\x2203' -- THERE EXISTS- $ Entry '/' '0' '\x2205' -- EMPTY SET- $ Entry 'D' 'E' '\x2206' -- INCREMENT- $ Entry 'N' 'B' '\x2207' -- NABLA- $ Entry '(' '-' '\x2208' -- ELEMENT OF- $ Entry '-' ')' '\x220B' -- CONTAINS AS MEMBER- $ Entry '*' 'P' '\x220F' -- N-ARY PRODUCT `- $ Entry '+' 'Z' '\x2211' -- N-ARY SUMMATION `- $ Entry '-' '2' '\x2212' -- MINUS SIGN- $ Entry '-' '+' '\x2213' -- MINUS-OR-PLUS SIGN- $ Entry '*' '-' '\x2217' -- ASTERISK OPERATOR- $ Entry 'O' 'b' '\x2218' -- RING OPERATOR- $ Entry 'S' 'b' '\x2219' -- BULLET OPERATOR- $ Entry 'R' 'T' '\x221A' -- SQUARE ROOT- $ Entry '0' '(' '\x221D' -- PROPORTIONAL TO- $ Entry '0' '0' '\x221E' -- INFINITY- $ Entry '-' 'L' '\x221F' -- RIGHT ANGLE- $ Entry '-' 'V' '\x2220' -- ANGLE- $ Entry 'P' 'P' '\x2225' -- PARALLEL TO- $ Entry 'A' 'N' '\x2227' -- LOGICAL AND- $ Entry 'O' 'R' '\x2228' -- LOGICAL OR- $ Entry '(' 'U' '\x2229' -- INTERSECTION- $ Entry ')' 'U' '\x222A' -- UNION- $ Entry 'I' 'n' '\x222B' -- INTEGRAL- $ Entry 'D' 'I' '\x222C' -- DOUBLE INTEGRAL- $ Entry 'I' 'o' '\x222E' -- CONTOUR INTEGRAL- $ Entry '.' ':' '\x2234' -- THEREFORE- $ Entry ':' '.' '\x2235' -- BECAUSE- $ Entry ':' 'R' '\x2236' -- RATIO- $ Entry ':' ':' '\x2237' -- PROPORTION- $ Entry '?' '1' '\x223C' -- TILDE OPERATOR- $ Entry 'C' 'G' '\x223E' -- INVERTED LAZY S- $ Entry '?' '-' '\x2243' -- ASYMPTOTICALLY EQUAL TO- $ Entry '?' '=' '\x2245' -- APPROXIMATELY EQUAL TO- $ Entry '?' '2' '\x2248' -- ALMOST EQUAL TO- $ Entry '=' '?' '\x224C' -- ALL EQUAL TO- $ Entry 'H' 'I' '\x2253' -- IMAGE OF OR APPROXIMATELY EQUAL TO- $ Entry '!' '=' '\x2260' -- NOT EQUAL TO- $ Entry '=' '3' '\x2261' -- IDENTICAL TO- $ Entry '=' '<' '\x2264' -- LESS-THAN OR EQUAL TO- $ Entry '>' '=' '\x2265' -- GREATER-THAN OR EQUAL TO- $ Entry '<' '*' '\x226A' -- MUCH LESS-THAN- $ Entry '*' '>' '\x226B' -- MUCH GREATER-THAN- $ Entry '!' '<' '\x226E' -- NOT LESS-THAN- $ Entry '!' '>' '\x226F' -- NOT GREATER-THAN- $ Entry '(' 'C' '\x2282' -- SUBSET OF- $ Entry ')' 'C' '\x2283' -- SUPERSET OF- $ Entry '(' '_' '\x2286' -- SUBSET OF OR EQUAL TO- $ Entry ')' '_' '\x2287' -- SUPERSET OF OR EQUAL TO- $ Entry '0' '.' '\x2299' -- CIRCLED DOT OPERATOR- $ Entry '0' '2' '\x229A' -- CIRCLED RING OPERATOR- $ Entry '-' 'T' '\x22A5' -- UP TACK- $ Entry '.' 'P' '\x22C5' -- DOT OPERATOR- $ Entry ':' '3' '\x22EE' -- VERTICAL ELLIPSIS- $ Entry '.' '3' '\x22EF' -- MIDLINE HORIZONTAL ELLIPSIS- $ Entry 'E' 'h' '\x2302' -- HOUSE- $ Entry '<' '7' '\x2308' -- LEFT CEILING- $ Entry '>' '7' '\x2309' -- RIGHT CEILING- $ Entry '7' '<' '\x230A' -- LEFT FLOOR- $ Entry '7' '>' '\x230B' -- RIGHT FLOOR- $ Entry 'N' 'I' '\x2310' -- REVERSED NOT SIGN- $ Entry '(' 'A' '\x2312' -- ARC- $ Entry 'T' 'R' '\x2315' -- TELEPHONE RECORDER- $ Entry 'I' 'u' '\x2320' -- TOP HALF INTEGRAL- $ Entry 'I' 'l' '\x2321' -- BOTTOM HALF INTEGRAL- $ Entry '<' '/' '\x2329' -- LEFT-POINTING ANGLE BRACKET- $ Entry '/' '>' '\x232A' -- RIGHT-POINTING ANGLE BRACKET- $ Entry 'V' 's' '\x2423' -- OPEN BOX- $ Entry '1' 'h' '\x2440' -- OCR HOOK- $ Entry '3' 'h' '\x2441' -- OCR CHAIR- $ Entry '2' 'h' '\x2442' -- OCR FORK- $ Entry '4' 'h' '\x2443' -- OCR INVERTED FORK- $ Entry '1' 'j' '\x2446' -- OCR BRANCH BANK IDENTIFICATION- $ Entry '2' 'j' '\x2447' -- OCR AMOUNT OF CHECK- $ Entry '3' 'j' '\x2448' -- OCR DASH- $ Entry '4' 'j' '\x2449' -- OCR CUSTOMER ACCOUNT NUMBER- $ Entry '1' '.' '\x2488' -- DIGIT ONE FULL STOP- $ Entry '2' '.' '\x2489' -- DIGIT TWO FULL STOP- $ Entry '3' '.' '\x248A' -- DIGIT THREE FULL STOP- $ Entry '4' '.' '\x248B' -- DIGIT FOUR FULL STOP- $ Entry '5' '.' '\x248C' -- DIGIT FIVE FULL STOP- $ Entry '6' '.' '\x248D' -- DIGIT SIX FULL STOP- $ Entry '7' '.' '\x248E' -- DIGIT SEVEN FULL STOP- $ Entry '8' '.' '\x248F' -- DIGIT EIGHT FULL STOP- $ Entry '9' '.' '\x2490' -- DIGIT NINE FULL STOP- $ Entry 'h' 'h' '\x2500' -- BOX DRAWINGS LIGHT HORIZONTAL- $ Entry 'H' 'H' '\x2501' -- BOX DRAWINGS HEAVY HORIZONTAL- $ Entry 'v' 'v' '\x2502' -- BOX DRAWINGS LIGHT VERTICAL- $ Entry 'V' 'V' '\x2503' -- BOX DRAWINGS HEAVY VERTICAL- $ Entry '3' '-' '\x2504' -- BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL- $ Entry '3' '_' '\x2505' -- BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL- $ Entry '3' '!' '\x2506' -- BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL- $ Entry '3' '/' '\x2507' -- BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL- $ Entry '4' '-' '\x2508' -- BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL- $ Entry '4' '_' '\x2509' -- BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL- $ Entry '4' '!' '\x250A' -- BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL- $ Entry '4' '/' '\x250B' -- BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL- $ Entry 'd' 'r' '\x250C' -- BOX DRAWINGS LIGHT DOWN AND RIGHT- $ Entry 'd' 'R' '\x250D' -- BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY- $ Entry 'D' 'r' '\x250E' -- BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT- $ Entry 'D' 'R' '\x250F' -- BOX DRAWINGS HEAVY DOWN AND RIGHT- $ Entry 'd' 'l' '\x2510' -- BOX DRAWINGS LIGHT DOWN AND LEFT- $ Entry 'd' 'L' '\x2511' -- BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY- $ Entry 'D' 'l' '\x2512' -- BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT- $ Entry 'L' 'D' '\x2513' -- BOX DRAWINGS HEAVY DOWN AND LEFT- $ Entry 'u' 'r' '\x2514' -- BOX DRAWINGS LIGHT UP AND RIGHT- $ Entry 'u' 'R' '\x2515' -- BOX DRAWINGS UP LIGHT AND RIGHT HEAVY- $ Entry 'U' 'r' '\x2516' -- BOX DRAWINGS UP HEAVY AND RIGHT LIGHT- $ Entry 'U' 'R' '\x2517' -- BOX DRAWINGS HEAVY UP AND RIGHT- $ Entry 'u' 'l' '\x2518' -- BOX VOICED SOUND MARKDRAWINGS LIGHT UP AND LEFT- $ Entry 'u' 'L' '\x2519' -- BOX DRAWINGS UP LIGHT AND LEFT HEAVY- $ Entry 'U' 'l' '\x251A' -- BOX DRAWINGS UP HEAVY AND LEFT LIGHT- $ Entry 'U' 'L' '\x251B' -- BOX DRAWINGS HEAVY UP AND LEFT- $ Entry 'v' 'r' '\x251C' -- BOX DRAWINGS LIGHT VERTICAL AND RIGHT- $ Entry 'v' 'R' '\x251D' -- BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY- $ Entry 'V' 'r' '\x2520' -- BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT- $ Entry 'V' 'R' '\x2523' -- BOX DRAWINGS HEAVY VERTICAL AND RIGHT- $ Entry 'v' 'l' '\x2524' -- BOX DRAWINGS LIGHT VERTICAL AND LEFT- $ Entry 'v' 'L' '\x2525' -- BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY- $ Entry 'V' 'l' '\x2528' -- BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT- $ Entry 'V' 'L' '\x252B' -- BOX DRAWINGS HEAVY VERTICAL AND LEFT- $ Entry 'd' 'h' '\x252C' -- BOX DRAWINGS LIGHT DOWN AND HORIZONTAL- $ Entry 'd' 'H' '\x252F' -- BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY- $ Entry 'D' 'h' '\x2530' -- BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT- $ Entry 'D' 'H' '\x2533' -- BOX DRAWINGS HEAVY DOWN AND HORIZONTAL- $ Entry 'u' 'h' '\x2534' -- BOX DRAWINGS LIGHT UP AND HORIZONTAL- $ Entry 'u' 'H' '\x2537' -- BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY- $ Entry 'U' 'h' '\x2538' -- BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT- $ Entry 'U' 'H' '\x253B' -- BOX DRAWINGS HEAVY UP AND HORIZONTAL- $ Entry 'v' 'h' '\x253C' -- BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL- $ Entry 'v' 'H' '\x253F' -- BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY- $ Entry 'V' 'h' '\x2542' -- BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT- $ Entry 'V' 'H' '\x254B' -- BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL- $ Entry 'F' 'D' '\x2571' -- BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT- $ Entry 'B' 'D' '\x2572' -- BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT- $ Entry 'T' 'B' '\x2580' -- UPPER HALF BLOCK- $ Entry 'L' 'B' '\x2584' -- LOWER HALF BLOCK- $ Entry 'F' 'B' '\x2588' -- FULL BLOCK- $ Entry 'l' 'B' '\x258C' -- LEFT HALF BLOCK- $ Entry 'R' 'B' '\x2590' -- RIGHT HALF BLOCK- $ Entry '.' 'S' '\x2591' -- LIGHT SHADE- $ Entry ':' 'S' '\x2592' -- MEDIUM SHADE- $ Entry '?' 'S' '\x2593' -- DARK SHADE- $ Entry 'f' 'S' '\x25A0' -- BLACK SQUARE- $ Entry 'O' 'S' '\x25A1' -- WHITE SQUARE- $ Entry 'R' 'O' '\x25A2' -- WHITE SQUARE WITH ROUNDED CORNERS- $ Entry 'R' 'r' '\x25A3' -- WHITE SQUARE CONTAINING BLACK SMALL SQUARE- $ Entry 'R' 'F' '\x25A4' -- SQUARE WITH HORIZONTAL FILL- $ Entry 'R' 'Y' '\x25A5' -- SQUARE WITH VERTICAL FILL- $ Entry 'R' 'H' '\x25A6' -- SQUARE WITH ORTHOGONAL CROSSHATCH FILL- $ Entry 'R' 'Z' '\x25A7' -- SQUARE WITH UPPER LEFT TO LOWER RIGHT FILL- $ Entry 'R' 'K' '\x25A8' -- SQUARE WITH UPPER RIGHT TO LOWER LEFT FILL- $ Entry 'R' 'X' '\x25A9' -- SQUARE WITH DIAGONAL CROSSHATCH FILL- $ Entry 's' 'B' '\x25AA' -- BLACK SMALL SQUARE- $ Entry 'S' 'R' '\x25AC' -- BLACK RECTANGLE- $ Entry 'O' 'r' '\x25AD' -- WHITE RECTANGLE- $ Entry 'U' 'T' '\x25B2' -- BLACK UP-POINTING TRIANGLE- $ Entry 'u' 'T' '\x25B3' -- WHITE UP-POINTING TRIANGLE- $ Entry 'P' 'R' '\x25B6' -- BLACK RIGHT-POINTING TRIANGLE- $ Entry 'T' 'r' '\x25B7' -- WHITE RIGHT-POINTING TRIANGLE- $ Entry 'D' 't' '\x25BC' -- BLACK DOWN-POINTING TRIANGLE- $ Entry 'd' 'T' '\x25BD' -- WHITE DOWN-POINTING TRIANGLE- $ Entry 'P' 'L' '\x25C0' -- BLACK LEFT-POINTING TRIANGLE- $ Entry 'T' 'l' '\x25C1' -- WHITE LEFT-POINTING TRIANGLE- $ Entry 'D' 'b' '\x25C6' -- BLACK DIAMOND- $ Entry 'D' 'w' '\x25C7' -- WHITE DIAMOND- $ Entry 'L' 'Z' '\x25CA' -- LOZENGE- $ Entry '0' 'm' '\x25CB' -- WHITE CIRCLE- $ Entry '0' 'o' '\x25CE' -- BULLSEYE- $ Entry '0' 'M' '\x25CF' -- BLACK CIRCLE- $ Entry '0' 'L' '\x25D0' -- CIRCLE WITH LEFT HALF BLACK- $ Entry '0' 'R' '\x25D1' -- CIRCLE WITH RIGHT HALF BLACK- $ Entry 'S' 'n' '\x25D8' -- INVERSE BULLET- $ Entry 'I' 'c' '\x25D9' -- INVERSE WHITE CIRCLE- $ Entry 'F' 'd' '\x25E2' -- BLACK LOWER RIGHT TRIANGLE- $ Entry 'B' 'd' '\x25E3' -- BLACK LOWER LEFT TRIANGLE- $ Entry '*' '2' '\x2605' -- BLACK STAR- $ Entry '*' '1' '\x2606' -- WHITE STAR- $ Entry '<' 'H' '\x261C' -- WHITE LEFT POINTING INDEX- $ Entry '>' 'H' '\x261E' -- WHITE RIGHT POINTING INDEX- $ Entry '0' 'u' '\x263A' -- WHITE SMILING FACE- $ Entry '0' 'U' '\x263B' -- BLACK SMILING FACE- $ Entry 'S' 'U' '\x263C' -- WHITE SUN WITH RAYS- $ Entry 'F' 'm' '\x2640' -- FEMALE SIGN- $ Entry 'M' 'l' '\x2642' -- MALE SIGN- $ Entry 'c' 'S' '\x2660' -- BLACK SPADE SUIT- $ Entry 'c' 'H' '\x2661' -- WHITE HEART SUIT- $ Entry 'c' 'D' '\x2662' -- WHITE DIAMOND SUIT- $ Entry 'c' 'C' '\x2663' -- BLACK CLUB SUIT- $ Entry 'M' 'd' '\x2669' -- QUARTER NOTE `- $ Entry 'M' '8' '\x266A' -- EIGHTH NOTE `- $ Entry 'M' '2' '\x266B' -- BEAMED EIGHTH NOTES- $ Entry 'M' 'b' '\x266D' -- MUSIC FLAT SIGN- $ Entry 'M' 'x' '\x266E' -- MUSIC NATURAL SIGN- $ Entry 'M' 'X' '\x266F' -- MUSIC SHARP SIGN- $ Entry 'O' 'K' '\x2713' -- CHECK MARK- $ Entry 'X' 'X' '\x2717' -- BALLOT X- $ Entry '-' 'X' '\x2720' -- MALTESE CROSS- $ Entry 'I' 'S' '\x3000' -- IDEOGRAPHIC SPACE- $ Entry ',' '_' '\x3001' -- IDEOGRAPHIC COMMA- $ Entry '.' '_' '\x3002' -- IDEOGRAPHIC FULL STOP- $ Entry '+' '"' '\x3003' -- DITTO MARK- $ Entry '+' '_' '\x3004' -- JAPANESE INDUSTRIAL STANDARD SYMBOL- $ Entry '*' '_' '\x3005' -- IDEOGRAPHIC ITERATION MARK- $ Entry ';' '_' '\x3006' -- IDEOGRAPHIC CLOSING MARK- $ Entry '0' '_' '\x3007' -- IDEOGRAPHIC NUMBER ZERO- $ Entry '<' '+' '\x300A' -- LEFT DOUBLE ANGLE BRACKET- $ Entry '>' '+' '\x300B' -- RIGHT DOUBLE ANGLE BRACKET- $ Entry '<' '\'' '\x300C' -- LEFT CORNER BRACKET- $ Entry '>' '\'' '\x300D' -- RIGHT CORNER BRACKET- $ Entry '<' '"' '\x300E' -- LEFT WHITE CORNER BRACKET- $ Entry '>' '"' '\x300F' -- RIGHT WHITE CORNER BRACKET- $ Entry '(' '"' '\x3010' -- LEFT BLACK LENTICULAR BRACKET- $ Entry ')' '"' '\x3011' -- RIGHT BLACK LENTICULAR BRACKET- $ Entry '=' 'T' '\x3012' -- POSTAL MARK- $ Entry '=' '_' '\x3013' -- GETA MARK- $ Entry '(' '\'' '\x3014' -- LEFT TORTOISE SHELL BRACKET- $ Entry ')' '\'' '\x3015' -- RIGHT TORTOISE SHELL BRACKET- $ Entry '(' 'I' '\x3016' -- LEFT WHITE LENTICULAR BRACKET- $ Entry ')' 'I' '\x3017' -- RIGHT WHITE LENTICULAR BRACKET- $ Entry '-' '?' '\x301C' -- WAVE DASH- $ Entry 'A' '5' '\x3041' -- HIRAGANA LETTER SMALL A- $ Entry 'a' '5' '\x3042' -- HIRAGANA LETTER A- $ Entry 'I' '5' '\x3043' -- HIRAGANA LETTER SMALL I- $ Entry 'i' '5' '\x3044' -- HIRAGANA LETTER I- $ Entry 'U' '5' '\x3045' -- HIRAGANA LETTER SMALL U- $ Entry 'u' '5' '\x3046' -- HIRAGANA LETTER U- $ Entry 'E' '5' '\x3047' -- HIRAGANA LETTER SMALL E- $ Entry 'e' '5' '\x3048' -- HIRAGANA LETTER E- $ Entry 'O' '5' '\x3049' -- HIRAGANA LETTER SMALL O- $ Entry 'o' '5' '\x304A' -- HIRAGANA LETTER O- $ Entry 'k' 'a' '\x304B' -- HIRAGANA LETTER KA- $ Entry 'g' 'a' '\x304C' -- HIRAGANA LETTER GA- $ Entry 'k' 'i' '\x304D' -- HIRAGANA LETTER KI- $ Entry 'g' 'i' '\x304E' -- HIRAGANA LETTER GI- $ Entry 'k' 'u' '\x304F' -- HIRAGANA LETTER KU- $ Entry 'g' 'u' '\x3050' -- HIRAGANA LETTER GU- $ Entry 'k' 'e' '\x3051' -- HIRAGANA LETTER KE- $ Entry 'g' 'e' '\x3052' -- HIRAGANA LETTER GE- $ Entry 'k' 'o' '\x3053' -- HIRAGANA LETTER KO- $ Entry 'g' 'o' '\x3054' -- HIRAGANA LETTER GO- $ Entry 's' 'a' '\x3055' -- HIRAGANA LETTER SA- $ Entry 'z' 'a' '\x3056' -- HIRAGANA LETTER ZA- $ Entry 's' 'i' '\x3057' -- HIRAGANA LETTER SI- $ Entry 'z' 'i' '\x3058' -- HIRAGANA LETTER ZI- $ Entry 's' 'u' '\x3059' -- HIRAGANA LETTER SU- $ Entry 'z' 'u' '\x305A' -- HIRAGANA LETTER ZU- $ Entry 's' 'e' '\x305B' -- HIRAGANA LETTER SE- $ Entry 'z' 'e' '\x305C' -- HIRAGANA LETTER ZE- $ Entry 's' 'o' '\x305D' -- HIRAGANA LETTER SO- $ Entry 'z' 'o' '\x305E' -- HIRAGANA LETTER ZO- $ Entry 't' 'a' '\x305F' -- HIRAGANA LETTER TA- $ Entry 'd' 'a' '\x3060' -- HIRAGANA LETTER DA- $ Entry 't' 'i' '\x3061' -- HIRAGANA LETTER TI- $ Entry 'd' 'i' '\x3062' -- HIRAGANA LETTER DI- $ Entry 't' 'U' '\x3063' -- HIRAGANA LETTER SMALL TU- $ Entry 't' 'u' '\x3064' -- HIRAGANA LETTER TU- $ Entry 'd' 'u' '\x3065' -- HIRAGANA LETTER DU- $ Entry 't' 'e' '\x3066' -- HIRAGANA LETTER TE- $ Entry 'd' 'e' '\x3067' -- HIRAGANA LETTER DE- $ Entry 't' 'o' '\x3068' -- HIRAGANA LETTER TO- $ Entry 'd' 'o' '\x3069' -- HIRAGANA LETTER DO- $ Entry 'n' 'a' '\x306A' -- HIRAGANA LETTER NA- $ Entry 'n' 'i' '\x306B' -- HIRAGANA LETTER NI- $ Entry 'n' 'u' '\x306C' -- HIRAGANA LETTER NU- $ Entry 'n' 'e' '\x306D' -- HIRAGANA LETTER NE- $ Entry 'n' 'o' '\x306E' -- HIRAGANA LETTER NO- $ Entry 'h' 'a' '\x306F' -- HIRAGANA LETTER HA- $ Entry 'b' 'a' '\x3070' -- HIRAGANA LETTER BA- $ Entry 'p' 'a' '\x3071' -- HIRAGANA LETTER PA- $ Entry 'h' 'i' '\x3072' -- HIRAGANA LETTER HI- $ Entry 'b' 'i' '\x3073' -- HIRAGANA LETTER BI- $ Entry 'p' 'i' '\x3074' -- HIRAGANA LETTER PI- $ Entry 'h' 'u' '\x3075' -- HIRAGANA LETTER HU- $ Entry 'b' 'u' '\x3076' -- HIRAGANA LETTER BU- $ Entry 'p' 'u' '\x3077' -- HIRAGANA LETTER PU- $ Entry 'h' 'e' '\x3078' -- HIRAGANA LETTER HE- $ Entry 'b' 'e' '\x3079' -- HIRAGANA LETTER BE- $ Entry 'p' 'e' '\x307A' -- HIRAGANA LETTER PE- $ Entry 'h' 'o' '\x307B' -- HIRAGANA LETTER HO- $ Entry 'b' 'o' '\x307C' -- HIRAGANA LETTER BO- $ Entry 'p' 'o' '\x307D' -- HIRAGANA LETTER PO- $ Entry 'm' 'a' '\x307E' -- HIRAGANA LETTER MA- $ Entry 'm' 'i' '\x307F' -- HIRAGANA LETTER MI- $ Entry 'm' 'u' '\x3080' -- HIRAGANA LETTER MU- $ Entry 'm' 'e' '\x3081' -- HIRAGANA LETTER ME- $ Entry 'm' 'o' '\x3082' -- HIRAGANA LETTER MO- $ Entry 'y' 'A' '\x3083' -- HIRAGANA LETTER SMALL YA- $ Entry 'y' 'a' '\x3084' -- HIRAGANA LETTER YA- $ Entry 'y' 'U' '\x3085' -- HIRAGANA LETTER SMALL YU- $ Entry 'y' 'u' '\x3086' -- HIRAGANA LETTER YU- $ Entry 'y' 'O' '\x3087' -- HIRAGANA LETTER SMALL YO- $ Entry 'y' 'o' '\x3088' -- HIRAGANA LETTER YO- $ Entry 'r' 'a' '\x3089' -- HIRAGANA LETTER RA- $ Entry 'r' 'i' '\x308A' -- HIRAGANA LETTER RI- $ Entry 'r' 'u' '\x308B' -- HIRAGANA LETTER RU- $ Entry 'r' 'e' '\x308C' -- HIRAGANA LETTER RE- $ Entry 'r' 'o' '\x308D' -- HIRAGANA LETTER RO- $ Entry 'w' 'A' '\x308E' -- HIRAGANA LETTER SMALL WA- $ Entry 'w' 'a' '\x308F' -- HIRAGANA LETTER WA- $ Entry 'w' 'i' '\x3090' -- HIRAGANA LETTER WI- $ Entry 'w' 'e' '\x3091' -- HIRAGANA LETTER WE- $ Entry 'w' 'o' '\x3092' -- HIRAGANA LETTER WO- $ Entry 'n' '5' '\x3093' -- HIRAGANA LETTER N `- $ Entry 'v' 'u' '\x3094' -- HIRAGANA LETTER VU- $ Entry '"' '5' '\x309B' -- KATAKANA-HIRAGANA VOICED SOUND MARK- $ Entry '0' '5' '\x309C' -- KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK- $ Entry '*' '5' '\x309D' -- HIRAGANA ITERATION MARK- $ Entry '+' '5' '\x309E' -- HIRAGANA VOICED ITERATION MARK- $ Entry 'a' '6' '\x30A1' -- KATAKANA LETTER SMALL A- $ Entry 'A' '6' '\x30A2' -- KATAKANA LETTER A- $ Entry 'i' '6' '\x30A3' -- KATAKANA LETTER SMALL I- $ Entry 'I' '6' '\x30A4' -- KATAKANA LETTER I- $ Entry 'u' '6' '\x30A5' -- KATAKANA LETTER SMALL U- $ Entry 'U' '6' '\x30A6' -- KATAKANA LETTER U- $ Entry 'e' '6' '\x30A7' -- KATAKANA LETTER SMALL E- $ Entry 'E' '6' '\x30A8' -- KATAKANA LETTER E- $ Entry 'o' '6' '\x30A9' -- KATAKANA LETTER SMALL O- $ Entry 'O' '6' '\x30AA' -- KATAKANA LETTER O- $ Entry 'K' 'a' '\x30AB' -- KATAKANA LETTER KA- $ Entry 'G' 'a' '\x30AC' -- KATAKANA LETTER GA- $ Entry 'K' 'i' '\x30AD' -- KATAKANA LETTER KI- $ Entry 'G' 'i' '\x30AE' -- KATAKANA LETTER GI- $ Entry 'K' 'u' '\x30AF' -- KATAKANA LETTER KU- $ Entry 'G' 'u' '\x30B0' -- KATAKANA LETTER GU- $ Entry 'K' 'e' '\x30B1' -- KATAKANA LETTER KE- $ Entry 'G' 'e' '\x30B2' -- KATAKANA LETTER GE- $ Entry 'K' 'o' '\x30B3' -- KATAKANA LETTER KO- $ Entry 'G' 'o' '\x30B4' -- KATAKANA LETTER GO- $ Entry 'S' 'a' '\x30B5' -- KATAKANA LETTER SA- $ Entry 'Z' 'a' '\x30B6' -- KATAKANA LETTER ZA- $ Entry 'S' 'i' '\x30B7' -- KATAKANA LETTER SI- $ Entry 'Z' 'i' '\x30B8' -- KATAKANA LETTER ZI- $ Entry 'S' 'u' '\x30B9' -- KATAKANA LETTER SU- $ Entry 'Z' 'u' '\x30BA' -- KATAKANA LETTER ZU- $ Entry 'S' 'e' '\x30BB' -- KATAKANA LETTER SE- $ Entry 'Z' 'e' '\x30BC' -- KATAKANA LETTER ZE- $ Entry 'S' 'o' '\x30BD' -- KATAKANA LETTER SO- $ Entry 'Z' 'o' '\x30BE' -- KATAKANA LETTER ZO- $ Entry 'T' 'a' '\x30BF' -- KATAKANA LETTER TA- $ Entry 'D' 'a' '\x30C0' -- KATAKANA LETTER DA- $ Entry 'T' 'i' '\x30C1' -- KATAKANA LETTER TI- $ Entry 'D' 'i' '\x30C2' -- KATAKANA LETTER DI- $ Entry 'T' 'U' '\x30C3' -- KATAKANA LETTER SMALL TU- $ Entry 'T' 'u' '\x30C4' -- KATAKANA LETTER TU- $ Entry 'D' 'u' '\x30C5' -- KATAKANA LETTER DU- $ Entry 'T' 'e' '\x30C6' -- KATAKANA LETTER TE- $ Entry 'D' 'e' '\x30C7' -- KATAKANA LETTER DE- $ Entry 'T' 'o' '\x30C8' -- KATAKANA LETTER TO- $ Entry 'D' 'o' '\x30C9' -- KATAKANA LETTER DO- $ Entry 'N' 'a' '\x30CA' -- KATAKANA LETTER NA- $ Entry 'N' 'i' '\x30CB' -- KATAKANA LETTER NI- $ Entry 'N' 'u' '\x30CC' -- KATAKANA LETTER NU- $ Entry 'N' 'e' '\x30CD' -- KATAKANA LETTER NE- $ Entry 'N' 'o' '\x30CE' -- KATAKANA LETTER NO- $ Entry 'H' 'a' '\x30CF' -- KATAKANA LETTER HA- $ Entry 'B' 'a' '\x30D0' -- KATAKANA LETTER BA- $ Entry 'P' 'a' '\x30D1' -- KATAKANA LETTER PA- $ Entry 'H' 'i' '\x30D2' -- KATAKANA LETTER HI- $ Entry 'B' 'i' '\x30D3' -- KATAKANA LETTER BI- $ Entry 'P' 'i' '\x30D4' -- KATAKANA LETTER PI- $ Entry 'H' 'u' '\x30D5' -- KATAKANA LETTER HU- $ Entry 'B' 'u' '\x30D6' -- KATAKANA LETTER BU- $ Entry 'P' 'u' '\x30D7' -- KATAKANA LETTER PU- $ Entry 'H' 'e' '\x30D8' -- KATAKANA LETTER HE- $ Entry 'B' 'e' '\x30D9' -- KATAKANA LETTER BE- $ Entry 'P' 'e' '\x30DA' -- KATAKANA LETTER PE- $ Entry 'H' 'o' '\x30DB' -- KATAKANA LETTER HO- $ Entry 'B' 'o' '\x30DC' -- KATAKANA LETTER BO- $ Entry 'P' 'o' '\x30DD' -- KATAKANA LETTER PO- $ Entry 'M' 'a' '\x30DE' -- KATAKANA LETTER MA- $ Entry 'M' 'i' '\x30DF' -- KATAKANA LETTER MI- $ Entry 'M' 'u' '\x30E0' -- KATAKANA LETTER MU- $ Entry 'M' 'e' '\x30E1' -- KATAKANA LETTER ME- $ Entry 'M' 'o' '\x30E2' -- KATAKANA LETTER MO- $ Entry 'Y' 'A' '\x30E3' -- KATAKANA LETTER SMALL YA- $ Entry 'Y' 'a' '\x30E4' -- KATAKANA LETTER YA- $ Entry 'Y' 'U' '\x30E5' -- KATAKANA LETTER SMALL YU- $ Entry 'Y' 'u' '\x30E6' -- KATAKANA LETTER YU- $ Entry 'Y' 'O' '\x30E7' -- KATAKANA LETTER SMALL YO- $ Entry 'Y' 'o' '\x30E8' -- KATAKANA LETTER YO- $ Entry 'R' 'a' '\x30E9' -- KATAKANA LETTER RA- $ Entry 'R' 'i' '\x30EA' -- KATAKANA LETTER RI- $ Entry 'R' 'u' '\x30EB' -- KATAKANA LETTER RU- $ Entry 'R' 'e' '\x30EC' -- KATAKANA LETTER RE- $ Entry 'R' 'o' '\x30ED' -- KATAKANA LETTER RO- $ Entry 'W' 'A' '\x30EE' -- KATAKANA LETTER SMALL WA- $ Entry 'W' 'a' '\x30EF' -- KATAKANA LETTER WA- $ Entry 'W' 'i' '\x30F0' -- KATAKANA LETTER WI- $ Entry 'W' 'e' '\x30F1' -- KATAKANA LETTER WE- $ Entry 'W' 'o' '\x30F2' -- KATAKANA LETTER WO- $ Entry 'N' '6' '\x30F3' -- KATAKANA LETTER N `- $ Entry 'V' 'u' '\x30F4' -- KATAKANA LETTER VU- $ Entry 'K' 'A' '\x30F5' -- KATAKANA LETTER SMALL KA- $ Entry 'K' 'E' '\x30F6' -- KATAKANA LETTER SMALL KE- $ Entry 'V' 'a' '\x30F7' -- KATAKANA LETTER VA- $ Entry 'V' 'i' '\x30F8' -- KATAKANA LETTER VI- $ Entry 'V' 'e' '\x30F9' -- KATAKANA LETTER VE- $ Entry 'V' 'o' '\x30FA' -- KATAKANA LETTER VO- $ Entry '.' '6' '\x30FB' -- KATAKANA MIDDLE DOT- $ Entry '-' '6' '\x30FC' -- KATAKANA-HIRAGANA PROLONGED SOUND MARK- $ Entry '*' '6' '\x30FD' -- KATAKANA ITERATION MARK- $ Entry '+' '6' '\x30FE' -- KATAKANA VOICED ITERATION MARK- $ Entry 'b' '4' '\x3105' -- BOPOMOFO LETTER B- $ Entry 'p' '4' '\x3106' -- BOPOMOFO LETTER P- $ Entry 'm' '4' '\x3107' -- BOPOMOFO LETTER M- $ Entry 'f' '4' '\x3108' -- BOPOMOFO LETTER F- $ Entry 'd' '4' '\x3109' -- BOPOMOFO LETTER D- $ Entry 't' '4' '\x310A' -- BOPOMOFO LETTER T- $ Entry 'n' '4' '\x310B' -- BOPOMOFO LETTER N `- $ Entry 'l' '4' '\x310C' -- BOPOMOFO LETTER L- $ Entry 'g' '4' '\x310D' -- BOPOMOFO LETTER G- $ Entry 'k' '4' '\x310E' -- BOPOMOFO LETTER K- $ Entry 'h' '4' '\x310F' -- BOPOMOFO LETTER H- $ Entry 'j' '4' '\x3110' -- BOPOMOFO LETTER J- $ Entry 'q' '4' '\x3111' -- BOPOMOFO LETTER Q- $ Entry 'x' '4' '\x3112' -- BOPOMOFO LETTER X- $ Entry 'z' 'h' '\x3113' -- BOPOMOFO LETTER ZH- $ Entry 'c' 'h' '\x3114' -- BOPOMOFO LETTER CH- $ Entry 's' 'h' '\x3115' -- BOPOMOFO LETTER SH- $ Entry 'r' '4' '\x3116' -- BOPOMOFO LETTER R- $ Entry 'z' '4' '\x3117' -- BOPOMOFO LETTER Z- $ Entry 'c' '4' '\x3118' -- BOPOMOFO LETTER C- $ Entry 's' '4' '\x3119' -- BOPOMOFO LETTER S- $ Entry 'a' '4' '\x311A' -- BOPOMOFO LETTER A- $ Entry 'o' '4' '\x311B' -- BOPOMOFO LETTER O- $ Entry 'e' '4' '\x311C' -- BOPOMOFO LETTER E- $ Entry 'a' 'i' '\x311E' -- BOPOMOFO LETTER AI- $ Entry 'e' 'i' '\x311F' -- BOPOMOFO LETTER EI- $ Entry 'a' 'u' '\x3120' -- BOPOMOFO LETTER AU- $ Entry 'o' 'u' '\x3121' -- BOPOMOFO LETTER OU- $ Entry 'a' 'n' '\x3122' -- BOPOMOFO LETTER AN- $ Entry 'e' 'n' '\x3123' -- BOPOMOFO LETTER EN- $ Entry 'a' 'N' '\x3124' -- BOPOMOFO LETTER ANG- $ Entry 'e' 'N' '\x3125' -- BOPOMOFO LETTER ENG- $ Entry 'e' 'r' '\x3126' -- BOPOMOFO LETTER ER- $ Entry 'i' '4' '\x3127' -- BOPOMOFO LETTER I- $ Entry 'u' '4' '\x3128' -- BOPOMOFO LETTER U- $ Entry 'i' 'u' '\x3129' -- BOPOMOFO LETTER IU- $ Entry 'v' '4' '\x312A' -- BOPOMOFO LETTER V- $ Entry 'n' 'G' '\x312B' -- BOPOMOFO LETTER NG- $ Entry 'g' 'n' '\x312C' -- BOPOMOFO LETTER GN- $ Entry '1' 'c' '\x3220' -- PARENTHESIZED IDEOGRAPH ONE- $ Entry '2' 'c' '\x3221' -- PARENTHESIZED IDEOGRAPH TWO- $ Entry '3' 'c' '\x3222' -- PARENTHESIZED IDEOGRAPH THREE- $ Entry '4' 'c' '\x3223' -- PARENTHESIZED IDEOGRAPH FOUR- $ Entry '5' 'c' '\x3224' -- PARENTHESIZED IDEOGRAPH FIVE- $ Entry '6' 'c' '\x3225' -- PARENTHESIZED IDEOGRAPH SIX- $ Entry '7' 'c' '\x3226' -- PARENTHESIZED IDEOGRAPH SEVEN- $ Entry '8' 'c' '\x3227' -- PARENTHESIZED IDEOGRAPH EIGHT- $ Entry '9' 'c' '\x3228' -- PARENTHESIZED IDEOGRAPH NINE- $ Entry 'f' 'f' '\xFB00' -- LATIN SMALL LIGATURE FF- $ Entry 'f' 'i' '\xFB01' -- LATIN SMALL LIGATURE FI- $ Entry 'f' 'l' '\xFB02' -- LATIN SMALL LIGATURE FL- $ Entry 'f' 't' '\xFB05' -- LATIN SMALL LIGATURE LONG S T- $ Entry 's' 't' '\xFB06' -- LATIN SMALL LIGATURE ST- $ Nil+{-# Language BangPatterns, QuasiQuotes, OverloadedStrings #-}+{-|+Module : Digraphs+Description : Character mnemonics+Copyright : (c) Eric Mertens, 2016+License : ISC+Maintainer : emertens@gmail.com++This module provides an implementation of /digraphs/ as implemented+in Vim and as specified in RFC 1345 (2-character only).++<https://tools.ietf.org/html/rfc1345>+<http://vimdoc.sourceforge.net/htmldoc/digraph.html>++-}+module Digraphs+ ( lookupDigraph+ , digraphs+ ) where++import DigraphQuote+import Data.Text (Text)+import qualified Data.Text as Text++-- | States for the digraph lookup state machine+data St+ = Ready -- ^ ready to match two-character name+ | Match -- ^ first character matched, ready for second+ | Found -- ^ both characters matched, return the next+ | Skip2 -- ^ skip the next two characters+ | Skip1 -- ^ skip the next character++-- | Find the entry in the digraph table give a two-character+-- key and return the matched value.+lookupDigraph :: Char -> Char -> Maybe Char+lookupDigraph !x !y = Text.foldr step finish digraphs Ready+ where+ finish _st = Nothing+ step z k st =+ case st of+ Ready | x == z -> k Match+ | otherwise -> k Skip2+ Match | y == z -> k Found+ | otherwise -> k Skip1+ Skip1 -> k Ready+ Skip2 -> k Skip1+ Found -> Just z++------------------------------------------------------------------------++-- These are explicitly prohibited by the IRC RFC+-- NU U+0000 -- NULL (NUL)+-- LF U+000a -- LINE FEED (LF)+-- CR U+000d -- CARRIAGE RETURN (CR)++digraphs :: Text+digraphs = [digraphTable|+ SH U+0001 -- START OF HEADING (SOH)+ SX U+0002 -- START OF TEXT (STX)+ EX U+0003 -- END OF TEXT (ETX)+ ET U+0004 -- END OF TRANSMISSION (EOT)+ EQ U+0005 -- ENQUIRY (ENQ)+ AK U+0006 -- ACKNOWLEDGE (ACK)+ BL U+0007 -- BELL (BEL)+ BS U+0008 -- BACKSPACE (BS)+ HT U+0009 -- CHARACTER TABULATION (HT)+ VT U+000B -- LINE TABULATION (VT)+ FF U+000C -- FORM FEED (FF)+ SO U+000E -- SHIFT OUT (SO)+ SI U+000F -- SHIFT IN (SI)+ DL U+0010 -- DATALINK ESCAPE (DLE)+ D1 U+0011 -- DEVICE CONTROL ONE (DC1)+ D2 U+0012 -- DEVICE CONTROL TWO (DC2)+ D3 U+0013 -- DEVICE CONTROL THREE (DC3)+ D4 U+0014 -- DEVICE CONTROL FOUR (DC4)+ NK U+0015 -- NEGATIVE ACKNOWLEDGE (NAK)+ SY U+0016 -- SYNCHRONOUS IDLE (SYN)+ EB U+0017 -- END OF TRANSMISSION BLOCK (ETB)+ CN U+0018 -- CANCEL (CAN)+ EM U+0019 -- END OF MEDIUM (EM)+ SB U+001A -- SUBSTITUTE (SUB)+ EC U+001B -- ESCAPE (ESC)+ FS U+001C -- FILE SEPARATOR (IS4)+ GS U+001D -- GROUP SEPARATOR (IS3)+ RS U+001E -- RECORD SEPARATOR (IS2)+ US U+001F -- UNIT SEPARATOR (IS1)+ SP U+0020 -- SPACE+ Nb U+0023 -- NUMBER SIGN+ DO U+0024 -- DOLLAR SIGN+ At U+0040 -- COMMERCIAL AT+ <( U+005B -- LEFT SQUARE BRACKET+ // U+005C -- REVERSE SOLIDUS+ )> U+005D -- RIGHT SQUARE BRACKET+ '> U+005E -- CIRCUMFLEX ACCENT+ '! U+0060 -- GRAVE ACCENT+ (! U+007B -- LEFT CURLY BRACKET+ !! U+007C -- VERTICAL LINE+ !) U+007D -- RIGHT CURLY BRACKET+ '? U+007E -- TILDE+ DT U+007F -- DELETE (DEL)+ PA U+0080 -- PADDING CHARACTER (PAD)+ HO U+0081 -- HIGH OCTET PRESET (HOP)+ BH U+0082 -- BREAK PERMITTED HERE (BPH)+ NH U+0083 -- NO BREAK HERE (NBH)+ IN U+0084 -- INDEX (IND)+ NL U+0085 -- NEXT LINE (NEL)+ SA U+0086 -- START OF SELECTED AREA (SSA)+ ES U+0087 -- END OF SELECTED AREA (ESA)+ HS U+0088 -- CHARACTER TABULATION SET (HTS)+ HJ U+0089 -- CHARACTER TABULATION WITH JUSTIFICATION (HTJ)+ VS U+008A -- LINE TABULATION SET (VTS)+ PD U+008B -- PARTIAL LINE FORWARD (PLD)+ PU U+008C -- PARTIAL LINE BACKWARD (PLU)+ RI U+008D -- REVERSE LINE FEED (RI)+ S2 U+008E -- SINGLE-SHIFT TWO (SS2)+ S3 U+008F -- SINGLE-SHIFT THREE (SS3)+ DC U+0090 -- DEVICE CONTROL STRING (DCS)+ P1 U+0091 -- PRIVATE USE ONE (PU1)+ P2 U+0092 -- PRIVATE USE TWO (PU2)+ TS U+0093 -- SET TRANSMIT STATE (STS)+ CC U+0094 -- CANCEL CHARACTER (CCH)+ MW U+0095 -- MESSAGE WAITING (MW)+ SG U+0096 -- START OF GUARDED AREA (SPA)+ EG U+0097 -- END OF GUARDED AREA (EPA)+ SS U+0098 -- START OF STRING (SOS)+ GC U+0099 -- SINGLE GRAPHIC CHARACTER INTRODUCER (SGCI)+ SC U+009A -- SINGLE CHARACTER INTRODUCER (SCI)+ CI U+009B -- CONTROL SEQUENCE INTRODUCER (CSI)+ ST U+009C -- STRING TERMINATOR (ST)+ OC U+009D -- OPERATING SYSTEM COMMAND (OSC)+ PM U+009E -- PRIVACY MESSAGE (PM)+ AC U+009F -- APPLICATION PROGRAM COMMAND (APC)+ NS U+00A0 -- NO-BREAK SPACE+ !I U+00A1 -- INVERTED EXCLAMATION MARK+ Ct U+00A2 -- CENT SIGN+ Pd U+00A3 -- POUND SIGN+ Cu U+00A4 -- CURRENCY SIGN+ Ye U+00A5 -- YEN SIGN+ BB U+00A6 -- BROKEN BAR+ SE U+00A7 -- SECTION SIGN+ ': U+00A8 -- DIAERESIS+ Co U+00A9 -- COPYRIGHT SIGN+ -a U+00AA -- FEMININE ORDINAL INDICATOR+ << U+00AB -- LEFT-POINTING DOUBLE ANGLE QUOTATION MARK+ NO U+00AC -- NOT SIGN+ -- U+00AD -- SOFT HYPHEN+ Rg U+00AE -- REGISTERED SIGN+ 'm U+00AF -- MACRON+ DG U+00B0 -- DEGREE SIGN+ +- U+00B1 -- PLUS-MINUS SIGN+ 2S U+00B2 -- SUPERSCRIPT TWO+ 3S U+00B3 -- SUPERSCRIPT THREE+ '' U+00B4 -- ACUTE ACCENT+ My U+00B5 -- MICRO SIGN+ PI U+00B6 -- PILCROW SIGN+ .M U+00B7 -- MIDDLE DOT+ ', U+00B8 -- CEDILLA+ 1S U+00B9 -- SUPERSCRIPT ONE+ -o U+00BA -- MASCULINE ORDINAL INDICATOR+ >> U+00BB -- RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK+ 14 U+00BC -- VULGAR FRACTION ONE QUARTER+ 12 U+00BD -- VULGAR FRACTION ONE HALF+ 34 U+00BE -- VULGAR FRACTION THREE QUARTERS+ ?I U+00BF -- INVERTED QUESTION MARK+ A! U+00C0 -- LATIN CAPITAL LETTER A WITH GRAVE+ A' U+00C1 -- LATIN CAPITAL LETTER A WITH ACUTE+ A> U+00C2 -- LATIN CAPITAL LETTER A WITH CIRCUMFLEX+ A? U+00C3 -- LATIN CAPITAL LETTER A WITH TILDE+ A: U+00C4 -- LATIN CAPITAL LETTER A WITH DIAERESIS+ AA U+00C5 -- LATIN CAPITAL LETTER A WITH RING ABOVE+ AE U+00C6 -- LATIN CAPITAL LETTER AE+ C, U+00C7 -- LATIN CAPITAL LETTER C WITH CEDILLA+ E! U+00C8 -- LATIN CAPITAL LETTER E WITH GRAVE+ E' U+00C9 -- LATIN CAPITAL LETTER E WITH ACUTE+ E> U+00CA -- LATIN CAPITAL LETTER E WITH CIRCUMFLEX+ E: U+00CB -- LATIN CAPITAL LETTER E WITH DIAERESIS+ I! U+00CC -- LATIN CAPITAL LETTER I WITH GRAVE+ I' U+00CD -- LATIN CAPITAL LETTER I WITH ACUTE+ I> U+00CE -- LATIN CAPITAL LETTER I WITH CIRCUMFLEX+ I: U+00CF -- LATIN CAPITAL LETTER I WITH DIAERESIS+ D- U+00D0 -- LATIN CAPITAL LETTER ETH (Icelandic)+ N? U+00D1 -- LATIN CAPITAL LETTER N WITH TILDE+ O! U+00D2 -- LATIN CAPITAL LETTER O WITH GRAVE+ O' U+00D3 -- LATIN CAPITAL LETTER O WITH ACUTE+ O> U+00D4 -- LATIN CAPITAL LETTER O WITH CIRCUMFLEX+ O? U+00D5 -- LATIN CAPITAL LETTER O WITH TILDE+ O: U+00D6 -- LATIN CAPITAL LETTER O WITH DIAERESIS+ *X U+00D7 -- MULTIPLICATION SIGN+ O/ U+00D8 -- LATIN CAPITAL LETTER O WITH STROKE+ U! U+00D9 -- LATIN CAPITAL LETTER U WITH GRAVE+ U' U+00DA -- LATIN CAPITAL LETTER U WITH ACUTE+ U> U+00DB -- LATIN CAPITAL LETTER U WITH CIRCUMFLEX+ U: U+00DC -- LATIN CAPITAL LETTER U WITH DIAERESIS+ Y' U+00DD -- LATIN CAPITAL LETTER Y WITH ACUTE+ TH U+00DE -- LATIN CAPITAL LETTER THORN (Icelandic)+ ss U+00DF -- LATIN SMALL LETTER SHARP S (German)+ a! U+00E0 -- LATIN SMALL LETTER A WITH GRAVE+ a' U+00E1 -- LATIN SMALL LETTER A WITH ACUTE+ a> U+00E2 -- LATIN SMALL LETTER A WITH CIRCUMFLEX+ a? U+00E3 -- LATIN SMALL LETTER A WITH TILDE+ a: U+00E4 -- LATIN SMALL LETTER A WITH DIAERESIS+ aa U+00E5 -- LATIN SMALL LETTER A WITH RING ABOVE+ ae U+00E6 -- LATIN SMALL LETTER AE+ c, U+00E7 -- LATIN SMALL LETTER C WITH CEDILLA+ e! U+00E8 -- LATIN SMALL LETTER E WITH GRAVE+ e' U+00E9 -- LATIN SMALL LETTER E WITH ACUTE+ e> U+00EA -- LATIN SMALL LETTER E WITH CIRCUMFLEX+ e: U+00EB -- LATIN SMALL LETTER E WITH DIAERESIS+ i! U+00EC -- LATIN SMALL LETTER I WITH GRAVE+ i' U+00ED -- LATIN SMALL LETTER I WITH ACUTE+ i> U+00EE -- LATIN SMALL LETTER I WITH CIRCUMFLEX+ i: U+00EF -- LATIN SMALL LETTER I WITH DIAERESIS+ d- U+00F0 -- LATIN SMALL LETTER ETH (Icelandic)+ n? U+00F1 -- LATIN SMALL LETTER N WITH TILDE+ o! U+00F2 -- LATIN SMALL LETTER O WITH GRAVE+ o' U+00F3 -- LATIN SMALL LETTER O WITH ACUTE+ o> U+00F4 -- LATIN SMALL LETTER O WITH CIRCUMFLEX+ o? U+00F5 -- LATIN SMALL LETTER O WITH TILDE+ o: U+00F6 -- LATIN SMALL LETTER O WITH DIAERESIS+ -: U+00F7 -- DIVISION SIGN+ o/ U+00F8 -- LATIN SMALL LETTER O WITH STROKE+ u! U+00F9 -- LATIN SMALL LETTER U WITH GRAVE+ u' U+00FA -- LATIN SMALL LETTER U WITH ACUTE+ u> U+00FB -- LATIN SMALL LETTER U WITH CIRCUMFLEX+ u: U+00FC -- LATIN SMALL LETTER U WITH DIAERESIS+ y' U+00FD -- LATIN SMALL LETTER Y WITH ACUTE+ th U+00FE -- LATIN SMALL LETTER THORN (Icelandic)+ y: U+00FF -- LATIN SMALL LETTER Y WITH DIAERESIS+ A- U+0100 -- LATIN CAPITAL LETTER A WITH MACRON+ a- U+0101 -- LATIN SMALL LETTER A WITH MACRON+ A( U+0102 -- LATIN CAPITAL LETTER A WITH BREVE+ a( U+0103 -- LATIN SMALL LETTER A WITH BREVE+ A; U+0104 -- LATIN CAPITAL LETTER A WITH OGONEK+ a; U+0105 -- LATIN SMALL LETTER A WITH OGONEK+ C' U+0106 -- LATIN CAPITAL LETTER C WITH ACUTE+ c' U+0107 -- LATIN SMALL LETTER C WITH ACUTE+ C> U+0108 -- LATIN CAPITAL LETTER C WITH CIRCUMFLEX+ c> U+0109 -- LATIN SMALL LETTER C WITH CIRCUMFLEX+ C. U+010A -- LATIN CAPITAL LETTER C WITH DOT ABOVE+ c. U+010B -- LATIN SMALL LETTER C WITH DOT ABOVE+ C< U+010C -- LATIN CAPITAL LETTER C WITH CARON+ c< U+010D -- LATIN SMALL LETTER C WITH CARON+ D< U+010E -- LATIN CAPITAL LETTER D WITH CARON+ d< U+010F -- LATIN SMALL LETTER D WITH CARON+ D/ U+0110 -- LATIN CAPITAL LETTER D WITH STROKE+ d/ U+0111 -- LATIN SMALL LETTER D WITH STROKE+ E- U+0112 -- LATIN CAPITAL LETTER E WITH MACRON+ e- U+0113 -- LATIN SMALL LETTER E WITH MACRON+ E( U+0114 -- LATIN CAPITAL LETTER E WITH BREVE+ e( U+0115 -- LATIN SMALL LETTER E WITH BREVE+ E. U+0116 -- LATIN CAPITAL LETTER E WITH DOT ABOVE+ e. U+0117 -- LATIN SMALL LETTER E WITH DOT ABOVE+ E; U+0118 -- LATIN CAPITAL LETTER E WITH OGONEK+ e; U+0119 -- LATIN SMALL LETTER E WITH OGONEK+ E< U+011A -- LATIN CAPITAL LETTER E WITH CARON+ e< U+011B -- LATIN SMALL LETTER E WITH CARON+ G> U+011C -- LATIN CAPITAL LETTER G WITH CIRCUMFLEX+ g> U+011D -- LATIN SMALL LETTER G WITH CIRCUMFLEX+ G( U+011E -- LATIN CAPITAL LETTER G WITH BREVE+ g( U+011F -- LATIN SMALL LETTER G WITH BREVE+ G. U+0120 -- LATIN CAPITAL LETTER G WITH DOT ABOVE+ g. U+0121 -- LATIN SMALL LETTER G WITH DOT ABOVE+ G, U+0122 -- LATIN CAPITAL LETTER G WITH CEDILLA+ g, U+0123 -- LATIN SMALL LETTER G WITH CEDILLA+ H> U+0124 -- LATIN CAPITAL LETTER H WITH CIRCUMFLEX+ h> U+0125 -- LATIN SMALL LETTER H WITH CIRCUMFLEX+ H/ U+0126 -- LATIN CAPITAL LETTER H WITH STROKE+ h/ U+0127 -- LATIN SMALL LETTER H WITH STROKE+ I? U+0128 -- LATIN CAPITAL LETTER I WITH TILDE+ i? U+0129 -- LATIN SMALL LETTER I WITH TILDE+ I- U+012A -- LATIN CAPITAL LETTER I WITH MACRON+ i- U+012B -- LATIN SMALL LETTER I WITH MACRON+ I( U+012C -- LATIN CAPITAL LETTER I WITH BREVE+ i( U+012D -- LATIN SMALL LETTER I WITH BREVE+ I; U+012E -- LATIN CAPITAL LETTER I WITH OGONEK+ i; U+012F -- LATIN SMALL LETTER I WITH OGONEK+ I. U+0130 -- LATIN CAPITAL LETTER I WITH DOT ABOVE+ i. U+0131 -- LATIN SMALL LETTER DOTLESS I+ IJ U+0132 -- LATIN CAPITAL LIGATURE IJ+ ij U+0133 -- LATIN SMALL LIGATURE IJ+ J> U+0134 -- LATIN CAPITAL LETTER J WITH CIRCUMFLEX+ j> U+0135 -- LATIN SMALL LETTER J WITH CIRCUMFLEX+ K, U+0136 -- LATIN CAPITAL LETTER K WITH CEDILLA+ k, U+0137 -- LATIN SMALL LETTER K WITH CEDILLA+ kk U+0138 -- LATIN SMALL LETTER KRA+ L' U+0139 -- LATIN CAPITAL LETTER L WITH ACUTE+ l' U+013A -- LATIN SMALL LETTER L WITH ACUTE+ L, U+013B -- LATIN CAPITAL LETTER L WITH CEDILLA+ l, U+013C -- LATIN SMALL LETTER L WITH CEDILLA+ L< U+013D -- LATIN CAPITAL LETTER L WITH CARON+ l< U+013E -- LATIN SMALL LETTER L WITH CARON+ L. U+013F -- LATIN CAPITAL LETTER L WITH MIDDLE DOT+ l. U+0140 -- LATIN SMALL LETTER L WITH MIDDLE DOT+ L/ U+0141 -- LATIN CAPITAL LETTER L WITH STROKE+ l/ U+0142 -- LATIN SMALL LETTER L WITH STROKE+ N' U+0143 -- LATIN CAPITAL LETTER N WITH ACUTE `+ n' U+0144 -- LATIN SMALL LETTER N WITH ACUTE `+ N, U+0145 -- LATIN CAPITAL LETTER N WITH CEDILLA `+ n, U+0146 -- LATIN SMALL LETTER N WITH CEDILLA `+ N< U+0147 -- LATIN CAPITAL LETTER N WITH CARON `+ n< U+0148 -- LATIN SMALL LETTER N WITH CARON `+ 'n U+0149 -- LATIN SMALL LETTER N PRECEDED BY APOSTROPHE `+ NG U+014A -- LATIN CAPITAL LETTER ENG+ ng U+014B -- LATIN SMALL LETTER ENG+ O- U+014C -- LATIN CAPITAL LETTER O WITH MACRON+ o- U+014D -- LATIN SMALL LETTER O WITH MACRON+ O( U+014E -- LATIN CAPITAL LETTER O WITH BREVE+ o( U+014F -- LATIN SMALL LETTER O WITH BREVE+ O" U+0150 -- LATIN CAPITAL LETTER O WITH DOUBLE ACUTE+ o" U+0151 -- LATIN SMALL LETTER O WITH DOUBLE ACUTE+ OE U+0152 -- LATIN CAPITAL LIGATURE OE+ oe U+0153 -- LATIN SMALL LIGATURE OE+ R' U+0154 -- LATIN CAPITAL LETTER R WITH ACUTE+ r' U+0155 -- LATIN SMALL LETTER R WITH ACUTE+ R, U+0156 -- LATIN CAPITAL LETTER R WITH CEDILLA+ r, U+0157 -- LATIN SMALL LETTER R WITH CEDILLA+ R< U+0158 -- LATIN CAPITAL LETTER R WITH CARON+ r< U+0159 -- LATIN SMALL LETTER R WITH CARON+ S' U+015A -- LATIN CAPITAL LETTER S WITH ACUTE+ s' U+015B -- LATIN SMALL LETTER S WITH ACUTE+ S> U+015C -- LATIN CAPITAL LETTER S WITH CIRCUMFLEX+ s> U+015D -- LATIN SMALL LETTER S WITH CIRCUMFLEX+ S, U+015E -- LATIN CAPITAL LETTER S WITH CEDILLA+ s, U+015F -- LATIN SMALL LETTER S WITH CEDILLA+ S< U+0160 -- LATIN CAPITAL LETTER S WITH CARON+ s< U+0161 -- LATIN SMALL LETTER S WITH CARON+ T, U+0162 -- LATIN CAPITAL LETTER T WITH CEDILLA+ t, U+0163 -- LATIN SMALL LETTER T WITH CEDILLA+ T< U+0164 -- LATIN CAPITAL LETTER T WITH CARON+ t< U+0165 -- LATIN SMALL LETTER T WITH CARON+ T/ U+0166 -- LATIN CAPITAL LETTER T WITH STROKE+ t/ U+0167 -- LATIN SMALL LETTER T WITH STROKE+ U? U+0168 -- LATIN CAPITAL LETTER U WITH TILDE+ u? U+0169 -- LATIN SMALL LETTER U WITH TILDE+ U- U+016A -- LATIN CAPITAL LETTER U WITH MACRON+ u- U+016B -- LATIN SMALL LETTER U WITH MACRON+ U( U+016C -- LATIN CAPITAL LETTER U WITH BREVE+ u( U+016D -- LATIN SMALL LETTER U WITH BREVE+ U0 U+016E -- LATIN CAPITAL LETTER U WITH RING ABOVE+ u0 U+016F -- LATIN SMALL LETTER U WITH RING ABOVE+ U" U+0170 -- LATIN CAPITAL LETTER U WITH DOUBLE ACUTE+ u" U+0171 -- LATIN SMALL LETTER U WITH DOUBLE ACUTE+ U; U+0172 -- LATIN CAPITAL LETTER U WITH OGONEK+ u; U+0173 -- LATIN SMALL LETTER U WITH OGONEK+ W> U+0174 -- LATIN CAPITAL LETTER W WITH CIRCUMFLEX+ w> U+0175 -- LATIN SMALL LETTER W WITH CIRCUMFLEX+ Y> U+0176 -- LATIN CAPITAL LETTER Y WITH CIRCUMFLEX+ y> U+0177 -- LATIN SMALL LETTER Y WITH CIRCUMFLEX+ Y: U+0178 -- LATIN CAPITAL LETTER Y WITH DIAERESIS+ Z' U+0179 -- LATIN CAPITAL LETTER Z WITH ACUTE+ z' U+017A -- LATIN SMALL LETTER Z WITH ACUTE+ Z. U+017B -- LATIN CAPITAL LETTER Z WITH DOT ABOVE+ z. U+017C -- LATIN SMALL LETTER Z WITH DOT ABOVE+ Z< U+017D -- LATIN CAPITAL LETTER Z WITH CARON+ z< U+017E -- LATIN SMALL LETTER Z WITH CARON+ O9 U+01A0 -- LATIN CAPITAL LETTER O WITH HORN+ o9 U+01A1 -- LATIN SMALL LETTER O WITH HORN+ OI U+01A2 -- LATIN CAPITAL LETTER OI+ oi U+01A3 -- LATIN SMALL LETTER OI+ yr U+01A6 -- LATIN LETTER YR+ U9 U+01AF -- LATIN CAPITAL LETTER U WITH HORN+ u9 U+01B0 -- LATIN SMALL LETTER U WITH HORN+ Z/ U+01B5 -- LATIN CAPITAL LETTER Z WITH STROKE+ z/ U+01B6 -- LATIN SMALL LETTER Z WITH STROKE+ ED U+01B7 -- LATIN CAPITAL LETTER EZH+ A< U+01CD -- LATIN CAPITAL LETTER A WITH CARON+ a< U+01CE -- LATIN SMALL LETTER A WITH CARON+ I< U+01CF -- LATIN CAPITAL LETTER I WITH CARON+ i< U+01D0 -- LATIN SMALL LETTER I WITH CARON+ O< U+01D1 -- LATIN CAPITAL LETTER O WITH CARON+ o< U+01D2 -- LATIN SMALL LETTER O WITH CARON+ U< U+01D3 -- LATIN CAPITAL LETTER U WITH CARON+ u< U+01D4 -- LATIN SMALL LETTER U WITH CARON+ A1 U+01DE -- LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON+ a1 U+01DF -- LATIN SMALL LETTER A WITH DIAERESIS AND MACRON+ A7 U+01E0 -- LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON+ a7 U+01E1 -- LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON+ A3 U+01E2 -- LATIN CAPITAL LETTER AE WITH MACRON+ a3 U+01E3 -- LATIN SMALL LETTER AE WITH MACRON+ G/ U+01E4 -- LATIN CAPITAL LETTER G WITH STROKE+ g/ U+01E5 -- LATIN SMALL LETTER G WITH STROKE+ G< U+01E6 -- LATIN CAPITAL LETTER G WITH CARON+ g< U+01E7 -- LATIN SMALL LETTER G WITH CARON+ K< U+01E8 -- LATIN CAPITAL LETTER K WITH CARON+ k< U+01E9 -- LATIN SMALL LETTER K WITH CARON+ O; U+01EA -- LATIN CAPITAL LETTER O WITH OGONEK+ o; U+01EB -- LATIN SMALL LETTER O WITH OGONEK+ O1 U+01EC -- LATIN CAPITAL LETTER O WITH OGONEK AND MACRON+ o1 U+01ED -- LATIN SMALL LETTER O WITH OGONEK AND MACRON+ EZ U+01EE -- LATIN CAPITAL LETTER EZH WITH CARON+ ez U+01EF -- LATIN SMALL LETTER EZH WITH CARON+ j< U+01F0 -- LATIN SMALL LETTER J WITH CARON+ G' U+01F4 -- LATIN CAPITAL LETTER G WITH ACUTE+ g' U+01F5 -- LATIN SMALL LETTER G WITH ACUTE+ ;S U+02BF -- MODIFIER LETTER LEFT HALF RING+ '< U+02C7 -- CARON+ '( U+02D8 -- BREVE+ '. U+02D9 -- DOT ABOVE+ '0 U+02DA -- RING ABOVE+ '; U+02DB -- OGONEK+ '" U+02DD -- DOUBLE ACUTE ACCENT+ A% U+0386 -- GREEK CAPITAL LETTER ALPHA WITH TONOS+ E% U+0388 -- GREEK CAPITAL LETTER EPSILON WITH TONOS+ Y% U+0389 -- GREEK CAPITAL LETTER ETA WITH TONOS+ I% U+038A -- GREEK CAPITAL LETTER IOTA WITH TONOS+ O% U+038C -- GREEK CAPITAL LETTER OMICRON WITH TONOS+ U% U+038E -- GREEK CAPITAL LETTER UPSILON WITH TONOS+ W% U+038F -- GREEK CAPITAL LETTER OMEGA WITH TONOS+ i3 U+0390 -- GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS+ A* U+0391 -- GREEK CAPITAL LETTER ALPHA+ B* U+0392 -- GREEK CAPITAL LETTER BETA+ G* U+0393 -- GREEK CAPITAL LETTER GAMMA+ D* U+0394 -- GREEK CAPITAL LETTER DELTA+ E* U+0395 -- GREEK CAPITAL LETTER EPSILON+ Z* U+0396 -- GREEK CAPITAL LETTER ZETA+ Y* U+0397 -- GREEK CAPITAL LETTER ETA+ H* U+0398 -- GREEK CAPITAL LETTER THETA+ I* U+0399 -- GREEK CAPITAL LETTER IOTA+ K* U+039A -- GREEK CAPITAL LETTER KAPPA+ L* U+039B -- GREEK CAPITAL LETTER LAMDA+ M* U+039C -- GREEK CAPITAL LETTER MU+ N* U+039D -- GREEK CAPITAL LETTER NU+ C* U+039E -- GREEK CAPITAL LETTER XI+ O* U+039F -- GREEK CAPITAL LETTER OMICRON+ P* U+03A0 -- GREEK CAPITAL LETTER PI+ R* U+03A1 -- GREEK CAPITAL LETTER RHO+ S* U+03A3 -- GREEK CAPITAL LETTER SIGMA+ T* U+03A4 -- GREEK CAPITAL LETTER TAU+ U* U+03A5 -- GREEK CAPITAL LETTER UPSILON+ F* U+03A6 -- GREEK CAPITAL LETTER PHI+ X* U+03A7 -- GREEK CAPITAL LETTER CHI+ Q* U+03A8 -- GREEK CAPITAL LETTER PSI+ W* U+03A9 -- GREEK CAPITAL LETTER OMEGA+ J* U+03AA -- GREEK CAPITAL LETTER IOTA WITH DIALYTIKA+ V* U+03AB -- GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA+ a% U+03AC -- GREEK SMALL LETTER ALPHA WITH TONOS+ e% U+03AD -- GREEK SMALL LETTER EPSILON WITH TONOS+ y% U+03AE -- GREEK SMALL LETTER ETA WITH TONOS+ i% U+03AF -- GREEK SMALL LETTER IOTA WITH TONOS+ u3 U+03B0 -- GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS+ a* U+03B1 -- GREEK SMALL LETTER ALPHA+ b* U+03B2 -- GREEK SMALL LETTER BETA+ g* U+03B3 -- GREEK SMALL LETTER GAMMA+ d* U+03B4 -- GREEK SMALL LETTER DELTA+ e* U+03B5 -- GREEK SMALL LETTER EPSILON+ z* U+03B6 -- GREEK SMALL LETTER ZETA+ y* U+03B7 -- GREEK SMALL LETTER ETA+ h* U+03B8 -- GREEK SMALL LETTER THETA+ i* U+03B9 -- GREEK SMALL LETTER IOTA+ k* U+03BA -- GREEK SMALL LETTER KAPPA+ l* U+03BB -- GREEK SMALL LETTER LAMDA+ m* U+03BC -- GREEK SMALL LETTER MU+ n* U+03BD -- GREEK SMALL LETTER NU+ c* U+03BE -- GREEK SMALL LETTER XI+ o* U+03BF -- GREEK SMALL LETTER OMICRON+ p* U+03C0 -- GREEK SMALL LETTER PI+ r* U+03C1 -- GREEK SMALL LETTER RHO+ *s U+03C2 -- GREEK SMALL LETTER FINAL SIGMA+ s* U+03C3 -- GREEK SMALL LETTER SIGMA+ t* U+03C4 -- GREEK SMALL LETTER TAU+ u* U+03C5 -- GREEK SMALL LETTER UPSILON+ f* U+03C6 -- GREEK SMALL LETTER PHI+ x* U+03C7 -- GREEK SMALL LETTER CHI+ q* U+03C8 -- GREEK SMALL LETTER PSI+ w* U+03C9 -- GREEK SMALL LETTER OMEGA+ j* U+03CA -- GREEK SMALL LETTER IOTA WITH DIALYTIKA+ v* U+03CB -- GREEK SMALL LETTER UPSILON WITH DIALYTIKA+ o% U+03CC -- GREEK SMALL LETTER OMICRON WITH TONOS+ u% U+03CD -- GREEK SMALL LETTER UPSILON WITH TONOS+ w% U+03CE -- GREEK SMALL LETTER OMEGA WITH TONOS+ 'G U+03D8 -- GREEK LETTER ARCHAIC KOPPA+ ,G U+03D9 -- GREEK SMALL LETTER ARCHAIC KOPPA+ T3 U+03DA -- GREEK LETTER STIGMA+ t3 U+03DB -- GREEK SMALL LETTER STIGMA+ M3 U+03DC -- GREEK LETTER DIGAMMA+ m3 U+03DD -- GREEK SMALL LETTER DIGAMMA+ K3 U+03DE -- GREEK LETTER KOPPA+ k3 U+03DF -- GREEK SMALL LETTER KOPPA+ P3 U+03E0 -- GREEK LETTER SAMPI+ p3 U+03E1 -- GREEK SMALL LETTER SAMPI+ '% U+03F4 -- GREEK CAPITAL THETA SYMBOL+ j3 U+03F5 -- GREEK LUNATE EPSILON SYMBOL+ IO U+0401 -- CYRILLIC CAPITAL LETTER IO+ D% U+0402 -- CYRILLIC CAPITAL LETTER DJE+ G% U+0403 -- CYRILLIC CAPITAL LETTER GJE+ IE U+0404 -- CYRILLIC CAPITAL LETTER UKRAINIAN IE+ DS U+0405 -- CYRILLIC CAPITAL LETTER DZE+ II U+0406 -- CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I+ YI U+0407 -- CYRILLIC CAPITAL LETTER YI+ J% U+0408 -- CYRILLIC CAPITAL LETTER JE+ LJ U+0409 -- CYRILLIC CAPITAL LETTER LJE+ NJ U+040A -- CYRILLIC CAPITAL LETTER NJE+ Ts U+040B -- CYRILLIC CAPITAL LETTER TSHE+ KJ U+040C -- CYRILLIC CAPITAL LETTER KJE+ V% U+040E -- CYRILLIC CAPITAL LETTER SHORT U+ DZ U+040F -- CYRILLIC CAPITAL LETTER DZHE+ A= U+0410 -- CYRILLIC CAPITAL LETTER A+ B= U+0411 -- CYRILLIC CAPITAL LETTER BE+ V= U+0412 -- CYRILLIC CAPITAL LETTER VE+ G= U+0413 -- CYRILLIC CAPITAL LETTER GHE+ D= U+0414 -- CYRILLIC CAPITAL LETTER DE+ E= U+0415 -- CYRILLIC CAPITAL LETTER IE+ Z% U+0416 -- CYRILLIC CAPITAL LETTER ZHE+ Z= U+0417 -- CYRILLIC CAPITAL LETTER ZE+ I= U+0418 -- CYRILLIC CAPITAL LETTER I+ J= U+0419 -- CYRILLIC CAPITAL LETTER SHORT I+ K= U+041A -- CYRILLIC CAPITAL LETTER KA+ L= U+041B -- CYRILLIC CAPITAL LETTER EL+ M= U+041C -- CYRILLIC CAPITAL LETTER EM+ N= U+041D -- CYRILLIC CAPITAL LETTER EN+ O= U+041E -- CYRILLIC CAPITAL LETTER O+ P= U+041F -- CYRILLIC CAPITAL LETTER PE+ R= U+0420 -- CYRILLIC CAPITAL LETTER ER+ S= U+0421 -- CYRILLIC CAPITAL LETTER ES+ T= U+0422 -- CYRILLIC CAPITAL LETTER TE+ U= U+0423 -- CYRILLIC CAPITAL LETTER U+ F= U+0424 -- CYRILLIC CAPITAL LETTER EF+ H= U+0425 -- CYRILLIC CAPITAL LETTER HA+ C= U+0426 -- CYRILLIC CAPITAL LETTER TSE+ C% U+0427 -- CYRILLIC CAPITAL LETTER CHE+ S% U+0428 -- CYRILLIC CAPITAL LETTER SHA+ Sc U+0429 -- CYRILLIC CAPITAL LETTER SHCHA+ =' U+042A -- CYRILLIC CAPITAL LETTER HARD SIGN+ Y= U+042B -- CYRILLIC CAPITAL LETTER YERU+ %' U+042C -- CYRILLIC CAPITAL LETTER SOFT SIGN+ JE U+042D -- CYRILLIC CAPITAL LETTER E+ JU U+042E -- CYRILLIC CAPITAL LETTER YU+ JA U+042F -- CYRILLIC CAPITAL LETTER YA+ a= U+0430 -- CYRILLIC SMALL LETTER A+ b= U+0431 -- CYRILLIC SMALL LETTER BE+ v= U+0432 -- CYRILLIC SMALL LETTER VE+ g= U+0433 -- CYRILLIC SMALL LETTER GHE+ d= U+0434 -- CYRILLIC SMALL LETTER DE+ e= U+0435 -- CYRILLIC SMALL LETTER IE+ z% U+0436 -- CYRILLIC SMALL LETTER ZHE+ z= U+0437 -- CYRILLIC SMALL LETTER ZE+ i= U+0438 -- CYRILLIC SMALL LETTER I+ j= U+0439 -- CYRILLIC SMALL LETTER SHORT I+ k= U+043A -- CYRILLIC SMALL LETTER KA+ l= U+043B -- CYRILLIC SMALL LETTER EL+ m= U+043C -- CYRILLIC SMALL LETTER EM+ n= U+043D -- CYRILLIC SMALL LETTER EN+ o= U+043E -- CYRILLIC SMALL LETTER O+ p= U+043F -- CYRILLIC SMALL LETTER PE+ r= U+0440 -- CYRILLIC SMALL LETTER ER+ s= U+0441 -- CYRILLIC SMALL LETTER ES+ t= U+0442 -- CYRILLIC SMALL LETTER TE+ u= U+0443 -- CYRILLIC SMALL LETTER U+ f= U+0444 -- CYRILLIC SMALL LETTER EF+ h= U+0445 -- CYRILLIC SMALL LETTER HA+ c= U+0446 -- CYRILLIC SMALL LETTER TSE+ c% U+0447 -- CYRILLIC SMALL LETTER CHE+ s% U+0448 -- CYRILLIC SMALL LETTER SHA+ sc U+0449 -- CYRILLIC SMALL LETTER SHCHA+ =' U+044A -- CYRILLIC SMALL LETTER HARD SIGN+ y= U+044B -- CYRILLIC SMALL LETTER YERU+ %' U+044C -- CYRILLIC SMALL LETTER SOFT SIGN+ je U+044D -- CYRILLIC SMALL LETTER E+ ju U+044E -- CYRILLIC SMALL LETTER YU+ ja U+044F -- CYRILLIC SMALL LETTER YA+ io U+0451 -- CYRILLIC SMALL LETTER IO+ d% U+0452 -- CYRILLIC SMALL LETTER DJE+ g% U+0453 -- CYRILLIC SMALL LETTER GJE+ ie U+0454 -- CYRILLIC SMALL LETTER UKRAINIAN IE+ ds U+0455 -- CYRILLIC SMALL LETTER DZE+ ii U+0456 -- CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I+ yi U+0457 -- CYRILLIC SMALL LETTER YI+ j% U+0458 -- CYRILLIC SMALL LETTER JE+ lj U+0459 -- CYRILLIC SMALL LETTER LJE+ nj U+045A -- CYRILLIC SMALL LETTER NJE+ ts U+045B -- CYRILLIC SMALL LETTER TSHE+ kj U+045C -- CYRILLIC SMALL LETTER KJE+ v% U+045E -- CYRILLIC SMALL LETTER SHORT U+ dz U+045F -- CYRILLIC SMALL LETTER DZHE+ Y3 U+0462 -- CYRILLIC CAPITAL LETTER YAT+ y3 U+0463 -- CYRILLIC SMALL LETTER YAT+ O3 U+046A -- CYRILLIC CAPITAL LETTER BIG YUS+ o3 U+046B -- CYRILLIC SMALL LETTER BIG YUS+ F3 U+0472 -- CYRILLIC CAPITAL LETTER FITA+ f3 U+0473 -- CYRILLIC SMALL LETTER FITA+ V3 U+0474 -- CYRILLIC CAPITAL LETTER IZHITSA+ v3 U+0475 -- CYRILLIC SMALL LETTER IZHITSA+ C3 U+0480 -- CYRILLIC CAPITAL LETTER KOPPA+ c3 U+0481 -- CYRILLIC SMALL LETTER KOPPA+ G3 U+0490 -- CYRILLIC CAPITAL LETTER GHE WITH UPTURN+ g3 U+0491 -- CYRILLIC SMALL LETTER GHE WITH UPTURN+ A+ U+05D0 -- HEBREW LETTER ALEF+ B+ U+05D1 -- HEBREW LETTER BET+ G+ U+05D2 -- HEBREW LETTER GIMEL+ D+ U+05D3 -- HEBREW LETTER DALET+ H+ U+05D4 -- HEBREW LETTER HE+ W+ U+05D5 -- HEBREW LETTER VAV+ Z+ U+05D6 -- HEBREW LETTER ZAYIN+ X+ U+05D7 -- HEBREW LETTER HET+ Tj U+05D8 -- HEBREW LETTER TET+ J+ U+05D9 -- HEBREW LETTER YOD+ K% U+05DA -- HEBREW LETTER FINAL KAF+ K+ U+05DB -- HEBREW LETTER KAF+ L+ U+05DC -- HEBREW LETTER LAMED+ M% U+05DD -- HEBREW LETTER FINAL MEM+ M+ U+05DE -- HEBREW LETTER MEM+ N% U+05DF -- HEBREW LETTER FINAL NUN `+ N+ U+05E0 -- HEBREW LETTER NUN `+ S+ U+05E1 -- HEBREW LETTER SAMEKH+ E+ U+05E2 -- HEBREW LETTER AYIN+ P% U+05E3 -- HEBREW LETTER FINAL PE+ P+ U+05E4 -- HEBREW LETTER PE+ Zj U+05E5 -- HEBREW LETTER FINAL TSADI+ ZJ U+05E6 -- HEBREW LETTER TSADI+ Q+ U+05E7 -- HEBREW LETTER QOF+ R+ U+05E8 -- HEBREW LETTER RESH+ Sh U+05E9 -- HEBREW LETTER SHIN+ T+ U+05EA -- HEBREW LETTER TAV+ ,+ U+060C -- ARABIC COMMA+ ;+ U+061B -- ARABIC SEMICOLON+ ?+ U+061F -- ARABIC QUESTION MARK+ H' U+0621 -- ARABIC LETTER HAMZA+ aM U+0622 -- ARABIC LETTER ALEF WITH MADDA ABOVE+ aH U+0623 -- ARABIC LETTER ALEF WITH HAMZA ABOVE+ wH U+0624 -- ARABIC LETTER WAW WITH HAMZA ABOVE+ ah U+0625 -- ARABIC LETTER ALEF WITH HAMZA BELOW+ yH U+0626 -- ARABIC LETTER YEH WITH HAMZA ABOVE+ a+ U+0627 -- ARABIC LETTER ALEF+ b+ U+0628 -- ARABIC LETTER BEH+ tm U+0629 -- ARABIC LETTER TEH MARBUTA+ t+ U+062A -- ARABIC LETTER TEH+ tk U+062B -- ARABIC LETTER THEH+ g+ U+062C -- ARABIC LETTER JEEM+ hk U+062D -- ARABIC LETTER HAH+ x+ U+062E -- ARABIC LETTER KHAH+ d+ U+062F -- ARABIC LETTER DAL+ dk U+0630 -- ARABIC LETTER THAL+ r+ U+0631 -- ARABIC LETTER REH+ z+ U+0632 -- ARABIC LETTER ZAIN+ s+ U+0633 -- ARABIC LETTER SEEN+ sn U+0634 -- ARABIC LETTER SHEEN+ c+ U+0635 -- ARABIC LETTER SAD+ dd U+0636 -- ARABIC LETTER DAD+ tj U+0637 -- ARABIC LETTER TAH+ zH U+0638 -- ARABIC LETTER ZAH+ e+ U+0639 -- ARABIC LETTER AIN+ i+ U+063A -- ARABIC LETTER GHAIN+ ++ U+0640 -- ARABIC TATWEEL+ f+ U+0641 -- ARABIC LETTER FEH+ q+ U+0642 -- ARABIC LETTER QAF+ k+ U+0643 -- ARABIC LETTER KAF+ l+ U+0644 -- ARABIC LETTER LAM+ m+ U+0645 -- ARABIC LETTER MEEM+ n+ U+0646 -- ARABIC LETTER NOON+ h+ U+0647 -- ARABIC LETTER HEH+ w+ U+0648 -- ARABIC LETTER WAW+ j+ U+0649 -- ARABIC LETTER ALEF MAKSURA+ y+ U+064A -- ARABIC LETTER YEH+ :+ U+064B -- ARABIC FATHATAN+ "+ U+064C -- ARABIC DAMMATAN+ =+ U+064D -- ARABIC KASRATAN+ /+ U+064E -- ARABIC FATHA+ '+ U+064F -- ARABIC DAMMA+ 1+ U+0650 -- ARABIC KASRA+ 3+ U+0651 -- ARABIC SHADDA+ 0+ U+0652 -- ARABIC SUKUN+ aS U+0670 -- ARABIC LETTER SUPERSCRIPT ALEF+ p+ U+067E -- ARABIC LETTER PEH+ v+ U+06A4 -- ARABIC LETTER VEH+ gf U+06AF -- ARABIC LETTER GAF+ 0a U+06F0 -- EXTENDED ARABIC-INDIC DIGIT ZERO+ 1a U+06F1 -- EXTENDED ARABIC-INDIC DIGIT ONE+ 2a U+06F2 -- EXTENDED ARABIC-INDIC DIGIT TWO+ 3a U+06F3 -- EXTENDED ARABIC-INDIC DIGIT THREE+ 4a U+06F4 -- EXTENDED ARABIC-INDIC DIGIT FOUR+ 5a U+06F5 -- EXTENDED ARABIC-INDIC DIGIT FIVE+ 6a U+06F6 -- EXTENDED ARABIC-INDIC DIGIT SIX+ 7a U+06F7 -- EXTENDED ARABIC-INDIC DIGIT SEVEN+ 8a U+06F8 -- EXTENDED ARABIC-INDIC DIGIT EIGHT+ 9a U+06F9 -- EXTENDED ARABIC-INDIC DIGIT NINE+ B. U+1E02 -- LATIN CAPITAL LETTER B WITH DOT ABOVE+ b. U+1E03 -- LATIN SMALL LETTER B WITH DOT ABOVE+ B_ U+1E06 -- LATIN CAPITAL LETTER B WITH LINE BELOW+ b_ U+1E07 -- LATIN SMALL LETTER B WITH LINE BELOW+ D. U+1E0A -- LATIN CAPITAL LETTER D WITH DOT ABOVE+ d. U+1E0B -- LATIN SMALL LETTER D WITH DOT ABOVE+ D_ U+1E0E -- LATIN CAPITAL LETTER D WITH LINE BELOW+ d_ U+1E0F -- LATIN SMALL LETTER D WITH LINE BELOW+ D, U+1E10 -- LATIN CAPITAL LETTER D WITH CEDILLA+ d, U+1E11 -- LATIN SMALL LETTER D WITH CEDILLA+ F. U+1E1E -- LATIN CAPITAL LETTER F WITH DOT ABOVE+ f. U+1E1F -- LATIN SMALL LETTER F WITH DOT ABOVE+ G- U+1E20 -- LATIN CAPITAL LETTER G WITH MACRON+ g- U+1E21 -- LATIN SMALL LETTER G WITH MACRON+ H. U+1E22 -- LATIN CAPITAL LETTER H WITH DOT ABOVE+ h. U+1E23 -- LATIN SMALL LETTER H WITH DOT ABOVE+ H: U+1E26 -- LATIN CAPITAL LETTER H WITH DIAERESIS+ h: U+1E27 -- LATIN SMALL LETTER H WITH DIAERESIS+ H, U+1E28 -- LATIN CAPITAL LETTER H WITH CEDILLA+ h, U+1E29 -- LATIN SMALL LETTER H WITH CEDILLA+ K' U+1E30 -- LATIN CAPITAL LETTER K WITH ACUTE+ k' U+1E31 -- LATIN SMALL LETTER K WITH ACUTE+ K_ U+1E34 -- LATIN CAPITAL LETTER K WITH LINE BELOW+ k_ U+1E35 -- LATIN SMALL LETTER K WITH LINE BELOW+ L_ U+1E3A -- LATIN CAPITAL LETTER L WITH LINE BELOW+ l_ U+1E3B -- LATIN SMALL LETTER L WITH LINE BELOW+ M' U+1E3E -- LATIN CAPITAL LETTER M WITH ACUTE+ m' U+1E3F -- LATIN SMALL LETTER M WITH ACUTE+ M. U+1E40 -- LATIN CAPITAL LETTER M WITH DOT ABOVE+ m. U+1E41 -- LATIN SMALL LETTER M WITH DOT ABOVE+ N. U+1E44 -- LATIN CAPITAL LETTER N WITH DOT ABOVE `+ n. U+1E45 -- LATIN SMALL LETTER N WITH DOT ABOVE `+ N_ U+1E48 -- LATIN CAPITAL LETTER N WITH LINE BELOW `+ n_ U+1E49 -- LATIN SMALL LETTER N WITH LINE BELOW `+ P' U+1E54 -- LATIN CAPITAL LETTER P WITH ACUTE+ p' U+1E55 -- LATIN SMALL LETTER P WITH ACUTE+ P. U+1E56 -- LATIN CAPITAL LETTER P WITH DOT ABOVE+ p. U+1E57 -- LATIN SMALL LETTER P WITH DOT ABOVE+ R. U+1E58 -- LATIN CAPITAL LETTER R WITH DOT ABOVE+ r. U+1E59 -- LATIN SMALL LETTER R WITH DOT ABOVE+ R_ U+1E5E -- LATIN CAPITAL LETTER R WITH LINE BELOW+ r_ U+1E5F -- LATIN SMALL LETTER R WITH LINE BELOW+ S. U+1E60 -- LATIN CAPITAL LETTER S WITH DOT ABOVE+ s. U+1E61 -- LATIN SMALL LETTER S WITH DOT ABOVE+ T. U+1E6A -- LATIN CAPITAL LETTER T WITH DOT ABOVE+ t. U+1E6B -- LATIN SMALL LETTER T WITH DOT ABOVE+ T_ U+1E6E -- LATIN CAPITAL LETTER T WITH LINE BELOW+ t_ U+1E6F -- LATIN SMALL LETTER T WITH LINE BELOW+ V? U+1E7C -- LATIN CAPITAL LETTER V WITH TILDE+ v? U+1E7D -- LATIN SMALL LETTER V WITH TILDE+ W! U+1E80 -- LATIN CAPITAL LETTER W WITH GRAVE+ w! U+1E81 -- LATIN SMALL LETTER W WITH GRAVE+ W' U+1E82 -- LATIN CAPITAL LETTER W WITH ACUTE+ w' U+1E83 -- LATIN SMALL LETTER W WITH ACUTE+ W: U+1E84 -- LATIN CAPITAL LETTER W WITH DIAERESIS+ w: U+1E85 -- LATIN SMALL LETTER W WITH DIAERESIS+ W. U+1E86 -- LATIN CAPITAL LETTER W WITH DOT ABOVE+ w. U+1E87 -- LATIN SMALL LETTER W WITH DOT ABOVE+ X. U+1E8A -- LATIN CAPITAL LETTER X WITH DOT ABOVE+ x. U+1E8B -- LATIN SMALL LETTER X WITH DOT ABOVE+ X: U+1E8C -- LATIN CAPITAL LETTER X WITH DIAERESIS+ x: U+1E8D -- LATIN SMALL LETTER X WITH DIAERESIS+ Y. U+1E8E -- LATIN CAPITAL LETTER Y WITH DOT ABOVE+ y. U+1E8F -- LATIN SMALL LETTER Y WITH DOT ABOVE+ Z> U+1E90 -- LATIN CAPITAL LETTER Z WITH CIRCUMFLEX+ z> U+1E91 -- LATIN SMALL LETTER Z WITH CIRCUMFLEX+ Z_ U+1E94 -- LATIN CAPITAL LETTER Z WITH LINE BELOW+ z_ U+1E95 -- LATIN SMALL LETTER Z WITH LINE BELOW+ h_ U+1E96 -- LATIN SMALL LETTER H WITH LINE BELOW+ t: U+1E97 -- LATIN SMALL LETTER T WITH DIAERESIS+ w0 U+1E98 -- LATIN SMALL LETTER W WITH RING ABOVE+ y0 U+1E99 -- LATIN SMALL LETTER Y WITH RING ABOVE+ A2 U+1EA2 -- LATIN CAPITAL LETTER A WITH HOOK ABOVE+ a2 U+1EA3 -- LATIN SMALL LETTER A WITH HOOK ABOVE+ E2 U+1EBA -- LATIN CAPITAL LETTER E WITH HOOK ABOVE+ e2 U+1EBB -- LATIN SMALL LETTER E WITH HOOK ABOVE+ E? U+1EBC -- LATIN CAPITAL LETTER E WITH TILDE+ e? U+1EBD -- LATIN SMALL LETTER E WITH TILDE+ I2 U+1EC8 -- LATIN CAPITAL LETTER I WITH HOOK ABOVE+ i2 U+1EC9 -- LATIN SMALL LETTER I WITH HOOK ABOVE+ O2 U+1ECE -- LATIN CAPITAL LETTER O WITH HOOK ABOVE+ o2 U+1ECF -- LATIN SMALL LETTER O WITH HOOK ABOVE+ U2 U+1EE6 -- LATIN CAPITAL LETTER U WITH HOOK ABOVE+ u2 U+1EE7 -- LATIN SMALL LETTER U WITH HOOK ABOVE+ Y! U+1EF2 -- LATIN CAPITAL LETTER Y WITH GRAVE+ y! U+1EF3 -- LATIN SMALL LETTER Y WITH GRAVE+ Y2 U+1EF6 -- LATIN CAPITAL LETTER Y WITH HOOK ABOVE+ y2 U+1EF7 -- LATIN SMALL LETTER Y WITH HOOK ABOVE+ Y? U+1EF8 -- LATIN CAPITAL LETTER Y WITH TILDE+ y? U+1EF9 -- LATIN SMALL LETTER Y WITH TILDE+ ;' U+1F00 -- GREEK SMALL LETTER ALPHA WITH PSILI+ ,' U+1F01 -- GREEK SMALL LETTER ALPHA WITH DASIA+ ;! U+1F02 -- GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA+ ,! U+1F03 -- GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA+ ?; U+1F04 -- GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA+ ?, U+1F05 -- GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA+ !: U+1F06 -- GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI+ ?: U+1F07 -- GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI+ 1N U+2002 -- EN SPACE+ 1M U+2003 -- EM SPACE+ 3M U+2004 -- THREE-PER-EM SPACE+ 4M U+2005 -- FOUR-PER-EM SPACE+ 6M U+2006 -- SIX-PER-EM SPACE+ 1T U+2009 -- THIN SPACE+ 1H U+200A -- HAIR SPACE+ -1 U+2010 -- HYPHEN+ -N U+2013 -- EN DASH `+ -M U+2014 -- EM DASH+ -3 U+2015 -- HORIZONTAL BAR+ !2 U+2016 -- DOUBLE VERTICAL LINE+ =2 U+2017 -- DOUBLE LOW LINE+ '6 U+2018 -- LEFT SINGLE QUOTATION MARK+ '9 U+2019 -- RIGHT SINGLE QUOTATION MARK+ .9 U+201A -- SINGLE LOW-9 QUOTATION MARK+ 9' U+201B -- SINGLE HIGH-REVERSED-9 QUOTATION MARK+ "6 U+201C -- LEFT DOUBLE QUOTATION MARK+ "9 U+201D -- RIGHT DOUBLE QUOTATION MARK+ :9 U+201E -- DOUBLE LOW-9 QUOTATION MARK+ 9" U+201F -- DOUBLE HIGH-REVERSED-9 QUOTATION MARK+ /- U+2020 -- DAGGER+ /= U+2021 -- DOUBLE DAGGER+ .. U+2025 -- TWO DOT LEADER+ %0 U+2030 -- PER MILLE SIGN+ 1' U+2032 -- PRIME+ 2' U+2033 -- DOUBLE PRIME+ 3' U+2034 -- TRIPLE PRIME+ 1" U+2035 -- REVERSED PRIME+ 2" U+2036 -- REVERSED DOUBLE PRIME+ 3" U+2037 -- REVERSED TRIPLE PRIME+ Ca U+2038 -- CARET+ <1 U+2039 -- SINGLE LEFT-POINTING ANGLE QUOTATION MARK+ >1 U+203A -- SINGLE RIGHT-POINTING ANGLE QUOTATION MARK+ :X U+203B -- REFERENCE MARK+ '- U+203E -- OVERLINE+ /f U+2044 -- FRACTION SLASH+ 0S U+2070 -- SUPERSCRIPT ZERO+ 4S U+2074 -- SUPERSCRIPT FOUR+ 5S U+2075 -- SUPERSCRIPT FIVE+ 6S U+2076 -- SUPERSCRIPT SIX+ 7S U+2077 -- SUPERSCRIPT SEVEN+ 8S U+2078 -- SUPERSCRIPT EIGHT+ 9S U+2079 -- SUPERSCRIPT NINE+ +S U+207A -- SUPERSCRIPT PLUS SIGN+ -S U+207B -- SUPERSCRIPT MINUS+ =S U+207C -- SUPERSCRIPT EQUALS SIGN+ (S U+207D -- SUPERSCRIPT LEFT PARENTHESIS+ )S U+207E -- SUPERSCRIPT RIGHT PARENTHESIS+ nS U+207F -- SUPERSCRIPT LATIN SMALL LETTER N `+ 0s U+2080 -- SUBSCRIPT ZERO+ 1s U+2081 -- SUBSCRIPT ONE+ 2s U+2082 -- SUBSCRIPT TWO+ 3s U+2083 -- SUBSCRIPT THREE+ 4s U+2084 -- SUBSCRIPT FOUR+ 5s U+2085 -- SUBSCRIPT FIVE+ 6s U+2086 -- SUBSCRIPT SIX+ 7s U+2087 -- SUBSCRIPT SEVEN+ 8s U+2088 -- SUBSCRIPT EIGHT+ 9s U+2089 -- SUBSCRIPT NINE+ +s U+208A -- SUBSCRIPT PLUS SIGN+ -s U+208B -- SUBSCRIPT MINUS+ =s U+208C -- SUBSCRIPT EQUALS SIGN+ (s U+208D -- SUBSCRIPT LEFT PARENTHESIS+ )s U+208E -- SUBSCRIPT RIGHT PARENTHESIS+ Li U+20A4 -- LIRA SIGN+ Pt U+20A7 -- PESETA SIGN+ W= U+20A9 -- WON SIGN+ Eu U+20AC -- EURO SIGN+ oC U+2103 -- DEGREE CELSIUS+ co U+2105 -- CARE OF+ oF U+2109 -- DEGREE FAHRENHEIT+ N0 U+2116 -- NUMERO SIGN+ PO U+2117 -- SOUND RECORDING COPYRIGHT+ Rx U+211E -- PRESCRIPTION TAKE+ SM U+2120 -- SERVICE MARK+ TM U+2122 -- TRADE MARK SIGN+ Om U+2126 -- OHM SIGN+ AO U+212B -- ANGSTROM SIGN+ 13 U+2153 -- VULGAR FRACTION ONE THIRD+ 23 U+2154 -- VULGAR FRACTION TWO THIRDS+ 15 U+2155 -- VULGAR FRACTION ONE FIFTH+ 25 U+2156 -- VULGAR FRACTION TWO FIFTHS+ 35 U+2157 -- VULGAR FRACTION THREE FIFTHS+ 45 U+2158 -- VULGAR FRACTION FOUR FIFTHS+ 16 U+2159 -- VULGAR FRACTION ONE SIXTH+ 56 U+215A -- VULGAR FRACTION FIVE SIXTHS+ 18 U+215B -- VULGAR FRACTION ONE EIGHTH+ 38 U+215C -- VULGAR FRACTION THREE EIGHTHS+ 58 U+215D -- VULGAR FRACTION FIVE EIGHTHS+ 78 U+215E -- VULGAR FRACTION SEVEN EIGHTHS+ 1R U+2160 -- ROMAN NUMERAL ONE+ 2R U+2161 -- ROMAN NUMERAL TWO+ 3R U+2162 -- ROMAN NUMERAL THREE+ 4R U+2163 -- ROMAN NUMERAL FOUR+ 5R U+2164 -- ROMAN NUMERAL FIVE+ 6R U+2165 -- ROMAN NUMERAL SIX+ 7R U+2166 -- ROMAN NUMERAL SEVEN+ 8R U+2167 -- ROMAN NUMERAL EIGHT+ 9R U+2168 -- ROMAN NUMERAL NINE+ aR U+2169 -- ROMAN NUMERAL TEN+ bR U+216A -- ROMAN NUMERAL ELEVEN+ cR U+216B -- ROMAN NUMERAL TWELVE+ 1r U+2170 -- SMALL ROMAN NUMERAL ONE+ 2r U+2171 -- SMALL ROMAN NUMERAL TWO+ 3r U+2172 -- SMALL ROMAN NUMERAL THREE+ 4r U+2173 -- SMALL ROMAN NUMERAL FOUR+ 5r U+2174 -- SMALL ROMAN NUMERAL FIVE+ 6r U+2175 -- SMALL ROMAN NUMERAL SIX+ 7r U+2176 -- SMALL ROMAN NUMERAL SEVEN+ 8r U+2177 -- SMALL ROMAN NUMERAL EIGHT+ 9r U+2178 -- SMALL ROMAN NUMERAL NINE+ ar U+2179 -- SMALL ROMAN NUMERAL TEN+ br U+217A -- SMALL ROMAN NUMERAL ELEVEN+ cr U+217B -- SMALL ROMAN NUMERAL TWELVE+ <- U+2190 -- LEFTWARDS ARROW+ -! U+2191 -- UPWARDS ARROW+ -> U+2192 -- RIGHTWARDS ARROW+ -v U+2193 -- DOWNWARDS ARROW+ <> U+2194 -- LEFT RIGHT ARROW+ UD U+2195 -- UP DOWN ARROW+ <= U+21D0 -- LEFTWARDS DOUBLE ARROW+ => U+21D2 -- RIGHTWARDS DOUBLE ARROW+ == U+21D4 -- LEFT RIGHT DOUBLE ARROW+ FA U+2200 -- FOR ALL+ dP U+2202 -- PARTIAL DIFFERENTIAL+ TE U+2203 -- THERE EXISTS+ /0 U+2205 -- EMPTY SET+ DE U+2206 -- INCREMENT+ NB U+2207 -- NABLA+ (- U+2208 -- ELEMENT OF+ -) U+220B -- CONTAINS AS MEMBER+ *P U+220F -- N-ARY PRODUCT `+ +Z U+2211 -- N-ARY SUMMATION `+ -2 U+2212 -- MINUS SIGN+ -+ U+2213 -- MINUS-OR-PLUS SIGN+ *- U+2217 -- ASTERISK OPERATOR+ Ob U+2218 -- RING OPERATOR+ Sb U+2219 -- BULLET OPERATOR+ RT U+221A -- SQUARE ROOT+ 0( U+221D -- PROPORTIONAL TO+ 00 U+221E -- INFINITY+ -L U+221F -- RIGHT ANGLE+ -V U+2220 -- ANGLE+ PP U+2225 -- PARALLEL TO+ AN U+2227 -- LOGICAL AND+ OR U+2228 -- LOGICAL OR+ (U U+2229 -- INTERSECTION+ )U U+222A -- UNION+ In U+222B -- INTEGRAL+ DI U+222C -- DOUBLE INTEGRAL+ Io U+222E -- CONTOUR INTEGRAL+ .: U+2234 -- THEREFORE+ :. U+2235 -- BECAUSE+ :R U+2236 -- RATIO+ :: U+2237 -- PROPORTION+ ?1 U+223C -- TILDE OPERATOR+ CG U+223E -- INVERTED LAZY S+ ?- U+2243 -- ASYMPTOTICALLY EQUAL TO+ ?= U+2245 -- APPROXIMATELY EQUAL TO+ ?2 U+2248 -- ALMOST EQUAL TO+ =? U+224C -- ALL EQUAL TO+ HI U+2253 -- IMAGE OF OR APPROXIMATELY EQUAL TO+ != U+2260 -- NOT EQUAL TO+ =3 U+2261 -- IDENTICAL TO+ =< U+2264 -- LESS-THAN OR EQUAL TO+ >= U+2265 -- GREATER-THAN OR EQUAL TO+ <* U+226A -- MUCH LESS-THAN+ *> U+226B -- MUCH GREATER-THAN+ !< U+226E -- NOT LESS-THAN+ !> U+226F -- NOT GREATER-THAN+ (C U+2282 -- SUBSET OF+ )C U+2283 -- SUPERSET OF+ (_ U+2286 -- SUBSET OF OR EQUAL TO+ )_ U+2287 -- SUPERSET OF OR EQUAL TO+ 0. U+2299 -- CIRCLED DOT OPERATOR+ 02 U+229A -- CIRCLED RING OPERATOR+ -T U+22A5 -- UP TACK+ .P U+22C5 -- DOT OPERATOR+ :3 U+22EE -- VERTICAL ELLIPSIS+ .3 U+22EF -- MIDLINE HORIZONTAL ELLIPSIS+ Eh U+2302 -- HOUSE+ <7 U+2308 -- LEFT CEILING+ >7 U+2309 -- RIGHT CEILING+ 7< U+230A -- LEFT FLOOR+ 7> U+230B -- RIGHT FLOOR+ NI U+2310 -- REVERSED NOT SIGN+ (A U+2312 -- ARC+ TR U+2315 -- TELEPHONE RECORDER+ Iu U+2320 -- TOP HALF INTEGRAL+ Il U+2321 -- BOTTOM HALF INTEGRAL+ </ U+2329 -- LEFT-POINTING ANGLE BRACKET+ /> U+232A -- RIGHT-POINTING ANGLE BRACKET+ Vs U+2423 -- OPEN BOX+ 1h U+2440 -- OCR HOOK+ 3h U+2441 -- OCR CHAIR+ 2h U+2442 -- OCR FORK+ 4h U+2443 -- OCR INVERTED FORK+ 1j U+2446 -- OCR BRANCH BANK IDENTIFICATION+ 2j U+2447 -- OCR AMOUNT OF CHECK+ 3j U+2448 -- OCR DASH+ 4j U+2449 -- OCR CUSTOMER ACCOUNT NUMBER+ 1. U+2488 -- DIGIT ONE FULL STOP+ 2. U+2489 -- DIGIT TWO FULL STOP+ 3. U+248A -- DIGIT THREE FULL STOP+ 4. U+248B -- DIGIT FOUR FULL STOP+ 5. U+248C -- DIGIT FIVE FULL STOP+ 6. U+248D -- DIGIT SIX FULL STOP+ 7. U+248E -- DIGIT SEVEN FULL STOP+ 8. U+248F -- DIGIT EIGHT FULL STOP+ 9. U+2490 -- DIGIT NINE FULL STOP+ hh U+2500 -- BOX DRAWINGS LIGHT HORIZONTAL+ HH U+2501 -- BOX DRAWINGS HEAVY HORIZONTAL+ vv U+2502 -- BOX DRAWINGS LIGHT VERTICAL+ VV U+2503 -- BOX DRAWINGS HEAVY VERTICAL+ 3- U+2504 -- BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL+ 3_ U+2505 -- BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL+ 3! U+2506 -- BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL+ 3/ U+2507 -- BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL+ 4- U+2508 -- BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL+ 4_ U+2509 -- BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL+ 4! U+250A -- BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL+ 4/ U+250B -- BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL+ dr U+250C -- BOX DRAWINGS LIGHT DOWN AND RIGHT+ dR U+250D -- BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY+ Dr U+250E -- BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT+ DR U+250F -- BOX DRAWINGS HEAVY DOWN AND RIGHT+ dl U+2510 -- BOX DRAWINGS LIGHT DOWN AND LEFT+ dL U+2511 -- BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY+ Dl U+2512 -- BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT+ LD U+2513 -- BOX DRAWINGS HEAVY DOWN AND LEFT+ ur U+2514 -- BOX DRAWINGS LIGHT UP AND RIGHT+ uR U+2515 -- BOX DRAWINGS UP LIGHT AND RIGHT HEAVY+ Ur U+2516 -- BOX DRAWINGS UP HEAVY AND RIGHT LIGHT+ UR U+2517 -- BOX DRAWINGS HEAVY UP AND RIGHT+ ul U+2518 -- BOX VOICED SOUND MARKDRAWINGS LIGHT UP AND LEFT+ uL U+2519 -- BOX DRAWINGS UP LIGHT AND LEFT HEAVY+ Ul U+251A -- BOX DRAWINGS UP HEAVY AND LEFT LIGHT+ UL U+251B -- BOX DRAWINGS HEAVY UP AND LEFT+ vr U+251C -- BOX DRAWINGS LIGHT VERTICAL AND RIGHT+ vR U+251D -- BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY+ Vr U+2520 -- BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT+ VR U+2523 -- BOX DRAWINGS HEAVY VERTICAL AND RIGHT+ vl U+2524 -- BOX DRAWINGS LIGHT VERTICAL AND LEFT+ vL U+2525 -- BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY+ Vl U+2528 -- BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT+ VL U+252B -- BOX DRAWINGS HEAVY VERTICAL AND LEFT+ dh U+252C -- BOX DRAWINGS LIGHT DOWN AND HORIZONTAL+ dH U+252F -- BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY+ Dh U+2530 -- BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT+ DH U+2533 -- BOX DRAWINGS HEAVY DOWN AND HORIZONTAL+ uh U+2534 -- BOX DRAWINGS LIGHT UP AND HORIZONTAL+ uH U+2537 -- BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY+ Uh U+2538 -- BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT+ UH U+253B -- BOX DRAWINGS HEAVY UP AND HORIZONTAL+ vh U+253C -- BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL+ vH U+253F -- BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY+ Vh U+2542 -- BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT+ VH U+254B -- BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL+ FD U+2571 -- BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT+ BD U+2572 -- BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT+ TB U+2580 -- UPPER HALF BLOCK+ LB U+2584 -- LOWER HALF BLOCK+ FB U+2588 -- FULL BLOCK+ lB U+258C -- LEFT HALF BLOCK+ RB U+2590 -- RIGHT HALF BLOCK+ .S U+2591 -- LIGHT SHADE+ :S U+2592 -- MEDIUM SHADE+ ?S U+2593 -- DARK SHADE+ fS U+25A0 -- BLACK SQUARE+ OS U+25A1 -- WHITE SQUARE+ RO U+25A2 -- WHITE SQUARE WITH ROUNDED CORNERS+ Rr U+25A3 -- WHITE SQUARE CONTAINING BLACK SMALL SQUARE+ RF U+25A4 -- SQUARE WITH HORIZONTAL FILL+ RY U+25A5 -- SQUARE WITH VERTICAL FILL+ RH U+25A6 -- SQUARE WITH ORTHOGONAL CROSSHATCH FILL+ RZ U+25A7 -- SQUARE WITH UPPER LEFT TO LOWER RIGHT FILL+ RK U+25A8 -- SQUARE WITH UPPER RIGHT TO LOWER LEFT FILL+ RX U+25A9 -- SQUARE WITH DIAGONAL CROSSHATCH FILL+ sB U+25AA -- BLACK SMALL SQUARE+ SR U+25AC -- BLACK RECTANGLE+ Or U+25AD -- WHITE RECTANGLE+ UT U+25B2 -- BLACK UP-POINTING TRIANGLE+ uT U+25B3 -- WHITE UP-POINTING TRIANGLE+ PR U+25B6 -- BLACK RIGHT-POINTING TRIANGLE+ Tr U+25B7 -- WHITE RIGHT-POINTING TRIANGLE+ Dt U+25BC -- BLACK DOWN-POINTING TRIANGLE+ dT U+25BD -- WHITE DOWN-POINTING TRIANGLE+ PL U+25C0 -- BLACK LEFT-POINTING TRIANGLE+ Tl U+25C1 -- WHITE LEFT-POINTING TRIANGLE+ Db U+25C6 -- BLACK DIAMOND+ Dw U+25C7 -- WHITE DIAMOND+ LZ U+25CA -- LOZENGE+ 0m U+25CB -- WHITE CIRCLE+ 0o U+25CE -- BULLSEYE+ 0M U+25CF -- BLACK CIRCLE+ 0L U+25D0 -- CIRCLE WITH LEFT HALF BLACK+ 0R U+25D1 -- CIRCLE WITH RIGHT HALF BLACK+ Sn U+25D8 -- INVERSE BULLET+ Ic U+25D9 -- INVERSE WHITE CIRCLE+ Fd U+25E2 -- BLACK LOWER RIGHT TRIANGLE+ Bd U+25E3 -- BLACK LOWER LEFT TRIANGLE+ *2 U+2605 -- BLACK STAR+ *1 U+2606 -- WHITE STAR+ <H U+261C -- WHITE LEFT POINTING INDEX+ >H U+261E -- WHITE RIGHT POINTING INDEX+ 0u U+263A -- WHITE SMILING FACE+ 0U U+263B -- BLACK SMILING FACE+ SU U+263C -- WHITE SUN WITH RAYS+ Fm U+2640 -- FEMALE SIGN+ Ml U+2642 -- MALE SIGN+ cS U+2660 -- BLACK SPADE SUIT+ cH U+2661 -- WHITE HEART SUIT+ cD U+2662 -- WHITE DIAMOND SUIT+ cC U+2663 -- BLACK CLUB SUIT+ Md U+2669 -- QUARTER NOTE `+ M8 U+266A -- EIGHTH NOTE `+ M2 U+266B -- BEAMED EIGHTH NOTES+ Mb U+266D -- MUSIC FLAT SIGN+ Mx U+266E -- MUSIC NATURAL SIGN+ MX U+266F -- MUSIC SHARP SIGN+ OK U+2713 -- CHECK MARK+ XX U+2717 -- BALLOT X+ -X U+2720 -- MALTESE CROSS+ IS U+3000 -- IDEOGRAPHIC SPACE+ ,_ U+3001 -- IDEOGRAPHIC COMMA+ ._ U+3002 -- IDEOGRAPHIC FULL STOP+ +" U+3003 -- DITTO MARK+ +_ U+3004 -- JAPANESE INDUSTRIAL STANDARD SYMBOL+ *_ U+3005 -- IDEOGRAPHIC ITERATION MARK+ ;_ U+3006 -- IDEOGRAPHIC CLOSING MARK+ 0_ U+3007 -- IDEOGRAPHIC NUMBER ZERO+ <+ U+300A -- LEFT DOUBLE ANGLE BRACKET+ >+ U+300B -- RIGHT DOUBLE ANGLE BRACKET+ <' U+300C -- LEFT CORNER BRACKET+ >' U+300D -- RIGHT CORNER BRACKET+ <" U+300E -- LEFT WHITE CORNER BRACKET+ >" U+300F -- RIGHT WHITE CORNER BRACKET+ (" U+3010 -- LEFT BLACK LENTICULAR BRACKET+ )" U+3011 -- RIGHT BLACK LENTICULAR BRACKET+ =T U+3012 -- POSTAL MARK+ =_ U+3013 -- GETA MARK+ (' U+3014 -- LEFT TORTOISE SHELL BRACKET+ )' U+3015 -- RIGHT TORTOISE SHELL BRACKET+ (I U+3016 -- LEFT WHITE LENTICULAR BRACKET+ )I U+3017 -- RIGHT WHITE LENTICULAR BRACKET+ -? U+301C -- WAVE DASH+ A5 U+3041 -- HIRAGANA LETTER SMALL A+ a5 U+3042 -- HIRAGANA LETTER A+ I5 U+3043 -- HIRAGANA LETTER SMALL I+ i5 U+3044 -- HIRAGANA LETTER I+ U5 U+3045 -- HIRAGANA LETTER SMALL U+ u5 U+3046 -- HIRAGANA LETTER U+ E5 U+3047 -- HIRAGANA LETTER SMALL E+ e5 U+3048 -- HIRAGANA LETTER E+ O5 U+3049 -- HIRAGANA LETTER SMALL O+ o5 U+304A -- HIRAGANA LETTER O+ ka U+304B -- HIRAGANA LETTER KA+ ga U+304C -- HIRAGANA LETTER GA+ ki U+304D -- HIRAGANA LETTER KI+ gi U+304E -- HIRAGANA LETTER GI+ ku U+304F -- HIRAGANA LETTER KU+ gu U+3050 -- HIRAGANA LETTER GU+ ke U+3051 -- HIRAGANA LETTER KE+ ge U+3052 -- HIRAGANA LETTER GE+ ko U+3053 -- HIRAGANA LETTER KO+ go U+3054 -- HIRAGANA LETTER GO+ sa U+3055 -- HIRAGANA LETTER SA+ za U+3056 -- HIRAGANA LETTER ZA+ si U+3057 -- HIRAGANA LETTER SI+ zi U+3058 -- HIRAGANA LETTER ZI+ su U+3059 -- HIRAGANA LETTER SU+ zu U+305A -- HIRAGANA LETTER ZU+ se U+305B -- HIRAGANA LETTER SE+ ze U+305C -- HIRAGANA LETTER ZE+ so U+305D -- HIRAGANA LETTER SO+ zo U+305E -- HIRAGANA LETTER ZO+ ta U+305F -- HIRAGANA LETTER TA+ da U+3060 -- HIRAGANA LETTER DA+ ti U+3061 -- HIRAGANA LETTER TI+ di U+3062 -- HIRAGANA LETTER DI+ tU U+3063 -- HIRAGANA LETTER SMALL TU+ tu U+3064 -- HIRAGANA LETTER TU+ du U+3065 -- HIRAGANA LETTER DU+ te U+3066 -- HIRAGANA LETTER TE+ de U+3067 -- HIRAGANA LETTER DE+ to U+3068 -- HIRAGANA LETTER TO+ do U+3069 -- HIRAGANA LETTER DO+ na U+306A -- HIRAGANA LETTER NA+ ni U+306B -- HIRAGANA LETTER NI+ nu U+306C -- HIRAGANA LETTER NU+ ne U+306D -- HIRAGANA LETTER NE+ no U+306E -- HIRAGANA LETTER NO+ ha U+306F -- HIRAGANA LETTER HA+ ba U+3070 -- HIRAGANA LETTER BA+ pa U+3071 -- HIRAGANA LETTER PA+ hi U+3072 -- HIRAGANA LETTER HI+ bi U+3073 -- HIRAGANA LETTER BI+ pi U+3074 -- HIRAGANA LETTER PI+ hu U+3075 -- HIRAGANA LETTER HU+ bu U+3076 -- HIRAGANA LETTER BU+ pu U+3077 -- HIRAGANA LETTER PU+ he U+3078 -- HIRAGANA LETTER HE+ be U+3079 -- HIRAGANA LETTER BE+ pe U+307A -- HIRAGANA LETTER PE+ ho U+307B -- HIRAGANA LETTER HO+ bo U+307C -- HIRAGANA LETTER BO+ po U+307D -- HIRAGANA LETTER PO+ ma U+307E -- HIRAGANA LETTER MA+ mi U+307F -- HIRAGANA LETTER MI+ mu U+3080 -- HIRAGANA LETTER MU+ me U+3081 -- HIRAGANA LETTER ME+ mo U+3082 -- HIRAGANA LETTER MO+ yA U+3083 -- HIRAGANA LETTER SMALL YA+ ya U+3084 -- HIRAGANA LETTER YA+ yU U+3085 -- HIRAGANA LETTER SMALL YU+ yu U+3086 -- HIRAGANA LETTER YU+ yO U+3087 -- HIRAGANA LETTER SMALL YO+ yo U+3088 -- HIRAGANA LETTER YO+ ra U+3089 -- HIRAGANA LETTER RA+ ri U+308A -- HIRAGANA LETTER RI+ ru U+308B -- HIRAGANA LETTER RU+ re U+308C -- HIRAGANA LETTER RE+ ro U+308D -- HIRAGANA LETTER RO+ wA U+308E -- HIRAGANA LETTER SMALL WA+ wa U+308F -- HIRAGANA LETTER WA+ wi U+3090 -- HIRAGANA LETTER WI+ we U+3091 -- HIRAGANA LETTER WE+ wo U+3092 -- HIRAGANA LETTER WO+ n5 U+3093 -- HIRAGANA LETTER N `+ vu U+3094 -- HIRAGANA LETTER VU+ "5 U+309B -- KATAKANA-HIRAGANA VOICED SOUND MARK+ 05 U+309C -- KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK+ *5 U+309D -- HIRAGANA ITERATION MARK+ +5 U+309E -- HIRAGANA VOICED ITERATION MARK+ a6 U+30A1 -- KATAKANA LETTER SMALL A+ A6 U+30A2 -- KATAKANA LETTER A+ i6 U+30A3 -- KATAKANA LETTER SMALL I+ I6 U+30A4 -- KATAKANA LETTER I+ u6 U+30A5 -- KATAKANA LETTER SMALL U+ U6 U+30A6 -- KATAKANA LETTER U+ e6 U+30A7 -- KATAKANA LETTER SMALL E+ E6 U+30A8 -- KATAKANA LETTER E+ o6 U+30A9 -- KATAKANA LETTER SMALL O+ O6 U+30AA -- KATAKANA LETTER O+ Ka U+30AB -- KATAKANA LETTER KA+ Ga U+30AC -- KATAKANA LETTER GA+ Ki U+30AD -- KATAKANA LETTER KI+ Gi U+30AE -- KATAKANA LETTER GI+ Ku U+30AF -- KATAKANA LETTER KU+ Gu U+30B0 -- KATAKANA LETTER GU+ Ke U+30B1 -- KATAKANA LETTER KE+ Ge U+30B2 -- KATAKANA LETTER GE+ Ko U+30B3 -- KATAKANA LETTER KO+ Go U+30B4 -- KATAKANA LETTER GO+ Sa U+30B5 -- KATAKANA LETTER SA+ Za U+30B6 -- KATAKANA LETTER ZA+ Si U+30B7 -- KATAKANA LETTER SI+ Zi U+30B8 -- KATAKANA LETTER ZI+ Su U+30B9 -- KATAKANA LETTER SU+ Zu U+30BA -- KATAKANA LETTER ZU+ Se U+30BB -- KATAKANA LETTER SE+ Ze U+30BC -- KATAKANA LETTER ZE+ So U+30BD -- KATAKANA LETTER SO+ Zo U+30BE -- KATAKANA LETTER ZO+ Ta U+30BF -- KATAKANA LETTER TA+ Da U+30C0 -- KATAKANA LETTER DA+ Ti U+30C1 -- KATAKANA LETTER TI+ Di U+30C2 -- KATAKANA LETTER DI+ TU U+30C3 -- KATAKANA LETTER SMALL TU+ Tu U+30C4 -- KATAKANA LETTER TU+ Du U+30C5 -- KATAKANA LETTER DU+ Te U+30C6 -- KATAKANA LETTER TE+ De U+30C7 -- KATAKANA LETTER DE+ To U+30C8 -- KATAKANA LETTER TO+ Do U+30C9 -- KATAKANA LETTER DO+ Na U+30CA -- KATAKANA LETTER NA+ Ni U+30CB -- KATAKANA LETTER NI+ Nu U+30CC -- KATAKANA LETTER NU+ Ne U+30CD -- KATAKANA LETTER NE+ No U+30CE -- KATAKANA LETTER NO+ Ha U+30CF -- KATAKANA LETTER HA+ Ba U+30D0 -- KATAKANA LETTER BA+ Pa U+30D1 -- KATAKANA LETTER PA+ Hi U+30D2 -- KATAKANA LETTER HI+ Bi U+30D3 -- KATAKANA LETTER BI+ Pi U+30D4 -- KATAKANA LETTER PI+ Hu U+30D5 -- KATAKANA LETTER HU+ Bu U+30D6 -- KATAKANA LETTER BU+ Pu U+30D7 -- KATAKANA LETTER PU+ He U+30D8 -- KATAKANA LETTER HE+ Be U+30D9 -- KATAKANA LETTER BE+ Pe U+30DA -- KATAKANA LETTER PE+ Ho U+30DB -- KATAKANA LETTER HO+ Bo U+30DC -- KATAKANA LETTER BO+ Po U+30DD -- KATAKANA LETTER PO+ Ma U+30DE -- KATAKANA LETTER MA+ Mi U+30DF -- KATAKANA LETTER MI+ Mu U+30E0 -- KATAKANA LETTER MU+ Me U+30E1 -- KATAKANA LETTER ME+ Mo U+30E2 -- KATAKANA LETTER MO+ YA U+30E3 -- KATAKANA LETTER SMALL YA+ Ya U+30E4 -- KATAKANA LETTER YA+ YU U+30E5 -- KATAKANA LETTER SMALL YU+ Yu U+30E6 -- KATAKANA LETTER YU+ YO U+30E7 -- KATAKANA LETTER SMALL YO+ Yo U+30E8 -- KATAKANA LETTER YO+ Ra U+30E9 -- KATAKANA LETTER RA+ Ri U+30EA -- KATAKANA LETTER RI+ Ru U+30EB -- KATAKANA LETTER RU+ Re U+30EC -- KATAKANA LETTER RE+ Ro U+30ED -- KATAKANA LETTER RO+ WA U+30EE -- KATAKANA LETTER SMALL WA+ Wa U+30EF -- KATAKANA LETTER WA+ Wi U+30F0 -- KATAKANA LETTER WI+ We U+30F1 -- KATAKANA LETTER WE+ Wo U+30F2 -- KATAKANA LETTER WO+ N6 U+30F3 -- KATAKANA LETTER N `+ Vu U+30F4 -- KATAKANA LETTER VU+ KA U+30F5 -- KATAKANA LETTER SMALL KA+ KE U+30F6 -- KATAKANA LETTER SMALL KE+ Va U+30F7 -- KATAKANA LETTER VA+ Vi U+30F8 -- KATAKANA LETTER VI+ Ve U+30F9 -- KATAKANA LETTER VE+ Vo U+30FA -- KATAKANA LETTER VO+ .6 U+30FB -- KATAKANA MIDDLE DOT+ -6 U+30FC -- KATAKANA-HIRAGANA PROLONGED SOUND MARK+ *6 U+30FD -- KATAKANA ITERATION MARK+ +6 U+30FE -- KATAKANA VOICED ITERATION MARK+ b4 U+3105 -- BOPOMOFO LETTER B+ p4 U+3106 -- BOPOMOFO LETTER P+ m4 U+3107 -- BOPOMOFO LETTER M+ f4 U+3108 -- BOPOMOFO LETTER F+ d4 U+3109 -- BOPOMOFO LETTER D+ t4 U+310A -- BOPOMOFO LETTER T+ n4 U+310B -- BOPOMOFO LETTER N `+ l4 U+310C -- BOPOMOFO LETTER L+ g4 U+310D -- BOPOMOFO LETTER G+ k4 U+310E -- BOPOMOFO LETTER K+ h4 U+310F -- BOPOMOFO LETTER H+ j4 U+3110 -- BOPOMOFO LETTER J+ q4 U+3111 -- BOPOMOFO LETTER Q+ x4 U+3112 -- BOPOMOFO LETTER X+ zh U+3113 -- BOPOMOFO LETTER ZH+ ch U+3114 -- BOPOMOFO LETTER CH+ sh U+3115 -- BOPOMOFO LETTER SH+ r4 U+3116 -- BOPOMOFO LETTER R+ z4 U+3117 -- BOPOMOFO LETTER Z+ c4 U+3118 -- BOPOMOFO LETTER C+ s4 U+3119 -- BOPOMOFO LETTER S+ a4 U+311A -- BOPOMOFO LETTER A+ o4 U+311B -- BOPOMOFO LETTER O+ e4 U+311C -- BOPOMOFO LETTER E+ ai U+311E -- BOPOMOFO LETTER AI+ ei U+311F -- BOPOMOFO LETTER EI+ au U+3120 -- BOPOMOFO LETTER AU+ ou U+3121 -- BOPOMOFO LETTER OU+ an U+3122 -- BOPOMOFO LETTER AN+ en U+3123 -- BOPOMOFO LETTER EN+ aN U+3124 -- BOPOMOFO LETTER ANG+ eN U+3125 -- BOPOMOFO LETTER ENG+ er U+3126 -- BOPOMOFO LETTER ER+ i4 U+3127 -- BOPOMOFO LETTER I+ u4 U+3128 -- BOPOMOFO LETTER U+ iu U+3129 -- BOPOMOFO LETTER IU+ v4 U+312A -- BOPOMOFO LETTER V+ nG U+312B -- BOPOMOFO LETTER NG+ gn U+312C -- BOPOMOFO LETTER GN+ 1c U+3220 -- PARENTHESIZED IDEOGRAPH ONE+ 2c U+3221 -- PARENTHESIZED IDEOGRAPH TWO+ 3c U+3222 -- PARENTHESIZED IDEOGRAPH THREE+ 4c U+3223 -- PARENTHESIZED IDEOGRAPH FOUR+ 5c U+3224 -- PARENTHESIZED IDEOGRAPH FIVE+ 6c U+3225 -- PARENTHESIZED IDEOGRAPH SIX+ 7c U+3226 -- PARENTHESIZED IDEOGRAPH SEVEN+ 8c U+3227 -- PARENTHESIZED IDEOGRAPH EIGHT+ 9c U+3228 -- PARENTHESIZED IDEOGRAPH NINE+ ff U+FB00 -- LATIN SMALL LIGATURE FF+ fi U+FB01 -- LATIN SMALL LIGATURE FI+ fl U+FB02 -- LATIN SMALL LIGATURE FL+ ft U+FB05 -- LATIN SMALL LIGATURE LONG S T+ st U+FB06 -- LATIN SMALL LIGATURE ST+ |]
src/RtsStats.hs view
@@ -1,5 +1,4 @@-{-# LANGUAGE OverloadedStrings, FlexibleContexts, FlexibleInstances, DeriveGeneric, TypeOperators, StandaloneDeriving, CPP, EmptyCase #-}-{-# OPTIONS_GHC -Wno-orphans -funfolding-use-threshold=2000 -funfolding-creation-threshold=1000 #-}+{-# Language OverloadedStrings, CPP #-} {-| Module : RtsStats@@ -22,94 +21,100 @@ import Data.Text (Text) import qualified Data.Text as Text import Data.Word-import GHC.Generics import GHC.Stats -type KVs = [(Text,Text)] -> [(Text,Text)]--cons :: (Text, Text) -> KVs-cons = (:)--class Fields a where fields :: a -> Text -> KVs-instance Fields Int64 where fields = commaFields-instance Fields Word32 where fields = commaFields-instance Fields Word64 where fields = commaFields-instance Fields Double where fields = showFields--showFields :: Show a => a -> Text -> KVs-showFields x n = cons (n, Text.pack (show x))-{-# NOINLINE showFields #-}+class Render a where render :: a -> Text+instance Render Word32 where render = prettyNum+instance Render Word64 where render = prettyNum+instance Render Int64 where render = prettyNum+instance Render Double where render = Text.pack . show -commaFields :: Show a => a -> Text -> KVs-commaFields x n = cons (n, Text.pack (addCommas (show x)))-{-# NOINLINE commaFields #-}+prettyNum :: Show a => Num a => Ord a => a -> Text+prettyNum n = Text.pack (addSign (addCommas (show (abs n))))+ where+ addSign = if n < 0 then (:) '-' else id addCommas :: String -> String addCommas = reverse . intercalate "," . chunksOf 3 . reverse -genericFields :: (Generic a, GFields (Rep a)) => a -> Text -> KVs-genericFields = gfields . from--class GFields f where- gfields :: f p -> Text -> KVs--instance GFields f => GFields (D1 c f) where- gfields (M1 x) = gfields x- {-# INLINE gfields #-}--instance GFields f => GFields (C1 c f) where- gfields (M1 x) = gfields x- {-# INLINE gfields #-}--instance (Selector s, GFields f) => GFields (S1 s f) where- gfields s@(M1 x) _ = gfields x (Text.pack (selName s))- {-# INLINE gfields #-}--instance (GFields f, GFields g) => GFields (f :*: g) where- gfields (x :*: y) n = gfields x n . gfields y n- {-# INLINE gfields #-}--instance (GFields f, GFields g) => GFields (f :+: g) where- gfields (L1 x) = gfields x- gfields (R1 x) = gfields x- {-# INLINE gfields #-}--instance GFields U1 where- gfields _ _ = id- {-# INLINE gfields #-}--instance GFields V1 where- gfields v _ = case v of {}- {-# INLINE gfields #-}--instance Fields a => GFields (K1 i a) where- gfields (K1 x) = fields x- {-# INLINE gfields #-}--statsToEntries :: Stats -> [(Text, Text)]-statsToEntries (Stats rts) = fields rts "stats" []- #if MIN_VERSION_base(4,10,0) newtype Stats = Stats RTSStats -deriving instance Generic RTSStats-deriving instance Generic GCDetails-instance Fields RTSStats where fields = genericFields-instance Fields GCDetails where fields = genericFields- getStats :: IO (Maybe Stats) getStats = do enabled <- getRTSStatsEnabled if enabled then Just . Stats <$> getRTSStats else pure Nothing +statsToEntries :: Stats -> [(Text, Text)]+statsToEntries (Stats rts) = seq rgc+ [ ("GCs" , render $ gcs rts)+ , ("Major GCs" , render $ major_gcs rts)+ , ("Allocated bytes" , render $ allocated_bytes rts)+ , ("Max live bytes" , render $ max_live_bytes rts)+ , ("Max large objects bytes" , render $ max_large_objects_bytes rts)+ , ("Max compact bytes" , render $ max_compact_bytes rts)+ , ("Max slop bytes" , render $ max_slop_bytes rts)+ , ("Max memory in use bytes" , render $ max_mem_in_use_bytes rts)+ , ("Cumulative live bytes" , render $ cumulative_live_bytes rts)+ , ("Copied bytes" , render $ copied_bytes rts)+ , ("Parallel copied bytes" , render $ par_copied_bytes rts)+ , ("Cumulative parallel max copied bytes", render $ cumulative_par_max_copied_bytes rts)+ , ("Mutator CPU ns" , render $ mutator_cpu_ns rts)+ , ("Mutator elapsed ns" , render $ mutator_elapsed_ns rts)+ , ("GC CPU ns" , render $ gc_cpu_ns rts)+ , ("GC elapsed ns" , render $ gc_elapsed_ns rts)+ , ("CPU ns" , render $ cpu_ns rts)+ , ("Elapsed ns" , render $ elapsed_ns rts)+ , ("==[Totals]==" , "" )+ , ("" , "" )+ , ("Generation" , render $ gcdetails_gen rgc)+ , ("Threads" , render $ gcdetails_threads rgc)+ , ("Allocated bytes" , render $ gcdetails_allocated_bytes rgc)+ , ("Live bytes" , render $ gcdetails_live_bytes rgc)+ , ("Large objects bytes" , render $ gcdetails_large_objects_bytes rgc)+ , ("Compact bytes" , render $ gcdetails_compact_bytes rgc)+ , ("Slop bytes" , render $ gcdetails_slop_bytes rgc)+ , ("Memory in use bytes" , render $ gcdetails_mem_in_use_bytes rgc)+ , ("Copied bytes" , render $ gcdetails_copied_bytes rgc)+ , ("Parallel max copied bytes" , render $ gcdetails_par_max_copied_bytes rgc)+ , ("Sync elapsed ns" , render $ gcdetails_sync_elapsed_ns rgc)+ , ("CPU ns" , render $ gcdetails_cpu_ns rgc)+ , ("Elapsed ns" , render $ gcdetails_elapsed_ns rgc)+ , ("==[Last GC]==" , "" )+ ]+ where+ rgc = gc rts+ #else -newtype Stats = Stats GCStats+statsToEntries :: Stats -> [(Text, Text)]+statsToEntries (Stats rts) = seq rts+ [ ("Allocated bytes" , render $ bytesAllocated rts)+ , ("GCs" , render $ numGcs rts)+ , ("Major GCs" , render $ numByteUsageSamples rts)+ , ("Cumulative allocated bytes" , render $ maxBytesUsed rts)+ , ("Cumulative live bytes" , render $ cumulativeBytesUsed rts)+ , ("Cumulative copied bytes" , render $ bytesCopied rts)+ , ("Max slop bytes" , render $ maxBytesSlop rts)+ , ("Maximum allocated megabytes", render $ peakMegabytesAllocated rts)+ , ("Mutator CPU seconds" , render $ mutatorCpuSeconds rts)+ , ("Mutator elapsed seconds" , render $ mutatorWallSeconds rts)+ , ("GC CPU seconds" , render $ gcCpuSeconds rts)+ , ("GC elapsed seconds" , render $ gcWallSeconds rts)+ , ("CPU seconds" , render $ cpuSeconds rts)+ , ("Elapsed seconds" , render $ wallSeconds rts)+ , ("Total bytes copied" , render $ parTotBytesCopied rts)+ , ("Max parallel bytes copied" , render $ parMaxBytesCopied rts)+ , ("==[Totals]==" , "" )+ , ("" , "" )+ , ("(last) Live bytes" , render $ currentBytesUsed rts)+ , ("(last) Slop bytes" , render $ currentBytesSlop rts)+ , ("==[Last GC]==" , "" )+ ] -deriving instance Generic GCStats-instance Fields GCStats where fields = genericFields+newtype Stats = Stats GCStats getStats :: IO (Maybe Stats) getStats =