minimap.ts 17.8 KB
Newer Older
A
Alex Dima 已提交
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

8
import 'vs/css!./minimap';
A
Alex Dima 已提交
9
import { ViewPart } from 'vs/editor/browser/view/viewPart';
A
Alex Dima 已提交
10 11
import { ViewContext } from 'vs/editor/common/view/viewContext';
import { IRenderingContext, IRestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
A
Alex Dima 已提交
12
import { getOrCreateMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer';
A
Alex Dima 已提交
13
import * as browser from 'vs/base/browser/browser';
A
Alex Dima 已提交
14
import { MinimapCharRenderer, ParsedColor, MinimapTokensColorTracker, Constants } from 'vs/editor/common/view/minimapCharRenderer';
A
Alex Dima 已提交
15
import * as editorCommon from 'vs/editor/common/editorCommon';
16
import { CharCode } from 'vs/base/common/charCode';
A
Alex Dima 已提交
17
import { IViewLayout, ViewLineData } from 'vs/editor/common/viewModel/viewModel';
A
Alex Dima 已提交
18
import { ColorId } from 'vs/editor/common/modes';
19 20
import { FastDomNode, createFastDomNode } from 'vs/base/browser/styleMutator';
import { IDisposable } from 'vs/base/common/lifecycle';
21
import { EditorScrollbar } from 'vs/editor/browser/viewParts/editorScrollbar/editorScrollbar';
22

23 24 25 26 27 28 29 30 31 32
const enum RenderMinimap {
	None = 0,
	Small = 1,
	Large = 2
}

class MinimapOptions {

	public readonly renderMinimap: RenderMinimap;

33 34
	public readonly pixelRatio: number;

35 36
	public readonly lineHeight: number;

37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
	/**
	 * container dom node width (in CSS px)
	 */
	public readonly minimapWidth: number;
	/**
	 * container dom node height (in CSS px)
	 */
	public readonly minimapHeight: number;

	/**
	 * canvas backing store width (in device px)
	 */
	public readonly canvasInnerWidth: number;
	/**
	 * canvas backing store height (in device px)
	 */
	public readonly canvasInnerHeight: number;

	/**
	 * canvas width (in CSS px)
	 */
	public readonly canvasOuterWidth: number;
	/**
	 * canvas height (in CSS px)
	 */
	public readonly canvasOuterHeight: number;

	constructor(configuration: editorCommon.IConfiguration) {
		const pixelRatio = browser.getPixelRatio();
		const layoutInfo = configuration.editor.layoutInfo;

		this.renderMinimap = layoutInfo.renderMinimap | 0;
69
		this.pixelRatio = pixelRatio;
70
		this.lineHeight = configuration.editor.lineHeight;
71 72 73 74 75 76 77 78 79 80 81 82
		this.minimapWidth = layoutInfo.minimapWidth;
		this.minimapHeight = layoutInfo.height;

		this.canvasInnerWidth = Math.floor(pixelRatio * this.minimapWidth);
		this.canvasInnerHeight = Math.floor(pixelRatio * this.minimapHeight);

		this.canvasOuterWidth = this.canvasInnerWidth / pixelRatio;
		this.canvasOuterHeight = this.canvasInnerHeight / pixelRatio;
	}

	public equals(other: MinimapOptions): boolean {
		return (this.renderMinimap === other.renderMinimap
83
			&& this.pixelRatio === other.pixelRatio
84
			&& this.lineHeight === other.lineHeight
85 86 87 88 89 90 91 92 93 94
			&& this.minimapWidth === other.minimapWidth
			&& this.minimapHeight === other.minimapHeight
			&& this.canvasInnerWidth === other.canvasInnerWidth
			&& this.canvasInnerHeight === other.canvasInnerHeight
			&& this.canvasOuterWidth === other.canvasOuterWidth
			&& this.canvasOuterHeight === other.canvasOuterHeight
		);
	}
}

95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
class MinimapLayout {

	/**
	 * slider dom node top (in CSS px)
	 */
	public readonly sliderTop: number;
	/**
	 * slider dom node height (in CSS px)
	 */
	public readonly sliderHeight: number;

	/**
	 * minimap render start line number.
	 */
	public readonly startLineNumber: number;
	/**
	 * minimap render end line number.
	 */
	public readonly endLineNumber: number;

	constructor(
A
Alex Dima 已提交
116
		lastRenderData: RenderData,
117 118 119
		options: MinimapOptions,
		viewportStartLineNumber: number,
		viewportEndLineNumber: number,
120
		viewportHeight: number,
121 122
		lineCount: number,
		scrollbarSliderCenter: number
123 124 125
	) {
		const pixelRatio = options.pixelRatio;
		const minimapLineHeight = (options.renderMinimap === RenderMinimap.Large ? Constants.x2_CHAR_HEIGHT : Constants.x1_CHAR_HEIGHT);
126
		const minimapLinesFitting = Math.floor(options.canvasInnerHeight / minimapLineHeight);
127
		const lineHeight = options.lineHeight;
128

A
Alex Dima 已提交
129 130 131 132 133 134 135 136 137 138
		// Sometimes, the number of rendered lines varies for a constant viewport height.
		// The reason is that only parts of the viewportStartLineNumber or viewportEndLineNumber are visible.
		// This leads to an apparent tremor in the minimap's slider height.
		// We try here to compensate, making the slider slightly incorrect in these cases, but more pleasing to the eye.
		let viewportLineCount = viewportEndLineNumber - viewportStartLineNumber + 1;
		const expectedViewportLineCount = Math.round(viewportHeight / lineHeight);
		if (viewportLineCount > expectedViewportLineCount) {
			viewportLineCount = expectedViewportLineCount;
		}

139 140 141 142 143
		if (minimapLinesFitting >= lineCount) {
			// All lines fit in the minimap => no minimap scrolling
			this.startLineNumber = 1;
			this.endLineNumber = lineCount;
		} else {
A
Alex Dima 已提交
144
			// The desire is to align (centers) the minimap's slider with the scrollbar's slider
145

A
Alex Dima 已提交
146
			// For a resolved this.startLineNumber, we can compute the minimap's slider's center with the following formula:
147
			// scrollbarSliderCenter = (viewportStartLineNumber - this.startLineNumber + viewportLineCount/2) * minimapLineHeight / pixelRatio;
A
Alex Dima 已提交
148
			// =>
149 150 151 152
			// scrollbarSliderCenter = (viewportStartLineNumber - this.startLineNumber + viewportLineCount/2) * minimapLineHeight / pixelRatio;
			// scrollbarSliderCenter * pixelRatio / minimapLineHeight = viewportStartLineNumber - this.startLineNumber + viewportLineCount/2
			// this.startLineNumber = viewportStartLineNumber + viewportLineCount/2 - scrollbarSliderCenter * pixelRatio / minimapLineHeight
			let desiredStartLineNumber = Math.floor(viewportStartLineNumber + viewportLineCount / 2 - scrollbarSliderCenter * pixelRatio / minimapLineHeight);
A
Alex Dima 已提交
153 154
			let desiredEndLineNumber = desiredStartLineNumber + minimapLinesFitting - 1;

A
Alex Dima 已提交
155 156 157
			// Aligning the slider's centers can result (correctly) in tremor.
			// i.e. scrolling down might result in the startLineNumber going up.
			// Avoid this tremor by being consistent w.r.t. the previous computed result
A
Alex Dima 已提交
158 159
			if (lastRenderData) {
				if (lastRenderData.viewportStartLineNumber <= viewportStartLineNumber) {
A
Alex Dima 已提交
160
					// going down => make sure we don't go above our previous decision
A
Alex Dima 已提交
161 162
					if (desiredStartLineNumber < lastRenderData.startLineNumber) {
						desiredStartLineNumber = lastRenderData.startLineNumber;
A
Alex Dima 已提交
163 164 165
						desiredEndLineNumber = desiredStartLineNumber + minimapLinesFitting - 1;
					}
				}
A
Alex Dima 已提交
166
				if (lastRenderData.viewportStartLineNumber >= viewportStartLineNumber) {
A
Alex Dima 已提交
167
					// going up => make sure we don't go below our previous decision
A
Alex Dima 已提交
168 169
					if (desiredEndLineNumber > lastRenderData.endLineNumber) {
						desiredEndLineNumber = lastRenderData.endLineNumber;
A
Alex Dima 已提交
170 171 172 173 174
						desiredStartLineNumber = desiredEndLineNumber - minimapLinesFitting + 1;
					}
				}
			}

A
Alex Dima 已提交
175 176 177 178 179 180 181 182 183 184 185 186
			// Aligning the slider's centers is a very good thing, but this would make
			// the minimap never scroll all the way to the top or to the bottom of the file.
			// We therefore check that the viewport lines are in the minimap viewport.

			// (a) validate on start line number
			if (desiredStartLineNumber < 1) {
				// must start after 1
				desiredStartLineNumber = 1;
				desiredEndLineNumber = desiredStartLineNumber + minimapLinesFitting - 1;
			}
			if (desiredStartLineNumber > viewportStartLineNumber) {
				// must contain the viewport's start line number
187
				desiredStartLineNumber = viewportStartLineNumber;
A
Alex Dima 已提交
188 189 190 191 192 193 194 195
				desiredEndLineNumber = desiredStartLineNumber + minimapLinesFitting - 1;
			}

			// (b) validate on end line number
			if (desiredEndLineNumber > lineCount) {
				// must end before line count
				desiredEndLineNumber = lineCount;
				desiredStartLineNumber = desiredEndLineNumber - minimapLinesFitting + 1;
196
			}
A
Alex Dima 已提交
197 198 199 200
			if (desiredEndLineNumber < viewportEndLineNumber) {
				// must contain the viewport's end line number
				desiredEndLineNumber = viewportEndLineNumber;
				desiredStartLineNumber = desiredEndLineNumber - minimapLinesFitting + 1;
201
			}
202

203
			this.startLineNumber = desiredStartLineNumber;
A
Alex Dima 已提交
204
			this.endLineNumber = desiredEndLineNumber;
205
		}
206 207

		this.sliderTop = Math.floor((viewportStartLineNumber - this.startLineNumber) * minimapLineHeight / pixelRatio);
A
Alex Dima 已提交
208
		this.sliderHeight = Math.floor(viewportLineCount * minimapLineHeight / pixelRatio);
A
Alex Dima 已提交
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
	}
}

class RenderData {
	/**
	 * editor viewport start line number.
	 */
	public readonly viewportStartLineNumber: number;
	/**
	 * editor viewport end line number.
	 */
	public readonly viewportEndLineNumber: number;

	/**
	 * minimap render start line number.
	 */
	public readonly startLineNumber: number;
	/**
	 * minimap render end line number.
	 */
	public readonly endLineNumber: number;

	constructor(
		viewportStartLineNumber: number,
		viewportEndLineNumber: number,
		startLineNumber: number,
		endLineNumber: number
	) {
A
Alex Dima 已提交
237
		this.viewportStartLineNumber = viewportStartLineNumber;
A
Alex Dima 已提交
238 239 240
		this.viewportEndLineNumber = viewportEndLineNumber;
		this.startLineNumber = startLineNumber;
		this.endLineNumber = endLineNumber;
241 242 243
	}
}

A
Alex Dima 已提交
244 245
export class Minimap extends ViewPart {

246
	private readonly _viewLayout: IViewLayout;
247
	private readonly _editorScrollbar: EditorScrollbar;
248

A
Alex Dima 已提交
249 250
	private readonly _domNode: FastDomNode<HTMLElement>;
	private readonly _canvas: FastDomNode<HTMLCanvasElement>;
251
	private readonly _slider: FastDomNode<HTMLElement>;
252 253
	private readonly _tokensColorTracker: MinimapTokensColorTracker;
	private readonly _tokensColorTrackerListener: IDisposable;
A
Alex Dima 已提交
254

A
Alex Dima 已提交
255 256
	private readonly _minimapCharRenderer: MinimapCharRenderer;

257
	private _options: MinimapOptions;
A
Alex Dima 已提交
258
	private _lastRenderData: RenderData;
259
	private _backgroundFillData: Uint8ClampedArray;
A
Alex Dima 已提交
260

261
	constructor(context: ViewContext, viewLayout: IViewLayout, editorScrollbar: EditorScrollbar) {
A
Alex Dima 已提交
262
		super(context);
263
		this._viewLayout = viewLayout;
264
		this._editorScrollbar = editorScrollbar;
A
Alex Dima 已提交
265

266
		this._options = new MinimapOptions(this._context.configuration);
A
Alex Dima 已提交
267
		this._lastRenderData = null;
268 269 270 271 272 273 274 275 276 277 278
		this._backgroundFillData = null;

		this._domNode = createFastDomNode(document.createElement('div'));
		this._domNode.setPosition('absolute');
		this._domNode.setRight(0);

		this._canvas = createFastDomNode(document.createElement('canvas'));
		this._canvas.setPosition('absolute');
		this._canvas.setLeft(0);
		this._domNode.domNode.appendChild(this._canvas.domNode);

279 280 281 282 283
		this._slider = createFastDomNode(document.createElement('div'));
		this._slider.setPosition('absolute');
		this._slider.setClassName('minimap-slider');
		this._domNode.domNode.appendChild(this._slider.domNode);

284 285 286
		this._tokensColorTracker = MinimapTokensColorTracker.getInstance();
		this._tokensColorTrackerListener = this._tokensColorTracker.onDidChange(() => this._backgroundFillData = null);

A
Alex Dima 已提交
287 288
		this._minimapCharRenderer = getOrCreateMinimapCharRenderer();

289
		this._applyLayout();
A
Alex Dima 已提交
290 291 292
	}

	public dispose(): void {
A
Alex Dima 已提交
293
		this._tokensColorTrackerListener.dispose();
A
Alex Dima 已提交
294
		super.dispose();
295 296 297 298 299 300 301 302 303 304 305
	}

	public getDomNode(): HTMLElement {
		return this._domNode.domNode;
	}

	private _applyLayout(): void {
		this._domNode.setWidth(this._options.minimapWidth);
		this._domNode.setHeight(this._options.minimapHeight);
		this._canvas.setWidth(this._options.canvasOuterWidth);
		this._canvas.setHeight(this._options.canvasOuterHeight);
A
Alex Dima 已提交
306 307
		this._canvas.domNode.width = this._options.canvasInnerWidth;
		this._canvas.domNode.height = this._options.canvasInnerHeight;
308
		this._slider.setWidth(this._options.minimapWidth);
309 310 311 312 313 314 315 316
		this._backgroundFillData = null;
	}

	private _getBackgroundFillData(): Uint8ClampedArray {
		if (this._backgroundFillData === null) {
			const WIDTH = this._options.canvasInnerWidth;
			const HEIGHT = this._options.canvasInnerHeight;

A
Alex Dima 已提交
317
			const background = this._tokensColorTracker.getColor(ColorId.DefaultBackground);
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
			const backgroundR = background.r;
			const backgroundG = background.g;
			const backgroundB = background.b;

			let result = new Uint8ClampedArray(WIDTH * HEIGHT * 4);
			let offset = 0;
			for (let i = 0; i < HEIGHT; i++) {
				for (let j = 0; j < WIDTH; j++) {
					result[offset] = backgroundR;
					result[offset + 1] = backgroundG;
					result[offset + 2] = backgroundB;
					result[offset + 3] = 255;
					offset += 4;
				}
			}

			this._backgroundFillData = result;
		}
		return this._backgroundFillData;
A
Alex Dima 已提交
337 338 339 340
	}

	// ---- begin view event handlers

341 342 343 344 345 346
	private _onOptionsMaybeChanged(): boolean {
		let opts = new MinimapOptions(this._context.configuration);
		if (this._options.equals(opts)) {
			return false;
		}
		this._options = opts;
A
Alex Dima 已提交
347
		this._lastRenderData = null;
348
		this._applyLayout();
A
Alex Dima 已提交
349 350
		return true;
	}
A
Alex Dima 已提交
351 352

	public onLineMappingChanged(): boolean {
A
Alex Dima 已提交
353
		this._lastRenderData = null;
A
Alex Dima 已提交
354 355 356
		return true;
	}
	public onModelFlushed(): boolean {
A
Alex Dima 已提交
357
		this._lastRenderData = null;
A
Alex Dima 已提交
358 359 360
		return true;
	}
	public onModelLinesDeleted(e: editorCommon.IViewLinesDeletedEvent): boolean {
A
Alex Dima 已提交
361
		// TODO@minimap: only do so when the lines are painted in the minimap
A
Alex Dima 已提交
362
		this._lastRenderData = null;
A
Alex Dima 已提交
363 364 365
		return true;
	}
	public onModelLineChanged(e: editorCommon.IViewLineChangedEvent): boolean {
A
Alex Dima 已提交
366
		// TODO@minimap: only do so when the lines are painted in the minimap
A
Alex Dima 已提交
367 368 369
		return true;
	}
	public onModelLinesInserted(e: editorCommon.IViewLinesInsertedEvent): boolean {
A
Alex Dima 已提交
370
		// TODO@minimap: only do so when the lines are painted in the minimap
A
Alex Dima 已提交
371
		this._lastRenderData = null;
A
Alex Dima 已提交
372 373 374 375 376 377
		return true;
	}
	public onModelTokensChanged(e: editorCommon.IViewTokensChangedEvent): boolean {
		// TODO@minimap: only do so when the lines are painted in the minimap
		return true;
	}
378 379 380 381 382 383
	public onConfigurationChanged(e: editorCommon.IConfigurationChangedEvent): boolean {
		return this._onOptionsMaybeChanged();
	}
	public onLayoutChanged(layoutInfo: editorCommon.EditorLayoutInfo): boolean {
		return this._onOptionsMaybeChanged();
	}
A
Alex Dima 已提交
384
	public onScrollChanged(e: editorCommon.IScrollEvent): boolean {
A
Alex Dima 已提交
385
		return e.scrollTopChanged || e.scrollHeightChanged;
386
	}
A
Alex Dima 已提交
387
	public onZonesChanged(): boolean {
A
Alex Dima 已提交
388
		this._lastRenderData = null;
A
Alex Dima 已提交
389 390
		return true;
	}
A
Alex Dima 已提交
391

A
Alex Dima 已提交
392
	// --- end event handlers
A
Alex Dima 已提交
393 394 395 396 397 398 399 400

	public prepareRender(ctx: IRenderingContext): void {
		// Nothing to read
		if (!this.shouldRender()) {
			throw new Error('I did not ask to render!');
		}
	}

401 402 403 404 405 406 407 408
	public render(renderingCtx: IRestrictedRenderingContext): void {
		const renderMinimap = this._options.renderMinimap;
		if (renderMinimap === RenderMinimap.None) {
			return;
		}

		const WIDTH = this._options.canvasInnerWidth;
		const HEIGHT = this._options.canvasInnerHeight;
A
Alex Dima 已提交
409
		const ctx = this._canvas.domNode.getContext('2d');
410 411
		const minimapLineHeight = (renderMinimap === RenderMinimap.Large ? Constants.x2_CHAR_HEIGHT : Constants.x1_CHAR_HEIGHT);
		const charWidth = (renderMinimap === RenderMinimap.Large ? Constants.x2_CHAR_WIDTH : Constants.x1_CHAR_WIDTH);
412

A
Alex Dima 已提交
413 414
		const layout = new MinimapLayout(
			this._lastRenderData,
415 416 417
			this._options,
			renderingCtx.visibleRange.startLineNumber,
			renderingCtx.visibleRange.endLineNumber,
418
			renderingCtx.viewportHeight,
419 420
			this._context.model.getLineCount(),
			this._editorScrollbar.getVerticalSliderVerticalCenter()
421
		);
A
Alex Dima 已提交
422

A
Alex Dima 已提交
423 424
		this._slider.setTop(layout.sliderTop);
		this._slider.setHeight(layout.sliderHeight);
A
Alex Dima 已提交
425

A
Alex Dima 已提交
426 427
		const startLineNumber = layout.startLineNumber;
		const endLineNumber = layout.endLineNumber;
A
Alex Dima 已提交
428

429 430 431 432
		// Prepare image data (fill with background color)
		let imageData = ctx.createImageData(WIDTH, HEIGHT);
		imageData.data.set(this._getBackgroundFillData());

A
Alex Dima 已提交
433
		let background = this._tokensColorTracker.getColor(ColorId.DefaultBackground);
434

A
Alex Dima 已提交
435
		let needed: boolean[] = [];
436
		for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
A
Alex Dima 已提交
437
			needed[lineNumber - startLineNumber] = true;
A
Alex Dima 已提交
438
		}
A
Alex Dima 已提交
439 440
		const data2 = this._context.model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed);
		const tabSize = data2.tabSize;
A
Alex Dima 已提交
441

A
Alex Dima 已提交
442
		// let start2 = performance.now();
443
		let dy = 0;
444
		for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
A
Alex Dima 已提交
445
			Minimap._renderLine(imageData, background, renderMinimap, charWidth, this._tokensColorTracker, this._minimapCharRenderer, dy, tabSize, data2.data[lineNumber - startLineNumber]);
446
			dy += minimapLineHeight;
A
Alex Dima 已提交
447
		}
A
Alex Dima 已提交
448 449
		// let end2 = performance.now();
		// console.log(`PAINTING MINIMAP TOOK ${end2 - start2} ms.`);
A
Alex Dima 已提交
450

A
Alex Dima 已提交
451 452 453 454 455 456 457
		this._lastRenderData = new RenderData(
			renderingCtx.visibleRange.startLineNumber,
			renderingCtx.visibleRange.endLineNumber,
			startLineNumber,
			endLineNumber
		);

458
		ctx.putImageData(imageData, 0, 0);
A
Alex Dima 已提交
459 460
	}

