Make the EditSession#computeWrapSplits way more simplier and add support for multipleWidthCharacter
This commit is contained in:
parent
fc111989ec
commit
61be32a3cf
3 changed files with 121 additions and 133 deletions
|
|
@ -711,138 +711,110 @@ var EditSession = function(text, mode) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// "Tokens"
|
||||||
|
var CHAR = 1,
|
||||||
|
CHAR_EXT = 2,
|
||||||
|
SPACE = 3,
|
||||||
|
TAB = 4,
|
||||||
|
TAB_SPACE = 5;
|
||||||
|
|
||||||
this.$computeWrapSplits = function(textLine, wrapLimit, tabSize) {
|
this.$computeWrapSplits = function(textLine, wrapLimit, tabSize) {
|
||||||
textLine = textLine.trimRight();
|
textLine = textLine.trimRight();
|
||||||
if (textLine.length == 0) {
|
if (textLine.length == 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
var reSpaces = /^\s*$/;
|
var tabSize = this.getTabSize();
|
||||||
var tabLine = textLine.split("\t");
|
var splits = [];
|
||||||
var wrapSplits = [];
|
var tokens = this.$getDisplayTokens(textLine);
|
||||||
|
var displayLength = tokens.length;
|
||||||
|
var lastSplit = 0, lastDocSplit = 0;
|
||||||
|
|
||||||
var screenColumn = 0;
|
function addSplit(screenPos) {
|
||||||
var documentColumn = 0;
|
var displayed = tokens.slice(lastSplit, screenPos);
|
||||||
var lastSplitColumn = 0;
|
|
||||||
|
|
||||||
var value;
|
// The document size is the current size - the extra width for tabs
|
||||||
var addedNewSplit;
|
// and multipleWidth characters.
|
||||||
|
var len = displayed.length;
|
||||||
|
displayed.join("").
|
||||||
|
// Get all the tabs.
|
||||||
|
replace(/4/g, function(m) {
|
||||||
|
len -= tabSize - 1;
|
||||||
|
}).
|
||||||
|
// Get all the multipleWidth characters.
|
||||||
|
replace(/2/g, function(m) {
|
||||||
|
len -= 1;
|
||||||
|
});
|
||||||
|
|
||||||
function addSplit(incrDocumentColumn) {
|
lastDocSplit += len;
|
||||||
var valueLenLeadingWhitespaces;
|
splits.push(lastDocSplit);
|
||||||
value = value.substring(incrDocumentColumn);
|
lastSplit = screenPos;
|
||||||
valueLenLeadingWhitespaces = value.length;
|
|
||||||
value = value.trimLeft();
|
|
||||||
|
|
||||||
incrDocumentColumn += valueLenLeadingWhitespaces - value.length;
|
|
||||||
|
|
||||||
// If there was a split saved before, then add it to the resulting
|
|
||||||
// array. It's not possible to store the split column here directly,
|
|
||||||
// as there migth be tabs/spaces following the current column that
|
|
||||||
// will cause the split position to get incremented.
|
|
||||||
if (lastSplitColumn) {
|
|
||||||
wrapSplits.push(lastSplitColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
lastSplitColumn = documentColumn += incrDocumentColumn;
|
|
||||||
screenColumn = 0;
|
|
||||||
|
|
||||||
addedNewSplit = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < tabLine.length; i++) {
|
while (displayLength - lastSplit > wrapLimit) {
|
||||||
value = tabLine[i];
|
// This is, where the split should be.
|
||||||
while (screenColumn + value.length >= wrapLimit) {
|
var split = lastSplit + wrapLimit;
|
||||||
// Find the last space that we use to split the line.
|
|
||||||
var lastSpaceIdx =
|
|
||||||
value.substring(0, wrapLimit - screenColumn + 1).
|
|
||||||
lastIndexOf(" ");
|
|
||||||
|
|
||||||
// There is no space to break the line.
|
// If there is a space or tab at this split position.
|
||||||
if (lastSpaceIdx == -1) {
|
if (tokens[split] >= SPACE) {
|
||||||
// True if this is the first tabLine we face
|
// Include all following spaces + tabs in this split as well.
|
||||||
// during this split. This means, there is no
|
while (tokens[split] >= SPACE) {
|
||||||
// space or tab to make the split -> force split.
|
split ++;
|
||||||
if (screenColumn == 0) {
|
}
|
||||||
addSplit(wrapLimit);
|
addSplit(split);
|
||||||
}
|
} else {
|
||||||
// True if we've added some content in this split line
|
// Search for the first non space/tab token.
|
||||||
// already. Add a split after this already existing content
|
for (split; split != lastSplit; split--) {
|
||||||
// in the split.
|
if (tokens[split] >= SPACE) {
|
||||||
else {
|
split++;
|
||||||
addSplit(0);
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// There is at least one space to wrap the line at.
|
// If we found one, then add the split.
|
||||||
|
if (split != lastSplit) {
|
||||||
|
addSplit(split);
|
||||||
|
}
|
||||||
|
// No space or tab around? Well, force a split then.
|
||||||
else {
|
else {
|
||||||
addSplit(lastSpaceIdx);
|
addSplit(lastSplit + wrapLimit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return splits;
|
||||||
|
}
|
||||||
|
|
||||||
// Add new content to the current split.
|
this.$getDisplayTokens = function(str) {
|
||||||
if (!addedNewSplit) {
|
var arr = [];
|
||||||
screenColumn += value.length + tabSize;
|
var tabSize = this.getTabSize();
|
||||||
documentColumn += value.length + 1;
|
|
||||||
}
|
|
||||||
// True if at least one new split was added during the while loop.
|
|
||||||
else {
|
|
||||||
var lenBefore = value.length;
|
|
||||||
value = value.trimLeft();
|
|
||||||
|
|
||||||
// True if value has some content. This content goes directly
|
for (var i = 0; i < str.length; i++) {
|
||||||
// after the last split.
|
var c = str.charCodeAt(i);
|
||||||
// If there is no content but we are in the last tabLine, we
|
// Tab
|
||||||
// already know that there is no following tabLine and we can
|
if (c == 9) {
|
||||||
// shortcut by using this if instead of the following else.
|
arr.push(TAB);
|
||||||
if (value.length != 0 || i == tabLine.length - 1) {
|
for (var n = 1; n < tabSize; n++) {
|
||||||
documentColumn = lastSplitColumn += lenBefore - value.length;
|
arr.push(TAB_SPACE);
|
||||||
screenColumn += value.length;
|
}
|
||||||
documentColumn += 1; // The tab after this for-loop.
|
}
|
||||||
}
|
// Space
|
||||||
// There is no value left after the split. This means, following
|
else if(c == 32) {
|
||||||
// spaces and tabs have to be ignored.
|
arr.push(SPACE);
|
||||||
else {
|
}
|
||||||
// Tab for this for-loop.
|
// CJK characters
|
||||||
documentColumn = lastSplitColumn += 1;
|
else if (
|
||||||
|
c >= 0x3040 && c <= 0x309F || // Hiragana
|
||||||
// While the following tabLines are empty/only spaces, set
|
c >= 0x30A0 && c <= 0x30FF || // Katakana
|
||||||
// the latest split position behind them.
|
c >= 0x4E00 && c <= 0x9FFF || // Single CJK ideographs
|
||||||
while ((i + 1) < tabLine.length - 1
|
c >= 0xF900 && c <= 0xFAFF ||
|
||||||
&& reSpaces.test(tabLine[i + 1]))
|
c >= 0x3400 && c <= 0x4DBF
|
||||||
{
|
) {
|
||||||
i++;
|
arr.push(CHAR, CHAR_EXT);
|
||||||
documentColumn = lastSplitColumn += tabLine[i].length + 1;
|
} else {
|
||||||
}
|
arr.push(CHAR);
|
||||||
|
|
||||||
// The tabLine[i + 1] is not empty. However, the trailing
|
|
||||||
// spaces have still to be removed.
|
|
||||||
value = tabLine[i + 1];
|
|
||||||
lenBefore = value.length;
|
|
||||||
value = value.trimLeft();
|
|
||||||
documentColumn = lastSplitColumn += lenBefore - value.length;
|
|
||||||
tabLine[i + 1] = value; // Store the trimed value.
|
|
||||||
}
|
|
||||||
addedNewSplit = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (lastSplitColumn) {
|
return arr;
|
||||||
wrapSplits.push(lastSplitColumn);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return wrapSplits;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getRowHeight = function(config, row) {
|
|
||||||
var rows;
|
|
||||||
if (!this.$useWrapMode) {
|
|
||||||
rows = 1;
|
|
||||||
} else {
|
|
||||||
rows = this.$wrapData[row].length + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rows * config.lineHeight;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the width of the a string on the screen while assuming that
|
* Calculates the width of the a string on the screen while assuming that
|
||||||
|
|
@ -853,36 +825,42 @@ var EditSession = function(text, mode) {
|
||||||
*/
|
*/
|
||||||
this.$getStringScreenWidth = function(str) {
|
this.$getStringScreenWidth = function(str) {
|
||||||
var screenColumn = 0;
|
var screenColumn = 0;
|
||||||
var remaining = docColumn;
|
var tabSize = this.getTabSize();
|
||||||
|
|
||||||
for (var i=0; i<str.length; i++) {
|
for (var i=0; i<str.length; i++) {
|
||||||
var c = str.charCodeAt(i);
|
var c = str.charCodeAt(i);
|
||||||
if (remaining > 0) {
|
// tab
|
||||||
remaining -= 1;
|
if (c == 9) {
|
||||||
// tab
|
screenColumn += tabSize;
|
||||||
if (c == 9) {
|
}
|
||||||
screenColumn += tabSize;
|
// CJK characters
|
||||||
}
|
else if (
|
||||||
// CJK characters
|
c >= 0x3040 && c <= 0x309F || // Hiragana
|
||||||
else if (
|
c >= 0x30A0 && c <= 0x30FF || // Katakana
|
||||||
c >= 0x3040 && c <= 0x309F || // Hiragana
|
c >= 0x4E00 && c <= 0x9FFF || // Single CJK ideographs
|
||||||
c >= 0x30A0 && c <= 0x30FF || // Katakana
|
c >= 0xF900 && c <= 0xFAFF ||
|
||||||
c >= 0x4E00 && c <= 0x9FFF || // Single CJK ideographs
|
c >= 0x3400 && c <= 0x4DBF
|
||||||
c >= 0xF900 && c <= 0xFAFF ||
|
) {
|
||||||
c >= 0x3400 && c <= 0x4DBF
|
screenColumn += 2;
|
||||||
) {
|
|
||||||
screenColumn += 2;
|
|
||||||
} else {
|
|
||||||
screenColumn += 1;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
break;
|
screenColumn += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return screenColumn;
|
return screenColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.getRowHeight = function(config, row) {
|
||||||
|
var rows;
|
||||||
|
if (!this.$useWrapMode) {
|
||||||
|
rows = 1;
|
||||||
|
} else {
|
||||||
|
rows = this.$wrapData[row].length + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows * config.lineHeight;
|
||||||
|
}
|
||||||
|
|
||||||
this.getScreenLastRowColumn = function(screenRow, returnDocPosition) {
|
this.getScreenLastRowColumn = function(screenRow, returnDocPosition) {
|
||||||
if (!this.$useWrapMode) {
|
if (!this.$useWrapMode) {
|
||||||
return this.$getStringScreenWidth(this.getLine(screenRow));
|
return this.$getStringScreenWidth(this.getLine(screenRow));
|
||||||
|
|
|
||||||
|
|
@ -289,7 +289,8 @@ var Test = {
|
||||||
var c = 0;
|
var c = 0;
|
||||||
|
|
||||||
function computeAndAssert(line, assertEqual) {
|
function computeAndAssert(line, assertEqual) {
|
||||||
splits = computeWrapSplits(line, wrapLimit, tabSize);
|
splits = computeWrapSplits.call(EditSession.prototype, line, wrapLimit, tabSize);
|
||||||
|
// console.log("String:", line, "Result:", splits, "Expected:", assertEqual);
|
||||||
assert.ok(splits.length == assertEqual.length);
|
assert.ok(splits.length == assertEqual.length);
|
||||||
for (var i = 0; i < splits.length; i++) {
|
for (var i = 0; i < splits.length; i++) {
|
||||||
assert.ok(splits[i] == assertEqual[i]);
|
assert.ok(splits[i] == assertEqual[i]);
|
||||||
|
|
@ -315,7 +316,7 @@ var Test = {
|
||||||
computeAndAssert("foo \t \tbar", [ 7 ]);
|
computeAndAssert("foo \t \tbar", [ 7 ]);
|
||||||
|
|
||||||
// Ignore spaces/tabs at beginning of split.
|
// Ignore spaces/tabs at beginning of split.
|
||||||
computeAndAssert("foo \t \t \t \t bar", [ 14]);
|
computeAndAssert("foo \t \t \t \t bar", [ 14 ]);
|
||||||
},
|
},
|
||||||
|
|
||||||
"test: documentToScreen": function() {
|
"test: documentToScreen": function() {
|
||||||
|
|
|
||||||
|
|
@ -264,6 +264,15 @@ var Test = {
|
||||||
session.setTabSize(2);
|
session.setTabSize(2);
|
||||||
assert.equal(session.getWidth(), 3);
|
assert.equal(session.getWidth(), 3);
|
||||||
assert.equal(session.getScreenWidth(), 4);
|
assert.equal(session.getScreenWidth(), 4);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test getDisplayString": function() {
|
||||||
|
var session = new EditSession(["12"]);
|
||||||
|
session.setTabSize(4);
|
||||||
|
|
||||||
|
assert.equal(session.$getDisplayTokens("\t").length, 4);
|
||||||
|
assert.equal(session.$getDisplayTokens("abc").length, 3);
|
||||||
|
assert.equal(session.$getDisplayTokens("abc\t").length, 7);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue