diff --git a/src/vs/editor/browser/viewParts/lines/rangeUtil.ts b/src/vs/editor/browser/viewParts/lines/rangeUtil.ts index f84a79df8d4b6855946dc2262ec9d52cefe1d2c4..7718ae4003ac397b7875048c7f0b2425f6a1c9c0 100644 --- a/src/vs/editor/browser/viewParts/lines/rangeUtil.ts +++ b/src/vs/editor/browser/viewParts/lines/rangeUtil.ts @@ -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 + // To cover cases of empty s, aboid using a range and use the '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) { diff --git a/src/vs/editor/common/viewLayout/lineDecorations.ts b/src/vs/editor/common/viewLayout/lineDecorations.ts index 1c49b6b95ba6966149ea3f769fa11f3fd6dfcae1..74428ae28d786dc1ff6f5c79021d87f30ae5131f 100644 --- a/src/vs/editor/common/viewLayout/lineDecorations.ts +++ b/src/vs/editor/common/viewLayout/lineDecorations.ts @@ -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; } diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index 2ac95ca427659efbe80b367ce1ed2b6f9bb16153..c032724b8ba7d33984430e5f21524b4c52f9dbec 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -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('//just\u00a0a\u00a0com', + '', + '', + 'ment', + ].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, '' + expectedOutput + ''); + 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, '' + expectedOutput + ''); }); + 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);