* Fix line feed losing a character

* Fix wrapping when tabs are involved
This commit is contained in:
Bryan Ashby 2015-06-14 21:19:25 -06:00
parent 8a75dbc91f
commit 2632c150ac
2 changed files with 95 additions and 33 deletions

View File

@ -187,20 +187,25 @@ function MultiLineEditTextView2(options) {
return text; return text;
}; };
this.getOutputText = function(startIndex, endIndex, includeEol) { this.getTextLines = function(startIndex, endIndex) {
var lines; var lines;
if(startIndex === endIndex) { if(startIndex === endIndex) {
lines = [ self.textLines[startIndex] ]; lines = [ self.textLines[startIndex] ];
} else { } else {
lines = self.textLines.slice(startIndex, endIndex + 1); // "slice extracts up to but not including end." lines = self.textLines.slice(startIndex, endIndex + 1); // "slice extracts up to but not including end."
} }
return lines;
}
this.getOutputText = function(startIndex, endIndex, includeEol) {
var lines = self.getTextLines(startIndex, endIndex);
// //
// Convert lines to contiguous string -- all expanded // Convert lines to contiguous string -- all expanded
// tabs put back to single '\t' characters. // tabs put back to single '\t' characters.
// //
var text = ''; var text = '';
var re = new RegExp('\\t{' + (self.tabWidth - 1) + '}', 'g'); var re = new RegExp('\\t{1,' + (self.tabWidth) + '}', 'g');
for(var i = 0; i < lines.length; ++i) { for(var i = 0; i < lines.length; ++i) {
text += lines[i].text.replace(re, '\t'); text += lines[i].text.replace(re, '\t');
if(includeEol && lines[i].eol) { if(includeEol && lines[i].eol) {
@ -210,19 +215,49 @@ function MultiLineEditTextView2(options) {
return text; return text;
}; };
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;
};
this.replaceCharacterInText = function(c, index, col) { this.replaceCharacterInText = function(c, index, col) {
self.textLines[index].text = strUtil.replaceAt( self.textLines[index].text = strUtil.replaceAt(
self.textLines[index].text, col, c); self.textLines[index].text, col, c);
}; };
this.insertCharacterInText = function(c, index, col) { 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;
}
};
this.insertCharactersInText = function(c, index, col) {
self.textLines[index].text = [ self.textLines[index].text = [
self.textLines[index].text.slice(0, col), self.textLines[index].text.slice(0, col),
c, c,
self.textLines[index].text.slice(col) self.textLines[index].text.slice(col)
].join(''); ].join('');
self.cursorPos.col++; //self.cursorPos.col++;
self.cursorPos.col += c.length;
var cursorOffset; var cursorOffset;
var absPos; var absPos;
@ -234,15 +269,24 @@ function MultiLineEditTextView2(options) {
// formatted array. // formatted array.
// //
var nextEolIndex = self.getNextEndOfLineIndex(index); var nextEolIndex = self.getNextEndOfLineIndex(index);
var wrapped = self.wordWrapSingleLine(self.getOutputText(index, nextEolIndex)); var wrapped = self.wordWrapSingleLine(self.getContiguousText(index, nextEolIndex), 'tabsIntact')
var newLines = wrapped.wrapped; var newLines = wrapped.wrapped;
/*
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)))
*/
// //
// If our cursor was within the bounds of the last wrapped word // If our cursor was within the bounds of the last wrapped word
// we'll want to adjust the cursor to the same relative position // we'll want to adjust the cursor to the same relative position
// on the next line. // on the next line.
// //
var lastCol = self.cursorPos.col - 1; //var lastCol = self.cursorPos.col - 1;
var lastCol = self.cursorPos.col - c.length;
if(lastCol >= wrapped.firstWrapRange.start && lastCol <= wrapped.firstWrapRange.end) { if(lastCol >= wrapped.firstWrapRange.start && lastCol <= wrapped.firstWrapRange.end) {
cursorOffset = self.cursorPos.col - wrapped.firstWrapRange.start; cursorOffset = self.cursorPos.col - wrapped.firstWrapRange.start;
} }
@ -251,7 +295,7 @@ function MultiLineEditTextView2(options) {
newLines[i] = { text : newLines[i] }; newLines[i] = { text : newLines[i] };
} }
newLines[newLines.length - 1].eol = true; newLines[newLines.length - 1].eol = true;
Array.prototype.splice.apply( Array.prototype.splice.apply(
self.textLines, self.textLines,
[ index, (nextEolIndex - index) + 1 ].concat(newLines)); [ index, (nextEolIndex - index) + 1 ].concat(newLines));
@ -260,11 +304,13 @@ function MultiLineEditTextView2(options) {
self.redrawRows(self.cursorPos.row, self.dimens.height); self.redrawRows(self.cursorPos.row, self.dimens.height);
if(!_.isUndefined(cursorOffset)) { if(!_.isUndefined(cursorOffset)) {
console.log('cursorOffset=' + cursorOffset)
self.cursorBeginOfNextLine(); self.cursorBeginOfNextLine();
self.cursorPos.col += cursorOffset; self.cursorPos.col += cursorOffset;
self.client.term.write(ansi.right(cursorOffset)); self.client.term.write(ansi.right(cursorOffset));
} else { } else {
absPos = self.getAbsolutePosition(self.cursorPos.row, self.cursorPos.col); absPos = self.getAbsolutePosition(self.cursorPos.row, self.cursorPos.col);
console.log('absPos=' + JSON.stringify(absPos))
self.client.term.write(ansi.goto(absPos.row, absPos.col)); self.client.term.write(ansi.goto(absPos.row, absPos.col));
} }
} else { } else {
@ -275,7 +321,7 @@ function MultiLineEditTextView2(options) {
self.client.term.write( self.client.term.write(
ansi.hideCursor() + ansi.hideCursor() +
self.getSGRFor('text') + self.getSGRFor('text') +
self.getRenderText(index).slice(self.cursorPos.col - 1) + self.getRenderText(index).slice(self.cursorPos.col - c.length) +
ansi.goto(absPos.row, absPos.col) + ansi.goto(absPos.row, absPos.col) +
ansi.showCursor() ansi.showCursor()
); );
@ -290,19 +336,12 @@ function MultiLineEditTextView2(options) {
}; };
this.calculateTabStops = function() { this.calculateTabStops = function() {
//
// :TODO: A system like this may be better for tabs:
// 1) Calculate tab stops
// 2) On movement/etc.: find next/prev or closest for up/down
//
// http://stackoverflow.com/questions/8584902/get-closest-number-out-of-array
self.tabStops = [ 0 ]; self.tabStops = [ 0 ];
var col = 0; var col = 0;
while(col < self.dimens.width) { while(col < self.dimens.width) {
col += self.getRemainingTabWidth(col); col += self.getRemainingTabWidth(col);
self.tabStops.push(col); self.tabStops.push(col);
} }
console.log(self.tabStops)
}; };
this.getNextTabStop = function(col) { this.getNextTabStop = function(col) {
@ -322,7 +361,12 @@ function MultiLineEditTextView2(options) {
return new Array(self.getRemainingTabWidth(col)).join(expandChar); return new Array(self.getRemainingTabWidth(col)).join(expandChar);
}; };
this.wordWrapSingleLine = function(s, width) { this.wordWrapSingleLine = function(s, tabHandling, width) {
tabHandling = tabHandling || 'expandTabs';
if(!_.isNumber(width)) {
width = self.dimens.width;
}
// //
// Notes // Notes
// * Sublime Text 3 for example considers spaces after a word // * Sublime Text 3 for example considers spaces after a word
@ -346,8 +390,8 @@ function MultiLineEditTextView2(options) {
var word; var word;
function addWord() { function addWord() {
word.match(new RegExp('.{0,' + self.dimens.width + '}', 'g')).forEach(function wrd(w) { word.match(new RegExp('.{0,' + width + '}', 'g')).forEach(function wrd(w) {
if(results.wrapped[i].length + w.length >= self.dimens.width) { if(results.wrapped[i].length + w.length >= width) {
if(0 === i) { if(0 === i) {
results.firstWrapRange = { start : wordStart, end : wordStart + w.length }; results.firstWrapRange = { start : wordStart, end : wordStart + w.length };
} }
@ -372,7 +416,11 @@ function MultiLineEditTextView2(options) {
// //
// Nice info here: http://c-for-dummies.com/blog/?p=424 // Nice info here: http://c-for-dummies.com/blog/?p=424
// //
word += self.expandTab(results.wrapped[i].length + word.length, '\t') + '\t'; if('expandTabs' === tabHandling) {
word += self.expandTab(results.wrapped[i].length + word.length, '\t') + '\t';
} else {
word += m[0];
}
break; break;
} }
@ -389,8 +437,8 @@ function MultiLineEditTextView2(options) {
return results; return results;
}; };
// :TODO: Change this to (text, row, col) & make proper adjustments // :TODO: rename to insertRawText()
this.insertText = function(text, index, col) { this.insertRawText = function(text, index, col) {
// //
// Perform the following on |text|: // Perform the following on |text|:
// * Normalize various line feed formats -> \n // * Normalize various line feed formats -> \n
@ -429,7 +477,7 @@ function MultiLineEditTextView2(options) {
var wrapped; var wrapped;
for(var i = 0; i < text.length; ++i) { for(var i = 0; i < text.length; ++i) {
wrapped = self.wordWrapSingleLine(text[i], self.dimens.width).wrapped; wrapped = self.wordWrapSingleLine(text[i], 'expandTabs', self.dimens.width).wrapped;
for(var j = 0; j < wrapped.length - 1; ++j) { for(var j = 0; j < wrapped.length - 1; ++j) {
self.textLines.splice(index++, 0, { text : wrapped[j] } ); self.textLines.splice(index++, 0, { text : wrapped[j] } );
@ -469,7 +517,7 @@ function MultiLineEditTextView2(options) {
self.cursorPos.col++; self.cursorPos.col++;
self.client.term.write(c); self.client.term.write(c);
} else { } else {
self.insertCharacterInText(c, index, self.cursorPos.col); self.insertCharactersInText(c, index, self.cursorPos.col);
/*if(self.cursorPos.col >= self.dimens.width) { /*if(self.cursorPos.col >= self.dimens.width) {
console.log('next line') console.log('next line')
@ -569,10 +617,11 @@ function MultiLineEditTextView2(options) {
// Break up text from cursor position, redraw, and update cursor // Break up text from cursor position, redraw, and update cursor
// position to start of next line // position to start of next line
// //
// :TODO: this needs converted to use the getContigousText() and such
var index = self.getTextLinesIndex(); var index = self.getTextLinesIndex();
var nextEolIndex = self.getNextEndOfLineIndex(index); var nextEolIndex = self.getNextEndOfLineIndex(index);
var text = self.getOutputText(index, nextEolIndex); var text = self.getOutputText(index, nextEolIndex);
var newLines = self.wordWrapSingleLine(text.slice(self.cursorPos.col + 1)).wrapped; var newLines = self.wordWrapSingleLine(text.slice(self.cursorPos.col)).wrapped;
newLines.unshift( { text : text.slice(0, self.cursorPos.col), eol : true } ); newLines.unshift( { text : text.slice(0, self.cursorPos.col), eol : true } );
for(var i = 1; i < newLines.length; ++i) { for(var i = 1; i < newLines.length; ++i) {
@ -595,8 +644,10 @@ function MultiLineEditTextView2(options) {
}; };
this.keyPressTab = function() { this.keyPressTab = function() {
// :TODO: Seems tabs are counted as words when wrapping... they should probably break @ nearest
// full tab if possible? Look into how Sublime handles this.
var index = self.getTextLinesIndex(); var index = self.getTextLinesIndex();
self.insertCharacterInText(self.expandTab(self.cursorPos.col, '\t'), index, self.cursorPos.col); self.insertCharactersInText(self.expandTab(self.cursorPos.col, '\t') + '\t', index, self.cursorPos.col);
}; };
this.keyPressBackspace = function() { this.keyPressBackspace = function() {
@ -647,7 +698,6 @@ function MultiLineEditTextView2(options) {
var newCol = self.tabStops.reduce(function r(prev, curr) { var newCol = self.tabStops.reduce(function r(prev, curr) {
return (Math.abs(curr - self.cursorPos.col) < Math.abs(prev - self.cursorPos.col) ? curr : prev); return (Math.abs(curr - self.cursorPos.col) < Math.abs(prev - self.cursorPos.col) ? curr : prev);
}); });
console.log('newCol=' + newCol)
if(newCol > self.cursorPos.col) { if(newCol > self.cursorPos.col) {
move = newCol - self.cursorPos.col; move = newCol - self.cursorPos.col;
@ -757,6 +807,12 @@ function MultiLineEditTextView2(options) {
require('util').inherits(MultiLineEditTextView2, View); require('util').inherits(MultiLineEditTextView2, View);
MultiLineEditTextView2.prototype.setWidth = function(width) {
MultiLineEditTextView2.super_.prototype.setWidth.call(this, width);
this.calculateTabStops();
};
MultiLineEditTextView2.prototype.redraw = function() { MultiLineEditTextView2.prototype.redraw = function() {
MultiLineEditTextView2.super_.prototype.redraw.call(this); MultiLineEditTextView2.super_.prototype.redraw.call(this);
@ -774,10 +830,11 @@ MultiLineEditTextView2.prototype.setText = function(text) {
//text = "Tab:\r\n\tA\tB\tC\tD\tE\tF\tG\r\n reeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeally long word!!!"; //text = "Tab:\r\n\tA\tB\tC\tD\tE\tF\tG\r\n reeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeally long word!!!";
text = require('fs').readFileSync('/home/nuskooler/Downloads/test_text.txt', { encoding : 'utf-8'}); text = require('fs').readFileSync('/home/nuskooler/Downloads/test_text.txt', { encoding : 'utf-8'});
this.insertText(text);//, 0, 0); this.insertRawText(text);//, 0, 0);
this.cursorEndOfDocument(); this.cursorEndOfDocument();
console.log(this.textLines) console.log(this.textLines)
console.log(this.calculateTabStops())
}; };
var HANDLED_SPECIAL_KEYS = [ var HANDLED_SPECIAL_KEYS = [

View File

@ -4,10 +4,11 @@
var miscUtil = require('./misc_util.js'); var miscUtil = require('./misc_util.js');
exports.stylizeString = stylizeString; exports.stylizeString = stylizeString;
exports.pad = pad; exports.pad = pad;
exports.replaceAt = replaceAt; exports.replaceAt = replaceAt;
exports.isPrintable = isPrintable; exports.isPrintable = isPrintable;
exports.debugEscapedString = debugEscapedString;
// :TODO: create Unicode verison of this // :TODO: create Unicode verison of this
var VOWELS = [ 'a', 'e', 'i', 'o', 'u' ]; var VOWELS = [ 'a', 'e', 'i', 'o', 'u' ];
@ -168,4 +169,8 @@ function isPrintable(s) {
function stringLength(s) { function stringLength(s) {
// :TODO: See https://mathiasbynens.be/notes/javascript-unicode // :TODO: See https://mathiasbynens.be/notes/javascript-unicode
return s.length; return s.length;
}
function debugEscapedString(s) {
return JSON.stringify(s).slice(1, -1);
} }