Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add full Unicode support #89

Merged
merged 2 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 2 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ A convenient converter of [DConf](https://wiki.gnome.org/Projects/dconf) files t
* [Introduction](#introduction)
* [Run](#run)
* [Custom root](#custom-root)
* [Emoji support](#emoji-support)
* [Supported types](#supported-types)
* [Gnome Shell configuration](#gnome-shell-configuration)
* [Installation](#installation)
Expand Down Expand Up @@ -107,20 +106,18 @@ Type `--help` for some more information.
dconf2nix - Nixify dconf configuration files

Usage: dconf2nix [-v|--version]
[[-r|--root ARG] [-e|--emoji] [--verbose] | (-i|--input ARG)
(-o|--output ARG) [-r|--root ARG] [-e|--emoji] [--verbose]]
[[-r|--root ARG] [--verbose] | (-i|--input ARG)
(-o|--output ARG) [-r|--root ARG] [--verbose]]
Convert a dconf file into a Nix file, as expected by Home Manager.

Available options:
-h,--help Show this help text
-v,--version Show the current version
-r,--root ARG Custom root path. e.g.: system/locale/
-e,--emoji Enable emoji support (adds a bit of overhead)
--verbose Verbose mode (debug)
-i,--input ARG Path to the dconf file (input)
-o,--output ARG Path to the Nix output file (to be created)
-r,--root ARG Custom root path. e.g.: system/locale/
-e,--emoji Enable emoji support (adds a bit of overhead)
--verbose Verbose mode (debug)
```

Expand All @@ -145,26 +142,6 @@ This will generate an output similar to the one below.
}
```

#### Emoji support

Emojis are supported since version `0.0.12`, and it needs to be explicitly enabled, as these add a little parsing overhead via the [emojis](https://hackage.haskell.org/package/emojis) package.

The following `dconf` input.

```ini
[ org/gnome/Characters ]
recent-characters=['💡']
some-other-character=['🤓']
```

Can be parsed as follows.

```console
$ dconf2nix -i data/emoji.settings -o output/emoji.nix --emoji
```

Failing to pass the `--emoji` flag will result in a timeout error.

### Supported types

For now, only types supported by Home Manager as specified [here](https://github.com/rycee/home-manager/blob/master/modules/lib/gvariant.nix) are supported. If there's enough interest, we might be able to work on supporting the [full specification](https://docs.gtk.org/glib/gvariant-text.html).
Expand Down
8 changes: 4 additions & 4 deletions app/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import DConf2Nix ( dconf2nixFile

main :: IO ()
main = runArgs >>= \case
FileInput (FileArgs i o r e v) ->
dconf2nixFile i o r e v
StdinInput (StdinArgs r e v) ->
dconf2nixStdin r e v
FileInput (FileArgs i o r v) ->
dconf2nixFile i o r v
StdinInput (StdinArgs r v) ->
dconf2nixStdin r v
2 changes: 1 addition & 1 deletion data/dconf.settings
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ locations=[<(uint32 2, <('Gdańsk', 'EPGD', true, [(0.94916821905848536, 0.32230
region='en_US.UTF-8'

[issue28/desktop/ibus/panel/emoji]
favorites=['\8211', '\8594', '\8593', '\8595', '\8482', '\\u00ad', '\176', '\\v', '\160', '\171', '\8451']
favorites=['', '', '', '', '', '\u00ad', '°', '\v', ' ', '«', '']

[issue28/org/gnome/desktop/input-sources]
mru-sources=[('xkb', 'us+altgr-intl'), ('ibus', 'mozc-jp')]
Expand Down
3 changes: 3 additions & 0 deletions data/unicode.settings
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[org/gnome/font-manager]
compare-preview-text='🤣'
preview-text='příliš žluťoučký kůň úpěl ďábelské ódy\nprilis zlutoucky kun upel dabelske ody\n◌̍◌̍ff̍ ̍čěů\n◌̎\n'
1 change: 0 additions & 1 deletion dconf2nix.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ library
other-modules: Paths_dconf2nix
build-depends: base
, containers
, emojis
, optparse-applicative
, parsec
, text
Expand Down
6 changes: 3 additions & 3 deletions output/dconf.nix
Original file line number Diff line number Diff line change
Expand Up @@ -361,20 +361,20 @@ with lib.hm.gvariant;
};

"org/gnome/Weather" = {
locations = [ (mkVariant [ (mkUint32 2) (mkVariant [ "Gda\324sk" "EPGD" true [ (mkTuple [ 0.9491682190584854 0.3223041410193837 ]) ] [ (mkTuple [ 0.9485864484589182 0.32579479952337237 ]) ] ]) ]) (mkVariant [ (mkUint32 2) (mkVariant [ "Gdynia, Dzia\322dowo County, Warmian-Masurian Voivodeship" "" false [ (mkTuple [ 0.9302794944578734 0.34699627038777753 ]) ] [ (mkTuple [ 0.938610530426954 0.3574455077502486 ]) ] ]) ]) (mkVariant [ (mkUint32 2) (mkVariant [ "Gdynia, Pomeranian Voivodeship" "" false [ (mkTuple [ 0.9514923902475622 0.3235888220312407 ]) ] [ (mkTuple [ 0.9485864484589182 0.32579479952337237 ]) ] ]) ]) ];
locations = [ (mkVariant [ (mkUint32 2) (mkVariant [ "Gdańsk" "EPGD" true [ (mkTuple [ 0.9491682190584854 0.3223041410193837 ]) ] [ (mkTuple [ 0.9485864484589182 0.32579479952337237 ]) ] ]) ]) (mkVariant [ (mkUint32 2) (mkVariant [ "Gdynia, Działdowo County, Warmian-Masurian Voivodeship" "" false [ (mkTuple [ 0.9302794944578734 0.34699627038777753 ]) ] [ (mkTuple [ 0.938610530426954 0.3574455077502486 ]) ] ]) ]) (mkVariant [ (mkUint32 2) (mkVariant [ "Gdynia, Pomeranian Voivodeship" "" false [ (mkTuple [ 0.9514923902475622 0.3235888220312407 ]) ] [ (mkTuple [ 0.9485864484589182 0.32579479952337237 ]) ] ]) ]) ];
};

"org/gnome/shell/weather" = {
automatic-location = true;
locations = [ (mkVariant [ (mkUint32 2) (mkVariant [ "Gda\324sk" "EPGD" true [ (mkTuple [ 0.9491682190584854 0.3223041410193837 ]) ] [ (mkTuple [ 0.9485864484589182 0.32579479952337237 ]) ] ]) ]) (mkVariant [ (mkUint32 2) (mkVariant [ "Gdynia, Dzia\322dowo County, Warmian-Masurian Voivodeship" "" false [ (mkTuple [ 0.9302794944578734 0.34699627038777753 ]) ] [ (mkTuple [ 0.938610530426954 0.3574455077502486 ]) ] ]) ]) (mkVariant [ (mkUint32 2) (mkVariant [ "Gdynia, Pomeranian Voivodeship" "" false [ (mkTuple [ 0.9514923902475622 0.3235888220312407 ]) ] [ (mkTuple [ 0.9485864484589182 0.32579479952337237 ]) ] ]) ]) ];
locations = [ (mkVariant [ (mkUint32 2) (mkVariant [ "Gdańsk" "EPGD" true [ (mkTuple [ 0.9491682190584854 0.3223041410193837 ]) ] [ (mkTuple [ 0.9485864484589182 0.32579479952337237 ]) ] ]) ]) (mkVariant [ (mkUint32 2) (mkVariant [ "Gdynia, Działdowo County, Warmian-Masurian Voivodeship" "" false [ (mkTuple [ 0.9302794944578734 0.34699627038777753 ]) ] [ (mkTuple [ 0.938610530426954 0.3574455077502486 ]) ] ]) ]) (mkVariant [ (mkUint32 2) (mkVariant [ "Gdynia, Pomeranian Voivodeship" "" false [ (mkTuple [ 0.9514923902475622 0.3235888220312407 ]) ] [ (mkTuple [ 0.9485864484589182 0.32579479952337237 ]) ] ]) ]) ];
};

"system/locale" = {
region = "en_US.UTF-8";
};

"issue28/desktop/ibus/panel/emoji" = {
favorites = [ "\137" "\396" "\395" "\397" "\322" "\\u00ad" "\176" "\\v" "\160" "\171" "\297" ];
favorites = [ "" "" "" "" "" "­" "°" " " " " "«" "" ];
};

"issue28/org/gnome/desktop/input-sources" = {
Expand Down
14 changes: 14 additions & 0 deletions output/unicode.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated via dconf2nix: https://github.com/nix-commmunity/dconf2nix
{ lib, ... }:

with lib.hm.gvariant;

{
dconf.settings = {
"org/gnome/font-manager" = {
compare-preview-text = "🤣";
preview-text = "příliš žluťoučký kůň úpěl ďábelské ódy\nprilis zlutoucky kun upel dabelske ody\n◌̍◌̍ff̍ ̍čěů\n◌̎\n";
};

};
}
9 changes: 1 addition & 8 deletions src/CommandLine.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,18 @@ data FileArgs = FileArgs
{ fileInput :: InputFilePath
, fileOutput :: OutputFilePath
, fileRoot :: Root
, fileEmojiSupport :: EmojiSupport
, fileVerbosity :: Verbosity
}

data StdinArgs = StdinArgs
{ stdinRoot :: Root
, stdinEmojiSupport :: EmojiSupport
, stdinVerbosity :: Verbosity
}

verbosityArgs :: Parser Verbosity
verbosityArgs =
flag Normal Verbose (long "verbose" <> help "Verbose mode (debug)")

emojiArgs :: Parser EmojiSupport
emojiArgs =
flag Disabled Enabled (long "emoji" <> short 'e' <> help "Enable emoji support (adds a bit of overhead)")

rootArgs :: Parser Root
rootArgs = Root <$> strOption
(long "root" <> short 'r' <> value T.empty <> help
Expand All @@ -53,12 +47,11 @@ fileArgs = fmap FileInput $ FileArgs
)
)
<*> rootArgs
<*> emojiArgs
<*> verbosityArgs

stdinArgs :: Parser Input
stdinArgs =
StdinInput <$> (StdinArgs <$> rootArgs <*> emojiArgs <*> verbosityArgs)
StdinInput <$> (StdinArgs <$> rootArgs <*> verbosityArgs)

versionInfo :: String
versionInfo = unlines
Expand Down
115 changes: 61 additions & 54 deletions src/DConf.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ module DConf
)
where

import Control.Monad ( replicateM )
import Data.Ix ( inRange )
import qualified Data.Map as Map
import Data.Maybe ( catMaybes )
import Data.Text ( Text )
import qualified Data.Text as T
import DConf.Data
import Text.Emoji ( emojis )
import Text.Parsec
import Data.Char

Expand Down Expand Up @@ -55,81 +57,86 @@ vInt64 = try $ do
many1 (string "int64 ") >> spaces
I64 . read <$> many1 digit

vTuple :: EmojiSupport -> Parsec Text () Value
vTuple es = do
rs <- bracket "(" ")" $ commaSeparated $ value es
vTuple :: Parsec Text () Value
vTuple = do
rs <- bracket "(" ")" $ commaSeparated $ value
case rs of
xs@(_ : _ : _) -> pure $ T xs
_ -> fail "Not a tuple"

vEmoji :: Parsec Text () Value
vEmoji =
let e = T.head . snd <$> emojis
f = choice $ char <$> e
s = many1 (string "'") *> f <* string "'"
d = many1 (char '"') *> f <* char '"'
in Emo <$> try (s <|> d)

vString :: Parsec Text () Text
vString = T.pack <$> (single <|> double)
where
single = bracket "'" "'" $ inputs "'"
double = bracket "\"" "\"" $ inputs "\""

lchar :: [Char] -> Parsec Text () Char
lchar extra = charExcept $ "\r\n" <> extra
octal = do
isOctal <- optionMaybe $ char '8'
let
base = case isOctal of
Just _ -> 8
Nothing -> 10
a <- digit
b <- digit
c <- digit
return $ chr $ (digitToInt a * base * base) + (digitToInt b * base) + digitToInt c
qchar :: Parsec Text () Char
lchar extra = charExcept $ "\r\n\\" <> extra

fromHexDigit :: Char -> Int
fromHexDigit n | inRange ('A', 'F') n = ord n - ord 'A' + 0xA
fromHexDigit n | inRange ('a', 'f') n = ord n - ord 'a' + 0xA
fromHexDigit n | inRange ('0', '9') n = ord n - ord '0'
fromHexDigit n = error $ "Expected a hexadecimal digit, '" ++ n : "' given"

hexNum :: Int -> Parsec Text () Int
hexNum l = do
digits <- replicateM l (fromHexDigit <$> hexDigit)
return $ foldl (\acc d -> 16 * acc + d) 0 digits

qchar :: Parsec Text () (Maybe Char)
qchar = do
_ <- char '\\'
octal <|> anyChar
inputs :: [Char] -> Parsec Text () String
inputs extra = many $ qchar <|> lchar extra
(
-- Unicode escapes of the form `\uxxxx` and `\Uxxxxxxxx` are supported, in hexadecimal.
(char 'u' *> (Just <$> (chr <$> hexNum 4)))
<|> (char 'U' *> (Just <$> (chr <$> hexNum 8)))
-- The usual control sequence escapes `\a`, `\b`, `\f`, `\n`, `\r`, `\t` and `\v` are supported.
<|> (char 'a' *> pure (Just '\a'))
<|> (char 'b' *> pure (Just '\b'))
<|> (char 'f' *> pure (Just '\f'))
<|> (char 'n' *> pure (Just '\n'))
<|> (char 'r' *> pure (Just '\r'))
<|> (char 't' *> pure (Just '\t'))
<|> (char 'v' *> pure (Just '\v'))
-- Additionally, a `\` before a newline character causes the newline to be ignored.
<|> (char '\n' *> pure Nothing)
-- Finally, any other character following `\` is copied literally (for example, `\"` or `\\`)
<|> (Just <$> anyChar))

emojiParser :: EmojiSupport -> [Parsec Text () Value]
emojiParser Enabled = [vEmoji]
emojiParser Disabled = []
inputs :: [Char] -> Parsec Text () String
inputs extra = catMaybes <$> (many $ qchar <|> (Just <$> lchar extra))

value :: EmojiSupport -> Parsec Text () Value
value es =
let xs = [vTyped es, vRecord es, vList es, vJson, vBool, vInt, vDouble, vUint32, vInt64]
ys = [fmap S vString, vTuple es, vVariant es]
in choice (xs ++ emojiParser es ++ ys)
value :: Parsec Text () Value
value = choice
[vTyped, vRecord, vList, vJson, vBool, vInt, vDouble, vUint32, vInt64, fmap S vString, vTuple, vVariant]

vVariant :: EmojiSupport -> Parsec Text () Value
vVariant es = fmap V $ bracket "<(" ")>" $ commaSeparated $ value es
vVariant :: Parsec Text () Value
vVariant = fmap V $ bracket "<(" ")>" $ commaSeparated $ value

vList :: EmojiSupport -> Parsec Text () Value
vList es = fmap L $ bracket "[" "]" $ commaSeparated $ value es
vList :: Parsec Text () Value
vList = fmap L $ bracket "[" "]" $ commaSeparated $ value

vTyped :: EmojiSupport -> Parsec Text () Value
vTyped es = do
vTyped :: Parsec Text () Value
vTyped = do
_ <- char '@'
_ <- anyChar
_ <- anyChar
_ <- spaces
value es
value

vJson :: Parsec Text () Value
vJson = try $ bracket "'" "'" $ do
_ <- lookAhead $ string "{"
js <- many (charExcept "\r\n\'")
pure $ Json (T.pack js)

vRecord :: EmojiSupport -> Parsec Text () Value
vRecord es = fmap R $ bracket "{" "}" $ commaSeparated $ do
vRecord :: Parsec Text () Value
vRecord = fmap R $ bracket "{" "}" $ commaSeparated $ do
k <- vString
_ <- char ':'
_ <- spaces
v <- value es
v <- value
return (k,v)

dconfHeader :: Parsec Text () Header
Expand All @@ -139,21 +146,21 @@ dconfHeader = bracket "[" "]" $ do
_ <- spaces
return $ T.pack h

kvLine :: EmojiSupport -> Parsec Text () (Key,Value)
kvLine es = do
kvLine :: Parsec Text () (Key,Value)
kvLine = do
k <- many1 $ satisfy $ \c -> isAlphaNum c || c == '-'
_ <- char '='
v <- value es
v <- value
_ <- endOfLine
return (Key $ T.pack k,v)

entryParser :: EmojiSupport -> Parsec Text () Entry
entryParser es = do
entryParser :: Parsec Text () Entry
entryParser = do
h <- dconfHeader <* endOfLine
kv <- many1 $ kvLine es
kv <- many1 $ kvLine
optional endOfLine
pure $ Entry h (Map.fromList kv)

dconfParser :: EmojiSupport -> Verbosity -> Parsec Text () [Entry]
dconfParser es Normal = manyTill (entryParser es) eof
dconfParser es Verbose = parserTraced "dconf" $ dconfParser es Normal
dconfParser :: Verbosity -> Parsec Text () [Entry]
dconfParser Normal = manyTill entryParser eof
dconfParser Verbose = parserTraced "dconf" $ dconfParser Normal
2 changes: 0 additions & 2 deletions src/DConf/Data.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import Data.Text ( Text )
newtype InputFilePath = InputFilePath FilePath deriving Show
newtype OutputFilePath = OutputFilePath FilePath deriving Show

data EmojiSupport = Enabled | Disabled
data Verbosity = Normal | Verbose

newtype Nix = Nix { unNix :: Text } deriving Show
Expand All @@ -21,7 +20,6 @@ data Value = S Text -- String
| I32 Int -- Int32
| I64 Int -- Int64
| D Double -- Double
| Emo Char -- Emoji (Unicode char)
| T [Value] -- Tuple of n-arity
| L [Value] -- List of values
| V [Value] -- Variant
Expand Down
12 changes: 6 additions & 6 deletions src/DConf2Nix.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ import Text.Parsec ( ParseError
, runParser
)

dconf2nixFile :: InputFilePath -> OutputFilePath -> Root -> EmojiSupport -> Verbosity -> IO ()
dconf2nixFile (InputFilePath input) (OutputFilePath output) root es v =
dconf2nixFile :: InputFilePath -> OutputFilePath -> Root -> Verbosity -> IO ()
dconf2nixFile (InputFilePath input) (OutputFilePath output) root v =
let run = handler (T.writeFile output) (T.appendFile output) root
parse = parseFromFile (dconfParser es v) input
parse = parseFromFile (dconfParser v) input
in run =<< parse

dconf2nixStdin :: Root -> EmojiSupport -> Verbosity -> IO ()
dconf2nixStdin root es v =
dconf2nixStdin :: Root -> Verbosity -> IO ()
dconf2nixStdin root v =
let run = handler T.putStr T.putStr root
parse = runParser (dconfParser es v) () "<stdin>"
parse = runParser (dconfParser v) () "<stdin>"
in run . parse =<< T.getContents

handler
Expand Down
Loading
Loading