All files / src/types/kmx/kmx-plus-builder build-keys.ts

41.14% Statements 72/175
100% Branches 0/0
0% Functions 0/1
41.14% Lines 72/175

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 1761x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                                                                                                                                                                                                                
 
import { constants } from "@keymanapp/ldml-keyboard-constants";
import { KMXPlus } from "@keymanapp/common-types";
import { build_strs_index, BUILDER_STR_REF, BUILDER_STRS } from "./build-strs.js";
import { build_list_index, BUILDER_LIST, BUILDER_LIST_REF } from "./build-list.js";
import { BUILDER_SECTION, BUILDER_U32CHAR } from "./builder-section.js";
 
import KeysFlick = KMXPlus.KeysFlick;
import KMXPlusData = KMXPlus.KMXPlusData;
import StrsItem = KMXPlus.StrsItem;
 
/* ------------------------------------------------------------------
 * keys section
   ------------------------------------------------------------------ */
 
/**
 * This struct is a single <key> in the keys keybag
 */
interface BUILDER_KEYS_KEY {
  to: BUILDER_STR_REF | BUILDER_U32CHAR; // str or single codepoint
  flags: number;
  id: BUILDER_STR_REF; // str with original key id
  _id: string; // original key id, for sorting
  switch: BUILDER_STR_REF; // str with layer of new l
  width: number; // ceil((width||1)*10), so 12 for width 1.2
  longPress: BUILDER_LIST_REF; // list of longPress sequences
  longPressDefault: BUILDER_STR_REF; // str with the default longPress target
  multiTap: BUILDER_LIST_REF; // list of multiTap sequences
  flicks: number; // index into the flicks[] subtable for this flick list
};
 
/**
 * This is a <flicks>, a list of <flick> elements.
 */
interface BUILDER_KEYS_FLICKS {
  count: number; // number of BUILDER_KEYS_FLICK entries in this flick list
  flick: number; // index into the flick[] subtable of the first flick in the list
  id: BUILDER_STR_REF; // str with the original id of this flicks
  _id: string; // copy of the flicks id, used for sorting during build
  _flicks: KeysFlick[]; // temporary copy of KeysFlick object
};
 
/**
 * This is a single <flick> element.
 */
interface BUILDER_KEYS_FLICK {
  directions: BUILDER_LIST_REF; // list of cardinal/intercardinal directions
  to: BUILDER_STR_REF; // str
};
 
 
interface BUILDER_KEYS_KMAP {
  vkey: number;
  mod: number;
  key: number; //index to keys.key
};
 
/**
 * Builder for the 'keys' section
 */
export interface BUILDER_KEYS extends BUILDER_SECTION {
  keyCount: number;
  flicksCount: number;
  flickCount: number;
  kmapCount: number;
  keys: BUILDER_KEYS_KEY[];
  flicks: BUILDER_KEYS_FLICKS[];
  flick: BUILDER_KEYS_FLICK[];
  kmap: BUILDER_KEYS_KMAP[];
};
 
export function build_keys(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_list: BUILDER_LIST): BUILDER_KEYS {
  if(kmxplus.keys.keys.length == 0 &&
      (kmxplus.keys.flicks.length <= 1)) { // if no keys and only the 'null' flick.
    return null;
  }

  const keys: BUILDER_KEYS = {
    ident: constants.hex_section_id(constants.section.keys),
    size: 0,
    keyCount: kmxplus.keys.keys.length,
    flicksCount: kmxplus.keys.flicks.length,
    flickCount: 0,
    kmapCount: kmxplus.keys.kmap.length,
    keys: [],
    flicks: [],
    flick: [],
    kmap: [],
    _offset: 0,
  };

  // flicks first: the keys will need to index into the flicks table.

  // Note that per the Keys class and spec, there is always a flicks=0 meaning 'no flicks'
  keys.flicks = kmxplus.keys.flicks.map((flicks) => {
    const result : BUILDER_KEYS_FLICKS = {
      count: flicks.flicks.length,
      flick: keys.flick.length, // index of first flick
      id: build_strs_index(sect_strs, flicks.id),
      _id: flicks.id.value,
      _flicks: flicks.flicks,
    };
    return result;
  });
  // Sort the flicks array by id
  keys.flicks.sort((a, b) => StrsItem.binaryStringCompare(a._id, b._id));
  // now, allocate 'flick' entries for each 'flicks'
  keys.flicks.forEach((flicks) => {
    flicks._flicks.forEach((flick) => {
      keys.flick.push({
        directions: build_list_index(sect_list, flick.directions),
        to: build_strs_index(sect_strs, flick.keyId),
      });
      keys.flickCount++;
    });
  });

  // now, keys
  keys.keys = kmxplus.keys.keys.map((key) => {
    const result : BUILDER_KEYS_KEY = {
      to: build_strs_index(sect_strs, key.to),
      flags: key.flags,
      id: build_strs_index(sect_strs, key.id),
      _id: key.id.value,
      switch: build_strs_index(sect_strs, key.switch),
      width: key.width,
      longPress: build_list_index(sect_list, key.longPress),
      longPressDefault: build_strs_index(sect_strs, key.longPressDefault),
      multiTap: build_list_index(sect_list, key.multiTap),
      flicks: keys.flicks.findIndex(v => v._id === (key.flicks || '')), // flicks id='' is the 'null' flicks
    };
    // Make sure the flicks were found
    if (result.flicks === -1) {
      throw new Error(`Keys: Could not find flicks id=${key.flicks} for key=${key.id.value}`);
    }
    return result;
  });
  // sort the keys by id
  keys.keys.sort((a, b) => StrsItem.binaryStringCompare(a._id, b._id));

  // finally, kmap
  keys.kmap = kmxplus.keys.kmap.map(({vkey, mod, key}) => {
    const result : BUILDER_KEYS_KMAP = {
      vkey,
      mod,
      key: keys.keys.findIndex(k => k._id === key),
    };
    // Make sure the key was found
    if (result.key === -1) {
      throw new Error(`Keys: Could not find keys.key id=${result.key} for keys.kmap.key=${key}`);
    }
    return result;
  });

  // Sort kmap by vkey, mod order, per C7043
  keys.kmap.sort((a,b) => {
    let rc = 0;
    if (rc === 0) {
      rc = (a.vkey - b.vkey);
    }
    if (rc === 0) {
      rc = (a.mod - b.mod);
    }
    return rc;
  });

  const offset = constants.length_keys +
    (constants.length_keys_key * keys.keyCount) +
    (constants.length_keys_flick_element * keys.flickCount) +
    (constants.length_keys_flick_list * keys.flicksCount) +
    (constants.length_keys_kmap * keys.kmapCount);
  keys.size = offset;

  return keys;
}