提交 441c12d2 编写于 作者: A Alex Dima

Render a minimap

上级 c52f03cc
......@@ -49,6 +49,7 @@ import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler
import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents';
import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
import { EditorScrollbar } from 'vs/editor/browser/viewParts/editorScrollbar/editorScrollbar';
import { Minimap } from 'vs/editor/browser/viewParts/minimap/minimap';
export class View extends ViewEventHandler implements editorBrowser.IView, IDisposable {
......@@ -270,6 +271,9 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp
let rulers = new Rulers(this._context);
this.viewParts.push(rulers);
let minimap = new Minimap(this._context);
this.viewParts.push(minimap);
// -------------- Wire dom nodes up
this.linesContentContainer = this._scrollbar.getScrollbarContainerDomNode();
......@@ -292,6 +296,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp
this.overflowGuardContainer.appendChild(this.overlayWidgets.domNode);
this.overflowGuardContainer.appendChild(this.textArea);
this.overflowGuardContainer.appendChild(this.textAreaCover);
this.overflowGuardContainer.appendChild(minimap.domNode);
this.domNode.appendChild(this.overflowGuardContainer);
this.domNode.appendChild(this.contentWidgets.overflowingContentWidgetsDomNode);
}
......
/*---------------------------------------------------------------------------------------------
* 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 'vs/css!./overlayWidgets';
// import { StyleMutator } from 'vs/base/browser/styleMutator';
import { EditorLayoutInfo } from 'vs/editor/common/editorCommon';
// import { ClassNames, IOverlayWidget, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
import { ViewPart/*, PartFingerprint, PartFingerprints*/ } from 'vs/editor/browser/view/viewPart';
import { ViewContext } from 'vs/editor/common/view/viewContext';
import { IRenderingContext, IRestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
import { /*createMinimapCharRenderer,*/ createMinimapCharRenderer2 } from 'vs/editor/common/view/runtimeMinimapCharRenderer';
import * as browser from 'vs/base/browser/browser';
import { Constants } from 'vs/editor/common/view/minimapCharRenderer';
// let charRenderer = createMinimapCharRenderer();
let charRenderer2 = createMinimapCharRenderer2();
// interface IWidgetData {
// widget: IOverlayWidget;
// preference: OverlayWidgetPositionPreference;
// }
// interface IWidgetMap {
// [key: string]: IWidgetData;
// }
export class Minimap extends ViewPart {
// private _widgets: IWidgetMap;
public domNode: HTMLCanvasElement;
// private _verticalScrollbarWidth: number;
// private _horizontalScrollbarHeight: number;
// private _editorHeight: number;
// private _editorWidth: number;
private _minimapWidth: number;
private _minimapHeight: number;
private _viewportColumn: number;
constructor(context: ViewContext) {
super(context);
// this._widgets = {};
// this._verticalScrollbarWidth = 0;
// this._horizontalScrollbarHeight = 0;
// this._editorHeight = 0;
// this._editorWidth = 0;
this._minimapWidth = this._context.configuration.editor.layoutInfo.minimapWidth;
this._minimapHeight = this._context.configuration.editor.layoutInfo.height;
this._viewportColumn = this._context.configuration.editor.layoutInfo.viewportColumn;
this.domNode = document.createElement('canvas');
this.domNode.style.position = 'absolute';
this.domNode.style.right = '0';
this.domNode.style.width = `${this._minimapWidth}px`;
this.domNode.style.height = `${this._minimapHeight}px`;
// PartFingerprints.write(this.domNode, PartFingerprint.OverlayWidgets);
// this.domNode.className = ClassNames.OVERLAY_WIDGETS;
}
public dispose(): void {
super.dispose();
// this._widgets = null;
}
// ---- begin view event handlers
public onLayoutChanged(layoutInfo: EditorLayoutInfo): boolean {
this._minimapWidth = this._context.configuration.editor.layoutInfo.minimapWidth;
this._minimapHeight = this._context.configuration.editor.layoutInfo.height;
this._viewportColumn = this._context.configuration.editor.layoutInfo.viewportColumn;
this.domNode.style.width = `${this._minimapWidth}px`;
this.domNode.width = this._minimapWidth;
this.domNode.style.height = `${this._minimapHeight}px`;
this.domNode.height = this._minimapHeight;
// this._verticalScrollbarWidth = layoutInfo.verticalScrollbarWidth;
// this._horizontalScrollbarHeight = layoutInfo.horizontalScrollbarHeight;
// this._editorHeight = layoutInfo.height;
// this._editorWidth = layoutInfo.width;
return true;
}
// ---- end view event handlers
// public addWidget(widget: IOverlayWidget): void {
// this._widgets[widget.getId()] = {
// widget: widget,
// preference: null
// };
// // This is sync because a widget wants to be in the dom
// let domNode = widget.getDomNode();
// domNode.style.position = 'absolute';
// domNode.setAttribute('widgetId', widget.getId());
// this.domNode.appendChild(domNode);
// this.setShouldRender();
// }
// public setWidgetPosition(widget: IOverlayWidget, preference: OverlayWidgetPositionPreference): boolean {
// let widgetData = this._widgets[widget.getId()];
// if (widgetData.preference === preference) {
// return false;
// }
// widgetData.preference = preference;
// this.setShouldRender();
// return true;
// }
// public removeWidget(widget: IOverlayWidget): void {
// let widgetId = widget.getId();
// if (this._widgets.hasOwnProperty(widgetId)) {
// let widgetData = this._widgets[widgetId];
// let domNode = widgetData.widget.getDomNode();
// delete this._widgets[widgetId];
// domNode.parentNode.removeChild(domNode);
// this.setShouldRender();
// }
// }
// private _renderWidget(widgetData: IWidgetData): void {
// let _RESTORE_STYLE_TOP = 'data-editor-restoreStyleTop';
// let domNode = widgetData.widget.getDomNode();
// if (widgetData.preference === null) {
// if (domNode.hasAttribute(_RESTORE_STYLE_TOP)) {
// let previousTop = domNode.getAttribute(_RESTORE_STYLE_TOP);
// domNode.removeAttribute(_RESTORE_STYLE_TOP);
// domNode.style.top = previousTop;
// }
// return;
// }
// if (widgetData.preference === OverlayWidgetPositionPreference.TOP_RIGHT_CORNER) {
// if (!domNode.hasAttribute(_RESTORE_STYLE_TOP)) {
// domNode.setAttribute(_RESTORE_STYLE_TOP, domNode.style.top);
// }
// StyleMutator.setTop(domNode, 0);
// StyleMutator.setRight(domNode, (2 * this._verticalScrollbarWidth));
// } else if (widgetData.preference === OverlayWidgetPositionPreference.BOTTOM_RIGHT_CORNER) {
// if (!domNode.hasAttribute(_RESTORE_STYLE_TOP)) {
// domNode.setAttribute(_RESTORE_STYLE_TOP, domNode.style.top);
// }
// let widgetHeight = domNode.clientHeight;
// StyleMutator.setTop(domNode, (this._editorHeight - widgetHeight - 2 * this._horizontalScrollbarHeight));
// StyleMutator.setRight(domNode, (2 * this._verticalScrollbarWidth));
// } else if (widgetData.preference === OverlayWidgetPositionPreference.TOP_CENTER) {
// if (!domNode.hasAttribute(_RESTORE_STYLE_TOP)) {
// domNode.setAttribute(_RESTORE_STYLE_TOP, domNode.style.top);
// }
// StyleMutator.setTop(domNode, 0);
// domNode.style.right = '50%';
// }
// }
public prepareRender(ctx: IRenderingContext): void {
// Nothing to read
if (!this.shouldRender()) {
throw new Error('I did not ask to render!');
}
}
public render(ctx: IRestrictedRenderingContext): void {
let pixelRatio = browser.getPixelRatio();
console.log(pixelRatio);
const WIDTH = pixelRatio * this._minimapWidth;
const HEIGHT = pixelRatio * this._minimapHeight;
this.domNode.width = WIDTH;
this.domNode.height = HEIGHT;
this.domNode.style.background = '#000';
let lineCount = Math.floor(HEIGHT / 4);
// let data = this._context.model.getMinimapLineRenderingData(2);
const lineLen = (this._viewportColumn - 1);
// let linePixelData = new Uint8ClampedArray(
// 8 * 4 * lineLen
// );
let start = performance.now();
// console.profile();
let ctx2 = this.domNode.getContext('2d');
let imageData = ctx2.createImageData(lineLen * 2, 4 * lineCount);
// set up the background
// let offset = 0;
// for (let i = 0; i < HEIGHT; i++) {
// for (let j = 0; j < WIDTH; j++) {
// imageData.data[offset] = 0;
// imageData.data[offset + 1] = 0;
// imageData.data[offset + 2] = 0;
// imageData.data[offset + 3] = 255;
// offset += 4;
// }
// }
for (let lineIndex = 0; lineIndex < lineCount; lineIndex++) {
let data = this._context.model.getMinimapLineRenderingData(lineIndex + 1);
let length = Math.min(data.content.length, lineLen);
let dy = lineIndex * Constants.x2_CHAR_HEIGHT;
for (let charIndex = 0; charIndex < length; charIndex++) {
let charCode = data.content.charCodeAt(charIndex);
let dx = charIndex * Constants.x2_CHAR_WIDTH;
charRenderer2.x2RenderChar(imageData, dx, dy, charCode);
// charRenderer2.x2RenderChar(imageData.data, lineLen, lineIndex, charIndex, charCode);
}
// break;
}
// console.log(imageData.data);
// ctx2.strokeStyle = '#000';
// for (i = 0; i <)
// ctx2.fillRect(0, 0, this._minimapWidth, this._minimapHeight);
ctx2.putImageData(imageData, 0, 0);
// console.profileEnd();
let end = performance.now();
console.log('TOOK ' + (end - start) + 'ms.');
// let data = this._context.model.getViewLineRenderingData(null, 1);
// console.log(data);
// let dest =
// StyleMutator.setWidth(this.domNode, this._editorWidth);
// let keys = Object.keys(this._widgets);
// for (let i = 0, len = keys.length; i < len; i++) {
// let widgetId = keys[i];
// this._renderWidget(this._widgets[widgetId]);
// }
}
}
......@@ -13,7 +13,7 @@ import { Extensions, IConfigurationRegistry, IConfigurationNode } from 'vs/platf
import { Registry } from 'vs/platform/platform';
import { DefaultConfig, DEFAULT_INDENTATION, DEFAULT_TRIM_AUTO_WHITESPACE } from 'vs/editor/common/config/defaultConfig';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { EditorLayoutProvider } from 'vs/editor/common/viewLayout/editorLayoutProvider';
import { EditorLayoutProvider, Minimap } from 'vs/editor/common/viewLayout/editorLayoutProvider';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { FontInfo, BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { Constants } from 'vs/editor/common/core/uint';
......@@ -177,13 +177,15 @@ class InternalEditorOptionsHelper {
lineHeight: fontInfo.lineHeight,
showLineNumbers: renderLineNumbers,
lineNumbersMinChars: lineNumbersMinChars,
maxLineNumber: maxLineNumber,
lineDecorationsWidth: lineDecorationsWidth,
typicalHalfwidthCharacterWidth: fontInfo.typicalHalfwidthCharacterWidth,
maxDigitWidth: fontInfo.maxDigitWidth,
maxLineNumber: maxLineNumber,
verticalScrollbarWidth: scrollbar.verticalScrollbarSize,
horizontalScrollbarHeight: scrollbar.horizontalScrollbarSize,
scrollbarArrowSize: scrollbar.arrowSize,
verticalScrollbarHasArrows: scrollbar.verticalHasArrows
verticalScrollbarHasArrows: scrollbar.verticalHasArrows,
minimap: Minimap.Large // TODO@minimap
});
if (isDominatedByLongLines && wrappingColumn > 0) {
......@@ -196,13 +198,13 @@ class InternalEditorOptionsHelper {
// If viewport width wrapping is enabled
bareWrappingInfo = {
isViewportWrapping: true,
wrappingColumn: Math.max(1, Math.floor((layoutInfo.contentWidth - layoutInfo.verticalScrollbarWidth) / fontInfo.typicalHalfwidthCharacterWidth))
wrappingColumn: Math.max(1, layoutInfo.viewportColumn)
};
} else if (wrappingColumn > 0 && wordWrap === true) {
// Enable smart viewport wrapping
bareWrappingInfo = {
isViewportWrapping: true,
wrappingColumn: Math.min(wrappingColumn, Math.floor((layoutInfo.contentWidth - layoutInfo.verticalScrollbarWidth) / fontInfo.typicalHalfwidthCharacterWidth))
wrappingColumn: Math.min(wrappingColumn, layoutInfo.viewportColumn)
};
} else if (wrappingColumn > 0) {
// Wrapping is enabled
......
......@@ -5,7 +5,7 @@
'use strict';
import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding';
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
import { ViewLineTokenFactory, ViewLineToken, ViewLineToken2 } from 'vs/editor/common/core/viewLineToken';
import { ColorId, FontStyle, StandardTokenType, LanguageId } from 'vs/editor/common/modes';
const STANDARD_TOKEN_TYPE_REGEXP = /\b(comment|string|regex)\b/;
......@@ -159,7 +159,7 @@ export class LineTokens {
* @return The index of the token containing the offset.
*/
public findTokenIndexAtOffset(offset: number): number {
return TokenMetadata.findIndexInSegmentsArray(this._tokens, offset);
return ViewLineTokenFactory.findIndexInSegmentsArray(this._tokens, offset);
}
public findTokenAtOffset(offset: number): LineToken {
......@@ -194,10 +194,18 @@ export class LineTokens {
}
public inflate(): ViewLineToken[] {
return TokenMetadata.inflateArr(this._tokens, this._textLength);
return ViewLineTokenFactory.inflateArr(this._tokens, this._textLength);
}
public sliceAndInflate(startOffset: number, endOffset: number, deltaOffset: number): ViewLineToken[] {
return TokenMetadata.sliceAndInflate(this._tokens, startOffset, endOffset, deltaOffset, this._textLength);
return ViewLineTokenFactory.sliceAndInflate(this._tokens, startOffset, endOffset, deltaOffset, this._textLength);
}
public inflate2(): ViewLineToken2[] {
return ViewLineTokenFactory.inflateArr2(this._tokens, this._textLength);
}
public sliceAndInflate2(startOffset: number, endOffset: number, deltaOffset: number): ViewLineToken2[] {
return ViewLineTokenFactory.sliceAndInflate2(this._tokens, startOffset, endOffset, deltaOffset, this._textLength);
}
}
......@@ -4,6 +4,9 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ColorId } from 'vs/editor/common/modes';
import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding';
/**
* A token on a line.
*/
......@@ -42,3 +45,117 @@ export class ViewLineToken {
return true;
}
}
/**
* A token on a line.
*/
export class ViewLineToken2 {
_viewLineToken2Brand: void;
/**
* last char index of this token (not inclusive).
*/
public readonly endIndex: number;
private readonly _metadata: number;
constructor(endIndex: number, metadata: number) {
this.endIndex = endIndex;
this._metadata = metadata;
}
public getForeground(): ColorId {
return TokenMetadata.getForeground(this._metadata);
}
}
export class ViewLineTokenFactory {
public static inflateArr(tokens: Uint32Array, lineLength: number): ViewLineToken[] {
let result: ViewLineToken[] = [];
for (let i = 0, len = (tokens.length >>> 1); i < len; i++) {
let endOffset = (i + 1 < len ? tokens[((i + 1) << 1)] : lineLength);
let metadata = tokens[(i << 1) + 1];
result[i] = new ViewLineToken(endOffset, TokenMetadata.getClassNameFromMetadata(metadata));
}
return result;
}
public static inflateArr2(tokens: Uint32Array, lineLength: number): ViewLineToken2[] {
let result: ViewLineToken2[] = [];
for (let i = 0, len = (tokens.length >>> 1); i < len; i++) {
let endOffset = (i + 1 < len ? tokens[((i + 1) << 1)] : lineLength);
let metadata = tokens[(i << 1) + 1];
result[i] = new ViewLineToken2(endOffset, metadata);
}
return result;
}
public static sliceAndInflate(tokens: Uint32Array, startOffset: number, endOffset: number, deltaOffset: number, lineLength: number): ViewLineToken[] {
let tokenIndex = this.findIndexInSegmentsArray(tokens, startOffset);
let result: ViewLineToken[] = [], resultLen = 0;
for (let i = tokenIndex, len = (tokens.length >>> 1); i < len; i++) {
let tokenStartOffset = tokens[(i << 1)];
if (tokenStartOffset >= endOffset) {
break;
}
let tokenEndOffset = (i + 1 < len ? tokens[((i + 1) << 1)] : lineLength);
let newEndOffset = tokenEndOffset - startOffset + deltaOffset;
let metadata = tokens[(i << 1) + 1];
result[resultLen++] = new ViewLineToken(newEndOffset, TokenMetadata.getClassNameFromMetadata(metadata));
}
return result;
}
public static sliceAndInflate2(tokens: Uint32Array, startOffset: number, endOffset: number, deltaOffset: number, lineLength: number): ViewLineToken2[] {
let tokenIndex = this.findIndexInSegmentsArray(tokens, startOffset);
let result: ViewLineToken2[] = [], resultLen = 0;
for (let i = tokenIndex, len = (tokens.length >>> 1); i < len; i++) {
let tokenStartOffset = tokens[(i << 1)];
if (tokenStartOffset >= endOffset) {
break;
}
let tokenEndOffset = (i + 1 < len ? tokens[((i + 1) << 1)] : lineLength);
let newEndOffset = tokenEndOffset - startOffset + deltaOffset;
let metadata = tokens[(i << 1) + 1];
result[resultLen++] = new ViewLineToken2(newEndOffset, metadata);
}
return result;
}
public static findIndexInSegmentsArray(tokens: Uint32Array, desiredIndex: number): number {
let low = 0;
let high = (tokens.length >>> 1) - 1;
while (low < high) {
let mid = low + Math.ceil((high - low) / 2);
let value = tokens[(mid << 1)];
if (value > desiredIndex) {
high = mid - 1;
} else {
low = mid;
}
}
return low;
}
}
......@@ -2810,6 +2810,16 @@ export class EditorLayoutInfo {
*/
readonly contentHeight: number;
/**
* The width of the minimap
*/
readonly minimapWidth: number;
/**
* The number of columns (of typical characters) fitting on a viewport line.
*/
readonly viewportColumn: number;
/**
* The width of the vertical scrollbar.
*/
......@@ -2842,6 +2852,8 @@ export class EditorLayoutInfo {
contentLeft: number;
contentWidth: number;
contentHeight: number;
minimapWidth: number;
viewportColumn: number;
verticalScrollbarWidth: number;
horizontalScrollbarHeight: number;
overviewRuler: OverviewRulerPosition;
......@@ -2860,6 +2872,8 @@ export class EditorLayoutInfo {
this.contentLeft = source.contentLeft | 0;
this.contentWidth = source.contentWidth | 0;
this.contentHeight = source.contentHeight | 0;
this.minimapWidth = source.minimapWidth | 0;
this.viewportColumn = source.viewportColumn | 0;
this.verticalScrollbarWidth = source.verticalScrollbarWidth | 0;
this.horizontalScrollbarHeight = source.horizontalScrollbarHeight | 0;
this.overviewRuler = source.overviewRuler.clone();
......@@ -2884,6 +2898,8 @@ export class EditorLayoutInfo {
&& this.contentLeft === other.contentLeft
&& this.contentWidth === other.contentWidth
&& this.contentHeight === other.contentHeight
&& this.minimapWidth === other.minimapWidth
&& this.viewportColumn === other.viewportColumn
&& this.verticalScrollbarWidth === other.verticalScrollbarWidth
&& this.horizontalScrollbarHeight === other.horizontalScrollbarHeight
&& this.overviewRuler.equals(other.overviewRuler)
......
......@@ -4,35 +4,10 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
import { ColorId, FontStyle, StandardTokenType, MetadataConsts, LanguageId } from 'vs/editor/common/modes';
export class TokenMetadata {
public static toBinaryStr(metadata: number): string {
let r = metadata.toString(2);
while (r.length < 32) {
r = '0' + r;
}
return r;
}
public static printMetadata(metadata: number): void {
let languageId = TokenMetadata.getLanguageId(metadata);
let tokenType = TokenMetadata.getTokenType(metadata);
let fontStyle = TokenMetadata.getFontStyle(metadata);
let foreground = TokenMetadata.getForeground(metadata);
let background = TokenMetadata.getBackground(metadata);
console.log({
languageId: languageId,
tokenType: tokenType,
fontStyle: fontStyle,
foreground: foreground,
background: background,
});
}
public static getLanguageId(metadata: number): LanguageId {
return (metadata & MetadataConsts.LANGUAGEID_MASK) >>> MetadataConsts.LANGUAGEID_OFFSET;
}
......@@ -53,7 +28,7 @@ export class TokenMetadata {
return (metadata & MetadataConsts.BACKGROUND_MASK) >>> MetadataConsts.BACKGROUND_OFFSET;
}
private static _getClassNameFromMetadata(metadata: number): string {
public static getClassNameFromMetadata(metadata: number): string {
let foreground = this.getForeground(metadata);
let className = 'mtk' + foreground;
......@@ -70,59 +45,4 @@ export class TokenMetadata {
return className;
}
public static inflateArr(tokens: Uint32Array, lineLength: number): ViewLineToken[] {
let result: ViewLineToken[] = [];
for (let i = 0, len = (tokens.length >>> 1); i < len; i++) {
let endOffset = (i + 1 < len ? tokens[((i + 1) << 1)] : lineLength);
let metadata = tokens[(i << 1) + 1];
result[i] = new ViewLineToken(endOffset, this._getClassNameFromMetadata(metadata));
}
return result;
}
public static sliceAndInflate(tokens: Uint32Array, startOffset: number, endOffset: number, deltaOffset: number, lineLength: number): ViewLineToken[] {
let tokenIndex = this.findIndexInSegmentsArray(tokens, startOffset);
let result: ViewLineToken[] = [], resultLen = 0;
for (let i = tokenIndex, len = (tokens.length >>> 1); i < len; i++) {
let tokenStartOffset = tokens[(i << 1)];
if (tokenStartOffset >= endOffset) {
break;
}
let tokenEndOffset = (i + 1 < len ? tokens[((i + 1) << 1)] : lineLength);
let newEndOffset = tokenEndOffset - startOffset + deltaOffset;
let metadata = tokens[(i << 1) + 1];
result[resultLen++] = new ViewLineToken(newEndOffset, this._getClassNameFromMetadata(metadata));
}
return result;
}
public static findIndexInSegmentsArray(tokens: Uint32Array, desiredIndex: number): number {
let low = 0;
let high = (tokens.length >>> 1) - 1;
while (low < high) {
let mid = low + Math.ceil((high - low) / 2);
let value = tokens[(mid << 1)];
if (value > desiredIndex) {
high = mid - 1;
} else {
low = mid;
}
}
return low;
}
}
......@@ -25,6 +25,131 @@ export const enum Constants {
CA_CHANNELS_CNT = 2,
}
export class MinimapCharRenderer2 {
_minimapCharRendererBrand: void;
public static create(x2CharData: Uint8ClampedArray, x1CharData: Uint8ClampedArray): MinimapCharRenderer2 {
let _x2CharData = this.toGrayscale(x2CharData);
let _x1CharData = this.toGrayscale(x1CharData);
return new MinimapCharRenderer2(_x2CharData, _x1CharData);
}
private static toGrayscale(charData: Uint8ClampedArray): Uint8ClampedArray {
let newLength = charData.length / 2;
let result = new Uint8ClampedArray(newLength);
let sourceOffset = 0;
for (var i = 0; i < newLength; i++) {
let color = charData[sourceOffset];
let alpha = charData[sourceOffset + 1];
let newColor = Math.round((color * alpha) / 255);
result[i] = newColor;
// console.log(`${color}, ${alpha} => ${newColor}`);
sourceOffset += 2;
}
return result;
}
public readonly x2charData: Uint8ClampedArray;
public readonly x1charData: Uint8ClampedArray;
constructor(x2CharData: Uint8ClampedArray, x1CharData: Uint8ClampedArray) {
const x2ExpectedLen = Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH /** Constants.CA_CHANNELS_CNT*/ * Constants.CHAR_COUNT;
if (x2CharData.length !== x2ExpectedLen) {
throw new Error('Invalid x2CharData');
}
const x1ExpectedLen = Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH /** Constants.CA_CHANNELS_CNT*/ * Constants.CHAR_COUNT;
if (x1CharData.length !== x1ExpectedLen) {
throw new Error('Invalid x1CharData');
}
this.x2charData = x2CharData;
this.x1charData = x1CharData;
}
private static _getChIndex(chCode: number): number {
if (chCode < Constants.START_CH_CODE || chCode > Constants.END_CH_CODE) {
chCode = CharCode.N;
}
return chCode - Constants.START_CH_CODE;
}
public x2RenderChar(target: ImageData, dx: number, dy: number, chCode: number): void {
const x2CharData = this.x2charData;
const chIndex = MinimapCharRenderer2._getChIndex(chCode);
const sourceOffset = chIndex * Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH;
const c1 = x2CharData[sourceOffset];
const c2 = x2CharData[sourceOffset + 1];
const c3 = x2CharData[sourceOffset + 2];
const c4 = x2CharData[sourceOffset + 3];
const c5 = x2CharData[sourceOffset + 4];
const c6 = x2CharData[sourceOffset + 5];
const c7 = x2CharData[sourceOffset + 6];
const c8 = x2CharData[sourceOffset + 7];
const outWidth = target.width * Constants.RGBA_CHANNELS_CNT;
let resultOffset = dy * outWidth + dx * Constants.RGBA_CHANNELS_CNT;
const dest = target.data;
dest[resultOffset + 0] = c1;
dest[resultOffset + 1] = c1;
dest[resultOffset + 2] = c1;
dest[resultOffset + 3] = 255;
dest[resultOffset + 4] = c2;
dest[resultOffset + 5] = c2;
dest[resultOffset + 6] = c2;
dest[resultOffset + 7] = 255;
resultOffset += outWidth;
dest[resultOffset + 0] = c3;
dest[resultOffset + 1] = c3;
dest[resultOffset + 2] = c3;
dest[resultOffset + 3] = 255;
dest[resultOffset + 4] = c4;
dest[resultOffset + 5] = c4;
dest[resultOffset + 6] = c4;
dest[resultOffset + 7] = 255;
resultOffset += outWidth;
dest[resultOffset + 0] = c5;
dest[resultOffset + 1] = c5;
dest[resultOffset + 2] = c5;
dest[resultOffset + 3] = 255;
dest[resultOffset + 4] = c6;
dest[resultOffset + 5] = c6;
dest[resultOffset + 6] = c6;
dest[resultOffset + 7] = 255;
resultOffset += outWidth;
dest[resultOffset + 0] = c7;
dest[resultOffset + 1] = c7;
dest[resultOffset + 2] = c7;
dest[resultOffset + 3] = 255;
dest[resultOffset + 4] = c8;
dest[resultOffset + 5] = c8;
dest[resultOffset + 6] = c8;
dest[resultOffset + 7] = 255;
}
public x1RenderChar(target: ImageData, dx: number, dy: number, chCode: number): void {
const x1CharData = this.x1charData;
const chIndex = MinimapCharRenderer2._getChIndex(chCode);
const sourceOffset = chIndex * Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH;
const c1 = x1CharData[sourceOffset];
const c2 = x1CharData[sourceOffset + 1];
const outWidth = target.width * Constants.RGBA_CHANNELS_CNT;
let resultOffset = dy * outWidth + dx * Constants.RGBA_CHANNELS_CNT;
const dest = target.data;
dest[resultOffset + 0] = c1;
dest[resultOffset + 1] = c1;
dest[resultOffset + 2] = c1;
dest[resultOffset + 3] = 255;
resultOffset += outWidth;
dest[resultOffset + 0] = c2;
dest[resultOffset + 1] = c2;
dest[resultOffset + 2] = c2;
dest[resultOffset + 3] = 255;
}
}
export class MinimapCharRenderer {
_minimapCharRendererBrand: void;
......@@ -79,7 +204,11 @@ export class MinimapCharRenderer {
const c8 = x2CharData[sourceOffset + 14];
const a8 = x2CharData[sourceOffset + 15];
// console.log(c1, a1, c2, a2, c3, a3, c4, a4, c5, a5, c6, a6, c7, a7, c8, a8);
let resultOffset = Constants.x2_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * charIndex;
resultOffset += lineIndex * Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * lineLen;
target[resultOffset + 0] = c1;
target[resultOffset + 1] = c1;
target[resultOffset + 2] = c1;
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { MinimapCharRenderer } from 'vs/editor/common/view/minimapCharRenderer';
import { MinimapCharRenderer, MinimapCharRenderer2 } from 'vs/editor/common/view/minimapCharRenderer';
function toUint8ClampedArrat(arr: number[]): Uint8ClampedArray {
let r = new Uint8ClampedArray(arr.length);
......@@ -31,6 +31,10 @@ export function createMinimapCharRenderer(): MinimapCharRenderer {
init();
return new MinimapCharRenderer(_x2Data, _x1Data);
}
export function createMinimapCharRenderer2(): MinimapCharRenderer2 {
init();
return MinimapCharRenderer2.create(_x2Data, _x1Data);
}
var x2Data = [
......
......@@ -6,6 +6,13 @@
import { EditorLayoutInfo, OverviewRulerPosition } from 'vs/editor/common/editorCommon';
// TODO@minimap
export const enum Minimap {
None = 0,
Small = 1,
Large = 2
}
export interface IEditorLayoutProviderOpts {
outerWidth: number;
outerHeight: number;
......@@ -15,15 +22,19 @@ export interface IEditorLayoutProviderOpts {
showLineNumbers: boolean;
lineNumbersMinChars: number;
maxLineNumber: number;
lineDecorationsWidth: number;
maxDigitWidth: number;
maxLineNumber: number;
typicalHalfwidthCharacterWidth: number;
maxDigitWidth: number;
verticalScrollbarWidth: number;
verticalScrollbarHasArrows: boolean;
scrollbarArrowSize: number;
horizontalScrollbarHeight: number;
minimap: Minimap;
}
export class EditorLayoutProvider {
......@@ -34,13 +45,15 @@ export class EditorLayoutProvider {
const lineHeight = _opts.lineHeight | 0;
const showLineNumbers = Boolean(_opts.showLineNumbers);
const lineNumbersMinChars = _opts.lineNumbersMinChars | 0;
const maxLineNumber = _opts.maxLineNumber | 0;
const lineDecorationsWidth = _opts.lineDecorationsWidth | 0;
const typicalHalfwidthCharacterWidth = Number(_opts.typicalHalfwidthCharacterWidth);
const maxDigitWidth = Number(_opts.maxDigitWidth);
const maxLineNumber = _opts.maxLineNumber | 0;
const verticalScrollbarWidth = _opts.verticalScrollbarWidth | 0;
const verticalScrollbarHasArrows = Boolean(_opts.verticalScrollbarHasArrows);
const scrollbarArrowSize = _opts.scrollbarArrowSize | 0;
const horizontalScrollbarHeight = _opts.horizontalScrollbarHeight | 0;
const minimap: Minimap = _opts.minimap | 0;
let lineNumbersWidth = 0;
if (showLineNumbers) {
......@@ -53,13 +66,40 @@ export class EditorLayoutProvider {
glyphMarginWidth = lineHeight;
}
let contentWidth = outerWidth - glyphMarginWidth - lineNumbersWidth - lineDecorationsWidth;
let glyphMarginLeft = 0;
let lineNumbersLeft = glyphMarginLeft + glyphMarginWidth;
let decorationsLeft = lineNumbersLeft + lineNumbersWidth;
let contentLeft = decorationsLeft + lineDecorationsWidth;
let remainingWidth = outerWidth - glyphMarginWidth - lineNumbersWidth - lineDecorationsWidth;
let minimapWidth: number;
let contentWidth: number;
if (minimap === Minimap.None) {
minimapWidth = 0;
contentWidth = remainingWidth;
} else {
// TODO@minimap: the view should decide on large vs small
// TODO@minimap: or it should be based on pixelRatio here
let minimapCharWidth = (minimap === Minimap.Large ? 2 : 1);
// Given:
// viewportColumn = (contentWidth - verticalScrollbarWidth) / typicalHalfwidthCharacterWidth
// minimapWidth = viewportColumn * minimapCharWidth
// contentWidth = remainingWidth - minimapWidth
// What are good values for contentWidth and minimapWidth ?
// minimapWidth = ((contentWidth - verticalScrollbarWidth) / typicalHalfwidthCharacterWidth) * minimapCharWidth
// typicalHalfwidthCharacterWidth * minimapWidth = (contentWidth - verticalScrollbarWidth) * minimapCharWidth
// typicalHalfwidthCharacterWidth * minimapWidth = (remainingWidth - minimapWidth - verticalScrollbarWidth) * minimapCharWidth
// (typicalHalfwidthCharacterWidth + minimapCharWidth) * minimapWidth = (remainingWidth - verticalScrollbarWidth) * minimapCharWidth
// minimapWidth = ((remainingWidth - verticalScrollbarWidth) * minimapCharWidth) / (typicalHalfwidthCharacterWidth + minimapCharWidth)
minimapWidth = Math.max(0, Math.floor(((remainingWidth - verticalScrollbarWidth) * minimapCharWidth) / (typicalHalfwidthCharacterWidth + minimapCharWidth)));
contentWidth = remainingWidth - minimapWidth;
}
let viewportColumn = Math.max(1, Math.floor((contentWidth - verticalScrollbarWidth) / typicalHalfwidthCharacterWidth));
let verticalArrowSize = (verticalScrollbarHasArrows ? scrollbarArrowSize : 0);
return new EditorLayoutInfo({
......@@ -82,6 +122,10 @@ export class EditorLayoutProvider {
contentWidth: contentWidth,
contentHeight: outerHeight,
minimapWidth: minimapWidth,
viewportColumn: viewportColumn,
verticalScrollbarWidth: verticalScrollbarWidth,
horizontalScrollbarHeight: horizontalScrollbarHeight,
......
......@@ -10,6 +10,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon';
import { LineTokens } from 'vs/editor/common/core/lineTokens';
import { PrefixSumComputerWithCache } from 'vs/editor/common/viewModel/prefixSumComputer';
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
import { MinimapLineRenderingData } from 'vs/editor/common/viewModel/viewModel';
export class OutputPosition {
_outputPositionBrand: void;
......@@ -49,6 +50,7 @@ export interface ISplitLine {
getViewLineMinColumn(model: IModel, modelLineNumber: number, outputLineIndex: number): number;
getViewLineMaxColumn(model: IModel, modelLineNumber: number, outputLineIndex: number): number;
getViewLineRenderingData(model: IModel, modelLineNumber: number, outputLineIndex: number): OutputLineRenderingData;
getMinimapLineRenderingData(model: IModel, modelLineNumber: number, outputLineIndex: number, tabSize: number): MinimapLineRenderingData;
getModelColumnOfViewPosition(outputLineIndex: number, outputColumn: number): number;
getViewPositionOfModelPosition(deltaLineNumber: number, inputColumn: number): Position;
......@@ -98,6 +100,17 @@ class VisibleIdentitySplitLine implements ISplitLine {
);
}
public getMinimapLineRenderingData(model: IModel, modelLineNumber: number, outputLineIndex: number, tabSize: number): MinimapLineRenderingData {
let lineTokens = model.getLineTokens(modelLineNumber, true);
let lineContent = lineTokens.getLineContent();
return new MinimapLineRenderingData(
lineContent,
lineTokens.inflate2(),
tabSize
);
}
public getModelColumnOfViewPosition(outputLineIndex: number, outputColumn: number): number {
return outputColumn;
}
......@@ -144,6 +157,10 @@ class InvisibleIdentitySplitLine implements ISplitLine {
throw new Error('Not supported');
}
public getMinimapLineRenderingData(model: IModel, modelLineNumber: number, outputLineIndex: number, tabSize: number): MinimapLineRenderingData {
throw new Error('Not supported');
}
public getModelColumnOfViewPosition(outputLineIndex: number, outputColumn: number): number {
throw new Error('Not supported');
}
......@@ -259,6 +276,32 @@ export class SplitLine implements ISplitLine {
);
}
public getMinimapLineRenderingData(model: IModel, modelLineNumber: number, outputLineIndex: number, tabSize: number): MinimapLineRenderingData {
if (!this._isVisible) {
throw new Error('Not supported');
}
let startOffset = this.getInputStartOffsetOfOutputLineIndex(outputLineIndex);
let endOffset = this.getInputEndOffsetOfOutputLineIndex(model, modelLineNumber, outputLineIndex);
let lineContent = model.getLineContent(modelLineNumber).substring(startOffset, endOffset);
if (outputLineIndex > 0) {
lineContent = this.wrappedIndent + lineContent;
}
let deltaStartIndex = 0;
if (outputLineIndex > 0) {
deltaStartIndex = this.wrappedIndentLength;
}
let lineTokens = model.getLineTokens(modelLineNumber, true);
return new MinimapLineRenderingData(
lineContent,
lineTokens.sliceAndInflate2(startOffset, endOffset, deltaStartIndex),
tabSize
);
}
public getModelColumnOfViewPosition(outputLineIndex: number, outputColumn: number): number {
if (!this._isVisible) {
throw new Error('Not supported');
......@@ -733,6 +776,17 @@ export class SplitLinesCollection {
return this.lines[lineIndex].getViewLineRenderingData(this.model, lineIndex + 1, remainder);
}
public getMinimapLineRenderingData(viewLineNumber: number): MinimapLineRenderingData {
this._ensureValidState();
viewLineNumber = this._toValidViewLineNumber(viewLineNumber);
let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1);
let lineIndex = r.index;
let remainder = r.remainder;
return this.lines[lineIndex].getMinimapLineRenderingData(this.model, lineIndex + 1, remainder, this.tabSize);
}
public validateViewPosition(viewLineNumber: number, viewColumn: number, expectedModelPosition: Position): Position {
this._ensureValidState();
viewLineNumber = this._toValidViewLineNumber(viewLineNumber);
......
......@@ -6,7 +6,7 @@
import { IEventEmitter } from 'vs/base/common/eventEmitter';
import { INewScrollPosition, IViewWhitespaceViewportData, Viewport, IModelDecoration, EndOfLinePreference, IPosition } from 'vs/editor/common/editorCommon';
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
import { ViewLineToken, ViewLineToken2 } from 'vs/editor/common/core/viewLineToken';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
......@@ -72,6 +72,7 @@ export interface IViewModel extends IEventEmitter {
getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[];
getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData;
getMinimapLineRenderingData(lineNumber: number): MinimapLineRenderingData;
getTabSize(): number;
getLineCount(): number;
......@@ -89,6 +90,31 @@ export interface IViewModel extends IEventEmitter {
validateModelPosition(modelPosition: IPosition): Position;
}
export class MinimapLineRenderingData {
/**
* The content at this view line.
*/
public readonly content: string;
/**
* The tokens at this view line.
*/
public readonly tokens: ViewLineToken2[];
/**
* The tab size for this view model.
*/
public readonly tabSize: number;
constructor(
content: string,
tokens: ViewLineToken2[],
tabSize: number
) {
this.content = content;
this.tokens = tokens;
this.tabSize = tabSize;
}
}
export class ViewLineRenderingData {
/**
* The minimum allowed column at this view line.
......
......@@ -13,7 +13,7 @@ import { Selection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ViewModelCursors } from 'vs/editor/common/viewModel/viewModelCursors';
import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations';
import { ViewLineRenderingData, ViewModelDecoration, IViewModel, ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel';
import { MinimapLineRenderingData, ViewLineRenderingData, ViewModelDecoration, IViewModel, ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel';
import { SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection';
export class CoordinatesConverter implements ICoordinatesConverter {
......@@ -534,6 +534,10 @@ export class ViewModel extends EventEmitter implements IViewModel {
);
}
public getMinimapLineRenderingData(lineNumber: number): MinimapLineRenderingData {
return this.lines.getMinimapLineRenderingData(lineNumber);
}
public getAllOverviewRulerDecorations(): ViewModelDecoration[] {
return this.decorations.getAllOverviewRulerDecorations();
}
......
......@@ -9,10 +9,10 @@ import { LineTokens } from 'vs/editor/common/core/lineTokens';
import { ModelLine, ILineEdit, LineMarker, MarkersTracker } from 'vs/editor/common/model/modelLine';
import { MetadataConsts } from 'vs/editor/common/modes';
import { Position } from 'vs/editor/common/core/position';
import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding';
import { ViewLineTokenFactory } from 'vs/editor/common/core/viewLineToken';
function assertLineTokens(_actual: LineTokens, _expected: TestToken[]): void {
let expected = TokenMetadata.inflateArr(TestToken.toTokens(_expected), _actual.getLineLength());
let expected = ViewLineTokenFactory.inflateArr(TestToken.toTokens(_expected), _actual.getLineLength());
let actual = _actual.inflate();
assert.deepEqual(actual, expected);
}
......
......@@ -2613,6 +2613,14 @@ declare module monaco.editor {
* The height of the content (actual height)
*/
readonly contentHeight: number;
/**
* The width of the minimap
*/
readonly minimapWidth: number;
/**
* The number of columns (of typical characters) fitting on a viewport line.
*/
readonly viewportColumn: number;
/**
* The width of the vertical scrollbar.
*/
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册