aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShin'ya Ueoka <ueokande@i-beam.org>2017-10-07 12:21:09 +0900
committerGitHub <noreply@github.com>2017-10-07 12:21:09 +0900
commitd995ab0030522f380d165f309ffc72b582366ddb (patch)
tree69a096e9a8610ae8966af05e91355efdd27ea811
parent482206f6c90985011b197623854b8bfbc26ee54c (diff)
parent9fb7bf96be786acfbad97f7c76bc423a401dd657 (diff)
Merge pull request #19 from ueokande/content-and-background-redux-completely
Refactor: full redux on content and background
-rw-r--r--src/actions/completion.js22
-rw-r--r--src/actions/console.js28
-rw-r--r--src/actions/follow.js2
-rw-r--r--src/actions/index.js10
-rw-r--r--src/actions/input.js2
-rw-r--r--src/actions/operation.js28
-rw-r--r--src/actions/setting.js6
-rw-r--r--src/background/index.js36
-rw-r--r--src/components/background-input.js4
-rw-r--r--src/components/background.js36
-rw-r--r--src/components/completion.js6
-rw-r--r--src/components/console.js30
-rw-r--r--src/components/content-input.js2
-rw-r--r--src/components/follow.js10
-rw-r--r--src/components/setting.js4
-rw-r--r--src/content/index.js37
-rw-r--r--src/content/messages.js6
-rw-r--r--src/pages/console.js47
-rw-r--r--src/pages/settings.js12
-rw-r--r--src/reducers/completion.js68
-rw-r--r--src/reducers/console.js62
-rw-r--r--src/reducers/follow.js2
-rw-r--r--src/reducers/index.js9
-rw-r--r--src/reducers/input.js2
-rw-r--r--src/reducers/setting.js2
-rw-r--r--src/shared/commands.js (renamed from src/actions/command.js)12
-rw-r--r--src/shared/operations.js (renamed from src/operations/index.js)0
-rw-r--r--src/shared/validators/setting.js2
-rw-r--r--test/actions/completion.test.js27
-rw-r--r--test/actions/console.test.js34
-rw-r--r--test/actions/follow.test.js4
-rw-r--r--test/actions/input.test.js4
-rw-r--r--test/components/follow.test.js2
-rw-r--r--test/content/hint-key-producer.test.js2
-rw-r--r--test/content/hint.test.js2
-rw-r--r--test/content/navigates.test.js2
-rw-r--r--test/reducers/completion.test.js90
-rw-r--r--test/reducers/console.test.js91
-rw-r--r--test/reducers/follow.test.js4
-rw-r--r--test/reducers/input.test.js4
-rw-r--r--test/reducers/setting.test.js4
-rw-r--r--test/shared/validators/setting.test.js2
-rw-r--r--test/store/index.test.js2
-rw-r--r--webpack.config.js3
44 files changed, 360 insertions, 404 deletions
diff --git a/src/actions/completion.js b/src/actions/completion.js
deleted file mode 100644
index 1ffb025..0000000
--- a/src/actions/completion.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import actions from '../actions';
-
-const setItems = (groups) => {
- return {
- type: actions.COMPLETION_SET_ITEMS,
- groups,
- };
-};
-
-const selectNext = () => {
- return {
- type: actions.COMPLETION_SELECT_NEXT
- };
-};
-
-const selectPrev = () => {
- return {
- type: actions.COMPLETION_SELECT_PREV
- };
-};
-
-export { setItems, selectNext, selectPrev };
diff --git a/src/actions/console.js b/src/actions/console.js
index e0ec631..4183489 100644
--- a/src/actions/console.js
+++ b/src/actions/console.js
@@ -1,4 +1,4 @@
-import actions from '../actions';
+import actions from 'actions';
const showCommand = (text) => {
return {
@@ -7,6 +7,19 @@ const showCommand = (text) => {
};
};
+const showError = (text) => {
+ return {
+ type: actions.CONSOLE_SHOW_ERROR,
+ text: text
+ };
+};
+
+const hide = () => {
+ return {
+ type: actions.CONSOLE_HIDE
+ };
+};
+
const setCompletions = (completions) => {
return {
type: actions.CONSOLE_SET_COMPLETIONS,
@@ -14,17 +27,18 @@ const setCompletions = (completions) => {
};
};
-const showError = (text) => {
+const completionNext = () => {
return {
- type: actions.CONSOLE_SHOW_ERROR,
- text: text
+ type: actions.CONSOLE_COMPLETION_NEXT,
};
};
-const hide = () => {
+const completionPrev = () => {
return {
- type: actions.CONSOLE_HIDE
+ type: actions.CONSOLE_COMPLETION_PREV,
};
};
-export { showCommand, setCompletions, showError, hide };
+export {
+ showCommand, showError, hide, setCompletions, completionNext, completionPrev
+};
diff --git a/src/actions/follow.js b/src/actions/follow.js
index 7ab689e..708cd95 100644
--- a/src/actions/follow.js
+++ b/src/actions/follow.js
@@ -1,4 +1,4 @@
-import actions from '../actions';
+import actions from 'actions';
const enable = (newTab) => {
return {
diff --git a/src/actions/index.js b/src/actions/index.js
index 4e8d4a7..6a64795 100644
--- a/src/actions/index.js
+++ b/src/actions/index.js
@@ -1,9 +1,11 @@
export default {
// console commands
- CONSOLE_SHOW_COMMAND: 'vimvixen.console.show.command',
- CONSOLE_SET_COMPLETIONS: 'vimvixen.console.set.completions',
- CONSOLE_SHOW_ERROR: 'vimvixen.console.show.error',
- CONSOLE_HIDE: 'vimvixen.console.hide',
+ CONSOLE_SHOW_COMMAND: 'console.show.command',
+ CONSOLE_SET_COMPLETIONS: 'console.set.completions',
+ CONSOLE_SHOW_ERROR: 'console.show.error',
+ CONSOLE_HIDE: 'console.hide',
+ CONSOLE_COMPLETION_NEXT: 'console.completion.next',
+ CONSOLE_COMPLETION_PREV: 'console.completion.prev',
// User input
INPUT_KEY_PRESS: 'input.key,press',
diff --git a/src/actions/input.js b/src/actions/input.js
index 67788dd..61acb76 100644
--- a/src/actions/input.js
+++ b/src/actions/input.js
@@ -1,4 +1,4 @@
-import actions from '../actions';
+import actions from 'actions';
const asKeymapChars = (key, ctrl) => {
if (ctrl) {
diff --git a/src/actions/operation.js b/src/actions/operation.js
index 5b7f127..295fd4f 100644
--- a/src/actions/operation.js
+++ b/src/actions/operation.js
@@ -1,8 +1,14 @@
-import operations from '../operations';
-import messages from '../content/messages';
-import * as consoleActions from './console';
-import * as tabs from '../background/tabs';
-import * as zooms from '../background/zooms';
+import operations from 'shared/operations';
+import messages from 'content/messages';
+import * as tabs from 'background/tabs';
+import * as zooms from 'background/zooms';
+
+const sendConsoleShowCommand = (tab, command) => {
+ return browser.tabs.sendMessage(tab.id, {
+ type: messages.CONSOLE_SHOW_COMMAND,
+ command,
+ });
+};
const exec = (operation, tab) => {
switch (operation.type) {
@@ -23,21 +29,21 @@ const exec = (operation, tab) => {
case operations.ZOOM_NEUTRAL:
return zooms.neutral();
case operations.COMMAND_SHOW:
- return consoleActions.showCommand('');
+ return sendConsoleShowCommand(tab, '');
case operations.COMMAND_SHOW_OPEN:
if (operation.alter) {
// alter url
- return consoleActions.showCommand('open ' + tab.url);
+ return sendConsoleShowCommand(tab, 'open ' + tab.url);
}
- return consoleActions.showCommand('open ');
+ return sendConsoleShowCommand(tab, 'open ');
case operations.COMMAND_SHOW_TABOPEN:
if (operation.alter) {
// alter url
- return consoleActions.showCommand('tabopen ' + tab.url);
+ return sendConsoleShowCommand(tab, 'tabopen ' + tab.url);
}
- return consoleActions.showCommand('tabopen ');
+ return sendConsoleShowCommand(tab, 'tabopen ');
case operations.COMMAND_SHOW_BUFFER:
- return consoleActions.showCommand('buffer ');
+ return sendConsoleShowCommand(tab, 'buffer ');
default:
return browser.tabs.sendMessage(tab.id, {
type: messages.CONTENT_OPERATION,
diff --git a/src/actions/setting.js b/src/actions/setting.js
index 7898f06..2a47608 100644
--- a/src/actions/setting.js
+++ b/src/actions/setting.js
@@ -1,6 +1,6 @@
-import actions from '../actions';
-import messages from '../content/messages';
-import DefaultSettings from '../shared/default-settings';
+import actions from 'actions';
+import messages from 'content/messages';
+import DefaultSettings from 'shared/default-settings';
const load = () => {
return browser.storage.local.get('settings').then((value) => {
diff --git a/src/background/index.js b/src/background/index.js
index e968c82..b966c13 100644
--- a/src/background/index.js
+++ b/src/background/index.js
@@ -1,30 +1,24 @@
-import * as consoleActions from '../actions/console';
-import * as settingsActions from '../actions/setting';
-import BackgroundComponent from '../components/background';
-import BackgroundInputComponent from '../components/background-input';
-import reducers from '../reducers';
-import messages from '../content/messages';
-import * as store from '../store';
+import * as settingsActions from 'actions/setting';
+import messages from 'content/messages';
+import BackgroundComponent from 'components/background';
+import BackgroundInputComponent from 'components/background-input';
+import reducers from 'reducers';
+import { createStore } from 'store';
-const backgroundStore = store.createStore(reducers, (e, sender) => {
+const store = createStore(reducers, (e, sender) => {
console.error('Vim-Vixen:', e);
if (sender) {
- backgroundStore.dispatch(consoleActions.showError(e.message), sender);
+ return browser.tabs.sendMessage(sender.tab.id, {
+ type: messages.CONSOLE_SHOW_ERROR,
+ text: e.message,
+ });
}
});
-const backgroundComponent = new BackgroundComponent(backgroundStore);
-const backgroundInputComponent = new BackgroundInputComponent(backgroundStore);
-backgroundStore.subscribe((sender) => {
+const backgroundComponent = new BackgroundComponent(store);
+const backgroundInputComponent = new BackgroundInputComponent(store);
+store.subscribe((sender) => {
backgroundComponent.update(sender);
backgroundInputComponent.update(sender);
});
-backgroundStore.subscribe((sender) => {
- if (sender) {
- return browser.tabs.sendMessage(sender.tab.id, {
- type: messages.STATE_UPDATE,
- state: backgroundStore.getState()
- });
- }
-});
-backgroundStore.dispatch(settingsActions.load());
+store.dispatch(settingsActions.load());
diff --git a/src/components/background-input.js b/src/components/background-input.js
index 4735d5a..bd6ecf9 100644
--- a/src/components/background-input.js
+++ b/src/components/background-input.js
@@ -1,5 +1,5 @@
-import * as inputActions from '../actions/input';
-import * as operationActions from '../actions/operation';
+import * as inputActions from 'actions/input';
+import * as operationActions from 'actions/operation';
export default class BackgroundInputComponent {
constructor(store) {
diff --git a/src/components/background.js b/src/components/background.js
index 0585a04..487e3af 100644
--- a/src/components/background.js
+++ b/src/components/background.js
@@ -1,9 +1,8 @@
-import messages from '../content/messages';
-import * as commandActions from '../actions/command';
-import * as consoleActions from '../actions/console';
-import * as inputActions from '../actions/input';
-import * as settingsActions from '../actions/setting';
-import * as tabActions from '../actions/tab';
+import messages from 'content/messages';
+import * as inputActions from 'actions/input';
+import * as settingsActions from 'actions/setting';
+import * as tabActions from 'actions/tab';
+import * as commands from 'shared/commands';
export default class BackgroundComponent {
constructor(store) {
@@ -12,9 +11,12 @@ export default class BackgroundComponent {
browser.runtime.onMessage.addListener((message, sender) => {
try {
- this.onMessage(message, sender);
+ return this.onMessage(message, sender);
} catch (e) {
- this.store.dispatch(consoleActions.showError(e.message), sender);
+ return browser.tabs.sendMessage(sender.tab.id, {
+ type: messages.CONSOLE_SHOW_ERROR,
+ text: e.message,
+ });
}
});
}
@@ -44,14 +46,18 @@ export default class BackgroundComponent {
return this.store.dispatch(
tabActions.openToTab(message.url, sender.tab), sender);
case messages.CONSOLE_BLURRED:
- return this.store.dispatch(
- consoleActions.hide(), sender);
+ return browser.tabs.sendMessage(sender.tab.id, {
+ type: messages.CONSOLE_HIDE,
+ });
case messages.CONSOLE_ENTERED:
- return this.store.dispatch(
- commandActions.exec(message.text, this.settings), sender);
- case messages.CONSOLE_CHANGEED:
- return this.store.dispatch(
- commandActions.complete(message.text, this.settings), sender);
+ return commands.exec(message.text, this.settings).catch((e) => {
+ return browser.tabs.sendMessage(sender.tab.id, {
+ type: messages.CONSOLE_SHOW_ERROR,
+ text: e.message,
+ });
+ });
+ case messages.CONSOLE_QUERY_COMPLETIONS:
+ return commands.complete(message.text, this.settings);
case messages.SETTINGS_RELOAD:
this.store.dispatch(settingsActions.load());
}
diff --git a/src/components/completion.js b/src/components/completion.js
index 489061c..f527a84 100644
--- a/src/components/completion.js
+++ b/src/components/completion.js
@@ -6,15 +6,15 @@ export default class Completion {
}
update() {
- let state = this.store.getState();
+ let state = this.store.getState().console;
if (JSON.stringify(this.prevState) === JSON.stringify(state)) {
return;
}
this.wrapper.innerHTML = '';
- for (let i = 0; i < state.groups.length; ++i) {
- let group = state.groups[i];
+ for (let i = 0; i < state.completions.length; ++i) {
+ let group = state.completions[i];
let title = this.createCompletionTitle(group.name);
this.wrapper.append(title);
diff --git a/src/components/console.js b/src/components/console.js
index 9580dcf..12341c1 100644
--- a/src/components/console.js
+++ b/src/components/console.js
@@ -1,5 +1,5 @@
-import messages from '../content/messages';
-import * as completionActions from '../actions/completion';
+import messages from 'content/messages';
+import * as consoleActions from 'actions/console';
export default class ConsoleComponent {
constructor(wrapper, store) {
@@ -36,12 +36,12 @@ export default class ConsoleComponent {
return browser.runtime.sendMessage({
type: messages.CONSOLE_ENTERED,
text: e.target.value
- });
+ }).then(this.onBlur);
case KeyboardEvent.DOM_VK_TAB:
if (e.shiftKey) {
- this.store.dispatch(completionActions.selectPrev());
+ this.store.dispatch(consoleActions.completionPrev());
} else {
- this.store.dispatch(completionActions.selectNext());
+ this.store.dispatch(consoleActions.completionNext());
}
e.stopPropagation();
e.preventDefault();
@@ -63,13 +63,15 @@ export default class ConsoleComponent {
this.prevValue = e.target.value;
return browser.runtime.sendMessage({
- type: messages.CONSOLE_CHANGEED,
+ type: messages.CONSOLE_QUERY_COMPLETIONS,
text: e.target.value
+ }).then((completions) => {
+ this.store.dispatch(consoleActions.setCompletions(completions));
});
}
- // TODO use store/reducer to update state.
- update(state) {
+ update() {
+ let state = this.store.getState().console;
if (!this.prevState.commandShown && state.commandShown) {
this.showCommand(state.commandText);
} else if (!state.commandShown) {
@@ -83,6 +85,18 @@ export default class ConsoleComponent {
this.hideError();
}
+ if (state.groupSelection >= 0 && state.itemSelection >= 0) {
+ let group = state.completions[state.groupSelection];
+ let item = group.items[state.itemSelection];
+ this.setCommandValue(item.content);
+ } else if (state.completions.length > 0 &&
+ JSON.stringify(this.prevState.completions) ===
+ JSON.stringify(state.completions)) {
+ // Reset input only completion groups not changed (unselected an item in
+ // completion) in order to avoid to override previous input
+ this.setCommandCompletionOrigin();
+ }
+
this.prevState = state;
}
diff --git a/src/components/content-input.js b/src/components/content-input.js
index 10c785b..38d57fd 100644
--- a/src/components/content-input.js
+++ b/src/components/content-input.js
@@ -1,4 +1,4 @@
-import messages from '../content/messages';
+import messages from 'content/messages';
export default class ContentInputComponent {
constructor(target) {
diff --git a/src/components/follow.js b/src/components/follow.js
index 4fe4c58..9221759 100644
--- a/src/components/follow.js
+++ b/src/components/follow.js
@@ -1,7 +1,7 @@
-import * as followActions from '../actions/follow';
-import messages from '../content/messages';
-import Hint from '../content/hint';
-import HintKeyProducer from '../content/hint-key-producer';
+import * as followActions from 'actions/follow';
+import messages from 'content/messages';
+import Hint from 'content/hint';
+import HintKeyProducer from 'content/hint-key-producer';
const DEFAULT_HINT_CHARSET = 'abcdefghijklmnopqrstuvwxyz';
@@ -44,7 +44,7 @@ export default class FollowComponent {
update() {
let prevState = this.state;
- this.state = this.store.getState();
+ this.state = this.store.getState().follow;
if (!prevState.enabled && this.state.enabled) {
this.create();
} else if (prevState.enabled && !this.state.enabled) {
diff --git a/src/components/setting.js b/src/components/setting.js
index 1f3b3fe..c2f99b6 100644
--- a/src/components/setting.js
+++ b/src/components/setting.js
@@ -1,5 +1,5 @@
-import * as settingActions from '../actions/setting';
-import { validate } from '../shared/validators/setting';
+import * as settingActions from 'actions/setting';
+import { validate } from 'shared/validators/setting';
export default class SettingComponent {
constructor(wrapper, store) {
diff --git a/src/content/index.js b/src/content/index.js
index 655bea4..b29118d 100644
--- a/src/content/index.js
+++ b/src/content/index.js
@@ -1,18 +1,18 @@
import './console-frame.scss';
import * as consoleFrames from './console-frames';
-import * as scrolls from '../content/scrolls';
-import * as navigates from '../content/navigates';
-import * as followActions from '../actions/follow';
-import * as store from '../store';
-import ContentInputComponent from '../components/content-input';
-import FollowComponent from '../components/follow';
-import followReducer from '../reducers/follow';
-import operations from '../operations';
+import * as scrolls from 'content/scrolls';
+import * as navigates from 'content/navigates';
+import * as followActions from 'actions/follow';
+import { createStore } from 'store';
+import ContentInputComponent from 'components/content-input';
+import FollowComponent from 'components/follow';
+import reducers from 'reducers';
+import operations from 'shared/operations';
import messages from './messages';
-const followStore = store.createStore(followReducer);
-const followComponent = new FollowComponent(window.document.body, followStore);
-followStore.subscribe(() => {
+const store = createStore(reducers);
+const followComponent = new FollowComponent(window.document.body, store);
+store.subscribe(() => {
try {
followComponent.update();
} catch (e) {
@@ -39,7 +39,7 @@ const execOperation = (operation) => {
case operations.SCROLL_END:
return scrolls.scrollRight(window);
case operations.FOLLOW_START:
- return followStore.dispatch(followActions.enable(false));
+ return store.dispatch(followActions.enable(false));
case operations.NAVIGATE_HISTORY_PREV:
return navigates.historyPrev(window);
case operations.NAVIGATE_HISTORY_NEXT:
@@ -55,17 +55,12 @@ const execOperation = (operation) => {
}
};
-const update = (state) => {
- if (!state.console.commandShown) {
- window.focus();
- consoleFrames.blur(window.document);
- }
-};
-
browser.runtime.onMessage.addListener((action) => {
switch (action.type) {
- case messages.STATE_UPDATE:
- return update(action.state);
+ case messages.CONSOLE_HIDE:
+ window.focus();
+ consoleFrames.blur(window.document);
+ return Promise.resolve();
case messages.CONTENT_OPERATION:
execOperation(action.operation);
return Promise.resolve();
diff --git a/src/content/messages.js b/src/content/messages.js
index df9fba2..0e66fa0 100644
--- a/src/content/messages.js
+++ b/src/content/messages.js
@@ -1,10 +1,12 @@
export default {
- STATE_UPDATE: 'state.update',
CONTENT_OPERATION: 'content.operation',
CONSOLE_BLURRED: 'console.blured',
CONSOLE_ENTERED: 'console.entered',
- CONSOLE_CHANGEED: 'console.changed',
+ CONSOLE_QUERY_COMPLETIONS: 'console.query.completions',
+ CONSOLE_SHOW_COMMAND: 'console.show.command',
+ CONSOLE_SHOW_ERROR: 'console.show.error',
+ CONSOLE_HIDE: 'console.hide',
KEYDOWN: 'keydown',
diff --git a/src/pages/console.js b/src/pages/console.js
index 2cbea25..b7be73d 100644
--- a/src/pages/console.js
+++ b/src/pages/console.js
@@ -1,45 +1,34 @@
import './console.scss';
-import messages from '../content/messages';
-import CompletionComponent from '../components/completion';
-import ConsoleComponent from '../components/console';
-import completionReducer from '../reducers/completion';
-import * as store from '../store';
-import * as completionActions from '../actions/completion';
+import messages from 'content/messages';
+import CompletionComponent from 'components/completion';
+import ConsoleComponent from 'components/console';
+import reducers from 'reducers';
+import { createStore } from 'store';
+import * as consoleActions from 'actions/console';
-const completionStore = store.createStore(completionReducer);
+const store = createStore(reducers);
let completionComponent = null;
let consoleComponent = null;
-let prevState = {};
window.addEventListener('load', () => {
let wrapper = document.querySelector('#vimvixen-console-completion');
- completionComponent = new CompletionComponent(wrapper, completionStore);
+ completionComponent = new CompletionComponent(wrapper, store);
- // TODO use root root store instead of completionStore
- consoleComponent = new ConsoleComponent(document.body, completionStore);
+ consoleComponent = new ConsoleComponent(document.body, store);
});
-completionStore.subscribe(() => {
+store.subscribe(() => {
completionComponent.update();
-
- let state = completionStore.getState();
-
- if (state.groupSelection >= 0) {
- let item = state.groups[state.groupSelection].items[state.itemSelection];
- consoleComponent.setCommandValue(item.content);
- } else if (state.groups.length > 0 &&
- JSON.stringify(prevState.groups) === JSON.stringify(state.groups)) {
- // Reset input only completion groups not changed (unselected an item in
- // completion) in order to avoid to override previous input
- consoleComponent.setCommandCompletionOrigin();
- }
- prevState = state;
+ consoleComponent.update();
});
browser.runtime.onMessage.addListener((action) => {
- if (action.type === messages.STATE_UPDATE) {
- let state = action.state.console;
- consoleComponent.update(state);
- completionStore.dispatch(completionActions.setItems(state.completions));
+ switch (action.type) {
+ case messages.CONSOLE_SHOW_COMMAND:
+ return store.dispatch(consoleActions.showCommand(action.command));
+ case messages.CONSOLE_SHOW_ERROR:
+ return store.dispatch(consoleActions.showError(action.text));
+ case messages.CONSOLE_HIDE:
+ return store.dispatch(consoleActions.hide(action.command));
}
});
diff --git a/src/pages/settings.js b/src/pages/settings.js
index 9bad967..6e25e6f 100644
--- a/src/pages/settings.js
+++ b/src/pages/settings.js
@@ -1,15 +1,15 @@
import './settings.scss';
-import SettingComponent from '../components/setting';
-import settingReducer from '../reducers/setting';
-import * as store from '../store';
+import SettingComponent from 'components/setting';
+import settingReducer from 'reducers/setting';
+import { createStore } from 'store';
-const settingStore = store.createStore(settingReducer);
+const store = createStore(settingReducer);
let settingComponent = null;
-settingStore.subscribe(() => {
+store.subscribe(() => {
settingComponent.update();
});
document.addEventListener('DOMContentLoaded', () => {
- settingComponent = new SettingComponent(document.body, settingStore);
+ settingComponent = new SettingComponent(document.body, store);
});
diff --git a/src/reducers/completion.js b/src/reducers/completion.js
deleted file mode 100644
index a8a6444..0000000
--- a/src/reducers/completion.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import actions from '../actions';
-
-const defaultState = {
- groupSelection: -1,
- itemSelection: -1,
- groups: [],
-};
-
-const nextSelection = (state) => {
- if (state.groupSelection < 0) {
- return [0, 0];
- }
-
- let group = state.groups[state.groupSelection];
- if (state.groupSelection + 1 >= state.groups.length &&
- state.itemSelection + 1 >= group.items.length) {
- return [-1, -1];
- }
- if (state.itemSelection + 1 >= group.items.length) {
- return [state.groupSelection + 1, 0];
- }
- return [state.groupSelection, state.itemSelection + 1];
-};
-
-const prevSelection = (state) => {
- if (state.groupSelection < 0) {
- return [
- state.groups.length - 1,
- state.groups[state.groups.length - 1].items.length - 1
- ];
- }
- if (state.groupSelection === 0 && state.itemSelection === 0) {
- return [-1, -1];
- } else if (state.itemSelection === 0) {
- return [
- state.groupSelection - 1,
- state.groups[state.groupSelection - 1].items.length - 1
- ];
- }
- return [state.groupSelection, state.itemSelection - 1];
-};
-
-export default function reducer(state = defaultState, action = {}) {
- switch (action.type) {
- case actions.COMPLETION_SET_ITEMS:
- return Object.assign({}, state, {
- groups: action.groups,
- groupSelection: -1,
- itemSelection: -1,
- });
- case actions.COMPLETION_SELECT_NEXT: {
- let next = nextSelection(state);
- return Object.assign({}, state, {
- groupSelection: next[0],
- itemSelection: next[1],
- });
- }
- case actions.COMPLETION_SELECT_PREV: {
- let next = prevSelection(state);
- return Object.assign({}, state, {
- groupSelection: next[0],
- itemSelection: next[1],
- });
- }
- default:
- return defaultState;
- }
-}
diff --git a/src/reducers/console.js b/src/reducers/console.js
index 5c49c3b..b9ed5b8 100644
--- a/src/reducers/console.js
+++ b/src/reducers/console.js
@@ -1,4 +1,4 @@
-import actions from '../actions';
+import actions from 'actions';
const defaultState = {
errorShown: false,
@@ -6,6 +6,42 @@ const defaultState = {
commandShown: false,
commandText: '',
completions: [],
+ groupSelection: -1,
+ itemSelection: -1,
+};
+
+const nextSelection = (state) => {
+ if (state.groupSelection < 0) {
+ return [0, 0];
+ }
+
+ let group = state.completions[state.groupSelection];
+ if (state.groupSelection + 1 >= state.completions.length &&
+ state.itemSelection + 1 >= group.items.length) {
+ return [-1, -1];
+ }
+ if (state.itemSelection + 1 >= group.items.length) {
+ return [state.groupSelection + 1, 0];
+ }
+ return [state.groupSelection, state.itemSelection + 1];
+};
+
+const prevSelection = (state) => {
+ if (state.groupSelection < 0) {
+ return [
+ state.completions.length - 1,
+ state.completions[state.completions.length - 1].items.length - 1
+ ];
+ }
+ if (state.groupSelection === 0 && state.itemSelection === 0) {
+ return [-1, -1];
+ } else if (state.itemSelection === 0) {
+ return [
+ state.groupSelection - 1,
+ state.completions[state.groupSelection - 1].items.length - 1
+ ];
+ }
+ return [state.groupSelection, state.itemSelection - 1];
};
export default function reducer(state = defaultState, action = {}) {
@@ -17,10 +53,6 @@ export default function reducer(state = defaultState, action = {}) {
errorShown: false,
completions: []
});
- case actions.CONSOLE_SET_COMPLETIONS:
- return Object.assign({}, state, {
- completions: action.completions
- });
case actions.CONSOLE_SHOW_ERROR:
return Object.assign({}, state, {
errorText: action.text,
@@ -36,6 +68,26 @@ export default function reducer(state = defaultState, action = {}) {
errorShown: false,
commandShown: false
});
+ case actions.CONSOLE_SET_COMPLETIONS:
+ return Object.assign({}, state, {
+ completions: action.completions,
+ groupSelection: -1,
+ itemSelection: -1,
+ });
+ case actions.CONSOLE_COMPLETION_NEXT: {
+ let next = nextSelection(state);
+ return Object.assign({}, state, {
+ groupSelection: next[0],
+ itemSelection: next[1],
+ });
+ }
+ case actions.CONSOLE_COMPLETION_PREV: {
+ let next = prevSelection(state);
+ return Object.assign({}, state, {
+ groupSelection: next[0],
+ itemSelection: next[1],
+ });
+ }
default:
return state;
}
diff --git a/src/reducers/follow.js b/src/reducers/follow.js
index 136b367..a2397b4 100644
--- a/src/reducers/follow.js
+++ b/src/reducers/follow.js
@@ -1,4 +1,4 @@
-import actions from '../actions';
+import actions from 'actions';
const defaultState = {
enabled: false,
diff --git a/src/reducers/index.js b/src/reducers/index.js
index 9beb81c..8ed6452 100644
--- a/src/reducers/index.js
+++ b/src/reducers/index.js
@@ -1,11 +1,13 @@
-import inputReducer from '../reducers/input';
-import consoleReducer from '../reducers/console';
-import settingReducer from '../reducers/setting';
+import inputReducer from 'reducers/input';
+import consoleReducer from 'reducers/console';
+import settingReducer from 'reducers/setting';
+import followReducer from 'reducers/follow';
const defaultState = {
input: inputReducer(undefined, {}),
console: consoleReducer(undefined, {}),
setting: settingReducer(undefined, {}),
+ follow: followReducer(undefined, {}),
};
export default function reducer(state = defaultState, action = {}) {
@@ -13,5 +15,6 @@ export default function reducer(state = defaultState, action = {}) {
input: inputReducer(state.input, action),
console: consoleReducer(state.console, action),
setting: settingReducer(state.setting, action),
+ follow: followReducer(state.follow, action),
});
}
diff --git a/src/reducers/input.js b/src/reducers/input.js
index 8be701e..2e4bcd8 100644
--- a/src/reducers/input.js
+++ b/src/reducers/input.js
@@ -1,4 +1,4 @@
-import actions from '../actions';
+import actions from 'actions';
const defaultState = {
keys: '',
diff --git a/src/reducers/setting.js b/src/reducers/setting.js
index 735d4fb..7326ed7 100644
--- a/src/reducers/setting.js
+++ b/src/reducers/setting.js
@@ -1,4 +1,4 @@
-import actions from '../actions';
+import actions from 'actions';
const defaultState = {
settings: {}
diff --git a/src/actions/command.js b/src/shared/commands.js
index f578afd..b1d8780 100644
--- a/src/actions/command.js
+++ b/src/shared/commands.js
@@ -1,6 +1,5 @@
-import * as tabs from '../background/tabs';
-import * as histories from '../background/histories';
-import * as consoleActions from './console';
+import * as tabs from 'background/tabs';
+import * as histories from 'background/histories';
const normalizeUrl = (string, searchConfig) => {
try {
@@ -132,16 +131,13 @@ const getCompletions = (command, keywords, settings) => {
const exec = (line, settings) => {
let name = line.split(' ')[0];
let remaining = line.replace(name + ' ', '');
- return doCommand(name, remaining, settings).then(() => {
- return consoleActions.hide();
- });
+ return doCommand(name, remaining, settings);
};
const complete = (line, settings) => {
let command = line.split(' ', 1)[0];
let keywords = line.replace(command + ' ', '');
- return getCompletions(command, keywords, settings)
- .then(consoleActions.setCompletions);
+ return getCompletions(command, keywords, settings);
};
export { exec, complete };
diff --git a/src/operations/index.js b/src/shared/operations.js
index b68f59d..b68f59d 100644
--- a/src/operations/index.js
+++ b/src/shared/operations.js
diff --git a/src/shared/validators/setting.js b/src/shared/validators/setting.js
index caba5cc..5039ec2 100644
--- a/src/shared/validators/setting.js
+++ b/src/shared/validators/setting.js
@@ -1,4 +1,4 @@
-import operations from '../../operations';
+import operations from 'shared/operations';
const VALID_TOP_KEYS = ['keymaps', 'search'];
const VALID_OPERATION_VALUES = Object.keys(operations).map((key) => {
diff --git a/test/actions/completion.test.js b/test/actions/completion.test.js
deleted file mode 100644
index da88f53..0000000
--- a/test/actions/completion.test.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { expect } from "chai";
-import actions from '../../src/actions';
-import * as completionActions from '../../src/actions/completion';
-
-describe("completion actions", () => {
- describe('setItems', () => {
- it('create COMPLETION_SET_ITEMS action', () => {
- let action = completionActions.setItems([1, 2, 3]);
- expect(action.type).to.equal(actions.COMPLETION_SET_ITEMS);
- expect(action.groups).to.deep.equal([1, 2, 3]);
- });
- });
-
- describe('selectNext', () => {
- it('create COMPLETION_SELECT_NEXT action', () => {
- let action = completionActions.selectNext();
- expect(action.type).to.equal(actions.COMPLETION_SELECT_NEXT);
- });
- });
-
- describe('selectPrev', () => {
- it('create COMPLETION_SELECT_PREV action', () => {
- let action = completionActions.selectPrev();
- expect(action.type).to.equal(actions.COMPLETION_SELECT_PREV);
- });
- });
-});
diff --git a/test/actions/console.test.js b/test/actions/console.test.js
index 512ee40..ff905bc 100644
--- a/test/actions/console.test.js
+++ b/test/actions/console.test.js
@@ -1,6 +1,6 @@
import { expect } from "chai";
-import actions from '../../src/actions';
-import * as consoleActions from '../../src/actions/console';
+import actions from 'actions';
+import * as consoleActions from 'actions/console';
describe("console actions", () => {
describe("showCommand", () => {
@@ -11,14 +11,6 @@ describe("console actions", () => {
});
});
- describe("setCompletions", () => {
- it('create CONSOLE_SET_COMPLETIONS action', () => {
- let action = consoleActions.setCompletions([1,2,3]);
- expect(action.type).to.equal(actions.CONSOLE_SET_COMPLETIONS);
- expect(action.completions).to.deep.equal([1, 2, 3]);
- });
- });
-
describe("showError", () => {
it('create CONSOLE_SHOW_ERROR action', () => {
let action = consoleActions.showError('an error');
@@ -33,5 +25,27 @@ describe("console actions", () => {
expect(action.type).to.equal(actions.CONSOLE_HIDE);
});
});
+
+ describe("setCompletions", () => {
+ it('create CONSOLE_SET_COMPLETIONS action', () => {
+ let action = consoleActions.setCompletions([1,2,3]);
+ expect(action.type).to.equal(actions.CONSOLE_SET_COMPLETIONS);
+ expect(action.completions).to.deep.equal([1, 2, 3]);
+ });
+ });
+
+ describe("completionPrev", () => {
+ it('create CONSOLE_COMPLETION_PREV action', () => {
+ let action = consoleActions.completionPrev();
+ expect(action.type).to.equal(actions.CONSOLE_COMPLETION_PREV);
+ });
+ });
+
+ describe("completionNext", () => {
+ it('create CONSOLE_COMPLETION_NEXT action', () => {
+ let action = consoleActions.completionNext();
+ expect(action.type).to.equal(actions.CONSOLE_COMPLETION_NEXT);
+ });
+ });
});
diff --git a/test/actions/follow.test.js b/test/actions/follow.test.js
index 9439de7..32ab9e2 100644
--- a/test/actions/follow.test.js
+++ b/test/actions/follow.test.js
@@ -1,6 +1,6 @@
import { expect } from "chai";
-import actions from '../../src/actions';
-import * as followActions from '../../src/actions/follow';
+import actions from 'actions';
+import * as followActions from 'actions/follow';
describe('follow actions', () => {
describe('enable', () => {
diff --git a/test/actions/input.test.js b/test/actions/input.test.js
index 904d3e7..0a2ab18 100644
--- a/test/actions/input.test.js
+++ b/test/actions/input.test.js
@@ -1,6 +1,6 @@
import { expect } from "chai";
-import actions from '../../src/actions';
-import * as inputActions from '../../src/actions/input';
+import actions from 'actions';
+import * as inputActions from 'actions/input';
describe("input actions", () => {
describe("keyPress", () => {
diff --git a/test/components/follow.test.js b/test/components/follow.test.js
index f2f870e..c83e211 100644
--- a/test/components/follow.test.js
+++ b/test/components/follow.test.js
@@ -1,5 +1,5 @@
import { expect } from "chai";
-import FollowComponent from '../../src/components/follow';
+import FollowComponent from 'components/follow';
describe('FollowComponent', () => {
describe('#codeChars', () => {
diff --git a/test/content/hint-key-producer.test.js b/test/content/hint-key-producer.test.js
index 74fb462..b2171ba 100644
--- a/test/content/hint-key-producer.test.js
+++ b/test/content/hint-key-producer.test.js
@@ -1,5 +1,5 @@
import { expect } from "chai";
-import HintKeyProducer from '../../src/content/hint-key-producer';
+import HintKeyProducer from 'content/hint-key-producer';
describe('HintKeyProducer class', () => {
describe('#constructor', () => {
diff --git a/test/content/hint.test.js b/test/content/hint.test.js
index 9b2ab6e..1547971 100644
--- a/test/content/hint.test.js
+++ b/test/content/hint.test.js
@@ -1,5 +1,5 @@
import { expect } from "chai";
-import Hint from '../../src/content/hint';
+import Hint from 'content/hint';
describe('Hint class', () => {
beforeEach(() => {
diff --git a/test/content/navigates.test.js b/test/content/navigates.test.js
index cf20435..b5144e9 100644
--- a/test/content/navigates.test.js
+++ b/test/content/navigates.test.js
@@ -1,5 +1,5 @@
import { expect } from "chai";
-import * as navigates from '../../src/content/navigates';
+import * as navigates from 'content/navigates';
describe('navigates module', () => {
describe('#linkPrev', () => {
diff --git a/test/reducers/completion.test.js b/test/reducers/completion.test.js
deleted file mode 100644
index 79163bf..0000000
--- a/test/reducers/completion.test.js
+++ /dev/null
@@ -1,90 +0,0 @@
-import { expect } from "chai";
-import actions from '../../src/actions';
-import completionReducer from '../../src/reducers/completion';
-
-describe("completion reducer", () => {
- it ('return the initial state', () => {
- let state = completionReducer(undefined, {});
- expect(state).to.have.property('groupSelection', -1);
- expect(state).to.have.property('itemSelection', -1);
- expect(state).to.have.deep.property('groups', []);
- });
-
- it ('return next state for COMPLETION_SET_ITEMS', () => {
- let state = {
- groupSelection: 0,
- itemSelection: 0,
- groups: [],
- }
- let action = {
- type: actions.COMPLETION_SET_ITEMS,
- groups: [{
- name: 'Apple',
- items: [1, 2, 3]
- }, {
- name: 'Banana',
- items: [4, 5, 6]
- }]
- }
- state = completionReducer(state, action);
- expect(state).to.have.property('groups', action.groups);
- expect(state).to.have.property('groupSelection', -1);
- expect(state).to.have.property('itemSelection', -1);
- });
-
- it ('return next state for COMPLETION_SELECT_NEXT', () => {
- let action = { type: actions.COMPLETION_SELECT_NEXT };
- let state = {
- groupSelection: -1,
- itemSelection: -1,
- groups: [{
- name: 'Apple',
- items: [1, 2]
- }, {
- name: 'Banana',
- items: [3]
- }]
- };
-
- state = completionReducer(state, action);
- expect(state).to.have.property('groupSelection', 0);
- expect(state).to.have.property('itemSelection', 0);
-
- state = completionReducer(state, action);
- expect(state).to.have.property('groupSelection', 0);
- expect(state).to.have.property('itemSelection', 1);
-
- state = completionReducer(state, action);
- state = completionReducer(state, action);
- expect(state).to.have.property('groupSelection', -1);
- expect(state).to.have.property('itemSelection', -1);
- });
-
- it ('return next state for COMPLETION_SELECT_PREV', () => {
- let action = { type: actions.COMPLETION_SELECT_PREV };
- let state = {
- groupSelection: -1,
- itemSelection: -1,
- groups: [{
- name: 'Apple',
- items: [1, 2]
- }, {
- name: 'Banana',
- items: [3]
- }]
- };
-
- state = completionReducer(state, action);
- expect(state).to.have.property('groupSelection', 1);
- expect(state).to.have.property('itemSelection', 0);
-
- state = completionReducer(state, action);
- expect(state).to.have.property('groupSelection', 0);
- expect(state).to.have.property('itemSelection', 1);
-
- state = completionReducer(state, action);
- state = completionReducer(state, action);
- expect(state).to.have.property('groupSelection', -1);
- expect(state).to.have.property('itemSelection', -1);
- });
-});
diff --git a/test/reducers/console.test.js b/test/reducers/console.test.js
index 9820a08..5ebf4bc 100644
--- a/test/reducers/console.test.js
+++ b/test/reducers/console.test.js
@@ -1,6 +1,6 @@
import { expect } from "chai";
-import actions from '../../src/actions';
-import consoleReducer from '../../src/reducers/console';
+import actions from 'actions';
+import consoleReducer from 'reducers/console';
describe("console reducer", () => {
it('return the initial state', () => {
@@ -10,6 +10,8 @@ describe("console reducer", () => {
expect(state).to.have.property('commandShown', false);
expect(state).to.have.property('commandText', '');
expect(state).to.have.deep.property('completions', []);
+ expect(state).to.have.property('groupSelection', -1);
+ expect(state).to.have.property('itemSelection', -1);
});
it('return next state for CONSOLE_SHOW_COMMAND', () => {
@@ -20,12 +22,6 @@ describe("console reducer", () => {
expect(state).to.have.property('errorShown', false);
});
- it('return next state for CONSOLE_SET_COMPLETIONS', () => {
- let action = { type: actions.CONSOLE_SET_COMPLETIONS, completions: [1, 2, 3] };
- let state = consoleReducer({}, action);
- expect(state).to.have.deep.property('completions', [1, 2, 3]);
- });
-
it('return next state for CONSOLE_SHOW_ERROR', () => {
let action = { type: actions.CONSOLE_SHOW_ERROR, text: 'an error' };
let state = consoleReducer({}, action);
@@ -40,4 +36,83 @@ describe("console reducer", () => {
expect(state).to.have.property('errorShown', false);
expect(state).to.have.property('commandShown', false);
});
+
+ it ('return next state for CONSOLE_SET_COMPLETIONS', () => {
+ let state = {
+ groupSelection: 0,
+ itemSelection: 0,
+ completions: [],
+ }
+ let action = {
+ type: actions.CONSOLE_SET_COMPLETIONS,
+ completions: [{
+ name: 'Apple',
+ items: [1, 2, 3]
+ }, {
+ name: 'Banana',
+ items: [4, 5, 6]
+ }]
+ }
+ state = consoleReducer(state, action);
+ expect(state).to.have.property('completions', action.completions);
+ expect(state).to.have.property('groupSelection', -1);
+ expect(state).to.have.property('itemSelection', -1);
+ });
+
+ it ('return next state for CONSOLE_COMPLETION_NEXT', () => {
+ let action = { type: actions.CONSOLE_COMPLETION_NEXT };
+ let state = {
+ groupSelection: -1,
+ itemSelection: -1,
+ completions: [{
+ name: 'Apple',
+ items: [1, 2]
+ }, {
+ name: 'Banana',
+ items: [3]
+ }]
+ };
+
+ state = consoleReducer(state, action);
+ expect(state).to.have.property('groupSelection', 0);
+ expect(state).to.have.property('itemSelection', 0);
+
+ state = consoleReducer(state, action);
+ expect(state).to.have.property('groupSelection', 0);
+ expect(state).to.have.property('itemSelection', 1);
+
+ state = consoleReducer(state, action);
+ state = consoleReducer(state, action);
+ expect(state).to.have.property('groupSelection', -1);
+ expect(state).to.have.property('itemSelection', -1);
+ });
+
+ it ('return next state for CONSOLE_COMPLETION_PREV', () => {
+ let action = { type: actions.CONSOLE_COMPLETION_PREV };
+ let state = {
+ groupSelection: -1,
+ itemSelection: -1,
+ completions: [{
+ name: 'Apple',
+ items: [1, 2]
+ }, {
+ name: 'Banana',
+ items: [3]
+ }]
+ };
+
+ state = consoleReducer(state, action);
+ expect(state).to.have.property('groupSelection', 1);
+ expect(state).to.have.property('itemSelection', 0);
+
+ state = consoleReducer(state, action);
+ expect(state).to.have.property('groupSelection', 0);
+ expect(state).to.have.property('itemSelection', 1);
+
+ state = consoleReducer(state, action);
+ state = consoleReducer(state, action);
+ expect(state).to.have.property('groupSelection', -1);
+ expect(state).to.have.property('itemSelection', -1);
+ });
+
});
diff --git a/test/reducers/follow.test.js b/test/reducers/follow.test.js
index 19a1300..79e75d4 100644
--- a/test/reducers/follow.test.js
+++ b/test/reducers/follow.test.js
@@ -1,6 +1,6 @@
import { expect } from "chai";
-import actions from '../../src/actions';
-import followReducer from '../../src/reducers/follow';
+import actions from 'actions';
+import followReducer from 'reducers/follow';
describe('follow reducer', () => {
it ('returns the initial state', () => {
diff --git a/test/reducers/input.test.js b/test/reducers/input.test.js
index 3c3bf39..7b5a89c 100644
--- a/test/reducers/input.test.js
+++ b/test/reducers/input.test.js
@@ -1,6 +1,6 @@
import { expect } from "chai";
-import actions from '../../src/actions';
-import inputReducer from '../../src/reducers/input';
+import actions from 'actions';
+import inputReducer from 'reducers/input';
describe("input reducer", () => {
it('return the initial state', () => {
diff --git a/test/reducers/setting.test.js b/test/reducers/setting.test.js
index 7261be6..1af031a 100644
--- a/test/reducers/setting.test.js
+++ b/test/reducers/setting.test.js
@@ -1,6 +1,6 @@
import { expect } from "chai";
-import actions from '../../src/actions';
-import settingReducer from '../../src/reducers/setting';
+import actions from 'actions';
+import settingReducer from 'reducers/setting';
describe("setting reducer", () => {
it('return the initial state', () => {
diff --git a/test/shared/validators/setting.test.js b/test/shared/validators/setting.test.js
index 9baf858..15d6a10 100644
--- a/test/shared/validators/setting.test.js
+++ b/test/shared/validators/setting.test.js
@@ -1,5 +1,5 @@
import { expect } from "chai";
-import { validate } from '../../../src/shared/validators/setting';
+import { validate } from 'shared/validators/setting';
describe("setting validator", () => {
describe("unknown top keys", () => {
diff --git a/test/store/index.test.js b/test/store/index.test.js
index e19d50e..5dce715 100644
--- a/test/store/index.test.js
+++ b/test/store/index.test.js
@@ -1,5 +1,5 @@
import { expect } from "chai";
-import { createStore } from '../../src/store';
+import { createStore } from 'store';
describe("Store class", () => {
const reducer = (state, action) => {
diff --git a/webpack.config.js b/webpack.config.js
index f1ba07a..3d4ef03 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -39,7 +39,8 @@ module.exports = {
},
resolve: {
- extensions: [ '.js' ]
+ extensions: [ '.js' ],
+ modules: [path.join(__dirname, 'src'), 'node_modules']
},
plugins: [