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
|
import { injectable, inject } from "tsyringe";
import KeymapRepository from "../repositories/KeymapRepository";
import SettingRepository from "../repositories/SettingRepository";
import AddonEnabledRepository from "../repositories/AddonEnabledRepository";
import * as operations from "../../shared/operations";
import Keymaps from "../../shared/settings/Keymaps";
import Key from "../../shared/settings/Key";
import KeySequence from "../domains/KeySequence";
import AddressRepository from "../repositories/AddressRepository";
const reservedKeymaps = Keymaps.fromJSON({
"<Esc>": { type: operations.CANCEL },
"<C-[>": { type: operations.CANCEL },
});
const enableAddonOps = [
operations.ADDON_ENABLE,
operations.ADDON_TOGGLE_ENABLED,
];
@injectable()
export default class KeymapUseCase {
constructor(
@inject("KeymapRepository")
private repository: KeymapRepository,
@inject("SettingRepository")
private settingRepository: SettingRepository,
@inject("AddonEnabledRepository")
private addonEnabledRepository: AddonEnabledRepository,
@inject("AddressRepository")
private addressRepository: AddressRepository
) {}
// eslint-disable-next-line max-statements
nextOps(key: Key): { repeat: number; op: operations.Operation } | null {
const sequence = this.repository.enqueueKey(key);
const baseSequence = sequence.trimNumericPrefix();
const keymaps = this.keymapEntityMap();
const matched = keymaps.filter(([seq]) => seq.startsWith(sequence));
const baseMatched = keymaps.filter(([seq]) => seq.startsWith(baseSequence));
if (baseSequence.length() === 1 && this.blacklistKey(key)) {
// ignore if the input starts with black list keys
this.repository.clear();
return null;
}
if (matched.length === 1 && sequence.length() === matched[0][0].length()) {
// keys are matched with an operation
this.repository.clear();
return { repeat: 1, op: matched[0][1] };
} else if (
baseMatched.length === 1 &&
baseSequence.length() === baseMatched[0][0].length()
) {
// keys are matched with an operation with a numeric prefix
this.repository.clear();
return { repeat: sequence.repeatCount(), op: baseMatched[0][1] };
} else if (matched.length >= 1 || baseMatched.length >= 1) {
// keys are matched with an operation's prefix
return null;
}
// matched with no operations
this.repository.clear();
return null;
}
cancel() {
this.repository.clear();
}
private keymapEntityMap(): [KeySequence, operations.Operation][] {
const keymaps = this.settingRepository
.get()
.keymaps.combine(reservedKeymaps);
let entries = keymaps
.entries()
.map(([keys, op]) => [KeySequence.fromMapKeys(keys), op]) as [
KeySequence,
operations.Operation
][];
if (!this.addonEnabledRepository.get()) {
// available keymaps are only ADDON_ENABLE and ADDON_TOGGLE_ENABLED if
// the addon disabled
entries = entries.filter(([_seq, { type }]) =>
enableAddonOps.includes(type)
);
}
return entries;
}
private blacklistKey(key: Key): boolean {
const url = this.addressRepository.getCurrentURL();
const blacklist = this.settingRepository.get().blacklist;
return blacklist.includeKey(url, key);
}
}
|