objective
Version:
UNDER CONSTRUCTION
809 lines (748 loc) • 20.6 kB
JavaScript
// TODO: dodgy when command longer than line
// Generated by CoffeeScript 1.9.2
var TODO, appendToCommand, autoComplete, autoCompleteAssemble, autoCompleteStartsWith, backspace, bell, command, commands, createEvent, cursorScroll, debug, emit, endSearch, error, fs, handleKeyStrokes, history, historyCursor, historyFile, historyScroll, historySearch, info, keypress, lastPart, latestCommand, normalize, offset, postPrompt, prePrompt, promptWidth, prompting, ref, ref1, ref2, runCommand, running, searchLine, searching, sep, setPrompt, showHelp, updateSearch, writeAutoCompletePosibilities, writePrompt, started, inStream, outStream, isLocal, callOnStop, name;
ref = objective.logger, info = ref.info, error = ref.error, TODO = ref.TODO;
debug = objective.logger.createDebug('prompt');
ref1 = require('./pipeline'), createEvent = ref1.createEvent, emit = ref1.emit;
createEvent('prompt.commands.register.ask');
keypress = require('keypress');
fs = require('fs');
ref2 = require('path'), normalize = ref2.normalize, sep = ref2.sep;
command = '';
prePrompt = '> ';
postPrompt = '';
promptWidth = prePrompt.length;
offset = 0;
searching = false;
running = false;
prompting = false;
handleKeyStrokes = null;
started = false;
isLocal = true;
bell = function() {
return outStream.write('\u0007');
};
commands = {
'help': {
run: function(args, callback) {
return showHelp(args, callback);
},
help: "\n\"hope this helps\"\n",
autoComplete: function(args, callback) {
var cmd;
return callback(null, (function() {
var results;
results = [];
for (cmd in commands) {
results.push(cmd);
}
return results;
})());
}
}
};
showHelp = function(args, callback) {
var cmd;
if (args.length > 0) {
cmd = args[0];
if (commands[cmd] == null) {
outStream.write("\n" + cmd + " does not exist\n\n");
return callback();
}
outStream.write(commands[cmd].help || ("\nno help for " + cmd + "\n\n"));
return callback();
}
outStream.write('\n');
outStream.write("help [command] Per command help.\n");
outStream.write('\n');
for (cmd in commands) {
outStream.write(cmd);
outStream.cursorTo(25);
outStream.write(commands[cmd].description || 'No description.');
outStream.write('\n');
}
return callback();
};
setPrompt = function(newPrompt) {
prePrompt = newPrompt;
return promptWidth = prePrompt.length;
};
writePrompt = function(newline) {
try {
if (newline) {
outStream.write("\n" + prePrompt + command + postPrompt);
} else {
outStream.write(prePrompt + command + postPrompt);
}
return outStream.cursorTo(promptWidth + command.length);
} catch(e) {}
};
appendToCommand = function(char) {
var ch, chars, j, len;
chars = [];
for (j = 0, len = command.length; j < len; j++) {
ch = command[j];
chars.push(ch);
}
chars.splice(command.length - offset, 0, char);
command = chars.join('');
outStream.clearLine();
outStream.cursorTo(0);
writePrompt();
outStream.cursorTo(command.length - offset + promptWidth);
if (searching) {
return updateSearch();
}
};
runCommand = function() {
if (!isLocal) outStream.write('\n');
var args, callback, cmd, err;
command = command.trim();
if (command.length === 0) {
return writePrompt(true);
}
running = true;
args = command.split(' ');
cmd = args[0];
args = args.slice(1);
if (commands[cmd] != null) {
if (history[0] !== command) {
history.unshift(command);
}
while (history.length > 2000) {
history.pop();
}
// console.log();
command = '';
try {
callback = function(err, res) {
running = false;
if (err != null) {
outStream.write('\n');
outStream.write("" + (err.toString()) + '\n');
} else {
// outStream.write('\n');
if (res != null) {
outStream.write(JSON.stringify(res,null,2) + '\n');
}
}
writePrompt(true);
return handleKeyStrokes = null;
};
// callback.write = function(text) {
// outStream.clearLine();
// outStream.cursorTo(0);
// return outStream.write(text.toString());
// };
// callback.writeLine = function(text) {
// return console.log(text);
// };
commands[cmd].run(args, callback);
handleKeyStrokes = commands[cmd].keyStrokes;
return;
} catch (_error) {
err = _error;
running = false;
outStream.write("" + (err.toString()) + '\n');
}
} else {
outStream.write("\n" + cmd + ": command not found\n");
command = '';
running = false;
}
return writePrompt(false);
};
lastPart = void 0;
autoCompleteStartsWith = function(args, array) {
var accum, col, j, k, l, len, len1, letter, newArray, part, ref3, shortest, word;
if (array.length === 0) {
array = null;
}
part = (function() {
try {
return args[args.length - 1];
} catch (_error) {}
})();
part || (part = '');
accum = '';
if (part.length > 0) {
newArray = [];
array.map(function(word) {
if (word.indexOf(part) === 0) {
return newArray.push(word);
}
});
if (newArray.length === 1) {
return [newArray[0], true, newArray];
}
if (newArray.length !== 0) {
array = newArray;
} else {
bell();
return [null, false, []];
}
}
shortest = 1000;
for (j = 0, len = array.length; j < len; j++) {
word = array[j];
if (!(shortest < word.length)) {
shortest = word.length;
}
}
for (col = k = 0, ref3 = shortest; 0 <= ref3 ? k <= ref3 : k >= ref3; col = 0 <= ref3 ? ++k : --k) {
letter = void 0;
for (l = 0, len1 = array.length; l < len1; l++) {
word = array[l];
if (!word[col]) {
continue;
}
letter || (letter = word[col]);
if (letter !== word[col]) {
return [accum, false, array];
}
}
if (letter != null) {
accum += letter;
}
}
return [accum, false, array];
};
writeAutoCompletePosibilities = function(array, type) {
var last, nextPaths, parts, path;
if (type === 'path') {
nextPaths = (function() {
var j, len, results;
results = [];
for (j = 0, len = array.length; j < len; j++) {
path = array[j];
parts = path.split(sep);
last = parts.length - 1;
if (parts[last] === '') {
results.push(path = parts[last - 1] + sep);
} else {
results.push(path = parts[last]);
}
}
return results;
})();
outStream.write("\n\n" + (nextPaths.join('\n')) + '\n\n');
return;
}
return outStream.write("\n\n" + (array.join('\n')) + '\n\n');
};
autoCompleteAssemble = function(possibles, args, completion, arg) {
var fullMatch, matches, part, type;
part = arg[0], fullMatch = arg[1], matches = arg[2];
if (part == null) {
return;
}
type = void 0;
try {
type = completion.type;
} catch (_error) {}
if (fullMatch) {
command = command.substr(0, command.length - args[args.length - 1].length);
command += part + ' ';
outStream.clearLine();
outStream.cursorTo(0);
writePrompt();
lastPart = void 0;
return;
}
if (part.length === 0) {
writeAutoCompletePosibilities(possibles, type);
writePrompt(true);
lastPart = void 0;
return;
}
if (part === lastPart) {
writeAutoCompletePosibilities(matches, type);
console.log();
}
lastPart = part;
command = command.substr(0, command.length - args[args.length - 1].length);
command += part;
outStream.clearLine();
outStream.cursorTo(0);
return writePrompt();
};
autoComplete = function() {
var args, base, cmd, err, j, len, line, possibilities, ref3;
offset = 0;
if (command.trim().length === 0) {
showHelp([], function() {});
writePrompt(true);
return;
}
args = command.split(' ');
cmd = args[0];
args = args.slice(1);
if ((commands[cmd] != null) && args.length > 0) {
(base = commands[cmd]).autoComplete || (base.autoComplete = function() {
bell();
return null;
});
try {
commands[cmd].autoComplete(args, function(err, possibles) {
var contents, e, f, file, j, len, parts, path, possibilities, stat, type;
if (err != null) {
outStream.write('\n');
outStream.write("Error in autoComplete " + (err.toString()) + '\n');
command = '';
writePrompt(true);
return;
}
if (!possibles) {
bell();
return;
}
if (possibles.constructor.name === 'Array') {
return autoCompleteAssemble(possibles, args, null, autoCompleteStartsWith(args, possibles));
}
if (possibles.type !== 'path') {
return;
}
path = args[args.length - 1];
parts = path.split(sep);
file = parts.pop();
path = parts.join(sep) + sep;
if (path === sep) {
if (args[args.length - 1][0] !== '/') {
if (args[args.length - 1] !== sep) {
path = '';
}
}
}
try {
stat = fs.lstatSync(path);
if (stat.isDirectory()) {
path = normalize(path + sep);
}
} catch (_error) {
path = '.' + sep;
}
possibilities = [];
contents = fs.readdirSync(path);
for (j = 0, len = contents.length; j < len; j++) {
f = contents[j];
f = normalize(path + sep + f);
try {
stat = fs.lstatSync(f);
if (stat.isDirectory()) {
f += sep;
}
if (possibles.onlyDirectories) {
if (stat.isDirectory()) {
possibilities.push(f);
}
continue;
}
possibilities.push(f);
} catch (_error) {
e = _error;
outStream.write('\n');
outStream.write("Error in autoComplete " + (e.toString()) + '\n');
writePrompt(true);
}
}
if (possibilities.length === 0) {
bell();
return;
}
autoCompleteAssemble(possibilities, args, possibles, autoCompleteStartsWith(args, possibilities));
type = possibles.type || '';
if (type === 'path') {
if (args[args.length - 1].length !== 0) {
if (command.match(/\/\s$/)) {
command = command.substr(0, command.length - 1);
outStream.clearLine();
outStream.cursorTo(0);
return writePrompt();
}
}
}
});
} catch (_error) {
err = _error;
outStream.write('\n');
outStream.write("Error in autoComplete " + (err.toString()) + '\n');
outStream.write(err.stack + '\n');
command = '';
writePrompt(true);
}
return;
}
possibilities = (function() {
var results;
results = [];
for (cmd in commands) {
results.push(cmd);
}
return results;
})();
autoCompleteAssemble(possibilities, [command], null, autoCompleteStartsWith([command], possibilities));
if (command.match(/\s$/)) {
cmd = command.substr(0, command.length - 1);
ref3 = commands[cmd].help.split('\n');
for (j = 0, len = ref3.length; j < len; j++) {
line = ref3[j];
if (line.match(/Usage\:/)) {
outStream.clearLine();
outStream.cursorTo(0);
outStream.write(line + '\n');
outStream.write('\n');
writePrompt();
}
}
return commands[cmd].autoComplete([''], function(err, res) {
if (res.type === 'path' && (res.startIn != null)) {
command = cmd + ' ' + res.startIn;
}
outStream.clearLine();
outStream.cursorTo(0);
return writePrompt();
});
}
};
historyFile = process.env.HOME + '/.objective/command_history';
historyCursor = 0;
latestCommand = '';
history = (function() {
try {
return fs.readFileSync(historyFile).toString().trim().split('\n');
} catch (_error) {}
})();
history || (history = []);
process.on('exit', function() {
try {
return fs.writeFileSync(historyFile, history.join('\n'));
} catch (_error) {}
});
historyScroll = function(direction) {
switch (direction) {
case 'up':
if (historyCursor === 0) {
latestCommand = command;
}
if (!(historyCursor >= history.length)) {
historyCursor++;
}
break;
case 'down':
if (historyCursor !== 0) {
historyCursor--;
}
}
command = history[historyCursor - 1] || latestCommand;
outStream.clearLine();
outStream.cursorTo(0);
return writePrompt();
};
historySearch = function() {
if (searching) {
return updateSearch(searchLine);
}
searching = true;
command = '';
setPrompt('(search)\'');
postPrompt = '\':';
outStream.clearLine();
outStream.cursorTo(0);
return writePrompt();
};
searchLine = 0;
updateSearch = function(start) {
var found, i, j, line, position, ref3, ref4;
if (start == null) {
start = 0;
}
searchLine = start;
if (command.length === 0) {
outStream.clearLine();
outStream.cursorTo(0);
postPrompt = '\':';
writePrompt(false);
return;
}
found = false;
for (i = j = ref3 = start, ref4 = history.length; ref3 <= ref4 ? j <= ref4 : j >= ref4; i = ref3 <= ref4 ? ++j : --j) {
line = history[i] || '';
position = line.indexOf(command);
if (position === -1) {
searchLine++;
continue;
}
postPrompt = "': " + line;
searchLine++;
found = true;
break;
}
if (!found) {
command = command.substr(0, command.length - 1);
postPrompt = '\':';
bell();
}
outStream.clearLine();
outStream.cursorTo(0);
return writePrompt(false);
};
endSearch = function() {
command = history[searchLine - 1] || '';
if (postPrompt.length < 3) {
command = '';
}
postPrompt = '';
setPrompt('> ');
outStream.clearLine();
outStream.cursorTo(0);
writePrompt(false);
return searching = false;
};
cursorScroll = function(direction) {
var position;
position = command.length - offset;
switch (direction) {
case 'left':
if (position !== 0) {
offset++;
position--;
}
break;
case 'right':
if (!(position >= command.length)) {
offset--;
position++;
}
}
return outStream.cursorTo(position + promptWidth);
};
backspace = function() {
var char, chars, j, len, position;
if (command.length === 0) {
return;
}
position = command.length - offset - 1;
if (position < 0) {
return;
}
chars = [];
for (j = 0, len = command.length; j < len; j++) {
char = command[j];
chars.push(char);
}
chars.splice(position, 1);
command = chars.join('');
outStream.clearLine();
outStream.cursorTo(0);
writePrompt();
outStream.cursorTo(position + promptWidth);
if (searching) {
return updateSearch();
}
};
var keyListener;
module.exports.clearListener = function() {
try {
inStream.removeListener('keypress', keyListener);
} catch(e) {}
}
module.exports.setStreams = function(inn, out, local, showHelp) {
if (typeof showHelp === 'undefined') showHelp = true;
process.stdout.write('\n');
module.exports.clearListener();
isLocal = local;
inStream = inn;
outStream = out;
keypress(inStream);
try {inStream.setRawMode(true);} catch(e) {}
inStream.on('keypress', keyListener = function(ch, key) {
if (running) {
if (handleKeyStrokes != null) {
handleKeyStrokes(ch, key);
}
return;
}
try {
if (key.name === 'return') {
historyCursor = 0;
if (searching) {
endSearch();
}
if (command.length > 0) {
return runCommand();
}
return writePrompt(true);
}
} catch (_error) {}
if (key == null) {
return appendToCommand(ch);
}
if ((ch != null) && ch.match(/^[a-zA-Z0-9_]*$/)) {
return appendToCommand(ch);
}
try {
if (key.ctrl && (key.name === 'c' || key.name === 'd')) {
historyCursor = 0;
if (searching) {
endSearch();
command = '';
outStream.clearLine();
outStream.cursorTo(0);
writePrompt(false);
return;
}
try {
if (command.length > 0) {
command = '';
return writePrompt(true);
}
} catch (_error) {}
try {
done();
} catch (_error) {}
if (isLocal) {
if (typeof callOnStop == 'function') {
module.exports.clearListener();
callOnStop();
} else {
return process.exit(1);
}
} else {
outStream.write('\nprompt gone\n');
try {
outStream.reset();
} catch (e) {};
if (started)
if (!remoteStarted)
module.exports.setStreams(process.stdin, console._stdout, true, false);
else
module.exports.clearListener()
// writePrompt(true);
}
}
} catch (_error) {}
if ((ch != null) && ch === ' ') {
return appendToCommand(ch);
}
try {
if (key.name === 'tab') {
if (searching) {
return endSearch();
}
return autoComplete();
}
} catch (_error) {}
try {
if (key.name === 'backspace') {
return backspace();
}
} catch (_error) {}
try {
if (key.ctrl && key.name === 'r') {
return historySearch();
}
} catch (_error) {}
try {
if (key.name === 'up' || key.name === 'down') {
if (searching) {
return;
}
return historyScroll(key.name);
}
} catch (_error) {}
try {
if (key.name === 'left' || key.name === 'right') {
return cursorScroll(key.name);
}
} catch (_error) {}
});
if (showHelp) {
outStream.write('\n');
outStream.write('\n');
outStream.write(" help . . provides\n");
outStream.write(" tab . . auto-completes command ((Twice)\n");
outStream.write(" ^c . . . quits or clears line\n");
outStream.write(" ^r . . . reverse searches command history\n");
outStream.write(" (tab chooses, return runs)\n");
outStream.write('\n');
// outStream.write('\n');
}
return writePrompt(true);
}
var remoteStarted = false;
module.exports.remoteStart = function(inn, out) {
if (remoteStarted) return false;
remoteStarted = true;
started = true;
var registrar;
objective.logger.info("Starting prompt");
prompting = true;
registrar = {};
Object.defineProperty(registrar, 'create', {
get: function() {
return function(name, config) {
if (commands[name] != null) {
if (config.altname == null) {
throw new Error("Command name collision on '" + name + "'");
}
name = config.altname;
}
return commands[name] = config;
};
}
});
return emit('prompt.commands.register.ask', registrar, function(err, res) {
if (err != null) {
return done(err);
}
module.exports.setStreams(inn, out, false);
// return true;
});
}
module.exports.start = function(onStop) {
callOnStop = onStop;
if (!onStop) require('../repl/local').stop();
if (objective.loadAsRoot) return false;
if (started) return false;
started = true;
var registrar;
objective.logger.info("Starting prompt");
prompting = true;
registrar = {};
Object.defineProperty(registrar, 'create', {
get: function() {
return function(name, config) {
if (commands[name] != null) {
if (config.altname == null) {
throw new Error("Command name collision on '" + name + "'");
}
name = config.altname;
}
return commands[name] = config;
};
}
});
return emit('prompt.commands.register.ask', registrar, function(err, res) {
if (err != null) {
// return done(err);
}
module.exports.setStreams(process.stdin, console._stdout, true);
// return true;
});
};
module.exports.writePrompt = writePrompt;
Object.defineProperty(module.exports, 'started', {
get: function() {
return started;
}
});
module.exports.startbg = function() {
if (prompting) {
return outStream.write('\n');
}
};
module.exports.endbg = function() {
if (prompting) {
return writePrompt(true);
}
};