mirror of
https://github.com/slap-editor/slap.git
synced 2024-09-11 12:58:30 +03:00
Merge branch 'master' into feature-cli-at-line
Conflicts: lib/ui/Editor.js
This commit is contained in:
commit
a2f8a37252
@ -1,5 +1,8 @@
|
||||
; This is your customizable slap configuration. Defaults are located here:
|
||||
; https://github.com/slap-editor/slap/blob/master/slap.ini
|
||||
; https://github.com/slap-editor/editor-widget/blob/master/editor-widget.ini
|
||||
; https://github.com/slap-editor/base-widget/blob/master/base-widget.ini
|
||||
; https://github.com/slap-editor/slap-clibboard-plugin/blob/master/slap-clibboard-plugin.ini
|
||||
|
||||
[logger]
|
||||
level = "info"
|
||||
|
49
lib/cli.js
49
lib/cli.js
@ -1,13 +1,15 @@
|
||||
var _ = require('lazy.js');
|
||||
var _ = require('lodash');
|
||||
var path = require('path');
|
||||
var rc = require('rc');
|
||||
|
||||
var util = require('./util');
|
||||
var util = require('slap-util');
|
||||
var package = require('../package');
|
||||
var baseDir = path.join(__dirname, '..');
|
||||
var configFile = path.join(baseDir, package.name + '.ini');
|
||||
var opts = util.parseOpts(rc(package.name, configFile));
|
||||
opts = _(opts).merge(opts.slap).toObject();
|
||||
opts = _.merge(opts, opts.slap);
|
||||
|
||||
var info = console._error || console.error;
|
||||
|
||||
// special invocation modes
|
||||
if (opts.h || opts.help) {
|
||||
@ -23,7 +25,7 @@ if (opts.h || opts.help) {
|
||||
if (path.dirname(command) === '.') command = '.' + path.sep + command;
|
||||
}
|
||||
|
||||
console.error([
|
||||
info([
|
||||
"Usage: " + command + " [options...] [<file1> [<file2> [...]]]",
|
||||
"",
|
||||
package.description,
|
||||
@ -36,15 +38,11 @@ if (opts.h || opts.help) {
|
||||
}
|
||||
|
||||
if (opts.v || opts.version) {
|
||||
var SORT_ORDER = ['slap', 'node', 'v8'];
|
||||
var versions = process.versions;
|
||||
var SORT_ORDER = ['slap', 'node', 'v8'].reverse();
|
||||
var versions = _.clone(process.versions);
|
||||
versions[package.name] = package.version;
|
||||
console.error(Object.keys(versions)
|
||||
.sort(function (a, b) {
|
||||
var sa = SORT_ORDER.indexOf(a); if (sa < 0) return 1;
|
||||
var sb = SORT_ORDER.indexOf(b); if (sb < 0) return -1;
|
||||
return sa - sb;
|
||||
})
|
||||
info(Object.keys(versions)
|
||||
.sort(function (a, b) { return SORT_ORDER.indexOf(b) - SORT_ORDER.indexOf(a); })
|
||||
.map(function (name) { return name+"@"+versions[name]; })
|
||||
.join(", "));
|
||||
return process.exit(0);
|
||||
@ -62,25 +60,24 @@ if (opts.perf.profile && process.execArgv.indexOf('--prof') === -1) {
|
||||
|
||||
var Promise = require('bluebird');
|
||||
var fs = Promise.promisifyAll(require('fs'));
|
||||
var iconv = require('iconv-lite');
|
||||
|
||||
var logger = require('./logger');
|
||||
var highlightClient = require('./highlight/client');
|
||||
var blessed = require('base-widget').blessed;
|
||||
var Slap = require('./ui/Slap');
|
||||
var Pane = require('./ui/Pane');
|
||||
|
||||
module.exports = function (options) {
|
||||
opts = _(opts).merge(options || {}).toObject();
|
||||
return util.getUserDir().catch(Promise.resolve()).then(function (userDir) {
|
||||
opts.logger = _(userDir ? {dir: userDir} : {}).merge(opts.logger).toObject();
|
||||
opts = _.merge(opts, options);
|
||||
return Slap.getUserDir().catch(Promise.resolve()).then(function (userDir) {
|
||||
if (userDir) opts = _.merge({logger: {file: path.resolve(userDir, package.name+'.log')}}, opts);
|
||||
opts = _.merge({
|
||||
editor: {logger: opts.logger},
|
||||
screenOpts: {logger: opts.logger}
|
||||
}, opts);
|
||||
util.logger(opts.logger);
|
||||
|
||||
logger(opts.logger);
|
||||
highlightClient.call('send', {type: 'logger', options: opts.logger}).done();
|
||||
|
||||
iconv.extendNodeEncodings();
|
||||
|
||||
logger.info("loading...");
|
||||
logger.verbose("configuration:", opts);
|
||||
util.logger.info("loading...");
|
||||
util.logger.verbose("configuration:", opts);
|
||||
if (!opts.screen) opts.screen = new blessed.Screen(opts.screenOpts);
|
||||
var slap = new Slap(opts);
|
||||
|
||||
Promise.all(opts._.map(function (path, i) {
|
||||
@ -88,7 +85,7 @@ module.exports = function (options) {
|
||||
})).done();
|
||||
|
||||
if (!opts._.length) { // if no files are passed
|
||||
new Pane().setCurrent(); // open a new empty file
|
||||
new Pane({parent: slap}).setCurrent(); // open a new empty file
|
||||
if (!opts.config) { // first run without a file passed
|
||||
slap.open(path.join(baseDir, 'README.md'), true)
|
||||
.tap(function (pane) { pane.editor.readOnly(true); })
|
||||
|
@ -1,30 +0,0 @@
|
||||
var fork = require('child_process').fork;
|
||||
var path = require('path');
|
||||
var minimist = require('rc/node_modules/minimist');
|
||||
var Promise = require('bluebird');
|
||||
|
||||
var init = Promise.resolve();
|
||||
var opts = minimist(process.execArgv);
|
||||
var forkOpts = {silent: false};
|
||||
if (['debug', 'debug-brk'].some(function (opt) { return opt in opts; })) {
|
||||
init = init
|
||||
.then(require('get-random-port'))
|
||||
.then(function (port) { forkOpts.execArgv = ['--debug=' + port]; });
|
||||
}
|
||||
|
||||
var client;
|
||||
function initClient () {
|
||||
var oldMessageListeners = client ? client.listeners('message') : [];
|
||||
client = fork(path.join(__dirname, 'server.js'), forkOpts);
|
||||
client.setMaxListeners(100);
|
||||
client.on('exit', initClient);
|
||||
oldMessageListeners.forEach(client.on.bind(client, 'message'));
|
||||
return client;
|
||||
}
|
||||
|
||||
init = init.then(initClient);
|
||||
|
||||
module.exports = init;
|
||||
|
||||
var buckets = 0;
|
||||
module.exports.getBucket = function () { return buckets++; };
|
@ -1,63 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var hljs = require('highlight.js'); hljs.configure({classPrefix: ''});
|
||||
var cheerio = require('cheerio');
|
||||
|
||||
var logger = require('../logger');
|
||||
var textUtil = require('../textUtil');
|
||||
|
||||
function highlight (text, language) {
|
||||
if (language === false) return [];
|
||||
|
||||
var highlighted;
|
||||
if (language) {
|
||||
try { highlighted = hljs.highlight(language, text, true); } catch (e) {}
|
||||
}
|
||||
if (!highlighted) highlighted = hljs.highlightAuto(text);
|
||||
|
||||
var $ = cheerio.load(highlighted.value);
|
||||
var ranges = [];
|
||||
do {
|
||||
var lastElCount = elCount;
|
||||
var elCount = $('*:not(:has(*))').replaceWith(function () {
|
||||
var $el = $(this);
|
||||
var text = '';
|
||||
[this].concat($el.parents().get(), [$.root()]).reverse().reduce(function (parent, el) {
|
||||
$(parent).contents().each(function () {
|
||||
var $sibling = $(this);
|
||||
if ($sibling.is(el)) return false;
|
||||
text += $sibling.text();
|
||||
});
|
||||
return el;
|
||||
});
|
||||
var lines = textUtil.splitLines(text);
|
||||
var linesPlusEl = textUtil.splitLines(text + $el.text());
|
||||
ranges.push({
|
||||
range: [
|
||||
[lines .length - 1, lines[lines.length - 1] .length],
|
||||
[linesPlusEl.length - 1, linesPlusEl[linesPlusEl.length - 1].length]
|
||||
],
|
||||
properties: {
|
||||
type: 'syntax',
|
||||
syntax: ($el.attr('class') || '').match(/\S+/g) || []
|
||||
}
|
||||
});
|
||||
return $el.text();
|
||||
}).length;
|
||||
} while (lastElCount !== elCount);
|
||||
|
||||
return ranges;
|
||||
};
|
||||
|
||||
process.on('message', function (message) {
|
||||
switch (message.type) {
|
||||
case 'highlight':
|
||||
process.send({
|
||||
ranges: highlight(message.text, message.language),
|
||||
revision: message.revision,
|
||||
bucket: message.bucket
|
||||
});
|
||||
break;
|
||||
case 'logger': logger(message.options); break;
|
||||
}
|
||||
});
|
@ -1,39 +0,0 @@
|
||||
var path = require('path');
|
||||
var winston = require('winston');
|
||||
var fs = require('fs');
|
||||
|
||||
var packageName = require('../package').name;
|
||||
|
||||
function logger (opts) {
|
||||
var logFile = path.join(opts.dir || '.', opts.filename || packageName + '.log');
|
||||
logger.stream = fs.createWriteStream(logFile, {flags: 'a'});
|
||||
var winstonLogger = new winston.Logger({
|
||||
exitOnError: false,
|
||||
transports: [
|
||||
new winston.transports.File({
|
||||
stream: logger.stream,
|
||||
level: opts.level || 'info',
|
||||
handleExceptions: true,
|
||||
json: false,
|
||||
prettyPrint: true,
|
||||
colorize: true
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
var levels = winston.config.npm.levels;
|
||||
if (levels[opts.level] > levels.debug) {
|
||||
console._error = console.error;
|
||||
console.error = function () {
|
||||
return logger.stream.write([].join.call(arguments, ' ') + '\n');
|
||||
};
|
||||
} else {
|
||||
require('longjohn');
|
||||
require('bluebird').longStackTraces();
|
||||
}
|
||||
|
||||
winstonLogger.extend(logger);
|
||||
}
|
||||
|
||||
module.exports = logger;
|
||||
global.logger = logger;
|
141
lib/markup.js
141
lib/markup.js
@ -1,141 +0,0 @@
|
||||
// Deals with blessed-style {bold}tags{/bold}
|
||||
|
||||
var logger = require('./logger');
|
||||
var blessed = require('blessed');
|
||||
|
||||
function Markup (style) {
|
||||
var self = this;
|
||||
self.style = style || '';
|
||||
self.contents = [];
|
||||
[].slice.call(arguments, 1).forEach(function (arg) { self.push(arg); });
|
||||
}
|
||||
|
||||
Markup.TAG_RE = /\{(\/?)([\w\-,;!#]*)\}/;
|
||||
Markup.TAG_RE_G = new RegExp(Markup.TAG_RE.source, 'g');
|
||||
Markup.parse = function (text) {
|
||||
if (text instanceof Markup) return text;
|
||||
var markup = new Markup();
|
||||
var hierarchy = [markup], match;
|
||||
while (match = text.match(Markup.TAG_RE)) {
|
||||
var tag = match[0];
|
||||
var parent = hierarchy[hierarchy.length - 1];
|
||||
if (match.index) parent.push(text.slice(0, match.index));
|
||||
if (!match[1]) { // open tag
|
||||
var replace = {open: '{', close: '}'}[match[2]];
|
||||
if (replace) {
|
||||
parent.push(replace);
|
||||
} else {
|
||||
var newMarkup = new Markup(match[0]);
|
||||
parent.push(newMarkup);
|
||||
hierarchy.push(newMarkup);
|
||||
}
|
||||
} else { // close tag
|
||||
var closed;
|
||||
if (match[0] === '{/}') closed = hierarchy.splice(1, Infinity);
|
||||
else if (parent.style === '{'+match[2]+'}') closed = [hierarchy.pop()];
|
||||
else throw new Error("invalid close tag");
|
||||
var lastItem = hierarchy[hierarchy.length - 1];
|
||||
closed.some(function (item) {
|
||||
if (!item.contents.length) {
|
||||
lastItem.contents.pop();
|
||||
return true;
|
||||
}
|
||||
lastItem = item;
|
||||
});
|
||||
}
|
||||
text = text.slice(match.index + tag.length);
|
||||
}
|
||||
if (hierarchy.length !== 1) throw new Error("mismatched tag");
|
||||
if (text) markup.push(text);
|
||||
return markup.clean();
|
||||
};
|
||||
Markup.closeTags = function (markedUp) {
|
||||
return (markedUp
|
||||
.replace(Markup.TAG_RE_G, '{/$2}', 'g') // 'g' flag ignored :(
|
||||
.match(Markup.TAG_RE_G) || [])
|
||||
.reverse()
|
||||
.join('');
|
||||
};
|
||||
Markup.getTaglessLength = function (val) {
|
||||
if (val instanceof Markup) return val.contents.reduce(function (total, item) {
|
||||
return total + Markup.getTaglessLength(item);
|
||||
}, 0);
|
||||
return val.length;
|
||||
};
|
||||
|
||||
Markup.prototype.clean = function () {
|
||||
if (!this.style && this.contents.length === 1) {
|
||||
var child = this.contents[0];
|
||||
if (child instanceof Markup) return child;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
Markup.prototype.tag = function (style, start, end) {
|
||||
if (typeof start !== 'number') start = 0;
|
||||
if (typeof end !== 'number') end = Infinity;
|
||||
|
||||
if (!style) return this;
|
||||
return this.slice(0, start).push(
|
||||
new Markup(style, this.slice(start, end)),
|
||||
this.slice(end));
|
||||
};
|
||||
Markup.prototype.slice = function (start, end) {
|
||||
if (typeof start !== 'number') start = 0;
|
||||
if (typeof end !== 'number') end = Infinity;
|
||||
|
||||
var i = 0;
|
||||
var markup = new Markup(this.style);
|
||||
this.contents.some(function (item) {
|
||||
var nextI = i + Markup.getTaglessLength(item);
|
||||
if (start < nextI && end >= i) {
|
||||
markup.push(item.slice(Math.max(0, start - i), Math.max(0, end - i)));
|
||||
}
|
||||
if (nextI >= end) return true;
|
||||
i = nextI;
|
||||
});
|
||||
return markup;
|
||||
};
|
||||
Markup.prototype.push = function () {
|
||||
var self = this;
|
||||
var contents = self.contents;
|
||||
|
||||
// unoptimized version of the following:
|
||||
// contents.push.apply(contents, arguments);
|
||||
|
||||
var lastItem = contents[contents.length - 1];
|
||||
[].forEach.call(arguments, function (item) {
|
||||
if (!item) return;
|
||||
if (item instanceof Markup) {
|
||||
if (!item.style || item.style === self.style) {
|
||||
self.push.apply(self, item.contents);
|
||||
lastItem = contents[contents.length - 1];
|
||||
return;
|
||||
}
|
||||
if (lastItem instanceof Markup && item.style === lastItem.style) {
|
||||
return lastItem.push.apply(lastItem, item.contents);
|
||||
}
|
||||
}
|
||||
if (typeof item === 'string' && typeof lastItem === 'string') {
|
||||
return contents[contents.length - 1] += item;
|
||||
}
|
||||
contents.push(item);
|
||||
lastItem = item;
|
||||
});
|
||||
|
||||
return self.clean();
|
||||
};
|
||||
Markup.prototype.toString = function () {
|
||||
return this.style + this.contents.map(function (item) {
|
||||
return typeof item === 'string' ? blessed.escape(item) : item;
|
||||
}).join('') + Markup.closeTags(this.style);
|
||||
};
|
||||
Object.defineProperty(Markup.prototype, 'length', {get: function () {
|
||||
return this.toString().length;
|
||||
}});
|
||||
|
||||
function markup (text, style, start, end) {
|
||||
return Markup.parse(text).tag(style, start, end);
|
||||
}
|
||||
markup.parse = Markup.parse;
|
||||
|
||||
module.exports = markup;
|
@ -1,37 +0,0 @@
|
||||
var _ = require('lazy.js');
|
||||
var Point = require('text-buffer/lib/point');
|
||||
|
||||
var util = require('./util');
|
||||
|
||||
exports._regExpRegExp = /^\/(.+)\/([im]?)$/;
|
||||
exports._lineRegExp = /\r\n|\r|\n/;
|
||||
|
||||
exports.splitLines = function (text) {
|
||||
var lines = [];
|
||||
var match, line;
|
||||
while (match = exports._lineRegExp.exec(text)) {
|
||||
line = text.slice(0, match.index) + match[0];
|
||||
text = text.slice(line.length);
|
||||
lines.push(line);
|
||||
}
|
||||
lines.push(text);
|
||||
return lines;
|
||||
};
|
||||
exports.escapeRegExp = function (text) {
|
||||
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
||||
};
|
||||
exports.regExpIndexOf = function(str, regex, index) {
|
||||
index = index || 0;
|
||||
var offset = str.slice(index).search(regex);
|
||||
return (offset >= 0) ? (index + offset) : offset;
|
||||
};
|
||||
exports.regExpLastIndexOf = function (str, regex, index) {
|
||||
if (index === 0 || index) str = str.slice(0, Math.max(0, index));
|
||||
var i;
|
||||
var offset = -1;
|
||||
while ((i = str.search(regex)) !== -1) {
|
||||
offset += i + 1;
|
||||
str = str.slice(i + 1);
|
||||
}
|
||||
return offset;
|
||||
};
|
@ -1,107 +0,0 @@
|
||||
var Promise = require('bluebird');
|
||||
var blessed = require('blessed');
|
||||
var _ = require('lazy.js');
|
||||
var Point = require('text-buffer/lib/point');
|
||||
|
||||
var Slap = require('./Slap');
|
||||
|
||||
var util = require('../util');
|
||||
|
||||
function BaseElement (opts) {
|
||||
var self = this;
|
||||
|
||||
if (!(self instanceof blessed.Node)) return new BaseElement(opts);
|
||||
|
||||
opts = _(Slap.global.options.element).merge(opts || {}).toObject();
|
||||
if (!('slap' in opts)) opts.slap = Slap.global;
|
||||
if (!('parent' in opts)) opts.parent = opts.slap;
|
||||
if (self instanceof BaseElement) blessed.Box.call(self, opts); // this should not be called if an element inherits from built-in blessed classes
|
||||
if (self.parent instanceof Pane) self.pane = self.parent;
|
||||
self.slap = opts.slap;
|
||||
self.focusable = opts.focusable;
|
||||
|
||||
self.ready = Promise.delay(0)
|
||||
.then(function () { return self._initHandlers(); })
|
||||
.return(self);
|
||||
}
|
||||
BaseElement.prototype.__proto__ = blessed.Box.prototype;
|
||||
|
||||
BaseElement.prototype.walkDepthFirst = function (direction, after, fn) {
|
||||
if (arguments.length === 2) fn = after;
|
||||
var children = this.children.slice();
|
||||
if (direction === -1) children.reverse();
|
||||
if (after) children = children.slice(children.indexOf(after) + 1);
|
||||
return children.some(function (child) {
|
||||
return fn.apply(child, arguments) || BaseElement.prototype.walkDepthFirst.call(child, direction, fn);
|
||||
});
|
||||
};
|
||||
BaseElement.prototype.focusFirst = function (direction, after) {
|
||||
return this.walkDepthFirst(direction, after, function () {
|
||||
if (this.visible && this.focusable) {
|
||||
this.focus();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
};
|
||||
BaseElement.prototype._focusDirection = function (direction) {
|
||||
var self = this;
|
||||
var descendantParent;
|
||||
var descendant = self.screen.focused;
|
||||
while (descendant.hasAncestor(self)) {
|
||||
descendantParent = descendant.parent;
|
||||
if (BaseElement.prototype.focusFirst.call(descendantParent, direction, descendant)) return self;
|
||||
descendant = descendantParent;
|
||||
}
|
||||
if (!self.focusFirst(direction)) throw new Error("no focusable descendant");
|
||||
return self;
|
||||
};
|
||||
BaseElement.prototype.focusNext = function () {
|
||||
return this._focusDirection(1);
|
||||
};
|
||||
BaseElement.prototype.focusPrev = function () {
|
||||
return this._focusDirection(-1);
|
||||
};
|
||||
BaseElement.prototype.focus = function () {
|
||||
if (!this.hasFocus()) return blessed.Box.prototype.focus.apply(this, arguments);
|
||||
return this;
|
||||
};
|
||||
BaseElement.prototype.isAttached = function () {
|
||||
return this.hasAncestor(this.screen);
|
||||
};
|
||||
BaseElement.prototype.hasFocus = function (asChild) {
|
||||
var self = this;
|
||||
var focused = self.screen.focused;
|
||||
return focused.visible && (focused === self || focused.hasAncestor(self) || (asChild && self.hasAncestor(focused)));
|
||||
};
|
||||
|
||||
BaseElement.prototype.pos = function () {
|
||||
return new Point(this.atop + this.itop, this.aleft + this.ileft);
|
||||
};
|
||||
BaseElement.prototype.size = function () {
|
||||
if (!this.isAttached()) return new Point(0, 0); // hack
|
||||
return new Point(this.height - this.iheight, this.width - this.iwidth);
|
||||
};
|
||||
|
||||
BaseElement.prototype.shrinkWidth = function () { return this.content.length + this.iwidth; };
|
||||
|
||||
BaseElement.prototype._initHandlers = function () {
|
||||
var self = this;
|
||||
self.on('focus', function () {
|
||||
logger.debug('focus', util.typeOf(self));
|
||||
if (!self.focusable) self.focusNext();
|
||||
});
|
||||
self.on('blur', function () { logger.debug('blur', util.typeOf(self)); });
|
||||
self.on('show', function () { self.setFront(); });
|
||||
self.on('keypress', _.noop); // 'element keypress' doesn't work correctly without this
|
||||
self.on('element keypress', function (el, ch, key) {
|
||||
switch (util.getBinding(self.options.bindings, key)) {
|
||||
case 'hide': self.hide(); return false;
|
||||
case 'focusNext': self.focusNext(); return false;
|
||||
case 'focusPrev': self.focusPrev(); return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = BaseElement;
|
||||
|
||||
var Pane = require('./Pane'); // circular import
|
@ -1,36 +1,33 @@
|
||||
var blessed = require('blessed');
|
||||
var _ = require('lazy.js');
|
||||
var _ = require('lodash');
|
||||
|
||||
var Slap = require('./Slap');
|
||||
var BaseForm = require('./BaseForm');
|
||||
var Field = require('./Field');
|
||||
var BaseWidget = require('base-widget');
|
||||
var Field = require('editor-widget').Field;
|
||||
|
||||
var util = require('../util');
|
||||
var util = require('slap-util');
|
||||
|
||||
function BaseFindForm (opts) {
|
||||
var self = this;
|
||||
|
||||
if (!(self instanceof blessed.Node)) return new BaseFindForm(opts);
|
||||
if (!(self instanceof BaseFindForm)) return new BaseFindForm(opts);
|
||||
|
||||
BaseForm.call(self, _({
|
||||
prevEditorState: {}
|
||||
})
|
||||
.merge(Slap.global.options.form.baseFind || {})
|
||||
.merge(opts || {})
|
||||
.toObject());
|
||||
BaseForm.call(self, _.merge({
|
||||
prevEditorState: {}
|
||||
}, Slap.global.options.form.baseFind, opts));
|
||||
|
||||
self.findField = new Field(_({
|
||||
self.findField = new Field(_.merge({
|
||||
parent: self,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0
|
||||
}).merge(self.options.findField || {}).toObject());
|
||||
}, Slap.global.options.editor, Slap.global.options.field, self.options.findField));
|
||||
}
|
||||
BaseFindForm.prototype.__proto__ = BaseForm.prototype;
|
||||
|
||||
BaseFindForm.prototype.find = function (text, direction) {
|
||||
var self = this;
|
||||
self.slap.header.message(null);
|
||||
self.screen.slap.header.message(null);
|
||||
if (text) self.emit('find', text, direction);
|
||||
else self.resetEditor();
|
||||
return self;
|
||||
@ -55,7 +52,7 @@ BaseFindForm.prototype._initHandlers = function () {
|
||||
self.find(textBuf.getText());
|
||||
});
|
||||
self.on('hide', function () {
|
||||
if (_(self.pane.forms).pluck('visible').compact().none()) {
|
||||
if (!_.some(self.pane.forms, 'visible')) {
|
||||
prevEditorState.selection = null;
|
||||
prevEditorState.scroll = null;
|
||||
}
|
||||
@ -64,7 +61,7 @@ BaseFindForm.prototype._initHandlers = function () {
|
||||
textBuf.on('changed', function () { self.find(textBuf.getText()); });
|
||||
self.findField.on('keypress', function (ch, key) {
|
||||
var text = textBuf.getText();
|
||||
switch (util.getBinding(self.options.bindings, key)) {
|
||||
switch (self.resolveBinding(key)) {
|
||||
case 'next': self.find(text, 1); return false;
|
||||
case 'prev': self.find(text, -1); return false;
|
||||
};
|
||||
|
@ -1,50 +1,49 @@
|
||||
var blessed = require('blessed');
|
||||
var _ = require('lazy.js');
|
||||
var _ = require('lodash');
|
||||
|
||||
var util = require('slap-util');
|
||||
|
||||
var BaseWidget = require('base-widget');
|
||||
var Slap = require('./Slap');
|
||||
var BaseElement = require('./BaseElement');
|
||||
|
||||
var util = require('../util');
|
||||
|
||||
function BaseForm (opts) {
|
||||
var self = this;
|
||||
|
||||
if (!(self instanceof blessed.Node)) return new BaseForm(opts);
|
||||
if (!(self instanceof BaseForm)) return new BaseForm(opts);
|
||||
|
||||
BaseElement.call(self, _({
|
||||
hidden: true,
|
||||
height: 1,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0
|
||||
})
|
||||
.merge(Slap.global.options.form || {})
|
||||
.merge(opts || {})
|
||||
.toObject());
|
||||
if (self.parent instanceof Pane) self.pane.forms.push(self);
|
||||
BaseWidget.call(self, _.merge({
|
||||
hidden: true,
|
||||
height: 1,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0
|
||||
}, Slap.global.options.form, opts));
|
||||
if (self.parent instanceof Pane) {
|
||||
self.pane = self.parent;
|
||||
self.pane.forms.push(self);
|
||||
}
|
||||
}
|
||||
BaseForm.prototype.__proto__ = BaseElement.prototype;
|
||||
BaseForm.prototype.__proto__ = BaseWidget.prototype;
|
||||
|
||||
BaseForm.prototype.cancel = function () { this.emit('cancel'); };
|
||||
BaseForm.prototype.submit = function () { this.emit('submit'); };
|
||||
BaseForm.prototype._initHandlers = function () {
|
||||
var self = this;
|
||||
self.on('element keypress', function (el, ch, key) {
|
||||
switch (util.getBinding(self.options.bindings, key)) {
|
||||
switch (self.resolveBinding(key)) {
|
||||
case 'cancel': self.cancel(); return false;
|
||||
};
|
||||
});
|
||||
self.on('show', function () { self.focus(); });
|
||||
self.on('hide', function () {
|
||||
self.slap._stopKeyPropagation().done();
|
||||
if (self.screen.focused.hasAncestor(self.pane) && !self.screen.focused.visible) self.pane.focus();
|
||||
self.screen.slap._stopKeyPropagation().done();
|
||||
if (self.screen.focused.hasAncestor(self.pane) && !self.screen.slap.focused.visible) self.pane.focus();
|
||||
});
|
||||
self.on('element blur', function (el) { if (self.visible && !self.hasFocus(true)) self.cancel(); });
|
||||
self.on('element submit', function (el) { if (el !== self) self.submit(); });
|
||||
self.on('element cancel', function (el) { if (el !== self) self.cancel(); });
|
||||
self.on('cancel', function () { self.hide(); });
|
||||
|
||||
return BaseElement.prototype._initHandlers.apply(self, arguments);
|
||||
return BaseWidget.prototype._initHandlers.apply(self, arguments);
|
||||
};
|
||||
|
||||
module.exports = BaseForm;
|
||||
|
@ -1,33 +1,33 @@
|
||||
var blessed = require('blessed');
|
||||
var _ = require('lazy.js');
|
||||
var _ = require('lodash');
|
||||
|
||||
var util = require('slap-util');
|
||||
|
||||
var BaseWidget = require('base-widget');
|
||||
var Slap = require('./Slap');
|
||||
var BaseElement = require('./BaseElement');
|
||||
|
||||
var util = require('../util');
|
||||
|
||||
function Button (opts) {
|
||||
var self = this;
|
||||
|
||||
if (!(self instanceof blessed.Node)) return new Button(opts);
|
||||
if (!(self instanceof Button)) return new Button(opts);
|
||||
|
||||
opts = _(Slap.global.options.button).merge({
|
||||
opts = _.merge({
|
||||
mouse: true,
|
||||
focusable: true,
|
||||
shrink: true,
|
||||
padding: {left: 1, right: 1}
|
||||
}).merge(opts || {}).toObject();
|
||||
}, Slap.global.options.button, opts);
|
||||
opts.style.focus = opts.style.hover;
|
||||
blessed.Button.call(self, opts);
|
||||
BaseElement.call(self, opts);
|
||||
BaseWidget.blessed.Button.call(self, opts);
|
||||
BaseWidget.call(self, opts);
|
||||
}
|
||||
Button.prototype.__proto__ = blessed.Button.prototype;
|
||||
Button.prototype.__proto__ = BaseWidget.blessed.Button.prototype;
|
||||
Button.prototype._initHandlers = function () {
|
||||
var self = this;
|
||||
self.on('keypress', function (ch, key) {
|
||||
if (key.name === 'enter') self.slap._stopKeyPropagation().done(); // FIXME: hack
|
||||
if (key.name === 'enter') self.screen.slap._stopKeyPropagation().done(); // FIXME: hack
|
||||
});
|
||||
return BaseElement.prototype._initHandlers.apply(self, arguments);
|
||||
return BaseWidget.prototype._initHandlers.apply(self, arguments);
|
||||
};
|
||||
|
||||
module.exports = Button;
|
||||
|
@ -1,41 +0,0 @@
|
||||
var blessed = require('blessed');
|
||||
var _ = require('lazy.js');
|
||||
|
||||
var Slap = require('./Slap');
|
||||
var Editor = require('./Editor');
|
||||
var BaseForm = require('./BaseForm');
|
||||
|
||||
var util = require('../util');
|
||||
var textUtil = require('../textUtil');
|
||||
|
||||
function Field (opts) {
|
||||
var self = this;
|
||||
|
||||
if (!(self instanceof blessed.Node)) return new Field(opts);
|
||||
|
||||
Editor.call(self, _({
|
||||
height: 1,
|
||||
multiLine: false
|
||||
})
|
||||
.merge(Slap.global.options.field || {})
|
||||
.merge(opts || {})
|
||||
.toObject());
|
||||
if (self.parent instanceof BaseForm) self.form = self.parent;
|
||||
self.language(false);
|
||||
}
|
||||
Field.prototype.__proto__ = Editor.prototype;
|
||||
|
||||
Field.prototype.submit = function (value) { this.emit('submit', value); }
|
||||
Field.prototype.cancel = function () { this.emit('cancel'); }
|
||||
Field.prototype._initHandlers = function () {
|
||||
var self = this;
|
||||
self.on('keypress', function (ch, key) {
|
||||
switch (util.getBinding(self.options.bindings, key)) {
|
||||
case 'submit': self.submit(self.textBuf.getText()); return false;
|
||||
case 'cancel': self.cancel(); return false;
|
||||
};
|
||||
});
|
||||
return Editor.prototype._initHandlers.apply(self, arguments);
|
||||
}
|
||||
|
||||
module.exports = Field;
|
@ -1,36 +1,36 @@
|
||||
var blessed = require('blessed');
|
||||
var _ = require('lazy.js');
|
||||
var _ = require('lodash');
|
||||
|
||||
var util = require('slap-util');
|
||||
|
||||
var BaseWidget = require('base-widget');
|
||||
var Slap = require('./Slap');
|
||||
var BaseElement = require('./BaseElement');
|
||||
|
||||
var util = require('../util');
|
||||
|
||||
function FileBrowser (opts) {
|
||||
var self = this;
|
||||
|
||||
if (!(self instanceof blessed.Node)) return new FileBrowser(opts);
|
||||
if (!(self instanceof FileBrowser)) return new FileBrowser(opts);
|
||||
|
||||
opts = _(Slap.global.options.fileBrowser).merge({
|
||||
focusable: true
|
||||
}).merge(opts || {}).toObject();
|
||||
BaseElement.call(self, opts);
|
||||
blessed.FileManager.call(self, _({
|
||||
opts = _.merge({
|
||||
keys: true,
|
||||
mouse: true
|
||||
}).merge(opts).toObject());
|
||||
mouse: true,
|
||||
focusable: true
|
||||
}, Slap.global.options.fileBrowser, opts);
|
||||
BaseWidget.blessed.FileManager.call(self, opts);
|
||||
BaseWidget.call(self, opts);
|
||||
|
||||
self.refresh();
|
||||
self.data.selectedStyle = self.style.selected;
|
||||
self.data.itemStyle = self.style.item;
|
||||
}
|
||||
FileBrowser.prototype.__proto__ = blessed.FileManager.prototype;
|
||||
FileBrowser.prototype.__proto__ = BaseWidget.blessed.FileManager.prototype;
|
||||
|
||||
FileBrowser.prototype._initHandlers = function () {
|
||||
var self = this;
|
||||
self.on('element mousedown', function (el) { self.focus(); });
|
||||
self.on('file', function (path) { self.slap.open(path, true).done(); });
|
||||
self.on('file', function (path) { self.screen.slap.open(path, true).done(); });
|
||||
self.on('cancel', function () {
|
||||
var slap = self.slap;
|
||||
var slap = self.screen.slap;
|
||||
var currentPane = slap.panes[slap.data.currentPane];
|
||||
if (currentPane) currentPane.focus();
|
||||
});
|
||||
@ -43,7 +43,7 @@ FileBrowser.prototype._initHandlers = function () {
|
||||
self.style.selected = self.data.itemStyle;
|
||||
});
|
||||
|
||||
return BaseElement.prototype._initHandlers.apply(self, arguments);
|
||||
return BaseWidget.prototype._initHandlers.apply(self, arguments);
|
||||
};
|
||||
|
||||
module.exports = FileBrowser;
|
||||
|
@ -1,39 +1,32 @@
|
||||
var blessed = require('blessed');
|
||||
var _ = require('lazy.js');
|
||||
var _ = require('lodash');
|
||||
var lodash = require('lodash');
|
||||
var Point = require('text-buffer/lib/point');
|
||||
|
||||
var util = require('slap-util');
|
||||
|
||||
var BaseWidget = require('base-widget');
|
||||
var Slap = require('./Slap');
|
||||
var BaseElement = require('./BaseElement');
|
||||
var BaseFindForm = require('./BaseFindForm');
|
||||
|
||||
var textUtil = require('../textUtil');
|
||||
|
||||
FindForm._label = " find (/.*/ for regex): ";
|
||||
function FindForm (opts) {
|
||||
var self = this;
|
||||
|
||||
if (!(self instanceof blessed.Node)) return new FindForm(opts);
|
||||
if (!(self instanceof FindForm)) return new FindForm(opts);
|
||||
|
||||
BaseFindForm.call(self, _({
|
||||
findField: {left: FindForm._label.length}
|
||||
})
|
||||
.merge(Slap.global.options.form.find || {})
|
||||
.merge(opts || {})
|
||||
.toObject());
|
||||
BaseFindForm.call(self, _.merge({
|
||||
findField: {left: FindForm._label.length}
|
||||
}, Slap.global.options.form.find, opts));
|
||||
|
||||
self.findLabel = new BaseElement(_({
|
||||
parent: self,
|
||||
tags: true,
|
||||
content: FindForm._label,
|
||||
top: 0,
|
||||
height: 1,
|
||||
left: 0,
|
||||
width: FindForm._label.length,
|
||||
style: self.options.style
|
||||
})
|
||||
.merge(self.options.findLabel || {})
|
||||
.toObject());
|
||||
self.findLabel = new BaseWidget(_.merge({
|
||||
parent: self,
|
||||
tags: true,
|
||||
content: FindForm._label,
|
||||
top: 0,
|
||||
height: 1,
|
||||
left: 0,
|
||||
width: FindForm._label.length,
|
||||
style: self.options.style
|
||||
}, self.options.findLabel));
|
||||
}
|
||||
FindForm.prototype.__proto__ = BaseFindForm.prototype;
|
||||
|
||||
@ -48,7 +41,7 @@ FindForm.prototype.selectRange = function (range) {
|
||||
};
|
||||
FindForm.prototype._initHandlers = function () {
|
||||
var self = this;
|
||||
var header = self.slap.header;
|
||||
var header = self.screen.slap.header;
|
||||
var editor = self.pane.editor;
|
||||
var selection = editor.selection;
|
||||
|
||||
@ -59,10 +52,10 @@ FindForm.prototype._initHandlers = function () {
|
||||
self.on('find', lodash.throttle(function (pattern, direction) {
|
||||
direction = direction || 0;
|
||||
editor.destroyMarkers({type: 'findMatch'});
|
||||
var regExpMatch = pattern.match(textUtil._regExpRegExp);
|
||||
var regExpMatch = pattern.match(util.text._regExpRegExp);
|
||||
pattern = regExpMatch
|
||||
? new RegExp(regExpMatch[1], regExpMatch[2])
|
||||
: new RegExp(textUtil.escapeRegExp(pattern), 'img');
|
||||
: new RegExp(util.text.escapeRegExp(pattern), 'img');
|
||||
|
||||
var selectionRange = selection.getRange();
|
||||
var matches = [];
|
||||
|
@ -1,36 +1,30 @@
|
||||
var blessed = require('blessed');
|
||||
var _ = require('lazy.js');
|
||||
var Point = require('text-buffer/lib/point');
|
||||
var _ = require('lodash');
|
||||
|
||||
var BaseWidget = require('base-widget');
|
||||
|
||||
var Slap = require('./Slap');
|
||||
var BaseElement = require('./BaseElement');
|
||||
var BaseFindForm = require('./BaseFindForm');
|
||||
|
||||
GoLineForm._label = " line number: ";
|
||||
function GoLineForm (opts) {
|
||||
var self = this;
|
||||
|
||||
if (!(self instanceof blessed.Node)) return new GoLineForm(opts);
|
||||
if (!(self instanceof GoLineForm)) return new GoLineForm(opts);
|
||||
|
||||
BaseFindForm.call(self, _({
|
||||
findField: {left: GoLineForm._label.length}
|
||||
})
|
||||
.merge(Slap.global.options.form.goLine || {})
|
||||
.merge(opts || {})
|
||||
.toObject());
|
||||
BaseFindForm.call(self, _.merge({
|
||||
findField: {left: GoLineForm._label.length}
|
||||
}, Slap.global.options.form.goLine, opts));
|
||||
|
||||
self.goLineLabel = new BaseElement(_({
|
||||
parent: self,
|
||||
tags: true,
|
||||
content: GoLineForm._label,
|
||||
top: 0,
|
||||
height: 1,
|
||||
left: 0,
|
||||
width: GoLineForm._label.length,
|
||||
style: self.options.style
|
||||
})
|
||||
.merge(self.options.goLineLabel || {})
|
||||
.toObject());
|
||||
self.goLineLabel = new BaseWidget(_.merge({
|
||||
parent: self,
|
||||
tags: true,
|
||||
content: GoLineForm._label,
|
||||
top: 0,
|
||||
height: 1,
|
||||
left: 0,
|
||||
width: GoLineForm._label.length,
|
||||
style: self.options.style
|
||||
}, self.options.goLineLabel));
|
||||
}
|
||||
GoLineForm.prototype.__proto__ = BaseFindForm.prototype;
|
||||
|
||||
@ -42,7 +36,7 @@ GoLineForm.prototype._initHandlers = function () {
|
||||
lineNumber = Number(lineNumber) - 1;
|
||||
if (lineNumber !== lineNumber) return; // isNaN(lineNumber)
|
||||
var selection = self.pane.editor.selection;
|
||||
selection.setHeadPosition(new Point(lineNumber, 0));
|
||||
selection.setHeadPosition([lineNumber, 0]);
|
||||
selection.clearTail();
|
||||
if (direction) self.hide();
|
||||
return self;
|
||||
|
107
lib/ui/Header.js
107
lib/ui/Header.js
@ -1,73 +1,58 @@
|
||||
var _ = require('lazy.js');
|
||||
var _ = require('lodash');
|
||||
var blessed = require('blessed');
|
||||
var path = require('path');
|
||||
|
||||
var Slap = require('./Slap');
|
||||
var BaseElement = require('./BaseElement');
|
||||
var Button = require('./Button');
|
||||
var util = require('slap-util');
|
||||
|
||||
var util = require('../util');
|
||||
var markup = require('../markup');
|
||||
var BaseWidget = require('base-widget');
|
||||
var Slap = require('./Slap');
|
||||
var Button = require('./Button');
|
||||
|
||||
function Header (opts) {
|
||||
var self = this;
|
||||
|
||||
if (!(self instanceof blessed.Node)) return new Header(opts);
|
||||
if (!(self instanceof Header)) return new Header(opts);
|
||||
|
||||
var opts = _({
|
||||
BaseWidget.call(self, _.merge(opts.headerPosition !== 'bottom'
|
||||
? {top: 0}
|
||||
: {bottom: 0}, {
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: 1
|
||||
})
|
||||
.merge(Slap.global.options.header || {})
|
||||
.merge(opts || {})
|
||||
.toObject();
|
||||
BaseElement.call(self, _(opts)
|
||||
.merge(opts.headerPosition !== 'bottom'
|
||||
? {top: 0, headerPosition: 'top'}
|
||||
: {bottom: 0, headerPosition: 'bottom'})
|
||||
.toObject());
|
||||
}, Slap.global.options.header, opts));
|
||||
|
||||
self.leftContent = new BaseElement(_({
|
||||
parent: self,
|
||||
tags: true,
|
||||
left: 1,
|
||||
shrink: true,
|
||||
style: self.options.style
|
||||
})
|
||||
.merge(self.options.leftContent || {})
|
||||
.toObject());
|
||||
self.leftContent = new BaseWidget(_.merge({
|
||||
parent: self,
|
||||
tags: true,
|
||||
left: 1,
|
||||
shrink: true,
|
||||
style: self.options.style
|
||||
}, self.options.leftContent));
|
||||
|
||||
var helpBinding = self.slap.options.bindings.help;
|
||||
var helpBinding = Slap.global.options.bindings.help;
|
||||
helpBinding = Array.isArray(helpBinding) ? helpBinding[0] : helpBinding;
|
||||
self.helpButton = new Button(_({
|
||||
parent: self,
|
||||
content: "Help" + (helpBinding ? ": " + helpBinding : "")
|
||||
})
|
||||
.merge(self.options.helpButton || {})
|
||||
.toObject());
|
||||
self.helpButton = new Button(_.merge({
|
||||
parent: self,
|
||||
content: "Help" + (helpBinding ? ": " + helpBinding : "")
|
||||
}, self.options.helpButton));
|
||||
|
||||
self.rightContent = new BaseElement(_({
|
||||
parent: self,
|
||||
tags: true,
|
||||
shrink: true,
|
||||
style: self.options.style
|
||||
})
|
||||
.merge(self.options.rightContent || {})
|
||||
.toObject());
|
||||
self.rightContent = new BaseWidget(_.merge({
|
||||
parent: self,
|
||||
tags: true,
|
||||
shrink: true,
|
||||
style: self.options.style
|
||||
}, self.options.rightContent));
|
||||
|
||||
self.messageContent = new BaseElement(_({
|
||||
parent: self,
|
||||
tags: true,
|
||||
shrink: true,
|
||||
style: self.options.style
|
||||
})
|
||||
.merge(self.options.messageContent || {})
|
||||
.toObject());
|
||||
self.messageContent = new BaseWidget(_.merge({
|
||||
parent: self,
|
||||
tags: true,
|
||||
shrink: true,
|
||||
style: self.options.style
|
||||
}, self.options.messageContent));
|
||||
|
||||
// self._blink(true);
|
||||
}
|
||||
Header.prototype.__proto__ = BaseElement.prototype;
|
||||
Header.prototype.__proto__ = BaseWidget.prototype;
|
||||
|
||||
Header.prototype._blink = util.getterSetter('blink', null, function (blink) {
|
||||
var self = this;
|
||||
@ -90,7 +75,7 @@ Header.prototype.message = util.getterSetter('message', null, function (message,
|
||||
}
|
||||
|
||||
// self._blink(false);
|
||||
return message !== null ? markup(' '+message+' ', self.options.style[styleName || 'info']) : null;
|
||||
return message !== null ? util.markup(' '+message+' ', self.options.style[styleName || 'info']) : null;
|
||||
});
|
||||
|
||||
Header.prototype._initHandlers = function () {
|
||||
@ -98,8 +83,8 @@ Header.prototype._initHandlers = function () {
|
||||
['message', 'blink'].forEach(function (evt) {
|
||||
self.on(evt, function () { self.parent.render(); });
|
||||
});
|
||||
self.helpButton.on('press', function () { self.slap.help(); });
|
||||
return BaseElement.prototype._initHandlers.apply(self, arguments);
|
||||
self.helpButton.on('press', function () { self.screen.slap.help(); });
|
||||
return BaseWidget.prototype._initHandlers.apply(self, arguments);
|
||||
};
|
||||
|
||||
Header.prototype.render = function () {
|
||||
@ -110,15 +95,15 @@ Header.prototype.render = function () {
|
||||
var right = [];
|
||||
|
||||
var style = self.options.style;
|
||||
var slap = self.slap;
|
||||
var slap = self.screen.slap;
|
||||
var editor = (slap.panes[slap.data.currentPane] || {}).editor;
|
||||
if (editor) {
|
||||
var originalPath = editor.textBuf.getPath();
|
||||
var markupPath = originalPath
|
||||
? blessed.escape(path.relative(self.slap.fileBrowser.cwd, originalPath))
|
||||
? blessed.escape(path.relative(slap.fileBrowser.cwd, originalPath))
|
||||
: "new file";
|
||||
if (editor.textBuf.isModified()) {
|
||||
markupPath = markup(markupPath+"*", style.changed);
|
||||
markupPath = util.markup(markupPath+"*", style.changed);
|
||||
}
|
||||
left.push(markupPath);
|
||||
|
||||
@ -129,24 +114,24 @@ Header.prototype.render = function () {
|
||||
"("+editor.textBuf.getLineCount()+")"
|
||||
];
|
||||
if (originalEncoding) right.push(blessed.escape(originalEncoding));
|
||||
if (editor.readOnly()) right.push(markup("read-only", style.warning));
|
||||
if (!slap.insertMode()) right.unshift(markup("OVR", style.overwrite));
|
||||
if (editor.readOnly()) right.push(util.markup("read-only", style.warning));
|
||||
if (!editor.insertMode()) right.unshift(util.markup("OVR", style.overwrite));
|
||||
}
|
||||
|
||||
self.leftContent.setContent(left.join(" "));
|
||||
self.rightContent.setContent(right.join(" "));
|
||||
|
||||
var message = self.message() || "";
|
||||
if (self._blink()) message = markup(message, style.blinkStyle);
|
||||
if (self._blink()) message = util.markup(message, style.blinkStyle);
|
||||
self.messageContent.setContent(message.toString());
|
||||
|
||||
// float: right basically
|
||||
['helpButton', 'rightContent', 'messageContent'].reduce(function (right, key) {
|
||||
self[key].right = right;
|
||||
return 2 + right + BaseElement.prototype.shrinkWidth.call(self[key]);
|
||||
return 2 + right + BaseWidget.prototype.shrinkWidth.call(self[key]);
|
||||
}, 1);
|
||||
|
||||
return BaseElement.prototype.render.apply(self, arguments);
|
||||
return BaseWidget.prototype.render.apply(self, arguments);
|
||||
};
|
||||
|
||||
module.exports = Header;
|
||||
|
@ -1,68 +1,68 @@
|
||||
var blessed = require('blessed');
|
||||
var _ = require('lazy.js');
|
||||
var _ = require('lodash');
|
||||
|
||||
var BaseElement = require('./BaseElement');
|
||||
var util = require('slap-util');
|
||||
|
||||
var util = require('../util');
|
||||
var BaseWidget = require('base-widget');
|
||||
var Editor = require('editor-widget');
|
||||
|
||||
function Pane (opts) {
|
||||
var self = this;
|
||||
|
||||
if (!(self instanceof blessed.Node)) return new Pane(opts);
|
||||
if (!(self instanceof Pane)) return new Pane(opts);
|
||||
|
||||
BaseElement.call(self, _({
|
||||
top: Slap.global.header.options.headerPosition === 'top' ? 1 : 0,
|
||||
bottom: Slap.global.header.options.headerPosition === 'bottom' ? 1 : 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
})
|
||||
.merge(Slap.global.options.pane || {})
|
||||
.merge(opts || {})
|
||||
.toObject());
|
||||
self.left = self.slap.fileBrowser.visible ? self.slap.fileBrowser.width : 0;
|
||||
BaseWidget.call(self, _.merge({
|
||||
top: Slap.global.header.options.headerPosition === 'top' ? 1 : 0,
|
||||
bottom: Slap.global.header.options.headerPosition === 'bottom' ? 1 : 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
}, Slap.global.options.pane, opts));
|
||||
self.left = Slap.global.fileBrowser.visible ? Slap.global.fileBrowser.width : 0;
|
||||
|
||||
self.forms = self.forms || [];
|
||||
|
||||
self.editor = self.options.editor || new Editor({
|
||||
self.editor = self.options.editor || new Editor(_.merge({
|
||||
parent: self,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0
|
||||
});
|
||||
}, Slap.global.options.editor));
|
||||
|
||||
self.findForm = new FindForm({parent: self});
|
||||
self.goLineForm = new GoLineForm({parent: self});
|
||||
self.saveAsForm = new SaveAsForm({parent: self});
|
||||
self.saveAsCloseForm = new SaveAsCloseForm({parent: self});
|
||||
|
||||
self.slap.panes.push(self);
|
||||
if (!self.parent.panes) self.parent.panes = [];
|
||||
self.parent.panes.push(self);
|
||||
}
|
||||
Pane.prototype.__proto__ = BaseElement.prototype;
|
||||
Pane.prototype.__proto__ = BaseWidget.prototype;
|
||||
|
||||
Pane.prototype.setCurrent = function () {
|
||||
var self = this;
|
||||
var slap = self.slap;
|
||||
var slap = self.screen.slap;
|
||||
var panes = slap.panes;
|
||||
var paneIndex = panes.indexOf(self);
|
||||
if (paneIndex === -1) { paneIndex = panes.length; panes.push(self); }
|
||||
slap.data.currentPane = paneIndex;
|
||||
self.ready.then(function () { if (self.isAttached()) self.focus(); }).done();
|
||||
self.ready
|
||||
.then(function () {
|
||||
if (!self.isAttached()) return;
|
||||
slap.data.currentPane = paneIndex;
|
||||
self.focus();
|
||||
})
|
||||
.done();
|
||||
return self;
|
||||
};
|
||||
Pane.prototype.close = function () {
|
||||
var self = this;
|
||||
self.detach();
|
||||
|
||||
var slap = self.slap;
|
||||
var slap = self.screen.slap;
|
||||
var paneIndex = slap.panes.indexOf(self);
|
||||
if (paneIndex !== -1) {
|
||||
slap.panes.splice(paneIndex, 1);
|
||||
if (slap.panes.length) {
|
||||
slap.panes[Math.max(paneIndex - 1, 0)].setCurrent();
|
||||
} else {
|
||||
slap.fileBrowser.focus();
|
||||
}
|
||||
if (slap.panes.length) slap.panes[Math.max(paneIndex - 1, 0)].setCurrent();
|
||||
else slap.fileBrowser.focus();
|
||||
}
|
||||
|
||||
self.emit('close');
|
||||
@ -86,12 +86,28 @@ Pane.prototype.requestClose = function () {
|
||||
}
|
||||
};
|
||||
|
||||
Pane.prototype.save = function (path) {
|
||||
var self = this;
|
||||
var header = self.screen.slap.header;
|
||||
var editor = self.editor;
|
||||
return editor.save(path)
|
||||
.tap(function () { header.message("saved to " + editor.textBuf.getPath(), 'success'); })
|
||||
.catch(function (err) {
|
||||
switch ((err.cause || err).code) {
|
||||
case 'EACCES': case 'EISDIR':
|
||||
header.message(err.message, 'error');
|
||||
break;
|
||||
default: throw err;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Pane.prototype._initHandlers = function () {
|
||||
var self = this;
|
||||
var editor = self.editor;
|
||||
self.on('element keypress', function (el, ch, key) {
|
||||
switch (util.getBinding(self.options.bindings, key)) {
|
||||
case 'save': if (!editor.readOnly()) editor.textBuf.getPath() ? editor.save().done() : self.saveAsForm.show(); return false;
|
||||
switch (self.resolveBinding(key)) {
|
||||
case 'save': if (!editor.readOnly()) editor.textBuf.getPath() ? self.save().done() : self.saveAsForm.show(); return false;
|
||||
case 'saveAs': if (!editor.readOnly()) self.saveAsForm.show(); return false;
|
||||
case 'close': self.requestClose(); return false;
|
||||
case 'find': self.findForm.show(); return false;
|
||||
@ -109,13 +125,17 @@ Pane.prototype._initHandlers = function () {
|
||||
if (!formHasFocus && visibleForms.length) visibleForms[0].focus();
|
||||
});
|
||||
|
||||
return BaseElement.prototype._initHandlers.apply(self, arguments);
|
||||
['path-changed', 'changed'].forEach(function (evt) {
|
||||
editor.textBuf.on(evt, function () { self.screen.slap.header.render(); });
|
||||
});
|
||||
editor.on('insertMode', function () { self.screen.slap.header.render(); });
|
||||
|
||||
return BaseWidget.prototype._initHandlers.apply(self, arguments);
|
||||
};
|
||||
|
||||
module.exports = Pane;
|
||||
|
||||
var Slap = require('./Slap');
|
||||
var Editor = require('./Editor');
|
||||
var SaveAsForm = require('./SaveAsForm');
|
||||
var SaveAsCloseForm = require('./SaveAsCloseForm');
|
||||
var FindForm = require('./FindForm');
|
||||
|
@ -1,5 +1,6 @@
|
||||
var blessed = require('blessed');
|
||||
var _ = require('lazy.js');
|
||||
var _ = require('lodash');
|
||||
|
||||
var BaseWidget = require('base-widget');
|
||||
|
||||
var Slap = require('./Slap');
|
||||
var SaveAsForm = require('./SaveAsForm');
|
||||
@ -8,25 +9,22 @@ var Button = require('./Button');
|
||||
function SaveAsCloseForm (opts) {
|
||||
var self = this;
|
||||
|
||||
if (!(self instanceof blessed.Node)) return new SaveAsCloseForm(opts);
|
||||
if (!(self instanceof SaveAsCloseForm)) return new SaveAsCloseForm(opts);
|
||||
|
||||
SaveAsForm.call(self, _(Slap.global.options.saveAsCloseForm).merge(opts || {}).toObject());
|
||||
SaveAsForm.call(self, _.merge({}, Slap.global.options.saveAsCloseForm, opts));
|
||||
|
||||
self.discardChangesButton = new Button(_({
|
||||
parent: self,
|
||||
content: "Discard changes",
|
||||
top: 0,
|
||||
right: 0
|
||||
})
|
||||
.merge(Slap.global.options.button.warning || {})
|
||||
.merge(self.options.discardChangesButton || {})
|
||||
.toObject());
|
||||
self.discardChangesButton = new Button(_.merge({
|
||||
parent: self,
|
||||
content: "Discard changes",
|
||||
top: 0,
|
||||
right: 0
|
||||
}, Slap.global.options.button.warning, self.options.discardChangesButton));
|
||||
}
|
||||
SaveAsCloseForm.prototype.__proto__ = SaveAsForm.prototype;
|
||||
|
||||
SaveAsCloseForm.prototype._initHandlers = function () {
|
||||
var self = this;
|
||||
self.on('show', function () { self.slap.header.message("unsaved changes, please save or discard", 'warning'); });
|
||||
self.on('show', function () { self.screen.slap.header.message("unsaved changes, please save or discard", 'warning'); });
|
||||
self.on('save', function () { self.pane.close(); });
|
||||
self.on('discardChanges', function () { self.pane.close(); });
|
||||
self.discardChangesButton.on('press', function () { self.emit('discardChanges'); });
|
||||
|
@ -1,46 +1,37 @@
|
||||
var blessed = require('blessed');
|
||||
var _ = require('lazy.js');
|
||||
var Point = require('text-buffer/lib/point');
|
||||
var _ = require('lodash');
|
||||
|
||||
var BaseWidget = require('base-widget');
|
||||
var Field = require('editor-widget').Field;
|
||||
var Slap = require('./Slap');
|
||||
var BaseElement = require('./BaseElement');
|
||||
var BaseForm = require('./BaseForm');
|
||||
var Field = require('./Field');
|
||||
|
||||
SaveAsForm._label = " save as: ";
|
||||
function SaveAsForm (opts) {
|
||||
var self = this;
|
||||
|
||||
if (!(self instanceof blessed.Node)) return new SaveAsForm(opts);
|
||||
if (!(self instanceof SaveAsForm)) return new SaveAsForm(opts);
|
||||
|
||||
BaseForm.call(self, _(Slap.global.options.form.saveAs)
|
||||
.merge({
|
||||
field: {left: SaveAsForm._label.length}
|
||||
})
|
||||
.merge(opts || {})
|
||||
.toObject());
|
||||
BaseForm.call(self, _.merge({
|
||||
field: {left: SaveAsForm._label.length}
|
||||
}, Slap.global.options.form.saveAs, opts));
|
||||
|
||||
self.saveAsLabel = new BaseElement(_({
|
||||
parent: self,
|
||||
tags: true,
|
||||
content: SaveAsForm._label,
|
||||
top: 0,
|
||||
height: 1,
|
||||
left: 0,
|
||||
width: SaveAsForm._label.length,
|
||||
style: self.options.style
|
||||
})
|
||||
.merge(self.options.saveAsLabel || {})
|
||||
.toObject());
|
||||
self.saveAsLabel = new BaseWidget(_.merge({
|
||||
parent: self,
|
||||
tags: true,
|
||||
content: SaveAsForm._label,
|
||||
top: 0,
|
||||
height: 1,
|
||||
left: 0,
|
||||
width: SaveAsForm._label.length,
|
||||
style: self.options.style
|
||||
}, self.options.saveAsLabel));
|
||||
|
||||
self.pathField = new Field(_({
|
||||
parent: self,
|
||||
top: 0,
|
||||
left: SaveAsForm._label.length,
|
||||
right: 0
|
||||
})
|
||||
.merge(self.options.pathField || {})
|
||||
.toObject());
|
||||
self.pathField = new Field(_.merge({
|
||||
parent: self,
|
||||
top: 0,
|
||||
left: SaveAsForm._label.length,
|
||||
right: 0
|
||||
}, Slap.global.options.editor, Slap.global.options.field, self.options.pathField));
|
||||
}
|
||||
SaveAsForm.prototype.__proto__ = BaseForm.prototype;
|
||||
|
||||
@ -48,16 +39,16 @@ SaveAsForm.prototype._initHandlers = function () {
|
||||
var self = this;
|
||||
self.on('show', function () {
|
||||
self.pathField.textBuf.setText(self.pane.editor.textBuf.getPath() || '');
|
||||
self.pathField.selection.setHeadPosition(new Point(0, Infinity));
|
||||
self.pathField.selection.setHeadPosition([0, Infinity]);
|
||||
self.pathField.focus();
|
||||
});
|
||||
self.on('submit', function () {
|
||||
var path = self.pathField.textBuf.getText();
|
||||
if (!path) {
|
||||
self.slap.header.message("couldn't save, no filename passed", 'error');
|
||||
self.screen.slap.header.message("couldn't save, no filename passed", 'error');
|
||||
return;
|
||||
}
|
||||
self.pane.editor.save(path).done(function (newPath) {
|
||||
self.pane.save(path).done(function (newPath) {
|
||||
if (newPath) {
|
||||
self.hide();
|
||||
self.emit('save', newPath);
|
||||
|
101
lib/ui/Slap.js
101
lib/ui/Slap.js
@ -1,37 +1,39 @@
|
||||
var _ = require('lazy.js');
|
||||
var _ = require('lodash');
|
||||
var lodash = require('lodash');
|
||||
var blessed = require('blessed');
|
||||
var Promise = require('bluebird');
|
||||
var path = require('path');
|
||||
var clap = require('node-clap');
|
||||
var mkdirp = Promise.promisify(require('mkdirp'));
|
||||
|
||||
var util = require('../util');
|
||||
var util = require('slap-util');
|
||||
|
||||
var BaseWidget = require('base-widget');
|
||||
var Editor = require('editor-widget');
|
||||
|
||||
function Slap (opts) {
|
||||
var self = this;
|
||||
|
||||
if (!(self instanceof blessed.Node)) return new Slap(opts);
|
||||
if (!(self instanceof Slap)) return new Slap(opts);
|
||||
|
||||
if (!Slap.global) Slap.global = this;
|
||||
blessed.Screen.call(self, opts);
|
||||
BaseElement.call(self, opts);
|
||||
if (opts.screen && !opts.screen.slap) opts.screen.slap = self;
|
||||
BaseWidget.call(self, opts);
|
||||
|
||||
self.panes = [];
|
||||
|
||||
self.header = new Header();
|
||||
self.header = new Header({parent: self});
|
||||
|
||||
self.fileBrowser = new FileBrowser({
|
||||
parent: self,
|
||||
top: self.header.options.headerPosition === 'top' ? 1 : 0,
|
||||
bottom: self.header.options.headerPosition === 'bottom' ? 1 : 0,
|
||||
left: 0,
|
||||
left: 0
|
||||
});
|
||||
self.fileBrowser.focus();
|
||||
|
||||
self.toggleInsertMode();
|
||||
}
|
||||
Slap.prototype.__proto__ = blessed.Screen.prototype;
|
||||
Slap.global = null;
|
||||
Slap.prototype.__proto__ = BaseWidget.prototype;
|
||||
|
||||
Slap.prototype.paneForPath = function (panePath) {
|
||||
var self = this;
|
||||
@ -49,7 +51,7 @@ Slap.prototype.open = Promise.method(function (filePath, current) {
|
||||
var self = this;
|
||||
|
||||
var pane = self.paneForPath(filePath);
|
||||
pane = pane || new Pane();
|
||||
pane = pane || new Pane({parent: self});
|
||||
return pane.editor.open(filePath)
|
||||
.then(function () {
|
||||
if (current) pane.setCurrent();
|
||||
@ -58,6 +60,9 @@ Slap.prototype.open = Promise.method(function (filePath, current) {
|
||||
.catch(function (err) {
|
||||
pane.close();
|
||||
switch ((err.cause || err).code) {
|
||||
case 'EACCES':
|
||||
self.header.message(err.message, 'error');
|
||||
break;
|
||||
case 'EISDIR':
|
||||
self.fileBrowser.refresh(filePath, _.noop);
|
||||
self.fileBrowser.focus();
|
||||
@ -67,14 +72,22 @@ Slap.prototype.open = Promise.method(function (filePath, current) {
|
||||
});
|
||||
});
|
||||
|
||||
Slap.prototype.insertMode = util.getterSetter('insertMode', null, Boolean);
|
||||
Slap.prototype.toggleInsertMode = function () { return this.insertMode(!this.insertMode()); };
|
||||
|
||||
Slap.prototype.quit = function () {
|
||||
Promise.all([
|
||||
require('../highlight/client').call('kill'),
|
||||
Promise.delay(20).then(function () { process.exit(0); }), // FIXME: .delay(20) hack for I/O flush
|
||||
]).done();
|
||||
var self = this;
|
||||
var quit = Promise.resolve();
|
||||
|
||||
if (Editor.highlightClient) quit = quit
|
||||
.return(Editor.highlightClient)
|
||||
.then(function (client) {
|
||||
if (!client) return;
|
||||
client.dontRespawn = true;
|
||||
self.panes.forEach(function (pane) { pane.detach(); });
|
||||
});
|
||||
|
||||
quit
|
||||
.delay(20) // FIXME: .delay(20) hack for I/O flush
|
||||
.then(function () { process.exit(0); })
|
||||
.done();
|
||||
|
||||
// this.program.input.removeAllListeners();
|
||||
|
||||
@ -96,32 +109,18 @@ Slap.prototype._stopKeyPropagation = function () {
|
||||
Slap.prototype._initHandlers = function () {
|
||||
var self = this;
|
||||
|
||||
self.on('keypress', function (ch, key) {
|
||||
var binding = util.getBinding(self.options.bindings, key);
|
||||
self.on('element keypress', function (el, ch, key) {
|
||||
var binding = self.resolveBinding(key);
|
||||
|
||||
var logLine = "keypress " + key.full;
|
||||
if (key.full !== key.sequence) logLine += " [raw: " + JSON.stringify(key.sequence) + "]";
|
||||
var bindingInfo;
|
||||
if (binding) {
|
||||
bindingInfo = binding + " on " + util.typeOf(self);
|
||||
} else {
|
||||
var focused = self.focused || {};
|
||||
do {
|
||||
var focusedBinding = util.getBinding((focused.options || {}).bindings, key);
|
||||
if (focusedBinding) {
|
||||
bindingInfo = focusedBinding + " on " + util.typeOf(focused);
|
||||
break;
|
||||
}
|
||||
} while (focused = focused.parent);
|
||||
}
|
||||
if (bindingInfo) {
|
||||
logger.silly(logLine + " (bound to " + bindingInfo + ")");
|
||||
} else {
|
||||
logger.silly(logLine);
|
||||
}
|
||||
var focused = {parent: self.screen.focused}, focusedBinding;
|
||||
while ((focused = focused.parent) && !(focusedBinding = BaseWidget.prototype.resolveBinding.call(focused, key)));
|
||||
if (focusedBinding) logLine += " (bound to "+focusedBinding+" on "+util.typeOf(focused) + ")";
|
||||
util.logger.silly(logLine);
|
||||
|
||||
switch (binding) {
|
||||
case 'new': new Pane().setCurrent(); return false;
|
||||
case 'new': new Pane({parent: self}).setCurrent(); return false;
|
||||
case 'open':
|
||||
if (!self.fileBrowser.visible) {
|
||||
self.fileBrowser.show();
|
||||
@ -178,7 +177,7 @@ Slap.prototype._initHandlers = function () {
|
||||
}
|
||||
});
|
||||
|
||||
self.on('mouse', function (mouseData) { logger.silly("mouse", mouseData); });
|
||||
self.on('mouse', function (mouseData) { util.logger.silly("mouse", mouseData); });
|
||||
|
||||
['element blur', 'element focus'].forEach(function (evt) {
|
||||
self.on(evt, function (el) {
|
||||
@ -188,19 +187,19 @@ Slap.prototype._initHandlers = function () {
|
||||
|
||||
self.on('element show', function (el) { if (el instanceof Pane) self.header.render(); });
|
||||
|
||||
['resize', 'element focus', 'insertMode'].forEach(function (evt) {
|
||||
self.on(evt, function () { self.render(); });
|
||||
['resize', 'element focus'].forEach(function (evt) {
|
||||
self.on(evt, function () { self.screen.render(); });
|
||||
});
|
||||
|
||||
return self._initPlugins().then(function () {
|
||||
return BaseElement.prototype._initHandlers.apply(self, arguments);
|
||||
});
|
||||
self.ready.call('_initPlugins').done();
|
||||
|
||||
return BaseWidget.prototype._initHandlers.apply(self, arguments);
|
||||
};
|
||||
|
||||
Slap.prototype._initPlugins = function () {
|
||||
var self = this;
|
||||
|
||||
return util.getUserDir().then(function (userDir) {
|
||||
return Slap.getUserDir().then(function (userDir) {
|
||||
return clap({
|
||||
val: self,
|
||||
module: require.main,
|
||||
@ -210,15 +209,19 @@ Slap.prototype._initPlugins = function () {
|
||||
})
|
||||
.map(function (obj) {
|
||||
return obj.promise
|
||||
.then(function () { logger.info("loaded plugin "+obj.plugin); })
|
||||
.catch(function (err) { logger.error("failed loading plugin "+obj.plugin+": "+(err.stack || err)); });
|
||||
.then(function () { util.logger.info("loaded plugin "+obj.plugin); })
|
||||
.catch(function (err) { util.logger.error("failed loading plugin "+obj.plugin+": "+(err.stack || err)); });
|
||||
});
|
||||
};
|
||||
|
||||
Slap.getUserDir = function () {
|
||||
var userDir = util.resolvePath('~/.' + require('../../package').name);
|
||||
return mkdirp(userDir).return(userDir);
|
||||
};
|
||||
|
||||
module.exports = Slap;
|
||||
|
||||
// circular imports
|
||||
var BaseElement = require('./BaseElement');
|
||||
var Header = require('./Header');
|
||||
var FileBrowser = require('./FileBrowser');
|
||||
var Pane = require('./Pane');
|
||||
|
80
lib/util.js
80
lib/util.js
@ -1,80 +0,0 @@
|
||||
var _ = require('lazy.js');
|
||||
var extend = require('xtend');
|
||||
var traverse = require('traverse');
|
||||
var path = require('path');
|
||||
var Promise = require('bluebird');
|
||||
var mkdirp = Promise.promisify(require('mkdirp'));
|
||||
|
||||
var package = require('../package');
|
||||
|
||||
var boolStrings = {true: true, false: false};
|
||||
|
||||
var util = _(require('util')).merge({
|
||||
clone: extend,
|
||||
extend: extend,
|
||||
|
||||
toArray: function (obj) { return [].slice.call(obj); },
|
||||
|
||||
mod: function (n, m) { return ((n % m) + m) % m; },
|
||||
|
||||
typeOf: function (val) {
|
||||
return (val.__proto__.constructor.toString().match(/\w+\s+(\w+)/) || [])[1];
|
||||
},
|
||||
|
||||
getterSetter: function (name, getter, setter) {
|
||||
getter = getter || _.identity;
|
||||
setter = setter || _.identity;
|
||||
return function () {
|
||||
if (arguments.length) {
|
||||
var newVal = setter.apply(this, arguments);
|
||||
this.data[name] = newVal;
|
||||
this.emit && this.emit(name, getter.call(this, newVal));
|
||||
return this;
|
||||
} else {
|
||||
return getter.call(this, this.data[name]);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
parseOpts: function (opts) {
|
||||
return traverse(opts).map(function (opt) {
|
||||
if (opt && typeof opt === 'string') {
|
||||
if (opt in boolStrings) return boolStrings[opt];
|
||||
|
||||
var number = Number(opt);
|
||||
if (number === number) return number; // if (!isNaN(number))
|
||||
}
|
||||
return opt;
|
||||
});
|
||||
},
|
||||
|
||||
resolvePath: function (givenPath) {
|
||||
if (!givenPath) givenPath = '';
|
||||
if (givenPath[0] === '~') {
|
||||
givenPath = path.join(process.platform !== 'win32'
|
||||
? process.env.HOME
|
||||
: process.env.USERPROFILE
|
||||
, givenPath.slice(1));
|
||||
}
|
||||
return path.resolve.apply(null, [].slice.call(arguments, 1).concat([givenPath]));
|
||||
},
|
||||
|
||||
getUserDir: function () {
|
||||
var userDir = util.resolvePath('~/.' + package.name);
|
||||
return mkdirp(userDir).return(userDir);
|
||||
},
|
||||
|
||||
getBinding: function (bindings, key) {
|
||||
for (var name in bindings) {
|
||||
if (bindings.hasOwnProperty(name)) {
|
||||
var keyBindings = bindings[name];
|
||||
if (!keyBindings) continue;
|
||||
if (typeof keyBindings === 'string') keyBindings = [keyBindings];
|
||||
if (keyBindings.some(function (binding) { return binding === key.full || binding === key.sequence; }))
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}).toObject();
|
||||
|
||||
module.exports = util;
|
58
lib/word.js
58
lib/word.js
@ -1,58 +0,0 @@
|
||||
//var word = /[^\s.\(\){}\[\]]+/g
|
||||
var word = /\w+/g
|
||||
|
||||
exports.prev = prev
|
||||
exports.next = next
|
||||
exports.current = current
|
||||
exports.wordEnd = wordEnd
|
||||
|
||||
function prev(string, i, r) {
|
||||
r = r || word
|
||||
r.lastIndex = 0
|
||||
r.global = true
|
||||
|
||||
var _m = null, m = null
|
||||
do {
|
||||
_m = m
|
||||
m = r.exec(string)
|
||||
} while (m && m.index < i);
|
||||
|
||||
if(!m || m.index >= i) return _m
|
||||
return m
|
||||
}
|
||||
|
||||
function next (string, i, r) {
|
||||
r = r || word
|
||||
r.lastIndex = i
|
||||
r.global = true
|
||||
|
||||
var _m = null, m = null
|
||||
do {
|
||||
m = r.exec(string)
|
||||
if(!m) return _m
|
||||
_m = m
|
||||
} while (m && m.index > i);
|
||||
|
||||
return r.exec(string)
|
||||
}
|
||||
|
||||
function current (string, i, r) {
|
||||
r = r || word
|
||||
r.lastIndex = i
|
||||
r.global = true
|
||||
var m
|
||||
do {
|
||||
m = r.exec(string)
|
||||
if(!m) return null
|
||||
//take the first match ends after this position.
|
||||
//console.error(m, i, '<', m.index + m[0].length)
|
||||
if(i < m.index + m[0].length)
|
||||
return m
|
||||
} while (m);
|
||||
|
||||
}
|
||||
|
||||
function wordEnd (string, i, r) {
|
||||
var m = current(string, i, r)
|
||||
return m && m.index + m[0].length
|
||||
}
|
28
package.json
28
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "slap",
|
||||
"version": "0.1.32",
|
||||
"version": "0.1.37",
|
||||
"description": "Sublime-like terminal-based text editor",
|
||||
"preferGlobal": true,
|
||||
"main": "lib/cli.js",
|
||||
@ -10,8 +10,8 @@
|
||||
"scripts": {
|
||||
"start": "./slap.js",
|
||||
"debug": "node-debug ./slap.js",
|
||||
"test": "test/index.js",
|
||||
"cover": "istanbul cover test/index.js"
|
||||
"test": "spec/index.js",
|
||||
"cover": "istanbul cover spec/index.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -21,34 +21,26 @@
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base-widget": "^1.0.0",
|
||||
"blessed": "^0.1.60",
|
||||
"bluebird": "^2.3.6",
|
||||
"cheerio": "^0.19.0",
|
||||
"es6-set": "^0.1.1",
|
||||
"highlight.js": "^8.2.0",
|
||||
"iconv-lite": "^0.4.4",
|
||||
"lazy.js": "^0.4.0",
|
||||
"editor-widget": "^1.0.0",
|
||||
"lodash": "^3.9.3",
|
||||
"longjohn": "^0.2.4",
|
||||
"mkdirp": "^0.5.0",
|
||||
"node-clap": "^0.0.5",
|
||||
"rc": "^1.0.0",
|
||||
"slap-clipboard-plugin": "^0.0.10",
|
||||
"text-buffer": "~6.1.3",
|
||||
"traverse": "^0.6.6",
|
||||
"slap-clipboard-plugin": "^0.0.12",
|
||||
"slap-util": "^1.0.1",
|
||||
"update-notifier": "^0.5.0",
|
||||
"winston": "^1.0.0",
|
||||
"xtend": "^4.0.0"
|
||||
"winston": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"get-random-port": "0.0.1",
|
||||
"istanbul": "^0.3.5",
|
||||
"node-inspector": "^0.10.0",
|
||||
"npm": "^2.7.4",
|
||||
"tape": "^2.12.3",
|
||||
"through2": "^0.6.3"
|
||||
"tape": "^2.12.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "<0.12"
|
||||
"node": "<0.11 >=0.12.5"
|
||||
}
|
||||
}
|
||||
|
172
slap.ini
172
slap.ini
@ -7,22 +7,6 @@
|
||||
[slap]
|
||||
fullUnicode = false
|
||||
|
||||
[editor]
|
||||
pageLines = 10
|
||||
doubleClickDuration = 600
|
||||
defaultEncoding = "UTF-8"
|
||||
highlight = true
|
||||
|
||||
[editor.buffer]
|
||||
useSpaces = true
|
||||
tabSize = 2
|
||||
visibleWhiteSpace = false
|
||||
visibleLineEndings = false
|
||||
|
||||
[editor.gutter]
|
||||
width = 6
|
||||
lineNumberWidth = 4
|
||||
|
||||
[header]
|
||||
messageDuration = 5000
|
||||
blinkRate = 500
|
||||
@ -31,18 +15,6 @@ headerPosition = "top"
|
||||
[fileBrowser]
|
||||
width = 12
|
||||
|
||||
[editor.buffer.cursorPadding]
|
||||
top = 2
|
||||
left = 2
|
||||
right = 2
|
||||
bottom = 2
|
||||
|
||||
[field.buffer.cursorPadding]
|
||||
left = 2
|
||||
right = 2
|
||||
top = 0
|
||||
bottom = 0
|
||||
|
||||
[logger]
|
||||
level = "info"
|
||||
|
||||
@ -70,97 +42,6 @@ close = "C-w"
|
||||
find = "C-f"
|
||||
goLine = "C-g"
|
||||
|
||||
[editor.bindings]
|
||||
goLeft = "left"
|
||||
goLeftWord[] = "C-left"
|
||||
goLeftWord[] = "M-left"
|
||||
goLeftWord[] = "\u001b\u001b[D"
|
||||
goLeftWord[] = "M-b"
|
||||
goLeftWord[] = "M-S-b"
|
||||
goLeftInfinity[] = "home"
|
||||
goLeftInfinity[] = "C-a"
|
||||
goRight = "right"
|
||||
goRightWord[] = "C-right"
|
||||
goRightWord[] = "M-right"
|
||||
goRightWord[] = "\u001b\u001b[C"
|
||||
goRightWord[] = "M-f"
|
||||
goRightWord[] = "M-S-f"
|
||||
goRightInfinity[] = "end"
|
||||
goRightInfinity[] = "C-e"
|
||||
|
||||
; must be above any "up"
|
||||
goUpParagraph[] = "\u001b\u001b[A"
|
||||
|
||||
goUp = "up"
|
||||
goUpParagraph[] = "C-up"
|
||||
goUpParagraph[] = "M-up"
|
||||
goUpParagraph[] = "M-{"
|
||||
goUpPage = "pageup"
|
||||
goUpInfinity[] = "C-home"
|
||||
goUpInfinity[] = "M-home"
|
||||
goUpInfinity[] = "M-<"
|
||||
|
||||
; must be above any "down"
|
||||
goDownParagraph[] = "\u001b\u001b[B"
|
||||
|
||||
goDown = "down"
|
||||
goDownParagraph[] = "C-down"
|
||||
goDownParagraph[] = "M-down"
|
||||
goDownParagraph[] = "M-}"
|
||||
goDownPage = "pagedown"
|
||||
goDownInfinity[] = "C-end"
|
||||
goDownInfinity[] = "M-end"
|
||||
goDownInfinity[] = "M->"
|
||||
goMatchingBracket[] = "C-m"
|
||||
goMatchingBracket[] = "C-]"
|
||||
goMatchingBracket[] = "\u001d"
|
||||
; selectAll = "C-a" ; "C-a" used for goLeftInfinity
|
||||
selectLeft = "S-left"
|
||||
selectLeftWord[] = "C-S-left"
|
||||
selectLeftWord[] = "M-S-left"
|
||||
selectLeftInfinity = "S-home"
|
||||
selectRight = "S-right"
|
||||
selectRightWord[] = "C-S-right"
|
||||
selectRightWord[] = "M-S-right"
|
||||
selectRightInfinity = "S-end"
|
||||
selectUp = "S-up"
|
||||
selectUpParagraph[] = "C-S-up"
|
||||
selectUpParagraph[] = "M-S-up"
|
||||
selectUpPage = "S-pageup"
|
||||
selectUpInfinity[] = "C-S-home"
|
||||
selectUpInfinity[] = "M-S-home"
|
||||
selectDown = "S-down"
|
||||
selectDownParagraph[] = "C-S-down"
|
||||
selectDownParagraph[] = "M-S-down"
|
||||
selectDownPage = "S-pagedown"
|
||||
selectDownInfinity[] = "C-S-end"
|
||||
selectDownInfinity[] = "M-S-end"
|
||||
selectMatchingBracket = "C-S-m"
|
||||
deleteLeft = "backspace"
|
||||
deleteRight = "delete"
|
||||
deleteLeftWord[] = "C-backspace"
|
||||
deleteLeftWord[] = "M-backspace"
|
||||
deleteLeftWord[] = "C-d"
|
||||
deleteLeftWord[] = "M-delete"
|
||||
deleteRightWord[] = "C-delete"
|
||||
deleteRightWord[] = "M-d"
|
||||
deleteLeftInfinity[] = "C-S-backspace"
|
||||
deleteLeftInfinity[] = "M-S-backspace"
|
||||
deleteRightInfinity[] = "C-S-delete"
|
||||
deleteRightInfinity[] = "M-S-delete"
|
||||
deleteLine = "C-k"
|
||||
duplicateLine = "C-b"
|
||||
indent[] = "tab"
|
||||
indent[] = "C-tab"
|
||||
dedent = "S-tab"
|
||||
undo = "C-z"
|
||||
redo = "C-y"
|
||||
focusNext = false
|
||||
focusPrev = false
|
||||
|
||||
[field.bindings]
|
||||
submit = "enter"
|
||||
|
||||
[form.bindings]
|
||||
cancel = "escape"
|
||||
|
||||
@ -172,12 +53,6 @@ prev = "up"
|
||||
[dialog.bindings]
|
||||
hide = "escape"
|
||||
|
||||
[element.bindings]
|
||||
focusNext[] = "tab"
|
||||
focusNext[] = "right"
|
||||
focusPrev[] = "S-tab"
|
||||
focusPrev[] = "left"
|
||||
|
||||
;;;;;;;;;;
|
||||
; Styles ;
|
||||
;;;;;;;;;;
|
||||
@ -207,52 +82,9 @@ bold = true
|
||||
[dialog.style]
|
||||
bg = "magenta"
|
||||
|
||||
[editor.style]
|
||||
selection = "{cyan-bg}"
|
||||
match = "{yellow-bg}"
|
||||
matchingBracket = "{green-bg}{bold}"
|
||||
mismatchedBracket = "{red-bg}{bold}"
|
||||
whiteSpace = "{magenta-fg}"
|
||||
keyword = "{red-fg}"
|
||||
built_in = "{yellow-fg}"
|
||||
preprocessor = "{red-fg}"
|
||||
title = "{underline}"
|
||||
params = "{bold}"
|
||||
class = ""
|
||||
function = ""
|
||||
decorator = "{bold}"
|
||||
shebang = "{yellow-bg}{black-fg}"
|
||||
variable = "{yellow-fg}"
|
||||
operator = "{green-fg}"
|
||||
subst = ""
|
||||
number = "{green-fg}{bold}"
|
||||
string = "{green-fg}{bold}"
|
||||
regexp = "{green-fg}{bold}"
|
||||
literal = "{green-fg}{bold}"
|
||||
comment = "{white-bg}{black-fg}"
|
||||
header = "{bold}"
|
||||
strong = "{bold}"
|
||||
code = "{green-fg}"
|
||||
link_label = ""
|
||||
link_url = "{yellow-fg}"
|
||||
bullet = "{blue-fg}"
|
||||
attribute = ""
|
||||
value = ""
|
||||
setting = ""
|
||||
label = ""
|
||||
symbol = ""
|
||||
constant = ""
|
||||
|
||||
[editor.gutter.style]
|
||||
bg = "magenta"
|
||||
currentLine = "{white-bg}{magenta-fg}{bold}"
|
||||
|
||||
[fileBrowser]
|
||||
selectedBg = "blue"
|
||||
|
||||
[field.style]
|
||||
default = "{underline}"
|
||||
|
||||
[button.style]
|
||||
bg = "white"
|
||||
fg = "black"
|
||||
@ -277,9 +109,5 @@ bg = "red"
|
||||
[perf]
|
||||
profile = false
|
||||
|
||||
[editor.perf]
|
||||
matchesRenderThrottle = 150
|
||||
updateContentThrottle = 16
|
||||
|
||||
[form.find.perf]
|
||||
findThrottle = 150
|
||||
|
@ -3,29 +3,19 @@
|
||||
|
||||
var test = require('tape');
|
||||
var Promise = require('bluebird');
|
||||
var through2 = require('through2');
|
||||
var fs = require('fs');
|
||||
var util = require('base-widget/spec/util');
|
||||
|
||||
var cli = require('../lib/cli');
|
||||
var Slap = require('../lib/ui/Slap');
|
||||
|
||||
test("cli", function (t) {
|
||||
var input = through2();
|
||||
input.setRawMode = function () {};
|
||||
input.isTTY = true;
|
||||
|
||||
var output = through2();
|
||||
output.isTTY = true;
|
||||
|
||||
Promise.using(cli({input: input, output: output}), function (slap) {
|
||||
Promise.using(cli({screen: util.screenFactory()}), function (slap) {
|
||||
t.test("should create an instance of slap", function (st) {
|
||||
st.plan(1);
|
||||
|
||||
st.ok(slap instanceof Slap);
|
||||
});
|
||||
|
||||
require('./Editor')(t)(slap);
|
||||
|
||||
return new Promise(function (resolve) { t.on('end', resolve); });
|
||||
}).done();
|
||||
});
|
@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
/*global require*/
|
||||
|
||||
require('./textUtil');
|
||||
require('./cli');
|
@ -1,36 +0,0 @@
|
||||
/*global require, global*/
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
var Pane = require('../lib/ui/Pane');
|
||||
|
||||
module.exports = function (t) {
|
||||
return function (slap) {
|
||||
t.test("Editor", function (st) {
|
||||
var pane = new Pane();
|
||||
var editor = pane.editor;
|
||||
|
||||
st.test(".open", function (sst) {
|
||||
sst.test("should open a file with perms 000 correctly", function (ssst) {
|
||||
ssst.plan(1);
|
||||
|
||||
var perms000File = path.resolve(__dirname, 'fixtures/perms-000');
|
||||
|
||||
// can't be checked in with 000 perms
|
||||
var originalPerms = (fs.statSync(perms000File).mode.toString(8).match(/[0-7]{3}$/) || [])[0] || '644';
|
||||
fs.chmodSync(perms000File, '000');
|
||||
|
||||
editor.open(perms000File)
|
||||
.then(function () {
|
||||
ssst.equal(editor.textBuf.getText(), '');
|
||||
})
|
||||
.finally(function () { fs.chmodSync(perms000File, originalPerms); })
|
||||
.done();
|
||||
});
|
||||
sst.end();
|
||||
});
|
||||
st.end();
|
||||
});
|
||||
};
|
||||
};
|
1
test/fixtures/benchmark-longline.json
vendored
1
test/fixtures/benchmark-longline.json
vendored
@ -1 +0,0 @@
|
||||
{"web-app":{"servlet":[{"servlet-name":"cofaxCDS","servlet-class":"org.cofax.cds.CDSServlet","init-param":{"configGlossary:installationAt":"Philadelphia, PA","configGlossary:adminEmail":"ksm@pobox.com","configGlossary:poweredBy":"Cofax","configGlossary:poweredByIcon":"/images/cofax.gif","configGlossary:staticPath":"/content/static","templateProcessorClass":"org.cofax.WysiwygTemplate","templateLoaderClass":"org.cofax.FilesTemplateLoader","templatePath":"templates","templateOverridePath":"","defaultListTemplate":"listTemplate.htm","defaultFileTemplate":"articleTemplate.htm","useJSP":false,"jspListTemplate":"listTemplate.jsp","jspFileTemplate":"articleTemplate.jsp","cachePackageTagsTrack":200,"cachePackageTagsStore":200,"cachePackageTagsRefresh":60,"cacheTemplatesTrack":100,"cacheTemplatesStore":50,"cacheTemplatesRefresh":15,"cachePagesTrack":200,"cachePagesStore":100,"cachePagesRefresh":10,"cachePagesDirtyRead":10,"searchEngineListTemplate":"forSearchEnginesList.htm","searchEngineFileTemplate":"forSearchEngines.htm","searchEngineRobotsDb":"WEB-INF/robots.db","useDataStore":true,"dataStoreClass":"org.cofax.SqlDataStore","redirectionClass":"org.cofax.SqlRedirection","dataStoreName":"cofax","dataStoreDriver":"com.microsoft.jdbc.sqlserver.SQLServerDriver","dataStoreUrl":"jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon","dataStoreUser":"sa","dataStorePassword":"dataStoreTestQuery","dataStoreTestQuery":"SET NOCOUNT ON;select test=\'test\';","dataStoreLogFile":"/usr/local/tomcat/logs/datastore.log","dataStoreInitConns":10,"dataStoreMaxConns":100,"dataStoreConnUsageLimit":100,"dataStoreLogLevel":"debug","maxUrlLength":500}},{"servlet-name":"cofaxEmail","servlet-class":"org.cofax.cds.EmailServlet","init-param":{"mailHost":"mail1","mailHostOverride":"mail2"}},{"servlet-name":"cofaxAdmin","servlet-class":"org.cofax.cds.AdminServlet"},{"servlet-name":"fileServlet","servlet-class":"org.cofax.cds.FileServlet"},{"servlet-name":"cofaxTools","servlet-class":"org.cofax.cms.CofaxToolsServlet","init-param":{"templatePath":"toolstemplates/","log":1,"logLocation":"/usr/local/tomcat/logs/CofaxTools.log","logMaxSize":"","dataLog":1,"dataLogLocation":"/usr/local/tomcat/logs/dataLog.log","dataLogMaxSize":"","removePageCache":"/content/admin/remove?cache=pages&id=","removeTemplateCache":"/content/admin/remove?cache=templates&id=","fileTransferFolder":"/usr/local/tomcat/webapps/content/fileTransferFolder","lookInContext":1,"adminGroupID":4,"betaServer":true}}],"servlet-mapping":{"cofaxCDS":"/","cofaxEmail":"/cofaxutil/aemail/*","cofaxAdmin":"/admin/*","fileServlet":"/static/*","cofaxTools":"/tools/*"},"taglib":{"taglib-uri":"cofax.tld","taglib-location":"/WEB-INF/tlds/cofax.tld"}}}
|
1092
test/fixtures/halfwidth_and_fullwidth_forms.html
vendored
1092
test/fixtures/halfwidth_and_fullwidth_forms.html
vendored
File diff suppressed because it is too large
Load Diff
1
test/fixtures/perms-000
vendored
1
test/fixtures/perms-000
vendored
@ -1 +0,0 @@
|
||||
perms-000
|
@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/*global require, global*/
|
||||
|
||||
var test = require('tape');
|
||||
var textUtil = require('../lib/textUtil');
|
||||
|
||||
test("textUtil", function (t) {
|
||||
t.test(".splitLines", function (st) {
|
||||
var text = "This is a line.\rThis is another line.\r\nHere's a third line.\n";
|
||||
var lines = textUtil.splitLines(text);
|
||||
|
||||
st.test("should split on varying line endings correctly", function (sst) {
|
||||
sst.plan(4);
|
||||
|
||||
sst.equal(lines[0], "This is a line.\r");
|
||||
sst.equal(lines[1], "This is another line.\r\n");
|
||||
sst.equal(lines[2], "Here's a third line.\n");
|
||||
sst.equal(lines[3], "");
|
||||
});
|
||||
st.test("should join back together to restore original string", function (sst) {
|
||||
sst.plan(1);
|
||||
|
||||
sst.equal(lines.join(''), text);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user