toml-config: allow specifying keymaps via RMLVO
This commit is contained in:
parent
6e400655c6
commit
d911de6007
4 changed files with 272 additions and 31 deletions
|
|
@ -3,19 +3,20 @@ use {
|
|||
config::{
|
||||
ConfigKeymap,
|
||||
context::Context,
|
||||
extractor::{Extractor, ExtractorError, opt, str},
|
||||
extractor::{Extractor, ExtractorError, opt, str, val},
|
||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||
},
|
||||
toml::{
|
||||
toml_span::{Span, Spanned, SpannedExt},
|
||||
toml_span::{DespanExt, Span, Spanned, SpannedExt},
|
||||
toml_value::Value,
|
||||
},
|
||||
},
|
||||
indexmap::IndexMap,
|
||||
jay_config::{
|
||||
config_dir,
|
||||
keyboard::{Keymap, parse_keymap},
|
||||
keyboard::{Keymap, keymap_from_names, parse_keymap},
|
||||
},
|
||||
kbvm::xkb::rmlvo::Group,
|
||||
std::{io, path::PathBuf},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
|
@ -28,9 +29,11 @@ pub enum KeymapParserError {
|
|||
Extractor(#[from] ExtractorError),
|
||||
#[error("The keymap is invalid")]
|
||||
Invalid,
|
||||
#[error("Keymap table must contain at least one of `name`, `map`")]
|
||||
#[error("Keymap table must contain at least one of `name`, `map`, `path`, `rmlvo`")]
|
||||
MissingField,
|
||||
#[error("Keymap must have both `name` and `map` fields in this context")]
|
||||
#[error(
|
||||
"Keymap must have both `name` and one of `map`, `path`, `rmlvo` fields in this context"
|
||||
)]
|
||||
DefinitionRequired,
|
||||
#[error("Could not read {0}")]
|
||||
ReadFile(String, #[source] io::Error),
|
||||
|
|
@ -56,14 +59,27 @@ impl Parser for KeymapParser<'_> {
|
|||
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||
) -> ParseResult<Self> {
|
||||
let mut ext = Extractor::new(self.cx, span, table);
|
||||
let (mut name_val, mut map_val, mut path) =
|
||||
ext.extract((opt(str("name")), opt(str("map")), opt(str("path"))))?;
|
||||
if map_val.is_some() && path.is_some() {
|
||||
let (mut name_val, mut map_val, mut path, mut rmlvo) = ext.extract((
|
||||
opt(str("name")),
|
||||
opt(str("map")),
|
||||
opt(str("path")),
|
||||
opt(val("rmlvo")),
|
||||
))?;
|
||||
if map_val.is_some() as u32 + path.is_some() as u32 + rmlvo.is_some() as u32 > 1 {
|
||||
log::warn!(
|
||||
"Both `name` and `path` are specified. Ignoring `path`: {}",
|
||||
self.cx.error3(span)
|
||||
"At most one of `map`, `path`, and `rmlvo` should be specified: {}",
|
||||
self.cx.error3(span),
|
||||
);
|
||||
path = None;
|
||||
let ignore_path = map_val.is_some();
|
||||
let ignore_rmlvo = map_val.is_some() || path.is_some();
|
||||
if ignore_path && path.is_some() {
|
||||
log::warn!("Ignoring `path`");
|
||||
path = None;
|
||||
}
|
||||
if ignore_rmlvo && rmlvo.is_some() {
|
||||
log::warn!("Ignoring `rmlvo`");
|
||||
rmlvo = None;
|
||||
}
|
||||
}
|
||||
let file_content;
|
||||
if let Some(path) = path {
|
||||
|
|
@ -78,10 +94,17 @@ impl Parser for KeymapParser<'_> {
|
|||
};
|
||||
map_val = Some(file_content.as_str().spanned(path.span));
|
||||
}
|
||||
if self.definition && (name_val.is_none() || map_val.is_none()) {
|
||||
let mut map = None;
|
||||
if let Some(val) = &map_val {
|
||||
map = Some(parse(val.span, val.value)?);
|
||||
}
|
||||
if let Some(val) = rmlvo {
|
||||
map = Some(val.parse(&mut RmlvoParser(self.cx))?);
|
||||
}
|
||||
if self.definition && (name_val.is_none() || map.is_none()) {
|
||||
return Err(KeymapParserError::DefinitionRequired.spanned(span));
|
||||
}
|
||||
if !self.definition && map_val.is_some() {
|
||||
if !self.definition && map.is_some() {
|
||||
if let Some(val) = name_val {
|
||||
log::warn!(
|
||||
"Cannot use both `name` and `map` in this position. Ignoring `name`: {}",
|
||||
|
|
@ -101,19 +124,70 @@ impl Parser for KeymapParser<'_> {
|
|||
self.cx.used.borrow_mut().keymaps.push(name.into());
|
||||
}
|
||||
}
|
||||
let res = match (name_val, map_val) {
|
||||
(Some(name_val), Some(map_val)) => ConfigKeymap::Defined {
|
||||
let res = match (name_val, map) {
|
||||
(Some(name_val), Some(map)) => ConfigKeymap::Defined {
|
||||
name: name_val.value.to_string(),
|
||||
map: parse(map_val.span, map_val.value)?,
|
||||
map,
|
||||
},
|
||||
(Some(name_val), None) => ConfigKeymap::Named(name_val.value.to_string()),
|
||||
(None, Some(map_val)) => ConfigKeymap::Literal(parse(map_val.span, map_val.value)?),
|
||||
(None, Some(map)) => ConfigKeymap::Literal(map),
|
||||
(None, None) => return Err(KeymapParserError::MissingField.spanned(span)),
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
struct RmlvoParser<'a>(&'a Context<'a>);
|
||||
|
||||
impl Parser for RmlvoParser<'_> {
|
||||
type Value = Keymap;
|
||||
type Error = KeymapParserError;
|
||||
const EXPECTED: &'static [DataType] = &[DataType::Table];
|
||||
|
||||
fn parse_table(
|
||||
&mut self,
|
||||
span: Span,
|
||||
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||
) -> ParseResult<Self> {
|
||||
let mut ext = Extractor::new(self.0, span, table);
|
||||
let (rules, model, layout, variants, options) = ext.extract((
|
||||
opt(str("rules")),
|
||||
opt(str("model")),
|
||||
opt(str("layout")),
|
||||
opt(str("variants")),
|
||||
opt(str("options")),
|
||||
))?;
|
||||
let mut groups = None::<Vec<_>>;
|
||||
if layout.is_some() || variants.is_some() {
|
||||
groups = Some(
|
||||
Group::from_layouts_and_variants(
|
||||
layout.despan().unwrap_or_default(),
|
||||
variants.despan().unwrap_or_default(),
|
||||
)
|
||||
.map(|g| jay_config::keyboard::Group {
|
||||
layout: g.layout,
|
||||
variant: g.variant,
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
let mut options_vec = None::<Vec<_>>;
|
||||
if let Some(options) = options {
|
||||
options_vec = Some(options.value.split(",").collect());
|
||||
}
|
||||
let map = keymap_from_names(
|
||||
rules.despan(),
|
||||
model.despan(),
|
||||
groups.as_deref(),
|
||||
options_vec.as_deref(),
|
||||
);
|
||||
match map.is_valid() {
|
||||
true => Ok(map),
|
||||
false => Err(KeymapParserError::Invalid.spanned(span)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(span: Span, string: &str) -> Result<Keymap, Spanned<KeymapParserError>> {
|
||||
let map = parse_keymap(string);
|
||||
match map.is_valid() {
|
||||
|
|
|
|||
|
|
@ -1643,7 +1643,7 @@
|
|||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Defines a keymap by its XKB representation.\n\n- Example:\n\n ```toml\n keymap = \"\"\"\n xkb_keymap {\n xkb_keycodes { include \"evdev+aliases(qwerty)\" };\n xkb_types { include \"complete\" };\n xkb_compat { include \"complete\" };\n xkb_symbols { include \"pc+us+inet(evdev)\" };\n };\n \"\"\"\n ```\n"
|
||||
"description": "Defines a keymap by its XKB representation.\n\n- Example 1:\n\n ```toml\n keymap = \"\"\"\n xkb_keymap {\n xkb_keycodes { include \"evdev+aliases(qwerty)\" };\n xkb_types { include \"complete\" };\n xkb_compat { include \"complete\" };\n xkb_symbols { include \"pc+us+inet(evdev)\" };\n };\n \"\"\"\n ```\n\n- Example 2:\n\n ```toml\n keymap.rmlvo = {\n layout = \"us,de\",\n variants = \"dvorak\",\n options = \"grp:ctrl_space_toggle\",\n }\n ```\n"
|
||||
},
|
||||
{
|
||||
"description": "Defines or references a keymap.\n\n- Example:\n\n ```toml\n keymap.name = \"my-keymap\"\n\n [[keymaps]]\n name = \"my-keymap\"\n path = \"./my-keymap.xkb\"\n ```\n",
|
||||
|
|
@ -1655,11 +1655,15 @@
|
|||
},
|
||||
"map": {
|
||||
"type": "string",
|
||||
"description": "Defines a keymap by its XKB representation.\n\nFor each keymap defined in the top-level `keymaps` array, exactly one of `map`\nand `path` has to be defined.\n"
|
||||
"description": "Defines a keymap by its XKB representation.\n\nFor each keymap defined in the top-level `keymaps` array, exactly one of\n`map`, `path`, and `rmlvo` has to be defined.\n"
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "Loads a keymap's XKB representation from a file.\n\nIf the path is relative, it will be interpreted relative to the Jay config\ndirectory.\n\nFor each keymap defined in the top-level `keymaps` array, exactly one of `map`\nand `path` has to be defined.\n"
|
||||
"description": "Loads a keymap's XKB representation from a file.\n\nIf the path is relative, it will be interpreted relative to the Jay config\ndirectory.\n\nFor each keymap defined in the top-level `keymaps` array, exactly one of\n`map`, `path`, and `rmlvo` has to be defined.\n"
|
||||
},
|
||||
"rmlvo": {
|
||||
"description": "Creates a keymap from RMLVO names.\n\nFor each keymap defined in the top-level `keymaps` array, exactly one of\n`map`, `path`, and `rmlvo` has to be defined.\n",
|
||||
"$ref": "#/$defs/Rmlvo"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
|
|
@ -1864,6 +1868,33 @@
|
|||
"delay"
|
||||
]
|
||||
},
|
||||
"Rmlvo": {
|
||||
"description": "An RMLVO keymap definition.\n\nIf a parameter is not given, a value from the environment or a default is used:\n\n| name | default |\n| ---------------------- | ---------------------- |\n| `XKB_DEFAULT_RULES` | `evdev` |\n| `XKB_DEFAULT_MODEL` | `pc105` |\n| `XKB_DEFAULT_LAYOUT` | `us` |\n| `XKB_DEFAULT_VARIANTS` | |\n| `XKB_DEFAULT_OPTIONS` | |\n\n- Example:\n\n ```toml\n keymap.rmlvo = { layout = \"us,de\", variants = \"dvorak\" }\n ```\n",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"rules": {
|
||||
"type": "string",
|
||||
"description": "The name of the rules file."
|
||||
},
|
||||
"model": {
|
||||
"type": "string",
|
||||
"description": "The name of the device model."
|
||||
},
|
||||
"layout": {
|
||||
"type": "string",
|
||||
"description": "A comma-separated list of layouts."
|
||||
},
|
||||
"variants": {
|
||||
"type": "string",
|
||||
"description": "A comma-separated list of variants."
|
||||
},
|
||||
"options": {
|
||||
"type": "string",
|
||||
"description": "A comma-separated list of options."
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
},
|
||||
"SimpleActionName": {
|
||||
"type": "string",
|
||||
"description": "The name of a `simple` Action.\n\nWhen used inside a window rule, the following actions apply to the matched window\ninstead fo the focused window:\n\n- `move-left`\n- `move-down`\n- `move-up`\n- `move-right`\n- `split-horizontal`\n- `split-vertical`\n- `toggle-split`\n- `tile-horizontal`\n- `tile-vertical`\n- `toggle-split`\n- `show-single`\n- `show-all`\n- `toggle-fullscreen`\n- `enter-fullscreen`\n- `exit-fullscreen`\n- `close`\n- `toggle-floating`\n- `float`\n- `tile`\n- `toggle-float-pinned`\n- `pin-float`\n- `unpin-float`\n\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-q = \"quit\"\n ```\n",
|
||||
|
|
|
|||
|
|
@ -3557,7 +3557,7 @@ Values of this type should have one of the following forms:
|
|||
|
||||
Defines a keymap by its XKB representation.
|
||||
|
||||
- Example:
|
||||
- Example 1:
|
||||
|
||||
```toml
|
||||
keymap = """
|
||||
|
|
@ -3570,6 +3570,16 @@ Defines a keymap by its XKB representation.
|
|||
"""
|
||||
```
|
||||
|
||||
- Example 2:
|
||||
|
||||
```toml
|
||||
keymap.rmlvo = {
|
||||
layout = "us,de",
|
||||
variants = "dvorak",
|
||||
options = "grp:ctrl_space_toggle",
|
||||
}
|
||||
```
|
||||
|
||||
#### A table
|
||||
|
||||
Defines or references a keymap.
|
||||
|
|
@ -3602,8 +3612,8 @@ The table has the following fields:
|
|||
|
||||
Defines a keymap by its XKB representation.
|
||||
|
||||
For each keymap defined in the top-level `keymaps` array, exactly one of `map`
|
||||
and `path` has to be defined.
|
||||
For each keymap defined in the top-level `keymaps` array, exactly one of
|
||||
`map`, `path`, and `rmlvo` has to be defined.
|
||||
|
||||
The value of this field should be a string.
|
||||
|
||||
|
|
@ -3614,11 +3624,20 @@ The table has the following fields:
|
|||
If the path is relative, it will be interpreted relative to the Jay config
|
||||
directory.
|
||||
|
||||
For each keymap defined in the top-level `keymaps` array, exactly one of `map`
|
||||
and `path` has to be defined.
|
||||
For each keymap defined in the top-level `keymaps` array, exactly one of
|
||||
`map`, `path`, and `rmlvo` has to be defined.
|
||||
|
||||
The value of this field should be a string.
|
||||
|
||||
- `rmlvo` (optional):
|
||||
|
||||
Creates a keymap from RMLVO names.
|
||||
|
||||
For each keymap defined in the top-level `keymaps` array, exactly one of
|
||||
`map`, `path`, and `rmlvo` has to be defined.
|
||||
|
||||
The value of this field should be a [Rmlvo](#types-Rmlvo).
|
||||
|
||||
|
||||
<a name="types-Libei"></a>
|
||||
### `Libei`
|
||||
|
|
@ -4099,6 +4118,62 @@ The table has the following fields:
|
|||
The numbers should be integers.
|
||||
|
||||
|
||||
<a name="types-Rmlvo"></a>
|
||||
### `Rmlvo`
|
||||
|
||||
An RMLVO keymap definition.
|
||||
|
||||
If a parameter is not given, a value from the environment or a default is used:
|
||||
|
||||
| name | default |
|
||||
| ---------------------- | ---------------------- |
|
||||
| `XKB_DEFAULT_RULES` | `evdev` |
|
||||
| `XKB_DEFAULT_MODEL` | `pc105` |
|
||||
| `XKB_DEFAULT_LAYOUT` | `us` |
|
||||
| `XKB_DEFAULT_VARIANTS` | |
|
||||
| `XKB_DEFAULT_OPTIONS` | |
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
keymap.rmlvo = { layout = "us,de", variants = "dvorak" }
|
||||
```
|
||||
|
||||
Values of this type should be tables.
|
||||
|
||||
The table has the following fields:
|
||||
|
||||
- `rules` (optional):
|
||||
|
||||
The name of the rules file.
|
||||
|
||||
The value of this field should be a string.
|
||||
|
||||
- `model` (optional):
|
||||
|
||||
The name of the device model.
|
||||
|
||||
The value of this field should be a string.
|
||||
|
||||
- `layout` (optional):
|
||||
|
||||
A comma-separated list of layouts.
|
||||
|
||||
The value of this field should be a string.
|
||||
|
||||
- `variants` (optional):
|
||||
|
||||
A comma-separated list of variants.
|
||||
|
||||
The value of this field should be a string.
|
||||
|
||||
- `options` (optional):
|
||||
|
||||
A comma-separated list of options.
|
||||
|
||||
The value of this field should be a string.
|
||||
|
||||
|
||||
<a name="types-SimpleActionName"></a>
|
||||
### `SimpleActionName`
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Keymap:
|
|||
description: |
|
||||
Defines a keymap by its XKB representation.
|
||||
|
||||
- Example:
|
||||
- Example 1:
|
||||
|
||||
```toml
|
||||
keymap = """
|
||||
|
|
@ -19,6 +19,16 @@ Keymap:
|
|||
};
|
||||
"""
|
||||
```
|
||||
|
||||
- Example 2:
|
||||
|
||||
```toml
|
||||
keymap.rmlvo = {
|
||||
layout = "us,de",
|
||||
variants = "dvorak",
|
||||
options = "grp:ctrl_space_toggle",
|
||||
}
|
||||
```
|
||||
- kind: table
|
||||
description: |
|
||||
Defines or references a keymap.
|
||||
|
|
@ -50,8 +60,8 @@ Keymap:
|
|||
description: |
|
||||
Defines a keymap by its XKB representation.
|
||||
|
||||
For each keymap defined in the top-level `keymaps` array, exactly one of `map`
|
||||
and `path` has to be defined.
|
||||
For each keymap defined in the top-level `keymaps` array, exactly one of
|
||||
`map`, `path`, and `rmlvo` has to be defined.
|
||||
path:
|
||||
kind: string
|
||||
required: false
|
||||
|
|
@ -61,8 +71,59 @@ Keymap:
|
|||
If the path is relative, it will be interpreted relative to the Jay config
|
||||
directory.
|
||||
|
||||
For each keymap defined in the top-level `keymaps` array, exactly one of `map`
|
||||
and `path` has to be defined.
|
||||
For each keymap defined in the top-level `keymaps` array, exactly one of
|
||||
`map`, `path`, and `rmlvo` has to be defined.
|
||||
rmlvo:
|
||||
ref: Rmlvo
|
||||
required: false
|
||||
description: |
|
||||
Creates a keymap from RMLVO names.
|
||||
|
||||
For each keymap defined in the top-level `keymaps` array, exactly one of
|
||||
`map`, `path`, and `rmlvo` has to be defined.
|
||||
|
||||
|
||||
Rmlvo:
|
||||
description: |
|
||||
An RMLVO keymap definition.
|
||||
|
||||
If a parameter is not given, a value from the environment or a default is used:
|
||||
|
||||
| name | default |
|
||||
| ---------------------- | ---------------------- |
|
||||
| `XKB_DEFAULT_RULES` | `evdev` |
|
||||
| `XKB_DEFAULT_MODEL` | `pc105` |
|
||||
| `XKB_DEFAULT_LAYOUT` | `us` |
|
||||
| `XKB_DEFAULT_VARIANTS` | |
|
||||
| `XKB_DEFAULT_OPTIONS` | |
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
keymap.rmlvo = { layout = "us,de", variants = "dvorak" }
|
||||
```
|
||||
kind: table
|
||||
fields:
|
||||
rules:
|
||||
kind: string
|
||||
required: false
|
||||
description: The name of the rules file.
|
||||
model:
|
||||
kind: string
|
||||
required: false
|
||||
description: The name of the device model.
|
||||
layout:
|
||||
kind: string
|
||||
required: false
|
||||
description: A comma-separated list of layouts.
|
||||
variants:
|
||||
kind: string
|
||||
required: false
|
||||
description: A comma-separated list of variants.
|
||||
options:
|
||||
kind: string
|
||||
required: false
|
||||
description: A comma-separated list of options.
|
||||
|
||||
|
||||
Action:
|
||||
|
|
@ -2998,7 +3059,7 @@ Config:
|
|||
required: false
|
||||
description: |
|
||||
Sets the fallback output mode.
|
||||
|
||||
|
||||
The default is `cursor`.
|
||||
|
||||
- Example:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue