mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Refresh master with initial release/0.24 snapshot (#332)
* Initial port of release/0.24 source code * Fix additional headers * Fix a typo in launch.json
This commit is contained in:
@@ -25,6 +25,13 @@ export interface IViewZoneData {
|
||||
afterLineNumber: number;
|
||||
}
|
||||
|
||||
export interface IMarginData {
|
||||
isAfterLines: boolean;
|
||||
glyphMarginWidth: number;
|
||||
lineNumbersWidth: number;
|
||||
offsetX: number;
|
||||
}
|
||||
|
||||
interface IETextRange {
|
||||
boundingHeight: number;
|
||||
boundingLeft: number;
|
||||
@@ -565,22 +572,28 @@ export class MouseTargetFactory {
|
||||
if (request.isInMarginArea) {
|
||||
let res = ctx.getFullLineRangeAtCoord(request.mouseVerticalOffset);
|
||||
let pos = res.range.getStartPosition();
|
||||
|
||||
let offset = Math.abs(request.pos.x - request.editorPos.x);
|
||||
const detail: IMarginData = {
|
||||
isAfterLines: res.isAfterLines,
|
||||
glyphMarginWidth: ctx.layoutInfo.glyphMarginWidth,
|
||||
lineNumbersWidth: ctx.layoutInfo.lineNumbersWidth,
|
||||
offsetX: offset
|
||||
};
|
||||
|
||||
if (offset <= ctx.layoutInfo.glyphMarginWidth) {
|
||||
// On the glyph margin
|
||||
return request.fulfill(MouseTargetType.GUTTER_GLYPH_MARGIN, pos, res.range, res.isAfterLines);
|
||||
return request.fulfill(MouseTargetType.GUTTER_GLYPH_MARGIN, pos, res.range, detail);
|
||||
}
|
||||
offset -= ctx.layoutInfo.glyphMarginWidth;
|
||||
|
||||
if (offset <= ctx.layoutInfo.lineNumbersWidth) {
|
||||
// On the line numbers
|
||||
return request.fulfill(MouseTargetType.GUTTER_LINE_NUMBERS, pos, res.range, res.isAfterLines);
|
||||
return request.fulfill(MouseTargetType.GUTTER_LINE_NUMBERS, pos, res.range, detail);
|
||||
}
|
||||
offset -= ctx.layoutInfo.lineNumbersWidth;
|
||||
|
||||
// On the line decorations
|
||||
return request.fulfill(MouseTargetType.GUTTER_LINE_DECORATIONS, pos, res.range, res.isAfterLines);
|
||||
return request.fulfill(MouseTargetType.GUTTER_LINE_DECORATIONS, pos, res.range, detail);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import 'vs/css!./textAreaHandler';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { TextAreaInput, ITextAreaInputHost, IPasteData, ICompositionData } from 'vs/editor/browser/controller/textAreaInput';
|
||||
import { ISimpleModel, ITypeData, TextAreaState, PagedScreenReaderStrategy } from 'vs/editor/browser/controller/textAreaState';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
@@ -164,10 +165,24 @@ export class TextAreaHandler extends ViewPart {
|
||||
|
||||
if (this._accessibilitySupport === platform.AccessibilitySupport.Disabled) {
|
||||
// We know for a fact that a screen reader is not attached
|
||||
// On OSX, we write the character before the cursor to allow for "long-press" composition
|
||||
if (platform.isMacintosh) {
|
||||
const selection = this._selections[0];
|
||||
if (selection.isEmpty()) {
|
||||
const position = selection.getStartPosition();
|
||||
if (position.column > 1) {
|
||||
const lineContent = this._context.model.getLineContent(position.lineNumber);
|
||||
const charBefore = lineContent.charAt(position.column - 2);
|
||||
if (!strings.isHighSurrogate(charBefore.charCodeAt(0))) {
|
||||
return new TextAreaState(charBefore, 1, 1, position, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return TextAreaState.EMPTY;
|
||||
}
|
||||
|
||||
return PagedScreenReaderStrategy.fromEditorSelection(currentState, simpleModel, this._selections[0]);
|
||||
return PagedScreenReaderStrategy.fromEditorSelection(currentState, simpleModel, this._selections[0], this._accessibilitySupport === platform.AccessibilitySupport.Unknown);
|
||||
},
|
||||
|
||||
deduceModelPosition: (viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position => {
|
||||
|
||||
@@ -111,7 +111,7 @@ export class TextAreaInput extends Disposable {
|
||||
this._nextCommand = ReadFromTextArea.Type;
|
||||
|
||||
this._register(dom.addStandardDisposableListener(textArea.domNode, 'keydown', (e: IKeyboardEvent) => {
|
||||
if (this._isDoingComposition && e.equals(KeyCode.KEY_IN_COMPOSITION)) {
|
||||
if (this._isDoingComposition && e.keyCode === KeyCode.KEY_IN_COMPOSITION) {
|
||||
// Stop propagation for keyDown events if the IME is processing key input
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ export class PagedScreenReaderStrategy {
|
||||
return new Range(startLineNumber, 1, endLineNumber + 1, 1);
|
||||
}
|
||||
|
||||
public static fromEditorSelection(previousState: TextAreaState, model: ISimpleModel, selection: Range): TextAreaState {
|
||||
public static fromEditorSelection(previousState: TextAreaState, model: ISimpleModel, selection: Range, trimLongText: boolean): TextAreaState {
|
||||
|
||||
let selectionStartPage = PagedScreenReaderStrategy._getPageOfLine(selection.startLineNumber);
|
||||
let selectionStartPageRange = PagedScreenReaderStrategy._getRangeForPage(selectionStartPage);
|
||||
@@ -273,15 +273,17 @@ export class PagedScreenReaderStrategy {
|
||||
|
||||
// Chromium handles very poorly text even of a few thousand chars
|
||||
// Cut text to avoid stalling the entire UI
|
||||
const LIMIT_CHARS = 500;
|
||||
if (pretext.length > LIMIT_CHARS) {
|
||||
pretext = pretext.substring(pretext.length - LIMIT_CHARS, pretext.length);
|
||||
}
|
||||
if (posttext.length > LIMIT_CHARS) {
|
||||
posttext = posttext.substring(0, LIMIT_CHARS);
|
||||
}
|
||||
if (text.length > 2 * LIMIT_CHARS) {
|
||||
text = text.substring(0, LIMIT_CHARS) + String.fromCharCode(8230) + text.substring(text.length - LIMIT_CHARS, text.length);
|
||||
if (trimLongText) {
|
||||
const LIMIT_CHARS = 500;
|
||||
if (pretext.length > LIMIT_CHARS) {
|
||||
pretext = pretext.substring(pretext.length - LIMIT_CHARS, pretext.length);
|
||||
}
|
||||
if (posttext.length > LIMIT_CHARS) {
|
||||
posttext = posttext.substring(0, LIMIT_CHARS);
|
||||
}
|
||||
if (text.length > 2 * LIMIT_CHARS) {
|
||||
text = text.substring(0, LIMIT_CHARS) + String.fromCharCode(8230) + text.substring(text.length - LIMIT_CHARS, text.length);
|
||||
}
|
||||
}
|
||||
|
||||
return new TextAreaState(pretext + text + posttext, pretext.length, pretext.length + text.length, new Position(selection.startLineNumber, selection.startColumn), new Position(selection.endLineNumber, selection.endColumn));
|
||||
|
||||
@@ -308,7 +308,8 @@ export class View extends ViewEventHandler {
|
||||
}
|
||||
|
||||
private getEditorClassName() {
|
||||
return this._context.configuration.editor.editorClassName + ' ' + getThemeTypeSelector(this._context.theme.type);
|
||||
let focused = this._textAreaHandler.isFocused() ? ' focused' : '';
|
||||
return this._context.configuration.editor.editorClassName + ' ' + getThemeTypeSelector(this._context.theme.type) + focused;
|
||||
}
|
||||
|
||||
// --- begin event handlers
|
||||
@@ -323,7 +324,7 @@ export class View extends ViewEventHandler {
|
||||
return false;
|
||||
}
|
||||
public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean {
|
||||
this.domNode.toggleClassName('focused', e.isFocused);
|
||||
this.domNode.setClassName(this.getEditorClassName());
|
||||
if (e.isFocused) {
|
||||
this.outgoingEvents.emitViewFocusGained();
|
||||
} else {
|
||||
@@ -419,6 +420,11 @@ export class View extends ViewEventHandler {
|
||||
this._context.model
|
||||
);
|
||||
|
||||
if (this.contentWidgets.shouldRender()) {
|
||||
// Give the content widgets a chance to set their max width before a possible synchronous layout
|
||||
this.contentWidgets.onBeforeRender(viewportData);
|
||||
}
|
||||
|
||||
if (this.viewLines.shouldRender()) {
|
||||
this.viewLines.renderText(viewportData);
|
||||
this.viewLines.onDidRender();
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ViewContext } from 'vs/editor/common/view/viewContext';
|
||||
import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
|
||||
import { Position, IPosition } from 'vs/editor/common/core/position';
|
||||
import * as viewEvents from 'vs/editor/common/view/viewEvents';
|
||||
import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
|
||||
|
||||
class Coordinate {
|
||||
_coordinateBrand: void;
|
||||
@@ -73,6 +74,14 @@ export class ViewContentWidgets extends ViewPart {
|
||||
public onFlushed(e: viewEvents.ViewFlushedEvent): boolean {
|
||||
return true;
|
||||
}
|
||||
public onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): boolean {
|
||||
let keys = Object.keys(this._widgets);
|
||||
for (let i = 0, len = keys.length; i < len; i++) {
|
||||
const widgetId = keys[i];
|
||||
this._widgets[widgetId].onLineMappingChanged(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean {
|
||||
return true;
|
||||
}
|
||||
@@ -132,6 +141,14 @@ export class ViewContentWidgets extends ViewPart {
|
||||
return false;
|
||||
}
|
||||
|
||||
public onBeforeRender(viewportData: ViewportData): void {
|
||||
let keys = Object.keys(this._widgets);
|
||||
for (let i = 0, len = keys.length; i < len; i++) {
|
||||
const widgetId = keys[i];
|
||||
this._widgets[widgetId].onBeforeRender(viewportData);
|
||||
}
|
||||
}
|
||||
|
||||
public prepareRender(ctx: RenderingContext): void {
|
||||
let keys = Object.keys(this._widgets);
|
||||
for (let i = 0, len = keys.length; i < len; i++) {
|
||||
@@ -173,8 +190,13 @@ class Widget {
|
||||
private _lineHeight: number;
|
||||
|
||||
private _position: IPosition;
|
||||
private _viewPosition: Position;
|
||||
private _preference: ContentWidgetPositionPreference[];
|
||||
private _cachedDomNodeClientWidth: number;
|
||||
private _cachedDomNodeClientHeight: number;
|
||||
private _maxWidth: number;
|
||||
private _isVisible: boolean;
|
||||
|
||||
private _renderData: Coordinate;
|
||||
|
||||
constructor(context: ViewContext, viewDomNode: FastDomNode<HTMLElement>, actual: IContentWidget) {
|
||||
@@ -192,15 +214,18 @@ class Widget {
|
||||
this._contentLeft = this._context.configuration.editor.layoutInfo.contentLeft;
|
||||
this._lineHeight = this._context.configuration.editor.lineHeight;
|
||||
|
||||
this._position = null;
|
||||
this._setPosition(null);
|
||||
this._preference = null;
|
||||
this._cachedDomNodeClientWidth = -1;
|
||||
this._cachedDomNodeClientHeight = -1;
|
||||
this._maxWidth = this._getMaxWidth();
|
||||
this._isVisible = false;
|
||||
this._renderData = null;
|
||||
|
||||
this.domNode.setPosition((this._fixedOverflowWidgets && this.allowEditorOverflow) ? 'fixed' : 'absolute');
|
||||
this._updateMaxWidth();
|
||||
this.domNode.setVisibility('hidden');
|
||||
this.domNode.setAttribute('widgetId', this.id);
|
||||
this.domNode.setMaxWidth(this._maxWidth);
|
||||
}
|
||||
|
||||
public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): void {
|
||||
@@ -210,22 +235,40 @@ class Widget {
|
||||
if (e.layoutInfo) {
|
||||
this._contentLeft = this._context.configuration.editor.layoutInfo.contentLeft;
|
||||
this._contentWidth = this._context.configuration.editor.layoutInfo.contentWidth;
|
||||
|
||||
this._updateMaxWidth();
|
||||
this._maxWidth = this._getMaxWidth();
|
||||
}
|
||||
}
|
||||
|
||||
private _updateMaxWidth(): void {
|
||||
const maxWidth = this.allowEditorOverflow
|
||||
? window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
|
||||
: this._contentWidth;
|
||||
public onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): void {
|
||||
this._setPosition(this._position);
|
||||
}
|
||||
|
||||
this.domNode.setMaxWidth(maxWidth);
|
||||
private _setPosition(position: IPosition): void {
|
||||
this._position = position;
|
||||
this._viewPosition = null;
|
||||
|
||||
if (this._position) {
|
||||
// Do not trust that widgets give a valid position
|
||||
const validModelPosition = this._context.model.validateModelPosition(this._position);
|
||||
if (this._context.model.coordinatesConverter.modelPositionIsVisible(validModelPosition)) {
|
||||
this._viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(validModelPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _getMaxWidth(): number {
|
||||
return (
|
||||
this.allowEditorOverflow
|
||||
? window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
|
||||
: this._contentWidth
|
||||
);
|
||||
}
|
||||
|
||||
public setPosition(position: IPosition, preference: ContentWidgetPositionPreference[]): void {
|
||||
this._position = position;
|
||||
this._setPosition(position);
|
||||
this._preference = preference;
|
||||
this._cachedDomNodeClientWidth = -1;
|
||||
this._cachedDomNodeClientHeight = -1;
|
||||
}
|
||||
|
||||
private _layoutBoxInViewport(topLeft: Coordinate, width: number, height: number, ctx: RenderingContext): IBoxLayoutResult {
|
||||
@@ -312,50 +355,44 @@ class Widget {
|
||||
return new Coordinate(topLeft.top, topLeft.left + this._contentLeft);
|
||||
}
|
||||
|
||||
private _getTopLeft(ctx: RenderingContext, position: Position): Coordinate {
|
||||
const visibleRange = ctx.visibleRangeForPosition(position);
|
||||
/**
|
||||
* Compute `this._topLeft`
|
||||
*/
|
||||
private _getTopLeft(ctx: RenderingContext): Coordinate {
|
||||
if (!this._viewPosition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const visibleRange = ctx.visibleRangeForPosition(this._viewPosition);
|
||||
if (!visibleRange) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const top = ctx.getVerticalOffsetForLineNumber(position.lineNumber) - ctx.scrollTop;
|
||||
const top = ctx.getVerticalOffsetForLineNumber(this._viewPosition.lineNumber) - ctx.scrollTop;
|
||||
return new Coordinate(top, visibleRange.left);
|
||||
}
|
||||
|
||||
private _prepareRenderWidget(ctx: RenderingContext): Coordinate {
|
||||
if (!this._position || !this._preference) {
|
||||
private _prepareRenderWidget(topLeft: Coordinate, ctx: RenderingContext): Coordinate {
|
||||
if (!topLeft) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Do not trust that widgets have a valid position
|
||||
let validModelPosition = this._context.model.validateModelPosition(this._position);
|
||||
|
||||
if (!this._context.model.coordinatesConverter.modelPositionIsVisible(validModelPosition)) {
|
||||
// this position is hidden by the view model
|
||||
return null;
|
||||
}
|
||||
|
||||
let position = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(validModelPosition);
|
||||
|
||||
let placement: IBoxLayoutResult = null;
|
||||
let fetchPlacement = (): void => {
|
||||
if (placement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const topLeft = this._getTopLeft(ctx, position);
|
||||
if (!topLeft) {
|
||||
return;
|
||||
if (this._cachedDomNodeClientWidth === -1 || this._cachedDomNodeClientHeight === -1) {
|
||||
const domNode = this.domNode.domNode;
|
||||
this._cachedDomNodeClientWidth = domNode.clientWidth;
|
||||
this._cachedDomNodeClientHeight = domNode.clientHeight;
|
||||
}
|
||||
|
||||
const domNode = this.domNode.domNode;
|
||||
const width = domNode.clientWidth;
|
||||
const height = domNode.clientHeight;
|
||||
|
||||
if (this.allowEditorOverflow) {
|
||||
placement = this._layoutBoxInPage(topLeft, width, height, ctx);
|
||||
placement = this._layoutBoxInPage(topLeft, this._cachedDomNodeClientWidth, this._cachedDomNodeClientHeight, ctx);
|
||||
} else {
|
||||
placement = this._layoutBoxInViewport(topLeft, width, height, ctx);
|
||||
placement = this._layoutBoxInViewport(topLeft, this._cachedDomNodeClientWidth, this._cachedDomNodeClientHeight, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -382,11 +419,6 @@ class Widget {
|
||||
return new Coordinate(placement.belowTop, placement.left);
|
||||
}
|
||||
} else {
|
||||
const topLeft = this._getTopLeft(ctx, position);
|
||||
if (!topLeft) {
|
||||
// Widget outside of viewport
|
||||
return null;
|
||||
}
|
||||
if (this.allowEditorOverflow) {
|
||||
return this._prepareRenderWidgetAtExactPositionOverflowing(topLeft);
|
||||
} else {
|
||||
@@ -398,8 +430,25 @@ class Widget {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* On this first pass, we ensure that the content widget (if it is in the viewport) has the max width set correctly.
|
||||
*/
|
||||
public onBeforeRender(viewportData: ViewportData): void {
|
||||
if (!this._viewPosition || !this._preference) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._viewPosition.lineNumber < viewportData.startLineNumber || this._viewPosition.lineNumber > viewportData.endLineNumber) {
|
||||
// Outside of viewport
|
||||
return;
|
||||
}
|
||||
|
||||
this.domNode.setMaxWidth(this._maxWidth);
|
||||
}
|
||||
|
||||
public prepareRender(ctx: RenderingContext): void {
|
||||
this._renderData = this._prepareRenderWidget(ctx);
|
||||
const topLeft = this._getTopLeft(ctx);
|
||||
this._renderData = this._prepareRenderWidget(topLeft, ctx);
|
||||
}
|
||||
|
||||
public render(ctx: RestrictedRenderingContext): void {
|
||||
|
||||
@@ -110,8 +110,12 @@ export class CurrentLineHighlightOverlay extends DynamicViewOverlay {
|
||||
public render(startLineNumber: number, lineNumber: number): string {
|
||||
if (lineNumber === this._primaryCursorLineNumber) {
|
||||
if (this._shouldShowCurrentLine()) {
|
||||
const paintedInMargin = this._willRenderMarginCurrentLine();
|
||||
const className = 'current-line' + (paintedInMargin ? ' current-line-both' : '');
|
||||
return (
|
||||
'<div class="current-line" style="width:'
|
||||
'<div class="'
|
||||
+ className
|
||||
+ '" style="width:'
|
||||
+ String(Math.max(this._scrollWidth, this._contentWidth))
|
||||
+ 'px; height:'
|
||||
+ String(this._lineHeight)
|
||||
@@ -125,9 +129,18 @@ export class CurrentLineHighlightOverlay extends DynamicViewOverlay {
|
||||
}
|
||||
|
||||
private _shouldShowCurrentLine(): boolean {
|
||||
return (this._renderLineHighlight === 'line' || this._renderLineHighlight === 'all') &&
|
||||
this._selectionIsEmpty &&
|
||||
this._primaryCursorIsInEditableRange;
|
||||
return (
|
||||
(this._renderLineHighlight === 'line' || this._renderLineHighlight === 'all')
|
||||
&& this._selectionIsEmpty
|
||||
&& this._primaryCursorIsInEditableRange
|
||||
);
|
||||
}
|
||||
|
||||
private _willRenderMarginCurrentLine(): boolean {
|
||||
return (
|
||||
(this._renderLineHighlight === 'gutter' || this._renderLineHighlight === 'all')
|
||||
&& this._primaryCursorIsInEditableRange
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,4 +9,8 @@
|
||||
left: 0;
|
||||
top: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.monaco-editor .margin-view-overlays .current-line-margin.current-line-margin-both {
|
||||
border-right: 0;
|
||||
}
|
||||
@@ -17,6 +17,7 @@ export class CurrentLineMarginHighlightOverlay extends DynamicViewOverlay {
|
||||
private _context: ViewContext;
|
||||
private _lineHeight: number;
|
||||
private _renderLineHighlight: 'none' | 'gutter' | 'line' | 'all';
|
||||
private _selectionIsEmpty: boolean;
|
||||
private _primaryCursorIsInEditableRange: boolean;
|
||||
private _primaryCursorLineNumber: number;
|
||||
private _contentLeft: number;
|
||||
@@ -27,6 +28,7 @@ export class CurrentLineMarginHighlightOverlay extends DynamicViewOverlay {
|
||||
this._lineHeight = this._context.configuration.editor.lineHeight;
|
||||
this._renderLineHighlight = this._context.configuration.editor.viewInfo.renderLineHighlight;
|
||||
|
||||
this._selectionIsEmpty = true;
|
||||
this._primaryCursorIsInEditableRange = true;
|
||||
this._primaryCursorLineNumber = 1;
|
||||
this._contentLeft = this._context.configuration.editor.layoutInfo.contentLeft;
|
||||
@@ -68,6 +70,13 @@ export class CurrentLineMarginHighlightOverlay extends DynamicViewOverlay {
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
const selectionIsEmpty = e.selections[0].isEmpty();
|
||||
if (this._selectionIsEmpty !== selectionIsEmpty) {
|
||||
this._selectionIsEmpty = selectionIsEmpty;
|
||||
hasChanged = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return hasChanged;
|
||||
}
|
||||
public onFlushed(e: viewEvents.ViewFlushedEvent): boolean {
|
||||
@@ -90,8 +99,12 @@ export class CurrentLineMarginHighlightOverlay extends DynamicViewOverlay {
|
||||
public render(startLineNumber: number, lineNumber: number): string {
|
||||
if (lineNumber === this._primaryCursorLineNumber) {
|
||||
if (this._shouldShowCurrentLine()) {
|
||||
const paintedInContent = this._willRenderContentCurrentLine();
|
||||
const className = 'current-line-margin' + (paintedInContent ? ' current-line-margin-both' : '');
|
||||
return (
|
||||
'<div class="current-line-margin" style="width:'
|
||||
'<div class="'
|
||||
+ className
|
||||
+ '" style="width:'
|
||||
+ String(this._contentLeft)
|
||||
+ 'px; height:'
|
||||
+ String(this._lineHeight)
|
||||
@@ -105,7 +118,18 @@ export class CurrentLineMarginHighlightOverlay extends DynamicViewOverlay {
|
||||
}
|
||||
|
||||
private _shouldShowCurrentLine(): boolean {
|
||||
return (this._renderLineHighlight === 'gutter' || this._renderLineHighlight === 'all') && this._primaryCursorIsInEditableRange;
|
||||
return (
|
||||
(this._renderLineHighlight === 'gutter' || this._renderLineHighlight === 'all')
|
||||
&& this._primaryCursorIsInEditableRange
|
||||
);
|
||||
}
|
||||
|
||||
private _willRenderContentCurrentLine(): boolean {
|
||||
return (
|
||||
(this._renderLineHighlight === 'line' || this._renderLineHighlight === 'all')
|
||||
&& this._selectionIsEmpty
|
||||
&& this._primaryCursorIsInEditableRange
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,15 +78,15 @@ export class DecorationsOverlay extends DynamicViewOverlay {
|
||||
let decorations: ViewModelDecoration[] = [], decorationsLen = 0;
|
||||
for (let i = 0, len = _decorations.length; i < len; i++) {
|
||||
let d = _decorations[i];
|
||||
if (d.source.options.className) {
|
||||
if (d.options.className) {
|
||||
decorations[decorationsLen++] = d;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort decorations for consistent render output
|
||||
decorations = decorations.sort((a, b) => {
|
||||
let aClassName = a.source.options.className;
|
||||
let bClassName = b.source.options.className;
|
||||
let aClassName = a.options.className;
|
||||
let bClassName = b.options.className;
|
||||
|
||||
if (aClassName < bClassName) {
|
||||
return -1;
|
||||
@@ -120,13 +120,13 @@ export class DecorationsOverlay extends DynamicViewOverlay {
|
||||
for (let i = 0, lenI = decorations.length; i < lenI; i++) {
|
||||
let d = decorations[i];
|
||||
|
||||
if (!d.source.options.isWholeLine) {
|
||||
if (!d.options.isWholeLine) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let decorationOutput = (
|
||||
'<div class="cdr '
|
||||
+ d.source.options.className
|
||||
+ d.options.className
|
||||
+ '" style="left:0;width:100%;height:'
|
||||
+ lineHeight
|
||||
+ 'px;"></div>'
|
||||
@@ -148,12 +148,12 @@ export class DecorationsOverlay extends DynamicViewOverlay {
|
||||
for (let i = 0, lenI = decorations.length; i < lenI; i++) {
|
||||
const d = decorations[i];
|
||||
|
||||
if (d.source.options.isWholeLine) {
|
||||
if (d.options.isWholeLine) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const className = d.source.options.className;
|
||||
const showIfCollapsed = d.source.options.showIfCollapsed;
|
||||
const className = d.options.className;
|
||||
const showIfCollapsed = d.options.showIfCollapsed;
|
||||
|
||||
let range = d.range;
|
||||
if (showIfCollapsed && range.endColumn === 1 && range.endLineNumber !== range.startLineNumber) {
|
||||
|
||||
@@ -145,7 +145,7 @@ export class GlyphMarginOverlay extends DedupOverlay {
|
||||
let r: DecorationToRender[] = [], rLen = 0;
|
||||
for (let i = 0, len = decorations.length; i < len; i++) {
|
||||
let d = decorations[i];
|
||||
let glyphMarginClassName = d.source.options.glyphMarginClassName;
|
||||
let glyphMarginClassName = d.options.glyphMarginClassName;
|
||||
if (glyphMarginClassName) {
|
||||
r[rLen++] = new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, glyphMarginClassName);
|
||||
}
|
||||
|
||||
@@ -77,6 +77,9 @@ export class IndentGuidesOverlay extends DynamicViewOverlay {
|
||||
public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean {
|
||||
return true;
|
||||
}
|
||||
public onLanguageConfigurationChanged(e: viewEvents.ViewLanguageConfigurationEvent): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- end event handlers
|
||||
|
||||
@@ -93,10 +96,12 @@ export class IndentGuidesOverlay extends DynamicViewOverlay {
|
||||
const lineHeight = this._lineHeight;
|
||||
const indentGuideWidth = dom.computeScreenAwareSize(1);
|
||||
|
||||
const indents = this._context.model.getLinesIndentGuides(visibleStartLineNumber, visibleEndLineNumber);
|
||||
|
||||
let output: string[] = [];
|
||||
for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) {
|
||||
let lineIndex = lineNumber - visibleStartLineNumber;
|
||||
let indent = this._context.model.getLineIndentGuide(lineNumber);
|
||||
const lineIndex = lineNumber - visibleStartLineNumber;
|
||||
const indent = indents[lineIndex];
|
||||
|
||||
let result = '';
|
||||
let leftMostVisiblePosition = ctx.visibleRangeForPosition(new Position(lineNumber, 1));
|
||||
|
||||
@@ -397,7 +397,7 @@ class RenderedViewLine implements IRenderedViewLine {
|
||||
|
||||
this._pixelOffsetCache = null;
|
||||
if (!containsRTL || this._characterMapping.length === 0 /* the line is empty */) {
|
||||
this._pixelOffsetCache = new Int32Array(this._characterMapping.length + 1);
|
||||
this._pixelOffsetCache = new Int32Array(Math.max(2, this._characterMapping.length + 1));
|
||||
for (let column = 0, len = this._characterMapping.length; column <= len; column++) {
|
||||
this._pixelOffsetCache[column] = -1;
|
||||
}
|
||||
|
||||
@@ -551,17 +551,17 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost<ViewLine>,
|
||||
}
|
||||
}
|
||||
|
||||
// (3) handle scrolling
|
||||
this._linesContent.setLayerHinting(this._canUseLayerHinting);
|
||||
const adjustedScrollTop = this._context.viewLayout.getCurrentScrollTop() - viewportData.bigNumbersDelta;
|
||||
this._linesContent.setTop(-adjustedScrollTop);
|
||||
this._linesContent.setLeft(-this._context.viewLayout.getCurrentScrollLeft());
|
||||
|
||||
// Update max line width (not so important, it is just so the horizontal scrollbar doesn't get too small)
|
||||
if (!this._updateLineWidthsFast()) {
|
||||
// Computing the width of some lines would be slow => delay it
|
||||
this._asyncUpdateLineWidths.schedule();
|
||||
}
|
||||
|
||||
// (3) handle scrolling
|
||||
this._linesContent.setLayerHinting(this._canUseLayerHinting);
|
||||
const adjustedScrollTop = this._context.viewLayout.getCurrentScrollTop() - viewportData.bigNumbersDelta;
|
||||
this._linesContent.setTop(-adjustedScrollTop);
|
||||
this._linesContent.setLeft(-this._context.viewLayout.getCurrentScrollLeft());
|
||||
}
|
||||
|
||||
// --- width
|
||||
|
||||
@@ -73,7 +73,7 @@ export class LinesDecorationsOverlay extends DedupOverlay {
|
||||
let r: DecorationToRender[] = [], rLen = 0;
|
||||
for (let i = 0, len = decorations.length; i < len; i++) {
|
||||
let d = decorations[i];
|
||||
let linesDecorationsClassName = d.source.options.linesDecorationsClassName;
|
||||
let linesDecorationsClassName = d.options.linesDecorationsClassName;
|
||||
if (linesDecorationsClassName) {
|
||||
r[rLen++] = new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, linesDecorationsClassName);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ export class MarginViewLineDecorationsOverlay extends DedupOverlay {
|
||||
let r: DecorationToRender[] = [], rLen = 0;
|
||||
for (let i = 0, len = decorations.length; i < len; i++) {
|
||||
let d = decorations[i];
|
||||
let marginClassName = d.source.options.marginClassName;
|
||||
let marginClassName = d.options.marginClassName;
|
||||
if (marginClassName) {
|
||||
r[rLen++] = new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, marginClassName);
|
||||
}
|
||||
|
||||
@@ -709,7 +709,7 @@ export class Minimap extends ViewPart {
|
||||
const imageData = this._getBuffer();
|
||||
|
||||
// Render untouched lines by using last rendered data.
|
||||
let needed = Minimap._renderUntouchedLines(
|
||||
let [_dirtyY1, _dirtyY2, needed] = Minimap._renderUntouchedLines(
|
||||
imageData,
|
||||
startLineNumber,
|
||||
endLineNumber,
|
||||
@@ -744,9 +744,13 @@ export class Minimap extends ViewPart {
|
||||
dy += minimapLineHeight;
|
||||
}
|
||||
|
||||
const dirtyY1 = (_dirtyY1 === -1 ? 0 : _dirtyY1);
|
||||
const dirtyY2 = (_dirtyY2 === -1 ? imageData.height : _dirtyY2);
|
||||
const dirtyHeight = dirtyY2 - dirtyY1;
|
||||
|
||||
// Finally, paint to the canvas
|
||||
const ctx = this._canvas.domNode.getContext('2d');
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
ctx.putImageData(imageData, 0, 0, 0, dirtyY1, imageData.width, dirtyHeight);
|
||||
|
||||
// Save rendered data for reuse on next frame if possible
|
||||
return new RenderData(
|
||||
@@ -762,14 +766,14 @@ export class Minimap extends ViewPart {
|
||||
endLineNumber: number,
|
||||
minimapLineHeight: number,
|
||||
lastRenderData: RenderData,
|
||||
): boolean[] {
|
||||
): [number, number, boolean[]] {
|
||||
|
||||
let needed: boolean[] = [];
|
||||
if (!lastRenderData) {
|
||||
for (let i = 0, len = endLineNumber - startLineNumber + 1; i < len; i++) {
|
||||
needed[i] = true;
|
||||
}
|
||||
return needed;
|
||||
return [-1, -1, needed];
|
||||
}
|
||||
|
||||
const _lastData = lastRenderData._get();
|
||||
@@ -780,6 +784,10 @@ export class Minimap extends ViewPart {
|
||||
const WIDTH = target.width;
|
||||
const targetData = target.data;
|
||||
|
||||
const maxDestPixel = (endLineNumber - startLineNumber + 1) * minimapLineHeight * WIDTH * 4;
|
||||
let dirtyPixel1 = -1; // the pixel offset up to which all the data is equal to the prev frame
|
||||
let dirtyPixel2 = -1; // the pixel offset after which all the data is equal to the prev frame
|
||||
|
||||
let copySourceStart = -1;
|
||||
let copySourceEnd = -1;
|
||||
let copyDestStart = -1;
|
||||
@@ -810,6 +818,12 @@ export class Minimap extends ViewPart {
|
||||
if (copySourceStart !== -1) {
|
||||
// flush existing copy request
|
||||
targetData.set(lastTargetData.subarray(copySourceStart, copySourceEnd), copyDestStart);
|
||||
if (dirtyPixel1 === -1 && copySourceStart === 0 && copySourceStart === copyDestStart) {
|
||||
dirtyPixel1 = copySourceEnd;
|
||||
}
|
||||
if (dirtyPixel2 === -1 && copySourceEnd === maxDestPixel && copySourceStart === copyDestStart) {
|
||||
dirtyPixel2 = copySourceStart;
|
||||
}
|
||||
}
|
||||
copySourceStart = sourceStart;
|
||||
copySourceEnd = sourceEnd;
|
||||
@@ -824,9 +838,18 @@ export class Minimap extends ViewPart {
|
||||
if (copySourceStart !== -1) {
|
||||
// flush existing copy request
|
||||
targetData.set(lastTargetData.subarray(copySourceStart, copySourceEnd), copyDestStart);
|
||||
if (dirtyPixel1 === -1 && copySourceStart === 0 && copySourceStart === copyDestStart) {
|
||||
dirtyPixel1 = copySourceEnd;
|
||||
}
|
||||
if (dirtyPixel2 === -1 && copySourceEnd === maxDestPixel && copySourceStart === copyDestStart) {
|
||||
dirtyPixel2 = copySourceStart;
|
||||
}
|
||||
}
|
||||
|
||||
return needed;
|
||||
const dirtyY1 = (dirtyPixel1 === -1 ? -1 : dirtyPixel1 / (WIDTH * 4));
|
||||
const dirtyY2 = (dirtyPixel2 === -1 ? -1 : dirtyPixel2 / (WIDTH * 4));
|
||||
|
||||
return [dirtyY1, dirtyY2, needed];
|
||||
}
|
||||
|
||||
private static _renderLine(
|
||||
|
||||
@@ -6,262 +6,416 @@
|
||||
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { ViewPart } from 'vs/editor/browser/view/viewPart';
|
||||
import { OverviewRulerImpl } from 'vs/editor/browser/viewParts/overviewRuler/overviewRulerImpl';
|
||||
import { ViewContext } from 'vs/editor/common/view/viewContext';
|
||||
import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { TokenizationRegistry } from 'vs/editor/common/modes';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as viewEvents from 'vs/editor/common/view/viewEvents';
|
||||
import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager';
|
||||
import { editorOverviewRulerBorder, editorCursorForeground } from 'vs/editor/common/view/editorColorRegistry';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { ThemeColor } from 'vs/platform/theme/common/themeService';
|
||||
import { ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
|
||||
|
||||
class Settings {
|
||||
|
||||
public readonly lineHeight: number;
|
||||
public readonly pixelRatio: number;
|
||||
public readonly overviewRulerLanes: number;
|
||||
|
||||
public readonly renderBorder: boolean;
|
||||
public readonly borderColor: string;
|
||||
|
||||
public readonly hideCursor: boolean;
|
||||
public readonly cursorColor: string;
|
||||
|
||||
public readonly themeType: 'light' | 'dark' | 'hc';
|
||||
public readonly backgroundColor: string;
|
||||
|
||||
public readonly top: number;
|
||||
public readonly right: number;
|
||||
public readonly domWidth: number;
|
||||
public readonly domHeight: number;
|
||||
public readonly canvasWidth: number;
|
||||
public readonly canvasHeight: number;
|
||||
|
||||
public readonly x: number[];
|
||||
public readonly w: number[];
|
||||
|
||||
constructor(config: editorCommon.IConfiguration, theme: ITheme) {
|
||||
this.lineHeight = config.editor.lineHeight;
|
||||
this.pixelRatio = config.editor.pixelRatio;
|
||||
this.overviewRulerLanes = config.editor.viewInfo.overviewRulerLanes;
|
||||
|
||||
this.renderBorder = config.editor.viewInfo.overviewRulerBorder;
|
||||
const borderColor = theme.getColor(editorOverviewRulerBorder);
|
||||
this.borderColor = borderColor ? borderColor.toString() : null;
|
||||
|
||||
this.hideCursor = config.editor.viewInfo.hideCursorInOverviewRuler;
|
||||
const cursorColor = theme.getColor(editorCursorForeground);
|
||||
this.cursorColor = cursorColor ? cursorColor.transparent(0.7).toString() : null;
|
||||
|
||||
this.themeType = theme.type;
|
||||
|
||||
const minimapEnabled = config.editor.viewInfo.minimap.enabled;
|
||||
const backgroundColor = (minimapEnabled ? TokenizationRegistry.getDefaultBackground() : null);
|
||||
this.backgroundColor = (backgroundColor ? Color.Format.CSS.formatHex(backgroundColor) : null);
|
||||
|
||||
const position = config.editor.layoutInfo.overviewRuler;
|
||||
this.top = position.top;
|
||||
this.right = position.right;
|
||||
this.domWidth = position.width;
|
||||
this.domHeight = position.height;
|
||||
this.canvasWidth = (this.domWidth * this.pixelRatio) | 0;
|
||||
this.canvasHeight = (this.domHeight * this.pixelRatio) | 0;
|
||||
|
||||
const [x, w] = this._initLanes(1, this.canvasWidth, this.overviewRulerLanes);
|
||||
this.x = x;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
private _initLanes(canvasLeftOffset: number, canvasWidth: number, laneCount: number): [number[], number[]] {
|
||||
const remainingWidth = canvasWidth - canvasLeftOffset;
|
||||
|
||||
if (laneCount >= 3) {
|
||||
const leftWidth = Math.floor(remainingWidth / 3);
|
||||
const rightWidth = Math.floor(remainingWidth / 3);
|
||||
const centerWidth = remainingWidth - leftWidth - rightWidth;
|
||||
const leftOffset = canvasLeftOffset;
|
||||
const centerOffset = leftOffset + leftWidth;
|
||||
const rightOffset = leftOffset + leftWidth + centerWidth;
|
||||
|
||||
return [
|
||||
[
|
||||
0,
|
||||
leftOffset, // Left
|
||||
centerOffset, // Center
|
||||
leftOffset, // Left | Center
|
||||
rightOffset, // Right
|
||||
leftOffset, // Left | Right
|
||||
centerOffset, // Center | Right
|
||||
leftOffset, // Left | Center | Right
|
||||
], [
|
||||
0,
|
||||
leftWidth, // Left
|
||||
centerWidth, // Center
|
||||
leftWidth + centerWidth, // Left | Center
|
||||
rightWidth, // Right
|
||||
leftWidth + centerWidth + rightWidth, // Left | Right
|
||||
centerWidth + rightWidth, // Center | Right
|
||||
leftWidth + centerWidth + rightWidth, // Left | Center | Right
|
||||
]
|
||||
];
|
||||
} else if (laneCount === 2) {
|
||||
const leftWidth = Math.floor(remainingWidth / 2);
|
||||
const rightWidth = remainingWidth - leftWidth;
|
||||
const leftOffset = canvasLeftOffset;
|
||||
const rightOffset = leftOffset + leftWidth;
|
||||
|
||||
return [
|
||||
[
|
||||
0,
|
||||
leftOffset, // Left
|
||||
leftOffset, // Center
|
||||
leftOffset, // Left | Center
|
||||
rightOffset, // Right
|
||||
leftOffset, // Left | Right
|
||||
leftOffset, // Center | Right
|
||||
leftOffset, // Left | Center | Right
|
||||
], [
|
||||
0,
|
||||
leftWidth, // Left
|
||||
leftWidth, // Center
|
||||
leftWidth, // Left | Center
|
||||
rightWidth, // Right
|
||||
leftWidth + rightWidth, // Left | Right
|
||||
leftWidth + rightWidth, // Center | Right
|
||||
leftWidth + rightWidth, // Left | Center | Right
|
||||
]
|
||||
];
|
||||
} else {
|
||||
const offset = canvasLeftOffset;
|
||||
const width = remainingWidth;
|
||||
|
||||
return [
|
||||
[
|
||||
0,
|
||||
offset, // Left
|
||||
offset, // Center
|
||||
offset, // Left | Center
|
||||
offset, // Right
|
||||
offset, // Left | Right
|
||||
offset, // Center | Right
|
||||
offset, // Left | Center | Right
|
||||
], [
|
||||
0,
|
||||
width, // Left
|
||||
width, // Center
|
||||
width, // Left | Center
|
||||
width, // Right
|
||||
width, // Left | Right
|
||||
width, // Center | Right
|
||||
width, // Left | Center | Right
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public equals(other: Settings): boolean {
|
||||
return (
|
||||
this.lineHeight === other.lineHeight
|
||||
&& this.pixelRatio === other.pixelRatio
|
||||
&& this.overviewRulerLanes === other.overviewRulerLanes
|
||||
&& this.renderBorder === other.renderBorder
|
||||
&& this.borderColor === other.borderColor
|
||||
&& this.hideCursor === other.hideCursor
|
||||
&& this.cursorColor === other.cursorColor
|
||||
&& this.themeType === other.themeType
|
||||
&& this.backgroundColor === other.backgroundColor
|
||||
&& this.top === other.top
|
||||
&& this.right === other.right
|
||||
&& this.domWidth === other.domWidth
|
||||
&& this.domHeight === other.domHeight
|
||||
&& this.canvasWidth === other.canvasWidth
|
||||
&& this.canvasHeight === other.canvasHeight
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const enum Constants {
|
||||
MIN_DECORATION_HEIGHT = 6
|
||||
}
|
||||
|
||||
const enum OverviewRulerLane {
|
||||
Left = 1,
|
||||
Center = 2,
|
||||
Right = 4,
|
||||
Full = 7
|
||||
}
|
||||
|
||||
export class DecorationsOverviewRuler extends ViewPart {
|
||||
|
||||
static MIN_DECORATION_HEIGHT = 6;
|
||||
static MAX_DECORATION_HEIGHT = 60;
|
||||
|
||||
private readonly _tokensColorTrackerListener: IDisposable;
|
||||
|
||||
private _overviewRuler: OverviewRulerImpl;
|
||||
|
||||
private _renderBorder: boolean;
|
||||
private _borderColor: string;
|
||||
private _cursorColor: string;
|
||||
|
||||
private _shouldUpdateDecorations: boolean;
|
||||
private _shouldUpdateCursorPosition: boolean;
|
||||
|
||||
private _hideCursor: boolean;
|
||||
private readonly _domNode: FastDomNode<HTMLCanvasElement>;
|
||||
private _settings: Settings;
|
||||
private _cursorPositions: Position[];
|
||||
|
||||
private _zonesFromDecorations: OverviewRulerZone[];
|
||||
private _zonesFromCursors: OverviewRulerZone[];
|
||||
|
||||
constructor(context: ViewContext) {
|
||||
super(context);
|
||||
this._overviewRuler = new OverviewRulerImpl(
|
||||
1,
|
||||
'decorationsOverviewRuler',
|
||||
this._context.viewLayout.getScrollHeight(),
|
||||
this._context.configuration.editor.lineHeight,
|
||||
this._context.configuration.editor.pixelRatio,
|
||||
DecorationsOverviewRuler.MIN_DECORATION_HEIGHT,
|
||||
DecorationsOverviewRuler.MAX_DECORATION_HEIGHT,
|
||||
(lineNumber: number) => this._context.viewLayout.getVerticalOffsetForLineNumber(lineNumber)
|
||||
);
|
||||
this._overviewRuler.setLanesCount(this._context.configuration.editor.viewInfo.overviewRulerLanes, false);
|
||||
this._overviewRuler.setLayout(this._context.configuration.editor.layoutInfo.overviewRuler, false);
|
||||
|
||||
this._renderBorder = this._context.configuration.editor.viewInfo.overviewRulerBorder;
|
||||
this._domNode = createFastDomNode(document.createElement('canvas'));
|
||||
this._domNode.setClassName('decorationsOverviewRuler');
|
||||
this._domNode.setPosition('absolute');
|
||||
this._domNode.setLayerHinting(true);
|
||||
|
||||
this._updateColors();
|
||||
this._settings = null;
|
||||
this._updateSettings(false);
|
||||
|
||||
this._updateBackground(false);
|
||||
this._tokensColorTrackerListener = TokenizationRegistry.onDidChange((e) => {
|
||||
if (e.changedColorMap) {
|
||||
this._updateBackground(true);
|
||||
this._updateSettings(true);
|
||||
}
|
||||
});
|
||||
|
||||
this._shouldUpdateDecorations = true;
|
||||
this._zonesFromDecorations = [];
|
||||
|
||||
this._shouldUpdateCursorPosition = true;
|
||||
this._hideCursor = this._context.configuration.editor.viewInfo.hideCursorInOverviewRuler;
|
||||
|
||||
this._zonesFromCursors = [];
|
||||
this._cursorPositions = [];
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
this._overviewRuler.dispose();
|
||||
this._tokensColorTrackerListener.dispose();
|
||||
}
|
||||
|
||||
private _updateBackground(render: boolean): void {
|
||||
const minimapEnabled = this._context.configuration.editor.viewInfo.minimap.enabled;
|
||||
this._overviewRuler.setUseBackground((minimapEnabled ? TokenizationRegistry.getDefaultBackground() : null), render);
|
||||
}
|
||||
|
||||
// ---- begin view event handlers
|
||||
|
||||
public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
|
||||
let prevLanesCount = this._overviewRuler.getLanesCount();
|
||||
let newLanesCount = this._context.configuration.editor.viewInfo.overviewRulerLanes;
|
||||
|
||||
if (prevLanesCount !== newLanesCount) {
|
||||
this._overviewRuler.setLanesCount(newLanesCount, false);
|
||||
private _updateSettings(renderNow: boolean): boolean {
|
||||
const newSettings = new Settings(this._context.configuration, this._context.theme);
|
||||
if (this._settings !== null && this._settings.equals(newSettings)) {
|
||||
// nothing to do
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e.lineHeight) {
|
||||
this._overviewRuler.setLineHeight(this._context.configuration.editor.lineHeight, false);
|
||||
}
|
||||
this._settings = newSettings;
|
||||
|
||||
if (e.pixelRatio) {
|
||||
this._overviewRuler.setPixelRatio(this._context.configuration.editor.pixelRatio, false);
|
||||
}
|
||||
this._domNode.setTop(this._settings.top);
|
||||
this._domNode.setRight(this._settings.right);
|
||||
this._domNode.setWidth(this._settings.domWidth);
|
||||
this._domNode.setHeight(this._settings.domHeight);
|
||||
this._domNode.domNode.width = this._settings.canvasWidth;
|
||||
this._domNode.domNode.height = this._settings.canvasHeight;
|
||||
|
||||
if (e.viewInfo) {
|
||||
this._renderBorder = this._context.configuration.editor.viewInfo.overviewRulerBorder;
|
||||
this._hideCursor = this._context.configuration.editor.viewInfo.hideCursorInOverviewRuler;
|
||||
this._shouldUpdateCursorPosition = true;
|
||||
this._updateBackground(false);
|
||||
}
|
||||
|
||||
if (e.layoutInfo) {
|
||||
this._overviewRuler.setLayout(this._context.configuration.editor.layoutInfo.overviewRuler, false);
|
||||
if (renderNow) {
|
||||
this._render();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---- begin view event handlers
|
||||
|
||||
public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
|
||||
return this._updateSettings(false);
|
||||
}
|
||||
public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean {
|
||||
this._shouldUpdateCursorPosition = true;
|
||||
this._cursorPositions = [];
|
||||
for (let i = 0, len = e.selections.length; i < len; i++) {
|
||||
this._cursorPositions[i] = e.selections[i].getPosition();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean {
|
||||
this._shouldUpdateDecorations = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public onFlushed(e: viewEvents.ViewFlushedEvent): boolean {
|
||||
this._shouldUpdateCursorPosition = true;
|
||||
this._shouldUpdateDecorations = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean {
|
||||
this._overviewRuler.setScrollHeight(e.scrollHeight, false);
|
||||
return super.onScrollChanged(e) || e.scrollHeightChanged;
|
||||
return e.scrollHeightChanged;
|
||||
}
|
||||
|
||||
public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean {
|
||||
this._updateColors();
|
||||
this._shouldUpdateDecorations = true;
|
||||
this._shouldUpdateCursorPosition = true;
|
||||
return true;
|
||||
// invalidate color cache
|
||||
this._context.model.invalidateOverviewRulerColorCache();
|
||||
return this._updateSettings(false);
|
||||
}
|
||||
|
||||
// ---- end view event handlers
|
||||
|
||||
public getDomNode(): HTMLElement {
|
||||
return this._overviewRuler.getDomNode();
|
||||
}
|
||||
|
||||
private _updateColors() {
|
||||
let borderColor = this._context.theme.getColor(editorOverviewRulerBorder);
|
||||
this._borderColor = borderColor ? borderColor.toString() : null;
|
||||
|
||||
let cursorColor = this._context.theme.getColor(editorCursorForeground);
|
||||
this._cursorColor = cursorColor ? cursorColor.transparent(0.7).toString() : null;
|
||||
|
||||
this._overviewRuler.setThemeType(this._context.theme.type, false);
|
||||
}
|
||||
|
||||
private _createZonesFromDecorations(): OverviewRulerZone[] {
|
||||
let decorations = this._context.model.getAllOverviewRulerDecorations();
|
||||
let zones: OverviewRulerZone[] = [];
|
||||
|
||||
for (let i = 0, len = decorations.length; i < len; i++) {
|
||||
let dec = decorations[i];
|
||||
let overviewRuler = dec.source.options.overviewRuler;
|
||||
zones[i] = new OverviewRulerZone(
|
||||
dec.range.startLineNumber,
|
||||
dec.range.endLineNumber,
|
||||
overviewRuler.position,
|
||||
0,
|
||||
this.resolveRulerColor(overviewRuler.color),
|
||||
this.resolveRulerColor(overviewRuler.darkColor),
|
||||
this.resolveRulerColor(overviewRuler.hcColor)
|
||||
);
|
||||
}
|
||||
|
||||
return zones;
|
||||
}
|
||||
|
||||
private resolveRulerColor(color: string | ThemeColor): string {
|
||||
if (editorCommon.isThemeColor(color)) {
|
||||
let c = this._context.theme.getColor(color.id) || Color.transparent;
|
||||
return c.toString();
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
private _createZonesFromCursors(): OverviewRulerZone[] {
|
||||
let zones: OverviewRulerZone[] = [];
|
||||
|
||||
for (let i = 0, len = this._cursorPositions.length; i < len; i++) {
|
||||
let cursor = this._cursorPositions[i];
|
||||
|
||||
zones[i] = new OverviewRulerZone(
|
||||
cursor.lineNumber,
|
||||
cursor.lineNumber,
|
||||
editorCommon.OverviewRulerLane.Full,
|
||||
2,
|
||||
this._cursorColor,
|
||||
this._cursorColor,
|
||||
this._cursorColor
|
||||
);
|
||||
}
|
||||
|
||||
return zones;
|
||||
return this._domNode.domNode;
|
||||
}
|
||||
|
||||
public prepareRender(ctx: RenderingContext): void {
|
||||
// Nothing to read
|
||||
}
|
||||
|
||||
public render(ctx: RestrictedRenderingContext): void {
|
||||
if (this._shouldUpdateDecorations || this._shouldUpdateCursorPosition) {
|
||||
public render(editorCtx: RestrictedRenderingContext): void {
|
||||
this._render();
|
||||
}
|
||||
|
||||
if (this._shouldUpdateDecorations) {
|
||||
this._shouldUpdateDecorations = false;
|
||||
this._zonesFromDecorations = this._createZonesFromDecorations();
|
||||
}
|
||||
private _render(): void {
|
||||
const canvasWidth = this._settings.canvasWidth;
|
||||
const canvasHeight = this._settings.canvasHeight;
|
||||
const lineHeight = this._settings.lineHeight;
|
||||
const viewLayout = this._context.viewLayout;
|
||||
const outerHeight = this._context.viewLayout.getScrollHeight();
|
||||
const heightRatio = canvasHeight / outerHeight;
|
||||
const decorations = this._context.model.getAllOverviewRulerDecorations(this._context.theme);
|
||||
|
||||
if (this._shouldUpdateCursorPosition) {
|
||||
this._shouldUpdateCursorPosition = false;
|
||||
if (this._hideCursor) {
|
||||
this._zonesFromCursors = [];
|
||||
} else {
|
||||
this._zonesFromCursors = this._createZonesFromCursors();
|
||||
}
|
||||
}
|
||||
const minDecorationHeight = (Constants.MIN_DECORATION_HEIGHT * this._settings.pixelRatio) | 0;
|
||||
const halfMinDecorationHeight = (minDecorationHeight / 2) | 0;
|
||||
|
||||
let allZones: OverviewRulerZone[] = [];
|
||||
allZones = allZones.concat(this._zonesFromCursors);
|
||||
allZones = allZones.concat(this._zonesFromDecorations);
|
||||
|
||||
this._overviewRuler.setZones(allZones, false);
|
||||
const canvasCtx = this._domNode.domNode.getContext('2d');
|
||||
if (this._settings.backgroundColor === null) {
|
||||
canvasCtx.clearRect(0, 0, canvasWidth, canvasHeight);
|
||||
} else {
|
||||
canvasCtx.fillStyle = this._settings.backgroundColor;
|
||||
canvasCtx.fillRect(0, 0, canvasWidth, canvasHeight);
|
||||
}
|
||||
|
||||
let hasRendered = this._overviewRuler.render(false);
|
||||
const x = this._settings.x;
|
||||
const w = this._settings.w;
|
||||
// Avoid flickering by always rendering the colors in the same order
|
||||
// colors that don't use transparency will be sorted last (they start with #)
|
||||
const colors = Object.keys(decorations);
|
||||
colors.sort();
|
||||
for (let cIndex = 0, cLen = colors.length; cIndex < cLen; cIndex++) {
|
||||
const color = colors[cIndex];
|
||||
|
||||
if (hasRendered && this._renderBorder && this._borderColor && this._overviewRuler.getLanesCount() > 0 && (this._zonesFromDecorations.length > 0 || this._zonesFromCursors.length > 0)) {
|
||||
let ctx2 = this._overviewRuler.getDomNode().getContext('2d');
|
||||
ctx2.beginPath();
|
||||
ctx2.lineWidth = 1;
|
||||
ctx2.strokeStyle = this._borderColor;
|
||||
ctx2.moveTo(0, 0);
|
||||
ctx2.lineTo(0, this._overviewRuler.getPixelHeight());
|
||||
ctx2.stroke();
|
||||
const colorDecorations = decorations[color];
|
||||
|
||||
ctx2.moveTo(0, 0);
|
||||
ctx2.lineTo(this._overviewRuler.getPixelWidth(), 0);
|
||||
ctx2.stroke();
|
||||
canvasCtx.fillStyle = color;
|
||||
|
||||
let prevLane = 0;
|
||||
let prevY1 = 0;
|
||||
let prevY2 = 0;
|
||||
for (let i = 0, len = colorDecorations.length; i < len; i++) {
|
||||
const lane = colorDecorations[3 * i];
|
||||
const startLineNumber = colorDecorations[3 * i + 1];
|
||||
const endLineNumber = colorDecorations[3 * i + 2];
|
||||
|
||||
let y1 = (viewLayout.getVerticalOffsetForLineNumber(startLineNumber) * heightRatio) | 0;
|
||||
let y2 = ((viewLayout.getVerticalOffsetForLineNumber(endLineNumber) + lineHeight) * heightRatio) | 0;
|
||||
let height = y2 - y1;
|
||||
if (height < minDecorationHeight) {
|
||||
let yCenter = ((y1 + y2) / 2) | 0;
|
||||
if (yCenter < halfMinDecorationHeight) {
|
||||
yCenter = halfMinDecorationHeight;
|
||||
} else if (yCenter + halfMinDecorationHeight > canvasHeight) {
|
||||
yCenter = canvasHeight - halfMinDecorationHeight;
|
||||
}
|
||||
y1 = yCenter - halfMinDecorationHeight;
|
||||
y2 = yCenter + halfMinDecorationHeight;
|
||||
}
|
||||
|
||||
if (y1 > prevY2 + 1 || lane !== prevLane) {
|
||||
// flush prev
|
||||
if (i !== 0) {
|
||||
canvasCtx.fillRect(x[prevLane], prevY1, w[prevLane], prevY2 - prevY1);
|
||||
}
|
||||
prevLane = lane;
|
||||
prevY1 = y1;
|
||||
prevY2 = y2;
|
||||
} else {
|
||||
// merge into prev
|
||||
if (y2 > prevY2) {
|
||||
prevY2 = y2;
|
||||
}
|
||||
}
|
||||
}
|
||||
canvasCtx.fillRect(x[prevLane], prevY1, w[prevLane], prevY2 - prevY1);
|
||||
}
|
||||
|
||||
// Draw cursors
|
||||
if (!this._settings.hideCursor) {
|
||||
const cursorHeight = (2 * this._settings.pixelRatio) | 0;
|
||||
const halfCursorHeight = (cursorHeight / 2) | 0;
|
||||
const cursorX = this._settings.x[OverviewRulerLane.Full];
|
||||
const cursorW = this._settings.w[OverviewRulerLane.Full];
|
||||
canvasCtx.fillStyle = this._settings.cursorColor;
|
||||
|
||||
let prevY1 = -100;
|
||||
let prevY2 = -100;
|
||||
for (let i = 0, len = this._cursorPositions.length; i < len; i++) {
|
||||
const cursor = this._cursorPositions[i];
|
||||
|
||||
let yCenter = (viewLayout.getVerticalOffsetForLineNumber(cursor.lineNumber) * heightRatio) | 0;
|
||||
if (yCenter < halfCursorHeight) {
|
||||
yCenter = halfCursorHeight;
|
||||
} else if (yCenter + halfCursorHeight > canvasHeight) {
|
||||
yCenter = canvasHeight - halfCursorHeight;
|
||||
}
|
||||
const y1 = yCenter - halfCursorHeight;
|
||||
const y2 = y1 + cursorHeight;
|
||||
|
||||
if (y1 > prevY2 + 1) {
|
||||
// flush prev
|
||||
if (i !== 0) {
|
||||
canvasCtx.fillRect(cursorX, prevY1, cursorW, prevY2 - prevY1);
|
||||
}
|
||||
prevY1 = y1;
|
||||
prevY2 = y2;
|
||||
} else {
|
||||
// merge into prev
|
||||
if (y2 > prevY2) {
|
||||
prevY2 = y2;
|
||||
}
|
||||
}
|
||||
}
|
||||
canvasCtx.fillRect(cursorX, prevY1, cursorW, prevY2 - prevY1);
|
||||
}
|
||||
|
||||
if (this._settings.renderBorder && this._settings.borderColor && this._settings.overviewRulerLanes > 0) {
|
||||
canvasCtx.beginPath();
|
||||
canvasCtx.lineWidth = 1;
|
||||
canvasCtx.strokeStyle = this._settings.borderColor;
|
||||
canvasCtx.moveTo(0, 0);
|
||||
canvasCtx.lineTo(0, canvasHeight);
|
||||
canvasCtx.stroke();
|
||||
|
||||
canvasCtx.moveTo(0, 0);
|
||||
canvasCtx.lineTo(canvasWidth, 0);
|
||||
canvasCtx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer';
|
||||
import { CoreEditorCommand } from 'vs/editor/common/controller/coreCommands';
|
||||
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { editorErrorForeground, editorErrorBorder, editorWarningForeground, editorWarningBorder } from 'vs/editor/common/view/editorColorRegistry';
|
||||
import { editorErrorForeground, editorErrorBorder, editorWarningForeground, editorWarningBorder, editorInfoBorder, editorInfoForeground } from 'vs/editor/common/view/editorColorRegistry';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
|
||||
@@ -400,6 +400,7 @@ export abstract class CodeEditorWidget extends CommonCodeEditor implements edito
|
||||
|
||||
this._view.render(false, true);
|
||||
this.hasView = true;
|
||||
this._view.domNode.domNode.setAttribute('data-uri', model.uri.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,19 +518,28 @@ function getSquigglySVGData(color: Color) {
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
let errorBorderColor = theme.getColor(editorErrorBorder);
|
||||
if (errorBorderColor) {
|
||||
collector.addRule(`.monaco-editor .redsquiggly { border-bottom: 4px double ${errorBorderColor}; }`);
|
||||
collector.addRule(`.monaco-editor .errorsquiggly { border-bottom: 4px double ${errorBorderColor}; }`);
|
||||
}
|
||||
let errorForeground = theme.getColor(editorErrorForeground);
|
||||
if (errorForeground) {
|
||||
collector.addRule(`.monaco-editor .redsquiggly { background: url("data:image/svg+xml,${getSquigglySVGData(errorForeground)}") repeat-x bottom left; }`);
|
||||
collector.addRule(`.monaco-editor .errorsquiggly { background: url("data:image/svg+xml,${getSquigglySVGData(errorForeground)}") repeat-x bottom left; }`);
|
||||
}
|
||||
|
||||
let warningBorderColor = theme.getColor(editorWarningBorder);
|
||||
if (warningBorderColor) {
|
||||
collector.addRule(`.monaco-editor .greensquiggly { border-bottom: 4px double ${warningBorderColor}; }`);
|
||||
collector.addRule(`.monaco-editor .warningsquiggly { border-bottom: 4px double ${warningBorderColor}; }`);
|
||||
}
|
||||
let warningForeground = theme.getColor(editorWarningForeground);
|
||||
if (warningForeground) {
|
||||
collector.addRule(`.monaco-editor .greensquiggly { background: url("data:image/svg+xml;utf8,${getSquigglySVGData(warningForeground)}") repeat-x bottom left; }`);
|
||||
collector.addRule(`.monaco-editor .warningsquiggly { background: url("data:image/svg+xml;utf8,${getSquigglySVGData(warningForeground)}") repeat-x bottom left; }`);
|
||||
}
|
||||
|
||||
let infoBorderColor = theme.getColor(editorInfoBorder);
|
||||
if (warningBorderColor) {
|
||||
collector.addRule(`.monaco-editor .infosquiggly { border-bottom: 4px double ${infoBorderColor}; }`);
|
||||
}
|
||||
let infoForeground = theme.getColor(editorInfoForeground);
|
||||
if (warningForeground) {
|
||||
collector.addRule(`.monaco-editor .infosquiggly { background: url("data:image/svg+xml;utf8,${getSquigglySVGData(infoForeground)}") repeat-x bottom left; }`);
|
||||
}
|
||||
});
|
||||
@@ -11,8 +11,11 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { CodeEditor } from 'vs/editor/browser/codeEditor';
|
||||
import { IConfigurationChangedEvent, IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IConfigurationChangedEvent, IEditorOptions, IDiffEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
|
||||
export class EmbeddedCodeEditorWidget extends CodeEditor {
|
||||
|
||||
@@ -40,7 +43,7 @@ export class EmbeddedCodeEditorWidget extends CodeEditor {
|
||||
this._register(parentEditor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => this._onParentConfigurationChanged(e)));
|
||||
}
|
||||
|
||||
public getParentEditor(): ICodeEditor {
|
||||
getParentEditor(): ICodeEditor {
|
||||
return this._parentEditor;
|
||||
}
|
||||
|
||||
@@ -49,7 +52,49 @@ export class EmbeddedCodeEditorWidget extends CodeEditor {
|
||||
super.updateOptions(this._overwriteOptions);
|
||||
}
|
||||
|
||||
public updateOptions(newOptions: IEditorOptions): void {
|
||||
updateOptions(newOptions: IEditorOptions): void {
|
||||
objects.mixin(this._overwriteOptions, newOptions, true);
|
||||
super.updateOptions(this._overwriteOptions);
|
||||
}
|
||||
}
|
||||
|
||||
export class EmbeddedDiffEditorWidget extends DiffEditorWidget {
|
||||
|
||||
private _parentEditor: ICodeEditor;
|
||||
private _overwriteOptions: IDiffEditorOptions;
|
||||
|
||||
constructor(
|
||||
domElement: HTMLElement,
|
||||
options: IDiffEditorOptions,
|
||||
parentEditor: ICodeEditor,
|
||||
@IEditorWorkerService editorWorkerService: IEditorWorkerService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@ICodeEditorService codeEditorService: ICodeEditorService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IMessageService messageService: IMessageService
|
||||
) {
|
||||
super(domElement, parentEditor.getRawConfiguration(), editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService, messageService);
|
||||
|
||||
this._parentEditor = parentEditor;
|
||||
this._overwriteOptions = options;
|
||||
|
||||
// Overwrite parent's options
|
||||
super.updateOptions(this._overwriteOptions);
|
||||
|
||||
this._register(parentEditor.onDidChangeConfiguration(e => this._onParentConfigurationChanged(e)));
|
||||
}
|
||||
|
||||
getParentEditor(): ICodeEditor {
|
||||
return this._parentEditor;
|
||||
}
|
||||
|
||||
private _onParentConfigurationChanged(e: IConfigurationChangedEvent): void {
|
||||
super.updateOptions(this._parentEditor.getRawConfiguration());
|
||||
super.updateOptions(this._overwriteOptions);
|
||||
}
|
||||
|
||||
updateOptions(newOptions: IEditorOptions): void {
|
||||
objects.mixin(this._overwriteOptions, newOptions, true);
|
||||
super.updateOptions(this._overwriteOptions);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user