diff --git a/src/vs/editor/browser/view/viewLayer.ts b/src/vs/editor/browser/view/viewLayer.ts index 61f6e0419e2461e72f6e262482386f5cbc678ec9..188c9961616635d1b8ca54205fc50ae921742831 100644 --- a/src/vs/editor/browser/view/viewLayer.ts +++ b/src/vs/editor/browser/view/viewLayer.ts @@ -7,6 +7,7 @@ import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { createStringBuilder, IStringBuilder } from 'vs/editor/common/core/stringBuilder'; /** * Represents a visible line @@ -22,7 +23,7 @@ export interface IVisibleLine { * Return null if the HTML should not be touched. * Return the new HTML otherwise. */ - renderLine(lineNumber: number, deltaTop: number, viewportData: ViewportData): string; + renderLine(lineNumber: number, deltaTop: number, viewportData: ViewportData, sb: IStringBuilder): boolean; /** * Layout the line. @@ -503,12 +504,12 @@ class ViewLayerRenderer { ctx.lines.splice(removeIndex, removeCount); } - private _finishRenderingNewLines(ctx: IRendererContext, domNodeIsEmpty: boolean, newLinesHTML: string[], wasNew: boolean[]): void { + private _finishRenderingNewLines(ctx: IRendererContext, domNodeIsEmpty: boolean, newLinesHTML: string, wasNew: boolean[]): void { let lastChild = this.domNode.lastChild; if (domNodeIsEmpty || !lastChild) { - this.domNode.innerHTML = newLinesHTML.join(''); + this.domNode.innerHTML = newLinesHTML; } else { - lastChild.insertAdjacentHTML('afterend', newLinesHTML.join('')); + lastChild.insertAdjacentHTML('afterend', newLinesHTML); } let currChild = this.domNode.lastChild; @@ -521,10 +522,10 @@ class ViewLayerRenderer { } } - private _finishRenderingInvalidLines(ctx: IRendererContext, invalidLinesHTML: string[], wasInvalid: boolean[]): void { + private _finishRenderingInvalidLines(ctx: IRendererContext, invalidLinesHTML: string, wasInvalid: boolean[]): void { let hugeDomNode = document.createElement('div'); - hugeDomNode.innerHTML = invalidLinesHTML.join(''); + hugeDomNode.innerHTML = invalidLinesHTML; for (let i = 0; i < ctx.linesLength; i++) { let line = ctx.lines[i]; @@ -537,14 +538,21 @@ class ViewLayerRenderer { } } + private static _sb1 = createStringBuilder(100000); + private static _sb2 = createStringBuilder(100000); + private _finishRendering(ctx: IRendererContext, domNodeIsEmpty: boolean, deltaTop: number[]): void { + const sb1 = ViewLayerRenderer._sb1; + const sb2 = ViewLayerRenderer._sb2; + + sb1.reset(); + sb2.reset(); + let hadNewLine = false; let wasNew: boolean[] = []; - let newLinesHTML: string[] = []; let hadInvalidLine = false; let wasInvalid: boolean[] = []; - let invalidLinesHTML: string[] = []; for (let i = 0, len = ctx.linesLength; i < len; i++) { let line = ctx.lines[i]; @@ -553,19 +561,19 @@ class ViewLayerRenderer { wasNew[i] = false; wasInvalid[i] = false; - let renderResult = line.renderLine(lineNumber, deltaTop[i], this.viewportData); + const lineDomNode = line.getDomNode(); - if (renderResult !== null) { - // Line needs rendering - let lineDomNode = line.getDomNode(); - if (!lineDomNode) { + if (!lineDomNode) { + let renderResult = line.renderLine(lineNumber, deltaTop[i], this.viewportData, sb1); + if (renderResult) { // Line is new - newLinesHTML.push(renderResult); wasNew[i] = true; hadNewLine = true; - } else { + } + } else { + let renderResult = line.renderLine(lineNumber, deltaTop[i], this.viewportData, sb2); + if (renderResult) { // Line is invalid - invalidLinesHTML.push(renderResult); wasInvalid[i] = true; hadInvalidLine = true; } @@ -573,11 +581,11 @@ class ViewLayerRenderer { } if (hadNewLine) { - this._finishRenderingNewLines(ctx, domNodeIsEmpty, newLinesHTML, wasNew); + this._finishRenderingNewLines(ctx, domNodeIsEmpty, sb1.build(), wasNew); } if (hadInvalidLine) { - this._finishRenderingInvalidLines(ctx, invalidLinesHTML, wasInvalid); + this._finishRenderingInvalidLines(ctx, sb2.build(), wasInvalid); } } } diff --git a/src/vs/editor/browser/view/viewOverlays.ts b/src/vs/editor/browser/view/viewOverlays.ts index 89c91ee335935d905941417a6f46250f98532b34..56e398547a0992e24525006e56c80787de50fb38 100644 --- a/src/vs/editor/browser/view/viewOverlays.ts +++ b/src/vs/editor/browser/view/viewOverlays.ts @@ -14,6 +14,7 @@ import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/v import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { ViewPart } from 'vs/editor/browser/view/viewPart'; +import { IStringBuilder } from 'vs/editor/common/core/stringBuilder'; export class ViewOverlays extends ViewPart implements IVisibleLinesHost { @@ -178,7 +179,7 @@ export class ViewOverlayLine implements IVisibleLine { } } - public renderLine(lineNumber: number, deltaTop: number, viewportData: ViewportData): string { + public renderLine(lineNumber: number, deltaTop: number, viewportData: ViewportData, sb: IStringBuilder): boolean { let result = ''; for (let i = 0, len = this._dynamicOverlays.length; i < len; i++) { let dynamicOverlay = this._dynamicOverlays[i]; @@ -187,12 +188,20 @@ export class ViewOverlayLine implements IVisibleLine { if (this._renderedContent === result) { // No rendering needed - return null; + return false; } this._renderedContent = result; - return `
${result}
`; + sb.appendASCIIString('
'); + sb.appendASCIIString(result); + sb.appendASCIIString('
'); + + return true; } public layoutLine(lineNumber: number, deltaTop: number): void { diff --git a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts index 455a49cb3fbd7c8bd1f2fa2f62afc56505dfd44a..33874c27b5170faf5b503a9745f14f9b15177e05 100644 --- a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts +++ b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts @@ -142,12 +142,12 @@ export class GlyphMarginOverlay extends DedupOverlay { protected _getDecorations(ctx: RenderingContext): DecorationToRender[] { let decorations = ctx.getDecorationsInViewport(); - let r: DecorationToRender[] = []; + let r: DecorationToRender[] = [], rLen = 0; for (let i = 0, len = decorations.length; i < len; i++) { let d = decorations[i]; let glyphMarginClassName = d.source.options.glyphMarginClassName; if (glyphMarginClassName) { - r.push(new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, glyphMarginClassName)); + r[rLen++] = new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, glyphMarginClassName); } } return r; diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index 15dc26899b7a00913ef18ad68636c61c505cf22d..c7e00b01e7cb01c306d0a880531a9e3f90da50a1 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -16,6 +16,7 @@ import { RangeUtil } from 'vs/editor/browser/viewParts/lines/rangeUtil'; import { HorizontalRange } from 'vs/editor/common/view/renderingContext'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { ThemeType, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; +import { IStringBuilder } from 'vs/editor/common/core/stringBuilder'; const canUseFastRenderedViewLine = (function () { if (platform.isNative) { @@ -156,10 +157,10 @@ export class ViewLine implements IVisibleLine { return false; } - public renderLine(lineNumber: number, deltaTop: number, viewportData: ViewportData): string { + public renderLine(lineNumber: number, deltaTop: number, viewportData: ViewportData, sb: IStringBuilder): boolean { if (this._isMaybeInvalid === false) { // it appears that nothing relevant has changed - return null; + return false; } this._isMaybeInvalid = false; @@ -204,10 +205,20 @@ export class ViewLine implements IVisibleLine { if (this._renderedViewLine && this._renderedViewLine.input.equals(renderLineInput)) { // no need to do anything, we have the same render input - return null; + return false; } - const output = renderViewLine(renderLineInput); + sb.appendASCIIString('
'); + + const output = renderViewLine(renderLineInput, sb); + + sb.appendASCIIString('
'); let renderedViewLine: IRenderedViewLine = null; if (canUseFastRenderedViewLine && options.useMonospaceOptimizations && !output.containsForeignElements) { @@ -239,7 +250,7 @@ export class ViewLine implements IVisibleLine { this._renderedViewLine = renderedViewLine; - return `
${output.html}
`; + return true; } public layoutLine(lineNumber: number, deltaTop: number): void { diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index d2d515946293f91bbc730ffb0e0aad9516982cc0..3ae7a7ec473e4a9fd31e09a320752e5c7303c07a 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -42,6 +42,7 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDeco import { DiffReview } from 'vs/editor/browser/widget/diffReview'; import URI from 'vs/base/common/uri'; import { IMessageService } from 'vs/platform/message/common/message'; +import { IStringBuilder, createStringBuilder } from 'vs/editor/common/core/stringBuilder'; interface IEditorDiffDecorations { decorations: editorCommon.IModelDeltaDecoration[]; @@ -1943,12 +1944,12 @@ class InlineViewZonesComputer extends ViewZonesComputer { } } - let html: string[] = []; + let sb = createStringBuilder(10000); let marginHTML: string[] = []; let lineDecorationsWidth = this.modifiedEditorConfiguration.layoutInfo.decorationsWidth; let lineHeight = this.modifiedEditorConfiguration.lineHeight; for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) { - html = html.concat(this.renderOriginalLine(lineNumber - lineChange.originalStartLineNumber, this.originalModel, this.modifiedEditorConfiguration, this.modifiedEditorTabSize, lineNumber, decorations)); + this.renderOriginalLine(lineNumber - lineChange.originalStartLineNumber, this.originalModel, this.modifiedEditorConfiguration, this.modifiedEditorTabSize, lineNumber, decorations, sb); if (this.renderIndicators) { let index = lineNumber - lineChange.originalStartLineNumber; @@ -1960,7 +1961,7 @@ class InlineViewZonesComputer extends ViewZonesComputer { let domNode = document.createElement('div'); domNode.className = 'view-lines line-delete'; - domNode.innerHTML = html.join(''); + domNode.innerHTML = sb.build(); Configuration.applyFontInfoSlow(domNode, this.modifiedEditorConfiguration.fontInfo); let marginDomNode = document.createElement('div'); @@ -1977,7 +1978,7 @@ class InlineViewZonesComputer extends ViewZonesComputer { }; } - private renderOriginalLine(count: number, originalModel: editorCommon.IModel, config: editorOptions.InternalEditorOptions, tabSize: number, lineNumber: number, decorations: InlineDecoration[]): string[] { + private renderOriginalLine(count: number, originalModel: editorCommon.IModel, config: editorOptions.InternalEditorOptions, tabSize: number, lineNumber: number, decorations: InlineDecoration[], sb: IStringBuilder): void { let lineContent = originalModel.getLineContent(lineNumber); let actualDecorations = LineDecoration.filter(decorations, lineNumber, 1, lineContent.length + 1); @@ -1988,7 +1989,16 @@ class InlineViewZonesComputer extends ViewZonesComputer { | (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET) ) >>> 0; - let r = renderViewLine(new RenderLineInput( + sb.appendASCIIString('
'); + + renderViewLine(new RenderLineInput( (config.fontInfo.isMonospace && !config.viewInfo.disableMonospaceOptimizations), lineContent, originalModel.mightContainRTL(), @@ -2001,21 +2011,9 @@ class InlineViewZonesComputer extends ViewZonesComputer { config.viewInfo.renderWhitespace, config.viewInfo.renderControlCharacters, config.viewInfo.fontLigatures - )); - - let myResult: string[] = []; - myResult.push('
'); - myResult = myResult.concat(r.html); - myResult.push('
'); + ), sb); - return myResult; + sb.appendASCIIString('
'); } } diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts index 777054eee062461e050b8542fe221f7ca2e672de..b40b43f5d7fa59fee27a58b685ab3c0d896fbde0 100644 --- a/src/vs/editor/browser/widget/diffReview.ts +++ b/src/vs/editor/browser/widget/diffReview.ts @@ -10,7 +10,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { renderViewLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer'; +import { renderViewLine2 as renderViewLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer'; import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; import { Configuration } from 'vs/editor/browser/config/configuration'; import { Position } from 'vs/editor/common/core/position'; diff --git a/src/vs/editor/common/core/stringBuilder.ts b/src/vs/editor/common/core/stringBuilder.ts new file mode 100644 index 0000000000000000000000000000000000000000..31f49aab619f692d4b14d95403b91620daea36ad --- /dev/null +++ b/src/vs/editor/common/core/stringBuilder.ts @@ -0,0 +1,148 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as strings from 'vs/base/common/strings'; + +declare var TextDecoder: any; // TODO@TypeScript +interface TextDecoder { + decode(view: Uint16Array): string; +} + +export interface IStringBuilder { + build(): string; + reset(): void; + write1(charCode: number): void; + appendASCII(charCode: number): void; + appendASCIIString(str: string): void; +} + +export let createStringBuilder: (capacity: number) => IStringBuilder; + +if ((self).TextDecoder) { + createStringBuilder = (capacity) => new StringBuilder(capacity); +} else { + createStringBuilder = (capacity) => new CompatStringBuilder(); +} + +class StringBuilder implements IStringBuilder { + + private readonly _decoder: TextDecoder; + private readonly _capacity: number; + private readonly _buffer: Uint16Array; + + private _completedStrings: string[]; + private _bufferLength: number; + + constructor(capacity: number) { + this._decoder = new TextDecoder('UTF-16LE'); + this._capacity = capacity | 0; + this._buffer = new Uint16Array(this._capacity); + + this._completedStrings = null; + this._bufferLength = 0; + } + + public reset(): void { + this._completedStrings = null; + this._bufferLength = 0; + } + + public build(): string { + if (this._completedStrings !== null) { + this._flushBuffer(); + return this._completedStrings.join(''); + } + return this._buildBuffer(); + } + + private _buildBuffer(): string { + if (this._bufferLength === 0) { + return ''; + } + + const view = new Uint16Array(this._buffer.buffer, 0, this._bufferLength); + return this._decoder.decode(view); + } + + private _flushBuffer(): void { + const bufferString = this._buildBuffer(); + this._bufferLength = 0; + + if (this._completedStrings === null) { + this._completedStrings = [bufferString]; + } else { + this._completedStrings.push(bufferString); + } + } + + public write1(charCode: number): void { + const remainingSpace = this._capacity - this._bufferLength; + + if (remainingSpace <= 1) { + if (remainingSpace === 0 || strings.isHighSurrogate(charCode)) { + this._flushBuffer(); + } + } + + this._buffer[this._bufferLength++] = charCode; + } + + public appendASCII(charCode: number): void { + if (this._bufferLength === this._capacity) { + // buffer is full + this._flushBuffer(); + } + this._buffer[this._bufferLength++] = charCode; + } + + public appendASCIIString(str: string): void { + const strLen = str.length; + + if (this._bufferLength + strLen >= this._capacity) { + // This string does not fit in the remaining buffer space + + this._flushBuffer(); + this._completedStrings.push(str); + return; + } + + for (let i = 0; i < strLen; i++) { + this._buffer[this._bufferLength++] = str.charCodeAt(i); + } + } +} + +class CompatStringBuilder implements IStringBuilder { + + private _pieces: string[]; + private _piecesLen: number; + + constructor() { + this._pieces = []; + this._piecesLen = 0; + } + + public reset(): void { + this._pieces = []; + this._piecesLen = 0; + } + + public build(): string { + return this._pieces.join(''); + } + + public write1(charCode: number): void { + this._pieces[this._piecesLen++] = String.fromCharCode(charCode); + } + + public appendASCII(charCode: number): void { + this._pieces[this._piecesLen++] = String.fromCharCode(charCode); + } + + public appendASCIIString(str: string): void { + this._pieces[this._piecesLen++] = str; + } +} diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index efa94b6b44bc422c3cd7e487f5ea4c86f70f92e5..0d6ca556af38eab95330afcdc68247cb149a8820 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -8,6 +8,7 @@ import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; import { CharCode } from 'vs/base/common/charCode'; import { LineDecoration, LineDecorationsNormalizer } from 'vs/editor/common/viewLayout/lineDecorations'; import * as strings from 'vs/base/common/strings'; +import { IStringBuilder, createStringBuilder } from 'vs/editor/common/core/stringBuilder'; export const enum RenderWhitespace { None = 0, @@ -223,19 +224,17 @@ export class RenderLineOutput { _renderLineOutputBrand: void; readonly characterMapping: CharacterMapping; - readonly html: string; readonly containsRTL: boolean; readonly containsForeignElements: boolean; - constructor(characterMapping: CharacterMapping, html: string, containsRTL: boolean, containsForeignElements: boolean) { + constructor(characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: boolean) { this.characterMapping = characterMapping; - this.html = html; this.containsRTL = containsRTL; this.containsForeignElements = containsForeignElements; } } -export function renderViewLine(input: RenderLineInput): RenderLineOutput { +export function renderViewLine(input: RenderLineInput, sb: IStringBuilder): RenderLineOutput { if (input.lineContent.length === 0) { let containsForeignElements = false; @@ -259,15 +258,31 @@ export function renderViewLine(input: RenderLineInput): RenderLineOutput { } } + sb.appendASCIIString(content); return new RenderLineOutput( new CharacterMapping(0, 0), - content, false, containsForeignElements ); } - return _renderLine(resolveRenderLineInput(input)); + return _renderLine(resolveRenderLineInput(input), sb); +} + +export class RenderLineOutput2 { + constructor( + public readonly characterMapping: CharacterMapping, + public readonly html: string, + public readonly containsRTL: boolean, + public readonly containsForeignElements: boolean + ) { + } +} + +export function renderViewLine2(input: RenderLineInput): RenderLineOutput2 { + let sb = createStringBuilder(10000); + let out = renderViewLine(input, sb); + return new RenderLineOutput2(out.characterMapping, sb.build(), out.containsRTL, out.containsForeignElements); } class ResolvedRenderLineInput { @@ -564,7 +579,7 @@ function _applyInlineDecorations(lineContent: string, len: number, tokens: LineP * This function is on purpose not split up into multiple functions to allow runtime type inference (i.e. performance reasons). * Notice how all the needed data is fully resolved and passed in (i.e. no other calls). */ -function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput { +function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): RenderLineOutput { const fontIsMonospace = input.fontIsMonospace; const containsForeignElements = input.containsForeignElements; const lineContent = input.lineContent; @@ -583,7 +598,8 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput { let tabsCharDelta = 0; let charOffsetInPart = 0; - let out = ''; + sb.appendASCIIString(''); + for (let partIndex = 0, tokensLen = parts.length; partIndex < tokensLen; partIndex++) { const part = parts[partIndex]; const partEndIndex = part.endIndex; @@ -591,10 +607,42 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput { const partRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && (partType.indexOf('vs-whitespace') >= 0)); charOffsetInPart = 0; + sb.appendASCIIString(' 0) { - partContent += '→'; - partContentCnt++; + sb.appendASCIIString('→'); insertSpacesCount--; } while (insertSpacesCount > 0) { - partContent += ' '; - partContentCnt++; + sb.appendASCIIString(' '); insertSpacesCount--; } } else { // must be CharCode.Space - partContent += '·'; - partContentCnt++; + sb.appendASCIIString('·'); } charOffsetInPart++; } characterMapping.setPartLength(partIndex, partContentCnt); - if (fontIsMonospace || containsForeignElements) { - out += `${partContent}`; - } else { - out += `${partContent}`; - } } else { let partContentCnt = 0; - let partContent = ''; + + if (containsRTL) { + sb.appendASCIIString(' dir="ltr"'); + } + sb.appendASCII(CharCode.GreaterThan); for (; charIndex < partEndIndex; charIndex++) { characterMapping.setPartData(charIndex, partIndex, charOffsetInPart); @@ -644,55 +688,55 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput { tabsCharDelta += insertSpacesCount - 1; charOffsetInPart += insertSpacesCount - 1; while (insertSpacesCount > 0) { - partContent += ' '; + sb.appendASCIIString(' '); partContentCnt++; insertSpacesCount--; } break; case CharCode.Space: - partContent += ' '; + sb.appendASCIIString(' '); partContentCnt++; break; case CharCode.LessThan: - partContent += '<'; + sb.appendASCIIString('<'); partContentCnt++; break; case CharCode.GreaterThan: - partContent += '>'; + sb.appendASCIIString('>'); partContentCnt++; break; case CharCode.Ampersand: - partContent += '&'; + sb.appendASCIIString('&'); partContentCnt++; break; case CharCode.Null: - partContent += '�'; + sb.appendASCIIString('�'); partContentCnt++; break; case CharCode.UTF8_BOM: case CharCode.LINE_SEPARATOR_2028: - partContent += '\ufffd'; + sb.write1(0xfffd); partContentCnt++; break; case CharCode.CarriageReturn: // zero width space, because carriage return would introduce a line break - partContent += '​'; + sb.appendASCIIString('​'); partContentCnt++; break; default: if (renderControlCharacters && charCode < 32) { - partContent += String.fromCharCode(9216 + charCode); + sb.write1(9216 + charCode); partContentCnt++; } else { - partContent += String.fromCharCode(charCode); + sb.write1(charCode); partContentCnt++; } } @@ -701,13 +745,10 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput { } characterMapping.setPartLength(partIndex, partContentCnt); - if (containsRTL) { - out += `${partContent}`; - } else { - out += `${partContent}`; - } - } + + sb.appendASCIIString(''); + } // When getting client rects for the last character, we will position the @@ -715,10 +756,10 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput { characterMapping.setPartData(len, parts.length - 1, charOffsetInPart); if (isOverflowing) { - out += ``; + sb.appendASCIIString(''); } - out += ''; + sb.appendASCIIString(''); - return new RenderLineOutput(characterMapping, out, containsRTL, containsForeignElements); + return new RenderLineOutput(characterMapping, containsRTL, containsForeignElements); } diff --git a/src/vs/editor/standalone/browser/colorizer.ts b/src/vs/editor/standalone/browser/colorizer.ts index d3834a81578dca6eea4489926153e9fc9bf4ced0..c8fa547767979680d7c12451138882cf2971daf1 100644 --- a/src/vs/editor/standalone/browser/colorizer.ts +++ b/src/vs/editor/standalone/browser/colorizer.ts @@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IModel } from 'vs/editor/common/editorCommon'; import { ColorId, MetadataConsts, FontStyle, TokenizationRegistry, ITokenizationSupport } from 'vs/editor/common/modes'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { renderViewLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer'; +import { renderViewLine2 as renderViewLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer'; import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; import * as strings from 'vs/base/common/strings'; diff --git a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts index 847031c48a136cbc131582aba7c42c73f8dad27f..f3d9bb997b5d45ea4d9a15689c7f876f956c7b03 100644 --- a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts +++ b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts @@ -5,7 +5,7 @@ 'use strict'; import * as assert from 'assert'; -import { renderViewLine, RenderLineInput, CharacterMapping } from 'vs/editor/common/viewLayout/viewLineRenderer'; +import { renderViewLine2 as renderViewLine, RenderLineInput, CharacterMapping } from 'vs/editor/common/viewLayout/viewLineRenderer'; import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; import { CharCode } from 'vs/base/common/charCode'; import { MetadataConsts } from 'vs/editor/common/modes'; @@ -56,7 +56,7 @@ suite('viewLineRenderer.renderLine', () => { assertCharacterReplacement('a\0b', 4, 'a�b', [[0, 1, 2, 3]], [3]); assertCharacterReplacement('a' + String.fromCharCode(CharCode.UTF8_BOM) + 'b', 4, 'a\ufffdb', [[0, 1, 2, 3]], [3]); assertCharacterReplacement('a\u2028b', 4, 'a\ufffdb', [[0, 1, 2, 3]], [3]); - assertCharacterReplacement('a\rb', 4, 'a​b', [[0, 1, 2, 3]], [3]); + assertCharacterReplacement('a\rb', 4, 'a​b', [[0, 1, 2, 3]], [3]); }); test('handles tabs', () => { @@ -355,10 +355,10 @@ suite('viewLineRenderer.renderLine', () => { ]; let expectedOutput = [ - 'var', - ' קודמות = ', - '"מיותר קודמות צ\'ט של, אם לשון העברית שינויים ויש, אם"', - ';' + 'var', + ' קודמות = ', + '"מיותר קודמות צ\'ט של, אם לשון העברית שינויים ויש, אם"', + ';' ].join(''); let _actual = renderViewLine(new RenderLineInput( @@ -546,7 +546,7 @@ suite('viewLineRenderer.renderLine', () => { let lineText = 'את גרמנית בהתייחסות שמו, שנתי המשפט אל חפש, אם כתב אחרים ולחבר. של התוכן אודות בויקיפדיה כלל, של עזרה כימיה היא. על עמוד יוצרים מיתולוגיה סדר, אם שכל שתפו לעברית שינויים, אם שאלות אנגלית עזה. שמות בקלות מה סדר.'; let lineParts = [createPart(lineText.length, 1)]; let expectedOutput = [ - 'את גרמנית בהתייחסות שמו, שנתי המשפט אל חפש, אם כתב אחרים ולחבר. של התוכן אודות בויקיפדיה כלל, של עזרה כימיה היא. על עמוד יוצרים מיתולוגיה סדר, אם שכל שתפו לעברית שינויים, אם שאלות אנגלית עזה. שמות בקלות מה סדר.' + 'את גרמנית בהתייחסות שמו, שנתי המשפט אל חפש, אם כתב אחרים ולחבר. של התוכן אודות בויקיפדיה כלל, של עזרה כימיה היא. על עמוד יוצרים מיתולוגיה סדר, אם שכל שתפו לעברית שינויים, אם שאלות אנגלית עזה. שמות בקלות מה סדר.' ]; let actual = renderViewLine(new RenderLineInput( false,