Make UnicodeData.txt path configurable, remove unsafePerformIO in favour

of ExtensibleState

Add facility to type Unicode character via xdotool
This commit is contained in:
Nick Hu 2017-05-12 17:07:03 +01:00
parent 676d83ce83
commit ecd0048d83
2 changed files with 81 additions and 40 deletions

View File

@ -32,6 +32,11 @@
sending messages to `Minimized` layout. `XMonad.Hooks.RestoreMinimized` has sending messages to `Minimized` layout. `XMonad.Hooks.RestoreMinimized` has
been completely deprecated, and its functions have no effect. been completely deprecated, and its functions have no effect.
* `XMonad.Prompt.Unicode`
- `unicodePrompt :: String -> XPConfig -> X ()` now additionally takes a
filepath to the `UnicodeData.txt` file containing unicode data.
### New Modules ### New Modules
* `XMonad.Hooks.Focus` * `XMonad.Hooks.Focus`
@ -145,6 +150,16 @@
- Update logout key combination (modm+shift+Q) to work with modern - Update logout key combination (modm+shift+Q) to work with modern
* `XMonad.Prompt.Unicode`
- Persist unicode data cache across XMonad instances due to
`ExtensibleState` now used instead of `unsafePerformIO`.
- `typeUnicodePrompt :: String -> XPConfig -> X ()` provided to insert the
Unicode character via `xdotool` instead of copying it to the paste buffer.
- `mkUnicodePrompt :: String -> [String] -> String -> XPConfig -> X ()`
acts as a generic function to pass the selected Unicode character to any
program.
## 0.13 (February 10, 2017) ## 0.13 (February 10, 2017)
### Breaking Changes ### Breaking Changes

View File

