mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-17 11:27:39 +00:00
change(web): remove .get, force key type to number, base rewind on key value
This commit is contained in:
parent
4d66ff671d
commit
d6d44c4d1d
7 changed files with 28 additions and 51 deletions
|
|
@ -14,10 +14,10 @@
|
|||
* reaching a specified capacity and that provides the ability to
|
||||
* forget _all_ entries more recent than a specified entry.
|
||||
*/
|
||||
export class RewindableCache<Key, Value> {
|
||||
export class RewindableCache<Value> {
|
||||
static readonly DEFAULT_MAX_SIZE = 5;
|
||||
|
||||
private map: Map<Key, Value> = new Map();
|
||||
private map: Map<number, Value> = new Map();
|
||||
private maxSize: number;
|
||||
|
||||
/**
|
||||
|
|
@ -41,37 +41,18 @@ export class RewindableCache<Key, Value> {
|
|||
* @param key
|
||||
* @returns
|
||||
*/
|
||||
peek(key: Key): Value {
|
||||
get(key: number): Value {
|
||||
const entry = this.map.get(key);
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the cache entry with the specified key and also updates its
|
||||
* most recent access time.
|
||||
* @param key
|
||||
* @returns
|
||||
*/
|
||||
get(key: Key): Value {
|
||||
const map = this.map;
|
||||
const entry = map.get(key);
|
||||
if(!map.has(key)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
map.delete(key);
|
||||
map.set(key, entry);
|
||||
|
||||
return this.peek(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified key-value pair as a cache entry, overwriting any
|
||||
* previous value for the specified key.
|
||||
* @param key
|
||||
* @returns
|
||||
*/
|
||||
add(key: Key, value: Value) {
|
||||
add(key: number, value: Value) {
|
||||
const map = this.map;
|
||||
|
||||
if(map.has(key)) {
|
||||
|
|
@ -81,7 +62,7 @@ export class RewindableCache<Key, Value> {
|
|||
map.set(key, value);
|
||||
|
||||
if(map.size > this.maxSize) {
|
||||
const oldest = map.keys().next().value as Key;
|
||||
const oldest = map.keys().next().value as number;
|
||||
map.delete(oldest);
|
||||
map.delete(oldest);
|
||||
}
|
||||
|
|
@ -95,7 +76,7 @@ export class RewindableCache<Key, Value> {
|
|||
* Note: returns an Array and is accordingly O(N).
|
||||
* @returns
|
||||
*/
|
||||
keys(): Key[] {
|
||||
keys(): number[] {
|
||||
return [...this.map.keys()].reverse();
|
||||
}
|
||||
|
||||
|
|
@ -107,30 +88,26 @@ export class RewindableCache<Key, Value> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Deletes all entries more recent than the specified key, leaving
|
||||
* Deletes all entries with a key less than the specified key, leaving
|
||||
* the specified key as the most recent entry.
|
||||
* @param key
|
||||
*/
|
||||
rewindTo(key: Key) {
|
||||
rewindTo(key: number) {
|
||||
const map = this.map;
|
||||
|
||||
if(!map.has(key)) {
|
||||
throw new Error("Key not found; cannot clear entries appearing afterward.");
|
||||
}
|
||||
|
||||
let keyFound: boolean = false;
|
||||
// Iteration: by insertion order
|
||||
// The most recent entries will appear after the specified key.
|
||||
for(const k of map.keys()) {
|
||||
if(!keyFound) {
|
||||
keyFound = k == key;
|
||||
continue;
|
||||
if(k > key) {
|
||||
// It is safe to delete a key from a Set or Map while iterating over it in ES6+.
|
||||
// https://stackoverflow.com/a/28306768
|
||||
map.delete(k);
|
||||
map.delete(k);
|
||||
}
|
||||
|
||||
// It is safe to delete a key from a Set while iterating over it in ES6+.
|
||||
// https://stackoverflow.com/a/28306768
|
||||
map.delete(k);
|
||||
map.delete(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ describe("RewindableCache", () => {
|
|||
cache.add(5, 'e');
|
||||
|
||||
assert.sameOrderedMembers([...cache.keys()], [5, 4]);
|
||||
assert.sameOrderedMembers([...cache.keys().map((k) => cache.peek(k))], ['e', 'd']);
|
||||
assert.sameOrderedMembers([...cache.keys().map((k) => cache.get(k))], ['e', 'd']);
|
||||
assert.equal(cache.size, 2);
|
||||
});
|
||||
|
||||
|
|
@ -75,12 +75,13 @@ describe("RewindableCache", () => {
|
|||
cache.add(4, 'd');
|
||||
cache.add(5, 'e');
|
||||
|
||||
cache.get(2);
|
||||
// make it more recently-accessed, but don't remove it.
|
||||
cache.rewindTo(3);
|
||||
|
||||
assert.sameOrderedMembers([...cache.keys()], [3, 1]);
|
||||
assert.sameOrderedMembers([...cache.keys().map((k) => cache.peek(k))], ['c', 'a']);
|
||||
assert.equal(cache.size, 2);
|
||||
cache.add(2, 'b'); // 2 is still ORIGINALLY older than 3, even if accessed more recently.
|
||||
assert.sameOrderedMembers([...cache.keys()], [2, 3, 1]);
|
||||
assert.sameOrderedMembers([...cache.keys().map((k) => cache.get(k))], ['b', 'c', 'a']);
|
||||
assert.equal(cache.size, 3);
|
||||
});
|
||||
|
||||
it("forgets no entries if invalid key is provided", () => {
|
||||
|
|
@ -102,7 +103,6 @@ describe("RewindableCache", () => {
|
|||
cache.add(4, 'd'); // 2
|
||||
cache.add(5, 'e'); // 3
|
||||
|
||||
assert.isUndefined(cache.peek(2), 'outdated cache entry should be forgotten');
|
||||
assert.isUndefined(cache.get(2), 'outdated cache entry should be forgotten');
|
||||
});
|
||||
});
|
||||
|
|
@ -103,7 +103,7 @@ export class InputProcessor {
|
|||
// Support for multitap context reversion; multitap keys should act as if they were
|
||||
// the first thing typed since `preInput`, the state before the original base key.
|
||||
if(keyEvent.baseTranscriptionToken) {
|
||||
const transcription = this.contextCache.peek(keyEvent.baseTranscriptionToken);
|
||||
const transcription = this.contextCache.get(keyEvent.baseTranscriptionToken);
|
||||
if(transcription) {
|
||||
this.contextCache.rewindTo(keyEvent.baseTranscriptionToken);
|
||||
// Has there been a context change at any point during the multitap? If so, we need
|
||||
|
|
|
|||
|
|
@ -377,7 +377,7 @@ export class LanguageProcessor extends EventEmitter<LanguageProcessorEventMap> {
|
|||
* @returns The matching `Transcription`, or `null` none is found.
|
||||
*/
|
||||
public getPredictionState(id: number): Transcription {
|
||||
return this.recentTranscriptions.peek(id);
|
||||
return this.recentTranscriptions.get(id);
|
||||
}
|
||||
|
||||
public shutdown() {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { RewindableCache } from "@keymanapp/web-utils";
|
|||
|
||||
const TRANSCRIPTION_BUFFER_SIZE = 10;
|
||||
|
||||
export class TranscriptionCache extends RewindableCache<number, Transcription> {
|
||||
export class TranscriptionCache extends RewindableCache<Transcription> {
|
||||
constructor() {
|
||||
super(TRANSCRIPTION_BUFFER_SIZE);
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ export class TranscriptionCache extends RewindableCache<number, Transcription> {
|
|||
const entries = [...this.keys()].map((key) => {
|
||||
return {
|
||||
key: key,
|
||||
entry: this.peek(key)
|
||||
entry: this.get(key)
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { ContextState } from './context-state.js';
|
|||
import { ContextTransition } from './context-transition.js';
|
||||
|
||||
export class ContextTracker {
|
||||
readonly cache = new RewindableCache<number, ContextTransition>(5);
|
||||
readonly cache = new RewindableCache<ContextTransition>(5);
|
||||
|
||||
// Aim: relocate to ContextTransition in some form?
|
||||
// Or can we split it up in some manner across the different types?
|
||||
|
|
@ -275,7 +275,7 @@ export class ContextTracker {
|
|||
|
||||
if(tokenizedPostContext.left.length > 0) {
|
||||
for(const id of this.cache.keys()) {
|
||||
const priorMatchState = this.cache.peek(id);
|
||||
const priorMatchState = this.cache.get(id);
|
||||
|
||||
// Skip intermediate multitap-produced contexts.
|
||||
// When multitapping, we skip all contexts from prior taps within the same interaction,
|
||||
|
|
@ -303,7 +303,7 @@ export class ContextTracker {
|
|||
if(transitionId !== undefined) {
|
||||
if(priorMatchState.transitionId != transitionId) {
|
||||
// Already has a taggedContext.
|
||||
this.cache.get(transformDistribution.find((entry) => entry.sample.id !== undefined).sample.id);
|
||||
this.cache.add(priorMatchState.transitionId, priorMatchState);
|
||||
}
|
||||
this.cache.add(transitionId, result);
|
||||
}
|
||||
|
|
@ -332,7 +332,7 @@ export class ContextTracker {
|
|||
if(key === undefined) {
|
||||
return undefined;
|
||||
} else {
|
||||
return this.cache.peek(key);
|
||||
return this.cache.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ export class ModelCompositor {
|
|||
}
|
||||
|
||||
// When the context is tracked, we prefer the tracked information.
|
||||
let contextMatchFound = this.contextTracker.cache.peek(-reversion.transformId);
|
||||
let contextMatchFound = this.contextTracker.cache.get(-reversion.transformId);
|
||||
|
||||
if(!contextMatchFound) {
|
||||
return fallbackSuggestions();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue