aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShin'ya Ueoka <ueokande@i-beam.org>2017-09-10 21:28:00 +0900
committerShin'ya Ueoka <ueokande@i-beam.org>2017-09-10 22:14:55 +0900
commit879b5afe66ee79424c3ffee3951ef1c0b8c86eaa (patch)
tree0306dfc26a4312f14084e295deefdf235abad34f /src
parentadc6a5175c0d8b83e45b9e8d99109c1605ad29ac (diff)
key input sequence as action/reducer
Diffstat (limited to 'src')
-rw-r--r--src/actions/index.js4
-rw-r--r--src/actions/input.js15
-rw-r--r--src/background/index.js56
-rw-r--r--src/background/keys.js (renamed from src/background/key-queue.js)67
-rw-r--r--src/background/tabs.js4
-rw-r--r--src/content/index.js10
-rw-r--r--src/reducers/input.js23
7 files changed, 103 insertions, 76 deletions
diff --git a/src/actions/index.js b/src/actions/index.js
index de3ab42..135dd4a 100644
--- a/src/actions/index.js
+++ b/src/actions/index.js
@@ -29,4 +29,8 @@ export default {
FOLLOW_START: 'follow.start',
HISTORY_PREV: 'history.prev',
HISTORY_NEXT: 'history.next',
+
+ // User input
+ INPUT_KEY_PRESS: 'input.key,press',
+ INPUT_CLEAR_KEYS: 'input.clear.keys',
};
diff --git a/src/actions/input.js b/src/actions/input.js
new file mode 100644
index 0000000..c72b9e0
--- /dev/null
+++ b/src/actions/input.js
@@ -0,0 +1,15 @@
+import actions from '../actions';
+
+export function keyPress(code, ctrl) {
+ return {
+ type: actions.INPUT_KEY_PRESS,
+ code,
+ ctrl
+ };
+}
+
+export function clearKeys() {
+ return {
+ type: actions.INPUT_CLEAR_KEYS
+ }
+}
diff --git a/src/background/index.js b/src/background/index.js
index 3fa8553..4d75b33 100644
--- a/src/background/index.js
+++ b/src/background/index.js
@@ -1,22 +1,10 @@
import * as tabs from './tabs';
-import KeyQueue from './key-queue';
+import * as keys from './keys';
+import * as inputActions from '../actions/input';
import backgroundReducers from '../reducers/background';
+import inputReducers from '../reducers/input';
-const queue = new KeyQueue();
-
-const keyPressHandle = (request, sender) => {
- let action = queue.push({
- code: request.code,
- ctrl: request.ctrl
- });
- if (!action) {
- return Promise.resolve();
- }
-
- return backgroundReducers(undefined, action, sender).then(() => {
- return browser.tabs.sendMessage(sender.tab.id, action);
- });
-};
+let inputState = inputReducers(undefined, {});
const normalizeUrl = (string) => {
try {
@@ -51,8 +39,6 @@ const cmdEnterHandle = (request, sender) => {
browser.runtime.onMessage.addListener((request, sender) => {
switch (request.type) {
- case 'event.keypress':
- return keyPressHandle(request, sender);
case 'event.cmd.enter':
return cmdEnterHandle(request, sender);
default:
@@ -60,6 +46,36 @@ browser.runtime.onMessage.addListener((request, sender) => {
}
});
-browser.runtime.onMessage.addListener((action, sender) => {
+const keyQueueChanged = (sender, prevState, state) => {
+ if (state.keys.length === 0) {
+ return Promise.resolve();
+ }
+
+ let prefix = keys.asKeymapChars(state.keys);
+ let matched = Object.keys(keys.defaultKeymap).filter((keys) => {
+ return keys.startsWith(prefix);
+ });
+ if (matched.length == 0) {
+ return handleMessage(inputActions.clearKeys(), sender);
+ } else if (matched.length > 1 || matched.length === 1 && prefix !== matched[0]) {
+ return Promise.resolve();
+ }
+ let action = keys.defaultKeymap[matched];
+ return handleMessage(inputActions.clearKeys(), sender).then(() => {
+ return backgroundReducers(undefined, action, sender).then(() => {
+ return browser.tabs.sendMessage(sender.tab.id, action);
+ });
+ });
+};
+
+const handleMessage = (action, sender) => {
+ let nextInputState = inputReducers(inputState, action);
+ if (JSON.stringify(nextInputState) !== JSON.stringify(inputState)) {
+ let prevState = inputState;
+ inputState = nextInputState;
+ return keyQueueChanged(sender, prevState, inputState);
+ }
return backgroundReducers(undefined, action, sender);
-});
+};
+
+browser.runtime.onMessage.addListener(handleMessage);
diff --git a/src/background/key-queue.js b/src/background/keys.js
index 924bf77..0ce53fa 100644
--- a/src/background/key-queue.js
+++ b/src/background/keys.js
@@ -1,6 +1,6 @@
import actions from '../actions';
-const DEFAULT_KEYMAP = {
+const defaultKeymap = {
':': { type: actions.CMD_OPEN },
'o': { type: actions.CMD_TABS_OPEN, alter: false },
'O': { type: actions.CMD_TABS_OPEN, alter: true },
@@ -32,51 +32,26 @@ const DEFAULT_KEYMAP = {
'L': { type: actions.HISTORY_NEXT },
}
-export default class KeyQueue {
-
- constructor(keymap = DEFAULT_KEYMAP) {
- this.data = [];
- this.keymap = keymap;
- }
-
- push(key) {
- this.data.push(key);
-
- let current = this.asKeymapChars();
- let filtered = Object.keys(this.keymap).filter((keys) => {
- return keys.startsWith(current);
- });
-
- if (filtered.length == 0) {
- this.data = [];
- return null;
- } else if (filtered.length === 1 && current === filtered[0]) {
- let action = this.keymap[filtered[0]];
- this.data = [];
- return action;
+const asKeymapChars = (keys) => {
+ return keys.map((k) => {
+ let c = String.fromCharCode(k.code);
+ if (k.ctrl) {
+ return '<C-' + c.toUpperCase() + '>';
+ } else {
+ return c
}
- return null;
- }
-
- asKeymapChars() {
- return this.data.map((k) => {
- let c = String.fromCharCode(k.code);
- if (k.ctrl) {
- return '<C-' + c.toUpperCase() + '>';
- } else {
- return c
- }
- }).join('');
- }
+ }).join('');
+}
- asCaretChars() {
- return this.data.map((k) => {
- let c = String.fromCharCode(k.code);
- if (k.ctrl) {
- return '^' + c.toUpperCase();
- } else {
- return c;
- }
- }).join('');
- }
+const asCaretChars = (keys) => {
+ return keys.map((k) => {
+ let c = String.fromCharCode(k.code);
+ if (k.ctrl) {
+ return '^' + c.toUpperCase();
+ } else {
+ return c;
+ }
+ }).join('');
}
+
+export { defaultKeymap, asKeymapChars, asCaretChars };
diff --git a/src/background/tabs.js b/src/background/tabs.js
index 111bbd9..bd69b4b 100644
--- a/src/background/tabs.js
+++ b/src/background/tabs.js
@@ -59,7 +59,7 @@ const getCompletions = (keyword) => {
};
const selectPrevTab = (current, count) => {
- return browser.tabs.query({ currentWindow: true }, (tabs) => {
+ return browser.tabs.query({ currentWindow: true }).then((tabs) => {
if (tabs.length < 2) {
return;
}
@@ -70,7 +70,7 @@ const selectPrevTab = (current, count) => {
};
const selectNextTab = (current, count) => {
- return browser.tabs.query({ currentWindow: true }, (tabs) => {
+ return browser.tabs.query({ currentWindow: true }).then((tabs) => {
if (tabs.length < 2) {
return;
}
diff --git a/src/content/index.js b/src/content/index.js
index 314dfea..12d079f 100644
--- a/src/content/index.js
+++ b/src/content/index.js
@@ -1,4 +1,5 @@
import '../console/console-frame.scss';
+import * as inputActions from '../actions/input';
import * as consoleFrames from '../console/frames';
import actions from '../actions';
import contentReducer from '../reducers/content';
@@ -14,14 +15,7 @@ window.addEventListener("keypress", (e) => {
if (e.target instanceof HTMLInputElement) {
return;
}
-
- let request = {
- type: 'event.keypress',
- code: e.which,
- ctrl: e.ctrlKey,
- }
-
- browser.runtime.sendMessage(request)
+ browser.runtime.sendMessage(inputActions.keyPress(e.which, e.ctrlKey))
.catch((err) => {
console.error("Vim Vixen:", err);
return consoleFrames.showError(err.message);
diff --git a/src/reducers/input.js b/src/reducers/input.js
new file mode 100644
index 0000000..25ff1a3
--- /dev/null
+++ b/src/reducers/input.js
@@ -0,0 +1,23 @@
+import actions from '../actions';
+
+const defaultState = {
+ keys: [],
+};
+
+export default function reducer(state = defaultState, action = {}) {
+ switch (action.type) {
+ case actions.INPUT_KEY_PRESS:
+ return Object.assign({}, state, {
+ keys: state.keys.concat([{
+ code: action.code,
+ ctrl: action.ctrl
+ }])
+ });
+ case actions.INPUT_CLEAR_KEYS:
+ return Object.assign({}, state, {
+ keys: [],
+ });
+ default:
+ return state;
+ }
+}