wip

上级 25e65e6e
......@@ -105,6 +105,222 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea
let breakingOffsets: number[] = [];
let breakingOffsetsVisibleColumn: number[] = [];
let breakingOffsetsCount: number = 0;
if (previousBreakingData/* && firstLineBreakingColumn >= 10 && Math.abs(previousBreakingData.breakingColumn - firstLineBreakingColumn) <= 3 */) {
const prevBreakingOffsets = previousBreakingData.breakOffsets;
const prevBreakingOffsetsVisibleColumn = previousBreakingData.breakingOffsetsVisibleColumn;
let breakingColumn = firstLineBreakingColumn;
const prevLen = prevBreakingOffsets.length;
let prevIndex = 0;
while (prevIndex < prevLen) {
// Allow for prevIndex to be -1 (for the case where we hit a tab when walking backwards from the first break)
let breakOffset = prevIndex < 0 ? 0 : prevBreakingOffsets[prevIndex];
let breakOffsetVisibleColumn = prevIndex < 0 ? 0 : prevBreakingOffsetsVisibleColumn[prevIndex];
if (breakOffsetVisibleColumn === breakingColumn) {
// perfect fit, nothing to do
breakingOffsets[breakingOffsetsCount] = breakOffset;
breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn;
breakingOffsetsCount++;
breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn;
prevIndex++;
} else if (breakOffsetVisibleColumn < breakingColumn) {
// try to add more characters
const initialBreakOffset = breakOffset;
let visibleColumn = breakOffsetVisibleColumn;
breakOffset = 0;
let prevCharCode = lineText.charCodeAt(initialBreakOffset - 1);
let prevCharCodeClass = classifier.get(prevCharCode);
let mustBreak = false;
for (let i = initialBreakOffset; i < len; i++) {
const charCode = lineText.charCodeAt(i);
const charCodeClass = classifier.get(charCode);
if (strings.isHighSurrogate(prevCharCode)) {
// A surrogate pair must always be considered as a single unit, so it is never to be broken
visibleColumn += 1;
prevCharCode = charCode;
prevCharCodeClass = charCodeClass;
continue;
}
if (canBreak(prevCharCodeClass, charCodeClass)) {
breakOffset = i;
breakOffsetVisibleColumn = visibleColumn;
}
const charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar);
visibleColumn += charWidth;
if (visibleColumn > breakingColumn) {
// We need to break at least before character at `i`:
if (breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) {
// Cannot break at `breakOffset`, must break at `i`
breakOffset = i;
breakOffsetVisibleColumn = visibleColumn - charWidth;
}
mustBreak = true;
break;
}
prevCharCode = charCode;
prevCharCodeClass = charCodeClass;
}
if (!mustBreak) {
// there is no more need to break => stop the outer loop!
// Add last segment
breakingOffsets[breakingOffsetsCount] = prevBreakingOffsets[prevBreakingOffsets.length - 1];
breakingOffsetsVisibleColumn[breakingOffsetsCount] = prevBreakingOffsetsVisibleColumn[prevBreakingOffsets.length - 1];
break;
}
breakingOffsets[breakingOffsetsCount] = breakOffset;
breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn;
breakingOffsetsCount++;
breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn;
prevIndex++;
} else if (breakOffsetVisibleColumn > breakingColumn) {
const initialBreakOffset = breakOffset;
let visibleColumn = breakOffsetVisibleColumn;
breakOffset = 0;
let charCode = lineText.charCodeAt(initialBreakOffset);
let charCodeClass = classifier.get(charCode);
let hitTab = false;
let firstValidBreakOffset = 0;
let firstValidBreakOffsetVisibleColumn = 0;
for (let i = initialBreakOffset - 1; i >= 0; i--) {
let prevCharCode = lineText.charCodeAt(i);
let prevCharCodeClass = classifier.get(prevCharCode);
if (strings.isHighSurrogate(prevCharCode)) {
// A surrogate pair must always be considered as a single unit, so it is never to be broken
visibleColumn -= 1;
charCode = prevCharCode;
charCodeClass = prevCharCodeClass;
continue;
}
if (prevCharCode === CharCode.Tab) {
// cannot determine the width of a tab when going backwards, so we must go forwards
hitTab = true;
break;
}
const charWidth = (strings.isFullWidthCharacter(prevCharCode) ? columnsForFullWidthChar : 1);
if (visibleColumn <= breakingColumn) {
if (firstValidBreakOffset === 0) {
firstValidBreakOffset = i + 1;
firstValidBreakOffsetVisibleColumn = visibleColumn;
}
if (visibleColumn <= breakingColumn - wrappedLineBreakingColumn) {
// went too far!
break;
}
if (canBreak(prevCharCodeClass, charCodeClass)) {
breakOffset = i + 1;
breakOffsetVisibleColumn = visibleColumn;
break;
}
}
visibleColumn -= charWidth;
charCode = prevCharCode;
charCodeClass = prevCharCodeClass;
}
if (hitTab) {
// cannot determine the width of a tab when going backwards, so we must go forwards
prevIndex--;
continue;
}
if (breakOffset === 0) {
// Could not find a good breaking point
breakOffset = firstValidBreakOffset;
breakOffsetVisibleColumn = firstValidBreakOffsetVisibleColumn;
}
breakingOffsets[breakingOffsetsCount] = breakOffset;
breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn;
breakingOffsetsCount++;
breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn;
}
if (prevIndex < 0) {
prevIndex = 0;
} else {
let currentDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn);
while (prevIndex + 1 < prevLen) {
const potentialDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn);
if (potentialDiff >= currentDiff) {
break;
}
currentDiff = potentialDiff;
prevIndex++;
}
}
}
if (breakingOffsetsCount === 0) {
return null;
}
return new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength);
const expected = createLineMapping(classifier, null, lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent);
const actual = new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength);
try {
actual.assertEqual(expected);
} catch (err) {
console.log(`BREAKING!!`);
console.log(err);
console.log(`
assertIncrementalLineMapping(
factory, ${str(lineText)}, 4,
${previousBreakingData.breakingColumn}, ${str(toAnnotatedText(lineText, previousBreakingData))},
${expected!.breakingColumn}, ${str(toAnnotatedText(lineText, expected))}
);
`);
function str(strr: string) {
return `'${strr.replace(/'/g, '\\\'')}'`;
}
function toAnnotatedText(text: string, lineBreakingData: LineBreakingData | null): string {
// Insert line break markers again, according to algorithm
let actualAnnotatedText = '';
if (lineBreakingData) {
let previousLineIndex = 0;
for (let i = 0, len = text.length; i < len; i++) {
let r = LineBreakingData.getOutputPositionOfInputOffset(lineBreakingData.breakOffsets, i);
if (previousLineIndex !== r.outputLineIndex) {
previousLineIndex = r.outputLineIndex;
actualAnnotatedText += '|';
}
actualAnnotatedText += text.charAt(i);
}
} else {
// No wrapping
actualAnnotatedText = text;
}
return actualAnnotatedText;
}
}
return actual;
breakingOffsets = [];
breakingOffsetsVisibleColumn = [];
breakingOffsetsCount = 0;
}
let breakOffset = 0;
let breakOffsetVisibleColumn = 0;
......@@ -165,6 +381,62 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea
return new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength);
}
// class BreakSearchResult {
// public static INSTANCE = new BreakSearchResult();
// prevCharCode: number = CharCode.Null;
// prevCharCodeClass: CharacterClass = CharacterClass.NONE;
// breakOffset: number = 0;
// breakOffsetVisibleColumn: number = 0;
// visibleColumn: number = 0;
// }
// function searchForBreak(classifier: WrappingCharacterClassifier, lineText: string, len: number, prevCharCode: number, prevCharCodeClass: number): boolean {
// let breakOffset = 0;
// let breakOffsetVisibleColumn = 0;
// for (let i = 1; i < len; i++) {
// const charCode = lineText.charCodeAt(i);
// const charCodeClass = classifier.get(charCode);
// if (strings.isHighSurrogate(prevCharCode)) {
// // A surrogate pair must always be considered as a single unit, so it is never to be broken
// visibleColumn += 1;
// prevCharCode = charCode;
// prevCharCodeClass = charCodeClass;
// continue;
// }
// if (canBreak(prevCharCodeClass, charCodeClass)) {
// breakOffset = i;
// breakOffsetVisibleColumn = visibleColumn;
// }
// const charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar);
// visibleColumn += charWidth;
// // check if adding character at `i` will go over the breaking column
// if (visibleColumn > breakingColumn) {
// // We need to break at least before character at `i`:
// if (breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) {
// // Cannot break at `breakOffset`, must break at `i`
// breakOffset = i;
// breakOffsetVisibleColumn = visibleColumn - charWidth;
// }
// breakingOffsets[breakingOffsetsCount] = breakOffset;
// breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn;
// breakingOffsetsCount++;
// breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn;
// breakOffset = 0;
// }
// prevCharCode = charCode;
// prevCharCodeClass = charCodeClass;
// }
// }
function computeCharWidth(charCode: number, visibleColumn: number, tabSize: number, columnsForFullWidthChar: number): number {
if (charCode === CharCode.Tab) {
return (tabSize - (visibleColumn % tabSize));
......
......@@ -34,6 +34,29 @@ export class LineBreakingData {
public readonly wrappedTextIndentLength: number
) { }
assertEqual(other: LineBreakingData | null): void {
if (other === null) {
throw new Error(`x--unexpected--1`);
}
if (other.breakingColumn !== this.breakingColumn) {
throw new Error(`x--unexpected--2`);
}
if (other.wrappedTextIndentLength !== this.wrappedTextIndentLength) {
throw new Error(`x--unexpected--3`);
}
if (other.breakOffsets.length !== this.breakOffsets.length) {
throw new Error(`x--unexpected--4`);
}
for (let i = 0; i < this.breakOffsets.length; i++) {
if (this.breakOffsets[i] !== other.breakOffsets[i]) {
throw new Error(`x--unexpected--5`);
}
if (this.breakingOffsetsVisibleColumn[i] !== other.breakingOffsetsVisibleColumn[i]) {
throw new Error(`x--unexpected--6`);
}
}
}
public static getInputOffsetOfOutputPosition(breakOffsets: number[], outputLineIndex: number, outputOffset: number): number {
if (outputLineIndex === 0) {
return outputOffset;
......
......@@ -3,49 +3,60 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { WrappingIndent } from 'vs/editor/common/config/editorOptions';
import { WrappingIndent, EditorOptions } from 'vs/editor/common/config/editorOptions';
import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper';
import { ILineMapperFactory, LineBreakingData } from 'vs/editor/common/viewModel/splitLinesCollection';
function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAfter: number, annotatedText: string, wrappingIndent = WrappingIndent.None): LineBreakingData | null {
// Create version of `annotatedText` with line break markers removed
let rawText = '';
function parseAnnotatedText(annotatedText: string): { text: string; indices: number[]; } {
let text = '';
let currentLineIndex = 0;
let lineIndices: number[] = [];
let indices: number[] = [];
for (let i = 0, len = annotatedText.length; i < len; i++) {
if (annotatedText.charAt(i) === '|') {
currentLineIndex++;
} else {
rawText += annotatedText.charAt(i);
lineIndices[rawText.length - 1] = currentLineIndex;
text += annotatedText.charAt(i);
indices[text.length - 1] = currentLineIndex;
}
}
return { text: text, indices: indices };
}
const lineMappingComputer = factory.createLineMappingComputer(tabSize, breakAfter, 2, wrappingIndent);
lineMappingComputer.addRequest(rawText, null);
const lineMappings = lineMappingComputer.finalize();
const mapper = lineMappings[0];
function toAnnotatedText(text: string, lineBreakingData: LineBreakingData | null): string {
// Insert line break markers again, according to algorithm
let actualAnnotatedText = '';
if (mapper) {
if (lineBreakingData) {
let previousLineIndex = 0;
for (let i = 0, len = rawText.length; i < len; i++) {
let r = LineBreakingData.getOutputPositionOfInputOffset(mapper.breakOffsets, i);
for (let i = 0, len = text.length; i < len; i++) {
let r = LineBreakingData.getOutputPositionOfInputOffset(lineBreakingData.breakOffsets, i);
if (previousLineIndex !== r.outputLineIndex) {
previousLineIndex = r.outputLineIndex;
actualAnnotatedText += '|';
}
actualAnnotatedText += rawText.charAt(i);
actualAnnotatedText += text.charAt(i);
}
} else {
// No wrapping
actualAnnotatedText = rawText;
actualAnnotatedText = text;
}
return actualAnnotatedText;
}
function getLineBreakingData(factory: ILineMapperFactory, tabSize: number, breakAfter: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, text: string, previousLineBreakingData: LineBreakingData | null): LineBreakingData | null {
const lineMappingComputer = factory.createLineMappingComputer(tabSize, breakAfter, columnsForFullWidthChar, wrappingIndent);
lineMappingComputer.addRequest(text, previousLineBreakingData);
return lineMappingComputer.finalize()[0];
}
function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAfter: number, annotatedText: string, wrappingIndent = WrappingIndent.None): LineBreakingData | null {
// Create version of `annotatedText` with line break markers removed
const text = parseAnnotatedText(annotatedText).text;
const lineBreakingData = getLineBreakingData(factory, tabSize, breakAfter, 2, wrappingIndent, text, null);
const actualAnnotatedText = toAnnotatedText(text, lineBreakingData);
assert.equal(actualAnnotatedText, annotatedText);
return mapper;
return lineBreakingData;
}
suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => {
......@@ -91,6 +102,54 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => {
assertLineMapping(factory, 4, 5, 'aa.(.|).aaa');
});
function assertIncrementalLineMapping(factory: ILineMapperFactory, text: string, tabSize: number, breakAfter1: number, annotatedText1: string, breakAfter2: number, annotatedText2: string, wrappingIndent = WrappingIndent.None): void {
// sanity check the test
assert.equal(text, parseAnnotatedText(annotatedText1).text);
assert.equal(text, parseAnnotatedText(annotatedText2).text);
// check that the direct mapping is ok for 1
const directLineBreakingData1 = getLineBreakingData(factory, tabSize, breakAfter1, 2, wrappingIndent, text, null);
assert.equal(toAnnotatedText(text, directLineBreakingData1), annotatedText1);
// check that the direct mapping is ok for 2
const directLineBreakingData2 = getLineBreakingData(factory, tabSize, breakAfter2, 2, wrappingIndent, text, null);
assert.equal(toAnnotatedText(text, directLineBreakingData2), annotatedText2);
// check that going from 1 to 2 is ok
const lineBreakingData2from1 = getLineBreakingData(factory, tabSize, breakAfter2, 2, wrappingIndent, text, directLineBreakingData1);
assert.equal(toAnnotatedText(text, lineBreakingData2from1), annotatedText2);
assert.deepEqual(lineBreakingData2from1, directLineBreakingData2);
// check that going from 2 to 1 is ok
const lineBreakingData1from2 = getLineBreakingData(factory, tabSize, breakAfter1, 2, wrappingIndent, text, directLineBreakingData2);
assert.equal(toAnnotatedText(text, lineBreakingData1from2), annotatedText1);
assert.deepEqual(lineBreakingData1from2, directLineBreakingData1);
}
test('CharacterHardWrappingLineMapper incremental 1', () => {
let factory = new CharacterHardWrappingLineMapperFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue);
assertIncrementalLineMapping(
factory, 'just some text and more', 4,
10, 'just some |text and |more',
15, 'just some text |and more'
);
assertIncrementalLineMapping(
factory, 'Cu scripserit suscipiantur eos, in affert pericula contentiones sed, cetero sanctus et pro. Ius vidit magna regione te, sit ei elaboraret liberavisse. Mundi verear eu mea, eam vero scriptorem in, vix in menandri assueverit. Natum definiebas cu vim. Vim doming vocibus efficiantur id. In indoctum deseruisse voluptatum vim, ad debitis verterem sed.', 4,
47, 'Cu scripserit suscipiantur eos, in affert |pericula contentiones sed, cetero sanctus et |pro. Ius vidit magna regione te, sit ei |elaboraret liberavisse. Mundi verear eu mea, |eam vero scriptorem in, vix in menandri |assueverit. Natum definiebas cu vim. Vim |doming vocibus efficiantur id. In indoctum |deseruisse voluptatum vim, ad debitis verterem |sed.',
142, 'Cu scripserit suscipiantur eos, in affert pericula contentiones sed, cetero sanctus et pro. Ius vidit magna regione te, sit ei elaboraret |liberavisse. Mundi verear eu mea, eam vero scriptorem in, vix in menandri assueverit. Natum definiebas cu vim. Vim doming vocibus efficiantur |id. In indoctum deseruisse voluptatum vim, ad debitis verterem sed.',
);
assertIncrementalLineMapping(
factory, 'An his legere persecuti, oblique delicata efficiantur ex vix, vel at graecis officiis maluisset. Et per impedit voluptua, usu discere maiorum at. Ut assum ornatus temporibus vis, an sea melius pericula. Ea dicunt oblique phaedrum nam, eu duo movet nobis. His melius facilis eu, vim malorum temporibus ne. Nec no sale regione, meliore civibus placerat id eam. Mea alii fabulas definitionem te, agam volutpat ad vis, et per bonorum nonumes repudiandae.', 4,
57, 'An his legere persecuti, oblique delicata efficiantur ex |vix, vel at graecis officiis maluisset. Et per impedit |voluptua, usu discere maiorum at. Ut assum ornatus |temporibus vis, an sea melius pericula. Ea dicunt |oblique phaedrum nam, eu duo movet nobis. His melius |facilis eu, vim malorum temporibus ne. Nec no sale |regione, meliore civibus placerat id eam. Mea alii |fabulas definitionem te, agam volutpat ad vis, et per |bonorum nonumes repudiandae.',
58, 'An his legere persecuti, oblique delicata efficiantur ex |vix, vel at graecis officiis maluisset. Et per impedit |voluptua, usu discere maiorum at. Ut assum ornatus |temporibus vis, an sea melius pericula. Ea dicunt oblique |phaedrum nam, eu duo movet nobis. His melius facilis eu, |vim malorum temporibus ne. Nec no sale regione, meliore |civibus placerat id eam. Mea alii fabulas definitionem te,| agam volutpat ad vis, et per bonorum nonumes repudiandae.'
);
});
test('CharacterHardWrappingLineMapper - CJK and Kinsoku Shori', () => {
let factory = new CharacterHardWrappingLineMapperFactory('(', '\t)');
assertLineMapping(factory, 4, 5, 'aa \u5b89|\u5b89');
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册