Skip to content

Commit

Permalink
Type fixes (#84)
Browse files Browse the repository at this point in the history
  • Loading branch information
PatrickMassot authored Feb 3, 2024
1 parent 6c76ed9 commit 4f67b98
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 55 deletions.
95 changes: 58 additions & 37 deletions kalamine/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import re
import sys
from pathlib import Path
from typing import Dict, List, Union
from typing import Dict, List, Union, Optional, TypeVar, Type, Set
from dataclasses import dataclass

import click
import tomli
Expand Down Expand Up @@ -100,8 +101,8 @@ def load_descriptor(file_path: Path) -> Dict:
with file_path.open(encoding="utf-8") as file:
return yaml.load(file, Loader=yaml.SafeLoader)

with file_path.open(mode="rb") as file:
return tomli.load(file)
with file_path.open(mode="rb") as dfile:
return tomli.load(dfile)


###
Expand All @@ -123,7 +124,27 @@ def load_descriptor(file_path: Path) -> Dict:
"1dk_shift": "'",
}

GEOMETRY = load_data("geometry.yaml")
@dataclass
class RowDescr:
offset: int
keys: List[str]

T = TypeVar('T', bound='GeometryDescr')

@dataclass
class GeometryDescr:
template: str
rows: List[RowDescr]

@classmethod
def from_dict(cls: Type[T], src: Dict) -> T:
return cls(template=src['template'],
rows = [RowDescr(**row) for row in src['rows']])

geometry_data = load_data( "geometry.yaml")

GEOMETRY = {key: GeometryDescr.from_dict(val)
for key, val in geometry_data.items()}


###
Expand All @@ -138,9 +159,9 @@ def __init__(self, filepath: Path) -> None:
"""Import a keyboard layout to instanciate the object."""

# initialize a blank layout
self.layers = [{}, {}, {}, {}, {}, {}]
self.dk_set = set()
self.dead_keys = {} # dictionary subset of DEAD_KEYS
self.layers: Dict[Layer, Dict[str, str]] = {layer: {} for layer in Layer}
self.dk_set: Set[str] = set()
self.dead_keys: Dict[str, Dict[str, str]] = {} # dictionary subset of DEAD_KEYS
self.meta = CONFIG.copy() # default parameters, hardcoded
self.has_altgr = False
self.has_1dk = False
Expand Down Expand Up @@ -174,7 +195,7 @@ def __init__(self, filepath: Path) -> None:
self.meta["lastChange"] = datetime.date.today().isoformat()

# keyboard layers: self.layers & self.dead_keys
rows = GEOMETRY[self.meta["geometry"]]["rows"]
rows = GEOMETRY[self.meta["geometry"]].rows
if "full" in cfg:
full = text_to_lines(cfg["full"])
self._parse_template(full, rows, Layer.BASE)
Expand Down Expand Up @@ -227,13 +248,13 @@ def layout_has_char(char: str) -> bool:

self.dead_keys = {}
for dk in DEAD_KEYS:
id = dk["char"]
id = dk.char
if id not in self.dk_set:
continue

self.dead_keys[id] = {}
deadkey = self.dead_keys[id]
deadkey[id] = dk["alt_self"]
deadkey[id] = dk.alt_self

if id == ODK_ID:
self.has_1dk = True
Expand All @@ -242,29 +263,30 @@ def layout_has_char(char: str) -> bool:
continue
for i in [Layer.ODK_SHIFT, Layer.ODK]:
if key_name in self.layers[i]:
deadkey[self.layers[i - Layer.ODK][key_name]] = self.layers[
deadkey[self.layers[i.necromance()][key_name]] = self.layers[
i
][key_name]
for space in all_spaces:
deadkey[space] = spc["1dk"]

else:
base = dk["base"]
alt = dk["alt"]
base = dk.base
alt = dk.alt
for i in range(len(base)):
if layout_has_char(base[i]):
deadkey[base[i]] = alt[i]
for space in all_spaces:
deadkey[space] = dk["alt_space"]
deadkey[space] = dk.alt_space

def _parse_template(self, template: str, rows: List[str], layer_number: Layer):
def _parse_template(self, template: List[str],
rows: List[RowDescr], layer_number: Layer) -> None:
"""Extract a keyboard layer from a template."""

j = 0
col_offset = 0 if layer_number == Layer.BASE else 2
for row in rows:
i = row["offset"] + col_offset
keys = row["keys"]
i = row.offset + col_offset
keys = row.keys

base = list(template[2 + j * 3])
shift = list(template[1 + j * 3])
Expand All @@ -287,13 +309,13 @@ def _parse_template(self, template: str, rows: List[str], layer_number: Layer):
# shift_key = base_key.upper()

if base_key != " ":
self.layers[layer_number + 0][key] = base_key
self.layers[layer_number][key] = base_key
if shift_key != " ":
self.layers[layer_number + 1][key] = shift_key
self.layers[layer_number.next()][key] = shift_key

for dk in DEAD_KEYS:
if base_key == dk["char"] or shift_key == dk["char"]:
self.dk_set.add(dk["char"])
if base_key == dk.char or shift_key == dk.char:
self.dk_set.add(dk.char)

i += 6
j += 1
Expand All @@ -303,8 +325,8 @@ def _parse_template(self, template: str, rows: List[str], layer_number: Layer):
#

def _fill_template(
self, template: str, rows: List[str], layer_number: Layer
) -> str:
self, template: List[str], rows: List[RowDescr], layer_number: Layer
) -> List[str]:
"""Fill a template with a keyboard layer."""

