未验证 提交 ee764991 编写于 作者: A Alex Dima

Fixes #91178: Use previous part when dealing with offsets with an after inline decoration

上级 0792b079
......@@ -121,6 +121,13 @@ export class RangeUtil {
startChildIndex = Math.min(max, Math.max(min, startChildIndex));
endChildIndex = Math.min(max, Math.max(min, endChildIndex));
if (startChildIndex === endChildIndex && startOffset === endOffset && startOffset === 0) {
// We must find the position at the beginning of a <span>
// To cover cases of empty <span>s, aboid using a range and use the <span>'s bounding box
const clientRects = domNode.children[startChildIndex].getClientRects();
return this._createHorizontalRangesFromClientRects(clientRects, clientRectDeltaLeft);
}
// If crossing over to a span only to select offset 0, then use the previous span's maximum offset
// Chrome is buggy and doesn't handle 0 offsets well sometimes.
if (startChildIndex !== endChildIndex) {
......
......@@ -72,16 +72,25 @@ export class LineDecoration {
return result;
}
private static _typeCompare(a: InlineDecorationType, b: InlineDecorationType): number {
const ORDER = [2, 0, 1, 3];
return ORDER[a] - ORDER[b];
}
public static compare(a: LineDecoration, b: LineDecoration): number {
if (a.startColumn === b.startColumn) {
if (a.endColumn === b.endColumn) {
if (a.className < b.className) {
return -1;
}
if (a.className > b.className) {
return 1;
const typeCmp = LineDecoration._typeCompare(a.type, b.type);
if (typeCmp === 0) {
if (a.className < b.className) {
return -1;
}
if (a.className > b.className) {
return 1;
}
return 0;
}
return 0;
return typeCmp;
}
return a.endColumn - b.endColumn;
}
......
......@@ -809,6 +809,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
let visibleColumn = startVisibleColumn;
let charOffsetInPart = 0;
let partDisplacement = 0;
let prevPartContentCnt = 0;
let partAbsoluteOffset = 0;
......@@ -822,6 +823,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
const partType = part.type;
const partRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && part.isWhitespace());
const partRendersWhitespaceWithWidth = partRendersWhitespace && !fontIsMonospace && (partType === 'mtkw'/*only whitespace*/ || !containsForeignElements);
const partIsEmptyAndHasPseudoAfter = (charIndex === partEndIndex && part.metadata === LinePartMetadata.PSEUDO_AFTER);
charOffsetInPart = 0;
sb.appendASCIIString('<span class="');
......@@ -853,7 +855,8 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
sb.appendASCII(CharCode.GreaterThan);
for (; charIndex < partEndIndex; charIndex++) {
characterMapping.setPartData(charIndex, partIndex, charOffsetInPart, partAbsoluteOffset);
characterMapping.setPartData(charIndex, partIndex - partDisplacement, charOffsetInPart, partAbsoluteOffset);
partDisplacement = 0;
const charCode = lineContent.charCodeAt(charIndex);
let charWidth: number;
......@@ -893,7 +896,8 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
sb.appendASCII(CharCode.GreaterThan);
for (; charIndex < partEndIndex; charIndex++) {
characterMapping.setPartData(charIndex, partIndex, charOffsetInPart, partAbsoluteOffset);
characterMapping.setPartData(charIndex, partIndex - partDisplacement, charOffsetInPart, partAbsoluteOffset);
partDisplacement = 0;
const charCode = lineContent.charCodeAt(charIndex);
let producedCharacters = 1;
......@@ -954,6 +958,12 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
prevPartContentCnt = partContentCnt;
}
if (partIsEmptyAndHasPseudoAfter) {
partDisplacement++;
} else {
partDisplacement = 0;
}
sb.appendASCIIString('</span>');
}
......
......@@ -388,6 +388,66 @@ suite('viewLineRenderer.renderLine', () => {
assertCharacterMapping(_actual.characterMapping, expectedOffsetsArr, [12, 12, 24, 1, 21, 2, 1, 20, 1, 1]);
});
test('issue #91178: after decoration type shown before cursor', () => {
const lineText = '//just a comment';
const lineParts = createViewLineTokens([
createPart(16, 1)
]);
const expectedOutput = [
'<span class="mtk1">//just\u00a0a\u00a0com</span>',
'<span class="mtk1 dec2"></span>',
'<span class="mtk1 dec1"></span>',
'<span class="mtk1">ment</span>',
].join('');
const expectedCharacterMapping = new CharacterMapping(17, 4);
expectedCharacterMapping.setPartData(0, 0, 0, 0);
expectedCharacterMapping.setPartData(1, 0, 1, 0);
expectedCharacterMapping.setPartData(2, 0, 2, 0);
expectedCharacterMapping.setPartData(3, 0, 3, 0);
expectedCharacterMapping.setPartData(4, 0, 4, 0);
expectedCharacterMapping.setPartData(5, 0, 5, 0);
expectedCharacterMapping.setPartData(6, 0, 6, 0);
expectedCharacterMapping.setPartData(7, 0, 7, 0);
expectedCharacterMapping.setPartData(8, 0, 8, 0);
expectedCharacterMapping.setPartData(9, 0, 9, 0);
expectedCharacterMapping.setPartData(10, 0, 10, 0);
expectedCharacterMapping.setPartData(11, 0, 11, 0);
expectedCharacterMapping.setPartData(12, 2, 0, 12);
expectedCharacterMapping.setPartData(13, 3, 1, 12);
expectedCharacterMapping.setPartData(14, 3, 2, 12);
expectedCharacterMapping.setPartData(15, 3, 3, 12);
expectedCharacterMapping.setPartData(16, 3, 4, 12);
const actual = renderViewLine(new RenderLineInput(
true,
false,
lineText,
false,
true,
false,
0,
lineParts,
[
new LineDecoration(13, 13, 'dec1', InlineDecorationType.After),
new LineDecoration(13, 13, 'dec2', InlineDecorationType.Before),
],
4,
0,
10,
10,
10,
-1,
'none',
false,
false,
null
));
assert.equal(actual.html, '<span>' + expectedOutput + '</span>');
assertCharacterMapping2(actual.characterMapping, expectedCharacterMapping);
});
test('issue Microsoft/monaco-editor#280: Improved source code rendering for RTL languages', () => {
let lineText = 'var קודמות = \"מיותר קודמות צ\'ט של, אם לשון העברית שינויים ויש, אם\";';
......@@ -693,6 +753,33 @@ suite('viewLineRenderer.renderLine', () => {
assert.equal(_actual.html, '<span>' + expectedOutput + '</span>');
});
interface ICharMappingData {
charOffset: number;
partIndex: number;
charIndex: number;
}
function decodeCharacterMapping(source: CharacterMapping) {
const mapping: ICharMappingData[] = [];
for (let charOffset = 0; charOffset < source.length; charOffset++) {
const partData = source.charOffsetToPartData(charOffset);
const partIndex = CharacterMapping.getPartIndex(partData);
const charIndex = CharacterMapping.getCharIndex(partData);
mapping.push({ charOffset, partIndex, charIndex });
}
const absoluteOffsets: number[] = [];
for (const absoluteOffset of source.getAbsoluteOffsets()) {
absoluteOffsets.push(absoluteOffset);
}
return { mapping, absoluteOffsets };
}
function assertCharacterMapping2(actual: CharacterMapping, expected: CharacterMapping): void {
const _actual = decodeCharacterMapping(actual);
const _expected = decodeCharacterMapping(expected);
assert.deepEqual(_actual, _expected);
}
function assertCharacterMapping(actual: CharacterMapping, expectedCharPartOffsets: number[][], expectedPartLengths: number[]): void {
assertCharPartOffsets(actual, expectedCharPartOffsets);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册