viewLineRenderer.ts 6.7 KB
Newer Older
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

A
Alex Dima 已提交
7
import {ViewLineToken} from 'vs/editor/common/core/viewLineToken';
A
Alex Dima 已提交
8 9

export class RenderLineInput {
A
Alex Dima 已提交
10
	_renderLineInputBrand: void;
11 12 13

	lineContent: string;
	tabSize: number;
14
	spaceWidth: number;
15 16
	stopRenderingLineAfter: number;
	renderWhitespace: boolean;
17
	parts: ViewLineToken[];
A
Alex Dima 已提交
18 19 20 21

	constructor(
		lineContent: string,
		tabSize: number,
22
		spaceWidth: number,
A
Alex Dima 已提交
23 24
		stopRenderingLineAfter: number,
		renderWhitespace: boolean,
25
		parts: ViewLineToken[]
A
Alex Dima 已提交
26 27 28
	) {
		this.lineContent = lineContent;
		this.tabSize = tabSize;
29
		this.spaceWidth = spaceWidth;
A
Alex Dima 已提交
30 31 32 33
		this.stopRenderingLineAfter = stopRenderingLineAfter;
		this.renderWhitespace = renderWhitespace;
		this.parts = parts;
	}
34 35
}

A
Alex Dima 已提交
36
export class RenderLineOutput {
A
Alex Dima 已提交
37
	_renderLineOutputBrand: void;
38 39
	charOffsetInPart: number[];
	lastRenderedPartIndex: number;
A
Alex Dima 已提交
40
	output: string;
A
Alex Dima 已提交
41 42 43 44 45 46

	constructor(charOffsetInPart: number[], lastRenderedPartIndex: number, output: string) {
		this.charOffsetInPart = charOffsetInPart;
		this.lastRenderedPartIndex = lastRenderedPartIndex;
		this.output = output;
	}
47 48
}

A
Alex Dima 已提交
49 50 51 52 53 54 55 56
const _space = ' '.charCodeAt(0);
const _tab = '\t'.charCodeAt(0);
const _lowerThan = '<'.charCodeAt(0);
const _greaterThan = '>'.charCodeAt(0);
const _ampersand = '&'.charCodeAt(0);
const _carriageReturn = '\r'.charCodeAt(0);
const _lineSeparator = '\u2028'.charCodeAt(0); //http://www.fileformat.info/info/unicode/char/2028/index.htm
const _bom = 65279;
57

A
Alex Dima 已提交
58
export function renderLine(input:RenderLineInput): RenderLineOutput {
59
	const lineText = input.lineContent;
A
Alex Dima 已提交
60
	const lineTextLength = lineText.length;
61
	const tabSize = input.tabSize;
62
	const spaceWidth = input.spaceWidth;
A
Alex Dima 已提交
63 64 65
	const actualLineParts = input.parts;
	const renderWhitespace = input.renderWhitespace;
	const charBreakIndex = (input.stopRenderingLineAfter === -1 ? lineTextLength : input.stopRenderingLineAfter - 1);
66 67

	if (lineTextLength === 0) {
A
Alex Dima 已提交
68 69 70
		return new RenderLineOutput(
			[],
			0,
71
			// This is basically for IE's hit test to work
A
Alex Dima 已提交
72 73
			'<span><span>&nbsp;</span></span>'
		);
74 75 76 77 78 79
	}

	if (actualLineParts.length === 0) {
		throw new Error('Cannot render non empty line without line parts!');
	}

80
	return renderLineActual(lineText, lineTextLength, tabSize, spaceWidth, actualLineParts.slice(0), renderWhitespace, charBreakIndex);
A
Alex Dima 已提交
81 82 83
}

function isWhitespace(type:string): boolean {
84 85 86 87 88
	return (type.indexOf('whitespace') >= 0);
}

function isIndentGuide(type:string): boolean {
	return (type.indexOf('indent-guide') >= 0);
A
Alex Dima 已提交
89 90
}