A
Alex Dima 已提交
461 462 463 464 465 466 467 468
	private static _renderLine(
		target: ImageData,
		backgroundColor: ParsedColor,
		renderMinimap: RenderMinimap,
		charWidth: number,
		colorTracker: MinimapTokensColorTracker,
		minimapCharRenderer: MinimapCharRenderer,
		dy: number,
A
Alex Dima 已提交
469
		tabSize: number,
A
Alex Dima 已提交
470
		lineData: ViewLineData
A
Alex Dima 已提交
471
	): void {
A
Alex Dima 已提交
472 473
		const content = lineData.content;
		const tokens = lineData.tokens;
474
		const maxDx = target.width - charWidth;
A
Alex Dima 已提交
475 476 477

		let dx = 0;
		let charIndex = 0;
478 479
		let tabsCharDelta = 0;

A
Alex Dima 已提交
480 481 482 483
		for (let tokenIndex = 0, tokensLen = tokens.length; tokenIndex < tokensLen; tokenIndex++) {
			const token = tokens[tokenIndex];
			const tokenEndIndex = token.endIndex;
			const tokenColorId = token.getForeground();
A
Alex Dima 已提交
484
			const tokenColor = colorTracker.getColor(tokenColorId);
A
Alex Dima 已提交
485 486

			for (; charIndex < tokenEndIndex; charIndex++) {
A
Alex Dima 已提交
487
				if (dx > maxDx) {
A
Alex Dima 已提交
488 489 490 491 492
					// hit edge of minimap
					return;
				}
				const charCode = content.charCodeAt(charIndex);

493 494 495 496
				if (charCode === CharCode.Tab) {
					let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize;
					tabsCharDelta += insertSpacesCount - 1;
					// No need to render anything since tab is invisible
497
					dx += insertSpacesCount * charWidth;
498 499
				} else if (charCode === CharCode.Space) {
					// No need to render anything since space is invisible
500
					dx += charWidth;
501
				} else {
502
					if (renderMinimap === RenderMinimap.Large) {
A
Alex Dima 已提交
503
						minimapCharRenderer.x2RenderChar(target, dx, dy, charCode, tokenColor, backgroundColor);
504
					} else {
A
Alex Dima 已提交
505
						minimapCharRenderer.x1RenderChar(target, dx, dy, charCode, tokenColor, backgroundColor);
506 507
					}
					dx += charWidth;
508
				}
A
Alex Dima 已提交
509 510 511 512
			}
		}
	}
}