@ -1,6 +1,7 @@
{- | {- |
Module : XMonad.Prompt.Unicode Module : XMonad.Prompt.Unicode
Copyright : (c) 2016 Joachim Breitner Copyright : (c) 2016 Joachim Breitner
2017 Nick Hu
License : BSD-style (see LICENSE) License : BSD-style (see LICENSE)
Maintainer : <mail@joachim-breitner.de> Maintainer : <mail@joachim-breitner.de>
@ -9,14 +10,18 @@ Stability : stable
A prompt for searching unicode characters by name and inserting them into A prompt for searching unicode characters by name and inserting them into
the clipboard. the clipboard.
Requires the file @\/usr\/share\/unicode\/UnicodeData.txt@ (shipped in the package The provided @unicodePrompt@ and @typeUnicodePrompt@ use @xsel@ and @xdotool@
@unicode-data@ on Debian) and the @xsel@ tool. respectively.
-} -}
{-# LANGUAGE DeriveDataTypeable #-}
module XMonad.Prompt.Unicode ( module XMonad.Prompt.Unicode (
-- * Usage -- * Usage
-- $usage -- $usage
unicodePrompt unicodePrompt,
typeUnicodePrompt,
mkUnicodePrompt
) where ) where
import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Char8 as BS
@ -33,9 +38,23 @@ import Data.List
import Text.Printf import Text.Printf
import XMonad import XMonad
import qualified XMonad.Util.ExtensibleState as XS
import XMonad.Util.Run import XMonad.Util.Run
import XMonad.Prompt import XMonad.Prompt
data Unicode = Unicode
instance XPrompt Unicode where
showXPrompt Unicode = "Unicode: "
commandToComplete Unicode s = s
nextCompletion Unicode = getNextCompletion
newtype UnicodeData = UnicodeData { getUnicodeData :: [(Char, BS.ByteString)] }
deriving (Typeable, Read, Show)
instance ExtensionClass UnicodeData where
initialValue = UnicodeData []
extensionType = StateExtension
{- $usage {- $usage
You can use this module by importing it, along with You can use this module by importing it, along with
@ -46,54 +65,61 @@ You can use this module by importing it, along with
and adding an appropriate keybinding, for example: and adding an appropriate keybinding, for example:
> , ((modm .|. controlMask, xK_u), unicodePrompt def) > , ((modm .|. controlMask, xK_u), unicodePrompt "/path/to/unicode-data" def)
More flexibility is given by the @mkUnicodePrompt@ function, which takes a
command and a list of arguments to pass as its first two arguments. See
@unicodePrompt@ for details.
-} -}
unicodeDataFilename :: String populateEntries :: String -> X Bool
unicodeDataFilename = "/usr/share/unicode/UnicodeData.txt" populateEntries unicodeDataFilename = do
entries <- fmap getUnicodeData (XS.get :: X UnicodeData)
entries :: [(Char, BS.ByteString)] if null entries
entries = unsafePerformIO $ do then do
datE <- tryIOError $ BS.readFile unicodeDataFilename datE <- liftIO . tryIOError $ BS.readFile unicodeDataFilename
case datE of case datE of
Left e -> do Left e -> liftIO $ do
hPutStrLn stderr $ "Could not read file \"" ++ unicodeDataFilename ++ "\"" hPutStrLn stderr $ "Could not read file \"" ++ unicodeDataFilename ++ "\""
hPutStrLn stderr $ show e hPrint stderr e
hPutStrLn stderr $ "Do you have unicode-data installed?" hPutStrLn stderr "Do you have unicode-data installed?"
return [] return False
Right dat -> return $ sortBy (comparing (BS.length . snd)) $ parseUnicodeData dat Right dat -> do
{-# NOINLINE entries #-} XS.put . UnicodeData . sortBy (comparing (BS.length . snd)) $ parseUnicodeData dat
return True
else return True
parseUnicodeData :: BS.ByteString -> [(Char, BS.ByteString)] parseUnicodeData :: BS.ByteString -> [(Char, BS.ByteString)]
parseUnicodeData = mapMaybe parseLine . BS.lines parseUnicodeData = mapMaybe parseLine . BS.lines
where where parseLine l = do
parseLine l = do field1 : field2 : _ <- return $ BS.split ';' l
field1 : field2 : _ <- return $ BS.split ';' l [(c,"")] <- return . readHex $ BS.unpack field1
[(c,"")] <- return $ readHex (BS.unpack field1) return (chr c, field2)
return (chr c, field2)
searchUnicode :: String -> [(Char, String)] searchUnicode :: [(Char, BS.ByteString)] -> String -> [(Char, String)]
searchUnicode s = map (second BS.unpack) $ filter go entries searchUnicode entries s = map (second BS.unpack) $ filter go entries
where w = map BS.pack $ filter (all isAscii) $ filter ((> 1) . length) $ words $ map toUpper s where w = map BS.pack . filter (all isAscii) . filter ((> 1) . length) . words $ map toUpper s
go (c,d) = all (`BS.isInfixOf` d) w go (c,d) = all (`BS.isInfixOf` d) w
-- | Prompt the user for a unicode character to be inserted into the paste buffer of the X server. mkUnicodePrompt :: String -> [String] -> String -> XPConfig -> X ()
unicodePrompt :: XPConfig -> X () mkUnicodePrompt prog args unicodeDataFilename config =
unicodePrompt config = mkXPrompt Unicode config unicodeCompl paste whenX (populateEntries unicodeDataFilename) $ do
entries <- fmap getUnicodeData (XS.get :: X UnicodeData)
mkXPrompt Unicode config (unicodeCompl entries) paste
where where
unicodeCompl [] = return [] unicodeCompl _ [] = return []
unicodeCompl s = do unicodeCompl entries s = do
return $ map (\(c,d) -> printf "%s %s" [c] d) $ take 20 $ searchUnicode s let m = searchUnicode entries s
return . map (\(c,d) -> printf "%s %s" [c] d) $ take 20 m
paste [] = return () paste [] = return ()
paste (c:_) = do paste (c:_) = do
runProcessWithInput "xsel" ["-i"] [c] runProcessWithInput prog args [c]
return () return ()
data Unicode = Unicode -- | Prompt the user for a Unicode character to be inserted into the paste buffer of the X server.
instance XPrompt Unicode where unicodePrompt :: String -> XPConfig -> X ()
showXPrompt Unicode = "Unicode: " unicodePrompt = mkUnicodePrompt "xsel" ["-i"]
commandToComplete Unicode s = s
nextCompletion Unicode = getNextCompletion
-- | Prompt the user for a Unicode character to be typed by @xdotool@.
typeUnicodePrompt :: String -> XPConfig -> X ()
typeUnicodePrompt = mkUnicodePrompt "xdotool" ["type", "--clearmodifiers", "--file", "-"]