1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-03-29 21:21:15 +02:00

Cleaned up command processing logic and fixed a few bugs

This commit is contained in:
Laurent Cozic 2017-10-17 18:18:31 +01:00
parent 7d45e1000d
commit d4af90832b
14 changed files with 236 additions and 128 deletions

View File

@ -17,4 +17,5 @@ tests/cli-integration/
*.*~ *.*~
tests/sync tests/sync
out.txt out.txt
linkToLocal.sh linkToLocal.sh
yarn-error.log

View File

@ -130,6 +130,7 @@ class AppGui {
const consoleWidget = new ConsoleWidget(); const consoleWidget = new ConsoleWidget();
consoleWidget.hStretch = true; consoleWidget.hStretch = true;
consoleWidget.hide();
const statusBar = new StatusBarWidget(); const statusBar = new StatusBarWidget();
statusBar.hStretch = true; statusBar.hStretch = true;
@ -142,8 +143,8 @@ class AppGui {
const vLayout = new VLayoutWidget(); const vLayout = new VLayoutWidget();
vLayout.name = 'vLayout'; vLayout.name = 'vLayout';
vLayout.addChild(hLayout, { type: 'stretch', factor: 1 }); vLayout.addChild(hLayout, { type: 'stretch', factor: 2 });
vLayout.addChild(consoleWidget, { type: 'fixed', factor: 3 }); vLayout.addChild(consoleWidget, { type: 'stretch', factor: 1 });
vLayout.addChild(statusBar, { type: 'fixed', factor: 1 }); vLayout.addChild(statusBar, { type: 'fixed', factor: 1 });
const win1 = new WindowWidget(); const win1 = new WindowWidget();
@ -167,13 +168,35 @@ class AppGui {
action: 'todo toggle $n', action: 'todo toggle $n',
} }
shortcuts['c'] = {
description: _('Toggle console between maximized/minimized/hidden/visible.'),
action: () => {
if (!this.consoleIsShown()) {
this.showConsole();
this.minimizeConsole();
} else {
if (this.consoleIsMaximized()) {
this.hideConsole();
} else {
this.maximizeConsole();
}
}
},
canRunAlongOtherCommands: true,
}
shortcuts[':'] = { shortcuts[':'] = {
description: _('Enter the console'), description: _('Enter command line mode'),
isDocOnly: true, action: async () => {
const cmd = await this.widget('statusBar').prompt();
if (!cmd) return;
this.stdout('> ' + cmd);
await this.processCommand(cmd);
},
}; };
shortcuts['ESC'] = { shortcuts['ESC'] = { // Built into terminal-kit inputField
description: _('Exit the console'), description: _('Exit command line mode'),
isDocOnly: true, isDocOnly: true,
}; };
@ -189,6 +212,18 @@ class AppGui {
}, },
} }
shortcuts['CTRL_C'] = {
description: _('Cancel the current command.'),
friendlyName: 'Ctrl+C',
isDocOnly: true,
}
shortcuts['CTRL_D'] = {
description: _('Exit the application.'),
friendlyName: 'Ctrl+D',
isDocOnly: true,
}
shortcuts['nn'] = { shortcuts['nn'] = {
description: _('Create a new note'), description: _('Create a new note'),
action: { type: 'prompt', initialText: 'mknote ' }, action: { type: 'prompt', initialText: 'mknote ' },
@ -204,46 +239,52 @@ class AppGui {
action: { type: 'prompt', initialText: 'mkbook ' }, action: { type: 'prompt', initialText: 'mkbook ' },
} }
shortcuts['CTRL_JCTRL_Z'] = {
friendlyName: 'Ctrl+J Ctrl+Z',
description: _('Maximise/minimise the console'),
action: () => { this.toggleMaximizeConsole(); },
}
return shortcuts; return shortcuts;
} }
toggleMaximizeConsole() { toggleConsole() {
this.maximizeConsole(!this.consoleIsMaximized()); this.showConsole(!this.consoleIsShown());
}
showConsole(doShow = true) {
const consoleWidget = this.widget('console');
consoleWidget.show(doShow);
this.widget('root').invalidate();
}
hideConsole() {
this.showConsole(false);
}
consoleIsShown() {
return this.widget('console').shown;
} }
maximizeConsole(doMaximize = true) { maximizeConsole(doMaximize = true) {
// const consoleWidget = this.widget('console'); const consoleWidget = this.widget('console');
// if (consoleWidget.isMaximized__ === undefined) { if (consoleWidget.isMaximized__ === undefined) {
// consoleWidget.isMaximized__ = false; consoleWidget.isMaximized__ = false;
// } }
// if (consoleWidget.isMaximized__ === doMaximize) return; if (consoleWidget.isMaximized__ === doMaximize) return;
// let constraints = { let constraints = {
// type: 'fixed', type: 'stretch',
// factor: !doMaximize ? 5 : this.widget('vLayout').height - 4, factor: !doMaximize ? 1 : 4,
// }; };
// consoleWidget.isMaximized__ = doMaximize; consoleWidget.isMaximized__ = doMaximize;
// this.widget('vLayout').setWidgetConstraints(consoleWidget, constraints); this.widget('vLayout').setWidgetConstraints(consoleWidget, constraints);
} }
minimizeConsole() { minimizeConsole() {
//this.maximizeConsole(false); this.maximizeConsole(false);
} }
consoleIsMaximized() { consoleIsMaximized() {
return false; return this.widget('console').isMaximized__ === true;
//return this.widget('console').isMaximized__ === true;
} }
widget(name) { widget(name) {
@ -355,10 +396,13 @@ class AppGui {
} }
} }
stdout(...object) { stdout(text) {
for (let i = 0; i < object.length; i++) { if (text === null || text === undefined) return;
const v = typeof object[i] === 'object' ? JSON.stringify(object[i]) : object[i];
this.widget('console').addItem(v); let lines = text.split('\n');
for (let i = 0; i < lines.length; i++) {
const v = typeof lines[i] === 'object' ? JSON.stringify(lines[i]) : lines[i];
this.widget('console').addLine(v);
} }
this.updateStatusBarMessage(); this.updateStatusBarMessage();
@ -369,7 +413,7 @@ class AppGui {
let msg = ''; let msg = '';
const text = consoleWidget.items.length ? consoleWidget.items[consoleWidget.items.length - 1] : ''; const text = consoleWidget.lastLine;
const cmd = this.app().currentCommand(); const cmd = this.app().currentCommand();
if (cmd) { if (cmd) {
@ -399,6 +443,10 @@ class AppGui {
term.on('key', async (name, matches, data) => { term.on('key', async (name, matches, data) => {
// -------------------------------------------------------------------------
// Handle special shortcuts
// -------------------------------------------------------------------------
if (name === 'CTRL_D') { if (name === 'CTRL_D') {
const cmd = this.app().currentCommand(); const cmd = this.app().currentCommand();
@ -418,11 +466,15 @@ class AppGui {
this.stdout(_('Press Ctrl+D or type "exit" to exit the application')); this.stdout(_('Press Ctrl+D or type "exit" to exit the application'));
} else { } else {
this.commandCancelCalled_ = true; this.commandCancelCalled_ = true;
await cmd.cancel(); await cmd.cancel()
this.commandCancelCalled_ = false; this.commandCancelCalled_ = false;
} }
return; return;
} }
// -------------------------------------------------------------------------
// Build up current shortcut
// -------------------------------------------------------------------------
const now = (new Date()).getTime(); const now = (new Date()).getTime();
@ -440,38 +492,34 @@ class AppGui {
this.lastShortcutKeyTime_ = now; this.lastShortcutKeyTime_ = now;
if (!this.app().currentCommand()) { // -------------------------------------------------------------------------
if (name === ':' && !statusBar.promptActive) { // Process shortcut and execute associated command
const cmd = await statusBar.prompt(); // -------------------------------------------------------------------------
await this.processCommand(cmd);
} else {
// Don't process shortcut keys if the console is active, except if the shortcut
// starts with CTRL (eg. CTRL+J CTRL+Z to maximize the console window).
if (!statusBar.promptActive || (this.currentShortcutKeys_.length && this.currentShortcutKeys_[0].indexOf('CTRL') === 0)) {
this.logger().debug('Now: ' + name + ', Keys: ', this.currentShortcutKeys_);
const shortcutKey = this.currentShortcutKeys_.join(''); const shortcutKey = this.currentShortcutKeys_.join('');
if (shortcutKey in this.shortcuts_) { const cmd = shortcutKey in this.shortcuts_ ? this.shortcuts_[shortcutKey] : null;
const cmd = this.shortcuts_[shortcutKey].action;
if (!cmd.isDocOnly) { let processShortcutKeys = !this.app().currentCommand() && !statusBar.promptActive && cmd;
this.currentShortcutKeys_ = []; if (cmd && cmd.canRunAlongOtherCommands) processShortcutKeys = true;
if (typeof cmd === 'function') { if (cmd && cmd.isDocOnly) processShortcutKeys = false;
await cmd();
} else if (typeof cmd === 'object') { this.logger().info('Shortcut:', shortcutKey, processShortcutKeys, cmd ? cmd.description : '(no command)');
if (cmd.type === 'prompt') {
const commandString = await statusBar.prompt(cmd.initialText ? cmd.initialText : ''); if (processShortcutKeys) {
this.stdout(commandString); this.currentShortcutKeys_ = [];
await this.processCommand(commandString); if (typeof cmd.action === 'function') {
} else { await cmd.action();
throw new Error('Unknown command: ' + JSON.stringify(cmd)); } else if (typeof cmd.action === 'object') {
} if (cmd.action.type === 'prompt') {
} else { const commandString = await statusBar.prompt(cmd.action.initialText ? cmd.action.initialText : '');
this.stdout(cmd); this.stdout(commandString);
await this.processCommand(cmd); await this.processCommand(commandString);
} } else {
} throw new Error('Unknown command: ' + JSON.stringify(cmd.action));
}
} }
} else { // String
this.stdout(cmd.action);
await this.processCommand(cmd.action);
} }
} }

View File

@ -270,8 +270,8 @@ class Application {
} }
setupCommand(cmd) { setupCommand(cmd) {
cmd.setStdout((...object) => { cmd.setStdout((text) => {
this.gui().stdout(...object); this.gui().stdout(text);
}); });
cmd.setDispatcher((action) => { cmd.setDispatcher((action) => {
@ -382,8 +382,14 @@ class Application {
const commandName = argv[0]; const commandName = argv[0];
this.activeCommand_ = this.findCommandByName(commandName); this.activeCommand_ = this.findCommandByName(commandName);
const cmdArgs = cliUtils.makeCommandArgs(this.activeCommand_, argv); const cmdArgs = cliUtils.makeCommandArgs(this.activeCommand_, argv);
await this.activeCommand_.action(cmdArgs); let outException = null;
try {
await this.activeCommand_.action(cmdArgs);
} catch (error) {
outException = error;
}
this.activeCommand_ = null; this.activeCommand_ = null;
if (outException) throw outException;
} }
currentCommand() { currentCommand() {

View File

@ -60,8 +60,8 @@ class BaseCommand {
this.stdout_ = fn; this.stdout_ = fn;
} }
stdout(...object) { stdout(text) {
if (this.stdout_) this.stdout_(...object); if (this.stdout_) this.stdout_(text);
} }
setPrompt(fn) { setPrompt(fn) {

View File

@ -38,11 +38,15 @@ class Command extends BaseCommand {
if (!verbose && !value) continue; if (!verbose && !value) continue;
this.stdout(renderKeyValue(keys[i])); this.stdout(renderKeyValue(keys[i]));
} }
app().gui().showConsole();
app().gui().maximizeConsole();
return; return;
} }
if (args.name && !args.value) { if (args.name && !args.value) {
this.stdout(renderKeyValue(args.name)); this.stdout(renderKeyValue(args.name));
app().gui().showConsole();
app().gui().maximizeConsole();
return; return;
} }

View File

@ -37,7 +37,6 @@ class Command extends BaseCommand {
} else if (args.command) { } else if (args.command) {
const command = app().findCommandByName(args['command']); const command = app().findCommandByName(args['command']);
if (!command) throw new Error(_('Cannot find "%s".', args.command)); if (!command) throw new Error(_('Cannot find "%s".', args.command));
this.stdout(renderCommandHelp(command, stdoutWidth)); this.stdout(renderCommandHelp(command, stdoutWidth));
} else { } else {
const commands = app().commands(); const commands = app().commands();
@ -50,21 +49,19 @@ class Command extends BaseCommand {
commandNames.sort(); commandNames.sort();
let lines = []; this.stdout(_('Type `help [command]` for more information about a command.'));
lines.push(_('Type `help [command]` for more information about a command.')); this.stdout('');
lines.push(''); this.stdout(_('The possible commands are:'));
lines.push(_('The possible commands are:')); this.stdout('');
lines.push(''); this.stdout(commandNames.join(', '));
lines.push(commandNames.join(', ')); this.stdout('');
lines.push(''); this.stdout(_('To maximise/minimise the console, press "C".'));
lines.push(_('To maximise/minimise the console, press Ctrl+J Ctrl+Z.')); this.stdout(_('To enter command line mode, press ":"'));
lines.push(_('To enter the console, press C')); this.stdout(_('To exit command line mode, press ESCAPE'));
lines.push(_('To exit the console, press ESCAPE')); this.stdout(_('To view a list of available shortcuts type `help shortcuts`'));
lines.push(_('To view a list of available shortcuts type `help shortcuts`'));
this.stdout(wrap(lines.join("\n"), '', stdoutWidth));
} }
app().gui().showConsole();
app().gui().maximizeConsole(); app().gui().maximizeConsole();
} }

View File

@ -1,18 +1,46 @@
const ListWidget = require('tkwidgets/ListWidget.js'); const TextWidget = require('tkwidgets/TextWidget.js');
class ConsoleWidget extends ListWidget { class ConsoleWidget extends TextWidget {
constructor() { constructor() {
super(); super();
this.lines_ = [];
this.updateText_ = false;
this.markdownRendering = false;
this.stickToBottom = true;
} }
get name() { get name() {
return 'console'; return 'console';
} }
addItem(v) { get lastLine() {
super.addItem(v); return this.lines_.length ? this.lines_[this.lines_.length-1] : '';
this.currentIndex = this.items.length - 1; }
addLine(line) {
this.lines_.push(line);
this.updateText_ = true;
this.invalidate();
}
onFocus() {
this.stickToBottom = false;
super.onFocus();
}
onBlur() {
this.stickToBottom = true;
super.onBlur();
}
render() {
if (this.updateText_) {
this.text = this.lines_.join("\n");
this.updateText_ = false;
}
super.render();
} }
} }

View File

@ -1,5 +1,6 @@
const BaseWidget = require('tkwidgets/BaseWidget.js'); const BaseWidget = require('tkwidgets/BaseWidget.js');
const chalk = require('chalk'); const chalk = require('chalk');
const termutils = require('tkwidgets/framework/termutils.js');
class StatusBarWidget extends BaseWidget { class StatusBarWidget extends BaseWidget {
@ -16,6 +17,10 @@ class StatusBarWidget extends BaseWidget {
return 'statusBar'; return 'statusBar';
} }
get canHaveFocus() {
return false;
}
setItemAt(index, text) { setItemAt(index, text) {
this.items_[index] = text; this.items_[index] = text;
this.invalidate(); this.invalidate();
@ -64,15 +69,14 @@ class StatusBarWidget extends BaseWidget {
this.term.write(textStyle(this.promptState_.promptString)); this.term.write(textStyle(this.promptState_.promptString));
this.term.showCursor(true);
if (this.inputEventEmitter_) { if (this.inputEventEmitter_) {
this.inputEventEmitter_.redraw(); this.inputEventEmitter_.redraw();
// TODO: use termutils: this.inputEventEmitter_.rebase(this.absoluteInnerX + termutils.textLength(this.promptState_.promptString), this.absoluteInnerY);
this.inputEventEmitter_.rebase(this.absoluteInnerX + this.promptState_.promptString.length, this.absoluteInnerY);
return; return;
} }
this.term.showCursor(true);
let options = { let options = {
cancelable: true, cancelable: true,
history: this.history, history: this.history,
@ -95,6 +99,12 @@ class StatusBarWidget extends BaseWidget {
} }
} }
// If the inputField spans several lines invalidate the root so that
// the interface is relayouted.
if (termutils.textLength(this.promptState_.promptString) + termutils.textLength(input) >= this.innerWidth - 5) {
this.root.invalidate();
}
this.inputEventEmitter_ = null; this.inputEventEmitter_ = null;
this.term.showCursor(false); this.term.showCursor(false);
this.promptState_ = null; this.promptState_ = null;
@ -108,7 +118,7 @@ class StatusBarWidget extends BaseWidget {
} else { } else {
for (let i = 0; i < this.items_.length; i++) { for (let i = 0; i < this.items_.length; i++) {
this.term.write(textStyle(this.items_[i])); this.term.write(textStyle(this.items_[i].trim()));
} }
} }

View File

@ -21,10 +21,13 @@ msgstr ""
msgid "Set a todo as completed / not completed" msgid "Set a todo as completed / not completed"
msgstr "" msgstr ""
msgid "Enter the console" msgid "Toggle console between maximized/minimized/hidden/visible."
msgstr "" msgstr ""
msgid "Exit the console" msgid "Enter command line mode"
msgstr ""
msgid "Exit command line mode"
msgstr "" msgstr ""
msgid "Create a new note" msgid "Create a new note"
@ -36,9 +39,6 @@ msgstr ""
msgid "Create a new notebook" msgid "Create a new notebook"
msgstr "" msgstr ""
msgid "Maximise/minimise the console"
msgstr ""
msgid "Press Ctrl+D or type \"exit\" to exit the application" msgid "Press Ctrl+D or type \"exit\" to exit the application"
msgstr "" msgstr ""
@ -210,13 +210,13 @@ msgstr ""
msgid "The possible commands are:" msgid "The possible commands are:"
msgstr "" msgstr ""
msgid "To maximise/minimise the console, press Ctrl+J Ctrl+Z." msgid "To maximise/minimise the console, press \"C\"."
msgstr "" msgstr ""
msgid "To enter the console, press C" msgid "To enter command line mode, press \":\""
msgstr "" msgstr ""
msgid "To exit the console, press ESCAPE" msgid "To exit command line mode, press ESCAPE"
msgstr "" msgstr ""
msgid "To view a list of available shortcuts type `help shortcuts`" msgid "To view a list of available shortcuts type `help shortcuts`"

View File

@ -23,12 +23,14 @@ msgstr "Supprimer la note"
msgid "Set a todo as completed / not completed" msgid "Set a todo as completed / not completed"
msgstr "Tâches non-complétées et récentes" msgstr "Tâches non-complétées et récentes"
msgid "Enter the console" msgid "Toggle console between maximized/minimized/hidden/visible."
msgstr "" msgstr ""
#, fuzzy msgid "Enter command line mode"
msgid "Exit the console" msgstr ""
msgstr "Quitter le logiciel."
msgid "Exit command line mode"
msgstr ""
#, fuzzy #, fuzzy
msgid "Create a new note" msgid "Create a new note"
@ -42,10 +44,6 @@ msgstr "Créer une nouvelle tâche."
msgid "Create a new notebook" msgid "Create a new notebook"
msgstr "Créer un carnet." msgstr "Créer un carnet."
#, fuzzy
msgid "Maximise/minimise the console"
msgstr "Quitter le logiciel."
msgid "Press Ctrl+D or type \"exit\" to exit the application" msgid "Press Ctrl+D or type \"exit\" to exit the application"
msgstr "" msgstr ""
@ -234,13 +232,14 @@ msgstr ""
msgid "The possible commands are:" msgid "The possible commands are:"
msgstr "" msgstr ""
msgid "To maximise/minimise the console, press Ctrl+J Ctrl+Z." #, fuzzy
msgid "To maximise/minimise the console, press \"C\"."
msgstr "Quitter le logiciel."
msgid "To enter command line mode, press \":\""
msgstr "" msgstr ""
msgid "To enter the console, press C" msgid "To exit command line mode, press ESCAPE"
msgstr ""
msgid "To exit the console, press ESCAPE"
msgstr "" msgstr ""
msgid "To view a list of available shortcuts type `help shortcuts`" msgid "To view a list of available shortcuts type `help shortcuts`"
@ -744,6 +743,14 @@ msgstr ""
msgid "Welcome" msgid "Welcome"
msgstr "Bienvenue" msgstr "Bienvenue"
#, fuzzy
#~ msgid "Show/Hide the console"
#~ msgstr "Quitter le logiciel."
#, fuzzy
#~ msgid "Exit the console"
#~ msgstr "Quitter le logiciel."
#, fuzzy #, fuzzy
#~ msgid "Last command: %s" #~ msgid "Last command: %s"
#~ msgstr "Commande invalide : \"%s\"" #~ msgstr "Commande invalide : \"%s\""

View File

@ -21,10 +21,13 @@ msgstr ""
msgid "Set a todo as completed / not completed" msgid "Set a todo as completed / not completed"
msgstr "" msgstr ""
msgid "Enter the console" msgid "Toggle console between maximized/minimized/hidden/visible."
msgstr "" msgstr ""
msgid "Exit the console" msgid "Enter command line mode"
msgstr ""
msgid "Exit command line mode"
msgstr "" msgstr ""
msgid "Create a new note" msgid "Create a new note"
@ -36,9 +39,6 @@ msgstr ""
msgid "Create a new notebook" msgid "Create a new notebook"
msgstr "" msgstr ""
msgid "Maximise/minimise the console"
msgstr ""
msgid "Press Ctrl+D or type \"exit\" to exit the application" msgid "Press Ctrl+D or type \"exit\" to exit the application"
msgstr "" msgstr ""
@ -210,13 +210,13 @@ msgstr ""
msgid "The possible commands are:" msgid "The possible commands are:"
msgstr "" msgstr ""
msgid "To maximise/minimise the console, press Ctrl+J Ctrl+Z." msgid "To maximise/minimise the console, press \"C\"."
msgstr "" msgstr ""
msgid "To enter the console, press C" msgid "To enter command line mode, press \":\""
msgstr "" msgstr ""
msgid "To exit the console, press ESCAPE" msgid "To exit command line mode, press ESCAPE"
msgstr "" msgstr ""
msgid "To view a list of available shortcuts type `help shortcuts`" msgid "To view a list of available shortcuts type `help shortcuts`"

View File

@ -7,7 +7,7 @@
"url": "https://github.com/laurent22/joplin" "url": "https://github.com/laurent22/joplin"
}, },
"url": "git://github.com/laurent22/joplin.git", "url": "git://github.com/laurent22/joplin.git",
"version": "0.10.30", "version": "0.10.35",
"bin": { "bin": {
"joplin": "./main.js" "joplin": "./main.js"
}, },

View File

@ -1 +1 @@
f4b153177f47463d4e54b97430d0ae63 e1aaff911b34e00fcdbf4ec7c40f0aa6

View File

@ -2146,6 +2146,12 @@ slash@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
slice-ansi@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d"
dependencies:
is-fullwidth-code-point "^2.0.0"
sntp@1.x.x: sntp@1.x.x:
version "1.0.9" version "1.0.9"
resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
@ -2314,11 +2320,12 @@ through@^2.3.4:
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
tkwidgets@^0.5.3: tkwidgets@^0.5.3:
version "0.5.4" version "0.5.9"
resolved "https://registry.yarnpkg.com/tkwidgets/-/tkwidgets-0.5.4.tgz#e1341efeaef6a915e9cc3c867e25992acab7fd77" resolved "https://registry.yarnpkg.com/tkwidgets/-/tkwidgets-0.5.9.tgz#0770fb9db91d8e41f22fdbe77e436c40affaec01"
dependencies: dependencies:
chalk "^2.1.0" chalk "^2.1.0"
node-emoji "^1.8.1" node-emoji "^1.8.1"
slice-ansi "^1.0.0"
string-width "^2.1.1" string-width "^2.1.1"
terminal-kit "^1.13.11" terminal-kit "^1.13.11"
wrap-ansi "^3.0.1" wrap-ansi "^3.0.1"