1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Removed vorpal

This commit is contained in:
Laurent Cozic 2017-08-04 17:51:01 +00:00
parent 60c8b7530b
commit 9ed22265ba
12 changed files with 322 additions and 455 deletions

View File

@ -14,7 +14,8 @@ import { _, setLocale, defaultLocale, closestSupportedLocale } from 'lib/locale.
import os from 'os';
import fs from 'fs-extra';
import yargParser from 'yargs-parser';
import omelette from 'omelette';
import { handleAutocompletion } from './autocompletion.js';
import { cliUtils } from './cli-utils.js';
class Application {
@ -22,12 +23,11 @@ class Application {
this.showPromptString_ = true;
this.logger_ = new Logger();
this.dbLogger_ = new Logger();
this.autocompletion_ = { active: false };
this.commands_ = {};
this.commandMetadata_ = null;
}
// vorpal() {
// return this.vorpal_;
// }
currentFolder() {
return this.currentFolder_;
}
@ -120,12 +120,6 @@ class Application {
continue;
}
// if (arg == '--redraw-disabled') {
// vorpalUtils.setRedrawEnabled(false);
// argv.splice(0, 1);
// continue;
// }
if (arg == '--update-geolocation-disabled') {
Note.updateGeolocationEnabled_ = false;
argv.splice(0, 1);
@ -151,6 +145,34 @@ class Application {
continue;
}
if (arg == '--autocompletion') {
this.autocompletion_.active = true;
argv.splice(0, 1);
continue;
}
if (arg == '--autocompletion') {
this.autocompletion_.active = true;
argv.splice(0, 1);
continue;
}
if (arg == '--ac-current') {
if (!nextArg) throw new Error(_('Usage: %s', '--ac-current <num>'));
this.autocompletion_.current = nextArg;
argv.splice(0, 2);
continue;
}
if (arg == '--ac-line') {
if (!nextArg) throw new Error(_('Usage: %s', '--ac-line <line>'));
let line = nextArg.replace(/\|__QUOTE__\|/g, '"');
line = line.split('|__SEP__|');
this.autocompletion_.line = line;
argv.splice(0, 2);
continue;
}
if (arg.length && arg[0] == '-') {
throw new Error(_('Unknown flag: %s', arg));
} else {
@ -199,63 +221,6 @@ class Application {
}
}
loadCommands_() {
return;
this.onLocaleChanged(); // Ensures that help and exit commands are translated
fs.readdirSync(__dirname).forEach((path) => {
if (path.indexOf('command-') !== 0) return;
const ext = fileExtension(path)
if (ext != 'js') return;
let CommandClass = require('./' + path);
let cmd = new CommandClass();
if (!cmd.enabled()) return;
let vorpalCmd = this.vorpal().command(cmd.usage(), cmd.description());
vorpalCmd.__commandObject = cmd;
// TODO: maybe remove if the PR is not merged
if ('disableTypeCasting' in vorpalCmd) vorpalCmd.disableTypeCasting();
for (let i = 0; i < cmd.aliases().length; i++) {
vorpalCmd.alias(cmd.aliases()[i]);
}
for (let i = 0; i < cmd.options().length; i++) {
let options = cmd.options()[i];
if (options.length == 2) vorpalCmd.option(options[0], options[1]);
if (options.length == 3) vorpalCmd.option(options[0], options[1], options[2]);
if (options.length > 3) throw new Error('Invalid number of option arguments');
}
if (cmd.autocomplete()) vorpalCmd.autocomplete(cmd.autocomplete());
let actionFn = async function(args, end) {
try {
const fn = cmd.action.bind(this);
await fn(args);
} catch (error) {
this.log(error);
}
vorpalUtils.redrawDone();
end();
};
vorpalCmd.action(actionFn);
let cancelFn = async function() {
const fn = cmd.cancel.bind(this);
await fn();
};
vorpalCmd.cancel(cancelFn);
if (cmd.hidden()) vorpalCmd.hidden();
});
}
baseModelListener(action) {
switch (action.type) {
@ -270,6 +235,47 @@ class Application {
}
}
loadAllCommands() {
fs.readdirSync(__dirname).forEach((path) => {
if (path.indexOf('command-') !== 0) return;
const ext = fileExtension(path)
if (ext != 'js') return;
let CommandClass = require('./' + path);
let cmd = new CommandClass();
if (!cmd.enabled()) return;
this.commands_[cmd.name()] = cmd;
});
}
async commandMetadata() {
if (this.commandMetadata_) return this.commandMetadata_;
const storage = require('node-persist');
await storage.init({ dir: 'commandMetadata', ttl: 1000 * 60 * 60 * 24 });
let output = await storage.getItem('metadata');
if (Setting.value('env') != 'dev' && output) {
this.commandMetadata_ = output;
return Object.assign({}, this.commandMetadata_);
}
this.loadAllCommands();
output = {};
for (let n in this.commands_) {
if (!this.commands_.hasOwnProperty(n)) continue;
const cmd = this.commands_[n];
output[n] = cmd.metadata();
}
await storage.setItem('metadata', output);
this.commandMetadata_ = output;
return Object.assign({}, this.commandMetadata_);
}
findCommandByName(name) {
let CommandClass = null;
try {
@ -288,98 +294,11 @@ class Application {
return cmd;
}
makeCommandArgs(cmd, argv) {
let cmdUsage = cmd.usage();
cmdUsage = yargParser(cmdUsage);
let output = {};
let options = cmd.options();
let booleanFlags = [];
let aliases = {};
for (let i = 0; i < options.length; i++) {
if (options[i].length != 2) throw new Error('Invalid options: ' + options[i]);
let flags = options[i][0];
let text = options[i][1];
flags = this.parseFlags(flags);
if (!flags.arg) {
booleanFlags.push(flags.short);
if (flags.long) booleanFlags.push(flags.long);
}
if (flags.short && flags.long) {
aliases[flags.long] = [flags.short];
}
}
let args = yargParser(argv, {
boolean: booleanFlags,
alias: aliases,
});
for (let i = 1; i < cmdUsage['_'].length; i++) {
const a = this.parseCommandArg(cmdUsage['_'][i]);
if (a.required && !args['_'][i]) throw new Error('Missing required arg: ' + a.name);
if (i >= a.length) {
output[a.name] = null;
} else {
output[a.name] = args['_'][i];
}
}
let argOptions = {};
for (let key in args) {
if (!args.hasOwnProperty(key)) continue;
if (key == '_') continue;
argOptions[key] = args[key];
}
output.options = argOptions;
return output;
}
parseFlags(flags) {
let output = {};
flags = flags.split(',');
for (let i = 0; i < flags.length; i++) {
let f = flags[i].trim();
if (f.substr(0, 2) == '--') {
f = f.split(' ');
output.long = f[0].substr(2).trim();
if (f.length == 2) {
output.arg = this.parseCommandArg(f[1].trim());
}
} else if (f.substr(0, 1) == '-') {
output.short = f.substr(1);
}
}
return output;
}
parseCommandArg(arg) {
if (arg.length <= 2) throw new Error('Invalid command arg: ' + arg);
const c1 = arg[0];
const c2 = arg[arg.length - 1];
const name = arg.substr(1, arg.length - 2);
if (c1 == '<' && c2 == '>') {
return { required: true, name: name };
} else if (c1 == '[' && c2 == ']') {
return { required: false, name: name };
} else {
throw new Error('Invalid command arg: ' + arg);
}
}
async execCommand(argv) {
if (!argv.length) throw new Error('Empty command');
const commandName = argv[0];
const command = this.findCommandByName(commandName);
const cmdArgs = this.makeCommandArgs(command, argv);
const cmdArgs = cliUtils.makeCommandArgs(command, argv);
await command.action(cmdArgs);
}
@ -438,109 +357,22 @@ class Application {
setLocale(Setting.value('locale'));
//this.loadCommands_();
let currentFolderId = Setting.value('activeFolderId');
this.currentFolder_ = null;
if (currentFolderId) this.currentFolder_ = await Folder.load(currentFolderId);
if (!this.currentFolder_) this.currentFolder_ = await Folder.defaultFolder();
Setting.setValue('activeFolderId', this.currentFolder_ ? this.currentFolder_.id : '');
const completion = omelette(`joplindev <title>`);
// Bind events for every template part.
completion.on('title', ({ before, reply }) => {
const child_process = require('child_process');
const stdout = child_process.execSync('joplindev autocompletion --before "' + before + '" notes');
reply(JSON.parse(stdout));
});
// Initialize the omelette.
completion.init()
// const omelCommand = ({ before, reply }) => {
// reply([ 'cat', 'ls' ]);
// }
// const omelTitle = ({ reply }) => {
// if (this.currentFolder_) {
// Note.previews(this.currentFolder_.id).then((notes) => {
// console.info(notes.length);
// const output = notes.map((n) => n.title);
// reply(['aa']);
// //reply(output);
// });
// } else {
// reply([]);
// }
// }
// omelette`joplindev ${omelCommand} ${omelTitle}`.init()
if (this.autocompletion_.active) {
let items = await handleAutocompletion(this.autocompletion_);
for (let i = 0; i < items.length; i++) {
items[i] = items[i].replace(/ /g, '\\ ');
}
console.info(items.join("\n"));
return;
}
this.execCommand(argv);
// if (this.currentFolder_) await this.vorpal().exec('use ' + this.escapeShellArg(this.currentFolder_.title));
// // If we still have arguments, pass it to Vorpal and exit
// if (argv.length) {
// let cmd = this.shellArgsToString(argv);
// await this.vorpal().exec(cmd);
// } else {
// setInterval(() => {
// reg.scheduleSync(0);
// }, 1000 * 60 * 5);
// this.updatePrompt();
// this.vorpal().show();
// this.vorpal().history(Setting.value('appId')); // Enables persistent history
// if (!this.currentFolder()) {
// this.vorpal().log(_('No notebook is defined. Create one with `mkbook <notebook>`.'));
// }
// }
}
}

View File

@ -0,0 +1,87 @@
import { app } from './app.js';
import { Note } from 'lib/models/note.js';
import { Folder } from 'lib/models/folder.js';
import { cliUtils } from './cli-utils.js';
import yargParser from 'yargs-parser';
async function handleAutocompletion(autocompletion) {
let args = autocompletion.line.slice();
args.splice(0, 1);
let current = autocompletion.current - 1;
const currentWord = args[current];
// Auto-complete the command name
if (current == 0) {
const metadata = await app().commandMetadata();
let commandNames = [];
for (let n in metadata) {
if (!metadata.hasOwnProperty(n)) continue;
const md = metadata[n];
if (md.hidden) continue;
if (currentWord == n) return [n];
if (n.indexOf(currentWord) === 0) commandNames.push(n);
}
return commandNames;
}
const commandName = args[0];
const metadata = await app().commandMetadata();
const md = metadata[commandName];
const options = md && md.options ? md.options : [];
// Auto-complete the command options
if (!currentWord) return [];
const includeLongs = currentWord.length == 1 ? currentWord.substr(0, 1) == '-' : currentWord.substr(0, 2) == '--';
const includeShorts = currentWord.length <= 2 && currentWord.substr(0, 1) == '-' && currentWord.substr(0, 2) != '--';
if (includeLongs || includeShorts) {
const output = [];
for (let i = 0; i < options.length; i++) {
const flags = cliUtils.parseFlags(options[i][0]);
const long = flags.long ? '--' + flags.long : null;
const short = flags.short ? '-' + flags.short : null;
if (includeLongs && long && long.indexOf(currentWord) === 0) output.push(long);
if (includeShorts && short && short.indexOf(currentWord) === 0) output.push(short);
}
return output;
}
// Auto-complete the command arguments
let argIndex = -1;
for (let i = 0; i < args.length; i++) {
const w = args[i];
if (i == 0 || w.indexOf('-') == 0) {
continue;
}
argIndex++;
}
if (argIndex < 0) return [];
let cmdUsage = yargParser(md.usage)['_'];
cmdUsage.splice(0, 1);
if (cmdUsage.length <= argIndex) return [];
let argName = cmdUsage[argIndex];
argName = cliUtils.parseCommandArg(argName).name;
if (argName == 'note') {
if (!app().currentFolder()) return [];
const notes = await Note.previews(app().currentFolder().id, { titlePattern: currentWord + '*' });
return notes.map((n) => n.title);
}
if (argName == 'notebook') {
const folders = await Folder.search({ titlePattern: currentWord + '*' });
return folders.map((n) => n.title);
}
return [];
}
export { handleAutocompletion };

View File

@ -34,6 +34,20 @@ class BaseCommand {
async cancel() {}
name() {
let r = this.usage().split(' ');
return r[0];
}
metadata() {
return {
name: this.name(),
usage: this.usage(),
options: this.options(),
hidden: this.hidden(),
};
}
}
export { BaseCommand };

View File

@ -1,8 +1,9 @@
import yargParser from 'yargs-parser';
const stringPadding = require('string-padding');
const cliUtils = {
const cliUtils = {};
printArray: function(logFunction, rows, headers = null) {
cliUtils.printArray = function(logFunction, rows, headers = null) {
if (!rows.length) return '';
const ALIGN_LEFT = 0;
@ -34,8 +35,93 @@ const cliUtils = {
}
logFunction(line.join(' '));
}
},
}
cliUtils.parseFlags = function(flags) {
let output = {};
flags = flags.split(',');
for (let i = 0; i < flags.length; i++) {
let f = flags[i].trim();
if (f.substr(0, 2) == '--') {
f = f.split(' ');
output.long = f[0].substr(2).trim();
if (f.length == 2) {
output.arg = cliUtils.parseCommandArg(f[1].trim());
}
} else if (f.substr(0, 1) == '-') {
output.short = f.substr(1);
}
}
return output;
}
cliUtils.parseCommandArg = function(arg) {
if (arg.length <= 2) throw new Error('Invalid command arg: ' + arg);
const c1 = arg[0];
const c2 = arg[arg.length - 1];
const name = arg.substr(1, arg.length - 2);
if (c1 == '<' && c2 == '>') {
return { required: true, name: name };
} else if (c1 == '[' && c2 == ']') {
return { required: false, name: name };
} else {
throw new Error('Invalid command arg: ' + arg);
}
}
cliUtils.makeCommandArgs = function(cmd, argv) {
let cmdUsage = cmd.usage();
cmdUsage = yargParser(cmdUsage);
let output = {};
let options = cmd.options();
let booleanFlags = [];
let aliases = {};
for (let i = 0; i < options.length; i++) {
if (options[i].length != 2) throw new Error('Invalid options: ' + options[i]);
let flags = options[i][0];
let text = options[i][1];
flags = cliUtils.parseFlags(flags);
if (!flags.arg) {
booleanFlags.push(flags.short);
if (flags.long) booleanFlags.push(flags.long);
}
if (flags.short && flags.long) {
aliases[flags.long] = [flags.short];
}
}
let args = yargParser(argv, {
boolean: booleanFlags,
alias: aliases,
});
for (let i = 1; i < cmdUsage['_'].length; i++) {
const a = cliUtils.parseCommandArg(cmdUsage['_'][i]);
if (a.required && !args['_'][i]) throw new Error('Missing required arg: ' + a.name);
if (i >= a.length) {
output[a.name] = null;
} else {
output[a.name] = args['_'][i];
}
}
let argOptions = {};
for (let key in args) {
if (!args.hasOwnProperty(key)) continue;
if (key == '_') continue;
argOptions[key] = args[key];
}
output.options = argOptions;
return output;
}
export { cliUtils };

View File

@ -16,6 +16,7 @@ class Command extends BaseCommand {
options() {
return [
[ '--before <before>', 'before' ],
[ '--multiline', 'multiline' ],
];
}
@ -24,17 +25,29 @@ class Command extends BaseCommand {
}
async action(args) {
console.info(args);
let output = [];
if (args.type == 'notes') {
// TODO:
if (!app().currentFolder()) throw new Error('no current folder');
let options = {};
// this.log(JSON.stringify(['XX'+args.options.before+'XX', 'aa','bb']));
// return;
//console.info(args.options.before);
if (args.options.before) options.titlePattern = args.options.before + '*';
const notes = await Note.previews(app().currentFolder().id, options);
output = notes.map((n) => n.title);
}
if (args.options.multiline) {
output = output.map((s) => s.replace(/ /g, '\\ '));
this.log(output.join("\n"));
} else {
this.log(JSON.stringify(output));
}
}
}

View File

@ -9,7 +9,7 @@ import { autocompleteItems } from './autocomplete.js';
class Command extends BaseCommand {
usage() {
return 'cat <title>';
return 'cat <note>';
}
description() {
@ -27,7 +27,7 @@ class Command extends BaseCommand {
}
async action(args) {
let title = args['title'];
let title = args['note'];
let item = await app().loadItem(BaseModel.TYPE_NOTE, title, { parent: app().currentFolder() });
if (!item) throw new Error(_('Cannot find "%s".', title));

View File

@ -9,11 +9,11 @@ import { autocompleteItems } from './autocomplete.js';
class Command extends BaseCommand {
usage() {
return 'cp <pattern> [notebook]';
return 'cp <note> [notebook]';
}
description() {
return _('Duplicates the notes matching <pattern> to [notebook]. If no notebook is specified the note is duplicated in the current notebook.');
return _('Duplicates the notes matching <note> to [notebook]. If no notebook is specified the note is duplicated in the current notebook.');
}
autocomplete() {
@ -30,8 +30,8 @@ class Command extends BaseCommand {
if (!folder) throw new Error(_('Cannot find "%s".', args['notebook']));
const notes = await app().loadItems(BaseModel.TYPE_NOTE, args['pattern']);
if (!notes.length) throw new Error(_('Cannot find "%s".', args['pattern']));
const notes = await app().loadItems(BaseModel.TYPE_NOTE, args['note']);
if (!notes.length) throw new Error(_('Cannot find "%s".', args['note']));
for (let i = 0; i < notes.length; i++) {
const newNote = await Note.copyToFolder(notes[i].id, folder.id);

View File

@ -11,7 +11,7 @@ import { autocompleteItems } from './autocomplete.js';
class Command extends BaseCommand {
usage() {
return 'edit <title>';
return 'edit <note>';
}
description() {
@ -40,7 +40,7 @@ class Command extends BaseCommand {
}
try {
let title = args['title'];
let title = args['note'];
if (!app().currentFolder()) throw new Error(_('No active notebook.'));
let note = await app().loadItem(BaseModel.TYPE_NOTE, title);
@ -70,8 +70,6 @@ class Command extends BaseCommand {
this.log(_('Starting to edit note. Close the editor to get back to the prompt.'));
//app().vorpal().hide();
await fs.writeFile(tempFilePath, content);
let watchTimeout = null;

View File

@ -1,148 +0,0 @@
import { time } from 'lib/time-utils.js';
import stringPadding from 'string-padding';
const vorpalUtils = {};
let vorpal_ = null;
let redrawStarted_ = false;
let redrawLastUpdateTime_ = 0;
let redrawLastLog_ = null;
let redrawEnabled_ = true;
let errorStackTraceEnabled_ = false;
function initialize(vorpal) {
vorpal_ = vorpal;
}
function printArray(commandInstance, rows, headers = null) {
if (!rows.length) return '';
const ALIGN_LEFT = 0;
const ALIGN_RIGHT = 1;
let colWidths = [];
let colAligns = [];
for (let i = 0; i < rows.length; i++) {
let row = rows[i];
for (let j = 0; j < row.length; j++) {
let item = row[j];
let width = item ? item.toString().length : 0;
let align = typeof item == 'number' ? ALIGN_RIGHT : ALIGN_LEFT;
if (!colWidths[j] || colWidths[j] < width) colWidths[j] = width;
if (colAligns.length <= j) colAligns[j] = align;
}
}
let lines = [];
for (let row = 0; row < rows.length; row++) {
let line = [];
for (let col = 0; col < colWidths.length; col++) {
let item = rows[row][col];
let width = colWidths[col];
let dir = colAligns[col] == ALIGN_LEFT ? stringPadding.RIGHT : stringPadding.LEFT;
line.push(stringPadding(item, width, ' ', dir));
}
commandInstance.log(line.join(' '));
}
}
function redrawEnabled() {
// // Always disabled for now - doesn't play well with command.cancel()
// // function (it makes the whole app quit instead of just the
// // current command).
return false;
return redrawEnabled_;
}
function setRedrawEnabled(v) {
redrawEnabled_ = v;
}
function setStackTraceEnabled(v) {
errorStackTraceEnabled_ = v;
}
function redraw(s) {
if (!redrawEnabled()) {
const now = time.unixMs();
if (now - redrawLastUpdateTime_ > 4000) {
if (vorpal_.activeCommand) {
vorpal_.activeCommand.log(s);
} else {
vorpal_.log(s);
}
redrawLastUpdateTime_ = now;
redrawLastLog_ = null;
} else {
redrawLastLog_ = s;
}
} else {
vorpal_.ui.redraw(s);
}
redrawStarted_ = true;
}
function redrawDone() {
if (!redrawStarted_) return;
if (!redrawEnabled()) {
if (redrawLastLog_) {
if (vorpal_.activeCommand) {
vorpal_.activeCommand.log(redrawLastLog_);
} else {
vorpal_.log(redrawLastLog_);
}
}
} else {
vorpal_.ui.redraw.done();
}
redrawLastLog_ = null;
redrawStarted_ = false;
}
function log(commandInstance, o) {
if (errorStackTraceEnabled_) {
commandInstance.log(o);
} else {
if (o instanceof Error) {
commandInstance.log(o.message);
} else {
commandInstance.log(o);
}
}
}
function cmdPromptConfirm(commandInstance, message) {
return new Promise((resolve, reject) => {
let options = {
type: 'confirm',
name: 'ok',
default: false, // This needs to be false so that, when pressing Ctrl+C, the prompt returns false
message: message,
};
commandInstance.prompt(options, (result) => {
if (result.ok) {
resolve(true);
} else {
resolve(false);
}
});
});
}
vorpalUtils.initialize = initialize;
vorpalUtils.printArray = printArray;
vorpalUtils.redraw = redraw;
vorpalUtils.redrawDone = redrawDone;
vorpalUtils.setRedrawEnabled = setRedrawEnabled;
vorpalUtils.setStackTraceEnabled = setStackTraceEnabled;
vorpalUtils.log = log;
vorpalUtils.cmdPromptConfirm = cmdPromptConfirm;
export { vorpalUtils };

View File

@ -15,6 +15,7 @@
"app-module-path": "^2.2.0",
"babel-plugin-transform-runtime": "^6.23.0",
"cache-require-paths": "^0.3.0",
"flat-cache": "^1.2.2",
"follow-redirects": "^1.2.4",
"form-data": "^2.1.4",
"fs-extra": "^3.0.1",
@ -25,8 +26,8 @@
"mkdirp": "^0.5.1",
"moment": "^2.18.1",
"moment-timezone": "^0.5.13",
"node-cache": "^4.1.1",
"node-fetch": "^1.7.1",
"omelette": "^0.4.4",
"os-tmpdir": "^1.0.2",
"promise": "^7.1.1",
"proper-lockfile": "^2.0.1",
@ -40,7 +41,6 @@
"string-to-stream": "^1.1.0",
"tcp-port-used": "^0.1.2",
"uuid": "^3.0.1",
"vorpal": "^1.12.0",
"yargs-parser": "^7.0.0"
},
"devDependencies": {

View File

@ -2141,21 +2141,6 @@ verror@1.3.6:
dependencies:
extsprintf "1.0.2"
vorpal@^1.12.0:
version "1.12.0"
resolved "https://registry.yarnpkg.com/vorpal/-/vorpal-1.12.0.tgz#4be7b2a4e48f8fcfc9cf3648c419d311c522159d"
dependencies:
babel-polyfill "^6.3.14"
chalk "^1.1.0"
in-publish "^2.0.0"
inquirer "0.11.0"
lodash "^4.5.1"
log-update "^1.0.2"
minimist "^1.2.0"
node-localstorage "^0.6.0"
strip-ansi "^3.0.0"
wrap-ansi "^2.0.0"
wide-align@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710"

View File

@ -109,7 +109,7 @@ class Logger {
let serializedObject = this.objectsToString(...object);
Logger.fsDriver().appendFileSync(target.path, line + serializedObject + "\n");
} else if (target.type == 'vorpal') {
target.vorpal.log(...object);
//target.vorpal.log(...object);
} else if (target.type == 'database') {
let msg = this.objectsToString(...object);