提交 121f96c2 编写于 作者: A Alex Dima

Use a StringBuilder for rendering view lines

上级 d5265968
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
import * as viewEvents from 'vs/editor/common/view/viewEvents'; import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { createStringBuilder, IStringBuilder } from 'vs/editor/common/core/stringBuilder';
/** /**
* Represents a visible line * Represents a visible line
...@@ -22,7 +23,7 @@ export interface IVisibleLine { ...@@ -22,7 +23,7 @@ export interface IVisibleLine {
* Return null if the HTML should not be touched. * Return null if the HTML should not be touched.
* Return the new HTML otherwise. * 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. * Layout the line.
...@@ -503,12 +504,12 @@ class ViewLayerRenderer<T extends IVisibleLine> { ...@@ -503,12 +504,12 @@ class ViewLayerRenderer<T extends IVisibleLine> {
ctx.lines.splice(removeIndex, removeCount); ctx.lines.splice(removeIndex, removeCount);
} }
private _finishRenderingNewLines(ctx: IRendererContext<T>, domNodeIsEmpty: boolean, newLinesHTML: string[], wasNew: boolean[]): void { private _finishRenderingNewLines(ctx: IRendererContext<T>, domNodeIsEmpty: boolean, newLinesHTML: string, wasNew: boolean[]): void {
let lastChild = <HTMLElement>this.domNode.lastChild; let lastChild = <HTMLElement>this.domNode.lastChild;
if (domNodeIsEmpty || !lastChild) { if (domNodeIsEmpty || !lastChild) {
this.domNode.innerHTML = newLinesHTML.join(''); this.domNode.innerHTML = newLinesHTML;
} else { } else {
lastChild.insertAdjacentHTML('afterend', newLinesHTML.join('')); lastChild.insertAdjacentHTML('afterend', newLinesHTML);
} }
let currChild = <HTMLElement>this.domNode.lastChild; let currChild = <HTMLElement>this.domNode.lastChild;
...@@ -521,10 +522,10 @@ class ViewLayerRenderer<T extends IVisibleLine> { ...@@ -521,10 +522,10 @@ class ViewLayerRenderer<T extends IVisibleLine> {
} }
} }
private _finishRenderingInvalidLines(ctx: IRendererContext<T>, invalidLinesHTML: string[], wasInvalid: boolean[]): void { private _finishRenderingInvalidLines(ctx: IRendererContext<T>, invalidLinesHTML: string, wasInvalid: boolean[]): void {
let hugeDomNode = document.createElement('div'); let hugeDomNode = document.createElement('div');
hugeDomNode.innerHTML = invalidLinesHTML.join(''); hugeDomNode.innerHTML = invalidLinesHTML;
for (let i = 0; i < ctx.linesLength; i++) { for (let i = 0; i < ctx.linesLength; i++) {
let line = ctx.lines[i]; let line = ctx.lines[i];
...@@ -537,14 +538,21 @@ class ViewLayerRenderer<T extends IVisibleLine> { ...@@ -537,14 +538,21 @@ class ViewLayerRenderer<T extends IVisibleLine> {
} }
} }
private static _sb1 = createStringBuilder(100000);
private static _sb2 = createStringBuilder(100000);
private _finishRendering(ctx: IRendererContext<T>, domNodeIsEmpty: boolean, deltaTop: number[]): void { private _finishRendering(ctx: IRendererContext<T>, domNodeIsEmpty: boolean, deltaTop: number[]): void {
const sb1 = ViewLayerRenderer._sb1;
const sb2 = ViewLayerRenderer._sb2;
sb1.reset();
sb2.reset();
let hadNewLine = false; let hadNewLine = false;
let wasNew: boolean[] = []; let wasNew: boolean[] = [];
let newLinesHTML: string[] = [];
let hadInvalidLine = false; let hadInvalidLine = false;
let wasInvalid: boolean[] = []; let wasInvalid: boolean[] = [];
let invalidLinesHTML: string[] = [];
for (let i = 0, len = ctx.linesLength; i < len; i++) { for (let i = 0, len = ctx.linesLength; i < len; i++) {
let line = ctx.lines[i]; let line = ctx.lines[i];
...@@ -553,19 +561,19 @@ class ViewLayerRenderer<T extends IVisibleLine> { ...@@ -553,19 +561,19 @@ class ViewLayerRenderer<T extends IVisibleLine> {
wasNew[i] = false; wasNew[i] = false;
wasInvalid[i] = false; wasInvalid[i] = false;
let renderResult = line.renderLine(lineNumber, deltaTop[i], this.viewportData); const lineDomNode = line.getDomNode();
if (renderResult !== null) { if (!lineDomNode) {
// Line needs rendering let renderResult = line.renderLine(lineNumber, deltaTop[i], this.viewportData, sb1);
let lineDomNode = line.getDomNode(); if (renderResult) {
if (!lineDomNode) {
// Line is new // Line is new
newLinesHTML.push(renderResult);
wasNew[i] = true; wasNew[i] = true;
hadNewLine = true; hadNewLine = true;
} else { }
} else {
let renderResult = line.renderLine(lineNumber, deltaTop[i], this.viewportData, sb2);
if (renderResult) {
// Line is invalid // Line is invalid
invalidLinesHTML.push(renderResult);
wasInvalid[i] = true; wasInvalid[i] = true;
hadInvalidLine = true; hadInvalidLine = true;
} }
...@@ -573,11 +581,11 @@ class ViewLayerRenderer<T extends IVisibleLine> { ...@@ -573,11 +581,11 @@ class ViewLayerRenderer<T extends IVisibleLine> {
} }
if (hadNewLine) { if (hadNewLine) {
this._finishRenderingNewLines(ctx, domNodeIsEmpty, newLinesHTML, wasNew); this._finishRenderingNewLines(ctx, domNodeIsEmpty, sb1.build(), wasNew);
} }
if (hadInvalidLine) { if (hadInvalidLine) {
this._finishRenderingInvalidLines(ctx, invalidLinesHTML, wasInvalid); this._finishRenderingInvalidLines(ctx, sb2.build(), wasInvalid);
} }
} }
} }
...@@ -14,6 +14,7 @@ import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/v ...@@ -14,6 +14,7 @@ import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/v
import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
import * as viewEvents from 'vs/editor/common/view/viewEvents'; import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { ViewPart } from 'vs/editor/browser/view/viewPart'; import { ViewPart } from 'vs/editor/browser/view/viewPart';
import { IStringBuilder } from 'vs/editor/common/core/stringBuilder';
export class ViewOverlays extends ViewPart implements IVisibleLinesHost<ViewOverlayLine> { export class ViewOverlays extends ViewPart implements IVisibleLinesHost<ViewOverlayLine> {
...@@ -178,7 +179,7 @@ export class ViewOverlayLine implements IVisibleLine { ...@@ -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 = ''; let result = '';
for (let i = 0, len = this._dynamicOverlays.length; i < len; i++) { for (let i = 0, len = this._dynamicOverlays.length; i < len; i++) {
let dynamicOverlay = this._dynamicOverlays[i]; let dynamicOverlay = this._dynamicOverlays[i];
...@@ -187,12 +188,20 @@ export class ViewOverlayLine implements IVisibleLine { ...@@ -187,12 +188,20 @@ export class ViewOverlayLine implements IVisibleLine {
if (this._renderedContent === result) { if (this._renderedContent === result) {
// No rendering needed // No rendering needed
return null; return false;
} }
this._renderedContent = result; this._renderedContent = result;
return `<div style="position:absolute;top:${deltaTop}px;width:100%;height:${this._lineHeight}px;">${result}</div>`; sb.appendASCIIString('<div style="position:absolute;top:');
sb.appendASCIIString(String(deltaTop));
sb.appendASCIIString('px;width:100%;height:');
sb.appendASCIIString(String(this._lineHeight));
sb.appendASCIIString('px;">');
sb.appendASCIIString(result);
sb.appendASCIIString('</div>');
return true;
} }
public layoutLine(lineNumber: number, deltaTop: number): void { public layoutLine(lineNumber: number, deltaTop: number): void {
......
...@@ -142,12 +142,12 @@ export class GlyphMarginOverlay extends DedupOverlay { ...@@ -142,12 +142,12 @@ export class GlyphMarginOverlay extends DedupOverlay {
protected _getDecorations(ctx: RenderingContext): DecorationToRender[] { protected _getDecorations(ctx: RenderingContext): DecorationToRender[] {
let decorations = ctx.getDecorationsInViewport(); let decorations = ctx.getDecorationsInViewport();
let r: DecorationToRender[] = []; let r: DecorationToRender[] = [], rLen = 0;
for (let i = 0, len = decorations.length; i < len; i++) { for (let i = 0, len = decorations.length; i < len; i++) {
let d = decorations[i]; let d = decorations[i];
let glyphMarginClassName = d.source.options.glyphMarginClassName; let glyphMarginClassName = d.source.options.glyphMarginClassName;
if (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; return r;
......
...@@ -16,6 +16,7 @@ import { RangeUtil } from 'vs/editor/browser/viewParts/lines/rangeUtil'; ...@@ -16,6 +16,7 @@ import { RangeUtil } from 'vs/editor/browser/viewParts/lines/rangeUtil';
import { HorizontalRange } from 'vs/editor/common/view/renderingContext'; import { HorizontalRange } from 'vs/editor/common/view/renderingContext';
import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
import { ThemeType, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; import { ThemeType, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
import { IStringBuilder } from 'vs/editor/common/core/stringBuilder';
const canUseFastRenderedViewLine = (function () { const canUseFastRenderedViewLine = (function () {
if (platform.isNative) { if (platform.isNative) {
...@@ -156,10 +157,10 @@ export class ViewLine implements IVisibleLine { ...@@ -156,10 +157,10 @@ export class ViewLine implements IVisibleLine {
return false; 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) { if (this._isMaybeInvalid === false) {
// it appears that nothing relevant has changed // it appears that nothing relevant has changed
return null; return false;
} }
this._isMaybeInvalid = false; this._isMaybeInvalid = false;
...@@ -204,10 +205,20 @@ export class ViewLine implements IVisibleLine { ...@@ -204,10 +205,20 @@ export class ViewLine implements IVisibleLine {
if (this._renderedViewLine && this._renderedViewLine.input.equals(renderLineInput)) { if (this._renderedViewLine && this._renderedViewLine.input.equals(renderLineInput)) {
// no need to do anything, we have the same render input // no need to do anything, we have the same render input
return null; return false;
} }
const output = renderViewLine(renderLineInput); sb.appendASCIIString('<div style="top:');
sb.appendASCIIString(String(deltaTop));
sb.appendASCIIString('px;height:');
sb.appendASCIIString(String(this._options.lineHeight));
sb.appendASCIIString('px;" class="');
sb.appendASCIIString(ViewLine.CLASS_NAME);
sb.appendASCIIString('">');
const output = renderViewLine(renderLineInput, sb);
sb.appendASCIIString('</div>');
let renderedViewLine: IRenderedViewLine = null; let renderedViewLine: IRenderedViewLine = null;
if (canUseFastRenderedViewLine && options.useMonospaceOptimizations && !output.containsForeignElements) { if (canUseFastRenderedViewLine && options.useMonospaceOptimizations && !output.containsForeignElements) {
...@@ -239,7 +250,7 @@ export class ViewLine implements IVisibleLine { ...@@ -239,7 +250,7 @@ export class ViewLine implements IVisibleLine {
this._renderedViewLine = renderedViewLine; this._renderedViewLine = renderedViewLine;
return `<div style="top:${deltaTop}px;height:${this._options.lineHeight}px;" class="${ViewLine.CLASS_NAME}">${output.html}</div>`; return true;
} }
public layoutLine(lineNumber: number, deltaTop: number): void { public layoutLine(lineNumber: number, deltaTop: number): void {
......
...@@ -42,6 +42,7 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDeco ...@@ -42,6 +42,7 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDeco
import { DiffReview } from 'vs/editor/browser/widget/diffReview'; import { DiffReview } from 'vs/editor/browser/widget/diffReview';
import URI from 'vs/base/common/uri'; import URI from 'vs/base/common/uri';
import { IMessageService } from 'vs/platform/message/common/message'; import { IMessageService } from 'vs/platform/message/common/message';
import { IStringBuilder, createStringBuilder } from 'vs/editor/common/core/stringBuilder';
interface IEditorDiffDecorations { interface IEditorDiffDecorations {
decorations: editorCommon.IModelDeltaDecoration[]; decorations: editorCommon.IModelDeltaDecoration[];
...@@ -1943,12 +1944,12 @@ class InlineViewZonesComputer extends ViewZonesComputer { ...@@ -1943,12 +1944,12 @@ class InlineViewZonesComputer extends ViewZonesComputer {
} }
} }
let html: string[] = []; let sb = createStringBuilder(10000);
let marginHTML: string[] = []; let marginHTML: string[] = [];
let lineDecorationsWidth = this.modifiedEditorConfiguration.layoutInfo.decorationsWidth; let lineDecorationsWidth = this.modifiedEditorConfiguration.layoutInfo.decorationsWidth;
let lineHeight = this.modifiedEditorConfiguration.lineHeight; let lineHeight = this.modifiedEditorConfiguration.lineHeight;
for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) { 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) { if (this.renderIndicators) {
let index = lineNumber - lineChange.originalStartLineNumber; let index = lineNumber - lineChange.originalStartLineNumber;
...@@ -1960,7 +1961,7 @@ class InlineViewZonesComputer extends ViewZonesComputer { ...@@ -1960,7 +1961,7 @@ class InlineViewZonesComputer extends ViewZonesComputer {
let domNode = document.createElement('div'); let domNode = document.createElement('div');
domNode.className = 'view-lines line-delete'; domNode.className = 'view-lines line-delete';
domNode.innerHTML = html.join(''); domNode.innerHTML = sb.build();
Configuration.applyFontInfoSlow(domNode, this.modifiedEditorConfiguration.fontInfo); Configuration.applyFontInfoSlow(domNode, this.modifiedEditorConfiguration.fontInfo);
let marginDomNode = document.createElement('div'); let marginDomNode = document.createElement('div');
...@@ -1977,7 +1978,7 @@ class InlineViewZonesComputer extends ViewZonesComputer { ...@@ -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 lineContent = originalModel.getLineContent(lineNumber);
let actualDecorations = LineDecoration.filter(decorations, lineNumber, 1, lineContent.length + 1); let actualDecorations = LineDecoration.filter(decorations, lineNumber, 1, lineContent.length + 1);
...@@ -1988,7 +1989,16 @@ class InlineViewZonesComputer extends ViewZonesComputer { ...@@ -1988,7 +1989,16 @@ class InlineViewZonesComputer extends ViewZonesComputer {
| (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET) | (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET)
) >>> 0; ) >>> 0;
let r = renderViewLine(new RenderLineInput( sb.appendASCIIString('<div class="view-line');
if (decorations.length === 0) {
// No char changes
sb.appendASCIIString(' char-delete');
}
sb.appendASCIIString('" style="top:');
sb.appendASCIIString(String(count * config.lineHeight));
sb.appendASCIIString('px;width:1000000px;">');
renderViewLine(new RenderLineInput(
(config.fontInfo.isMonospace && !config.viewInfo.disableMonospaceOptimizations), (config.fontInfo.isMonospace && !config.viewInfo.disableMonospaceOptimizations),
lineContent, lineContent,
originalModel.mightContainRTL(), originalModel.mightContainRTL(),
...@@ -2001,21 +2011,9 @@ class InlineViewZonesComputer extends ViewZonesComputer { ...@@ -2001,21 +2011,9 @@ class InlineViewZonesComputer extends ViewZonesComputer {
config.viewInfo.renderWhitespace, config.viewInfo.renderWhitespace,
config.viewInfo.renderControlCharacters, config.viewInfo.renderControlCharacters,
config.viewInfo.fontLigatures config.viewInfo.fontLigatures
)); ), sb);
let myResult: string[] = [];
myResult.push('<div class="view-line');
if (decorations.length === 0) {
// No char changes
myResult.push(' char-delete');
}
myResult.push('" style="top:');
myResult.push(String(count * config.lineHeight));
myResult.push('px;width:1000000px;">');
myResult = myResult.concat(r.html);
myResult.push('</div>');
return myResult; sb.appendASCIIString('</div>');
} }
} }
......
...@@ -10,7 +10,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; ...@@ -10,7 +10,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import * as dom from 'vs/base/browser/dom'; import * as dom from 'vs/base/browser/dom';
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
import * as editorCommon from 'vs/editor/common/editorCommon'; 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 { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
import { Configuration } from 'vs/editor/browser/config/configuration'; import { Configuration } from 'vs/editor/browser/config/configuration';
import { Position } from 'vs/editor/common/core/position'; import { Position } from 'vs/editor/common/core/position';
......
/*---------------------------------------------------------------------------------------------
* 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 ((<any>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;
}
}
...@@ -8,6 +8,7 @@ import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; ...@@ -8,6 +8,7 @@ import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
import { CharCode } from 'vs/base/common/charCode'; import { CharCode } from 'vs/base/common/charCode';
import { LineDecoration, LineDecorationsNormalizer } from 'vs/editor/common/viewLayout/lineDecorations'; import { LineDecoration, LineDecorationsNormalizer } from 'vs/editor/common/viewLayout/lineDecorations';
import * as strings from 'vs/base/common/strings'; import * as strings from 'vs/base/common/strings';
import { IStringBuilder, createStringBuilder } from 'vs/editor/common/core/stringBuilder';
export const enum RenderWhitespace { export const enum RenderWhitespace {
None = 0, None = 0,
...@@ -223,19 +224,17 @@ export class RenderLineOutput { ...@@ -223,19 +224,17 @@ export class RenderLineOutput {
_renderLineOutputBrand: void; _renderLineOutputBrand: void;
readonly characterMapping: CharacterMapping; readonly characterMapping: CharacterMapping;
readonly html: string;
readonly containsRTL: boolean; readonly containsRTL: boolean;
readonly containsForeignElements: boolean; readonly containsForeignElements: boolean;
constructor(characterMapping: CharacterMapping, html: string, containsRTL: boolean, containsForeignElements: boolean) { constructor(characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: boolean) {
this.characterMapping = characterMapping; this.characterMapping = characterMapping;
this.html = html;
this.containsRTL = containsRTL; this.containsRTL = containsRTL;
this.containsForeignElements = containsForeignElements; this.containsForeignElements = containsForeignElements;
} }
} }
export function renderViewLine(input: RenderLineInput): RenderLineOutput { export function renderViewLine(input: RenderLineInput, sb: IStringBuilder): RenderLineOutput {
if (input.lineContent.length === 0) { if (input.lineContent.length === 0) {
let containsForeignElements = false; let containsForeignElements = false;
...@@ -259,15 +258,31 @@ export function renderViewLine(input: RenderLineInput): RenderLineOutput { ...@@ -259,15 +258,31 @@ export function renderViewLine(input: RenderLineInput): RenderLineOutput {
} }
} }
sb.appendASCIIString(content);
return new RenderLineOutput( return new RenderLineOutput(
new CharacterMapping(0, 0), new CharacterMapping(0, 0),
content,
false, false,
containsForeignElements 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 { class ResolvedRenderLineInput {
...@@ -564,7 +579,7 @@ function _applyInlineDecorations(lineContent: string, len: number, tokens: LineP ...@@ -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). * 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). * 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 fontIsMonospace = input.fontIsMonospace;
const containsForeignElements = input.containsForeignElements; const containsForeignElements = input.containsForeignElements;
const lineContent = input.lineContent; const lineContent = input.lineContent;
...@@ -583,7 +598,8 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput { ...@@ -583,7 +598,8 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput {
let tabsCharDelta = 0; let tabsCharDelta = 0;
let charOffsetInPart = 0; let charOffsetInPart = 0;
let out = '<span>'; sb.appendASCIIString('<span>');
for (let partIndex = 0, tokensLen = parts.length; partIndex < tokensLen; partIndex++) { for (let partIndex = 0, tokensLen = parts.length; partIndex < tokensLen; partIndex++) {
const part = parts[partIndex]; const part = parts[partIndex];
const partEndIndex = part.endIndex; const partEndIndex = part.endIndex;
...@@ -591,10 +607,42 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput { ...@@ -591,10 +607,42 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput {
const partRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && (partType.indexOf('vs-whitespace') >= 0)); const partRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && (partType.indexOf('vs-whitespace') >= 0));
charOffsetInPart = 0; charOffsetInPart = 0;
sb.appendASCIIString('<span class="');
sb.appendASCIIString(partType);
sb.appendASCII(CharCode.DoubleQuote);
if (partRendersWhitespace) { if (partRendersWhitespace) {
let partContentCnt = 0; let partContentCnt = 0;
let partContent = ''; {
let _charIndex = charIndex;
let _tabsCharDelta = tabsCharDelta;
let _charOffsetInPart = charOffsetInPart;
for (; _charIndex < partEndIndex; _charIndex++) {
const charCode = lineContent.charCodeAt(_charIndex);
if (charCode === CharCode.Tab) {
let insertSpacesCount = tabSize - (_charIndex + _tabsCharDelta) % tabSize;
_tabsCharDelta += insertSpacesCount - 1;
_charOffsetInPart += insertSpacesCount - 1;
partContentCnt += insertSpacesCount;
} else {
partContentCnt++;
}
_charOffsetInPart++;
}
}
if (fontIsMonospace || containsForeignElements) {
} else {
sb.appendASCIIString(' style="width:');
sb.appendASCIIString(String(spaceWidth * partContentCnt));
sb.appendASCIIString('px"');
}
sb.appendASCII(CharCode.GreaterThan);
for (; charIndex < partEndIndex; charIndex++) { for (; charIndex < partEndIndex; charIndex++) {
characterMapping.setPartData(charIndex, partIndex, charOffsetInPart); characterMapping.setPartData(charIndex, partIndex, charOffsetInPart);
const charCode = lineContent.charCodeAt(charIndex); const charCode = lineContent.charCodeAt(charIndex);
...@@ -604,35 +652,31 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput { ...@@ -604,35 +652,31 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput {
tabsCharDelta += insertSpacesCount - 1; tabsCharDelta += insertSpacesCount - 1;
charOffsetInPart += insertSpacesCount - 1; charOffsetInPart += insertSpacesCount - 1;
if (insertSpacesCount > 0) { if (insertSpacesCount > 0) {
partContent += '&rarr;'; sb.appendASCIIString('&rarr;');
partContentCnt++;
insertSpacesCount--; insertSpacesCount--;
} }
while (insertSpacesCount > 0) { while (insertSpacesCount > 0) {
partContent += '&nbsp;'; sb.appendASCIIString('&nbsp;');
partContentCnt++;
insertSpacesCount--; insertSpacesCount--;
} }
} else { } else {
// must be CharCode.Space // must be CharCode.Space
partContent += '&middot;'; sb.appendASCIIString('&middot;');
partContentCnt++;
} }
charOffsetInPart++; charOffsetInPart++;
} }
characterMapping.setPartLength(partIndex, partContentCnt); characterMapping.setPartLength(partIndex, partContentCnt);
if (fontIsMonospace || containsForeignElements) {
out += `<span class="${partType}">${partContent}</span>`;
} else {
out += `<span class="${partType}" style="width:${spaceWidth * partContentCnt}px">${partContent}</span>`;
}
} else { } else {
let partContentCnt = 0; let partContentCnt = 0;
let partContent = '';
if (containsRTL) {
sb.appendASCIIString(' dir="ltr"');
}
sb.appendASCII(CharCode.GreaterThan);
for (; charIndex < partEndIndex; charIndex++) { for (; charIndex < partEndIndex; charIndex++) {
characterMapping.setPartData(charIndex, partIndex, charOffsetInPart); characterMapping.setPartData(charIndex, partIndex, charOffsetInPart);
...@@ -644,55 +688,55 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput { ...@@ -644,55 +688,55 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput {
tabsCharDelta += insertSpacesCount - 1; tabsCharDelta += insertSpacesCount - 1;
charOffsetInPart += insertSpacesCount - 1; charOffsetInPart += insertSpacesCount - 1;
while (insertSpacesCount > 0) { while (insertSpacesCount > 0) {
partContent += '&nbsp;'; sb.appendASCIIString('&nbsp;');
partContentCnt++; partContentCnt++;
insertSpacesCount--; insertSpacesCount--;
} }
break; break;
case CharCode.Space: case CharCode.Space:
partContent += '&nbsp;'; sb.appendASCIIString('&nbsp;');
partContentCnt++; partContentCnt++;
break; break;
case CharCode.LessThan: case CharCode.LessThan:
partContent += '&lt;'; sb.appendASCIIString('&lt;');
partContentCnt++; partContentCnt++;
break; break;
case CharCode.GreaterThan: case CharCode.GreaterThan:
partContent += '&gt;'; sb.appendASCIIString('&gt;');
partContentCnt++; partContentCnt++;
break; break;
case CharCode.Ampersand: case CharCode.Ampersand:
partContent += '&amp;'; sb.appendASCIIString('&amp;');
partContentCnt++; partContentCnt++;
break; break;
case CharCode.Null: case CharCode.Null:
partContent += '&#00;'; sb.appendASCIIString('&#00;');
partContentCnt++; partContentCnt++;
break; break;
case CharCode.UTF8_BOM: case CharCode.UTF8_BOM:
case CharCode.LINE_SEPARATOR_2028: case CharCode.LINE_SEPARATOR_2028:
partContent += '\ufffd'; sb.write1(0xfffd);
partContentCnt++; partContentCnt++;
break; break;
case CharCode.CarriageReturn: case CharCode.CarriageReturn:
// zero width space, because carriage return would introduce a line break // zero width space, because carriage return would introduce a line break
partContent += '&#8203'; sb.appendASCIIString('&#8203;');
partContentCnt++; partContentCnt++;
break; break;
default: default:
if (renderControlCharacters && charCode < 32) { if (renderControlCharacters && charCode < 32) {
partContent += String.fromCharCode(9216 + charCode); sb.write1(9216 + charCode);
partContentCnt++; partContentCnt++;
} else { } else {
partContent += String.fromCharCode(charCode); sb.write1(charCode);
partContentCnt++; partContentCnt++;
} }
} }
...@@ -701,13 +745,10 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput { ...@@ -701,13 +745,10 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput {
} }
characterMapping.setPartLength(partIndex, partContentCnt); characterMapping.setPartLength(partIndex, partContentCnt);
if (containsRTL) {
out += `<span dir="ltr" class="${partType}">${partContent}</span>`;
} else {
out += `<span class="${partType}">${partContent}</span>`;
}
} }
sb.appendASCIIString('</span>');
} }
// When getting client rects for the last character, we will position the // When getting client rects for the last character, we will position the
...@@ -715,10 +756,10 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput { ...@@ -715,10 +756,10 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput {
characterMapping.setPartData(len, parts.length - 1, charOffsetInPart); characterMapping.setPartData(len, parts.length - 1, charOffsetInPart);
if (isOverflowing) { if (isOverflowing) {
out += `<span>&hellip;</span>`; sb.appendASCIIString('<span>&hellip;</span>');
} }
out += '</span>'; sb.appendASCIIString('</span>');
return new RenderLineOutput(characterMapping, out, containsRTL, containsForeignElements); return new RenderLineOutput(characterMapping, containsRTL, containsForeignElements);
} }
...@@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; ...@@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { IModel } from 'vs/editor/common/editorCommon'; import { IModel } from 'vs/editor/common/editorCommon';
import { ColorId, MetadataConsts, FontStyle, TokenizationRegistry, ITokenizationSupport } from 'vs/editor/common/modes'; import { ColorId, MetadataConsts, FontStyle, TokenizationRegistry, ITokenizationSupport } from 'vs/editor/common/modes';
import { IModeService } from 'vs/editor/common/services/modeService'; 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 { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
import { LineTokens } from 'vs/editor/common/core/lineTokens'; import { LineTokens } from 'vs/editor/common/core/lineTokens';
import * as strings from 'vs/base/common/strings'; import * as strings from 'vs/base/common/strings';
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
'use strict'; 'use strict';
import * as assert from 'assert'; 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 { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
import { CharCode } from 'vs/base/common/charCode'; import { CharCode } from 'vs/base/common/charCode';
import { MetadataConsts } from 'vs/editor/common/modes'; import { MetadataConsts } from 'vs/editor/common/modes';
...@@ -56,7 +56,7 @@ suite('viewLineRenderer.renderLine', () => { ...@@ -56,7 +56,7 @@ suite('viewLineRenderer.renderLine', () => {
assertCharacterReplacement('a\0b', 4, 'a&#00;b', [[0, 1, 2, 3]], [3]); assertCharacterReplacement('a\0b', 4, 'a&#00;b', [[0, 1, 2, 3]], [3]);
assertCharacterReplacement('a' + String.fromCharCode(CharCode.UTF8_BOM) + 'b', 4, 'a\ufffdb', [[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\u2028b', 4, 'a\ufffdb', [[0, 1, 2, 3]], [3]);
assertCharacterReplacement('a\rb', 4, 'a&#8203b', [[0, 1, 2, 3]], [3]); assertCharacterReplacement('a\rb', 4, 'a&#8203;b', [[0, 1, 2, 3]], [3]);
}); });
test('handles tabs', () => { test('handles tabs', () => {
...@@ -355,10 +355,10 @@ suite('viewLineRenderer.renderLine', () => { ...@@ -355,10 +355,10 @@ suite('viewLineRenderer.renderLine', () => {
]; ];
let expectedOutput = [ let expectedOutput = [
'<span dir="ltr" class="mtk6">var</span>', '<span class="mtk6" dir="ltr">var</span>',
'<span dir="ltr" class="mtk1">&nbsp;קודמות&nbsp;=&nbsp;</span>', '<span class="mtk1" dir="ltr">&nbsp;קודמות&nbsp;=&nbsp;</span>',
'<span dir="ltr" class="mtk20">"מיותר&nbsp;קודמות&nbsp;צ\'ט&nbsp;של,&nbsp;אם&nbsp;לשון&nbsp;העברית&nbsp;שינויים&nbsp;ויש,&nbsp;אם"</span>', '<span class="mtk20" dir="ltr">"מיותר&nbsp;קודמות&nbsp;צ\'ט&nbsp;של,&nbsp;אם&nbsp;לשון&nbsp;העברית&nbsp;שינויים&nbsp;ויש,&nbsp;אם"</span>',
'<span dir="ltr" class="mtk1">;</span>' '<span class="mtk1" dir="ltr">;</span>'
].join(''); ].join('');
let _actual = renderViewLine(new RenderLineInput( let _actual = renderViewLine(new RenderLineInput(
...@@ -546,7 +546,7 @@ suite('viewLineRenderer.renderLine', () => { ...@@ -546,7 +546,7 @@ suite('viewLineRenderer.renderLine', () => {
let lineText = 'את גרמנית בהתייחסות שמו, שנתי המשפט אל חפש, אם כתב אחרים ולחבר. של התוכן אודות בויקיפדיה כלל, של עזרה כימיה היא. על עמוד יוצרים מיתולוגיה סדר, אם שכל שתפו לעברית שינויים, אם שאלות אנגלית עזה. שמות בקלות מה סדר.'; let lineText = 'את גרמנית בהתייחסות שמו, שנתי המשפט אל חפש, אם כתב אחרים ולחבר. של התוכן אודות בויקיפדיה כלל, של עזרה כימיה היא. על עמוד יוצרים מיתולוגיה סדר, אם שכל שתפו לעברית שינויים, אם שאלות אנגלית עזה. שמות בקלות מה סדר.';
let lineParts = [createPart(lineText.length, 1)]; let lineParts = [createPart(lineText.length, 1)];
let expectedOutput = [ let expectedOutput = [
'<span dir="ltr" class="mtk1">את&nbsp;גרמנית&nbsp;בהתייחסות&nbsp;שמו,&nbsp;שנתי&nbsp;המשפט&nbsp;אל&nbsp;חפש,&nbsp;אם&nbsp;כתב&nbsp;אחרים&nbsp;ולחבר.&nbsp;של&nbsp;התוכן&nbsp;אודות&nbsp;בויקיפדיה&nbsp;כלל,&nbsp;של&nbsp;עזרה&nbsp;כימיה&nbsp;היא.&nbsp;על&nbsp;עמוד&nbsp;יוצרים&nbsp;מיתולוגיה&nbsp;סדר,&nbsp;אם&nbsp;שכל&nbsp;שתפו&nbsp;לעברית&nbsp;שינויים,&nbsp;אם&nbsp;שאלות&nbsp;אנגלית&nbsp;עזה.&nbsp;שמות&nbsp;בקלות&nbsp;מה&nbsp;סדר.</span>' '<span class="mtk1" dir="ltr">את&nbsp;גרמנית&nbsp;בהתייחסות&nbsp;שמו,&nbsp;שנתי&nbsp;המשפט&nbsp;אל&nbsp;חפש,&nbsp;אם&nbsp;כתב&nbsp;אחרים&nbsp;ולחבר.&nbsp;של&nbsp;התוכן&nbsp;אודות&nbsp;בויקיפדיה&nbsp;כלל,&nbsp;של&nbsp;עזרה&nbsp;כימיה&nbsp;היא.&nbsp;על&nbsp;עמוד&nbsp;יוצרים&nbsp;מיתולוגיה&nbsp;סדר,&nbsp;אם&nbsp;שכל&nbsp;שתפו&nbsp;לעברית&nbsp;שינויים,&nbsp;אם&nbsp;שאלות&nbsp;אנגלית&nbsp;עזה.&nbsp;שמות&nbsp;בקלות&nbsp;מה&nbsp;סדר.</span>'
]; ];
let actual = renderViewLine(new RenderLineInput( let actual = renderViewLine(new RenderLineInput(
false, false,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册