2015-06-01 03:18:23 +00:00
|
|
|
|
/* jslint node: true */
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
var View = require('./view.js').View;
|
|
|
|
|
var miscUtil = require('./misc_util.js');
|
|
|
|
|
var strUtil = require('./string_util.js');
|
|
|
|
|
var ansi = require('./ansi_term.js');
|
2015-06-01 23:10:27 +00:00
|
|
|
|
//var TextBuffer = require('./text_buffer.js').TextBuffer;
|
2015-06-01 03:18:23 +00:00
|
|
|
|
|
|
|
|
|
var assert = require('assert');
|
|
|
|
|
var _ = require('lodash');
|
|
|
|
|
|
2015-06-05 22:20:26 +00:00
|
|
|
|
// :TODO: Determine CTRL-* keys for various things
|
|
|
|
|
// See http://www.bbsdocumentary.com/library/PROGRAMS/GRAPHICS/ANSI/bansi.txt
|
|
|
|
|
// http://wiki.synchro.net/howto:editor:slyedit#edit_mode
|
|
|
|
|
// http://sublime-text-unofficial-documentation.readthedocs.org/en/latest/reference/keyboard_shortcuts_win.html
|
|
|
|
|
|
|
|
|
|
/* Mystic
|
|
|
|
|
[^B] Reformat Paragraph [^O] Show this help file
|
|
|
|
|
[^I] Insert tab space [^Q] Enter quote mode
|
|
|
|
|
[^K] Cut current line of text [^V] Toggle insert/overwrite
|
|
|
|
|
[^U] Paste previously cut text [^Y] Delete current line
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BASIC MOVEMENT COMMANDS
|
|
|
|
|
|
|
|
|
|
UP/^E LEFT/^S PGUP/^R HOME/^F
|
|
|
|
|
DOWN/^X RIGHT/^D PGDN/^C END/^G
|
|
|
|
|
*/
|
|
|
|
|
|
2015-06-07 01:34:55 +00:00
|
|
|
|
//
|
|
|
|
|
// Some other interesting implementations, resources, etc.
|
|
|
|
|
//
|
|
|
|
|
// Editors - BBS
|
|
|
|
|
// * https://github.com/M-griffin/Enthral/blob/master/src/msg_fse.cpp
|
|
|
|
|
//
|
|
|
|
|
// Editors - Other
|
|
|
|
|
// * http://joe-editor.sourceforge.net/
|
|
|
|
|
// * http://www.jbox.dk/downloads/edit.c
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// To-Do
|
|
|
|
|
//
|
|
|
|
|
// * Word wrap from pos to next { eol : true } when inserting text
|
|
|
|
|
// * Page up/down just divide by and set top index
|
|
|
|
|
// * Index pos % for emit scroll events
|
|
|
|
|
// *
|
|
|
|
|
|
2015-06-03 04:18:00 +00:00
|
|
|
|
var SPECIAL_KEY_MAP_DEFAULT = {
|
2015-06-05 04:29:14 +00:00
|
|
|
|
lineFeed : [ 'return' ],
|
2015-06-03 04:18:00 +00:00
|
|
|
|
exit : [ 'esc' ],
|
|
|
|
|
backspace : [ 'backspace' ],
|
|
|
|
|
del : [ 'del' ],
|
2015-06-14 03:49:12 +00:00
|
|
|
|
tab : [ 'tab' ],
|
2015-06-03 04:18:00 +00:00
|
|
|
|
up : [ 'up arrow' ],
|
|
|
|
|
down : [ 'down arrow' ],
|
|
|
|
|
end : [ 'end' ],
|
|
|
|
|
home : [ 'home' ],
|
|
|
|
|
left : [ 'left arrow' ],
|
|
|
|
|
right : [ 'right arrow' ],
|
2015-06-05 22:20:26 +00:00
|
|
|
|
clearLine : [ 'ctrl + y' ],
|
2015-06-04 04:00:33 +00:00
|
|
|
|
pageUp : [ 'page up' ],
|
|
|
|
|
pageDown : [ 'page down' ],
|
2015-06-07 01:34:55 +00:00
|
|
|
|
insert : [ 'insert', 'ctrl + v' ],
|
2015-06-04 04:00:33 +00:00
|
|
|
|
};
|
2015-06-03 04:18:00 +00:00
|
|
|
|
|
2015-06-01 03:18:23 +00:00
|
|
|
|
exports.MultiLineEditTextView2 = MultiLineEditTextView2;
|
|
|
|
|
|
|
|
|
|
function MultiLineEditTextView2(options) {
|
|
|
|
|
if(!_.isBoolean(options.acceptsFocus)) {
|
|
|
|
|
options.acceptsFocus = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!_.isBoolean(this.acceptsInput)) {
|
|
|
|
|
options.acceptsInput = true;
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-03 04:18:00 +00:00
|
|
|
|
if(!_.isObject(options.specialKeyMap)) {
|
|
|
|
|
options.specialKeyMap = SPECIAL_KEY_MAP_DEFAULT;
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-01 03:18:23 +00:00
|
|
|
|
View.call(this, options);
|
|
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
|
|
//
|
2015-06-01 23:10:27 +00:00
|
|
|
|
// ANSI seems to want tabs to default to 8 characters. See the following:
|
2015-06-01 03:18:23 +00:00
|
|
|
|
// * http://www.ansi-bbs.org/ansi-bbs2/control_chars/
|
|
|
|
|
// * http://www.bbsdocumentary.com/library/PROGRAMS/GRAPHICS/ANSI/bansi.txt
|
|
|
|
|
//
|
2015-06-07 01:34:55 +00:00
|
|
|
|
// This seems overkill though, so let's default to 4 :)
|
|
|
|
|
//
|
|
|
|
|
this.tabWidth = _.isNumber(options.tabWidth) ? options.tabWidth : 4;
|
2015-06-01 03:18:23 +00:00
|
|
|
|
|
2015-06-02 05:00:54 +00:00
|
|
|
|
this.textLines = [];
|
|
|
|
|
this.topVisibleIndex = 0;
|
2015-06-03 04:18:00 +00:00
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// cursorPos represents zero-based row, col positions
|
|
|
|
|
// within the editor itself
|
|
|
|
|
//
|
2015-06-02 22:36:55 +00:00
|
|
|
|
this.cursorPos = { col : 0, row : 0 };
|
2015-06-01 03:18:23 +00:00
|
|
|
|
|
2015-06-08 22:51:27 +00:00
|
|
|
|
this.getSGRFor = function(sgrFor) {
|
|
|
|
|
return {
|
|
|
|
|
text : self.getSGR(),
|
|
|
|
|
}[sgrFor] || self.getSGR();
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-04 04:00:33 +00:00
|
|
|
|
// :TODO: Most of the calls to this could be avoided via incrementRow(), decrementRow() that keeps track or such
|
2015-06-03 04:18:00 +00:00
|
|
|
|
this.getTextLinesIndex = function(row) {
|
2015-06-03 23:09:03 +00:00
|
|
|
|
if(!_.isNumber(row)) {
|
|
|
|
|
row = self.cursorPos.row;
|
|
|
|
|
}
|
|
|
|
|
var index = self.topVisibleIndex + row;
|
2015-06-03 04:18:00 +00:00
|
|
|
|
return index;
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-06 06:33:59 +00:00
|
|
|
|
this.getRemainingLinesBelowRow = function(row) {
|
|
|
|
|
if(!_.isNumber(row)) {
|
|
|
|
|
row = self.cursorPos.row;
|
|
|
|
|
}
|
|
|
|
|
return self.textLines.length - (self.topVisibleIndex + row) - 1;
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-07 01:34:55 +00:00
|
|
|
|
this.getNextEndOfLineIndex = function(startIndex) {
|
2015-06-08 03:32:47 +00:00
|
|
|
|
for(var i = startIndex; i < self.textLines.length; i++) {
|
2015-06-07 01:34:55 +00:00
|
|
|
|
if(self.textLines[i].eol) {
|
2015-06-08 03:32:47 +00:00
|
|
|
|
return i;
|
2015-06-07 01:34:55 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-06-08 03:32:47 +00:00
|
|
|
|
return self.textLines.length;
|
2015-06-07 01:34:55 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-06-07 06:04:09 +00:00
|
|
|
|
this.redrawRows = function(startRow, endRow) {
|
2015-06-08 22:51:27 +00:00
|
|
|
|
self.client.term.write(self.getSGRFor('text') + ansi.hideCursor());
|
2015-06-07 06:04:09 +00:00
|
|
|
|
|
|
|
|
|
var startIndex = self.getTextLinesIndex(startRow);
|
|
|
|
|
var endIndex = Math.min(self.getTextLinesIndex(endRow), self.textLines.length);
|
|
|
|
|
var absPos = self.getAbsolutePosition(startRow, 0);
|
|
|
|
|
|
|
|
|
|
for(var i = startIndex; i < endIndex; ++i) {
|
|
|
|
|
self.client.term.write(ansi.goto(absPos.row++, absPos.col));
|
|
|
|
|
self.client.term.write(self.getRenderText(i));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.client.term.write(ansi.showCursor());
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-01 03:18:23 +00:00
|
|
|
|
this.redrawVisibleArea = function() {
|
2015-06-07 01:34:55 +00:00
|
|
|
|
assert(self.topVisibleIndex <= self.textLines.length);
|
2015-06-07 06:04:09 +00:00
|
|
|
|
self.redrawRows(0, self.dimens.height);
|
2015-06-01 23:10:27 +00:00
|
|
|
|
};
|
2015-06-02 05:00:54 +00:00
|
|
|
|
|
2015-06-02 22:36:55 +00:00
|
|
|
|
this.getVisibleText = function(index) {
|
2015-06-03 23:09:03 +00:00
|
|
|
|
if(!_.isNumber(index)) {
|
|
|
|
|
index = self.getTextLinesIndex();
|
|
|
|
|
}
|
2015-06-02 22:36:55 +00:00
|
|
|
|
return self.textLines[index].text.replace(/\t/g, ' ');
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-03 23:09:03 +00:00
|
|
|
|
this.getText = function(index) {
|
|
|
|
|
if(!_.isNumber(index)) {
|
|
|
|
|
index = self.getTextLinesIndex();
|
|
|
|
|
}
|
|
|
|
|
return self.textLines[index].text;
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-12 05:09:13 +00:00
|
|
|
|
this.getCharacter = function(index, col) {
|
|
|
|
|
if(!_.isNumber(col)) {
|
|
|
|
|
col = self.cursorPos.col;
|
|
|
|
|
}
|
|
|
|
|
return self.getText(index).charAt(col);
|
2015-06-12 22:44:32 +00:00
|
|
|
|
};
|
2015-06-12 05:09:13 +00:00
|
|
|
|
|
2015-06-03 23:09:03 +00:00
|
|
|
|
this.getTextEndOfLineColumn = function(index) {
|
2015-06-07 01:34:55 +00:00
|
|
|
|
return Math.max(0, self.getText(index).length);
|
2015-06-03 23:09:03 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-06-02 22:36:55 +00:00
|
|
|
|
this.getRenderText = function(index) {
|
|
|
|
|
var text = self.getVisibleText(index);
|
|
|
|
|
var remain = self.dimens.width - text.length;
|
|
|
|
|
if(remain > 0) {
|
|
|
|
|
text += new Array(remain).join(' ');
|
|
|
|
|
}
|
|
|
|
|
return text;
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-15 03:19:25 +00:00
|
|
|
|
this.getTextLines = function(startIndex, endIndex) {
|
|
|
|
|
var lines;
|
2015-06-07 01:34:55 +00:00
|
|
|
|
if(startIndex === endIndex) {
|
|
|
|
|
lines = [ self.textLines[startIndex] ];
|
|
|
|
|
} else {
|
2015-06-08 05:45:06 +00:00
|
|
|
|
lines = self.textLines.slice(startIndex, endIndex + 1); // "slice extracts up to but not including end."
|
2015-06-07 01:34:55 +00:00
|
|
|
|
}
|
2015-06-15 03:19:25 +00:00
|
|
|
|
return lines;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.getOutputText = function(startIndex, endIndex, includeEol) {
|
|
|
|
|
var lines = self.getTextLines(startIndex, endIndex);
|
2015-06-07 01:34:55 +00:00
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Convert lines to contiguous string -- all expanded
|
|
|
|
|
// tabs put back to single '\t' characters.
|
|
|
|
|
//
|
|
|
|
|
var text = '';
|
2015-06-15 03:19:25 +00:00
|
|
|
|
var re = new RegExp('\\t{1,' + (self.tabWidth) + '}', 'g');
|
2015-06-07 01:34:55 +00:00
|
|
|
|
for(var i = 0; i < lines.length; ++i) {
|
|
|
|
|
text += lines[i].text.replace(re, '\t');
|
2015-06-09 04:41:57 +00:00
|
|
|
|
if(includeEol && lines[i].eol) {
|
2015-06-07 01:34:55 +00:00
|
|
|
|
text += '\n';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return text;
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-15 03:19:25 +00:00
|
|
|
|
this.getContiguousText = function(startIndex, endIndex, includeEol) {
|
|
|
|
|
var lines = self.getTextLines(startIndex, endIndex);
|
|
|
|
|
var text = '';
|
|
|
|
|
for(var i = 0; i < lines.length; ++i) {
|
|
|
|
|
text += lines[i].text;
|
|
|
|
|
if(includeEol && lines[i].eol) {
|
|
|
|
|
text += '\n'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return text;
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-04 04:00:33 +00:00
|
|
|
|
this.replaceCharacterInText = function(c, index, col) {
|
|
|
|
|
self.textLines[index].text = strUtil.replaceAt(
|
|
|
|
|
self.textLines[index].text, col, c);
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-15 21:54:33 +00:00
|
|
|
|
/*
|
2015-06-15 03:19:25 +00:00
|
|
|
|
this.editTextAtPosition = function(editAction, text, index, col) {
|
|
|
|
|
switch(editAction) {
|
|
|
|
|
case 'insert' :
|
|
|
|
|
self.insertCharactersInText(text, index, col);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'deleteForward' :
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'deleteBack' :
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'replace' :
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
};
|
2015-06-15 21:54:33 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
this.removeCharactersInText = function(count, index, col) {
|
|
|
|
|
self.textLines[index].text =
|
|
|
|
|
self.textLines[index].text.slice(col, count) +
|
|
|
|
|
self.textLines[index].text.slice(col + count);
|
|
|
|
|
};
|
2015-06-15 03:19:25 +00:00
|
|
|
|
|
|
|
|
|
this.insertCharactersInText = function(c, index, col) {
|
2015-06-07 01:34:55 +00:00
|
|
|
|
self.textLines[index].text = [
|
|
|
|
|
self.textLines[index].text.slice(0, col),
|
|
|
|
|
c,
|
|
|
|
|
self.textLines[index].text.slice(col)
|
|
|
|
|
].join('');
|
2015-06-14 03:49:12 +00:00
|
|
|
|
|
2015-06-15 03:19:25 +00:00
|
|
|
|
//self.cursorPos.col++;
|
|
|
|
|
self.cursorPos.col += c.length;
|
2015-06-14 03:49:12 +00:00
|
|
|
|
|
|
|
|
|
var cursorOffset;
|
|
|
|
|
var absPos;
|
|
|
|
|
|
|
|
|
|
if(self.getText(index).length >= self.dimens.width) {
|
|
|
|
|
//
|
|
|
|
|
// Past available space -- word wrap from current point
|
|
|
|
|
// to the next EOL. Update textLines with the newly
|
|
|
|
|
// formatted array.
|
|
|
|
|
//
|
|
|
|
|
var nextEolIndex = self.getNextEndOfLineIndex(index);
|
2015-06-15 21:54:33 +00:00
|
|
|
|
var wrapped = self.wordWrapSingleLine(self.getContiguousText(index, nextEolIndex), 'tabsIntact');
|
2015-06-14 03:49:12 +00:00
|
|
|
|
var newLines = wrapped.wrapped;
|
|
|
|
|
|
2015-06-15 03:19:25 +00:00
|
|
|
|
/*
|
|
|
|
|
console.log(strUtil.debugEscapedString(self.getText(index)) + ' / ' + self.getText(index).length)
|
|
|
|
|
console.log(strUtil.debugEscapedString(self.getOutputText(index, nextEolIndex)))
|
|
|
|
|
console.log(newLines)
|
|
|
|
|
console.log(newLines[0].length)
|
|
|
|
|
console.log(strUtil.debugEscapedString(self.getContiguousText(index, nextEolIndex)))
|
|
|
|
|
*/
|
|
|
|
|
|
2015-06-14 03:49:12 +00:00
|
|
|
|
//
|
|
|
|
|
// If our cursor was within the bounds of the last wrapped word
|
|
|
|
|
// we'll want to adjust the cursor to the same relative position
|
|
|
|
|
// on the next line.
|
|
|
|
|
//
|
2015-06-15 03:19:25 +00:00
|
|
|
|
//var lastCol = self.cursorPos.col - 1;
|
|
|
|
|
var lastCol = self.cursorPos.col - c.length;
|
2015-06-14 03:49:12 +00:00
|
|
|
|
if(lastCol >= wrapped.firstWrapRange.start && lastCol <= wrapped.firstWrapRange.end) {
|
|
|
|
|
cursorOffset = self.cursorPos.col - wrapped.firstWrapRange.start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(var i = 0; i < newLines.length; ++i) {
|
|
|
|
|
newLines[i] = { text : newLines[i] };
|
|
|
|
|
}
|
|
|
|
|
newLines[newLines.length - 1].eol = true;
|
2015-06-15 03:19:25 +00:00
|
|
|
|
|
2015-06-14 03:49:12 +00:00
|
|
|
|
Array.prototype.splice.apply(
|
|
|
|
|
self.textLines,
|
|
|
|
|
[ index, (nextEolIndex - index) + 1 ].concat(newLines));
|
|
|
|
|
|
|
|
|
|
// redraw from current row to end of visible area
|
|
|
|
|
self.redrawRows(self.cursorPos.row, self.dimens.height);
|
|
|
|
|
|
|
|
|
|
if(!_.isUndefined(cursorOffset)) {
|
2015-06-15 03:19:25 +00:00
|
|
|
|
console.log('cursorOffset=' + cursorOffset)
|
2015-06-14 03:49:12 +00:00
|
|
|
|
self.cursorBeginOfNextLine();
|
|
|
|
|
self.cursorPos.col += cursorOffset;
|
|
|
|
|
self.client.term.write(ansi.right(cursorOffset));
|
|
|
|
|
} else {
|
|
|
|
|
absPos = self.getAbsolutePosition(self.cursorPos.row, self.cursorPos.col);
|
2015-06-15 03:19:25 +00:00
|
|
|
|
console.log('absPos=' + JSON.stringify(absPos))
|
2015-06-14 03:49:12 +00:00
|
|
|
|
self.client.term.write(ansi.goto(absPos.row, absPos.col));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
//
|
|
|
|
|
// We must only redraw from col -> end of current visible line
|
|
|
|
|
//
|
|
|
|
|
absPos = self.getAbsolutePosition(self.cursorPos.row, self.cursorPos.col);
|
|
|
|
|
self.client.term.write(
|
|
|
|
|
ansi.hideCursor() +
|
|
|
|
|
self.getSGRFor('text') +
|
2015-06-15 03:19:25 +00:00
|
|
|
|
self.getRenderText(index).slice(self.cursorPos.col - c.length) +
|
2015-06-14 03:49:12 +00:00
|
|
|
|
ansi.goto(absPos.row, absPos.col) +
|
|
|
|
|
ansi.showCursor()
|
|
|
|
|
);
|
|
|
|
|
}
|
2015-06-07 01:34:55 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-06-06 06:33:59 +00:00
|
|
|
|
this.getRemainingTabWidth = function(col) {
|
|
|
|
|
if(!_.isNumber(col)) {
|
|
|
|
|
col = self.cursorPos.col;
|
|
|
|
|
}
|
|
|
|
|
return self.tabWidth - (col % self.tabWidth);
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-12 22:44:32 +00:00
|
|
|
|
this.calculateTabStops = function() {
|
2015-06-14 03:49:12 +00:00
|
|
|
|
self.tabStops = [ 0 ];
|
2015-06-12 22:44:32 +00:00
|
|
|
|
var col = 0;
|
|
|
|
|
while(col < self.dimens.width) {
|
|
|
|
|
col += self.getRemainingTabWidth(col);
|
|
|
|
|
self.tabStops.push(col);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-14 03:49:12 +00:00
|
|
|
|
this.getNextTabStop = function(col) {
|
|
|
|
|
var i = self.tabStops.length;
|
|
|
|
|
while(self.tabStops[--i] > col);
|
|
|
|
|
return self.tabStops[++i];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.getPrevTabStop = function(col) {
|
|
|
|
|
var i = self.tabStops.length;
|
|
|
|
|
while(self.tabStops[--i] >= col);
|
|
|
|
|
return self.tabStops[i];
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-01 23:10:27 +00:00
|
|
|
|
this.expandTab = function(col, expandChar) {
|
|
|
|
|
expandChar = expandChar || ' ';
|
2015-06-06 06:33:59 +00:00
|
|
|
|
return new Array(self.getRemainingTabWidth(col)).join(expandChar);
|
2015-06-01 23:10:27 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-06-15 03:19:25 +00:00
|
|
|
|
this.wordWrapSingleLine = function(s, tabHandling, width) {
|
|
|
|
|
tabHandling = tabHandling || 'expandTabs';
|
|
|
|
|
if(!_.isNumber(width)) {
|
|
|
|
|
width = self.dimens.width;
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-01 23:10:27 +00:00
|
|
|
|
//
|
|
|
|
|
// Notes
|
|
|
|
|
// * Sublime Text 3 for example considers spaces after a word
|
|
|
|
|
// part of said word. For example, "word " would be wraped
|
|
|
|
|
// in it's entirity.
|
|
|
|
|
//
|
|
|
|
|
// * Tabs in Sublime Text 3 are also treated as a word, so, e.g.
|
|
|
|
|
// "\t" may resolve to " " and must fit within the space.
|
|
|
|
|
//
|
2015-06-06 06:33:59 +00:00
|
|
|
|
// * If a word is ultimately too long to fit, break it up until it does.
|
|
|
|
|
//
|
|
|
|
|
// RegExp below is JavaScript '\s' minus the '\t'
|
|
|
|
|
//
|
2015-06-02 05:00:54 +00:00
|
|
|
|
var re = new RegExp(
|
2015-06-02 22:36:55 +00:00
|
|
|
|
'\t|[ \f\n\r\v\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006' +
|
2015-06-11 03:49:18 +00:00
|
|
|
|
'\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]', 'g');
|
2015-06-01 23:10:27 +00:00
|
|
|
|
var m;
|
2015-06-11 03:49:18 +00:00
|
|
|
|
var wordStart = 0;
|
|
|
|
|
var results = { wrapped : [ '' ] };
|
2015-06-01 23:10:27 +00:00
|
|
|
|
var i = 0;
|
|
|
|
|
var word;
|
|
|
|
|
|
|
|
|
|
function addWord() {
|
2015-06-15 03:19:25 +00:00
|
|
|
|
word.match(new RegExp('.{0,' + width + '}', 'g')).forEach(function wrd(w) {
|
|
|
|
|
if(results.wrapped[i].length + w.length >= width) {
|
2015-06-10 22:12:21 +00:00
|
|
|
|
if(0 === i) {
|
2015-06-11 03:49:18 +00:00
|
|
|
|
results.firstWrapRange = { start : wordStart, end : wordStart + w.length };
|
2015-06-10 22:12:21 +00:00
|
|
|
|
}
|
2015-06-11 03:49:18 +00:00
|
|
|
|
results.wrapped[++i] = w;
|
2015-06-06 06:33:59 +00:00
|
|
|
|
} else {
|
2015-06-11 03:49:18 +00:00
|
|
|
|
results.wrapped[i] += w;
|
2015-06-06 06:33:59 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
2015-06-01 23:10:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-06-11 03:49:18 +00:00
|
|
|
|
while((m = re.exec(s)) !== null) {
|
|
|
|
|
word = s.substring(wordStart, re.lastIndex - 1);
|
|
|
|
|
|
|
|
|
|
switch(m[0].charAt(0)) {
|
|
|
|
|
case ' ' :
|
|
|
|
|
word += m[0];
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '\t' :
|
|
|
|
|
//
|
|
|
|
|
// Expand tab given position
|
|
|
|
|
//
|
|
|
|
|
// Nice info here: http://c-for-dummies.com/blog/?p=424
|
|
|
|
|
//
|
2015-06-15 03:19:25 +00:00
|
|
|
|
if('expandTabs' === tabHandling) {
|
|
|
|
|
word += self.expandTab(results.wrapped[i].length + word.length, '\t') + '\t';
|
|
|
|
|
} else {
|
|
|
|
|
word += m[0];
|
|
|
|
|
}
|
2015-06-11 03:49:18 +00:00
|
|
|
|
break;
|
2015-06-01 23:10:27 +00:00
|
|
|
|
}
|
2015-06-11 03:49:18 +00:00
|
|
|
|
|
|
|
|
|
addWord();
|
|
|
|
|
wordStart = re.lastIndex + m[0].length - 1;
|
|
|
|
|
}
|
2015-06-01 23:10:27 +00:00
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Remainder
|
|
|
|
|
//
|
|
|
|
|
word = s.substring(wordStart);
|
|
|
|
|
addWord();
|
|
|
|
|
|
2015-06-11 03:49:18 +00:00
|
|
|
|
return results;
|
2015-06-01 23:10:27 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-06-15 03:19:25 +00:00
|
|
|
|
// :TODO: rename to insertRawText()
|
|
|
|
|
this.insertRawText = function(text, index, col) {
|
2015-06-01 23:10:27 +00:00
|
|
|
|
//
|
|
|
|
|
// Perform the following on |text|:
|
|
|
|
|
// * Normalize various line feed formats -> \n
|
|
|
|
|
// * Remove some control characters (e.g. \b)
|
|
|
|
|
// * Word wrap lines such that they fit in the visible workspace.
|
|
|
|
|
// Each actual line will then take 1:n elements in textLines[].
|
|
|
|
|
// * Each tab will be appropriately expanded and take 1:n \t
|
|
|
|
|
// characters. This allows us to know when we're in tab space
|
|
|
|
|
// when doing cursor movement/etc.
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// Try to handle any possible newline that can be fed to us.
|
|
|
|
|
// See http://stackoverflow.com/questions/5034781/js-regex-to-split-by-line
|
|
|
|
|
//
|
2015-06-03 04:18:00 +00:00
|
|
|
|
// :TODO: support index/col insertion point
|
2015-06-02 05:00:54 +00:00
|
|
|
|
|
2015-06-03 04:18:00 +00:00
|
|
|
|
if(_.isNumber(index)) {
|
2015-06-02 05:00:54 +00:00
|
|
|
|
if(_.isNumber(col)) {
|
|
|
|
|
//
|
2015-06-03 04:18:00 +00:00
|
|
|
|
// Modify text to have information from index
|
2015-06-02 05:00:54 +00:00
|
|
|
|
// before and and after column
|
|
|
|
|
//
|
|
|
|
|
// :TODO: Need to clean this string (e.g. collapse tabs)
|
|
|
|
|
text = self.textLines
|
|
|
|
|
|
2015-06-03 04:18:00 +00:00
|
|
|
|
// :TODO: Remove original line @ index
|
2015-06-02 05:00:54 +00:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2015-06-03 04:18:00 +00:00
|
|
|
|
index = self.textLines.length;
|
2015-06-02 05:00:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-06-07 01:34:55 +00:00
|
|
|
|
text = text
|
2015-06-01 23:10:27 +00:00
|
|
|
|
.replace(/\b/g, '')
|
|
|
|
|
.split(/\r\n|[\n\v\f\r\x85\u2028\u2029]/g);
|
|
|
|
|
|
|
|
|
|
var wrapped;
|
2015-06-02 05:00:54 +00:00
|
|
|
|
|
2015-06-07 01:34:55 +00:00
|
|
|
|
for(var i = 0; i < text.length; ++i) {
|
2015-06-15 03:19:25 +00:00
|
|
|
|
wrapped = self.wordWrapSingleLine(text[i], 'expandTabs', self.dimens.width).wrapped;
|
2015-06-02 05:00:54 +00:00
|
|
|
|
|
|
|
|
|
for(var j = 0; j < wrapped.length - 1; ++j) {
|
2015-06-03 04:18:00 +00:00
|
|
|
|
self.textLines.splice(index++, 0, { text : wrapped[j] } );
|
2015-06-02 05:00:54 +00:00
|
|
|
|
}
|
2015-06-03 04:18:00 +00:00
|
|
|
|
self.textLines.splice(index++, 0, { text : wrapped[wrapped.length - 1], eol : true });
|
2015-06-01 23:10:27 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
2015-06-02 05:00:54 +00:00
|
|
|
|
|
2015-06-04 23:06:37 +00:00
|
|
|
|
this.getAbsolutePosition = function(row, col) {
|
2015-06-07 06:04:09 +00:00
|
|
|
|
return {
|
|
|
|
|
row : self.position.row + row,
|
|
|
|
|
col : self.position.col + col,
|
|
|
|
|
};
|
2015-06-04 23:06:37 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.moveClientCusorToCursorPos = function() {
|
|
|
|
|
var absPos = self.getAbsolutePosition(self.cursorPos.row, self.cursorPos.col);
|
|
|
|
|
self.client.term.write(ansi.goto(absPos.row, absPos.col));
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-14 03:49:12 +00:00
|
|
|
|
|
2015-06-07 01:34:55 +00:00
|
|
|
|
this.keyPressCharacter = function(c) {
|
2015-06-08 05:45:06 +00:00
|
|
|
|
var index = self.getTextLinesIndex();
|
2015-06-04 04:00:33 +00:00
|
|
|
|
|
2015-06-07 01:34:55 +00:00
|
|
|
|
//
|
|
|
|
|
// :TODO: stuff that needs to happen
|
|
|
|
|
// * Break up into smaller methods
|
|
|
|
|
// * Even in overtype mode, word wrapping must apply if past bounds
|
|
|
|
|
// * A lot of this can be used for backspacing also
|
|
|
|
|
// * See how Sublime treats tabs in *non* overtype mode... just overwrite them?
|
|
|
|
|
//
|
2015-06-12 03:05:52 +00:00
|
|
|
|
//
|
2015-06-04 04:00:33 +00:00
|
|
|
|
|
|
|
|
|
if(self.overtypeMode) {
|
|
|
|
|
// :TODO: special handing for insert over eol mark?
|
2015-06-07 01:34:55 +00:00
|
|
|
|
self.replaceCharacterInText(c, index, self.cursorPos.col);
|
2015-06-04 04:00:33 +00:00
|
|
|
|
self.cursorPos.col++;
|
|
|
|
|
self.client.term.write(c);
|
|
|
|
|
} else {
|
2015-06-15 03:19:25 +00:00
|
|
|
|
self.insertCharactersInText(c, index, self.cursorPos.col);
|
2015-06-07 06:04:09 +00:00
|
|
|
|
|
2015-06-12 03:05:52 +00:00
|
|
|
|
/*if(self.cursorPos.col >= self.dimens.width) {
|
2015-06-08 03:32:47 +00:00
|
|
|
|
console.log('next line')
|
|
|
|
|
self.cursorBeginOfNextLine();
|
2015-06-08 06:03:09 +00:00
|
|
|
|
//self.client.term.write(ansi.right(cursorOffset))
|
2015-06-12 03:05:52 +00:00
|
|
|
|
}*/
|
2015-06-04 04:00:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-04 23:06:37 +00:00
|
|
|
|
this.keyPressUp = function() {
|
2015-06-03 04:18:00 +00:00
|
|
|
|
if(self.cursorPos.row > 0) {
|
|
|
|
|
self.cursorPos.row--;
|
|
|
|
|
self.client.term.write(ansi.up());
|
|
|
|
|
|
2015-06-14 03:49:12 +00:00
|
|
|
|
if(!self.adjustCursorToNextTab('up')) {
|
|
|
|
|
self.adjustCursorIfPastEndOfLine(false);
|
|
|
|
|
}
|
2015-06-04 04:00:33 +00:00
|
|
|
|
} else {
|
|
|
|
|
self.scrollDocumentDown();
|
|
|
|
|
self.adjustCursorIfPastEndOfLine(true);
|
2015-06-03 04:18:00 +00:00
|
|
|
|
}
|
2015-06-02 05:00:54 +00:00
|
|
|
|
};
|
2015-06-02 22:36:55 +00:00
|
|
|
|
|
2015-06-04 23:06:37 +00:00
|
|
|
|
this.keyPressDown = function() {
|
2015-06-03 23:09:03 +00:00
|
|
|
|
var lastVisibleRow = Math.min(self.dimens.height, self.textLines.length) - 1;
|
|
|
|
|
if(self.cursorPos.row < lastVisibleRow) {
|
|
|
|
|
self.cursorPos.row++;
|
|
|
|
|
self.client.term.write(ansi.down());
|
|
|
|
|
|
2015-06-14 03:49:12 +00:00
|
|
|
|
if(!self.adjustCursorToNextTab('down')) {
|
|
|
|
|
self.adjustCursorIfPastEndOfLine(false);
|
|
|
|
|
}
|
2015-06-03 23:09:03 +00:00
|
|
|
|
} else {
|
2015-06-04 04:00:33 +00:00
|
|
|
|
self.scrollDocumentUp();
|
|
|
|
|
self.adjustCursorIfPastEndOfLine(true);
|
2015-06-03 23:09:03 +00:00
|
|
|
|
}
|
2015-06-02 22:36:55 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-06-04 23:06:37 +00:00
|
|
|
|
this.keyPressLeft = function() {
|
2015-06-03 04:18:00 +00:00
|
|
|
|
if(self.cursorPos.col > 0) {
|
2015-06-12 05:09:13 +00:00
|
|
|
|
var prevChar = self.getCharacter();
|
|
|
|
|
|
2015-06-03 04:18:00 +00:00
|
|
|
|
self.cursorPos.col--;
|
|
|
|
|
self.client.term.write(ansi.left());
|
2015-06-12 05:09:13 +00:00
|
|
|
|
|
|
|
|
|
if('\t' === prevChar) {
|
|
|
|
|
self.adjustCursorToNextTab('left');
|
|
|
|
|
}
|
2015-06-03 04:18:00 +00:00
|
|
|
|
} else {
|
2015-06-06 06:33:59 +00:00
|
|
|
|
self.cursorEndOfPreviousLine();
|
2015-06-03 04:18:00 +00:00
|
|
|
|
}
|
2015-06-02 22:36:55 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-06-04 23:06:37 +00:00
|
|
|
|
this.keyPressRight = function() {
|
2015-06-03 23:09:03 +00:00
|
|
|
|
var eolColumn = self.getTextEndOfLineColumn();
|
|
|
|
|
if(self.cursorPos.col < eolColumn) {
|
2015-06-12 05:09:13 +00:00
|
|
|
|
var prevChar = self.getCharacter();
|
|
|
|
|
|
2015-06-02 22:36:55 +00:00
|
|
|
|
self.cursorPos.col++;
|
|
|
|
|
self.client.term.write(ansi.right());
|
2015-06-03 04:18:00 +00:00
|
|
|
|
|
2015-06-12 05:09:13 +00:00
|
|
|
|
if('\t' === prevChar) {
|
|
|
|
|
self.adjustCursorToNextTab('right');
|
|
|
|
|
}
|
2015-06-02 22:36:55 +00:00
|
|
|
|
} else {
|
2015-06-06 06:33:59 +00:00
|
|
|
|
self.cursorBeginOfNextLine();
|
2015-06-02 22:36:55 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-04 23:06:37 +00:00
|
|
|
|
this.keyPressHome = function() {
|
2015-06-03 04:18:00 +00:00
|
|
|
|
var firstNonWhitespace = self.getVisibleText().search(/\S/);
|
|
|
|
|
if(-1 !== firstNonWhitespace) {
|
|
|
|
|
self.cursorPos.col = firstNonWhitespace;
|
|
|
|
|
} else {
|
|
|
|
|
self.cursorPos.col = 0;
|
|
|
|
|
}
|
2015-06-09 04:41:57 +00:00
|
|
|
|
console.log('"' + self.getVisibleText() + '"')
|
2015-06-03 04:18:00 +00:00
|
|
|
|
self.moveClientCusorToCursorPos();
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-04 23:06:37 +00:00
|
|
|
|
this.keyPressEnd = function() {
|
2015-06-03 23:09:03 +00:00
|
|
|
|
self.cursorPos.col = self.getTextEndOfLineColumn();
|
2015-06-03 04:18:00 +00:00
|
|
|
|
self.moveClientCusorToCursorPos();
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-04 23:06:37 +00:00
|
|
|
|
this.keyPressPageUp = function() {
|
2015-06-04 04:00:33 +00:00
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-04 23:06:37 +00:00
|
|
|
|
this.keyPressPageDown = function() {
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.keyPressLineFeed = function() {
|
2015-06-14 03:49:12 +00:00
|
|
|
|
//
|
|
|
|
|
// Break up text from cursor position, redraw, and update cursor
|
|
|
|
|
// position to start of next line
|
|
|
|
|
//
|
|
|
|
|
var index = self.getTextLinesIndex();
|
|
|
|
|
var nextEolIndex = self.getNextEndOfLineIndex(index);
|
2015-06-15 05:18:21 +00:00
|
|
|
|
var text = self.getContiguousText(index, nextEolIndex);
|
|
|
|
|
var newLines = self.wordWrapSingleLine(text.slice(self.cursorPos.col), 'tabsIntact').wrapped;
|
2015-06-14 03:49:12 +00:00
|
|
|
|
|
|
|
|
|
newLines.unshift( { text : text.slice(0, self.cursorPos.col), eol : true } );
|
|
|
|
|
for(var i = 1; i < newLines.length; ++i) {
|
|
|
|
|
newLines[i] = { text : newLines[i] };
|
|
|
|
|
}
|
|
|
|
|
newLines[newLines.length - 1].eol = true;
|
|
|
|
|
|
|
|
|
|
Array.prototype.splice.apply(
|
|
|
|
|
self.textLines,
|
|
|
|
|
[ index, (nextEolIndex - index) + 1 ].concat(newLines));
|
2015-06-04 04:00:33 +00:00
|
|
|
|
|
2015-06-14 03:49:12 +00:00
|
|
|
|
// redraw from current row to end of visible area
|
|
|
|
|
self.redrawRows(self.cursorPos.row, self.dimens.height);
|
|
|
|
|
self.cursorBeginOfNextLine();
|
2015-06-04 04:00:33 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-06-07 01:34:55 +00:00
|
|
|
|
this.keyPressInsert = function() {
|
|
|
|
|
// :TODO: emit event
|
|
|
|
|
self.overtypeMode = !self.overtypeMode;
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-14 03:49:12 +00:00
|
|
|
|
this.keyPressTab = function() {
|
|
|
|
|
var index = self.getTextLinesIndex();
|
2015-06-15 03:19:25 +00:00
|
|
|
|
self.insertCharactersInText(self.expandTab(self.cursorPos.col, '\t') + '\t', index, self.cursorPos.col);
|
2015-06-14 03:49:12 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.keyPressBackspace = function() {
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.keyPressDel = function() {
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-06 06:33:59 +00:00
|
|
|
|
this.adjustCursorIfPastEndOfLine = function(forceUpdate) {
|
2015-06-03 23:09:03 +00:00
|
|
|
|
var eolColumn = self.getTextEndOfLineColumn();
|
|
|
|
|
if(self.cursorPos.col > eolColumn) {
|
|
|
|
|
self.cursorPos.col = eolColumn;
|
2015-06-06 06:33:59 +00:00
|
|
|
|
forceUpdate = true;
|
2015-06-04 04:00:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-06-06 06:33:59 +00:00
|
|
|
|
if(forceUpdate) {
|
2015-06-03 23:09:03 +00:00
|
|
|
|
self.moveClientCusorToCursorPos();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-06 06:33:59 +00:00
|
|
|
|
this.adjustCursorToNextTab = function(direction) {
|
2015-06-14 03:49:12 +00:00
|
|
|
|
if('\t' === self.getCharacter()) {
|
2015-06-06 06:33:59 +00:00
|
|
|
|
//
|
|
|
|
|
// When pressing right or left, jump to the next
|
|
|
|
|
// tabstop in that direction.
|
|
|
|
|
//
|
2015-06-12 22:44:32 +00:00
|
|
|
|
var move;
|
2015-06-12 05:09:13 +00:00
|
|
|
|
switch(direction) {
|
|
|
|
|
case 'right' :
|
2015-06-14 03:49:12 +00:00
|
|
|
|
move = self.getNextTabStop(self.cursorPos.col) - self.cursorPos.col;
|
2015-06-12 05:09:13 +00:00
|
|
|
|
self.cursorPos.col += move;
|
|
|
|
|
self.client.term.write(ansi.right(move));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'left' :
|
2015-06-14 03:49:12 +00:00
|
|
|
|
move = self.cursorPos.col - self.getPrevTabStop(self.cursorPos.col);
|
2015-06-12 05:09:13 +00:00
|
|
|
|
self.cursorPos.col -= move;
|
|
|
|
|
self.client.term.write(ansi.left(move));
|
|
|
|
|
break;
|
2015-06-14 03:49:12 +00:00
|
|
|
|
|
|
|
|
|
case 'up' :
|
|
|
|
|
case 'down' :
|
|
|
|
|
//
|
|
|
|
|
// To nearest tabstop
|
|
|
|
|
//
|
|
|
|
|
var newCol = self.tabStops.reduce(function r(prev, curr) {
|
|
|
|
|
return (Math.abs(curr - self.cursorPos.col) < Math.abs(prev - self.cursorPos.col) ? curr : prev);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if(newCol > self.cursorPos.col) {
|
|
|
|
|
move = newCol - self.cursorPos.col;
|
|
|
|
|
self.cursorPos.col += move;
|
|
|
|
|
self.client.term.write(ansi.right(move));
|
|
|
|
|
} else if(newCol < self.cursorPos.col) {
|
|
|
|
|
move = self.cursorPos.col - newCol;
|
|
|
|
|
self.cursorPos.col -= move;
|
|
|
|
|
self.client.term.write(ansi.left(move));
|
|
|
|
|
}
|
|
|
|
|
break;
|
2015-06-06 06:33:59 +00:00
|
|
|
|
}
|
2015-06-14 03:49:12 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
2015-06-06 06:33:59 +00:00
|
|
|
|
}
|
2015-06-14 03:49:12 +00:00
|
|
|
|
return false; // did not fall on a tab
|
2015-06-06 06:33:59 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-06-03 23:09:03 +00:00
|
|
|
|
this.cursorStartOfDocument = function() {
|
2015-06-02 22:36:55 +00:00
|
|
|
|
self.topVisibleIndex = 0;
|
|
|
|
|
self.cursorPos = { row : 0, col : 0 };
|
|
|
|
|
|
|
|
|
|
self.redraw();
|
|
|
|
|
self.moveClientCusorToCursorPos();
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-03 23:09:03 +00:00
|
|
|
|
this.cursorEndOfDocument = function() {
|
2015-06-02 22:36:55 +00:00
|
|
|
|
self.topVisibleIndex = Math.max(self.textLines.length - self.dimens.height, 0);
|
2015-06-03 04:18:00 +00:00
|
|
|
|
self.cursorPos.row = (self.textLines.length - self.topVisibleIndex) - 1;
|
2015-06-03 23:09:03 +00:00
|
|
|
|
self.cursorPos.col = self.getTextEndOfLineColumn();
|
2015-06-02 22:36:55 +00:00
|
|
|
|
|
|
|
|
|
self.redraw();
|
|
|
|
|
self.moveClientCusorToCursorPos();
|
|
|
|
|
};
|
2015-06-03 04:18:00 +00:00
|
|
|
|
|
2015-06-06 06:33:59 +00:00
|
|
|
|
this.cursorBeginOfNextLine = function() {
|
|
|
|
|
// e.g. when scrolling right past eol
|
|
|
|
|
var linesBelow = self.getRemainingLinesBelowRow();
|
|
|
|
|
|
|
|
|
|
if(linesBelow > 0) {
|
|
|
|
|
var lastVisibleRow = Math.min(self.dimens.height, self.textLines.length) - 1;
|
|
|
|
|
if(self.cursorPos.row < lastVisibleRow) {
|
|
|
|
|
self.cursorPos.row++;
|
|
|
|
|
} else {
|
|
|
|
|
self.scrollDocumentUp();
|
|
|
|
|
}
|
|
|
|
|
self.keyPressHome(); // same as pressing 'home'
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.cursorEndOfPreviousLine = function() {
|
2015-06-07 01:34:55 +00:00
|
|
|
|
// e.g. when scrolling left past start of line
|
|
|
|
|
var moveToEnd;
|
|
|
|
|
if(self.cursorPos.row > 0) {
|
|
|
|
|
self.cursorPos.row--;
|
|
|
|
|
moveToEnd = true;
|
|
|
|
|
} else if(self.topVisibleIndex > 0) {
|
|
|
|
|
self.scrollDocumentDown();
|
|
|
|
|
moveToEnd = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(moveToEnd) {
|
2015-06-06 06:33:59 +00:00
|
|
|
|
self.keyPressEnd(); // same as pressing 'end'
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-14 03:49:12 +00:00
|
|
|
|
/*
|
|
|
|
|
this.cusorEndOfNextLine = function() {
|
|
|
|
|
var linesBelow = self.getRemainingLinesBelowRow();
|
|
|
|
|
|
|
|
|
|
if(linesBelow > 0) {
|
|
|
|
|
var lastVisibleRow = Math.min(self.dimens.height, self.textLines.length) - 1;
|
|
|
|
|
if(self.cursorPos.row < lastVisibleRow) {
|
|
|
|
|
self.cursorPos.row++;
|
|
|
|
|
} else {
|
|
|
|
|
self.scrollDocumentUp();
|
|
|
|
|
}
|
|
|
|
|
self.keyPressEnd(); // same as pressing 'end'
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
*/
|
|
|
|
|
|
2015-06-03 23:09:03 +00:00
|
|
|
|
this.scrollDocumentUp = function() {
|
|
|
|
|
//
|
|
|
|
|
// Note: We scroll *up* when the cursor goes *down* beyond
|
|
|
|
|
// the visible area!
|
|
|
|
|
//
|
2015-06-06 06:33:59 +00:00
|
|
|
|
var linesBelow = self.getRemainingLinesBelowRow();
|
2015-06-03 23:09:03 +00:00
|
|
|
|
if(linesBelow > 0) {
|
2015-06-04 04:00:33 +00:00
|
|
|
|
self.topVisibleIndex++;
|
|
|
|
|
self.redraw();
|
2015-06-03 23:09:03 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.scrollDocumentDown = function() {
|
|
|
|
|
//
|
|
|
|
|
// Note: We scroll *down* when the cursor goes *up* beyond
|
|
|
|
|
// the visible area!
|
2015-06-04 04:00:33 +00:00
|
|
|
|
//
|
|
|
|
|
if(self.topVisibleIndex > 0) {
|
|
|
|
|
self.topVisibleIndex--;
|
|
|
|
|
self.redraw();
|
|
|
|
|
}
|
2015-06-03 23:09:03 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-06-01 03:18:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
require('util').inherits(MultiLineEditTextView2, View);
|
|
|
|
|
|
2015-06-15 03:19:25 +00:00
|
|
|
|
MultiLineEditTextView2.prototype.setWidth = function(width) {
|
|
|
|
|
MultiLineEditTextView2.super_.prototype.setWidth.call(this, width);
|
|
|
|
|
|
|
|
|
|
this.calculateTabStops();
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-01 03:18:23 +00:00
|
|
|
|
MultiLineEditTextView2.prototype.redraw = function() {
|
|
|
|
|
MultiLineEditTextView2.super_.prototype.redraw.call(this);
|
|
|
|
|
|
|
|
|
|
this.redrawVisibleArea();
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-08 22:51:27 +00:00
|
|
|
|
MultiLineEditTextView2.prototype.setFocus = function(focused) {
|
|
|
|
|
this.client.term.write(this.getSGRFor('text'));
|
|
|
|
|
|
|
|
|
|
MultiLineEditTextView2.super_.prototype.setFocus.call(this, focused);
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-01 03:18:23 +00:00
|
|
|
|
MultiLineEditTextView2.prototype.setText = function(text) {
|
2015-06-07 01:34:55 +00:00
|
|
|
|
this.textLines = [ ];
|
|
|
|
|
//text = "Tab:\r\n\tA\tB\tC\tD\tE\tF\tG\r\n reeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeally long word!!!";
|
2015-06-14 03:49:12 +00:00
|
|
|
|
text = require('fs').readFileSync('/home/nuskooler/Downloads/test_text.txt', { encoding : 'utf-8'});
|
2015-06-07 01:34:55 +00:00
|
|
|
|
|
2015-06-15 03:19:25 +00:00
|
|
|
|
this.insertRawText(text);//, 0, 0);
|
2015-06-03 23:09:03 +00:00
|
|
|
|
this.cursorEndOfDocument();
|
2015-06-12 22:44:32 +00:00
|
|
|
|
console.log(this.textLines)
|
2015-06-15 03:19:25 +00:00
|
|
|
|
|
|
|
|
|
|
2015-06-02 05:00:54 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-06-05 22:20:26 +00:00
|
|
|
|
var HANDLED_SPECIAL_KEYS = [
|
|
|
|
|
'up', 'down', 'left', 'right',
|
|
|
|
|
'home', 'end',
|
|
|
|
|
'pageUp', 'pageDown',
|
|
|
|
|
'lineFeed',
|
2015-06-07 01:34:55 +00:00
|
|
|
|
'insert',
|
2015-06-14 03:49:12 +00:00
|
|
|
|
'tab',
|
|
|
|
|
'backspace', 'del',
|
2015-06-05 22:20:26 +00:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
MultiLineEditTextView2.prototype.onKeyPress = function(ch, key) {
|
|
|
|
|
var self = this;
|
|
|
|
|
var handled;
|
|
|
|
|
|
|
|
|
|
if(key) {
|
|
|
|
|
HANDLED_SPECIAL_KEYS.forEach(function aKey(specialKey) {
|
|
|
|
|
if(self.isSpecialKeyMapped(specialKey, key.name)) {
|
|
|
|
|
self[_.camelCase('keyPress ' + specialKey)]();
|
|
|
|
|
handled = true;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(ch && strUtil.isPrintable(ch)) {
|
|
|
|
|
this.keyPressCharacter(ch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!handled) {
|
2015-06-05 22:37:17 +00:00
|
|
|
|
MultiLineEditTextView2.super_.prototype.onKeyPress.call(this, ch, key);
|
2015-06-05 22:20:26 +00:00
|
|
|
|
}
|
|
|
|
|
};
|