mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-05 16:35:33 +00:00
160 lines
No EOL
3.9 KiB
TypeScript
160 lines
No EOL
3.9 KiB
TypeScript
namespace com.keyman.text {
|
|
// Defines the base Deadkey-tracking object.
|
|
|
|
export class Deadkey {
|
|
p: number; // Position of deadkey
|
|
d: number; // Numerical id of the deadkey
|
|
o: number; // Ordinal value of the deadkey (resolves same-place conflicts)
|
|
matched: number;
|
|
|
|
static ordinalSeed: number = 0;
|
|
|
|
constructor(pos: number, id: number) {
|
|
this.p = pos;
|
|
this.d = id;
|
|
this.o = Deadkey.ordinalSeed++;
|
|
}
|
|
|
|
match(p: number, d: number): boolean {
|
|
var result:boolean = (this.p == p && this.d == d);
|
|
|
|
return result;
|
|
}
|
|
|
|
set(): void {
|
|
this.matched = 1;
|
|
}
|
|
|
|
reset(): void {
|
|
this.matched = 0;
|
|
}
|
|
|
|
before(other: Deadkey): boolean {
|
|
return this.o < other.o;
|
|
}
|
|
|
|
clone(): Deadkey {
|
|
let dk = new Deadkey(this.p, this.d);
|
|
dk.o = this.o;
|
|
|
|
return dk;
|
|
}
|
|
|
|
/**
|
|
* Sorts the deadkeys in reverse order.
|
|
*/
|
|
static sortFunc = function(a: Deadkey, b: Deadkey) {
|
|
// We want descending order, so we want 'later' deadkeys first.
|
|
if(a.p != b.p) {
|
|
return b.p - a.p;
|
|
} else {
|
|
return b.o - a.o;
|
|
}
|
|
};
|
|
}
|
|
|
|
// Object-orients deadkey management.
|
|
export class DeadkeyTracker {
|
|
dks: Deadkey[] = [];
|
|
|
|
toSortedArray(): Deadkey[] {
|
|
this.dks = this.dks.sort(Deadkey.sortFunc);
|
|
return [].concat(this.dks);
|
|
}
|
|
|
|
clone(): DeadkeyTracker {
|
|
let dkt = new DeadkeyTracker();
|
|
let dks = this.toSortedArray();
|
|
|
|
// Make sure to clone the deadkeys themselves - the Deadkey object is mutable.
|
|
dkt.dks = [];
|
|
dks.forEach(function(value: Deadkey) {
|
|
dkt.dks.push(value.clone());
|
|
});
|
|
|
|
return dkt;
|
|
}
|
|
|
|
/**
|
|
* Function isMatch
|
|
* Scope Public
|
|
* @param {number} caretPos current cursor position
|
|
* @param {number} n expected offset of deadkey from cursor
|
|
* @param {number} d deadkey
|
|
* @return {boolean} True if deadkey found selected context matches val
|
|
* Description Match deadkey at current cursor position
|
|
*/
|
|
isMatch(caretPos: number, n: number, d: number): boolean {
|
|
if(this.dks.length == 0) {
|
|
return false; // I3318
|
|
}
|
|
|
|
var sp=caretPos;
|
|
n = sp - n;
|
|
for(var i = 0; i < this.dks.length; i++) {
|
|
// Don't re-match an already-matched deadkey. It's possible to have two identical
|
|
// entries, and they should be kept separately.
|
|
if(this.dks[i].match(n, d) && !this.dks[i].matched) {
|
|
this.dks[i].set();
|
|
// Assumption: since we match the first possible entry in the array, we
|
|
// match the entry with the lower ordinal - the 'first' deadkey in the position.
|
|
return true; // I3318
|
|
}
|
|
}
|
|
|
|
this.resetMatched(); // I3318
|
|
|
|
return false;
|
|
}
|
|
|
|
add(dk: Deadkey) {
|
|
this.dks = this.dks.concat(dk);
|
|
}
|
|
|
|
remove(dk: Deadkey) {
|
|
var index = this.dks.indexOf(dk);
|
|
this.dks.splice(index, 1);
|
|
}
|
|
|
|
clear() {
|
|
this.dks = [];
|
|
}
|
|
|
|
resetMatched() {
|
|
for(let dk of this.dks) {
|
|
dk.reset();
|
|
}
|
|
}
|
|
|
|
deleteMatched(): void {
|
|
for(var Li = 0; Li < this.dks.length; Li++) {
|
|
if(this.dks[Li].matched) {
|
|
this.dks.splice(Li--, 1); // Don't forget to decrement!
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Function adjustPositions (formerly _DeadkeyAdjustPos)
|
|
* Scope Private
|
|
* @param {number} Lstart start position in context
|
|
* @param {number} Ldelta characters to adjust by
|
|
* Description Adjust saved positions of deadkeys in context
|
|
*/
|
|
adjustPositions(Lstart: number, Ldelta: number): void {
|
|
if(Ldelta == 0) {
|
|
return;
|
|
}
|
|
|
|
for(let dk of this.dks) {
|
|
if(dk.p > Lstart) {
|
|
dk.p += Ldelta;
|
|
}
|
|
}
|
|
}
|
|
|
|
count(): number {
|
|
return this.dks.length;
|
|
}
|
|
}
|
|
} |