Implements gutter

This commit is contained in:
Dan Kaplun 2014-06-12 19:18:05 -05:00
parent d035a26d33
commit 07a465e692
3 changed files with 68 additions and 27 deletions

View File

@ -17,12 +17,34 @@ function Editor (opts) {
if (!(self instanceof blessed.Node)) return new Editor(opts);
blessed.Box.call(self, _({
multiLine: true
}).merge(opts || {}).toObject());
['parent', 'left', 'right', 'top', 'bottom'].forEach(function (key) {
delete opts[key];
});
self.gutter = new blessed.Box(_({
parent: self,
tags: true,
wrap: false,
style: {},
top: 0,
left: 0,
width: 0,
bottom: 0
}).merge(opts.gutter || {}).toObject());
self.buffer = new blessed.Box(_({
parent: self,
tags: true,
wrap: false,
tabSize: 4,
style: {},
multiLine: true
}).merge(opts || {}).toObject());
top: 0,
left: self.options.multiLine && self.gutter.visible ? self.gutter.width : 0,
right: 0,
bottom: 0
}).merge(opts || {}).merge(opts.buffer || {}).toObject());
self
.text('', false)
@ -123,7 +145,7 @@ Editor.prototype.visiblePos = function (pos) {
}
return {
x: self.line(pos.y)
.slice(0, pos.x)
.slice(0, Math.max(pos.x, 0))
.replace(Editor._tabRegExp, _.repeat('\t', self.options.tabSize).join(''))
.length,
y: pos.y
@ -133,7 +155,7 @@ Editor.prototype.realPos = function (pos) {
return {
x: this.line(pos.y)
.replace(Editor._tabRegExp, _.repeat('\t', this.options.tabSize).join(''))
.slice(0, pos.x)
.slice(0, Math.max(pos.x, 0))
.replace(new RegExp('\\t{1,'+this.options.tabSize+'}', 'g'), '\t')
.length,
y: pos.y
@ -144,8 +166,7 @@ Editor.prototype.scroll = util.getterSetter('scroll', util.clone, Coordinate.set
var result = Coordinate({
x: Infinity,
y: this.data.lines.length
});
if (this.parent) result = result.subtract(this.size());
}).subtract(this.bufferSize());
return result;
}));
@ -361,17 +382,17 @@ Editor.prototype.findAll = function (pattern, start, end) {
.toArray();
};
Editor.prototype.pos = function () {
Editor.prototype.bufferPos = function () {
return Coordinate({
x: this.left + this.ileft,
y: this.top + this.itop
x: this.buffer.left + this.buffer.ileft,
y: this.buffer.top + this.buffer.itop
});
};
Editor.prototype.size = function () {
Editor.prototype.bufferSize = function () {
return Coordinate({
x: this.width - this.iwidth,
y: this.height - this.iheight
x: this.buffer.width - this.buffer.iwidth,
y: this.buffer.height - this.buffer.iheight
});
};
@ -619,7 +640,7 @@ Editor.prototype._initHandlers = function () {
self.on('mouse', function (mouseData) {
var mouse = self.realPos(Coordinate(mouseData)
.subtract(self.pos())
.subtract(self.bufferPos())
.add(self.scroll()));
if (mouseData.action === 'wheeldown' || mouseData.action === 'wheelup') {
@ -675,7 +696,7 @@ Editor.prototype._initHandlers = function () {
var minScroll = cursor
.subtract({x: cursorPadding.left || 0, y: cursorPadding.top || 0});
var maxScroll = cursor
.subtract(self.size())
.subtract(self.bufferSize())
.add({x: cursorPadding.right || 0, y: cursorPadding.bottom || 0})
.add({x: 1, y: 1});
@ -708,9 +729,9 @@ Editor.prototype._updateCursor = function () {
var self = this;
var scroll = self.scroll();
var cursorOnScreen = Coordinate(self.visiblePos(self.cursor()))
.add(self.pos())
.add(self.bufferPos())
.subtract(self.scroll());
if (cursorOnScreen.within(self.pos(), self.pos().add(self.size()))) {
if (cursorOnScreen.subtract(self.bufferPos()).within(Coordinate.origin(), self.bufferSize())) {
self.screen.program.move(cursorOnScreen.x, cursorOnScreen.y);
self.screen.program.showCursor();
} else {
@ -743,7 +764,7 @@ Editor.prototype.render = function () {
var self = this;
var scroll = self.scroll();
var size = self.size();
var size = self.bufferSize();
var cursor = self.cursor();
var selection = self.select();
var matchingBracket = self.matchingBracket(cursor);
@ -758,13 +779,18 @@ Editor.prototype.render = function () {
var style = self.options.style;
var selectionStyle = style.selection;
var matchStyle = style.match;
var currentLineStyle = style.currentLine;
var bracketStyle = matchingBracket && matchingBracket.match ? style.matchingBracket : style.mismatchedBracket;
self.setContent(self.data.markup.lines
// .concat(_.repeat('', self.size().y).toArray())
var gutterWidth = self.gutter.width;
var lineNumberWidth = self.gutter.options.lineNumberWidth;
var currentLineStyle = self.gutter.options.style.currentLine;
var bufferContent = [];
var gutterContent = [];
self.data.markup.lines
.slice(scroll.y, scroll.y + size.y)
.map(function (line, y) {
.forEach(function (line, y) {
var x = scroll.x;
y += scroll.y;
@ -805,13 +831,20 @@ Editor.prototype.render = function () {
}
});
if (self.screen.focused === self && currentLineStyle && y === visibleCursor.y) {
line = markup(line, currentLineStyle);
bufferContent.push(line + '{/}');
var gutterLine = (_.repeat(' ', lineNumberWidth).join('') + (y + 1)).slice(-lineNumberWidth);
gutterLine += _.repeat(' ', gutterWidth).join('');
if (currentLineStyle && y === visibleCursor.y) {
gutterLine = markup(gutterLine, currentLineStyle);
}
return line + '{/}';
})
.join('\n'));
gutterContent.push(gutterLine + '{/}');
});
self.buffer.setContent(bufferContent.join('\n'));
self.gutter.setContent(gutterContent.join('\n'));
return blessed.Box.prototype.render.apply(self, arguments);
};

View File

@ -25,6 +25,7 @@ function HelpDialog (opts) {
bottom: self.padding.bottom
})
.merge(_(self.screen.options.editor).omit(['visibleWhiteSpace', 'visibleLineEndings']).toObject())
.merge({gutter: {hidden: true}})
.merge(opts || {})
.toObject())

View File

@ -8,6 +8,10 @@ doubleClickDuration = 600
visibleWhiteSpace = false
visibleLineEndings = false
[editor.gutter]
width = 6
lineNumberWidth = 4
[header]
messageDuration = 5000
blinkRate = 500
@ -113,7 +117,6 @@ bg = "magenta"
[editor.style]
selection = "{cyan-bg}"
match = "{yellow-bg}"
currentLine = "{blue-bg}"
matchingBracket = "{green-bg}{bold}"
mismatchedBracket = "{red-bg}{bold}"
whiteSpace = "{magenta-fg}"
@ -144,6 +147,10 @@ value = ""
setting = ""
label = ""
[editor.gutter.style]
bg = "magenta"
currentLine = "{white-bg}{magenta-fg}{bold}"
[field.style]
currentLine = "{underline}"