mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 17:19:48 +01:00
Merge branch 'master' into ben/electron-sandbox
This commit is contained in:
@@ -13,10 +13,18 @@ export const enum ScrollbarVisibility {
|
||||
}
|
||||
|
||||
export interface ScrollEvent {
|
||||
oldWidth: number;
|
||||
oldScrollWidth: number;
|
||||
oldScrollLeft: number;
|
||||
|
||||
width: number;
|
||||
scrollWidth: number;
|
||||
scrollLeft: number;
|
||||
|
||||
oldHeight: number;
|
||||
oldScrollHeight: number;
|
||||
oldScrollTop: number;
|
||||
|
||||
height: number;
|
||||
scrollHeight: number;
|
||||
scrollTop: number;
|
||||
@@ -134,10 +142,18 @@ export class ScrollState implements IScrollDimensions, IScrollPosition {
|
||||
const scrollTopChanged = (this.scrollTop !== previous.scrollTop);
|
||||
|
||||
return {
|
||||
oldWidth: previous.width,
|
||||
oldScrollWidth: previous.scrollWidth,
|
||||
oldScrollLeft: previous.scrollLeft,
|
||||
|
||||
width: this.width,
|
||||
scrollWidth: this.scrollWidth,
|
||||
scrollLeft: this.scrollLeft,
|
||||
|
||||
oldHeight: previous.height,
|
||||
oldScrollHeight: previous.scrollHeight,
|
||||
oldScrollTop: previous.scrollTop,
|
||||
|
||||
height: this.height,
|
||||
scrollHeight: this.scrollHeight,
|
||||
scrollTop: this.scrollTop,
|
||||
|
||||
@@ -258,14 +258,13 @@ export class TextAreaHandler extends ViewPart {
|
||||
const lineNumber = this._selections[0].startLineNumber;
|
||||
const column = this._selections[0].startColumn - (e.moveOneCharacterLeft ? 1 : 0);
|
||||
|
||||
this._context.privateViewEventBus.emit(new viewEvents.ViewRevealRangeRequestEvent(
|
||||
this._context.model.revealRange(
|
||||
'keyboard',
|
||||
new Range(lineNumber, column, lineNumber, column),
|
||||
null,
|
||||
viewEvents.VerticalRevealType.Simple,
|
||||
true,
|
||||
new Range(lineNumber, column, lineNumber, column),
|
||||
viewEvents.VerticalRevealType.Simple,
|
||||
ScrollType.Immediate
|
||||
));
|
||||
);
|
||||
|
||||
// Find range pixel position
|
||||
const visibleRange = this._viewHelper.visibleRangeForPositionRelativeToEditor(lineNumber, column);
|
||||
@@ -308,12 +307,10 @@ export class TextAreaHandler extends ViewPart {
|
||||
|
||||
this._register(this._textAreaInput.onFocus(() => {
|
||||
this._context.model.setHasFocus(true);
|
||||
this._context.privateViewEventBus.emit(new viewEvents.ViewFocusChangedEvent(true));
|
||||
}));
|
||||
|
||||
this._register(this._textAreaInput.onBlur(() => {
|
||||
this._context.model.setHasFocus(false);
|
||||
this._context.privateViewEventBus.emit(new viewEvents.ViewFocusChangedEvent(false));
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { CoreEditorCommand, CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands';
|
||||
import { IEditorMouseEvent, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser';
|
||||
import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents';
|
||||
import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { IConfiguration } from 'vs/editor/common/editorCommon';
|
||||
@@ -49,18 +49,18 @@ export class ViewController {
|
||||
|
||||
private readonly configuration: IConfiguration;
|
||||
private readonly viewModel: IViewModel;
|
||||
private readonly outgoingEvents: ViewOutgoingEvents;
|
||||
private readonly userInputEvents: ViewUserInputEvents;
|
||||
private readonly commandDelegate: ICommandDelegate;
|
||||
|
||||
constructor(
|
||||
configuration: IConfiguration,
|
||||
viewModel: IViewModel,
|
||||
outgoingEvents: ViewOutgoingEvents,
|
||||
userInputEvents: ViewUserInputEvents,
|
||||
commandDelegate: ICommandDelegate
|
||||
) {
|
||||
this.configuration = configuration;
|
||||
this.viewModel = viewModel;
|
||||
this.outgoingEvents = outgoingEvents;
|
||||
this.userInputEvents = userInputEvents;
|
||||
this.commandDelegate = commandDelegate;
|
||||
}
|
||||
|
||||
@@ -289,42 +289,42 @@ export class ViewController {
|
||||
}
|
||||
|
||||
public emitKeyDown(e: IKeyboardEvent): void {
|
||||
this.outgoingEvents.emitKeyDown(e);
|
||||
this.userInputEvents.emitKeyDown(e);
|
||||
}
|
||||
|
||||
public emitKeyUp(e: IKeyboardEvent): void {
|
||||
this.outgoingEvents.emitKeyUp(e);
|
||||
this.userInputEvents.emitKeyUp(e);
|
||||
}
|
||||
|
||||
public emitContextMenu(e: IEditorMouseEvent): void {
|
||||
this.outgoingEvents.emitContextMenu(e);
|
||||
this.userInputEvents.emitContextMenu(e);
|
||||
}
|
||||
|
||||
public emitMouseMove(e: IEditorMouseEvent): void {
|
||||
this.outgoingEvents.emitMouseMove(e);
|
||||
this.userInputEvents.emitMouseMove(e);
|
||||
}
|
||||
|
||||
public emitMouseLeave(e: IPartialEditorMouseEvent): void {
|
||||
this.outgoingEvents.emitMouseLeave(e);
|
||||
this.userInputEvents.emitMouseLeave(e);
|
||||
}
|
||||
|
||||
public emitMouseUp(e: IEditorMouseEvent): void {
|
||||
this.outgoingEvents.emitMouseUp(e);
|
||||
this.userInputEvents.emitMouseUp(e);
|
||||
}
|
||||
|
||||
public emitMouseDown(e: IEditorMouseEvent): void {
|
||||
this.outgoingEvents.emitMouseDown(e);
|
||||
this.userInputEvents.emitMouseDown(e);
|
||||
}
|
||||
|
||||
public emitMouseDrag(e: IEditorMouseEvent): void {
|
||||
this.outgoingEvents.emitMouseDrag(e);
|
||||
this.userInputEvents.emitMouseDrag(e);
|
||||
}
|
||||
|
||||
public emitMouseDrop(e: IPartialEditorMouseEvent): void {
|
||||
this.outgoingEvents.emitMouseDrop(e);
|
||||
this.userInputEvents.emitMouseDrop(e);
|
||||
}
|
||||
|
||||
public emitMouseWheel(e: IMouseWheelEvent): void {
|
||||
this.outgoingEvents.emitMouseWheel(e);
|
||||
this.userInputEvents.emitMouseWheel(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { PointerHandler } from 'vs/editor/browser/controller/pointerHandler';
|
||||
import { ITextAreaHandlerHelper, TextAreaHandler } from 'vs/editor/browser/controller/textAreaHandler';
|
||||
import { IContentWidget, IContentWidgetPosition, IOverlayWidget, IOverlayWidgetPosition, IMouseTarget, IViewZoneChangeAccessor, IEditorAriaOptions } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICommandDelegate, ViewController } from 'vs/editor/browser/view/viewController';
|
||||
import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents';
|
||||
import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents';
|
||||
import { ContentViewOverlays, MarginViewOverlays } from 'vs/editor/browser/view/viewOverlays';
|
||||
import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart';
|
||||
import { ViewContentWidgets } from 'vs/editor/browser/viewParts/contentWidgets/contentWidgets';
|
||||
@@ -42,7 +42,6 @@ import { Range } from 'vs/editor/common/core/range';
|
||||
import { IConfiguration, ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { RenderingContext } from 'vs/editor/common/view/renderingContext';
|
||||
import { ViewContext } from 'vs/editor/common/view/viewContext';
|
||||
import { ViewEventDispatcher } from 'vs/editor/common/view/viewEventDispatcher';
|
||||
import * as viewEvents from 'vs/editor/common/view/viewEvents';
|
||||
import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
|
||||
import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
|
||||
@@ -64,31 +63,27 @@ export interface IOverlayWidgetData {
|
||||
|
||||
export class View extends ViewEventHandler {
|
||||
|
||||
private readonly eventDispatcher: ViewEventDispatcher;
|
||||
|
||||
private _scrollbar: EditorScrollbar;
|
||||
private readonly _scrollbar: EditorScrollbar;
|
||||
private readonly _context: ViewContext;
|
||||
private _selections: Selection[];
|
||||
|
||||
// The view lines
|
||||
private viewLines: ViewLines;
|
||||
private readonly _viewLines: ViewLines;
|
||||
|
||||
// These are parts, but we must do some API related calls on them, so we keep a reference
|
||||
private viewZones: ViewZones;
|
||||
private contentWidgets: ViewContentWidgets;
|
||||
private overlayWidgets: ViewOverlayWidgets;
|
||||
private viewCursors: ViewCursors;
|
||||
private viewParts: ViewPart[];
|
||||
private readonly _viewZones: ViewZones;
|
||||
private readonly _contentWidgets: ViewContentWidgets;
|
||||
private readonly _overlayWidgets: ViewOverlayWidgets;
|
||||
private readonly _viewCursors: ViewCursors;
|
||||
private readonly _viewParts: ViewPart[];
|
||||
|
||||
private readonly _textAreaHandler: TextAreaHandler;
|
||||
private readonly pointerHandler: PointerHandler;
|
||||
|
||||
private readonly outgoingEvents: ViewOutgoingEvents;
|
||||
private readonly _pointerHandler: PointerHandler;
|
||||
|
||||
// Dom nodes
|
||||
private linesContent: FastDomNode<HTMLElement>;
|
||||
public domNode: FastDomNode<HTMLElement>;
|
||||
private overflowGuardContainer: FastDomNode<HTMLElement>;
|
||||
private readonly _linesContent: FastDomNode<HTMLElement>;
|
||||
public readonly domNode: FastDomNode<HTMLElement>;
|
||||
private readonly _overflowGuardContainer: FastDomNode<HTMLElement>;
|
||||
|
||||
// Actual mutable state
|
||||
private _renderAnimationFrame: IDisposable | null;
|
||||
@@ -98,77 +93,73 @@ export class View extends ViewEventHandler {
|
||||
configuration: IConfiguration,
|
||||
themeService: IThemeService,
|
||||
model: IViewModel,
|
||||
outgoingEvents: ViewOutgoingEvents
|
||||
userInputEvents: ViewUserInputEvents
|
||||
) {
|
||||
super();
|
||||
this._selections = [new Selection(1, 1, 1, 1)];
|
||||
this._renderAnimationFrame = null;
|
||||
this.outgoingEvents = outgoingEvents;
|
||||
|
||||
const viewController = new ViewController(configuration, model, this.outgoingEvents, commandDelegate);
|
||||
|
||||
// The event dispatcher will always go through _renderOnce before dispatching any events
|
||||
this.eventDispatcher = new ViewEventDispatcher((callback: () => void) => this._renderOnce(callback));
|
||||
|
||||
// Ensure the view is the first event handler in order to update the layout
|
||||
this.eventDispatcher.addEventHandler(this);
|
||||
const viewController = new ViewController(configuration, model, userInputEvents, commandDelegate);
|
||||
|
||||
// The view context is passed on to most classes (basically to reduce param. counts in ctors)
|
||||
this._context = new ViewContext(configuration, themeService.getColorTheme(), model, this.eventDispatcher);
|
||||
this._context = new ViewContext(configuration, themeService.getColorTheme(), model);
|
||||
|
||||
// Ensure the view is the first event handler in order to update the layout
|
||||
this._context.addEventHandler(this);
|
||||
|
||||
this._register(themeService.onDidColorThemeChange(theme => {
|
||||
this._context.theme.update(theme);
|
||||
this.eventDispatcher.emit(new viewEvents.ViewThemeChangedEvent());
|
||||
this._context.model.onDidColorThemeChange();
|
||||
this.render(true, false);
|
||||
}));
|
||||
|
||||
this.viewParts = [];
|
||||
this._viewParts = [];
|
||||
|
||||
// Keyboard handler
|
||||
this._textAreaHandler = new TextAreaHandler(this._context, viewController, this.createTextAreaHandlerHelper());
|
||||
this.viewParts.push(this._textAreaHandler);
|
||||
this._textAreaHandler = new TextAreaHandler(this._context, viewController, this._createTextAreaHandlerHelper());
|
||||
this._viewParts.push(this._textAreaHandler);
|
||||
|
||||
// These two dom nodes must be constructed up front, since references are needed in the layout provider (scrolling & co.)
|
||||
this.linesContent = createFastDomNode(document.createElement('div'));
|
||||
this.linesContent.setClassName('lines-content' + ' monaco-editor-background');
|
||||
this.linesContent.setPosition('absolute');
|
||||
this._linesContent = createFastDomNode(document.createElement('div'));
|
||||
this._linesContent.setClassName('lines-content' + ' monaco-editor-background');
|
||||
this._linesContent.setPosition('absolute');
|
||||
|
||||
this.domNode = createFastDomNode(document.createElement('div'));
|
||||
this.domNode.setClassName(this.getEditorClassName());
|
||||
this.domNode.setClassName(this._getEditorClassName());
|
||||
// Set role 'code' for better screen reader support https://github.com/microsoft/vscode/issues/93438
|
||||
this.domNode.setAttribute('role', 'code');
|
||||
|
||||
this.overflowGuardContainer = createFastDomNode(document.createElement('div'));
|
||||
PartFingerprints.write(this.overflowGuardContainer, PartFingerprint.OverflowGuard);
|
||||
this.overflowGuardContainer.setClassName('overflow-guard');
|
||||
this._overflowGuardContainer = createFastDomNode(document.createElement('div'));
|
||||
PartFingerprints.write(this._overflowGuardContainer, PartFingerprint.OverflowGuard);
|
||||
this._overflowGuardContainer.setClassName('overflow-guard');
|
||||
|
||||
this._scrollbar = new EditorScrollbar(this._context, this.linesContent, this.domNode, this.overflowGuardContainer);
|
||||
this.viewParts.push(this._scrollbar);
|
||||
this._scrollbar = new EditorScrollbar(this._context, this._linesContent, this.domNode, this._overflowGuardContainer);
|
||||
this._viewParts.push(this._scrollbar);
|
||||
|
||||
// View Lines
|
||||
this.viewLines = new ViewLines(this._context, this.linesContent);
|
||||
this._viewLines = new ViewLines(this._context, this._linesContent);
|
||||
|
||||
// View Zones
|
||||
this.viewZones = new ViewZones(this._context);
|
||||
this.viewParts.push(this.viewZones);
|
||||
this._viewZones = new ViewZones(this._context);
|
||||
this._viewParts.push(this._viewZones);
|
||||
|
||||
// Decorations overview ruler
|
||||
const decorationsOverviewRuler = new DecorationsOverviewRuler(this._context);
|
||||
this.viewParts.push(decorationsOverviewRuler);
|
||||
this._viewParts.push(decorationsOverviewRuler);
|
||||
|
||||
|
||||
const scrollDecoration = new ScrollDecorationViewPart(this._context);
|
||||
this.viewParts.push(scrollDecoration);
|
||||
this._viewParts.push(scrollDecoration);
|
||||
|
||||
const contentViewOverlays = new ContentViewOverlays(this._context);
|
||||
this.viewParts.push(contentViewOverlays);
|
||||
this._viewParts.push(contentViewOverlays);
|
||||
contentViewOverlays.addDynamicOverlay(new CurrentLineHighlightOverlay(this._context));
|
||||
contentViewOverlays.addDynamicOverlay(new SelectionsOverlay(this._context));
|
||||
contentViewOverlays.addDynamicOverlay(new IndentGuidesOverlay(this._context));
|
||||
contentViewOverlays.addDynamicOverlay(new DecorationsOverlay(this._context));
|
||||
|
||||
const marginViewOverlays = new MarginViewOverlays(this._context);
|
||||
this.viewParts.push(marginViewOverlays);
|
||||
this._viewParts.push(marginViewOverlays);
|
||||
marginViewOverlays.addDynamicOverlay(new CurrentLineMarginHighlightOverlay(this._context));
|
||||
marginViewOverlays.addDynamicOverlay(new GlyphMarginOverlay(this._context));
|
||||
marginViewOverlays.addDynamicOverlay(new MarginViewLineDecorationsOverlay(this._context));
|
||||
@@ -176,26 +167,26 @@ export class View extends ViewEventHandler {
|
||||
marginViewOverlays.addDynamicOverlay(new LineNumbersOverlay(this._context));
|
||||
|
||||
const margin = new Margin(this._context);
|
||||
margin.getDomNode().appendChild(this.viewZones.marginDomNode);
|
||||
margin.getDomNode().appendChild(this._viewZones.marginDomNode);
|
||||
margin.getDomNode().appendChild(marginViewOverlays.getDomNode());
|
||||
this.viewParts.push(margin);
|
||||
this._viewParts.push(margin);
|
||||
|
||||
// Content widgets
|
||||
this.contentWidgets = new ViewContentWidgets(this._context, this.domNode);
|
||||
this.viewParts.push(this.contentWidgets);
|
||||
this._contentWidgets = new ViewContentWidgets(this._context, this.domNode);
|
||||
this._viewParts.push(this._contentWidgets);
|
||||
|
||||
this.viewCursors = new ViewCursors(this._context);
|
||||
this.viewParts.push(this.viewCursors);
|
||||
this._viewCursors = new ViewCursors(this._context);
|
||||
this._viewParts.push(this._viewCursors);
|
||||
|
||||
// Overlay widgets
|
||||
this.overlayWidgets = new ViewOverlayWidgets(this._context);
|
||||
this.viewParts.push(this.overlayWidgets);
|
||||
this._overlayWidgets = new ViewOverlayWidgets(this._context);
|
||||
this._viewParts.push(this._overlayWidgets);
|
||||
|
||||
const rulers = new Rulers(this._context);
|
||||
this.viewParts.push(rulers);
|
||||
this._viewParts.push(rulers);
|
||||
|
||||
const minimap = new Minimap(this._context);
|
||||
this.viewParts.push(minimap);
|
||||
this._viewParts.push(minimap);
|
||||
|
||||
// -------------- Wire dom nodes up
|
||||
|
||||
@@ -204,78 +195,74 @@ export class View extends ViewEventHandler {
|
||||
overviewRulerData.parent.insertBefore(decorationsOverviewRuler.getDomNode(), overviewRulerData.insertBefore);
|
||||
}
|
||||
|
||||
this.linesContent.appendChild(contentViewOverlays.getDomNode());
|
||||
this.linesContent.appendChild(rulers.domNode);
|
||||
this.linesContent.appendChild(this.viewZones.domNode);
|
||||
this.linesContent.appendChild(this.viewLines.getDomNode());
|
||||
this.linesContent.appendChild(this.contentWidgets.domNode);
|
||||
this.linesContent.appendChild(this.viewCursors.getDomNode());
|
||||
this.overflowGuardContainer.appendChild(margin.getDomNode());
|
||||
this.overflowGuardContainer.appendChild(this._scrollbar.getDomNode());
|
||||
this.overflowGuardContainer.appendChild(scrollDecoration.getDomNode());
|
||||
this.overflowGuardContainer.appendChild(this._textAreaHandler.textArea);
|
||||
this.overflowGuardContainer.appendChild(this._textAreaHandler.textAreaCover);
|
||||
this.overflowGuardContainer.appendChild(this.overlayWidgets.getDomNode());
|
||||
this.overflowGuardContainer.appendChild(minimap.getDomNode());
|
||||
this.domNode.appendChild(this.overflowGuardContainer);
|
||||
this.domNode.appendChild(this.contentWidgets.overflowingContentWidgetsDomNode);
|
||||
this._linesContent.appendChild(contentViewOverlays.getDomNode());
|
||||
this._linesContent.appendChild(rulers.domNode);
|
||||
this._linesContent.appendChild(this._viewZones.domNode);
|
||||
this._linesContent.appendChild(this._viewLines.getDomNode());
|
||||
this._linesContent.appendChild(this._contentWidgets.domNode);
|
||||
this._linesContent.appendChild(this._viewCursors.getDomNode());
|
||||
this._overflowGuardContainer.appendChild(margin.getDomNode());
|
||||
this._overflowGuardContainer.appendChild(this._scrollbar.getDomNode());
|
||||
this._overflowGuardContainer.appendChild(scrollDecoration.getDomNode());
|
||||
this._overflowGuardContainer.appendChild(this._textAreaHandler.textArea);
|
||||
this._overflowGuardContainer.appendChild(this._textAreaHandler.textAreaCover);
|
||||
this._overflowGuardContainer.appendChild(this._overlayWidgets.getDomNode());
|
||||
this._overflowGuardContainer.appendChild(minimap.getDomNode());
|
||||
this.domNode.appendChild(this._overflowGuardContainer);
|
||||
this.domNode.appendChild(this._contentWidgets.overflowingContentWidgetsDomNode);
|
||||
|
||||
this._applyLayout();
|
||||
|
||||
// Pointer handler
|
||||
this.pointerHandler = this._register(new PointerHandler(this._context, viewController, this.createPointerHandlerHelper()));
|
||||
|
||||
this._register(model.addViewEventListener((events: viewEvents.ViewEvent[]) => {
|
||||
this.eventDispatcher.emitMany(events);
|
||||
}));
|
||||
this._pointerHandler = this._register(new PointerHandler(this._context, viewController, this._createPointerHandlerHelper()));
|
||||
}
|
||||
|
||||
private _flushAccumulatedAndRenderNow(): void {
|
||||
this._renderNow();
|
||||
}
|
||||
|
||||
private createPointerHandlerHelper(): IPointerHandlerHelper {
|
||||
private _createPointerHandlerHelper(): IPointerHandlerHelper {
|
||||
return {
|
||||
viewDomNode: this.domNode.domNode,
|
||||
linesContentDomNode: this.linesContent.domNode,
|
||||
linesContentDomNode: this._linesContent.domNode,
|
||||
|
||||
focusTextArea: () => {
|
||||
this.focus();
|
||||
},
|
||||
|
||||
getLastRenderData: (): PointerHandlerLastRenderData => {
|
||||
const lastViewCursorsRenderData = this.viewCursors.getLastRenderData() || [];
|
||||
const lastViewCursorsRenderData = this._viewCursors.getLastRenderData() || [];
|
||||
const lastTextareaPosition = this._textAreaHandler.getLastRenderData();
|
||||
return new PointerHandlerLastRenderData(lastViewCursorsRenderData, lastTextareaPosition);
|
||||
},
|
||||
shouldSuppressMouseDownOnViewZone: (viewZoneId: string) => {
|
||||
return this.viewZones.shouldSuppressMouseDownOnViewZone(viewZoneId);
|
||||
return this._viewZones.shouldSuppressMouseDownOnViewZone(viewZoneId);
|
||||
},
|
||||
shouldSuppressMouseDownOnWidget: (widgetId: string) => {
|
||||
return this.contentWidgets.shouldSuppressMouseDownOnWidget(widgetId);
|
||||
return this._contentWidgets.shouldSuppressMouseDownOnWidget(widgetId);
|
||||
},
|
||||
getPositionFromDOMInfo: (spanNode: HTMLElement, offset: number) => {
|
||||
this._flushAccumulatedAndRenderNow();
|
||||
return this.viewLines.getPositionFromDOMInfo(spanNode, offset);
|
||||
return this._viewLines.getPositionFromDOMInfo(spanNode, offset);
|
||||
},
|
||||
|
||||
visibleRangeForPosition: (lineNumber: number, column: number) => {
|
||||
this._flushAccumulatedAndRenderNow();
|
||||
return this.viewLines.visibleRangeForPosition(new Position(lineNumber, column));
|
||||
return this._viewLines.visibleRangeForPosition(new Position(lineNumber, column));
|
||||
},
|
||||
|
||||
getLineWidth: (lineNumber: number) => {
|
||||
this._flushAccumulatedAndRenderNow();
|
||||
return this.viewLines.getLineWidth(lineNumber);
|
||||
return this._viewLines.getLineWidth(lineNumber);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private createTextAreaHandlerHelper(): ITextAreaHandlerHelper {
|
||||
private _createTextAreaHandlerHelper(): ITextAreaHandlerHelper {
|
||||
return {
|
||||
visibleRangeForPositionRelativeToEditor: (lineNumber: number, column: number) => {
|
||||
this._flushAccumulatedAndRenderNow();
|
||||
return this.viewLines.visibleRangeForPosition(new Position(lineNumber, column));
|
||||
return this._viewLines.visibleRangeForPosition(new Position(lineNumber, column));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -287,27 +274,26 @@ export class View extends ViewEventHandler {
|
||||
this.domNode.setWidth(layoutInfo.width);
|
||||
this.domNode.setHeight(layoutInfo.height);
|
||||
|
||||
this.overflowGuardContainer.setWidth(layoutInfo.width);
|
||||
this.overflowGuardContainer.setHeight(layoutInfo.height);
|
||||
this._overflowGuardContainer.setWidth(layoutInfo.width);
|
||||
this._overflowGuardContainer.setHeight(layoutInfo.height);
|
||||
|
||||
this.linesContent.setWidth(1000000);
|
||||
this.linesContent.setHeight(1000000);
|
||||
this._linesContent.setWidth(1000000);
|
||||
this._linesContent.setHeight(1000000);
|
||||
}
|
||||
|
||||
private getEditorClassName() {
|
||||
private _getEditorClassName() {
|
||||
const focused = this._textAreaHandler.isFocused() ? ' focused' : '';
|
||||
return this._context.configuration.options.get(EditorOption.editorClassName) + ' ' + getThemeTypeSelector(this._context.theme.type) + focused;
|
||||
}
|
||||
|
||||
// --- begin event handlers
|
||||
|
||||
public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
|
||||
this.domNode.setClassName(this.getEditorClassName());
|
||||
this._applyLayout();
|
||||
return false;
|
||||
public handleEvents(events: viewEvents.ViewEvent[]): void {
|
||||
super.handleEvents(events);
|
||||
this._scheduleRender();
|
||||
}
|
||||
public onContentSizeChanged(e: viewEvents.ViewContentSizeChangedEvent): boolean {
|
||||
this.outgoingEvents.emitContentSizeChange(e);
|
||||
public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
|
||||
this.domNode.setClassName(this._getEditorClassName());
|
||||
this._applyLayout();
|
||||
return false;
|
||||
}
|
||||
public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean {
|
||||
@@ -315,20 +301,11 @@ export class View extends ViewEventHandler {
|
||||
return false;
|
||||
}
|
||||
public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean {
|
||||
this.domNode.setClassName(this.getEditorClassName());
|
||||
if (e.isFocused) {
|
||||
this.outgoingEvents.emitViewFocusGained();
|
||||
} else {
|
||||
this.outgoingEvents.emitViewFocusLost();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean {
|
||||
this.outgoingEvents.emitScrollChanged(e);
|
||||
this.domNode.setClassName(this._getEditorClassName());
|
||||
return false;
|
||||
}
|
||||
public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean {
|
||||
this.domNode.setClassName(this.getEditorClassName());
|
||||
this.domNode.setClassName(this._getEditorClassName());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -340,26 +317,18 @@ export class View extends ViewEventHandler {
|
||||
this._renderAnimationFrame = null;
|
||||
}
|
||||
|
||||
this.eventDispatcher.removeEventHandler(this);
|
||||
this.outgoingEvents.dispose();
|
||||
this._context.removeEventHandler(this);
|
||||
|
||||
this.viewLines.dispose();
|
||||
this._viewLines.dispose();
|
||||
|
||||
// Destroy view parts
|
||||
for (let i = 0, len = this.viewParts.length; i < len; i++) {
|
||||
this.viewParts[i].dispose();
|
||||
for (let i = 0, len = this._viewParts.length; i < len; i++) {
|
||||
this._viewParts[i].dispose();
|
||||
}
|
||||
this.viewParts = [];
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private _renderOnce<T>(callback: () => T): T {
|
||||
const r = safeInvokeNoArg(callback);
|
||||
this._scheduleRender();
|
||||
return r;
|
||||
}
|
||||
|
||||
private _scheduleRender(): void {
|
||||
if (this._renderAnimationFrame === null) {
|
||||
this._renderAnimationFrame = dom.runAtThisOrScheduleAtNextAnimationFrame(this._onRenderScheduled.bind(this), 100);
|
||||
@@ -377,8 +346,8 @@ export class View extends ViewEventHandler {
|
||||
|
||||
private _getViewPartsToRender(): ViewPart[] {
|
||||
let result: ViewPart[] = [], resultLen = 0;
|
||||
for (let i = 0, len = this.viewParts.length; i < len; i++) {
|
||||
const viewPart = this.viewParts[i];
|
||||
for (let i = 0, len = this._viewParts.length; i < len; i++) {
|
||||
const viewPart = this._viewParts[i];
|
||||
if (viewPart.shouldRender()) {
|
||||
result[resultLen++] = viewPart;
|
||||
}
|
||||
@@ -393,7 +362,7 @@ export class View extends ViewEventHandler {
|
||||
|
||||
let viewPartsToRender = this._getViewPartsToRender();
|
||||
|
||||
if (!this.viewLines.shouldRender() && viewPartsToRender.length === 0) {
|
||||
if (!this._viewLines.shouldRender() && viewPartsToRender.length === 0) {
|
||||
// Nothing to render
|
||||
return;
|
||||
}
|
||||
@@ -408,20 +377,20 @@ export class View extends ViewEventHandler {
|
||||
this._context.model
|
||||
);
|
||||
|
||||
if (this.contentWidgets.shouldRender()) {
|
||||
if (this._contentWidgets.shouldRender()) {
|
||||
// Give the content widgets a chance to set their max width before a possible synchronous layout
|
||||
this.contentWidgets.onBeforeRender(viewportData);
|
||||
this._contentWidgets.onBeforeRender(viewportData);
|
||||
}
|
||||
|
||||
if (this.viewLines.shouldRender()) {
|
||||
this.viewLines.renderText(viewportData);
|
||||
this.viewLines.onDidRender();
|
||||
if (this._viewLines.shouldRender()) {
|
||||
this._viewLines.renderText(viewportData);
|
||||
this._viewLines.onDidRender();
|
||||
|
||||
// Rendering of viewLines might cause scroll events to occur, so collect view parts to render again
|
||||
viewPartsToRender = this._getViewPartsToRender();
|
||||
}
|
||||
|
||||
const renderingContext = new RenderingContext(this._context.viewLayout, viewportData, this.viewLines);
|
||||
const renderingContext = new RenderingContext(this._context.viewLayout, viewportData, this._viewLines);
|
||||
|
||||
// Render the rest of the parts
|
||||
for (let i = 0, len = viewPartsToRender.length; i < len; i++) {
|
||||
@@ -446,7 +415,7 @@ export class View extends ViewEventHandler {
|
||||
this._context.model.setScrollPosition({ scrollTop: scrollPosition.scrollTop }, ScrollType.Immediate);
|
||||
this._context.model.tokenizeViewport();
|
||||
this._renderNow();
|
||||
this.viewLines.updateLineWidths();
|
||||
this._viewLines.updateLineWidths();
|
||||
this._context.model.setScrollPosition({ scrollLeft: scrollPosition.scrollLeft }, ScrollType.Immediate);
|
||||
}
|
||||
|
||||
@@ -457,7 +426,7 @@ export class View extends ViewEventHandler {
|
||||
});
|
||||
const viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(modelPosition);
|
||||
this._flushAccumulatedAndRenderNow();
|
||||
const visibleRange = this.viewLines.visibleRangeForPosition(new Position(viewPosition.lineNumber, viewPosition.column));
|
||||
const visibleRange = this._viewLines.visibleRangeForPosition(new Position(viewPosition.lineNumber, viewPosition.column));
|
||||
if (!visibleRange) {
|
||||
return -1;
|
||||
}
|
||||
@@ -465,33 +434,28 @@ export class View extends ViewEventHandler {
|
||||
}
|
||||
|
||||
public getTargetAtClientPoint(clientX: number, clientY: number): IMouseTarget | null {
|
||||
const mouseTarget = this.pointerHandler.getTargetAtClientPoint(clientX, clientY);
|
||||
const mouseTarget = this._pointerHandler.getTargetAtClientPoint(clientX, clientY);
|
||||
if (!mouseTarget) {
|
||||
return null;
|
||||
}
|
||||
return ViewOutgoingEvents.convertViewToModelMouseTarget(mouseTarget, this._context.model.coordinatesConverter);
|
||||
return ViewUserInputEvents.convertViewToModelMouseTarget(mouseTarget, this._context.model.coordinatesConverter);
|
||||
}
|
||||
|
||||
public createOverviewRuler(cssClassName: string): OverviewRuler {
|
||||
return new OverviewRuler(this._context, cssClassName);
|
||||
}
|
||||
|
||||
public change(callback: (changeAccessor: IViewZoneChangeAccessor) => any): boolean {
|
||||
return this._renderOnce(() => {
|
||||
const zonesHaveChanged = this.viewZones.changeViewZones(callback);
|
||||
if (zonesHaveChanged) {
|
||||
this._context.privateViewEventBus.emit(new viewEvents.ViewZonesChangedEvent());
|
||||
}
|
||||
return zonesHaveChanged;
|
||||
});
|
||||
public change(callback: (changeAccessor: IViewZoneChangeAccessor) => any): void {
|
||||
this._viewZones.changeViewZones(callback);
|
||||
this._scheduleRender();
|
||||
}
|
||||
|
||||
public render(now: boolean, everything: boolean): void {
|
||||
if (everything) {
|
||||
// Force everything to render...
|
||||
this.viewLines.forceShouldRender();
|
||||
for (let i = 0, len = this.viewParts.length; i < len; i++) {
|
||||
const viewPart = this.viewParts[i];
|
||||
this._viewLines.forceShouldRender();
|
||||
for (let i = 0, len = this._viewParts.length; i < len; i++) {
|
||||
const viewPart = this._viewParts[i];
|
||||
viewPart.forceShouldRender();
|
||||
}
|
||||
}
|
||||
@@ -519,7 +483,7 @@ export class View extends ViewEventHandler {
|
||||
}
|
||||
|
||||
public addContentWidget(widgetData: IContentWidgetData): void {
|
||||
this.contentWidgets.addWidget(widgetData.widget);
|
||||
this._contentWidgets.addWidget(widgetData.widget);
|
||||
this.layoutContentWidget(widgetData);
|
||||
this._scheduleRender();
|
||||
}
|
||||
@@ -533,31 +497,31 @@ export class View extends ViewEventHandler {
|
||||
}
|
||||
}
|
||||
const newPreference = widgetData.position ? widgetData.position.preference : null;
|
||||
this.contentWidgets.setWidgetPosition(widgetData.widget, newRange, newPreference);
|
||||
this._contentWidgets.setWidgetPosition(widgetData.widget, newRange, newPreference);
|
||||
this._scheduleRender();
|
||||
}
|
||||
|
||||
public removeContentWidget(widgetData: IContentWidgetData): void {
|
||||
this.contentWidgets.removeWidget(widgetData.widget);
|
||||
this._contentWidgets.removeWidget(widgetData.widget);
|
||||
this._scheduleRender();
|
||||
}
|
||||
|
||||
public addOverlayWidget(widgetData: IOverlayWidgetData): void {
|
||||
this.overlayWidgets.addWidget(widgetData.widget);
|
||||
this._overlayWidgets.addWidget(widgetData.widget);
|
||||
this.layoutOverlayWidget(widgetData);
|
||||
this._scheduleRender();
|
||||
}
|
||||
|
||||
public layoutOverlayWidget(widgetData: IOverlayWidgetData): void {
|
||||
const newPreference = widgetData.position ? widgetData.position.preference : null;
|
||||
const shouldRender = this.overlayWidgets.setWidgetPosition(widgetData.widget, newPreference);
|
||||
const shouldRender = this._overlayWidgets.setWidgetPosition(widgetData.widget, newPreference);
|
||||
if (shouldRender) {
|
||||
this._scheduleRender();
|
||||
}
|
||||
}
|
||||
|
||||
public removeOverlayWidget(widgetData: IOverlayWidgetData): void {
|
||||
this.overlayWidgets.removeWidget(widgetData.widget);
|
||||
this._overlayWidgets.removeWidget(widgetData.widget);
|
||||
this._scheduleRender();
|
||||
}
|
||||
|
||||
|
||||
+6
-38
@@ -4,26 +4,19 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { MouseTarget } from 'vs/editor/browser/controller/mouseTarget';
|
||||
import { IEditorMouseEvent, IMouseTarget, IPartialEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IScrollEvent, IContentSizeChangedEvent } from 'vs/editor/common/editorCommon';
|
||||
import * as viewEvents from 'vs/editor/common/view/viewEvents';
|
||||
import { IViewModel, ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
|
||||
|
||||
export interface EventCallback<T> {
|
||||
(event: T): void;
|
||||
}
|
||||
|
||||
export class ViewOutgoingEvents extends Disposable {
|
||||
export class ViewUserInputEvents {
|
||||
|
||||
public onDidContentSizeChange: EventCallback<IContentSizeChangedEvent> | null = null;
|
||||
public onDidScroll: EventCallback<IScrollEvent> | null = null;
|
||||
public onDidGainFocus: EventCallback<void> | null = null;
|
||||
public onDidLoseFocus: EventCallback<void> | null = null;
|
||||
public onKeyDown: EventCallback<IKeyboardEvent> | null = null;
|
||||
public onKeyUp: EventCallback<IKeyboardEvent> | null = null;
|
||||
public onContextMenu: EventCallback<IEditorMouseEvent> | null = null;
|
||||
@@ -35,35 +28,10 @@ export class ViewOutgoingEvents extends Disposable {
|
||||
public onMouseDrop: EventCallback<IPartialEditorMouseEvent> | null = null;
|
||||
public onMouseWheel: EventCallback<IMouseWheelEvent> | null = null;
|
||||
|
||||
private readonly _viewModel: IViewModel;
|
||||
private readonly _coordinatesConverter: ICoordinatesConverter;
|
||||
|
||||
constructor(viewModel: IViewModel) {
|
||||
super();
|
||||
this._viewModel = viewModel;
|
||||
}
|
||||
|
||||
public emitContentSizeChange(e: viewEvents.ViewContentSizeChangedEvent): void {
|
||||
if (this.onDidContentSizeChange) {
|
||||
this.onDidContentSizeChange(e);
|
||||
}
|
||||
}
|
||||
|
||||
public emitScrollChanged(e: viewEvents.ViewScrollChangedEvent): void {
|
||||
if (this.onDidScroll) {
|
||||
this.onDidScroll(e);
|
||||
}
|
||||
}
|
||||
|
||||
public emitViewFocusGained(): void {
|
||||
if (this.onDidGainFocus) {
|
||||
this.onDidGainFocus(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
public emitViewFocusLost(): void {
|
||||
if (this.onDidLoseFocus) {
|
||||
this.onDidLoseFocus(undefined);
|
||||
}
|
||||
constructor(coordinatesConverter: ICoordinatesConverter) {
|
||||
this._coordinatesConverter = coordinatesConverter;
|
||||
}
|
||||
|
||||
public emitKeyDown(e: IKeyboardEvent): void {
|
||||
@@ -139,7 +107,7 @@ export class ViewOutgoingEvents extends Disposable {
|
||||
}
|
||||
|
||||
private _convertViewToModelMouseTarget(target: IMouseTarget): IMouseTarget {
|
||||
return ViewOutgoingEvents.convertViewToModelMouseTarget(target, this._viewModel.coordinatesConverter);
|
||||
return ViewUserInputEvents.convertViewToModelMouseTarget(target, this._coordinatesConverter);
|
||||
}
|
||||
|
||||
public static convertViewToModelMouseTarget(target: IMouseTarget, coordinatesConverter: ICoordinatesConverter): IMouseTarget {
|
||||
@@ -1011,14 +1011,13 @@ export class Minimap extends ViewPart implements IMinimapModel {
|
||||
if (this._samplingState) {
|
||||
lineNumber = this._samplingState.minimapLines[lineNumber - 1];
|
||||
}
|
||||
this._context.privateViewEventBus.emit(new viewEvents.ViewRevealRangeRequestEvent(
|
||||
this._context.model.revealRange(
|
||||
'mouse',
|
||||
new Range(lineNumber, 1, lineNumber, 1),
|
||||
null,
|
||||
viewEvents.VerticalRevealType.Center,
|
||||
false,
|
||||
new Range(lineNumber, 1, lineNumber, 1),
|
||||
viewEvents.VerticalRevealType.Center,
|
||||
ScrollType.Smooth
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
public setScrollTop(scrollTop: number): void {
|
||||
|
||||
@@ -222,8 +222,6 @@ export class ViewZones extends ViewPart {
|
||||
changeAccessor.addZone = invalidFunc;
|
||||
changeAccessor.removeZone = invalidFunc;
|
||||
changeAccessor.layoutZone = invalidFunc;
|
||||
|
||||
return zonesHaveChanged;
|
||||
});
|
||||
|
||||
return zonesHaveChanged;
|
||||
|
||||
@@ -21,9 +21,9 @@ import { EditorExtensionsRegistry, IEditorContributionDescription } from 'vs/edi
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { ICommandDelegate } from 'vs/editor/browser/view/viewController';
|
||||
import { IContentWidgetData, IOverlayWidgetData, View } from 'vs/editor/browser/view/viewImpl';
|
||||
import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents';
|
||||
import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents';
|
||||
import { ConfigurationChangedEvent, EditorLayoutInfo, IEditorOptions, EditorOption, IComputedEditorOptions, FindComputedEditorOptionValueById, IEditorConstructionOptions, filterValidationDecorations } from 'vs/editor/common/config/editorOptions';
|
||||
import { Cursor, CursorStateChangedEvent } from 'vs/editor/common/controller/cursor';
|
||||
import { Cursor } from 'vs/editor/common/controller/cursor';
|
||||
import { CursorColumns } from 'vs/editor/common/controller/cursorCommon';
|
||||
import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
|
||||
import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
@@ -54,6 +54,7 @@ import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/m
|
||||
import { DOMLineBreaksComputerFactory } from 'vs/editor/browser/view/domLineBreaksComputer';
|
||||
import { WordOperations } from 'vs/editor/common/controller/cursorWordOperations';
|
||||
import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { OutgoingViewModelEventKind } from 'vs/editor/common/viewModel/viewModelEventDispatcher';
|
||||
|
||||
let EDITOR_ID = 0;
|
||||
|
||||
@@ -1384,10 +1385,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
||||
if (!this._modelData || !this._modelData.hasRealView) {
|
||||
return;
|
||||
}
|
||||
const hasChanges = this._modelData.view.change(callback);
|
||||
if (hasChanges) {
|
||||
this._onDidChangeViewZones.fire();
|
||||
}
|
||||
this._modelData.view.change(callback);
|
||||
}
|
||||
|
||||
public getTargetAtClientPoint(clientX: number, clientY: number): editorBrowser.IMouseTarget | null {
|
||||
@@ -1475,38 +1473,56 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
||||
// Someone might destroy the model from under the editor, so prevent any exceptions by setting a null model
|
||||
listenersToRemove.push(model.onWillDispose(() => this.setModel(null)));
|
||||
|
||||
listenersToRemove.push(viewModel.cursor.onDidAttemptReadOnlyEdit(() => {
|
||||
this._onDidAttemptReadOnlyEdit.fire(undefined);
|
||||
}));
|
||||
listenersToRemove.push(viewModel.onEvent((e) => {
|
||||
switch (e.kind) {
|
||||
case OutgoingViewModelEventKind.ContentSizeChanged:
|
||||
this._onDidContentSizeChange.fire(e);
|
||||
break;
|
||||
case OutgoingViewModelEventKind.FocusChanged:
|
||||
this._editorTextFocus.setValue(e.hasFocus);
|
||||
break;
|
||||
case OutgoingViewModelEventKind.ScrollChanged:
|
||||
this._onDidScrollChange.fire(e);
|
||||
break;
|
||||
case OutgoingViewModelEventKind.ViewZonesChanged:
|
||||
this._onDidChangeViewZones.fire();
|
||||
break;
|
||||
case OutgoingViewModelEventKind.ReadOnlyEditAttempt:
|
||||
this._onDidAttemptReadOnlyEdit.fire();
|
||||
break;
|
||||
case OutgoingViewModelEventKind.CursorStateChanged: {
|
||||
if (e.reachedMaxCursorCount) {
|
||||
this._notificationService.warn(nls.localize('cursors.maximum', "The number of cursors has been limited to {0}.", Cursor.MAX_CURSOR_COUNT));
|
||||
}
|
||||
|
||||
const positions: Position[] = [];
|
||||
for (let i = 0, len = e.selections.length; i < len; i++) {
|
||||
positions[i] = e.selections[i].getPosition();
|
||||
}
|
||||
|
||||
const e1: ICursorPositionChangedEvent = {
|
||||
position: positions[0],
|
||||
secondaryPositions: positions.slice(1),
|
||||
reason: e.reason,
|
||||
source: e.source
|
||||
};
|
||||
this._onDidChangeCursorPosition.fire(e1);
|
||||
|
||||
const e2: ICursorSelectionChangedEvent = {
|
||||
selection: e.selections[0],
|
||||
secondarySelections: e.selections.slice(1),
|
||||
modelVersionId: e.modelVersionId,
|
||||
oldSelections: e.oldSelections,
|
||||
oldModelVersionId: e.oldModelVersionId,
|
||||
source: e.source,
|
||||
reason: e.reason
|
||||
};
|
||||
this._onDidChangeCursorSelection.fire(e2);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
listenersToRemove.push(viewModel.cursor.onDidChange((e: CursorStateChangedEvent) => {
|
||||
if (e.reachedMaxCursorCount) {
|
||||
this._notificationService.warn(nls.localize('cursors.maximum', "The number of cursors has been limited to {0}.", Cursor.MAX_CURSOR_COUNT));
|
||||
}
|
||||
|
||||
const positions: Position[] = [];
|
||||
for (let i = 0, len = e.selections.length; i < len; i++) {
|
||||
positions[i] = e.selections[i].getPosition();
|
||||
}
|
||||
|
||||
const e1: ICursorPositionChangedEvent = {
|
||||
position: positions[0],
|
||||
secondaryPositions: positions.slice(1),
|
||||
reason: e.reason,
|
||||
source: e.source
|
||||
};
|
||||
this._onDidChangeCursorPosition.fire(e1);
|
||||
|
||||
const e2: ICursorSelectionChangedEvent = {
|
||||
selection: e.selections[0],
|
||||
secondarySelections: e.selections.slice(1),
|
||||
modelVersionId: e.modelVersionId,
|
||||
oldSelections: e.oldSelections,
|
||||
oldModelVersionId: e.oldModelVersionId,
|
||||
source: e.source,
|
||||
reason: e.reason
|
||||
};
|
||||
this._onDidChangeCursorSelection.fire(e2);
|
||||
}));
|
||||
|
||||
const [view, hasRealView] = this._createView(viewModel);
|
||||
@@ -1587,32 +1603,24 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
||||
};
|
||||
}
|
||||
|
||||
const onDidChangeTextFocus = (textFocus: boolean) => {
|
||||
this._editorTextFocus.setValue(textFocus);
|
||||
};
|
||||
|
||||
const viewOutgoingEvents = new ViewOutgoingEvents(viewModel);
|
||||
viewOutgoingEvents.onDidContentSizeChange = (e) => this._onDidContentSizeChange.fire(e);
|
||||
viewOutgoingEvents.onDidScroll = (e) => this._onDidScrollChange.fire(e);
|
||||
viewOutgoingEvents.onDidGainFocus = () => onDidChangeTextFocus(true);
|
||||
viewOutgoingEvents.onDidLoseFocus = () => onDidChangeTextFocus(false);
|
||||
viewOutgoingEvents.onKeyDown = (e) => this._onKeyDown.fire(e);
|
||||
viewOutgoingEvents.onKeyUp = (e) => this._onKeyUp.fire(e);
|
||||
viewOutgoingEvents.onContextMenu = (e) => this._onContextMenu.fire(e);
|
||||
viewOutgoingEvents.onMouseMove = (e) => this._onMouseMove.fire(e);
|
||||
viewOutgoingEvents.onMouseLeave = (e) => this._onMouseLeave.fire(e);
|
||||
viewOutgoingEvents.onMouseDown = (e) => this._onMouseDown.fire(e);
|
||||
viewOutgoingEvents.onMouseUp = (e) => this._onMouseUp.fire(e);
|
||||
viewOutgoingEvents.onMouseDrag = (e) => this._onMouseDrag.fire(e);
|
||||
viewOutgoingEvents.onMouseDrop = (e) => this._onMouseDrop.fire(e);
|
||||
viewOutgoingEvents.onMouseWheel = (e) => this._onMouseWheel.fire(e);
|
||||
const viewUserInputEvents = new ViewUserInputEvents(viewModel.coordinatesConverter);
|
||||
viewUserInputEvents.onKeyDown = (e) => this._onKeyDown.fire(e);
|
||||
viewUserInputEvents.onKeyUp = (e) => this._onKeyUp.fire(e);
|
||||
viewUserInputEvents.onContextMenu = (e) => this._onContextMenu.fire(e);
|
||||
viewUserInputEvents.onMouseMove = (e) => this._onMouseMove.fire(e);
|
||||
viewUserInputEvents.onMouseLeave = (e) => this._onMouseLeave.fire(e);
|
||||
viewUserInputEvents.onMouseDown = (e) => this._onMouseDown.fire(e);
|
||||
viewUserInputEvents.onMouseUp = (e) => this._onMouseUp.fire(e);
|
||||
viewUserInputEvents.onMouseDrag = (e) => this._onMouseDrag.fire(e);
|
||||
viewUserInputEvents.onMouseDrop = (e) => this._onMouseDrop.fire(e);
|
||||
viewUserInputEvents.onMouseWheel = (e) => this._onMouseWheel.fire(e);
|
||||
|
||||
const view = new View(
|
||||
commandDelegate,
|
||||
this._configuration,
|
||||
this._themeService,
|
||||
viewModel,
|
||||
viewOutgoingEvents
|
||||
viewUserInputEvents
|
||||
);
|
||||
|
||||
return [view, true];
|
||||
|
||||
@@ -283,6 +283,9 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
|
||||
private _onDidChange = this._register(new Emitter<ConfigurationChangedEvent>());
|
||||
public readonly onDidChange: Event<ConfigurationChangedEvent> = this._onDidChange.event;
|
||||
|
||||
private _onDidChangeFast = this._register(new Emitter<ConfigurationChangedEvent>());
|
||||
public readonly onDidChangeFast: Event<ConfigurationChangedEvent> = this._onDidChangeFast.event;
|
||||
|
||||
public readonly isSimpleWidget: boolean;
|
||||
private _computeOptionsMemory: ComputeOptionsMemory;
|
||||
public options!: ComputedEditorOptions;
|
||||
@@ -334,6 +337,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
|
||||
}
|
||||
|
||||
this.options = newOptions;
|
||||
this._onDidChangeFast.fire(changeEvent);
|
||||
this._onDidChange.fire(changeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { CursorCollection } from 'vs/editor/common/controller/cursorCollection';
|
||||
import { CursorColumns, CursorConfiguration, CursorContext, CursorState, EditOperationResult, EditOperationType, IColumnSelectData, PartialCursorState, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon';
|
||||
@@ -17,51 +16,10 @@ import { ISelection, Selection, SelectionDirection } from 'vs/editor/common/core
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, ICursorStateComputer, IIdentifiedSingleEditOperation, IValidEditOperation } from 'vs/editor/common/model';
|
||||
import { RawContentChangedType, ModelRawContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
|
||||
import { ViewEventsCollector, VerticalRevealType, ViewCursorStateChangedEvent, ViewRevealRangeRequestEvent } from 'vs/editor/common/view/viewEvents';
|
||||
import { VerticalRevealType, ViewCursorStateChangedEvent, ViewRevealRangeRequestEvent } from 'vs/editor/common/view/viewEvents';
|
||||
import { dispose, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel';
|
||||
|
||||
export class CursorStateChangedEvent {
|
||||
/**
|
||||
* The new selections.
|
||||
* The primary selection is always at index 0.
|
||||
*/
|
||||
readonly selections: Selection[];
|
||||
/**
|
||||
* The new model version id that `selections` apply to.
|
||||
*/
|
||||
readonly modelVersionId: number;
|
||||
/**
|
||||
* The old selections.
|
||||
*/
|
||||
readonly oldSelections: Selection[] | null;
|
||||
/**
|
||||
* The model version id the that `oldSelections` apply to.
|
||||
*/
|
||||
readonly oldModelVersionId: number;
|
||||
/**
|
||||
* Source of the call that caused the event.
|
||||
*/
|
||||
readonly source: string;
|
||||
/**
|
||||
* Reason.
|
||||
*/
|
||||
readonly reason: CursorChangeReason;
|
||||
/**
|
||||
* The number of cursors was limited because it has reached the maximum cursor count.
|
||||
*/
|
||||
readonly reachedMaxCursorCount: boolean;
|
||||
|
||||
constructor(selections: Selection[], modelVersionId: number, oldSelections: Selection[] | null, oldModelVersionId: number, source: string, reason: CursorChangeReason, reachedMaxCursorCount: boolean) {
|
||||
this.selections = selections;
|
||||
this.modelVersionId = modelVersionId;
|
||||
this.oldSelections = oldSelections;
|
||||
this.oldModelVersionId = oldModelVersionId;
|
||||
this.source = source;
|
||||
this.reason = reason;
|
||||
this.reachedMaxCursorCount = reachedMaxCursorCount;
|
||||
}
|
||||
}
|
||||
import { CursorStateChangedEvent, ViewModelEventsCollector } from 'vs/editor/common/viewModel/viewModelEventDispatcher';
|
||||
|
||||
/**
|
||||
* A snapshot of the cursor and the model state
|
||||
@@ -165,12 +123,6 @@ export class Cursor extends Disposable {
|
||||
|
||||
public static readonly MAX_CURSOR_COUNT = 10000;
|
||||
|
||||
private readonly _onDidAttemptReadOnlyEdit: Emitter<void> = this._register(new Emitter<void>());
|
||||
public readonly onDidAttemptReadOnlyEdit: Event<void> = this._onDidAttemptReadOnlyEdit.event;
|
||||
|
||||
private readonly _onDidChange: Emitter<CursorStateChangedEvent> = this._register(new Emitter<CursorStateChangedEvent>());
|
||||
public readonly onDidChange: Event<CursorStateChangedEvent> = this._onDidChange.event;
|
||||
|
||||
private readonly _model: ITextModel;
|
||||
private _knownModelVersionId: number;
|
||||
private readonly _viewModel: ICursorSimpleModel;
|
||||
@@ -215,7 +167,7 @@ export class Cursor extends Disposable {
|
||||
this._cursors.updateContext(this.context);
|
||||
}
|
||||
|
||||
public onLineMappingChanged(eventsCollector: ViewEventsCollector): void {
|
||||
public onLineMappingChanged(eventsCollector: ViewModelEventsCollector): void {
|
||||
if (this._knownModelVersionId !== this._model.getVersionId()) {
|
||||
// There are model change events that I didn't yet receive.
|
||||
//
|
||||
@@ -262,7 +214,7 @@ export class Cursor extends Disposable {
|
||||
return this._cursors.getAll();
|
||||
}
|
||||
|
||||
public setStates(eventsCollector: ViewEventsCollector, source: string | null | undefined, reason: CursorChangeReason, states: PartialCursorState[] | null): boolean {
|
||||
public setStates(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, reason: CursorChangeReason, states: PartialCursorState[] | null): boolean {
|
||||
let reachedMaxCursorCount = false;
|
||||
if (states !== null && states.length > Cursor.MAX_CURSOR_COUNT) {
|
||||
states = states.slice(0, Cursor.MAX_CURSOR_COUNT);
|
||||
@@ -284,7 +236,7 @@ export class Cursor extends Disposable {
|
||||
this._columnSelectData = columnSelectData;
|
||||
}
|
||||
|
||||
public revealPrimary(eventsCollector: ViewEventsCollector, source: string | null | undefined, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void {
|
||||
public revealPrimary(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void {
|
||||
const viewPositions = this._cursors.getViewPositions();
|
||||
if (viewPositions.length > 1) {
|
||||
this._emitCursorRevealRange(eventsCollector, source, null, this._cursors.getViewSelections(), VerticalRevealType.Simple, revealHorizontal, scrollType);
|
||||
@@ -296,7 +248,7 @@ export class Cursor extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private _revealPrimaryCursor(eventsCollector: ViewEventsCollector, source: string | null | undefined, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void {
|
||||
private _revealPrimaryCursor(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void {
|
||||
const viewPositions = this._cursors.getViewPositions();
|
||||
if (viewPositions.length > 1) {
|
||||
this._emitCursorRevealRange(eventsCollector, source, null, this._cursors.getViewSelections(), verticalType, revealHorizontal, scrollType);
|
||||
@@ -307,8 +259,8 @@ export class Cursor extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private _emitCursorRevealRange(eventsCollector: ViewEventsCollector, source: string | null | undefined, viewRange: Range | null, viewSelections: Selection[] | null, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType) {
|
||||
eventsCollector.emit(new ViewRevealRangeRequestEvent(source, viewRange, viewSelections, verticalType, revealHorizontal, scrollType));
|
||||
private _emitCursorRevealRange(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, viewRange: Range | null, viewSelections: Selection[] | null, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType) {
|
||||
eventsCollector.emitViewEvent(new ViewRevealRangeRequestEvent(source, viewRange, viewSelections, verticalType, revealHorizontal, scrollType));
|
||||
}
|
||||
|
||||
public saveState(): editorCommon.ICursorState[] {
|
||||
@@ -335,7 +287,7 @@ export class Cursor extends Disposable {
|
||||
return result;
|
||||
}
|
||||
|
||||
public restoreState(eventsCollector: ViewEventsCollector, states: editorCommon.ICursorState[]): void {
|
||||
public restoreState(eventsCollector: ViewModelEventsCollector, states: editorCommon.ICursorState[]): void {
|
||||
|
||||
let desiredSelections: ISelection[] = [];
|
||||
|
||||
@@ -376,7 +328,7 @@ export class Cursor extends Disposable {
|
||||
this.revealPrimary(eventsCollector, 'restoreState', true, editorCommon.ScrollType.Immediate);
|
||||
}
|
||||
|
||||
public onModelContentChanged(eventsCollector: ViewEventsCollector, e: ModelRawContentChangedEvent): void {
|
||||
public onModelContentChanged(eventsCollector: ViewModelEventsCollector, e: ModelRawContentChangedEvent): void {
|
||||
|
||||
this._knownModelVersionId = e.versionId;
|
||||
if (this._isHandling) {
|
||||
@@ -441,7 +393,7 @@ export class Cursor extends Disposable {
|
||||
return this._cursors.getPrimaryCursor().modelState.position;
|
||||
}
|
||||
|
||||
public setSelections(eventsCollector: ViewEventsCollector, source: string | null | undefined, selections: readonly ISelection[]): void {
|
||||
public setSelections(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, selections: readonly ISelection[]): void {
|
||||
this.setStates(eventsCollector, source, CursorChangeReason.NotSet, CursorState.fromModelSelections(selections));
|
||||
}
|
||||
|
||||
@@ -533,7 +485,7 @@ export class Cursor extends Disposable {
|
||||
// -----------------------------------------------------------------------------------------------------------
|
||||
// ----- emitting events
|
||||
|
||||
private _emitStateChangedIfNecessary(eventsCollector: ViewEventsCollector, source: string | null | undefined, reason: CursorChangeReason, oldState: CursorModelState | null, reachedMaxCursorCount: boolean): boolean {
|
||||
private _emitStateChangedIfNecessary(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, reason: CursorChangeReason, oldState: CursorModelState | null, reachedMaxCursorCount: boolean): boolean {
|
||||
const newState = new CursorModelState(this._model, this);
|
||||
if (newState.equals(oldState)) {
|
||||
return false;
|
||||
@@ -543,7 +495,7 @@ export class Cursor extends Disposable {
|
||||
const viewSelections = this._cursors.getViewSelections();
|
||||
|
||||
// Let the view get the event first.
|
||||
eventsCollector.emit(new ViewCursorStateChangedEvent(viewSelections, selections));
|
||||
eventsCollector.emitViewEvent(new ViewCursorStateChangedEvent(viewSelections, selections));
|
||||
|
||||
// Only after the view has been notified, let the rest of the world know...
|
||||
if (!oldState
|
||||
@@ -552,7 +504,7 @@ export class Cursor extends Disposable {
|
||||
) {
|
||||
const oldSelections = oldState ? oldState.cursorState.map(s => s.modelState.selection) : null;
|
||||
const oldModelVersionId = oldState ? oldState.modelVersionId : 0;
|
||||
this._onDidChange.fire(new CursorStateChangedEvent(selections, newState.modelVersionId, oldSelections, oldModelVersionId, source || 'keyboard', reason, reachedMaxCursorCount));
|
||||
eventsCollector.emitOutgoingEvent(new CursorStateChangedEvent(oldSelections, selections, oldModelVersionId, newState.modelVersionId, source || 'keyboard', reason, reachedMaxCursorCount));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -597,7 +549,7 @@ export class Cursor extends Disposable {
|
||||
return indices;
|
||||
}
|
||||
|
||||
public executeEdits(eventsCollector: ViewEventsCollector, source: string | null | undefined, edits: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): void {
|
||||
public executeEdits(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, edits: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): void {
|
||||
let autoClosingIndices: [number, number][] | null = null;
|
||||
if (source === 'snippet') {
|
||||
autoClosingIndices = this._findAutoClosingPairs(edits);
|
||||
@@ -639,10 +591,9 @@ export class Cursor extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private _executeEdit(callback: () => void, eventsCollector: ViewEventsCollector, source: string | null | undefined, cursorChangeReason: CursorChangeReason = CursorChangeReason.NotSet): void {
|
||||
private _executeEdit(callback: () => void, eventsCollector: ViewModelEventsCollector, source: string | null | undefined, cursorChangeReason: CursorChangeReason = CursorChangeReason.NotSet): void {
|
||||
if (this.context.cursorConfig.readOnly) {
|
||||
// we cannot edit when read only...
|
||||
this._onDidAttemptReadOnlyEdit.fire(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -665,15 +616,17 @@ export class Cursor extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
public startComposition(eventsCollector: ViewEventsCollector): void {
|
||||
this._isDoingComposition = true;
|
||||
public setIsDoingComposition(isDoingComposition: boolean): void {
|
||||
this._isDoingComposition = isDoingComposition;
|
||||
}
|
||||
|
||||
public startComposition(eventsCollector: ViewModelEventsCollector): void {
|
||||
this._selectionsWhenCompositionStarted = this.getSelections().slice(0);
|
||||
}
|
||||
|
||||
public endComposition(eventsCollector: ViewEventsCollector, source?: string | null | undefined): void {
|
||||
this._isDoingComposition = false;
|
||||
public endComposition(eventsCollector: ViewModelEventsCollector, source?: string | null | undefined): void {
|
||||
this._executeEdit(() => {
|
||||
if (!this._isDoingComposition && source === 'keyboard') {
|
||||
if (source === 'keyboard') {
|
||||
// composition finishes, let's check if we need to auto complete if necessary.
|
||||
const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions);
|
||||
this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._model, this._selectionsWhenCompositionStarted, this.getSelections(), autoClosedCharacters));
|
||||
@@ -682,7 +635,7 @@ export class Cursor extends Disposable {
|
||||
}, eventsCollector, source);
|
||||
}
|
||||
|
||||
public type(eventsCollector: ViewEventsCollector, text: string, source?: string | null | undefined): void {
|
||||
public type(eventsCollector: ViewModelEventsCollector, text: string, source?: string | null | undefined): void {
|
||||
this._executeEdit(() => {
|
||||
if (source === 'keyboard') {
|
||||
// If this event is coming straight from the keyboard, look for electric characters and enter
|
||||
@@ -706,25 +659,25 @@ export class Cursor extends Disposable {
|
||||
}, eventsCollector, source);
|
||||
}
|
||||
|
||||
public replacePreviousChar(eventsCollector: ViewEventsCollector, text: string, replaceCharCnt: number, source?: string | null | undefined): void {
|
||||
public replacePreviousChar(eventsCollector: ViewModelEventsCollector, text: string, replaceCharCnt: number, source?: string | null | undefined): void {
|
||||
this._executeEdit(() => {
|
||||
this._executeEditOperation(TypeOperations.replacePreviousChar(this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), text, replaceCharCnt));
|
||||
}, eventsCollector, source);
|
||||
}
|
||||
|
||||
public paste(eventsCollector: ViewEventsCollector, text: string, pasteOnNewLine: boolean, multicursorText?: string[] | null | undefined, source?: string | null | undefined): void {
|
||||
public paste(eventsCollector: ViewModelEventsCollector, text: string, pasteOnNewLine: boolean, multicursorText?: string[] | null | undefined, source?: string | null | undefined): void {
|
||||
this._executeEdit(() => {
|
||||
this._executeEditOperation(TypeOperations.paste(this.context.cursorConfig, this._model, this.getSelections(), text, pasteOnNewLine, multicursorText || []));
|
||||
}, eventsCollector, source, CursorChangeReason.Paste);
|
||||
}
|
||||
|
||||
public cut(eventsCollector: ViewEventsCollector, source?: string | null | undefined): void {
|
||||
public cut(eventsCollector: ViewModelEventsCollector, source?: string | null | undefined): void {
|
||||
this._executeEdit(() => {
|
||||
this._executeEditOperation(DeleteOperations.cut(this.context.cursorConfig, this._model, this.getSelections()));
|
||||
}, eventsCollector, source);
|
||||
}
|
||||
|
||||
public executeCommand(eventsCollector: ViewEventsCollector, command: editorCommon.ICommand, source?: string | null | undefined): void {
|
||||
public executeCommand(eventsCollector: ViewModelEventsCollector, command: editorCommon.ICommand, source?: string | null | undefined): void {
|
||||
this._executeEdit(() => {
|
||||
this._cursors.killSecondaryCursors();
|
||||
|
||||
@@ -735,7 +688,7 @@ export class Cursor extends Disposable {
|
||||
}, eventsCollector, source);
|
||||
}
|
||||
|
||||
public executeCommands(eventsCollector: ViewEventsCollector, commands: editorCommon.ICommand[], source?: string | null | undefined): void {
|
||||
public executeCommands(eventsCollector: ViewModelEventsCollector, commands: editorCommon.ICommand[], source?: string | null | undefined): void {
|
||||
this._executeEdit(() => {
|
||||
this._executeEditOperation(new EditOperationResult(EditOperationType.Other, commands, {
|
||||
shouldPushStackElementBefore: false,
|
||||
|
||||
@@ -150,6 +150,7 @@ export interface ILineChange extends IChange {
|
||||
* @internal
|
||||
*/
|
||||
export interface IConfiguration extends IDisposable {
|
||||
onDidChangeFast(listener: (e: ConfigurationChangedEvent) => void): IDisposable;
|
||||
onDidChange(listener: (e: ConfigurationChangedEvent) => void): IDisposable;
|
||||
|
||||
readonly options: IComputedEditorOptions;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IConfiguration } from 'vs/editor/common/editorCommon';
|
||||
import { ViewEventDispatcher } from 'vs/editor/common/view/viewEventDispatcher';
|
||||
import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
|
||||
import { IViewLayout, IViewModel } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { IColorTheme, ThemeType } from 'vs/platform/theme/common/themeService';
|
||||
@@ -37,27 +36,24 @@ export class ViewContext {
|
||||
public readonly configuration: IConfiguration;
|
||||
public readonly model: IViewModel;
|
||||
public readonly viewLayout: IViewLayout;
|
||||
public readonly privateViewEventBus: ViewEventDispatcher;
|
||||
public readonly theme: EditorTheme;
|
||||
|
||||
constructor(
|
||||
configuration: IConfiguration,
|
||||
theme: IColorTheme,
|
||||
model: IViewModel,
|
||||
privateViewEventBus: ViewEventDispatcher
|
||||
model: IViewModel
|
||||
) {
|
||||
this.configuration = configuration;
|
||||
this.theme = new EditorTheme(theme);
|
||||
this.model = model;
|
||||
this.viewLayout = model.viewLayout;
|
||||
this.privateViewEventBus = privateViewEventBus;
|
||||
}
|
||||
|
||||
public addEventHandler(eventHandler: ViewEventHandler): void {
|
||||
this.privateViewEventBus.addEventHandler(eventHandler);
|
||||
this.model.addViewEventHandler(eventHandler);
|
||||
}
|
||||
|
||||
public removeEventHandler(eventHandler: ViewEventHandler): void {
|
||||
this.privateViewEventBus.removeEventHandler(eventHandler);
|
||||
this.model.removeViewEventHandler(eventHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ViewEvent } from 'vs/editor/common/view/viewEvents';
|
||||
import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
|
||||
|
||||
export class ViewEventDispatcher {
|
||||
|
||||
private readonly _eventHandlerGateKeeper: (callback: () => void) => void;
|
||||
private readonly _eventHandlers: ViewEventHandler[];
|
||||
private _eventQueue: ViewEvent[] | null;
|
||||
private _isConsumingQueue: boolean;
|
||||
|
||||
constructor(eventHandlerGateKeeper: (callback: () => void) => void) {
|
||||
this._eventHandlerGateKeeper = eventHandlerGateKeeper;
|
||||
this._eventHandlers = [];
|
||||
this._eventQueue = null;
|
||||
this._isConsumingQueue = false;
|
||||
}
|
||||
|
||||
public addEventHandler(eventHandler: ViewEventHandler): void {
|
||||
for (let i = 0, len = this._eventHandlers.length; i < len; i++) {
|
||||
if (this._eventHandlers[i] === eventHandler) {
|
||||
console.warn('Detected duplicate listener in ViewEventDispatcher', eventHandler);
|
||||
}
|
||||
}
|
||||
this._eventHandlers.push(eventHandler);
|
||||
}
|
||||
|
||||
public removeEventHandler(eventHandler: ViewEventHandler): void {
|
||||
for (let i = 0; i < this._eventHandlers.length; i++) {
|
||||
if (this._eventHandlers[i] === eventHandler) {
|
||||
this._eventHandlers.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public emit(event: ViewEvent): void {
|
||||
|
||||
if (this._eventQueue) {
|
||||
this._eventQueue.push(event);
|
||||
} else {
|
||||
this._eventQueue = [event];
|
||||
}
|
||||
|
||||
if (!this._isConsumingQueue) {
|
||||
this.consumeQueue();
|
||||
}
|
||||
}
|
||||
|
||||
public emitMany(events: ViewEvent[]): void {
|
||||
if (this._eventQueue) {
|
||||
this._eventQueue = this._eventQueue.concat(events);
|
||||
} else {
|
||||
this._eventQueue = events;
|
||||
}
|
||||
|
||||
if (!this._isConsumingQueue) {
|
||||
this.consumeQueue();
|
||||
}
|
||||
}
|
||||
|
||||
private consumeQueue(): void {
|
||||
this._eventHandlerGateKeeper(() => {
|
||||
try {
|
||||
this._isConsumingQueue = true;
|
||||
|
||||
this._doConsumeQueue();
|
||||
|
||||
} finally {
|
||||
this._isConsumingQueue = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _doConsumeQueue(): void {
|
||||
while (this._eventQueue) {
|
||||
// Empty event queue, as events might come in while sending these off
|
||||
let events = this._eventQueue;
|
||||
this._eventQueue = null;
|
||||
|
||||
// Use a clone of the event handlers list, as they might remove themselves
|
||||
let eventHandlers = this._eventHandlers.slice(0);
|
||||
for (let i = 0, len = eventHandlers.length; i < len; i++) {
|
||||
eventHandlers[i].handleEvents(events);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,33 +3,30 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ScrollEvent } from 'vs/base/common/scrollable';
|
||||
import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { ScrollType, IContentSizeChangedEvent } from 'vs/editor/common/editorCommon';
|
||||
import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents';
|
||||
|
||||
export const enum ViewEventType {
|
||||
ViewConfigurationChanged = 1,
|
||||
ViewContentSizeChanged = 2,
|
||||
ViewCursorStateChanged = 3,
|
||||
ViewDecorationsChanged = 4,
|
||||
ViewFlushed = 5,
|
||||
ViewFocusChanged = 6,
|
||||
ViewLanguageConfigurationChanged = 7,
|
||||
ViewLineMappingChanged = 8,
|
||||
ViewLinesChanged = 9,
|
||||
ViewLinesDeleted = 10,
|
||||
ViewLinesInserted = 11,
|
||||
ViewRevealRangeRequest = 12,
|
||||
ViewScrollChanged = 13,
|
||||
ViewThemeChanged = 14,
|
||||
ViewTokensChanged = 15,
|
||||
ViewTokensColorsChanged = 16,
|
||||
ViewZonesChanged = 17,
|
||||
ViewConfigurationChanged,
|
||||
ViewCursorStateChanged,
|
||||
ViewDecorationsChanged,
|
||||
ViewFlushed,
|
||||
ViewFocusChanged,
|
||||
ViewLanguageConfigurationChanged,
|
||||
ViewLineMappingChanged,
|
||||
ViewLinesChanged,
|
||||
ViewLinesDeleted,
|
||||
ViewLinesInserted,
|
||||
ViewRevealRangeRequest,
|
||||
ViewScrollChanged,
|
||||
ViewThemeChanged,
|
||||
ViewTokensChanged,
|
||||
ViewTokensColorsChanged,
|
||||
ViewZonesChanged,
|
||||
}
|
||||
|
||||
export class ViewConfigurationChangedEvent {
|
||||
@@ -47,25 +44,6 @@ export class ViewConfigurationChangedEvent {
|
||||
}
|
||||
}
|
||||
|
||||
export class ViewContentSizeChangedEvent implements IContentSizeChangedEvent {
|
||||
|
||||
public readonly type = ViewEventType.ViewContentSizeChanged;
|
||||
|
||||
public readonly contentWidth: number;
|
||||
public readonly contentHeight: number;
|
||||
|
||||
public readonly contentWidthChanged: boolean;
|
||||
public readonly contentHeightChanged: boolean;
|
||||
|
||||
constructor(source: IContentSizeChangedEvent) {
|
||||
this.contentWidth = source.contentWidth;
|
||||
this.contentHeight = source.contentHeight;
|
||||
|
||||
this.contentWidthChanged = source.contentWidthChanged;
|
||||
this.contentHeightChanged = source.contentHeightChanged;
|
||||
}
|
||||
}
|
||||
|
||||
export class ViewCursorStateChangedEvent {
|
||||
|
||||
public readonly type = ViewEventType.ViewCursorStateChanged;
|
||||
@@ -308,7 +286,6 @@ export class ViewZonesChangedEvent {
|
||||
|
||||
export type ViewEvent = (
|
||||
ViewConfigurationChangedEvent
|
||||
| ViewContentSizeChangedEvent
|
||||
| ViewCursorStateChangedEvent
|
||||
| ViewDecorationsChangedEvent
|
||||
| ViewFlushedEvent
|
||||
@@ -325,107 +302,3 @@ export type ViewEvent = (
|
||||
| ViewTokensColorsChangedEvent
|
||||
| ViewZonesChangedEvent
|
||||
);
|
||||
|
||||
export interface IViewEventListener {
|
||||
(events: ViewEvent[]): void;
|
||||
}
|
||||
|
||||
export interface IViewEventEmitter {
|
||||
addViewEventListener(listener: IViewEventListener): IDisposable;
|
||||
}
|
||||
|
||||
export class ViewEventEmitter extends Disposable implements IViewEventEmitter {
|
||||
private _listeners: IViewEventListener[];
|
||||
private _collector: ViewEventsCollector | null;
|
||||
private _collectorCnt: number;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._listeners = [];
|
||||
this._collector = null;
|
||||
this._collectorCnt = 0;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._listeners = [];
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
protected _beginEmitViewEvents(): ViewEventsCollector {
|
||||
this._collectorCnt++;
|
||||
if (this._collectorCnt === 1) {
|
||||
this._collector = new ViewEventsCollector();
|
||||
}
|
||||
return this._collector!;
|
||||
}
|
||||
|
||||
protected _endEmitViewEvents(): void {
|
||||
this._collectorCnt--;
|
||||
if (this._collectorCnt === 0) {
|
||||
const events = this._collector!.finalize();
|
||||
this._collector = null;
|
||||
if (events.length > 0) {
|
||||
this._emit(events);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected _emitSingleViewEvent(event: ViewEvent): void {
|
||||
try {
|
||||
const eventsCollector = this._beginEmitViewEvents();
|
||||
eventsCollector.emit(event);
|
||||
} finally {
|
||||
this._endEmitViewEvents();
|
||||
}
|
||||
}
|
||||
|
||||
private _emit(events: ViewEvent[]): void {
|
||||
const listeners = this._listeners.slice(0);
|
||||
for (let i = 0, len = listeners.length; i < len; i++) {
|
||||
safeInvokeListener(listeners[i], events);
|
||||
}
|
||||
}
|
||||
|
||||
public addViewEventListener(listener: IViewEventListener): IDisposable {
|
||||
this._listeners.push(listener);
|
||||
return toDisposable(() => {
|
||||
let listeners = this._listeners;
|
||||
for (let i = 0, len = listeners.length; i < len; i++) {
|
||||
if (listeners[i] === listener) {
|
||||
listeners.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ViewEventsCollector {
|
||||
|
||||
private _events: ViewEvent[];
|
||||
private _eventsLen = 0;
|
||||
|
||||
constructor() {
|
||||
this._events = [];
|
||||
this._eventsLen = 0;
|
||||
}
|
||||
|
||||
public emit(event: ViewEvent) {
|
||||
this._events[this._eventsLen++] = event;
|
||||
}
|
||||
|
||||
public finalize(): ViewEvent[] {
|
||||
let result = this._events;
|
||||
this._events = [];
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function safeInvokeListener(listener: IViewEventListener, events: ViewEvent[]): void {
|
||||
try {
|
||||
listener(events);
|
||||
} catch (e) {
|
||||
errors.onUnexpectedError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,11 @@ import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility, INewScrollPosition } from 'vs/base/common/scrollable';
|
||||
import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { IConfiguration, IContentSizeChangedEvent, ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { IConfiguration, ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { LinesLayout, IEditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout';
|
||||
import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
|
||||
import { IViewLayout, IViewWhitespaceViewportData, Viewport } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { ContentSizeChangedEvent } from 'vs/editor/common/viewModel/viewModelEventDispatcher';
|
||||
|
||||
const SMOOTH_SCROLLING_TIME = 125;
|
||||
|
||||
@@ -75,8 +76,8 @@ class EditorScrollable extends Disposable {
|
||||
|
||||
public readonly onDidScroll: Event<ScrollEvent>;
|
||||
|
||||
private readonly _onDidContentSizeChange = this._register(new Emitter<IContentSizeChangedEvent>());
|
||||
public readonly onDidContentSizeChange: Event<IContentSizeChangedEvent> = this._onDidContentSizeChange.event;
|
||||
private readonly _onDidContentSizeChange = this._register(new Emitter<ContentSizeChangedEvent>());
|
||||
public readonly onDidContentSizeChange: Event<ContentSizeChangedEvent> = this._onDidContentSizeChange.event;
|
||||
|
||||
constructor(smoothScrollDuration: number, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) {
|
||||
super();
|
||||
@@ -119,13 +120,10 @@ class EditorScrollable extends Disposable {
|
||||
const contentWidthChanged = (oldDimensions.contentWidth !== dimensions.contentWidth);
|
||||
const contentHeightChanged = (oldDimensions.contentHeight !== dimensions.contentHeight);
|
||||
if (contentWidthChanged || contentHeightChanged) {
|
||||
this._onDidContentSizeChange.fire({
|
||||
contentWidth: dimensions.contentWidth,
|
||||
contentHeight: dimensions.contentHeight,
|
||||
|
||||
contentWidthChanged: contentWidthChanged,
|
||||
contentHeightChanged: contentHeightChanged
|
||||
});
|
||||
this._onDidContentSizeChange.fire(new ContentSizeChangedEvent(
|
||||
oldDimensions.contentWidth, oldDimensions.contentHeight,
|
||||
dimensions.contentWidth, dimensions.contentHeight
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +151,7 @@ export class ViewLayout extends Disposable implements IViewLayout {
|
||||
|
||||
private readonly _scrollable: EditorScrollable;
|
||||
public readonly onDidScroll: Event<ScrollEvent>;
|
||||
public readonly onDidContentSizeChange: Event<IContentSizeChangedEvent>;
|
||||
public readonly onDidContentSizeChange: Event<ContentSizeChangedEvent>;
|
||||
|
||||
constructor(configuration: IConfiguration, lineCount: number, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) {
|
||||
super();
|
||||
@@ -353,11 +351,12 @@ export class ViewLayout extends Disposable implements IViewLayout {
|
||||
}
|
||||
|
||||
// ---- IVerticalLayoutProvider
|
||||
public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): void {
|
||||
public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): boolean {
|
||||
const hadAChange = this._linesLayout.changeWhitespace(callback);
|
||||
if (hadAChange) {
|
||||
this.onHeightMaybeChanged();
|
||||
}
|
||||
return hadAChange;
|
||||
}
|
||||
public getVerticalOffsetForLineNumber(lineNumber: number): number {
|
||||
return this._linesLayout.getVerticalOffsetForLineNumber(lineNumber);
|
||||
|
||||
@@ -36,9 +36,7 @@ export class ViewEventHandler extends Disposable {
|
||||
public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
|
||||
return false;
|
||||
}
|
||||
public onContentSizeChanged(e: viewEvents.ViewContentSizeChangedEvent): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean {
|
||||
return false;
|
||||
}
|
||||
@@ -102,12 +100,6 @@ export class ViewEventHandler extends Disposable {
|
||||
}
|
||||
break;
|
||||
|
||||
case viewEvents.ViewEventType.ViewContentSizeChanged:
|
||||
if (this.onContentSizeChanged(e)) {
|
||||
shouldRender = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case viewEvents.ViewEventType.ViewCursorStateChanged:
|
||||
if (this.onCursorStateChanged(e)) {
|
||||
shouldRender = true;
|
||||
|
||||
@@ -10,12 +10,13 @@ import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { INewScrollPosition, ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecorationOptions, TextModelResolvedOptions, ITextModel } from 'vs/editor/common/model';
|
||||
import { IViewEventEmitter, VerticalRevealType } from 'vs/editor/common/view/viewEvents';
|
||||
import { VerticalRevealType } from 'vs/editor/common/view/viewEvents';
|
||||
import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
|
||||
import { IEditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout';
|
||||
import { EditorTheme } from 'vs/editor/common/view/viewContext';
|
||||
import { ICursorSimpleModel, PartialCursorState, CursorState, IColumnSelectData, EditOperationType, CursorConfiguration } from 'vs/editor/common/controller/cursorCommon';
|
||||
import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
|
||||
import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
|
||||
|
||||
export interface IViewWhitespaceViewportData {
|
||||
readonly id: string;
|
||||
@@ -83,7 +84,7 @@ export interface ICoordinatesConverter {
|
||||
modelPositionIsVisible(modelPosition: Position): boolean;
|
||||
}
|
||||
|
||||
export interface IViewModel extends IViewEventEmitter, ICursorSimpleModel {
|
||||
export interface IViewModel extends ICursorSimpleModel {
|
||||
|
||||
readonly model: ITextModel;
|
||||
|
||||
@@ -93,12 +94,16 @@ export interface IViewModel extends IViewEventEmitter, ICursorSimpleModel {
|
||||
|
||||
readonly cursorConfig: CursorConfiguration;
|
||||
|
||||
addViewEventHandler(eventHandler: ViewEventHandler): void;
|
||||
removeViewEventHandler(eventHandler: ViewEventHandler): void;
|
||||
|
||||
/**
|
||||
* Gives a hint that a lot of requests are about to come in for these line numbers.
|
||||
*/
|
||||
setViewport(startLineNumber: number, endLineNumber: number, centeredLineNumber: number): void;
|
||||
tokenizeViewport(): void;
|
||||
setHasFocus(hasFocus: boolean): void;
|
||||
onDidColorThemeChange(): void;
|
||||
|
||||
getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[];
|
||||
getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData;
|
||||
|
||||
@@ -0,0 +1,393 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
|
||||
import { ViewEvent } from 'vs/editor/common/view/viewEvents';
|
||||
import { IContentSizeChangedEvent } from 'vs/editor/common/editorCommon';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
|
||||
|
||||
export class ViewModelEventDispatcher extends Disposable {
|
||||
|
||||
private readonly _onEvent = this._register(new Emitter<OutgoingViewModelEvent>());
|
||||
public readonly onEvent = this._onEvent.event;
|
||||
|
||||
private readonly _eventHandlers: ViewEventHandler[];
|
||||
private _viewEventQueue: ViewEvent[] | null;
|
||||
private _isConsumingViewEventQueue: boolean;
|
||||
private _collector: ViewModelEventsCollector | null;
|
||||
private _collectorCnt: number;
|
||||
private _outgoingEvents: OutgoingViewModelEvent[];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._eventHandlers = [];
|
||||
this._viewEventQueue = null;
|
||||
this._isConsumingViewEventQueue = false;
|
||||
this._collector = null;
|
||||
this._collectorCnt = 0;
|
||||
this._outgoingEvents = [];
|
||||
}
|
||||
|
||||
public emitOutgoingEvent(e: OutgoingViewModelEvent): void {
|
||||
this._addOutgoingEvent(e);
|
||||
this._emitOugoingEvents();
|
||||
}
|
||||
|
||||
private _addOutgoingEvent(e: OutgoingViewModelEvent): void {
|
||||
for (let i = 0, len = this._outgoingEvents.length; i < len; i++) {
|
||||
if (this._outgoingEvents[i].kind === e.kind) {
|
||||
this._outgoingEvents[i] = this._outgoingEvents[i].merge(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// not merged
|
||||
this._outgoingEvents.push(e);
|
||||
}
|
||||
|
||||
private _emitOugoingEvents(): void {
|
||||
while (this._outgoingEvents.length > 0) {
|
||||
if (this._collector || this._isConsumingViewEventQueue) {
|
||||
// right now collecting or emitting view events, so let's postpone emitting
|
||||
return;
|
||||
}
|
||||
const event = this._outgoingEvents.shift()!;
|
||||
if (event.isNoOp()) {
|
||||
continue;
|
||||
}
|
||||
this._onEvent.fire(event);
|
||||
}
|
||||
}
|
||||
|
||||
public addViewEventHandler(eventHandler: ViewEventHandler): void {
|
||||
for (let i = 0, len = this._eventHandlers.length; i < len; i++) {
|
||||
if (this._eventHandlers[i] === eventHandler) {
|
||||
console.warn('Detected duplicate listener in ViewEventDispatcher', eventHandler);
|
||||
}
|
||||
}
|
||||
this._eventHandlers.push(eventHandler);
|
||||
}
|
||||
|
||||
public removeViewEventHandler(eventHandler: ViewEventHandler): void {
|
||||
for (let i = 0; i < this._eventHandlers.length; i++) {
|
||||
if (this._eventHandlers[i] === eventHandler) {
|
||||
this._eventHandlers.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public beginEmitViewEvents(): ViewModelEventsCollector {
|
||||
this._collectorCnt++;
|
||||
if (this._collectorCnt === 1) {
|
||||
this._collector = new ViewModelEventsCollector();
|
||||
}
|
||||
return this._collector!;
|
||||
}
|
||||
|
||||
public endEmitViewEvents(): void {
|
||||
this._collectorCnt--;
|
||||
if (this._collectorCnt === 0) {
|
||||
const outgoingEvents = this._collector!.outgoingEvents;
|
||||
const viewEvents = this._collector!.viewEvents;
|
||||
this._collector = null;
|
||||
|
||||
for (const outgoingEvent of outgoingEvents) {
|
||||
this._addOutgoingEvent(outgoingEvent);
|
||||
}
|
||||
|
||||
if (viewEvents.length > 0) {
|
||||
this._emitMany(viewEvents);
|
||||
}
|
||||
}
|
||||
this._emitOugoingEvents();
|
||||
}
|
||||
|
||||
public emitSingleViewEvent(event: ViewEvent): void {
|
||||
try {
|
||||
const eventsCollector = this.beginEmitViewEvents();
|
||||
eventsCollector.emitViewEvent(event);
|
||||
} finally {
|
||||
this.endEmitViewEvents();
|
||||
}
|
||||
}
|
||||
|
||||
private _emitMany(events: ViewEvent[]): void {
|
||||
if (this._viewEventQueue) {
|
||||
this._viewEventQueue = this._viewEventQueue.concat(events);
|
||||
} else {
|
||||
this._viewEventQueue = events;
|
||||
}
|
||||
|
||||
if (!this._isConsumingViewEventQueue) {
|
||||
this._consumeViewEventQueue();
|
||||
}
|
||||
}
|
||||
|
||||
private _consumeViewEventQueue(): void {
|
||||
try {
|
||||
this._isConsumingViewEventQueue = true;
|
||||
this._doConsumeQueue();
|
||||
} finally {
|
||||
this._isConsumingViewEventQueue = false;
|
||||
}
|
||||
}
|
||||
|
||||
private _doConsumeQueue(): void {
|
||||
while (this._viewEventQueue) {
|
||||
// Empty event queue, as events might come in while sending these off
|
||||
const events = this._viewEventQueue;
|
||||
this._viewEventQueue = null;
|
||||
|
||||
// Use a clone of the event handlers list, as they might remove themselves
|
||||
const eventHandlers = this._eventHandlers.slice(0);
|
||||
for (const eventHandler of eventHandlers) {
|
||||
eventHandler.handleEvents(events);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ViewModelEventsCollector {
|
||||
|
||||
public readonly viewEvents: ViewEvent[];
|
||||
public readonly outgoingEvents: OutgoingViewModelEvent[];
|
||||
|
||||
constructor() {
|
||||
this.viewEvents = [];
|
||||
this.outgoingEvents = [];
|
||||
}
|
||||
|
||||
public emitViewEvent(event: ViewEvent) {
|
||||
this.viewEvents.push(event);
|
||||
}
|
||||
|
||||
public emitOutgoingEvent(e: OutgoingViewModelEvent): void {
|
||||
this.outgoingEvents.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
export const enum OutgoingViewModelEventKind {
|
||||
ContentSizeChanged,
|
||||
FocusChanged,
|
||||
ScrollChanged,
|
||||
ViewZonesChanged,
|
||||
ReadOnlyEditAttempt,
|
||||
CursorStateChanged,
|
||||
}
|
||||
|
||||
export class ContentSizeChangedEvent implements IContentSizeChangedEvent {
|
||||
|
||||
public readonly kind = OutgoingViewModelEventKind.ContentSizeChanged;
|
||||
|
||||
private readonly _oldContentWidth: number;
|
||||
private readonly _oldContentHeight: number;
|
||||
|
||||
readonly contentWidth: number;
|
||||
readonly contentHeight: number;
|
||||
readonly contentWidthChanged: boolean;
|
||||
readonly contentHeightChanged: boolean;
|
||||
|
||||
constructor(oldContentWidth: number, oldContentHeight: number, contentWidth: number, contentHeight: number) {
|
||||
this._oldContentWidth = oldContentWidth;
|
||||
this._oldContentHeight = oldContentHeight;
|
||||
this.contentWidth = contentWidth;
|
||||
this.contentHeight = contentHeight;
|
||||
this.contentWidthChanged = (this._oldContentWidth !== this.contentWidth);
|
||||
this.contentHeightChanged = (this._oldContentHeight !== this.contentHeight);
|
||||
}
|
||||
|
||||
public isNoOp(): boolean {
|
||||
return (!this.contentWidthChanged && !this.contentHeightChanged);
|
||||
}
|
||||
|
||||
|
||||
public merge(other: OutgoingViewModelEvent): ContentSizeChangedEvent {
|
||||
if (other.kind !== OutgoingViewModelEventKind.ContentSizeChanged) {
|
||||
return this;
|
||||
}
|
||||
return new ContentSizeChangedEvent(this._oldContentWidth, this._oldContentHeight, other.contentWidth, other.contentHeight);
|
||||
}
|
||||
}
|
||||
|
||||
export class FocusChangedEvent {
|
||||
|
||||
public readonly kind = OutgoingViewModelEventKind.FocusChanged;
|
||||
|
||||
readonly oldHasFocus: boolean;
|
||||
readonly hasFocus: boolean;
|
||||
|
||||
constructor(oldHasFocus: boolean, hasFocus: boolean) {
|
||||
this.oldHasFocus = oldHasFocus;
|
||||
this.hasFocus = hasFocus;
|
||||
}
|
||||
|
||||
public isNoOp(): boolean {
|
||||
return (this.oldHasFocus === this.hasFocus);
|
||||
}
|
||||
|
||||
public merge(other: OutgoingViewModelEvent): FocusChangedEvent {
|
||||
if (other.kind !== OutgoingViewModelEventKind.FocusChanged) {
|
||||
return this;
|
||||
}
|
||||
return new FocusChangedEvent(this.oldHasFocus, other.hasFocus);
|
||||
}
|
||||
}
|
||||
|
||||
export class ScrollChangedEvent {
|
||||
|
||||
public readonly kind = OutgoingViewModelEventKind.ScrollChanged;
|
||||
|
||||
private readonly _oldScrollWidth: number;
|
||||
private readonly _oldScrollLeft: number;
|
||||
private readonly _oldScrollHeight: number;
|
||||
private readonly _oldScrollTop: number;
|
||||
|
||||
public readonly scrollWidth: number;
|
||||
public readonly scrollLeft: number;
|
||||
public readonly scrollHeight: number;
|
||||
public readonly scrollTop: number;
|
||||
|
||||
public readonly scrollWidthChanged: boolean;
|
||||
public readonly scrollLeftChanged: boolean;
|
||||
public readonly scrollHeightChanged: boolean;
|
||||
public readonly scrollTopChanged: boolean;
|
||||
|
||||
constructor(
|
||||
oldScrollWidth: number, oldScrollLeft: number, oldScrollHeight: number, oldScrollTop: number,
|
||||
scrollWidth: number, scrollLeft: number, scrollHeight: number, scrollTop: number,
|
||||
) {
|
||||
this._oldScrollWidth = oldScrollWidth;
|
||||
this._oldScrollLeft = oldScrollLeft;
|
||||
this._oldScrollHeight = oldScrollHeight;
|
||||
this._oldScrollTop = oldScrollTop;
|
||||
|
||||
this.scrollWidth = scrollWidth;
|
||||
this.scrollLeft = scrollLeft;
|
||||
this.scrollHeight = scrollHeight;
|
||||
this.scrollTop = scrollTop;
|
||||
|
||||
this.scrollWidthChanged = (this._oldScrollWidth !== this.scrollWidth);
|
||||
this.scrollLeftChanged = (this._oldScrollLeft !== this.scrollLeft);
|
||||
this.scrollHeightChanged = (this._oldScrollHeight !== this.scrollHeight);
|
||||
this.scrollTopChanged = (this._oldScrollTop !== this.scrollTop);
|
||||
}
|
||||
|
||||
public isNoOp(): boolean {
|
||||
return (!this.scrollWidthChanged && !this.scrollLeftChanged && !this.scrollHeightChanged && !this.scrollTopChanged);
|
||||
}
|
||||
|
||||
public merge(other: OutgoingViewModelEvent): ScrollChangedEvent {
|
||||
if (other.kind !== OutgoingViewModelEventKind.ScrollChanged) {
|
||||
return this;
|
||||
}
|
||||
return new ScrollChangedEvent(
|
||||
this._oldScrollWidth, this._oldScrollLeft, this._oldScrollHeight, this._oldScrollTop,
|
||||
other.scrollWidth, other.scrollLeft, other.scrollHeight, other.scrollTop
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class ViewZonesChangedEvent {
|
||||
|
||||
public readonly kind = OutgoingViewModelEventKind.ViewZonesChanged;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public isNoOp(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public merge(other: OutgoingViewModelEvent): ViewZonesChangedEvent {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export class CursorStateChangedEvent {
|
||||
|
||||
public readonly kind = OutgoingViewModelEventKind.CursorStateChanged;
|
||||
|
||||
public readonly oldSelections: Selection[] | null;
|
||||
public readonly selections: Selection[];
|
||||
public readonly oldModelVersionId: number;
|
||||
public readonly modelVersionId: number;
|
||||
public readonly source: string;
|
||||
public readonly reason: CursorChangeReason;
|
||||
public readonly reachedMaxCursorCount: boolean;
|
||||
|
||||
constructor(oldSelections: Selection[] | null, selections: Selection[], oldModelVersionId: number, modelVersionId: number, source: string, reason: CursorChangeReason, reachedMaxCursorCount: boolean) {
|
||||
this.oldSelections = oldSelections;
|
||||
this.selections = selections;
|
||||
this.oldModelVersionId = oldModelVersionId;
|
||||
this.modelVersionId = modelVersionId;
|
||||
this.source = source;
|
||||
this.reason = reason;
|
||||
this.reachedMaxCursorCount = reachedMaxCursorCount;
|
||||
}
|
||||
|
||||
private static _selectionsAreEqual(a: Selection[] | null, b: Selection[] | null): boolean {
|
||||
if (!a && !b) {
|
||||
return true;
|
||||
}
|
||||
if (!a || !b) {
|
||||
return false;
|
||||
}
|
||||
const aLen = a.length;
|
||||
const bLen = b.length;
|
||||
if (aLen !== bLen) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < aLen; i++) {
|
||||
if (!a[i].equalsSelection(b[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public isNoOp(): boolean {
|
||||
return (
|
||||
CursorStateChangedEvent._selectionsAreEqual(this.oldSelections, this.selections)
|
||||
&& this.oldModelVersionId === this.modelVersionId
|
||||
);
|
||||
}
|
||||
|
||||
public merge(other: OutgoingViewModelEvent): CursorStateChangedEvent {
|
||||
if (other.kind !== OutgoingViewModelEventKind.CursorStateChanged) {
|
||||
return this;
|
||||
}
|
||||
return new CursorStateChangedEvent(
|
||||
this.oldSelections, other.selections, this.oldModelVersionId, other.modelVersionId, other.source, other.reason, this.reachedMaxCursorCount || other.reachedMaxCursorCount
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class ReadOnlyEditAttemptEvent {
|
||||
|
||||
public readonly kind = OutgoingViewModelEventKind.ReadOnlyEditAttempt;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public isNoOp(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public merge(other: OutgoingViewModelEvent): ReadOnlyEditAttemptEvent {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export type OutgoingViewModelEvent = (
|
||||
ContentSizeChangedEvent
|
||||
| FocusChangedEvent
|
||||
| ScrollChangedEvent
|
||||
| ViewZonesChangedEvent
|
||||
| ReadOnlyEditAttemptEvent
|
||||
| CursorStateChangedEvent
|
||||
);
|
||||
@@ -4,7 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { ConfigurationChangedEvent, EDITOR_FONT_DEFAULTS, EditorOption, filterValidationDecorations } from 'vs/editor/common/config/editorOptions';
|
||||
import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
@@ -29,26 +30,30 @@ import { Cursor } from 'vs/editor/common/controller/cursor';
|
||||
import { PartialCursorState, CursorState, IColumnSelectData, EditOperationType, CursorConfiguration } from 'vs/editor/common/controller/cursorCommon';
|
||||
import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
|
||||
import { IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout';
|
||||
import { ViewModelEventDispatcher, OutgoingViewModelEvent, FocusChangedEvent, ScrollChangedEvent, ViewZonesChangedEvent, ViewModelEventsCollector, ReadOnlyEditAttemptEvent } from 'vs/editor/common/viewModel/viewModelEventDispatcher';
|
||||
import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
|
||||
|
||||
const USE_IDENTITY_LINES_COLLECTION = true;
|
||||
|
||||
export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel {
|
||||
export class ViewModel extends Disposable implements IViewModel {
|
||||
|
||||
private readonly editorId: number;
|
||||
private readonly configuration: IConfiguration;
|
||||
private readonly _editorId: number;
|
||||
private readonly _configuration: IConfiguration;
|
||||
public readonly model: ITextModel;
|
||||
private readonly _eventDispatcher: ViewModelEventDispatcher;
|
||||
public readonly onEvent: Event<OutgoingViewModelEvent>;
|
||||
public cursorConfig: CursorConfiguration;
|
||||
private readonly _tokenizeViewportSoon: RunOnceScheduler;
|
||||
private readonly _updateConfigurationViewLineCount: RunOnceScheduler;
|
||||
private hasFocus: boolean;
|
||||
private viewportStartLine: number;
|
||||
private viewportStartLineTrackedRange: string | null;
|
||||
private viewportStartLineDelta: number;
|
||||
private readonly lines: IViewModelLinesCollection;
|
||||
private _hasFocus: boolean;
|
||||
private _viewportStartLine: number;
|
||||
private _viewportStartLineTrackedRange: string | null;
|
||||
private _viewportStartLineDelta: number;
|
||||
private readonly _lines: IViewModelLinesCollection;
|
||||
public readonly coordinatesConverter: ICoordinatesConverter;
|
||||
public readonly viewLayout: ViewLayout;
|
||||
public readonly cursor: Cursor;
|
||||
private readonly decorations: ViewModelDecorations;
|
||||
private readonly _cursor: Cursor;
|
||||
private readonly _decorations: ViewModelDecorations;
|
||||
|
||||
constructor(
|
||||
editorId: number,
|
||||
@@ -60,29 +65,31 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
) {
|
||||
super();
|
||||
|
||||
this.editorId = editorId;
|
||||
this.configuration = configuration;
|
||||
this._editorId = editorId;
|
||||
this._configuration = configuration;
|
||||
this.model = model;
|
||||
this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this.configuration);
|
||||
this._eventDispatcher = new ViewModelEventDispatcher();
|
||||
this.onEvent = this._eventDispatcher.onEvent;
|
||||
this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this._configuration);
|
||||
this._tokenizeViewportSoon = this._register(new RunOnceScheduler(() => this.tokenizeViewport(), 50));
|
||||
this._updateConfigurationViewLineCount = this._register(new RunOnceScheduler(() => this._updateConfigurationViewLineCountNow(), 0));
|
||||
this.hasFocus = false;
|
||||
this.viewportStartLine = -1;
|
||||
this.viewportStartLineTrackedRange = null;
|
||||
this.viewportStartLineDelta = 0;
|
||||
this._hasFocus = false;
|
||||
this._viewportStartLine = -1;
|
||||
this._viewportStartLineTrackedRange = null;
|
||||
this._viewportStartLineDelta = 0;
|
||||
|
||||
if (USE_IDENTITY_LINES_COLLECTION && this.model.isTooLargeForTokenization()) {
|
||||
|
||||
this.lines = new IdentityLinesCollection(this.model);
|
||||
this._lines = new IdentityLinesCollection(this.model);
|
||||
|
||||
} else {
|
||||
const options = this.configuration.options;
|
||||
const options = this._configuration.options;
|
||||
const fontInfo = options.get(EditorOption.fontInfo);
|
||||
const wrappingStrategy = options.get(EditorOption.wrappingStrategy);
|
||||
const wrappingInfo = options.get(EditorOption.wrappingInfo);
|
||||
const wrappingIndent = options.get(EditorOption.wrappingIndent);
|
||||
|
||||
this.lines = new SplitLinesCollection(
|
||||
this._lines = new SplitLinesCollection(
|
||||
this.model,
|
||||
domLineBreaksComputerFactory,
|
||||
monospaceLineBreaksComputerFactory,
|
||||
@@ -94,38 +101,42 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
);
|
||||
}
|
||||
|
||||
this.coordinatesConverter = this.lines.createCoordinatesConverter();
|
||||
this.coordinatesConverter = this._lines.createCoordinatesConverter();
|
||||
|
||||
this.cursor = this._register(new Cursor(model, this, this.coordinatesConverter, this.cursorConfig));
|
||||
this._cursor = this._register(new Cursor(model, this, this.coordinatesConverter, this.cursorConfig));
|
||||
|
||||
this.viewLayout = this._register(new ViewLayout(this.configuration, this.getLineCount(), scheduleAtNextAnimationFrame));
|
||||
this.viewLayout = this._register(new ViewLayout(this._configuration, this.getLineCount(), scheduleAtNextAnimationFrame));
|
||||
|
||||
this._register(this.viewLayout.onDidScroll((e) => {
|
||||
if (e.scrollTopChanged) {
|
||||
this._tokenizeViewportSoon.schedule();
|
||||
}
|
||||
this._emitSingleViewEvent(new viewEvents.ViewScrollChangedEvent(e));
|
||||
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewScrollChangedEvent(e));
|
||||
this._eventDispatcher.emitOutgoingEvent(new ScrollChangedEvent(
|
||||
e.oldScrollWidth, e.oldScrollLeft, e.oldScrollHeight, e.oldScrollTop,
|
||||
e.scrollWidth, e.scrollLeft, e.scrollHeight, e.scrollTop
|
||||
));
|
||||
}));
|
||||
|
||||
this._register(this.viewLayout.onDidContentSizeChange((e) => {
|
||||
this._emitSingleViewEvent(new viewEvents.ViewContentSizeChangedEvent(e));
|
||||
this._eventDispatcher.emitOutgoingEvent(e);
|
||||
}));
|
||||
|
||||
this.decorations = new ViewModelDecorations(this.editorId, this.model, this.configuration, this.lines, this.coordinatesConverter);
|
||||
this._decorations = new ViewModelDecorations(this._editorId, this.model, this._configuration, this._lines, this.coordinatesConverter);
|
||||
|
||||
this._registerModelEvents();
|
||||
|
||||
this._register(this.configuration.onDidChange((e) => {
|
||||
this._register(this._configuration.onDidChangeFast((e) => {
|
||||
try {
|
||||
const eventsCollector = this._beginEmitViewEvents();
|
||||
const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
|
||||
this._onConfigurationChanged(eventsCollector, e);
|
||||
} finally {
|
||||
this._endEmitViewEvents();
|
||||
this._eventDispatcher.endEmitViewEvents();
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(MinimapTokensColorTracker.getInstance().onDidChange(() => {
|
||||
this._emitSingleViewEvent(new viewEvents.ViewTokensColorsChangedEvent());
|
||||
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewTokensColorsChangedEvent());
|
||||
}));
|
||||
|
||||
this._updateConfigurationViewLineCountNow();
|
||||
@@ -135,14 +146,23 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
// First remove listeners, as disposing the lines might end up sending
|
||||
// model decoration changed events ... and we no longer care about them ...
|
||||
super.dispose();
|
||||
this.decorations.dispose();
|
||||
this.lines.dispose();
|
||||
this._decorations.dispose();
|
||||
this._lines.dispose();
|
||||
this.invalidateMinimapColorCache();
|
||||
this.viewportStartLineTrackedRange = this.model._setTrackedRange(this.viewportStartLineTrackedRange, null, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
|
||||
this._viewportStartLineTrackedRange = this.model._setTrackedRange(this._viewportStartLineTrackedRange, null, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
|
||||
this._eventDispatcher.dispose();
|
||||
}
|
||||
|
||||
public addViewEventHandler(eventHandler: ViewEventHandler): void {
|
||||
this._eventDispatcher.addViewEventHandler(eventHandler);
|
||||
}
|
||||
|
||||
public removeViewEventHandler(eventHandler: ViewEventHandler): void {
|
||||
this._eventDispatcher.removeViewEventHandler(eventHandler);
|
||||
}
|
||||
|
||||
private _updateConfigurationViewLineCountNow(): void {
|
||||
this.configuration.setViewLineCount(this.lines.getViewLineCount());
|
||||
this._configuration.setViewLineCount(this._lines.getViewLineCount());
|
||||
}
|
||||
|
||||
public tokenizeViewport(): void {
|
||||
@@ -153,32 +173,38 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
}
|
||||
|
||||
public setHasFocus(hasFocus: boolean): void {
|
||||
this.hasFocus = hasFocus;
|
||||
this.cursor.setHasFocus(hasFocus);
|
||||
this._hasFocus = hasFocus;
|
||||
this._cursor.setHasFocus(hasFocus);
|
||||
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewFocusChangedEvent(hasFocus));
|
||||
this._eventDispatcher.emitOutgoingEvent(new FocusChangedEvent(!hasFocus, hasFocus));
|
||||
}
|
||||
|
||||
private _onConfigurationChanged(eventsCollector: viewEvents.ViewEventsCollector, e: ConfigurationChangedEvent): void {
|
||||
public onDidColorThemeChange(): void {
|
||||
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewThemeChangedEvent());
|
||||
}
|
||||
|
||||
private _onConfigurationChanged(eventsCollector: ViewModelEventsCollector, e: ConfigurationChangedEvent): void {
|
||||
|
||||
// We might need to restore the current centered view range, so save it (if available)
|
||||
let previousViewportStartModelPosition: Position | null = null;
|
||||
if (this.viewportStartLine !== -1) {
|
||||
let previousViewportStartViewPosition = new Position(this.viewportStartLine, this.getLineMinColumn(this.viewportStartLine));
|
||||
if (this._viewportStartLine !== -1) {
|
||||
let previousViewportStartViewPosition = new Position(this._viewportStartLine, this.getLineMinColumn(this._viewportStartLine));
|
||||
previousViewportStartModelPosition = this.coordinatesConverter.convertViewPositionToModelPosition(previousViewportStartViewPosition);
|
||||
}
|
||||
let restorePreviousViewportStart = false;
|
||||
|
||||
const options = this.configuration.options;
|
||||
const options = this._configuration.options;
|
||||
const fontInfo = options.get(EditorOption.fontInfo);
|
||||
const wrappingStrategy = options.get(EditorOption.wrappingStrategy);
|
||||
const wrappingInfo = options.get(EditorOption.wrappingInfo);
|
||||
const wrappingIndent = options.get(EditorOption.wrappingIndent);
|
||||
|
||||
if (this.lines.setWrappingSettings(fontInfo, wrappingStrategy, wrappingInfo.wrappingColumn, wrappingIndent)) {
|
||||
eventsCollector.emit(new viewEvents.ViewFlushedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
this.cursor.onLineMappingChanged(eventsCollector);
|
||||
this.decorations.onLineMappingChanged();
|
||||
if (this._lines.setWrappingSettings(fontInfo, wrappingStrategy, wrappingInfo.wrappingColumn, wrappingIndent)) {
|
||||
eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent());
|
||||
eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent());
|
||||
eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
this._cursor.onLineMappingChanged(eventsCollector);
|
||||
this._decorations.onLineMappingChanged();
|
||||
this.viewLayout.onFlushed(this.getLineCount());
|
||||
|
||||
if (this.viewLayout.getCurrentScrollTop() !== 0) {
|
||||
@@ -191,22 +217,22 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
|
||||
if (e.hasChanged(EditorOption.readOnly)) {
|
||||
// Must read again all decorations due to readOnly filtering
|
||||
this.decorations.reset();
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
this._decorations.reset();
|
||||
eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
}
|
||||
|
||||
eventsCollector.emit(new viewEvents.ViewConfigurationChangedEvent(e));
|
||||
eventsCollector.emitViewEvent(new viewEvents.ViewConfigurationChangedEvent(e));
|
||||
this.viewLayout.onConfigurationChanged(e);
|
||||
|
||||
if (restorePreviousViewportStart && previousViewportStartModelPosition) {
|
||||
const viewPosition = this.coordinatesConverter.convertModelPositionToViewPosition(previousViewportStartModelPosition);
|
||||
const viewPositionTop = this.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber);
|
||||
this.viewLayout.setScrollPosition({ scrollTop: viewPositionTop + this.viewportStartLineDelta }, ScrollType.Immediate);
|
||||
this.viewLayout.setScrollPosition({ scrollTop: viewPositionTop + this._viewportStartLineDelta }, ScrollType.Immediate);
|
||||
}
|
||||
|
||||
if (CursorConfiguration.shouldRecreate(e)) {
|
||||
this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this.configuration);
|
||||
this.cursor.updateConfiguration(this.cursorConfig);
|
||||
this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this._configuration);
|
||||
this._cursor.updateConfiguration(this.cursorConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +240,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
|
||||
this._register(this.model.onDidChangeRawContentFast((e) => {
|
||||
try {
|
||||
const eventsCollector = this._beginEmitViewEvents();
|
||||
const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
|
||||
|
||||
let hadOtherModelChange = false;
|
||||
let hadModelLineChangeThatChangedLineMapping = false;
|
||||
@@ -223,7 +249,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
const versionId = e.versionId;
|
||||
|
||||
// Do a first pass to compute line mappings, and a second pass to actually interpret them
|
||||
const lineBreaksComputer = this.lines.createLineBreaksComputer();
|
||||
const lineBreaksComputer = this._lines.createLineBreaksComputer();
|
||||
for (const change of changes) {
|
||||
switch (change.changeType) {
|
||||
case textModelEvents.RawContentChangedType.LinesInserted: {
|
||||
@@ -245,17 +271,17 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
|
||||
switch (change.changeType) {
|
||||
case textModelEvents.RawContentChangedType.Flush: {
|
||||
this.lines.onModelFlushed();
|
||||
eventsCollector.emit(new viewEvents.ViewFlushedEvent());
|
||||
this.decorations.reset();
|
||||
this._lines.onModelFlushed();
|
||||
eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent());
|
||||
this._decorations.reset();
|
||||
this.viewLayout.onFlushed(this.getLineCount());
|
||||
hadOtherModelChange = true;
|
||||
break;
|
||||
}
|
||||
case textModelEvents.RawContentChangedType.LinesDeleted: {
|
||||
const linesDeletedEvent = this.lines.onModelLinesDeleted(versionId, change.fromLineNumber, change.toLineNumber);
|
||||
const linesDeletedEvent = this._lines.onModelLinesDeleted(versionId, change.fromLineNumber, change.toLineNumber);
|
||||
if (linesDeletedEvent !== null) {
|
||||
eventsCollector.emit(linesDeletedEvent);
|
||||
eventsCollector.emitViewEvent(linesDeletedEvent);
|
||||
this.viewLayout.onLinesDeleted(linesDeletedEvent.fromLineNumber, linesDeletedEvent.toLineNumber);
|
||||
}
|
||||
hadOtherModelChange = true;
|
||||
@@ -265,9 +291,9 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
const insertedLineBreaks = lineBreaks.slice(lineBreaksOffset, lineBreaksOffset + change.detail.length);
|
||||
lineBreaksOffset += change.detail.length;
|
||||
|
||||
const linesInsertedEvent = this.lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, insertedLineBreaks);
|
||||
const linesInsertedEvent = this._lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, insertedLineBreaks);
|
||||
if (linesInsertedEvent !== null) {
|
||||
eventsCollector.emit(linesInsertedEvent);
|
||||
eventsCollector.emitViewEvent(linesInsertedEvent);
|
||||
this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber);
|
||||
}
|
||||
hadOtherModelChange = true;
|
||||
@@ -277,17 +303,17 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
const changedLineBreakData = lineBreaks[lineBreaksOffset];
|
||||
lineBreaksOffset++;
|
||||
|
||||
const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this.lines.onModelLineChanged(versionId, change.lineNumber, changedLineBreakData);
|
||||
const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this._lines.onModelLineChanged(versionId, change.lineNumber, changedLineBreakData);
|
||||
hadModelLineChangeThatChangedLineMapping = lineMappingChanged;
|
||||
if (linesChangedEvent) {
|
||||
eventsCollector.emit(linesChangedEvent);
|
||||
eventsCollector.emitViewEvent(linesChangedEvent);
|
||||
}
|
||||
if (linesInsertedEvent) {
|
||||
eventsCollector.emit(linesInsertedEvent);
|
||||
eventsCollector.emitViewEvent(linesInsertedEvent);
|
||||
this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber);
|
||||
}
|
||||
if (linesDeletedEvent) {
|
||||
eventsCollector.emit(linesDeletedEvent);
|
||||
eventsCollector.emitViewEvent(linesDeletedEvent);
|
||||
this.viewLayout.onLinesDeleted(linesDeletedEvent.fromLineNumber, linesDeletedEvent.toLineNumber);
|
||||
}
|
||||
break;
|
||||
@@ -298,39 +324,39 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
}
|
||||
}
|
||||
}
|
||||
this.lines.acceptVersionId(versionId);
|
||||
this._lines.acceptVersionId(versionId);
|
||||
this.viewLayout.onHeightMaybeChanged();
|
||||
|
||||
if (!hadOtherModelChange && hadModelLineChangeThatChangedLineMapping) {
|
||||
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
this.cursor.onLineMappingChanged(eventsCollector);
|
||||
this.decorations.onLineMappingChanged();
|
||||
eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent());
|
||||
eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
this._cursor.onLineMappingChanged(eventsCollector);
|
||||
this._decorations.onLineMappingChanged();
|
||||
}
|
||||
} finally {
|
||||
this._endEmitViewEvents();
|
||||
this._eventDispatcher.endEmitViewEvents();
|
||||
}
|
||||
|
||||
// Update the configuration and reset the centered view line
|
||||
this.viewportStartLine = -1;
|
||||
this.configuration.setMaxLineNumber(this.model.getLineCount());
|
||||
this._viewportStartLine = -1;
|
||||
this._configuration.setMaxLineNumber(this.model.getLineCount());
|
||||
this._updateConfigurationViewLineCountNow();
|
||||
|
||||
// Recover viewport
|
||||
if (!this.hasFocus && this.model.getAttachedEditorCount() >= 2 && this.viewportStartLineTrackedRange) {
|
||||
const modelRange = this.model._getTrackedRange(this.viewportStartLineTrackedRange);
|
||||
if (!this._hasFocus && this.model.getAttachedEditorCount() >= 2 && this._viewportStartLineTrackedRange) {
|
||||
const modelRange = this.model._getTrackedRange(this._viewportStartLineTrackedRange);
|
||||
if (modelRange) {
|
||||
const viewPosition = this.coordinatesConverter.convertModelPositionToViewPosition(modelRange.getStartPosition());
|
||||
const viewPositionTop = this.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber);
|
||||
this.viewLayout.setScrollPosition({ scrollTop: viewPositionTop + this.viewportStartLineDelta }, ScrollType.Immediate);
|
||||
this.viewLayout.setScrollPosition({ scrollTop: viewPositionTop + this._viewportStartLineDelta }, ScrollType.Immediate);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const eventsCollector = this._beginEmitViewEvents();
|
||||
this.cursor.onModelContentChanged(eventsCollector, e);
|
||||
const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
|
||||
this._cursor.onModelContentChanged(eventsCollector, e);
|
||||
} finally {
|
||||
this._endEmitViewEvents();
|
||||
this._eventDispatcher.endEmitViewEvents();
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -345,7 +371,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
toLineNumber: viewEndLineNumber
|
||||
};
|
||||
}
|
||||
this._emitSingleViewEvent(new viewEvents.ViewTokensChangedEvent(viewRanges));
|
||||
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewTokensChangedEvent(viewRanges));
|
||||
|
||||
if (e.tokenizationSupportChanged) {
|
||||
this._tokenizeViewportSoon.schedule();
|
||||
@@ -353,65 +379,65 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
}));
|
||||
|
||||
this._register(this.model.onDidChangeLanguageConfiguration((e) => {
|
||||
this._emitSingleViewEvent(new viewEvents.ViewLanguageConfigurationEvent());
|
||||
this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this.configuration);
|
||||
this.cursor.updateConfiguration(this.cursorConfig);
|
||||
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewLanguageConfigurationEvent());
|
||||
this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this._configuration);
|
||||
this._cursor.updateConfiguration(this.cursorConfig);
|
||||
}));
|
||||
|
||||
this._register(this.model.onDidChangeLanguage((e) => {
|
||||
this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this.configuration);
|
||||
this.cursor.updateConfiguration(this.cursorConfig);
|
||||
this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this._configuration);
|
||||
this._cursor.updateConfiguration(this.cursorConfig);
|
||||
}));
|
||||
|
||||
this._register(this.model.onDidChangeOptions((e) => {
|
||||
// A tab size change causes a line mapping changed event => all view parts will repaint OK, no further event needed here
|
||||
if (this.lines.setTabSize(this.model.getOptions().tabSize)) {
|
||||
if (this._lines.setTabSize(this.model.getOptions().tabSize)) {
|
||||
try {
|
||||
const eventsCollector = this._beginEmitViewEvents();
|
||||
eventsCollector.emit(new viewEvents.ViewFlushedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
this.cursor.onLineMappingChanged(eventsCollector);
|
||||
this.decorations.onLineMappingChanged();
|
||||
const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
|
||||
eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent());
|
||||
eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent());
|
||||
eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
this._cursor.onLineMappingChanged(eventsCollector);
|
||||
this._decorations.onLineMappingChanged();
|
||||
this.viewLayout.onFlushed(this.getLineCount());
|
||||
} finally {
|
||||
this._endEmitViewEvents();
|
||||
this._eventDispatcher.endEmitViewEvents();
|
||||
}
|
||||
this._updateConfigurationViewLineCount.schedule();
|
||||
}
|
||||
|
||||
this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this.configuration);
|
||||
this.cursor.updateConfiguration(this.cursorConfig);
|
||||
this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this._configuration);
|
||||
this._cursor.updateConfiguration(this.cursorConfig);
|
||||
}));
|
||||
|
||||
this._register(this.model.onDidChangeDecorations((e) => {
|
||||
this.decorations.onModelDecorationsChanged();
|
||||
this._emitSingleViewEvent(new viewEvents.ViewDecorationsChangedEvent(e));
|
||||
this._decorations.onModelDecorationsChanged();
|
||||
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewDecorationsChangedEvent(e));
|
||||
}));
|
||||
}
|
||||
|
||||
public setHiddenAreas(ranges: Range[]): void {
|
||||
try {
|
||||
const eventsCollector = this._beginEmitViewEvents();
|
||||
let lineMappingChanged = this.lines.setHiddenAreas(ranges);
|
||||
const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
|
||||
let lineMappingChanged = this._lines.setHiddenAreas(ranges);
|
||||
if (lineMappingChanged) {
|
||||
eventsCollector.emit(new viewEvents.ViewFlushedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
this.cursor.onLineMappingChanged(eventsCollector);
|
||||
this.decorations.onLineMappingChanged();
|
||||
eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent());
|
||||
eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent());
|
||||
eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
this._cursor.onLineMappingChanged(eventsCollector);
|
||||
this._decorations.onLineMappingChanged();
|
||||
this.viewLayout.onFlushed(this.getLineCount());
|
||||
this.viewLayout.onHeightMaybeChanged();
|
||||
}
|
||||
} finally {
|
||||
this._endEmitViewEvents();
|
||||
this._eventDispatcher.endEmitViewEvents();
|
||||
}
|
||||
this._updateConfigurationViewLineCount.schedule();
|
||||
}
|
||||
|
||||
public getVisibleRangesPlusViewportAboveBelow(): Range[] {
|
||||
const layoutInfo = this.configuration.options.get(EditorOption.layoutInfo);
|
||||
const lineHeight = this.configuration.options.get(EditorOption.lineHeight);
|
||||
const layoutInfo = this._configuration.options.get(EditorOption.layoutInfo);
|
||||
const lineHeight = this._configuration.options.get(EditorOption.lineHeight);
|
||||
const linesAround = Math.max(20, Math.round(layoutInfo.height / lineHeight));
|
||||
const partialData = this.viewLayout.getLinesViewportData();
|
||||
const startViewLineNumber = Math.max(1, partialData.completelyVisibleStartLineNumber - linesAround);
|
||||
@@ -430,7 +456,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
|
||||
private _toModelVisibleRanges(visibleViewRange: Range): Range[] {
|
||||
const visibleRange = this.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange);
|
||||
const hiddenAreas = this.lines.getHiddenAreas();
|
||||
const hiddenAreas = this._lines.getHiddenAreas();
|
||||
|
||||
if (hiddenAreas.length === 0) {
|
||||
return [visibleRange];
|
||||
@@ -540,43 +566,43 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
}
|
||||
|
||||
public getLineCount(): number {
|
||||
return this.lines.getViewLineCount();
|
||||
return this._lines.getViewLineCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives a hint that a lot of requests are about to come in for these line numbers.
|
||||
*/
|
||||
public setViewport(startLineNumber: number, endLineNumber: number, centeredLineNumber: number): void {
|
||||
this.viewportStartLine = startLineNumber;
|
||||
this._viewportStartLine = startLineNumber;
|
||||
let position = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(startLineNumber, this.getLineMinColumn(startLineNumber)));
|
||||
this.viewportStartLineTrackedRange = this.model._setTrackedRange(this.viewportStartLineTrackedRange, new Range(position.lineNumber, position.column, position.lineNumber, position.column), TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
|
||||
this._viewportStartLineTrackedRange = this.model._setTrackedRange(this._viewportStartLineTrackedRange, new Range(position.lineNumber, position.column, position.lineNumber, position.column), TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
|
||||
const viewportStartLineTop = this.viewLayout.getVerticalOffsetForLineNumber(startLineNumber);
|
||||
const scrollTop = this.viewLayout.getCurrentScrollTop();
|
||||
this.viewportStartLineDelta = scrollTop - viewportStartLineTop;
|
||||
this._viewportStartLineDelta = scrollTop - viewportStartLineTop;
|
||||
}
|
||||
|
||||
public getActiveIndentGuide(lineNumber: number, minLineNumber: number, maxLineNumber: number): IActiveIndentGuideInfo {
|
||||
return this.lines.getActiveIndentGuide(lineNumber, minLineNumber, maxLineNumber);
|
||||
return this._lines.getActiveIndentGuide(lineNumber, minLineNumber, maxLineNumber);
|
||||
}
|
||||
|
||||
public getLinesIndentGuides(startLineNumber: number, endLineNumber: number): number[] {
|
||||
return this.lines.getViewLinesIndentGuides(startLineNumber, endLineNumber);
|
||||
return this._lines.getViewLinesIndentGuides(startLineNumber, endLineNumber);
|
||||
}
|
||||
|
||||
public getLineContent(lineNumber: number): string {
|
||||
return this.lines.getViewLineContent(lineNumber);
|
||||
return this._lines.getViewLineContent(lineNumber);
|
||||
}
|
||||
|
||||
public getLineLength(lineNumber: number): number {
|
||||
return this.lines.getViewLineLength(lineNumber);
|
||||
return this._lines.getViewLineLength(lineNumber);
|
||||
}
|
||||
|
||||
public getLineMinColumn(lineNumber: number): number {
|
||||
return this.lines.getViewLineMinColumn(lineNumber);
|
||||
return this._lines.getViewLineMinColumn(lineNumber);
|
||||
}
|
||||
|
||||
public getLineMaxColumn(lineNumber: number): number {
|
||||
return this.lines.getViewLineMaxColumn(lineNumber);
|
||||
return this._lines.getViewLineMaxColumn(lineNumber);
|
||||
}
|
||||
|
||||
public getLineFirstNonWhitespaceColumn(lineNumber: number): number {
|
||||
@@ -596,15 +622,15 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
}
|
||||
|
||||
public getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[] {
|
||||
return this.decorations.getDecorationsViewportData(visibleRange).decorations;
|
||||
return this._decorations.getDecorationsViewportData(visibleRange).decorations;
|
||||
}
|
||||
|
||||
public getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData {
|
||||
let mightContainRTL = this.model.mightContainRTL();
|
||||
let mightContainNonBasicASCII = this.model.mightContainNonBasicASCII();
|
||||
let tabSize = this.getTabSize();
|
||||
let lineData = this.lines.getViewLineData(lineNumber);
|
||||
let allInlineDecorations = this.decorations.getDecorationsViewportData(visibleRange).inlineDecorations;
|
||||
let lineData = this._lines.getViewLineData(lineNumber);
|
||||
let allInlineDecorations = this._decorations.getDecorationsViewportData(visibleRange).inlineDecorations;
|
||||
let inlineDecorations = allInlineDecorations[lineNumber - visibleRange.startLineNumber];
|
||||
|
||||
return new ViewLineRenderingData(
|
||||
@@ -622,11 +648,11 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
}
|
||||
|
||||
public getViewLineData(lineNumber: number): ViewLineData {
|
||||
return this.lines.getViewLineData(lineNumber);
|
||||
return this._lines.getViewLineData(lineNumber);
|
||||
}
|
||||
|
||||
public getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): MinimapLinesRenderingData {
|
||||
let result = this.lines.getViewLinesData(startLineNumber, endLineNumber, needed);
|
||||
let result = this._lines.getViewLinesData(startLineNumber, endLineNumber, needed);
|
||||
return new MinimapLinesRenderingData(
|
||||
this.getTabSize(),
|
||||
result
|
||||
@@ -634,7 +660,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
}
|
||||
|
||||
public getAllOverviewRulerDecorations(theme: EditorTheme): IOverviewRulerDecorations {
|
||||
return this.lines.getAllOverviewRulerDecorations(this.editorId, filterValidationDecorations(this.configuration.options), theme);
|
||||
return this._lines.getAllOverviewRulerDecorations(this._editorId, filterValidationDecorations(this._configuration.options), theme);
|
||||
}
|
||||
|
||||
public invalidateOverviewRulerColorCache(): void {
|
||||
@@ -776,7 +802,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
range = new Range(lineNumber, this.model.getLineMinColumn(lineNumber), lineNumber, this.model.getLineMaxColumn(lineNumber));
|
||||
}
|
||||
|
||||
const fontInfo = this.configuration.options.get(EditorOption.fontInfo);
|
||||
const fontInfo = this._configuration.options.get(EditorOption.fontInfo);
|
||||
const colorMap = this._getColorMap();
|
||||
const fontFamily = fontInfo.fontFamily === EDITOR_FONT_DEFAULTS.fontFamily ? fontInfo.fontFamily : `'${fontInfo.fontFamily}', ${EDITOR_FONT_DEFAULTS.fontFamily}`;
|
||||
|
||||
@@ -846,90 +872,100 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
//#region cursor operations
|
||||
|
||||
public getPrimaryCursorState(): CursorState {
|
||||
return this.cursor.getPrimaryCursorState();
|
||||
return this._cursor.getPrimaryCursorState();
|
||||
}
|
||||
public getLastAddedCursorIndex(): number {
|
||||
return this.cursor.getLastAddedCursorIndex();
|
||||
return this._cursor.getLastAddedCursorIndex();
|
||||
}
|
||||
public getCursorStates(): CursorState[] {
|
||||
return this.cursor.getCursorStates();
|
||||
return this._cursor.getCursorStates();
|
||||
}
|
||||
public setCursorStates(source: string | null | undefined, reason: CursorChangeReason, states: PartialCursorState[] | null): void {
|
||||
this._withViewEventsCollector(eventsCollector => this.cursor.setStates(eventsCollector, source, reason, states));
|
||||
this._withViewEventsCollector(eventsCollector => this._cursor.setStates(eventsCollector, source, reason, states));
|
||||
}
|
||||
public getCursorColumnSelectData(): IColumnSelectData {
|
||||
return this.cursor.getCursorColumnSelectData();
|
||||
return this._cursor.getCursorColumnSelectData();
|
||||
}
|
||||
public setCursorColumnSelectData(columnSelectData: IColumnSelectData): void {
|
||||
this.cursor.setCursorColumnSelectData(columnSelectData);
|
||||
this._cursor.setCursorColumnSelectData(columnSelectData);
|
||||
}
|
||||
public getPrevEditOperationType(): EditOperationType {
|
||||
return this.cursor.getPrevEditOperationType();
|
||||
return this._cursor.getPrevEditOperationType();
|
||||
}
|
||||
public setPrevEditOperationType(type: EditOperationType): void {
|
||||
this.cursor.setPrevEditOperationType(type);
|
||||
this._cursor.setPrevEditOperationType(type);
|
||||
}
|
||||
public getSelection(): Selection {
|
||||
return this.cursor.getSelection();
|
||||
return this._cursor.getSelection();
|
||||
}
|
||||
public getSelections(): Selection[] {
|
||||
return this.cursor.getSelections();
|
||||
return this._cursor.getSelections();
|
||||
}
|
||||
public getPosition(): Position {
|
||||
return this.cursor.getPrimaryCursorState().modelState.position;
|
||||
return this._cursor.getPrimaryCursorState().modelState.position;
|
||||
}
|
||||
public setSelections(source: string | null | undefined, selections: readonly ISelection[]): void {
|
||||
this._withViewEventsCollector(eventsCollector => this.cursor.setSelections(eventsCollector, source, selections));
|
||||
this._withViewEventsCollector(eventsCollector => this._cursor.setSelections(eventsCollector, source, selections));
|
||||
}
|
||||
public saveCursorState(): ICursorState[] {
|
||||
return this.cursor.saveState();
|
||||
return this._cursor.saveState();
|
||||
}
|
||||
public restoreCursorState(states: ICursorState[]): void {
|
||||
this._withViewEventsCollector(eventsCollector => this.cursor.restoreState(eventsCollector, states));
|
||||
this._withViewEventsCollector(eventsCollector => this._cursor.restoreState(eventsCollector, states));
|
||||
}
|
||||
|
||||
private _executeCursorEdit(callback: (eventsCollector: ViewModelEventsCollector) => void): void {
|
||||
if (this._cursor.context.cursorConfig.readOnly) {
|
||||
// we cannot edit when read only...
|
||||
this._eventDispatcher.emitOutgoingEvent(new ReadOnlyEditAttemptEvent());
|
||||
return;
|
||||
}
|
||||
this._withViewEventsCollector(callback);
|
||||
}
|
||||
public executeEdits(source: string | null | undefined, edits: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): void {
|
||||
this._withViewEventsCollector(eventsCollector => this.cursor.executeEdits(eventsCollector, source, edits, cursorStateComputer));
|
||||
this._executeCursorEdit(eventsCollector => this._cursor.executeEdits(eventsCollector, source, edits, cursorStateComputer));
|
||||
}
|
||||
public startComposition(): void {
|
||||
this._withViewEventsCollector(eventsCollector => this.cursor.startComposition(eventsCollector));
|
||||
this._cursor.setIsDoingComposition(true);
|
||||
this._executeCursorEdit(eventsCollector => this._cursor.startComposition(eventsCollector));
|
||||
}
|
||||
public endComposition(source?: string | null | undefined): void {
|
||||
this._withViewEventsCollector(eventsCollector => this.cursor.endComposition(eventsCollector, source));
|
||||
this._cursor.setIsDoingComposition(false);
|
||||
this._executeCursorEdit(eventsCollector => this._cursor.endComposition(eventsCollector, source));
|
||||
}
|
||||
public type(text: string, source?: string | null | undefined): void {
|
||||
this._withViewEventsCollector(eventsCollector => this.cursor.type(eventsCollector, text, source));
|
||||
this._executeCursorEdit(eventsCollector => this._cursor.type(eventsCollector, text, source));
|
||||
}
|
||||
public replacePreviousChar(text: string, replaceCharCnt: number, source?: string | null | undefined): void {
|
||||
this._withViewEventsCollector(eventsCollector => this.cursor.replacePreviousChar(eventsCollector, text, replaceCharCnt, source));
|
||||
this._executeCursorEdit(eventsCollector => this._cursor.replacePreviousChar(eventsCollector, text, replaceCharCnt, source));
|
||||
}
|
||||
public paste(text: string, pasteOnNewLine: boolean, multicursorText?: string[] | null | undefined, source?: string | null | undefined): void {
|
||||
this._withViewEventsCollector(eventsCollector => this.cursor.paste(eventsCollector, text, pasteOnNewLine, multicursorText, source));
|
||||
this._executeCursorEdit(eventsCollector => this._cursor.paste(eventsCollector, text, pasteOnNewLine, multicursorText, source));
|
||||
}
|
||||
public cut(source?: string | null | undefined): void {
|
||||
this._withViewEventsCollector(eventsCollector => this.cursor.cut(eventsCollector, source));
|
||||
this._executeCursorEdit(eventsCollector => this._cursor.cut(eventsCollector, source));
|
||||
}
|
||||
public executeCommand(command: ICommand, source?: string | null | undefined): void {
|
||||
this._withViewEventsCollector(eventsCollector => this.cursor.executeCommand(eventsCollector, command, source));
|
||||
this._executeCursorEdit(eventsCollector => this._cursor.executeCommand(eventsCollector, command, source));
|
||||
}
|
||||
public executeCommands(commands: ICommand[], source?: string | null | undefined): void {
|
||||
this._withViewEventsCollector(eventsCollector => this.cursor.executeCommands(eventsCollector, commands, source));
|
||||
this._executeCursorEdit(eventsCollector => this._cursor.executeCommands(eventsCollector, commands, source));
|
||||
}
|
||||
public revealPrimaryCursor(source: string | null | undefined, revealHorizontal: boolean): void {
|
||||
this._withViewEventsCollector(eventsCollector => this.cursor.revealPrimary(eventsCollector, source, revealHorizontal, ScrollType.Smooth));
|
||||
this._withViewEventsCollector(eventsCollector => this._cursor.revealPrimary(eventsCollector, source, revealHorizontal, ScrollType.Smooth));
|
||||
}
|
||||
public revealTopMostCursor(source: string | null | undefined): void {
|
||||
const viewPosition = this.cursor.getTopMostViewPosition();
|
||||
const viewPosition = this._cursor.getTopMostViewPosition();
|
||||
const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column);
|
||||
this._withViewEventsCollector(eventsCollector => eventsCollector.emit(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, viewEvents.VerticalRevealType.Simple, true, ScrollType.Smooth)));
|
||||
this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, viewEvents.VerticalRevealType.Simple, true, ScrollType.Smooth)));
|
||||
}
|
||||
public revealBottomMostCursor(source: string | null | undefined): void {
|
||||
const viewPosition = this.cursor.getBottomMostViewPosition();
|
||||
const viewPosition = this._cursor.getBottomMostViewPosition();
|
||||
const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column);
|
||||
this._withViewEventsCollector(eventsCollector => eventsCollector.emit(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, viewEvents.VerticalRevealType.Simple, true, ScrollType.Smooth)));
|
||||
this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, viewEvents.VerticalRevealType.Simple, true, ScrollType.Smooth)));
|
||||
}
|
||||
public revealRange(source: string | null | undefined, revealHorizontal: boolean, viewRange: Range, verticalType: viewEvents.VerticalRevealType, scrollType: ScrollType): void {
|
||||
this._withViewEventsCollector(eventsCollector => eventsCollector.emit(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, verticalType, revealHorizontal, scrollType)));
|
||||
this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, verticalType, revealHorizontal, scrollType)));
|
||||
}
|
||||
|
||||
//#endregion
|
||||
@@ -951,19 +987,23 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
this.viewLayout.deltaScrollNow(deltaScrollLeft, deltaScrollTop);
|
||||
}
|
||||
public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): void {
|
||||
return this.viewLayout.changeWhitespace(callback);
|
||||
const hadAChange = this.viewLayout.changeWhitespace(callback);
|
||||
if (hadAChange) {
|
||||
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewZonesChangedEvent());
|
||||
this._eventDispatcher.emitOutgoingEvent(new ViewZonesChangedEvent());
|
||||
}
|
||||
}
|
||||
public setMaxLineWidth(maxLineWidth: number): void {
|
||||
this.viewLayout.setMaxLineWidth(maxLineWidth);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
private _withViewEventsCollector(callback: (eventsCollector: viewEvents.ViewEventsCollector) => void): void {
|
||||
private _withViewEventsCollector(callback: (eventsCollector: ViewModelEventsCollector) => void): void {
|
||||
try {
|
||||
const eventsCollector = this._beginEmitViewEvents();
|
||||
const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
|
||||
callback(eventsCollector);
|
||||
} finally {
|
||||
this._endEmitViewEvents();
|
||||
this._eventDispatcher.endEmitViewEvents();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import * as assert from 'assert';
|
||||
import { CoreEditingCommands, CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { CursorStateChangedEvent } from 'vs/editor/common/controller/cursor';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
@@ -24,6 +23,7 @@ import { IRelaxedTextModelCreationOptions, createTextModel } from 'vs/editor/tes
|
||||
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
|
||||
import { javascriptOnEnterRules } from 'vs/editor/test/common/modes/supports/javascriptOnEnterRules';
|
||||
import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl';
|
||||
import { OutgoingViewModelEventKind } from 'vs/editor/common/viewModel/viewModelEventDispatcher';
|
||||
|
||||
// --------- utils
|
||||
|
||||
@@ -782,7 +782,7 @@ suite('Editor Controller - Cursor', () => {
|
||||
|
||||
test('no move doesn\'t trigger event', () => {
|
||||
runTest((editor, viewModel) => {
|
||||
viewModel.cursor.onDidChange((e) => {
|
||||
viewModel.onEvent((e) => {
|
||||
assert.ok(false, 'was not expecting event');
|
||||
});
|
||||
moveTo(editor, viewModel, 1, 1);
|
||||
@@ -792,9 +792,11 @@ suite('Editor Controller - Cursor', () => {
|
||||
test('move eventing', () => {
|
||||
runTest((editor, viewModel) => {
|
||||
let events = 0;
|
||||
viewModel.cursor.onDidChange((e: CursorStateChangedEvent) => {
|
||||
events++;
|
||||
assert.deepEqual(e.selections, [new Selection(1, 2, 1, 2)]);
|
||||
viewModel.onEvent((e) => {
|
||||
if (e.kind === OutgoingViewModelEventKind.CursorStateChanged) {
|
||||
events++;
|
||||
assert.deepEqual(e.selections, [new Selection(1, 2, 1, 2)]);
|
||||
}
|
||||
});
|
||||
moveTo(editor, viewModel, 1, 2);
|
||||
assert.equal(events, 1, 'receives 1 event');
|
||||
@@ -804,9 +806,11 @@ suite('Editor Controller - Cursor', () => {
|
||||
test('move in selection mode eventing', () => {
|
||||
runTest((editor, viewModel) => {
|
||||
let events = 0;
|
||||
viewModel.cursor.onDidChange((e: CursorStateChangedEvent) => {
|
||||
events++;
|
||||
assert.deepEqual(e.selections, [new Selection(1, 1, 1, 2)]);
|
||||
viewModel.onEvent((e) => {
|
||||
if (e.kind === OutgoingViewModelEventKind.CursorStateChanged) {
|
||||
events++;
|
||||
assert.deepEqual(e.selections, [new Selection(1, 1, 1, 2)]);
|
||||
}
|
||||
});
|
||||
moveTo(editor, viewModel, 1, 2, true);
|
||||
assert.equal(events, 1, 'receives 1 event');
|
||||
|
||||
@@ -7,6 +7,8 @@ import * as assert from 'assert';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { EndOfLineSequence } from 'vs/editor/common/model';
|
||||
import { testViewModel } from 'vs/editor/test/common/viewModel/testViewModel';
|
||||
import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
|
||||
import { ViewEvent } from 'vs/editor/common/view/viewEvents';
|
||||
|
||||
suite('ViewModel', () => {
|
||||
|
||||
@@ -63,9 +65,11 @@ suite('ViewModel', () => {
|
||||
let viewLineCount: number[] = [];
|
||||
|
||||
viewLineCount.push(viewModel.getLineCount());
|
||||
viewModel.addViewEventListener((events) => {
|
||||
// Access the view model
|
||||
viewLineCount.push(viewModel.getLineCount());
|
||||
viewModel.addViewEventHandler(new class extends ViewEventHandler {
|
||||
handleEvents(events: ViewEvent[]): void {
|
||||
// Access the view model
|
||||
viewLineCount.push(viewModel.getLineCount());
|
||||
}
|
||||
});
|
||||
model.undo();
|
||||
viewLineCount.push(viewModel.getLineCount());
|
||||
|
||||
@@ -41,15 +41,16 @@ export interface ISyncData {
|
||||
|
||||
function isSyncData(thing: any): thing is ISyncData {
|
||||
if (thing
|
||||
&& (thing.version && typeof thing.version === 'number')
|
||||
&& (thing.content && typeof thing.content === 'string')) {
|
||||
&& (thing.version !== undefined && typeof thing.version === 'number')
|
||||
&& (thing.content !== undefined && typeof thing.content === 'string')) {
|
||||
|
||||
// backward compatibility
|
||||
if (Object.keys(thing).length === 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Object.keys(thing).length === 3
|
||||
&& (thing.machineId && typeof thing.machineId === 'string')) {
|
||||
&& (thing.machineId !== undefined && typeof thing.machineId === 'string')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -88,7 +89,7 @@ export abstract class AbstractSynchroniser extends Disposable {
|
||||
@IUserDataSyncStoreService protected readonly userDataSyncStoreService: IUserDataSyncStoreService,
|
||||
@IUserDataSyncBackupStoreService protected readonly userDataSyncBackupStoreService: IUserDataSyncBackupStoreService,
|
||||
@IUserDataSyncEnablementService protected readonly userDataSyncEnablementService: IUserDataSyncEnablementService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@ITelemetryService protected readonly telemetryService: ITelemetryService,
|
||||
@IUserDataSyncLogService protected readonly logService: IUserDataSyncLogService,
|
||||
@IConfigurationService protected readonly configurationService: IConfigurationService,
|
||||
) {
|
||||
@@ -324,14 +325,13 @@ export abstract class AbstractSynchroniser extends Disposable {
|
||||
if (userData.content === null) {
|
||||
return { ref: parsed.ref, syncData: null } as T;
|
||||
}
|
||||
let syncData: ISyncData = JSON.parse(userData.content);
|
||||
const syncData: ISyncData = JSON.parse(userData.content);
|
||||
|
||||
// Migration from old content to sync data
|
||||
if (!isSyncData(syncData)) {
|
||||
syncData = { version: this.version, content: userData.content };
|
||||
/* Check if syncData is of expected type. Return only if matches */
|
||||
if (isSyncData(syncData)) {
|
||||
return { ...parsed, ...{ syncData, content: undefined } };
|
||||
}
|
||||
|
||||
return { ...parsed, ...{ syncData, content: undefined } };
|
||||
} catch (error) {
|
||||
if (!(error instanceof FileOperationError && error.fileOperationResult === FileOperationResult.FILE_NOT_FOUND)) {
|
||||
// log error always except when file does not exist
|
||||
@@ -355,20 +355,16 @@ export abstract class AbstractSynchroniser extends Disposable {
|
||||
return { ref, syncData };
|
||||
}
|
||||
|
||||
protected parseSyncData(content: string): ISyncData | null {
|
||||
let syncData: ISyncData | null = null;
|
||||
protected parseSyncData(content: string): ISyncData {
|
||||
try {
|
||||
syncData = <ISyncData>JSON.parse(content);
|
||||
|
||||
// Migration from old content to sync data
|
||||
if (!isSyncData(syncData)) {
|
||||
syncData = { version: this.version, content };
|
||||
const syncData: ISyncData = JSON.parse(content);
|
||||
if (isSyncData(syncData)) {
|
||||
return syncData;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
} catch (error) {
|
||||
this.logService.error(error);
|
||||
}
|
||||
return syncData;
|
||||
throw new UserDataSyncError(localize('incompatible sync data', "Cannot parse sync data as it is not compatible with current version."), UserDataSyncErrorCode.Incompatible, this.resource);
|
||||
}
|
||||
|
||||
private async getUserData(refOrLastSyncData: string | IRemoteUserData | null): Promise<IUserData> {
|
||||
@@ -382,8 +378,8 @@ export abstract class AbstractSynchroniser extends Disposable {
|
||||
}
|
||||
|
||||
protected async updateRemoteUserData(content: string, ref: string | null): Promise<IRemoteUserData> {
|
||||
const machineId = await this.currentMachineIdPromise;
|
||||
const syncData: ISyncData = { version: this.version, machineId, content };
|
||||
await this.currentMachineIdPromise;
|
||||
const syncData: ISyncData = { version: this.version, content };
|
||||
ref = await this.userDataSyncStoreService.write(this.resource, JSON.stringify(syncData), ref);
|
||||
return { ref, syncData };
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { values, keys } from 'vs/base/common/map';
|
||||
import { ISyncExtension } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
|
||||
export interface IMergeResult {
|
||||
added: ISyncExtension[];
|
||||
@@ -30,8 +31,6 @@ export function merge(localExtensions: ISyncExtension[], remoteExtensions: ISync
|
||||
};
|
||||
}
|
||||
|
||||
// massage incoming extension - add disabled property
|
||||
const massageIncomingExtension = (extension: ISyncExtension): ISyncExtension => ({ ...extension, ...{ disabled: !!extension.disabled } });
|
||||
localExtensions = localExtensions.map(massageIncomingExtension);
|
||||
remoteExtensions = remoteExtensions.map(massageIncomingExtension);
|
||||
lastSyncExtensions = lastSyncExtensions ? lastSyncExtensions.map(massageIncomingExtension) : null;
|
||||
@@ -54,7 +53,14 @@ export function merge(localExtensions: ISyncExtension[], remoteExtensions: ISync
|
||||
};
|
||||
const localExtensionsMap = localExtensions.reduce(addExtensionToMap, new Map<string, ISyncExtension>());
|
||||
const remoteExtensionsMap = remoteExtensions.reduce(addExtensionToMap, new Map<string, ISyncExtension>());
|
||||
const newRemoteExtensionsMap = remoteExtensions.reduce(addExtensionToMap, new Map<string, ISyncExtension>());
|
||||
const newRemoteExtensionsMap = remoteExtensions.reduce((map: Map<string, ISyncExtension>, extension: ISyncExtension) => {
|
||||
const key = getKey(extension);
|
||||
extension = deepClone(extension);
|
||||
if (localExtensionsMap.get(key)?.installed) {
|
||||
extension.installed = true;
|
||||
}
|
||||
return addExtensionToMap(map, extension);
|
||||
}, new Map<string, ISyncExtension>());
|
||||
const lastSyncExtensionsMap = lastSyncExtensions ? lastSyncExtensions.reduce(addExtensionToMap, new Map<string, ISyncExtension>()) : null;
|
||||
const skippedExtensionsMap = skippedExtensions.reduce(addExtensionToMap, new Map<string, ISyncExtension>());
|
||||
const ignoredExtensionsSet = ignoredExtensions.reduce((set, id) => {
|
||||
@@ -63,90 +69,82 @@ export function merge(localExtensions: ISyncExtension[], remoteExtensions: ISync
|
||||
}, new Set<string>());
|
||||
|
||||
const localToRemote = compare(localExtensionsMap, remoteExtensionsMap, ignoredExtensionsSet);
|
||||
if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) {
|
||||
// No changes found between local and remote.
|
||||
return { added: [], removed: [], updated: [], remote: null };
|
||||
}
|
||||
if (localToRemote.added.size > 0 || localToRemote.removed.size > 0 || localToRemote.updated.size > 0) {
|
||||
|
||||
const baseToLocal = compare(lastSyncExtensionsMap, localExtensionsMap, ignoredExtensionsSet);
|
||||
const baseToRemote = compare(lastSyncExtensionsMap, remoteExtensionsMap, ignoredExtensionsSet);
|
||||
const baseToLocal = compare(lastSyncExtensionsMap, localExtensionsMap, ignoredExtensionsSet);
|
||||
const baseToRemote = compare(lastSyncExtensionsMap, remoteExtensionsMap, ignoredExtensionsSet);
|
||||
|
||||
// massage outgoing extension - remove disabled property
|
||||
const massageOutgoingExtension = (extension: ISyncExtension, key: string): ISyncExtension => {
|
||||
const massagedExtension: ISyncExtension = {
|
||||
identifier: {
|
||||
id: extension.identifier.id,
|
||||
uuid: startsWith(key, 'uuid:') ? key.substring('uuid:'.length) : undefined
|
||||
},
|
||||
};
|
||||
if (extension.disabled) {
|
||||
massagedExtension.disabled = true;
|
||||
}
|
||||
if (extension.version) {
|
||||
massagedExtension.version = extension.version;
|
||||
}
|
||||
return massagedExtension;
|
||||
};
|
||||
|
||||
// Remotely removed extension.
|
||||
for (const key of values(baseToRemote.removed)) {
|
||||
const e = localExtensionsMap.get(key);
|
||||
if (e) {
|
||||
removed.push(e.identifier);
|
||||
}
|
||||
}
|
||||
|
||||
// Remotely added extension
|
||||
for (const key of values(baseToRemote.added)) {
|
||||
// Got added in local
|
||||
if (baseToLocal.added.has(key)) {
|
||||
// Is different from local to remote
|
||||
if (localToRemote.updated.has(key)) {
|
||||
updated.push(massageOutgoingExtension(remoteExtensionsMap.get(key)!, key));
|
||||
// Remotely removed extension.
|
||||
for (const key of values(baseToRemote.removed)) {
|
||||
const e = localExtensionsMap.get(key);
|
||||
if (e) {
|
||||
removed.push(e.identifier);
|
||||
}
|
||||
} else {
|
||||
// Add to local
|
||||
added.push(massageOutgoingExtension(remoteExtensionsMap.get(key)!, key));
|
||||
}
|
||||
}
|
||||
|
||||
// Remotely updated extensions
|
||||
for (const key of values(baseToRemote.updated)) {
|
||||
// Update in local always
|
||||
updated.push(massageOutgoingExtension(remoteExtensionsMap.get(key)!, key));
|
||||
}
|
||||
|
||||
// Locally added extensions
|
||||
for (const key of values(baseToLocal.added)) {
|
||||
// Not there in remote
|
||||
if (!baseToRemote.added.has(key)) {
|
||||
newRemoteExtensionsMap.set(key, localExtensionsMap.get(key)!);
|
||||
}
|
||||
}
|
||||
|
||||
// Locally updated extensions
|
||||
for (const key of values(baseToLocal.updated)) {
|
||||
// If removed in remote
|
||||
if (baseToRemote.removed.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If not updated in remote
|
||||
if (!baseToRemote.updated.has(key)) {
|
||||
newRemoteExtensionsMap.set(key, localExtensionsMap.get(key)!);
|
||||
// Remotely added extension
|
||||
for (const key of values(baseToRemote.added)) {
|
||||
// Got added in local
|
||||
if (baseToLocal.added.has(key)) {
|
||||
// Is different from local to remote
|
||||
if (localToRemote.updated.has(key)) {
|
||||
updated.push(massageOutgoingExtension(remoteExtensionsMap.get(key)!, key));
|
||||
}
|
||||
} else {
|
||||
// Add only installed extension to local
|
||||
const remoteExtension = remoteExtensionsMap.get(key)!;
|
||||
if (remoteExtension.installed) {
|
||||
added.push(massageOutgoingExtension(remoteExtension, key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Locally removed extensions
|
||||
for (const key of values(baseToLocal.removed)) {
|
||||
// If not skipped and not updated in remote
|
||||
if (!skippedExtensionsMap.has(key) && !baseToRemote.updated.has(key)) {
|
||||
newRemoteExtensionsMap.delete(key);
|
||||
// Remotely updated extensions
|
||||
for (const key of values(baseToRemote.updated)) {
|
||||
// Update in local always
|
||||
updated.push(massageOutgoingExtension(remoteExtensionsMap.get(key)!, key));
|
||||
}
|
||||
|
||||
// Locally added extensions
|
||||
for (const key of values(baseToLocal.added)) {
|
||||
// Not there in remote
|
||||
if (!baseToRemote.added.has(key)) {
|
||||
newRemoteExtensionsMap.set(key, localExtensionsMap.get(key)!);
|
||||
}
|
||||
}
|
||||
|
||||
// Locally updated extensions
|
||||
for (const key of values(baseToLocal.updated)) {
|
||||
// If removed in remote
|
||||
if (baseToRemote.removed.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If not updated in remote
|
||||
if (!baseToRemote.updated.has(key)) {
|
||||
const extension = deepClone(localExtensionsMap.get(key)!);
|
||||
// Retain installed property
|
||||
if (newRemoteExtensionsMap.get(key)?.installed) {
|
||||
extension.installed = true;
|
||||
}
|
||||
newRemoteExtensionsMap.set(key, extension);
|
||||
}
|
||||
}
|
||||
|
||||
// Locally removed extensions
|
||||
for (const key of values(baseToLocal.removed)) {
|
||||
// If not skipped and not updated in remote
|
||||
if (!skippedExtensionsMap.has(key) && !baseToRemote.updated.has(key)) {
|
||||
// Remove only if it is an installed extension
|
||||
if (lastSyncExtensionsMap?.get(key)?.installed) {
|
||||
newRemoteExtensionsMap.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const remote: ISyncExtension[] = [];
|
||||
const remoteChanges = compare(remoteExtensionsMap, newRemoteExtensionsMap, new Set<string>());
|
||||
const remoteChanges = compare(remoteExtensionsMap, newRemoteExtensionsMap, new Set<string>(), { checkInstalledProperty: true });
|
||||
if (remoteChanges.added.size > 0 || remoteChanges.updated.size > 0 || remoteChanges.removed.size > 0) {
|
||||
newRemoteExtensionsMap.forEach((value, key) => remote.push(massageOutgoingExtension(value, key)));
|
||||
}
|
||||
@@ -154,7 +152,7 @@ export function merge(localExtensions: ISyncExtension[], remoteExtensions: ISync
|
||||
return { added, removed, updated, remote: remote.length ? remote : null };
|
||||
}
|
||||
|
||||
function compare(from: Map<string, ISyncExtension> | null, to: Map<string, ISyncExtension>, ignoredExtensions: Set<string>): { added: Set<string>, removed: Set<string>, updated: Set<string> } {
|
||||
function compare(from: Map<string, ISyncExtension> | null, to: Map<string, ISyncExtension>, ignoredExtensions: Set<string>, { checkInstalledProperty }: { checkInstalledProperty: boolean } = { checkInstalledProperty: false }): { added: Set<string>, removed: Set<string>, updated: Set<string> } {
|
||||
const fromKeys = from ? keys(from).filter(key => !ignoredExtensions.has(key)) : [];
|
||||
const toKeys = keys(to).filter(key => !ignoredExtensions.has(key));
|
||||
const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set<string>());
|
||||
@@ -170,6 +168,7 @@ function compare(from: Map<string, ISyncExtension> | null, to: Map<string, ISync
|
||||
if (!toExtension
|
||||
|| fromExtension.disabled !== toExtension.disabled
|
||||
|| fromExtension.version !== toExtension.version
|
||||
|| (checkInstalledProperty && fromExtension.installed !== toExtension.installed)
|
||||
) {
|
||||
updated.add(key);
|
||||
}
|
||||
@@ -177,3 +176,28 @@ function compare(from: Map<string, ISyncExtension> | null, to: Map<string, ISync
|
||||
|
||||
return { added, removed, updated };
|
||||
}
|
||||
|
||||
// massage incoming extension - add optional properties
|
||||
function massageIncomingExtension(extension: ISyncExtension): ISyncExtension {
|
||||
return { ...extension, ...{ disabled: !!extension.disabled, installed: !!extension.installed } };
|
||||
}
|
||||
|
||||
// massage outgoing extension - remove optional properties
|
||||
function massageOutgoingExtension(extension: ISyncExtension, key: string): ISyncExtension {
|
||||
const massagedExtension: ISyncExtension = {
|
||||
identifier: {
|
||||
id: extension.identifier.id,
|
||||
uuid: startsWith(key, 'uuid:') ? key.substring('uuid:'.length) : undefined
|
||||
},
|
||||
};
|
||||
if (extension.disabled) {
|
||||
massagedExtension.disabled = true;
|
||||
}
|
||||
if (extension.installed) {
|
||||
massagedExtension.installed = true;
|
||||
}
|
||||
if (extension.version) {
|
||||
massagedExtension.version = extension.version;
|
||||
}
|
||||
return massagedExtension;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,10 @@ interface ILastSyncUserData extends IRemoteUserData {
|
||||
export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser {
|
||||
|
||||
private static readonly EXTENSIONS_DATA_URI = URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'extensions', path: `/current.json` });
|
||||
protected readonly version: number = 2;
|
||||
/*
|
||||
Version 3 - Introduce installed property to skip installing built in extensions
|
||||
*/
|
||||
protected readonly version: number = 3;
|
||||
protected isEnabled(): boolean { return super.isEnabled() && this.extensionGalleryService.isEnabled(); }
|
||||
|
||||
constructor(
|
||||
@@ -85,7 +88,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
||||
|
||||
if (remoteUserData.syncData !== null) {
|
||||
const localExtensions = await this.getLocalExtensions();
|
||||
const remoteExtensions = this.parseExtensions(remoteUserData.syncData);
|
||||
const remoteExtensions = await this.parseAndMigrateExtensions(remoteUserData.syncData);
|
||||
const { added, updated, remote, removed } = merge(localExtensions, remoteExtensions, localExtensions, [], this.getIgnoredExtensions());
|
||||
await this.apply({
|
||||
added, removed, updated, remote, remoteUserData, localExtensions, skippedExtensions: [], lastSyncUserData,
|
||||
@@ -204,7 +207,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
||||
|
||||
protected async performReplace(syncData: ISyncData, remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise<void> {
|
||||
const localExtensions = await this.getLocalExtensions();
|
||||
const syncExtensions = this.parseExtensions(syncData);
|
||||
const syncExtensions = await this.parseAndMigrateExtensions(syncData);
|
||||
const { added, updated, removed } = merge(localExtensions, syncExtensions, localExtensions, [], this.getIgnoredExtensions());
|
||||
|
||||
await this.apply({
|
||||
@@ -215,8 +218,8 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
||||
}
|
||||
|
||||
protected async generatePreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise<IExtensionsSyncPreviewResult> {
|
||||
const remoteExtensions: ISyncExtension[] | null = remoteUserData.syncData ? this.parseExtensions(remoteUserData.syncData) : null;
|
||||
const lastSyncExtensions: ISyncExtension[] | null = lastSyncUserData ? this.parseExtensions(lastSyncUserData.syncData!) : null;
|
||||
const remoteExtensions: ISyncExtension[] | null = remoteUserData.syncData ? await this.parseAndMigrateExtensions(remoteUserData.syncData) : null;
|
||||
const lastSyncExtensions: ISyncExtension[] | null = lastSyncUserData ? await this.parseAndMigrateExtensions(lastSyncUserData.syncData!) : null;
|
||||
const skippedExtensions: ISyncExtension[] = lastSyncUserData ? lastSyncUserData.skippedExtensions || [] : [];
|
||||
|
||||
const localExtensions = await this.getLocalExtensions();
|
||||
@@ -353,31 +356,50 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
||||
return newSkippedExtensions;
|
||||
}
|
||||
|
||||
private parseExtensions(syncData: ISyncData): ISyncExtension[] {
|
||||
let extensions: ISyncExtension[] = JSON.parse(syncData.content);
|
||||
if (syncData.version !== this.version) {
|
||||
extensions = extensions.map(e => {
|
||||
private async parseAndMigrateExtensions(syncData: ISyncData): Promise<ISyncExtension[]> {
|
||||
const extensions = this.parseExtensions(syncData);
|
||||
if (syncData.version === 1
|
||||
|| syncData.version === 2
|
||||
) {
|
||||
const systemExtensions = await this.extensionManagementService.getInstalled(ExtensionType.System);
|
||||
for (const extension of extensions) {
|
||||
// #region Migration from v1 (enabled -> disabled)
|
||||
if (!(<any>e).enabled) {
|
||||
e.disabled = true;
|
||||
if (syncData.version === 1) {
|
||||
if ((<any>extension).enabled === false) {
|
||||
extension.disabled = true;
|
||||
}
|
||||
delete (<any>extension).enabled;
|
||||
}
|
||||
delete (<any>e).enabled;
|
||||
// #endregion
|
||||
return e;
|
||||
});
|
||||
|
||||
// #region Migration from v2 (set installed property on extension)
|
||||
if (syncData.version === 2) {
|
||||
if (systemExtensions.every(installed => !areSameExtensions(installed.identifier, extension.identifier))) {
|
||||
extension.installed = true;
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
}
|
||||
}
|
||||
return extensions;
|
||||
}
|
||||
|
||||
private parseExtensions(syncData: ISyncData): ISyncExtension[] {
|
||||
return JSON.parse(syncData.content);
|
||||
}
|
||||
|
||||
private async getLocalExtensions(): Promise<ISyncExtension[]> {
|
||||
const installedExtensions = await this.extensionManagementService.getInstalled();
|
||||
const disabledExtensions = this.extensionEnablementService.getDisabledExtensions();
|
||||
return installedExtensions
|
||||
.map(({ identifier }) => {
|
||||
.map(({ identifier, type }) => {
|
||||
const syncExntesion: ISyncExtension = { identifier };
|
||||
if (disabledExtensions.some(disabledExtension => areSameExtensions(disabledExtension, identifier))) {
|
||||
syncExntesion.disabled = true;
|
||||
}
|
||||
if (type === ExtensionType.User) {
|
||||
syncExntesion.installed = true;
|
||||
}
|
||||
return syncExntesion;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { joinPath, isEqual, dirname, basename } from 'vs/base/common/resources';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { Edit } from 'vs/base/common/jsonFormatter';
|
||||
import { setProperty, applyEdits } from 'vs/base/common/jsonEdit';
|
||||
|
||||
export interface ISettingsSyncContent {
|
||||
settings: string;
|
||||
@@ -414,4 +416,49 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser {
|
||||
throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync settings as there are errors/warning in settings file."), UserDataSyncErrorCode.LocalInvalidContent, this.resource);
|
||||
}
|
||||
}
|
||||
|
||||
async recoverSettings(): Promise<void> {
|
||||
try {
|
||||
const fileContent = await this.getLocalFileContent();
|
||||
if (!fileContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const syncData: ISyncData = JSON.parse(fileContent.value.toString());
|
||||
if (!isSyncData(syncData)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.telemetryService.publicLog2('sync/settingsCorrupted');
|
||||
const settingsSyncContent = this.parseSettingsSyncContent(syncData.content);
|
||||
if (!settingsSyncContent || !settingsSyncContent.settings) {
|
||||
return;
|
||||
}
|
||||
|
||||
let settings = settingsSyncContent.settings;
|
||||
const formattingOptions = await this.getFormattingOptions();
|
||||
for (const key in syncData) {
|
||||
if (['version', 'content', 'machineId'].indexOf(key) === -1 && (syncData as any)[key] !== undefined) {
|
||||
const edits: Edit[] = setProperty(settings, [key], (syncData as any)[key], formattingOptions);
|
||||
if (edits.length) {
|
||||
settings = applyEdits(settings, edits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await this.fileService.writeFile(this.file, VSBuffer.fromString(settings));
|
||||
} catch (e) {/* ignore */ }
|
||||
}
|
||||
}
|
||||
|
||||
function isSyncData(thing: any): thing is ISyncData {
|
||||
if (thing
|
||||
&& (thing.version !== undefined && typeof thing.version === 'number')
|
||||
&& (thing.content !== undefined && typeof thing.content === 'string')
|
||||
&& (thing.machineId !== undefined && typeof thing.machineId === 'string')
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -239,6 +239,7 @@ export interface ISyncExtension {
|
||||
identifier: IExtensionIdentifier;
|
||||
version?: string;
|
||||
disabled?: boolean;
|
||||
installed?: boolean;
|
||||
}
|
||||
|
||||
export interface IStorageValue {
|
||||
|
||||
@@ -118,8 +118,15 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
this.updateLastSyncTime();
|
||||
}
|
||||
|
||||
private recoveredSettings: boolean = false;
|
||||
async sync(): Promise<void> {
|
||||
await this.checkEnablement();
|
||||
|
||||
if (!this.recoveredSettings) {
|
||||
await this.settingsSynchroniser.recoverSettings();
|
||||
this.recoveredSettings = true;
|
||||
}
|
||||
|
||||
await this.syncThrottler.queue(() => this.doSync());
|
||||
}
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@ import * as assert from 'assert';
|
||||
import { ISyncExtension } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { merge } from 'vs/platform/userDataSync/common/extensionsMerge';
|
||||
|
||||
suite('ExtensionsMerge - No Conflicts', () => {
|
||||
suite('ExtensionsMerge', () => {
|
||||
|
||||
test('merge returns local extension if remote does not exist', async () => {
|
||||
test('merge returns local extension if remote does not exist', () => {
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, null, null, [], []);
|
||||
@@ -24,15 +24,15 @@ suite('ExtensionsMerge - No Conflicts', () => {
|
||||
assert.deepEqual(actual.remote, localExtensions);
|
||||
});
|
||||
|
||||
test('merge returns local extension if remote does not exist with ignored extensions', async () => {
|
||||
test('merge returns local extension if remote does not exist with ignored extensions', () => {
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
const expected: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, null, null, [], ['a']);
|
||||
@@ -43,15 +43,15 @@ suite('ExtensionsMerge - No Conflicts', () => {
|
||||
assert.deepEqual(actual.remote, expected);
|
||||
});
|
||||
|
||||
test('merge returns local extension if remote does not exist with ignored extensions (ignore case)', async () => {
|
||||
test('merge returns local extension if remote does not exist with ignored extensions (ignore case)', () => {
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
const expected: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, null, null, [], ['A']);
|
||||
@@ -62,19 +62,19 @@ suite('ExtensionsMerge - No Conflicts', () => {
|
||||
assert.deepEqual(actual.remote, expected);
|
||||
});
|
||||
|
||||
test('merge returns local extension if remote does not exist with skipped extensions', async () => {
|
||||
test('merge returns local extension if remote does not exist with skipped extensions', () => {
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
const skippedExtension: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
];
|
||||
const expected: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, null, null, skippedExtension, []);
|
||||
@@ -85,18 +85,18 @@ suite('ExtensionsMerge - No Conflicts', () => {
|
||||
assert.deepEqual(actual.remote, expected);
|
||||
});
|
||||
|
||||
test('merge returns local extension if remote does not exist with skipped and ignored extensions', async () => {
|
||||
test('merge returns local extension if remote does not exist with skipped and ignored extensions', () => {
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
const skippedExtension: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
];
|
||||
const expected: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, null, null, skippedExtension, ['a']);
|
||||
@@ -107,180 +107,180 @@ suite('ExtensionsMerge - No Conflicts', () => {
|
||||
assert.deepEqual(actual.remote, expected);
|
||||
});
|
||||
|
||||
test('merge local and remote extensions when there is no base', async () => {
|
||||
test('merge local and remote extensions when there is no base', () => {
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
const expected: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, null, [], []);
|
||||
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' } }, { identifier: { id: 'c', uuid: 'c' } }]);
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true }, { identifier: { id: 'c', uuid: 'c' }, installed: true }]);
|
||||
assert.deepEqual(actual.removed, []);
|
||||
assert.deepEqual(actual.updated, []);
|
||||
assert.deepEqual(actual.remote, expected);
|
||||
});
|
||||
|
||||
test('merge local and remote extensions when there is no base and with ignored extensions', async () => {
|
||||
test('merge local and remote extensions when there is no base and with ignored extensions', () => {
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
const expected: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, null, [], ['a']);
|
||||
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' } }, { identifier: { id: 'c', uuid: 'c' } }]);
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true }, { identifier: { id: 'c', uuid: 'c' }, installed: true }]);
|
||||
assert.deepEqual(actual.removed, []);
|
||||
assert.deepEqual(actual.updated, []);
|
||||
assert.deepEqual(actual.remote, expected);
|
||||
});
|
||||
|
||||
test('merge local and remote extensions when remote is moved forwarded', async () => {
|
||||
test('merge local and remote extensions when remote is moved forwarded', () => {
|
||||
const baseExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []);
|
||||
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' } }, { identifier: { id: 'c', uuid: 'c' } }]);
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true }, { identifier: { id: 'c', uuid: 'c' }, installed: true }]);
|
||||
assert.deepEqual(actual.removed, [{ id: 'a', uuid: 'a' }, { id: 'd', uuid: 'd' }]);
|
||||
assert.deepEqual(actual.updated, []);
|
||||
assert.equal(actual.remote, null);
|
||||
});
|
||||
|
||||
test('merge local and remote extensions when remote is moved forwarded with disabled extension', async () => {
|
||||
test('merge local and remote extensions when remote is moved forwarded with disabled extension', () => {
|
||||
const baseExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, disabled: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, disabled: true, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []);
|
||||
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' } }, { identifier: { id: 'c', uuid: 'c' } }]);
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true }, { identifier: { id: 'c', uuid: 'c' }, installed: true }]);
|
||||
assert.deepEqual(actual.removed, [{ id: 'a', uuid: 'a' }]);
|
||||
assert.deepEqual(actual.updated, [{ identifier: { id: 'd', uuid: 'd' }, disabled: true }]);
|
||||
assert.deepEqual(actual.updated, [{ identifier: { id: 'd', uuid: 'd' }, disabled: true, installed: true }]);
|
||||
assert.equal(actual.remote, null);
|
||||
});
|
||||
|
||||
test('merge local and remote extensions when remote moved forwarded with ignored extensions', async () => {
|
||||
test('merge local and remote extensions when remote moved forwarded with ignored extensions', () => {
|
||||
const baseExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], ['a']);
|
||||
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' } }, { identifier: { id: 'c', uuid: 'c' } }]);
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true }, { identifier: { id: 'c', uuid: 'c' }, installed: true }]);
|
||||
assert.deepEqual(actual.removed, [{ id: 'd', uuid: 'd' }]);
|
||||
assert.deepEqual(actual.updated, []);
|
||||
assert.equal(actual.remote, null);
|
||||
});
|
||||
|
||||
test('merge local and remote extensions when remote is moved forwarded with skipped extensions', async () => {
|
||||
test('merge local and remote extensions when remote is moved forwarded with skipped extensions', () => {
|
||||
const baseExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const skippedExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, []);
|
||||
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' } }, { identifier: { id: 'c', uuid: 'c' } }]);
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true }, { identifier: { id: 'c', uuid: 'c' }, installed: true }]);
|
||||
assert.deepEqual(actual.removed, [{ id: 'd', uuid: 'd' }]);
|
||||
assert.deepEqual(actual.updated, []);
|
||||
assert.equal(actual.remote, null);
|
||||
});
|
||||
|
||||
test('merge local and remote extensions when remote is moved forwarded with skipped and ignored extensions', async () => {
|
||||
test('merge local and remote extensions when remote is moved forwarded with skipped and ignored extensions', () => {
|
||||
const baseExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const skippedExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, ['b']);
|
||||
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'c', uuid: 'c' } }]);
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'c', uuid: 'c' }, installed: true }]);
|
||||
assert.deepEqual(actual.removed, [{ id: 'd', uuid: 'd' }]);
|
||||
assert.deepEqual(actual.updated, []);
|
||||
assert.equal(actual.remote, null);
|
||||
});
|
||||
|
||||
test('merge local and remote extensions when local is moved forwarded', async () => {
|
||||
test('merge local and remote extensions when local is moved forwarded', () => {
|
||||
const baseExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []);
|
||||
@@ -291,19 +291,19 @@ suite('ExtensionsMerge - No Conflicts', () => {
|
||||
assert.deepEqual(actual.remote, localExtensions);
|
||||
});
|
||||
|
||||
test('merge local and remote extensions when local is moved forwarded with disabled extensions', async () => {
|
||||
test('merge local and remote extensions when local is moved forwarded with disabled extensions', () => {
|
||||
const baseExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' }, disabled: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, disabled: true, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []);
|
||||
@@ -314,18 +314,18 @@ suite('ExtensionsMerge - No Conflicts', () => {
|
||||
assert.deepEqual(actual.remote, localExtensions);
|
||||
});
|
||||
|
||||
test('merge local and remote extensions when local is moved forwarded with ignored settings', async () => {
|
||||
test('merge local and remote extensions when local is moved forwarded with ignored settings', () => {
|
||||
const baseExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], ['b']);
|
||||
@@ -334,30 +334,30 @@ suite('ExtensionsMerge - No Conflicts', () => {
|
||||
assert.deepEqual(actual.removed, []);
|
||||
assert.deepEqual(actual.updated, []);
|
||||
assert.deepEqual(actual.remote, [
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
]);
|
||||
});
|
||||
|
||||
test('merge local and remote extensions when local is moved forwarded with skipped extensions', async () => {
|
||||
test('merge local and remote extensions when local is moved forwarded with skipped extensions', () => {
|
||||
const baseExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const skippedExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const expected: ISyncExtension[] = [
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, []);
|
||||
@@ -368,25 +368,25 @@ suite('ExtensionsMerge - No Conflicts', () => {
|
||||
assert.deepEqual(actual.remote, expected);
|
||||
});
|
||||
|
||||
test('merge local and remote extensions when local is moved forwarded with skipped and ignored extensions', async () => {
|
||||
test('merge local and remote extensions when local is moved forwarded with skipped and ignored extensions', () => {
|
||||
const baseExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const skippedExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const expected: ISyncExtension[] = [
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, ['c']);
|
||||
@@ -397,54 +397,54 @@ suite('ExtensionsMerge - No Conflicts', () => {
|
||||
assert.deepEqual(actual.remote, expected);
|
||||
});
|
||||
|
||||
test('merge local and remote extensions when both moved forwarded', async () => {
|
||||
test('merge local and remote extensions when both moved forwarded', () => {
|
||||
const baseExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'e', uuid: 'e' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'e', uuid: 'e' }, installed: true },
|
||||
];
|
||||
const expected: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'e', uuid: 'e' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'e', uuid: 'e' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []);
|
||||
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'e', uuid: 'e' } }]);
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'e', uuid: 'e' }, installed: true }]);
|
||||
assert.deepEqual(actual.removed, [{ id: 'a', uuid: 'a' }]);
|
||||
assert.deepEqual(actual.updated, []);
|
||||
assert.deepEqual(actual.remote, expected);
|
||||
});
|
||||
|
||||
test('merge local and remote extensions when both moved forwarded with ignored extensions', async () => {
|
||||
test('merge local and remote extensions when both moved forwarded with ignored extensions', () => {
|
||||
const baseExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'e', uuid: 'e' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'e', uuid: 'e' }, installed: true },
|
||||
];
|
||||
const expected: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'e', uuid: 'e' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'e', uuid: 'e' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], ['a', 'e']);
|
||||
@@ -455,58 +455,58 @@ suite('ExtensionsMerge - No Conflicts', () => {
|
||||
assert.deepEqual(actual.remote, expected);
|
||||
});
|
||||
|
||||
test('merge local and remote extensions when both moved forwarded with skipped extensions', async () => {
|
||||
test('merge local and remote extensions when both moved forwarded with skipped extensions', () => {
|
||||
const baseExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const skippedExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
];
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'e', uuid: 'e' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'e', uuid: 'e' }, installed: true },
|
||||
];
|
||||
const expected: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'e', uuid: 'e' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'e', uuid: 'e' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, []);
|
||||
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'e', uuid: 'e' } }]);
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'e', uuid: 'e' }, installed: true }]);
|
||||
assert.deepEqual(actual.removed, []);
|
||||
assert.deepEqual(actual.updated, []);
|
||||
assert.deepEqual(actual.remote, expected);
|
||||
});
|
||||
|
||||
test('merge local and remote extensions when both moved forwarded with skipped and ignoredextensions', async () => {
|
||||
test('merge local and remote extensions when both moved forwarded with skipped and ignoredextensions', () => {
|
||||
const baseExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const skippedExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
];
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'e', uuid: 'e' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'e', uuid: 'e' }, installed: true },
|
||||
];
|
||||
const expected: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'e', uuid: 'e' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'e', uuid: 'e' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, ['e']);
|
||||
@@ -517,30 +517,134 @@ suite('ExtensionsMerge - No Conflicts', () => {
|
||||
assert.deepEqual(actual.remote, expected);
|
||||
});
|
||||
|
||||
test('merge when remote extension has no uuid and different extension id case', async () => {
|
||||
test('merge when remote extension has no uuid and different extension id case', () => {
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'A' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'A' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
];
|
||||
const expected: ISyncExtension[] = [
|
||||
{ identifier: { id: 'A', uuid: 'a' } },
|
||||
{ identifier: { id: 'd', uuid: 'd' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'c', uuid: 'c' } },
|
||||
{ identifier: { id: 'A', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'd', uuid: 'd' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' }, installed: true },
|
||||
{ identifier: { id: 'c', uuid: 'c' }, installed: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, null, [], []);
|
||||
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'd', uuid: 'd' } }]);
|
||||
assert.deepEqual(actual.added, [{ identifier: { id: 'd', uuid: 'd' }, installed: true }]);
|
||||
assert.deepEqual(actual.removed, []);
|
||||
assert.deepEqual(actual.updated, []);
|
||||
assert.deepEqual(actual.remote, expected);
|
||||
});
|
||||
|
||||
test('merge when remote extension is not an installed extension', () => {
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, null, [], []);
|
||||
|
||||
assert.deepEqual(actual.added, []);
|
||||
assert.deepEqual(actual.removed, []);
|
||||
assert.deepEqual(actual.updated, []);
|
||||
assert.deepEqual(actual.remote, null);
|
||||
});
|
||||
|
||||
test('merge when remote extension is not an installed extension but is an installed extension locally', () => {
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, null, [], []);
|
||||
|
||||
assert.deepEqual(actual.added, []);
|
||||
assert.deepEqual(actual.removed, []);
|
||||
assert.deepEqual(actual.updated, []);
|
||||
assert.deepEqual(actual.remote, localExtensions);
|
||||
});
|
||||
|
||||
test('merge when an extension is not an installed extension remotely and does not exist locally', () => {
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], []);
|
||||
|
||||
assert.deepEqual(actual.added, []);
|
||||
assert.deepEqual(actual.removed, []);
|
||||
assert.deepEqual(actual.updated, []);
|
||||
assert.deepEqual(actual.remote, null);
|
||||
});
|
||||
|
||||
test('merge when an extension is an installed extension remotely but not locally and updated locally', () => {
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' }, disabled: true },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true },
|
||||
];
|
||||
const expected: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true, disabled: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], []);
|
||||
|
||||
assert.deepEqual(actual.added, []);
|
||||
assert.deepEqual(actual.removed, []);
|
||||
assert.deepEqual(actual.updated, []);
|
||||
assert.deepEqual(actual.remote, expected);
|
||||
});
|
||||
|
||||
test('merge when an extension is an installed extension remotely but not locally and updated remotely', () => {
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' }, installed: true, disabled: true },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, localExtensions, [], []);
|
||||
|
||||
assert.deepEqual(actual.added, []);
|
||||
assert.deepEqual(actual.removed, []);
|
||||
assert.deepEqual(actual.updated, remoteExtensions);
|
||||
assert.deepEqual(actual.remote, null);
|
||||
});
|
||||
|
||||
test('merge not installed extensions', () => {
|
||||
const localExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
];
|
||||
const remoteExtensions: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
];
|
||||
const expected: ISyncExtension[] = [
|
||||
{ identifier: { id: 'b', uuid: 'b' } },
|
||||
{ identifier: { id: 'a', uuid: 'a' } },
|
||||
];
|
||||
|
||||
const actual = merge(localExtensions, remoteExtensions, null, [], []);
|
||||
|
||||
assert.deepEqual(actual.added, []);
|
||||
assert.deepEqual(actual.removed, []);
|
||||
assert.deepEqual(actual.updated, []);
|
||||
assert.deepEqual(actual.remote, expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -42,8 +42,8 @@ class TestSynchroniser extends AbstractSynchroniser {
|
||||
protected async performReplace(syncData: ISyncData, remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<void> { }
|
||||
|
||||
async apply(ref: string): Promise<void> {
|
||||
ref = await this.userDataSyncStoreService.write(this.resource, '', ref);
|
||||
await this.updateLastSyncUserData({ ref, syncData: { content: '', version: this.version } });
|
||||
const remoteUserData = await this.updateRemoteUserData('', ref);
|
||||
await this.updateLastSyncUserData(remoteUserData);
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
|
||||
@@ -384,6 +384,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
|
||||
protected _handleCounter: number;
|
||||
protected _handlers: Map<number, HandlerData>;
|
||||
protected _taskExecutions: Map<string, TaskExecutionImpl>;
|
||||
protected _taskExecutionPromises: Map<string, Promise<TaskExecutionImpl>>;
|
||||
protected _providedCustomExecutions2: Map<string, types.CustomExecution>;
|
||||
private _notProvidedCustomExecutions: Set<string>; // Used for custom executions tasks that are created and run through executeTask.
|
||||
protected _activeCustomExecutions2: Map<string, types.CustomExecution>;
|
||||
@@ -412,6 +413,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
|
||||
this._handleCounter = 0;
|
||||
this._handlers = new Map<number, HandlerData>();
|
||||
this._taskExecutions = new Map<string, TaskExecutionImpl>();
|
||||
this._taskExecutionPromises = new Map<string, Promise<TaskExecutionImpl>>();
|
||||
this._providedCustomExecutions2 = new Map<string, types.CustomExecution>();
|
||||
this._notProvidedCustomExecutions = new Set<string>();
|
||||
this._activeCustomExecutions2 = new Map<string, types.CustomExecution>();
|
||||
@@ -496,6 +498,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
|
||||
|
||||
public async $OnDidEndTask(execution: tasks.TaskExecutionDTO): Promise<void> {
|
||||
const _execution = await this.getTaskExecution(execution);
|
||||
this._taskExecutionPromises.delete(execution.id);
|
||||
this._taskExecutions.delete(execution.id);
|
||||
this.customExecutionComplete(execution);
|
||||
this._onDidTerminateTask.fire({
|
||||
@@ -626,17 +629,24 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
|
||||
return taskExecution;
|
||||
}
|
||||
|
||||
let result: TaskExecutionImpl | undefined = this._taskExecutions.get(execution.id);
|
||||
let result: Promise<TaskExecutionImpl> | undefined = this._taskExecutionPromises.get(execution.id);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider);
|
||||
if (!taskToCreate) {
|
||||
throw new Error('Unexpected: Task does not exist.');
|
||||
}
|
||||
const createdResult: TaskExecutionImpl = new TaskExecutionImpl(this, execution.id, taskToCreate);
|
||||
this._taskExecutions.set(execution.id, createdResult);
|
||||
return createdResult;
|
||||
const createdResult: Promise<TaskExecutionImpl> = new Promise(async (resolve, reject) => {
|
||||
const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider);
|
||||
if (!taskToCreate) {
|
||||
reject('Unexpected: Task does not exist.');
|
||||
} else {
|
||||
resolve(new TaskExecutionImpl(this, execution.id, taskToCreate));
|
||||
}
|
||||
});
|
||||
|
||||
this._taskExecutionPromises.set(execution.id, createdResult);
|
||||
return createdResult.then(result => {
|
||||
this._taskExecutions.set(execution.id, result);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
protected checkDeprecation(task: vscode.Task, handler: HandlerData) {
|
||||
|
||||
@@ -11,7 +11,6 @@ import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { selectBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IRemoteExplorerService, REMOTE_EXPLORER_TYPE_KEY } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox';
|
||||
@@ -40,9 +39,7 @@ export class SwitchRemoteViewItem extends SelectActionViewItem {
|
||||
@IStorageService private readonly storageService: IStorageService
|
||||
) {
|
||||
super(null, action, optionsItems, 0, contextViewService, { ariaLabel: nls.localize('remotes', 'Switch Remote') });
|
||||
this._register(attachSelectBoxStyler(this.selectBox, themeService, {
|
||||
selectBackground: SIDE_BAR_BACKGROUND
|
||||
}));
|
||||
this._register(attachSelectBoxStyler(this.selectBox, themeService));
|
||||
|
||||
this.setSelectionForConnection(optionsItems, environmentService, remoteExplorerService);
|
||||
}
|
||||
|
||||
@@ -57,5 +57,5 @@
|
||||
}
|
||||
|
||||
.monaco-workbench .part > .title > .title-actions .switch-remote > .monaco-select-box {
|
||||
padding: 0 22px 0 6px;
|
||||
padding: 1px 22px 2px 6px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user