if layer_number == Layer.BASE:
Expand All @@ -316,8 +338,8 @@ def _fill_template(

j = 0
for row in rows:
i = row["offset"] + col_offset
keys = row["keys"]
i = row.offset + col_offset
keys = row.keys

base = list(template[2 + j * 3])
shift = list(template[1 + j * 3])
Expand All @@ -328,8 +350,8 @@ def _fill_template(
base_key = self.layers[layer_number][key]

shift_key = " "
if key in self.layers[layer_number + 1]:
shift_key = self.layers[layer_number + 1][key]
if key in self.layers[layer_number.next()]:
shift_key = self.layers[layer_number.next()][key]

dead_base = len(base_key) == 2 and base_key[0] == "*"
dead_shift = len(shift_key) == 2 and shift_key[0] == "*"
Expand Down Expand Up @@ -359,13 +381,12 @@ def _fill_template(

return template

def _get_geometry(self, layers: Union[List[Layer], None] = None) -> str:
def _get_geometry(self, layers: Optional[List[Layer]] = None) -> List[str]:
"""`geometry` view of the requested layers."""
if layers is None:
layers = [Layer.BASE]
layers = layers or [Layer.BASE]

rows = GEOMETRY[self.geometry]["rows"]
template = GEOMETRY[self.geometry]["template"].split("\n")[:-1]
rows = GEOMETRY[self.geometry].rows
template = GEOMETRY[self.geometry].template.split("\n")[:-1]
for i in layers:
template = self._fill_template(template, rows, i)
return template
Expand All @@ -384,17 +405,17 @@ def geometry(self, value: str) -> None:
self.meta["geometry"] = shape

@property
def base(self) -> str:
def base(self) -> List[str]:
"""Base + 1dk layers."""
return self._get_geometry([0, Layer.ODK])
return self._get_geometry([Layer.BASE, Layer.ODK])

@property
def full(self) -> str:
def full(self) -> List[str]:
"""Base + AltGr layers."""
return self._get_geometry([0, Layer.ALTGR])
return self._get_geometry([Layer.BASE, Layer.ALTGR])

@property
def altgr(self) -> str:
def altgr(self) -> List[str]:
"""AltGr layer only."""
return self._get_geometry([Layer.ALTGR])

Expand Down
32 changes: 16 additions & 16 deletions kalamine/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

DK_INDEX = {}
for dk in DEAD_KEYS:
DK_INDEX[dk["char"]] = dk
DK_INDEX[dk.char] = dk


###
Expand Down Expand Up @@ -49,7 +49,7 @@ def xkb_keymap(layout: "KeyboardLayout", xkbcomp: bool = False) -> List[str]:
odk_symbol = "ISO_Level5_Latch" if eight_level else "ISO_Level3_Latch"
max_length = 16 # `ISO_Level3_Latch` should be the longest symbol name

output = []
output: List[str] = []
for key_name in LAYER_KEYS:
if key_name.startswith("-"): # separator
if output:
Expand All @@ -59,13 +59,13 @@ def xkb_keymap(layout: "KeyboardLayout", xkbcomp: bool = False) -> List[str]:

symbols = []
description = " //"
for layer in layout.layers:
for layer in layout.layers.values():
if key_name in layer:
keysym = layer[key_name]
desc = keysym
# dead key?
if keysym in DK_INDEX:
name = DK_INDEX[keysym]["name"]
name = DK_INDEX[keysym].name
desc = layout.dead_keys[keysym][keysym]
symbol = odk_symbol if keysym == ODK_ID else f"dead_{name}"
# regular key: use a keysym if possible, utf-8 otherwise
Expand Down Expand Up @@ -296,7 +296,7 @@ def klc_deadkeys(layout: "KeyboardLayout") -> List[str]:
continue
dk = layout.dead_keys[k]

output.append(f"// DEADKEY: {DK_INDEX[k]['name'].upper()} //" + "{{{")
output.append(f"// DEADKEY: {DK_INDEX[k].name.upper()} //" + "{{{")
output.append(f"DEADKEY\t{hex_ord(dk[' '])}")

for base, alt in dk.items():
Expand Down Expand Up @@ -328,7 +328,7 @@ def klc_dk_index(layout: "KeyboardLayout") -> List[str]:
if k not in layout.dead_keys:
continue
dk = layout.dead_keys[k]
output.append(f"{hex_ord(dk[' '])}\t\"{DK_INDEX[k]['name'].upper()}\"")
output.append(f"{hex_ord(dk[' '])}\t\"{DK_INDEX[k].name.upper()}\"")
return output


Expand All @@ -338,7 +338,7 @@ def klc_dk_index(layout: "KeyboardLayout") -> List[str]:
#


def osx_keymap(layout: "KeyboardLayout") -> List[str]:
def osx_keymap(layout: "KeyboardLayout") -> List[List[str]]:
"""macOS layout, main part."""

ret_str = []
Expand All @@ -356,7 +356,7 @@ def has_dead_keys(letter):
return True
return False

output = []
output: List[str] = []
for key_name in LAYER_KEYS:
if key_name in ["ae13", "ab11"]: # ABNT / JIS keys
continue # these two keys are not supported yet
Expand All @@ -373,7 +373,7 @@ def has_dead_keys(letter):
if key_name in layer:
key = layer[key_name]
if key in layout.dead_keys:
symbol = f"dead_{DK_INDEX[key]['name']}"
symbol = f"dead_{DK_INDEX[key].name}"
final_key = False
else:
symbol = xml_proof(key.upper() if caps else key)
Expand All @@ -398,7 +398,7 @@ def osx_actions(layout: "KeyboardLayout") -> List[str]:
def when(state, action):
state_attr = f'state="{state}"'.ljust(18)
if action in layout.dead_keys:
action_attr = f"next=\"{DK_INDEX[action]['name']}\""
action_attr = f"next=\"{DK_INDEX[action].name}\""
elif action.startswith("dead_"):
action_attr = f'next="{action[5:]}"'
else:
Expand All @@ -414,12 +414,12 @@ def append_actions(symbol, actions):

# dead key definitions
for key in layout.dead_keys:
name = DK_INDEX[key]["name"]
name = DK_INDEX[key].name
term = layout.dead_keys[key][key]
ret_actions.append(f'<action id="dead_{name}">')
ret_actions.append(f' <when state="none" next="{name}" />')
if name == "1dk" and term in layout.dead_keys:
nested_dk = DK_INDEX[term]["name"]
nested_dk = DK_INDEX[term].name
ret_actions.append(f' <when state="1dk" next="{nested_dk}" />')
ret_actions.append("</action>")
continue
Expand All @@ -436,7 +436,7 @@ def append_actions(symbol, actions):
continue

key = layout.layers[i][key_name]
if i and key == layout.layers[0][key_name]:
if i and key == layout.layers[Layer.BASE][key_name]:
continue
if key in layout.dead_keys:
continue
Expand All @@ -445,15 +445,15 @@ def append_actions(symbol, actions):
for k in DK_INDEX:
if k in layout.dead_keys:
if key in layout.dead_keys[k]:
actions.append((DK_INDEX[k]["name"], layout.dead_keys[k][key]))
actions.append((DK_INDEX[k].name, layout.dead_keys[k][key]))
if actions:
append_actions(xml_proof(key), actions)

# spacebar actions
actions = []
for k in DK_INDEX:
if k in layout.dead_keys:
actions.append((DK_INDEX[k]["name"], layout.dead_keys[k][" "]))
actions.append((DK_INDEX[k].name, layout.dead_keys[k][" "]))
append_actions("&#x0020;", actions) # space
append_actions("&#x00a0;", actions) # no-break space
append_actions("&#x202f;", actions) # fine no-break space
Expand All @@ -469,7 +469,7 @@ def osx_terminators(layout: "KeyboardLayout") -> List[str]:
if key not in layout.dead_keys:
continue
dk = layout.dead_keys[key]
name = DK_INDEX[key]["name"]
name = DK_INDEX[key].name
term = dk[key]
if name == "1dk" and term in layout.dead_keys:
term = dk[" "]
Expand Down
Loading

0 comments on commit 4f67b98

Please sign in to comment.