A
Alex Dima 已提交
91
function renderLineActual(lineText:string, lineTextLength:number, tabSize:number, spaceWidth:number, actualLineParts:ViewLineToken[], renderWhitespace:boolean, charBreakIndex:number): RenderLineOutput {
A
Alex Dima 已提交
92 93 94 95
	lineTextLength = +lineTextLength;
	tabSize = +tabSize;
	charBreakIndex = +charBreakIndex;

A
Alex Dima 已提交
96
	let charIndex = 0;
A
Alex Dima 已提交
97
	let out = '';
A
Alex Dima 已提交
98 99
	let charOffsetInPartArr: number[] = [];
	let charOffsetInPart = 0;
100
	let tabsCharDelta = 0;
101

A
Alex Dima 已提交
102
	out += '<span>';
A
Alex Dima 已提交
103 104 105
	for (let partIndex = 0, partIndexLen = actualLineParts.length; partIndex < partIndexLen; partIndex++) {
		let part = actualLineParts[partIndex];

106 107
		let parsRendersWhitespace = (renderWhitespace && isWhitespace(part.type));
		let partIsFixedWidth = parsRendersWhitespace || isIndentGuide(part.type);
108

A
Alex Dima 已提交
109 110
		let toCharIndex = lineTextLength;
		if (partIndex + 1 < partIndexLen) {
A
Alex Dima 已提交
111 112
			let nextPart = actualLineParts[partIndex + 1];
			toCharIndex = Math.min(lineTextLength, nextPart.startIndex);
113 114
		}

A
Alex Dima 已提交
115
		charOffsetInPart = 0;
116
		if (partIsFixedWidth) {
117 118 119 120 121 122

			let partContentCnt = 0;
			let partContent = '';
			for (; charIndex < toCharIndex; charIndex++) {
				charOffsetInPartArr[charIndex] = charOffsetInPart;
				let charCode = lineText.charCodeAt(charIndex);
A
Alex Dima 已提交
123

124
				if (charCode === _tab) {
A
Alex Dima 已提交
125 126 127 128
					let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize;
					tabsCharDelta += insertSpacesCount - 1;
					charOffsetInPart += insertSpacesCount - 1;
					if (insertSpacesCount > 0) {
129
						partContent += parsRendersWhitespace ? '&rarr;' : '&nbsp;';
130
						partContentCnt++;
A
Alex Dima 已提交
131 132 133
						insertSpacesCount--;
					}
					while (insertSpacesCount > 0) {
134 135
						partContent += '&nbsp;';
						partContentCnt++;
A
Alex Dima 已提交
136 137
						insertSpacesCount--;
					}
138 139
				} else {
					// must be _space
140
					partContent += parsRendersWhitespace ? '&middot;' : '&nbsp;';
141 142 143 144 145 146 147 148 149 150
					partContentCnt++;
				}

				charOffsetInPart ++;

				if (charIndex >= charBreakIndex) {
					out += '<span class="token '+part.type+'" style="width:'+(spaceWidth * partContentCnt)+'px">';
					out += partContent;
					out += '&hellip;</span></span>';
					charOffsetInPartArr[charIndex] = charOffsetInPart;
A
Alex Dima 已提交
151 152 153 154 155
					return new RenderLineOutput(
						charOffsetInPartArr,
						partIndex,
						out
					);
156
				}
A
Alex Dima 已提交
157
			}
158 159 160 161 162 163 164 165 166
			out += '<span class="token '+part.type+'" style="width:'+(spaceWidth * partContentCnt)+'px">';
			out += partContent;
			out += '</span>';
		} else {
			out += '<span class="token ';
			out += part.type;
			out += '">';

			for (; charIndex < toCharIndex; charIndex++) {
A
Alex Dima 已提交
167
				charOffsetInPartArr[charIndex] = charOffsetInPart;
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
				let charCode = lineText.charCodeAt(charIndex);

				switch (charCode) {
					case _tab:
						let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize;
						tabsCharDelta += insertSpacesCount - 1;
						charOffsetInPart += insertSpacesCount - 1;
						while (insertSpacesCount > 0) {
							out += '&nbsp;';
							insertSpacesCount--;
						}
						break;

					case _space:
						out += '&nbsp;';
						break;

					case _lowerThan:
						out += '&lt;';
						break;

					case _greaterThan:
						out += '&gt;';
						break;

					case _ampersand:
						out += '&amp;';
						break;

					case 0:
						out += '&#00;';
						break;

					case _bom:
					case _lineSeparator:
						out += '\ufffd';
						break;

					case _carriageReturn:
						// zero width space, because carriage return would introduce a line break
						out += '&#8203';
						break;

					default:
						out += lineText.charAt(charIndex);
				}

				charOffsetInPart ++;

				if (charIndex >= charBreakIndex) {
					out += '&hellip;</span></span>';
					charOffsetInPartArr[charIndex] = charOffsetInPart;
A
Alex Dima 已提交
220 221 222 223 224
					return new RenderLineOutput(
						charOffsetInPartArr,
						partIndex,
						out
					);
225
				}
A
Alex Dima 已提交
226
			}
227 228

			out += '</span>';
A
Alex Dima 已提交
229
		}
230

231
	}
A
Alex Dima 已提交
232
	out += '</span>';
233 234 235

	// When getting client rects for the last character, we will position the
	// text range at the end of the span, insteaf of at the beginning of next span
A
Alex Dima 已提交
236
	charOffsetInPartArr.push(charOffsetInPart);
237

A
Alex Dima 已提交
238 239 240 241 242
	return new RenderLineOutput(
		charOffsetInPartArr,
		actualLineParts.length - 1,
		out
	);
243
}