diff --git a/CliClient/.gitignore b/CliClient/.gitignore index 1508cba48..540f2f47c 100644 --- a/CliClient/.gitignore +++ b/CliClient/.gitignore @@ -17,4 +17,5 @@ tests/cli-integration/ *.*~ tests/sync out.txt -linkToLocal.sh \ No newline at end of file +linkToLocal.sh +yarn-error.log \ No newline at end of file diff --git a/CliClient/app/app-gui.js b/CliClient/app/app-gui.js index 0e9eed01c..49c574fa9 100644 --- a/CliClient/app/app-gui.js +++ b/CliClient/app/app-gui.js @@ -130,6 +130,7 @@ class AppGui { const consoleWidget = new ConsoleWidget(); consoleWidget.hStretch = true; + consoleWidget.hide(); const statusBar = new StatusBarWidget(); statusBar.hStretch = true; @@ -142,8 +143,8 @@ class AppGui { const vLayout = new VLayoutWidget(); vLayout.name = 'vLayout'; - vLayout.addChild(hLayout, { type: 'stretch', factor: 1 }); - vLayout.addChild(consoleWidget, { type: 'fixed', factor: 3 }); + vLayout.addChild(hLayout, { type: 'stretch', factor: 2 }); + vLayout.addChild(consoleWidget, { type: 'stretch', factor: 1 }); vLayout.addChild(statusBar, { type: 'fixed', factor: 1 }); const win1 = new WindowWidget(); @@ -167,13 +168,35 @@ class AppGui { 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[':'] = { - description: _('Enter the console'), - isDocOnly: true, + description: _('Enter command line mode'), + action: async () => { + const cmd = await this.widget('statusBar').prompt(); + if (!cmd) return; + this.stdout('> ' + cmd); + await this.processCommand(cmd); + }, }; - shortcuts['ESC'] = { - description: _('Exit the console'), + shortcuts['ESC'] = { // Built into terminal-kit inputField + description: _('Exit command line mode'), 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'] = { description: _('Create a new note'), action: { type: 'prompt', initialText: 'mknote ' }, @@ -204,46 +239,52 @@ class AppGui { action: { type: 'prompt', initialText: 'mkbook ' }, } - shortcuts['CTRL_JCTRL_Z'] = { - friendlyName: 'Ctrl+J Ctrl+Z', - description: _('Maximise/minimise the console'), - action: () => { this.toggleMaximizeConsole(); }, - } - return shortcuts; } - toggleMaximizeConsole() { - this.maximizeConsole(!this.consoleIsMaximized()); + toggleConsole() { + 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) { - // const consoleWidget = this.widget('console'); + const consoleWidget = this.widget('console'); - // if (consoleWidget.isMaximized__ === undefined) { - // consoleWidget.isMaximized__ = false; - // } + if (consoleWidget.isMaximized__ === undefined) { + consoleWidget.isMaximized__ = false; + } - // if (consoleWidget.isMaximized__ === doMaximize) return; + if (consoleWidget.isMaximized__ === doMaximize) return; - // let constraints = { - // type: 'fixed', - // factor: !doMaximize ? 5 : this.widget('vLayout').height - 4, - // }; + let constraints = { + type: 'stretch', + factor: !doMaximize ? 1 : 4, + }; - // consoleWidget.isMaximized__ = doMaximize; + consoleWidget.isMaximized__ = doMaximize; - // this.widget('vLayout').setWidgetConstraints(consoleWidget, constraints); + this.widget('vLayout').setWidgetConstraints(consoleWidget, constraints); } minimizeConsole() { - //this.maximizeConsole(false); + this.maximizeConsole(false); } consoleIsMaximized() { - return false; - - //return this.widget('console').isMaximized__ === true; + return this.widget('console').isMaximized__ === true; } widget(name) { @@ -355,10 +396,13 @@ class AppGui { } } - stdout(...object) { - for (let i = 0; i < object.length; i++) { - const v = typeof object[i] === 'object' ? JSON.stringify(object[i]) : object[i]; - this.widget('console').addItem(v); + stdout(text) { + if (text === null || text === undefined) return; + + 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(); @@ -369,7 +413,7 @@ class AppGui { let msg = ''; - const text = consoleWidget.items.length ? consoleWidget.items[consoleWidget.items.length - 1] : ''; + const text = consoleWidget.lastLine; const cmd = this.app().currentCommand(); if (cmd) { @@ -399,6 +443,10 @@ class AppGui { term.on('key', async (name, matches, data) => { + // ------------------------------------------------------------------------- + // Handle special shortcuts + // ------------------------------------------------------------------------- + if (name === 'CTRL_D') { const cmd = this.app().currentCommand(); @@ -418,11 +466,15 @@ class AppGui { this.stdout(_('Press Ctrl+D or type "exit" to exit the application')); } else { this.commandCancelCalled_ = true; - await cmd.cancel(); + await cmd.cancel() this.commandCancelCalled_ = false; } return; } + + // ------------------------------------------------------------------------- + // Build up current shortcut + // ------------------------------------------------------------------------- const now = (new Date()).getTime(); @@ -440,38 +492,34 @@ class AppGui { this.lastShortcutKeyTime_ = now; - if (!this.app().currentCommand()) { - if (name === ':' && !statusBar.promptActive) { - 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_); + // ------------------------------------------------------------------------- + // Process shortcut and execute associated command + // ------------------------------------------------------------------------- - const shortcutKey = this.currentShortcutKeys_.join(''); - if (shortcutKey in this.shortcuts_) { - const cmd = this.shortcuts_[shortcutKey].action; - if (!cmd.isDocOnly) { - this.currentShortcutKeys_ = []; - if (typeof cmd === 'function') { - await cmd(); - } else if (typeof cmd === 'object') { - if (cmd.type === 'prompt') { - const commandString = await statusBar.prompt(cmd.initialText ? cmd.initialText : ''); - this.stdout(commandString); - await this.processCommand(commandString); - } else { - throw new Error('Unknown command: ' + JSON.stringify(cmd)); - } - } else { - this.stdout(cmd); - await this.processCommand(cmd); - } - } - } + const shortcutKey = this.currentShortcutKeys_.join(''); + const cmd = shortcutKey in this.shortcuts_ ? this.shortcuts_[shortcutKey] : null; + + let processShortcutKeys = !this.app().currentCommand() && !statusBar.promptActive && cmd; + if (cmd && cmd.canRunAlongOtherCommands) processShortcutKeys = true; + if (cmd && cmd.isDocOnly) processShortcutKeys = false; + + this.logger().info('Shortcut:', shortcutKey, processShortcutKeys, cmd ? cmd.description : '(no command)'); + + if (processShortcutKeys) { + this.currentShortcutKeys_ = []; + if (typeof cmd.action === 'function') { + await cmd.action(); + } else if (typeof cmd.action === 'object') { + if (cmd.action.type === 'prompt') { + const commandString = await statusBar.prompt(cmd.action.initialText ? cmd.action.initialText : ''); + this.stdout(commandString); + 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); } } diff --git a/CliClient/app/app.js b/CliClient/app/app.js index 1d1a35793..d9214e707 100644 --- a/CliClient/app/app.js +++ b/CliClient/app/app.js @@ -270,8 +270,8 @@ class Application { } setupCommand(cmd) { - cmd.setStdout((...object) => { - this.gui().stdout(...object); + cmd.setStdout((text) => { + this.gui().stdout(text); }); cmd.setDispatcher((action) => { @@ -382,8 +382,14 @@ class Application { const commandName = argv[0]; this.activeCommand_ = this.findCommandByName(commandName); 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; + if (outException) throw outException; } currentCommand() { diff --git a/CliClient/app/base-command.js b/CliClient/app/base-command.js index ff4ff8616..88455c950 100644 --- a/CliClient/app/base-command.js +++ b/CliClient/app/base-command.js @@ -60,8 +60,8 @@ class BaseCommand { this.stdout_ = fn; } - stdout(...object) { - if (this.stdout_) this.stdout_(...object); + stdout(text) { + if (this.stdout_) this.stdout_(text); } setPrompt(fn) { diff --git a/CliClient/app/command-config.js b/CliClient/app/command-config.js index 5720ddd47..04a673f3f 100644 --- a/CliClient/app/command-config.js +++ b/CliClient/app/command-config.js @@ -38,11 +38,15 @@ class Command extends BaseCommand { if (!verbose && !value) continue; this.stdout(renderKeyValue(keys[i])); } + app().gui().showConsole(); + app().gui().maximizeConsole(); return; } if (args.name && !args.value) { this.stdout(renderKeyValue(args.name)); + app().gui().showConsole(); + app().gui().maximizeConsole(); return; } diff --git a/CliClient/app/command-help.js b/CliClient/app/command-help.js index ae93d8d6f..af248c801 100644 --- a/CliClient/app/command-help.js +++ b/CliClient/app/command-help.js @@ -37,7 +37,6 @@ class Command extends BaseCommand { } else if (args.command) { const command = app().findCommandByName(args['command']); if (!command) throw new Error(_('Cannot find "%s".', args.command)); - this.stdout(renderCommandHelp(command, stdoutWidth)); } else { const commands = app().commands(); @@ -50,21 +49,19 @@ class Command extends BaseCommand { commandNames.sort(); - let lines = []; - lines.push(_('Type `help [command]` for more information about a command.')); - lines.push(''); - lines.push(_('The possible commands are:')); - lines.push(''); - lines.push(commandNames.join(', ')); - lines.push(''); - lines.push(_('To maximise/minimise the console, press Ctrl+J Ctrl+Z.')); - lines.push(_('To enter the console, press C')); - lines.push(_('To exit the console, press ESCAPE')); - lines.push(_('To view a list of available shortcuts type `help shortcuts`')); - - this.stdout(wrap(lines.join("\n"), '', stdoutWidth)); + this.stdout(_('Type `help [command]` for more information about a command.')); + this.stdout(''); + this.stdout(_('The possible commands are:')); + this.stdout(''); + this.stdout(commandNames.join(', ')); + this.stdout(''); + this.stdout(_('To maximise/minimise the console, press "C".')); + this.stdout(_('To enter command line mode, press ":"')); + this.stdout(_('To exit command line mode, press ESCAPE')); + this.stdout(_('To view a list of available shortcuts type `help shortcuts`')); } + app().gui().showConsole(); app().gui().maximizeConsole(); } diff --git a/CliClient/app/gui/ConsoleWidget.js b/CliClient/app/gui/ConsoleWidget.js index af0e22753..78cc282a5 100644 --- a/CliClient/app/gui/ConsoleWidget.js +++ b/CliClient/app/gui/ConsoleWidget.js @@ -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() { super(); + this.lines_ = []; + this.updateText_ = false; + this.markdownRendering = false; + this.stickToBottom = true; } get name() { return 'console'; } - addItem(v) { - super.addItem(v); - this.currentIndex = this.items.length - 1; + get lastLine() { + return this.lines_.length ? this.lines_[this.lines_.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(); } } diff --git a/CliClient/app/gui/StatusBarWidget.js b/CliClient/app/gui/StatusBarWidget.js index 57f9fe620..7bbb60d9d 100644 --- a/CliClient/app/gui/StatusBarWidget.js +++ b/CliClient/app/gui/StatusBarWidget.js @@ -1,5 +1,6 @@ const BaseWidget = require('tkwidgets/BaseWidget.js'); const chalk = require('chalk'); +const termutils = require('tkwidgets/framework/termutils.js'); class StatusBarWidget extends BaseWidget { @@ -16,6 +17,10 @@ class StatusBarWidget extends BaseWidget { return 'statusBar'; } + get canHaveFocus() { + return false; + } + setItemAt(index, text) { this.items_[index] = text; this.invalidate(); @@ -64,15 +69,14 @@ class StatusBarWidget extends BaseWidget { this.term.write(textStyle(this.promptState_.promptString)); - this.term.showCursor(true); - if (this.inputEventEmitter_) { this.inputEventEmitter_.redraw(); - // TODO: use termutils: - this.inputEventEmitter_.rebase(this.absoluteInnerX + this.promptState_.promptString.length, this.absoluteInnerY); + this.inputEventEmitter_.rebase(this.absoluteInnerX + termutils.textLength(this.promptState_.promptString), this.absoluteInnerY); return; } + this.term.showCursor(true); + let options = { cancelable: true, 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.term.showCursor(false); this.promptState_ = null; @@ -108,7 +118,7 @@ class StatusBarWidget extends BaseWidget { } else { for (let i = 0; i < this.items_.length; i++) { - this.term.write(textStyle(this.items_[i])); + this.term.write(textStyle(this.items_[i].trim())); } } diff --git a/CliClient/locales/en_GB.po b/CliClient/locales/en_GB.po index 40b394aa7..1fa8d18f6 100644 --- a/CliClient/locales/en_GB.po +++ b/CliClient/locales/en_GB.po @@ -21,10 +21,13 @@ msgstr "" msgid "Set a todo as completed / not completed" msgstr "" -msgid "Enter the console" +msgid "Toggle console between maximized/minimized/hidden/visible." msgstr "" -msgid "Exit the console" +msgid "Enter command line mode" +msgstr "" + +msgid "Exit command line mode" msgstr "" msgid "Create a new note" @@ -36,9 +39,6 @@ msgstr "" msgid "Create a new notebook" msgstr "" -msgid "Maximise/minimise the console" -msgstr "" - msgid "Press Ctrl+D or type \"exit\" to exit the application" msgstr "" @@ -210,13 +210,13 @@ msgstr "" msgid "The possible commands are:" msgstr "" -msgid "To maximise/minimise the console, press Ctrl+J Ctrl+Z." +msgid "To maximise/minimise the console, press \"C\"." msgstr "" -msgid "To enter the console, press C" +msgid "To enter command line mode, press \":\"" msgstr "" -msgid "To exit the console, press ESCAPE" +msgid "To exit command line mode, press ESCAPE" msgstr "" msgid "To view a list of available shortcuts type `help shortcuts`" diff --git a/CliClient/locales/fr_FR.po b/CliClient/locales/fr_FR.po index de9e8c100..f517e3945 100644 --- a/CliClient/locales/fr_FR.po +++ b/CliClient/locales/fr_FR.po @@ -23,12 +23,14 @@ msgstr "Supprimer la note" msgid "Set a todo as completed / not completed" msgstr "Tâches non-complétées et récentes" -msgid "Enter the console" +msgid "Toggle console between maximized/minimized/hidden/visible." msgstr "" -#, fuzzy -msgid "Exit the console" -msgstr "Quitter le logiciel." +msgid "Enter command line mode" +msgstr "" + +msgid "Exit command line mode" +msgstr "" #, fuzzy msgid "Create a new note" @@ -42,10 +44,6 @@ msgstr "Créer une nouvelle tâche." msgid "Create a new notebook" 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" msgstr "" @@ -234,13 +232,14 @@ msgstr "" msgid "The possible commands are:" 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 "" -msgid "To enter the console, press C" -msgstr "" - -msgid "To exit the console, press ESCAPE" +msgid "To exit command line mode, press ESCAPE" msgstr "" msgid "To view a list of available shortcuts type `help shortcuts`" @@ -744,6 +743,14 @@ msgstr "" msgid "Welcome" msgstr "Bienvenue" +#, fuzzy +#~ msgid "Show/Hide the console" +#~ msgstr "Quitter le logiciel." + +#, fuzzy +#~ msgid "Exit the console" +#~ msgstr "Quitter le logiciel." + #, fuzzy #~ msgid "Last command: %s" #~ msgstr "Commande invalide : \"%s\"" diff --git a/CliClient/locales/joplin.pot b/CliClient/locales/joplin.pot index 40b394aa7..1fa8d18f6 100644 --- a/CliClient/locales/joplin.pot +++ b/CliClient/locales/joplin.pot @@ -21,10 +21,13 @@ msgstr "" msgid "Set a todo as completed / not completed" msgstr "" -msgid "Enter the console" +msgid "Toggle console between maximized/minimized/hidden/visible." msgstr "" -msgid "Exit the console" +msgid "Enter command line mode" +msgstr "" + +msgid "Exit command line mode" msgstr "" msgid "Create a new note" @@ -36,9 +39,6 @@ msgstr "" msgid "Create a new notebook" msgstr "" -msgid "Maximise/minimise the console" -msgstr "" - msgid "Press Ctrl+D or type \"exit\" to exit the application" msgstr "" @@ -210,13 +210,13 @@ msgstr "" msgid "The possible commands are:" msgstr "" -msgid "To maximise/minimise the console, press Ctrl+J Ctrl+Z." +msgid "To maximise/minimise the console, press \"C\"." msgstr "" -msgid "To enter the console, press C" +msgid "To enter command line mode, press \":\"" msgstr "" -msgid "To exit the console, press ESCAPE" +msgid "To exit command line mode, press ESCAPE" msgstr "" msgid "To view a list of available shortcuts type `help shortcuts`" diff --git a/CliClient/package.json b/CliClient/package.json index 2310f2bd2..d71f86852 100644 --- a/CliClient/package.json +++ b/CliClient/package.json @@ -7,7 +7,7 @@ "url": "https://github.com/laurent22/joplin" }, "url": "git://github.com/laurent22/joplin.git", - "version": "0.10.30", + "version": "0.10.35", "bin": { "joplin": "./main.js" }, diff --git a/CliClient/package.json.md5 b/CliClient/package.json.md5 index 953c058bf..a408f0033 100644 --- a/CliClient/package.json.md5 +++ b/CliClient/package.json.md5 @@ -1 +1 @@ -f4b153177f47463d4e54b97430d0ae63 \ No newline at end of file +e1aaff911b34e00fcdbf4ec7c40f0aa6 \ No newline at end of file diff --git a/CliClient/yarn.lock b/CliClient/yarn.lock index 79ab3a84f..0177284a9 100644 --- a/CliClient/yarn.lock +++ b/CliClient/yarn.lock @@ -2146,6 +2146,12 @@ slash@^1.0.0: version "1.0.0" 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: version "1.0.9" 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" tkwidgets@^0.5.3: - version "0.5.4" - resolved "https://registry.yarnpkg.com/tkwidgets/-/tkwidgets-0.5.4.tgz#e1341efeaef6a915e9cc3c867e25992acab7fd77" + version "0.5.9" + resolved "https://registry.yarnpkg.com/tkwidgets/-/tkwidgets-0.5.9.tgz#0770fb9db91d8e41f22fdbe77e436c40affaec01" dependencies: chalk "^2.1.0" node-emoji "^1.8.1" + slice-ansi "^1.0.0" string-width "^2.1.1" terminal-kit "^1.13.11" wrap-ansi "^3.0.1"