Skip to content

Commit

Permalink
Do not sync large models to web workers (#30180)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexdima committed Jul 7, 2017
1 parent 32aebe6 commit e633f3b
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 14 deletions.
34 changes: 33 additions & 1 deletion src/vs/editor/browser/widget/diffEditorWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
'use strict';

import 'vs/css!./media/diffEditor';
import * as nls from 'vs/nls';
import { RunOnceScheduler } from 'vs/base/common/async';
import { Disposable } from 'vs/base/common/lifecycle';
import * as objects from 'vs/base/common/objects';
import * as dom from 'vs/base/browser/dom';
import Severity from 'vs/base/common/severity';
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
import { ISashEvent, IVerticalSashLayoutProvider, Sash } from 'vs/base/browser/ui/sash/sash';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
Expand Down Expand Up @@ -38,6 +40,8 @@ import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager';
import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations';
import { DiffReview } from 'vs/editor/browser/widget/diffReview';
import URI from 'vs/base/common/uri';
import { IMessageService } from "vs/platform/message/common/message";

interface IEditorDiffDecorations {
decorations: editorCommon.IModelDeltaDecoration[];
Expand Down Expand Up @@ -186,6 +190,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
protected _contextKeyService: IContextKeyService;
private _codeEditorService: ICodeEditorService;
private _themeService: IThemeService;
private readonly _messageService: IMessageService;

private _reviewPane: DiffReview;

Expand All @@ -196,7 +201,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
@IContextKeyService contextKeyService: IContextKeyService,
@IInstantiationService instantiationService: IInstantiationService,
@ICodeEditorService codeEditorService: ICodeEditorService,
@IThemeService themeService: IThemeService
@IThemeService themeService: IThemeService,
@IMessageService messageService: IMessageService
) {
super();

Expand All @@ -205,6 +211,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._contextKeyService = contextKeyService.createScoped(domElement);
this._contextKeyService.createKey('isInDiffEditor', true);
this._themeService = themeService;
this._messageService = messageService;

this.id = (++DIFF_EDITOR_ID);

Expand Down Expand Up @@ -826,6 +833,19 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._beginUpdateDecorationsTimeout = window.setTimeout(() => this._beginUpdateDecorations(), DiffEditorWidget.UPDATE_DIFF_DECORATIONS_DELAY);
}

private _lastOriginalWarning: URI = null;
private _lastModifiedWarning: URI = null;

private static _equals(a: URI, b: URI): boolean {
if (!a && !b) {
return true;
}
if (!a || !b) {
return false;
}
return (a.toString() === b.toString());
}

private _beginUpdateDecorations(): void {
this._beginUpdateDecorationsTimeout = -1;
const currentOriginalModel = this.originalEditor.getModel();
Expand All @@ -840,6 +860,18 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._diffComputationToken++;
let currentToken = this._diffComputationToken;

if (!this._editorWorkerService.canComputeDiff(currentOriginalModel.uri, currentModifiedModel.uri)) {
if (
!DiffEditorWidget._equals(currentOriginalModel.uri, this._lastOriginalWarning)
|| !DiffEditorWidget._equals(currentModifiedModel.uri, this._lastModifiedWarning)
) {
this._lastOriginalWarning = currentOriginalModel.uri;
this._lastModifiedWarning = currentModifiedModel.uri;
this._messageService.show(Severity.Warning, nls.localize("diff.tooLarge", "Cannot compare files because one file is too large."));
}
return;
}

this._editorWorkerService.computeDiff(currentOriginalModel.uri, currentModifiedModel.uri, this._ignoreTrimWhitespace).then((result) => {
if (currentToken === this._diffComputationToken
&& currentOriginalModel === this.originalEditor.getModel()
Expand Down
6 changes: 6 additions & 0 deletions src/vs/editor/common/services/editorWorkerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@ export var IEditorWorkerService = createDecorator<IEditorWorkerService>(ID_EDITO
export interface IEditorWorkerService {
_serviceBrand: any;

canComputeDiff(original: URI, modified: URI): boolean;
computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): TPromise<ILineChange[]>;

canComputeDirtyDiff(original: URI, modified: URI): boolean;
computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): TPromise<IChange[]>;

computeMoreMinimalEdits(resource: URI, edits: TextEdit[], ranges: IRange[]): TPromise<TextEdit[]>;

canNavigateValueSet(resource: URI): boolean;
navigateValueSet(resource: URI, range: IRange, up: boolean): TPromise<IInplaceReplaceSupportResult>;
}
50 changes: 47 additions & 3 deletions src/vs/editor/common/services/editorWorkerServiceImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,21 @@ const STOP_SYNC_MODEL_DELTA_TIME_MS = 60 * 1000;
*/
const STOP_WORKER_DELTA_TIME_MS = 5 * 60 * 1000;

function canSyncModel(modelService: IModelService, resource: URI): boolean {
let model = modelService.getModel(resource);
if (!model) {
return false;
}
if (model.isTooLargeForTokenization()) {
return false;
}
return true;
}

export class EditorWorkerServiceImpl extends Disposable implements IEditorWorkerService {
public _serviceBrand: any;

private readonly _modelService: IModelService;
private readonly _workerManager: WorkerManager;

constructor(
Expand All @@ -43,25 +55,37 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker
@IModeService modeService: IModeService
) {
super();
this._workerManager = this._register(new WorkerManager(modelService));
this._modelService = modelService;
this._workerManager = this._register(new WorkerManager(this._modelService));

// todo@joh make sure this happens only once
this._register(modes.LinkProviderRegistry.register('*', <modes.LinkProvider>{
provideLinks: (model, token) => {
if (!canSyncModel(this._modelService, model.uri)) {
return TPromise.as([]); // File too large
}
return wireCancellationToken(token, this._workerManager.withWorker().then(client => client.computeLinks(model.uri)));
}
}));
this._register(modes.SuggestRegistry.register('*', new WordBasedCompletionItemProvider(this._workerManager, configurationService, modeService)));
this._register(modes.SuggestRegistry.register('*', new WordBasedCompletionItemProvider(this._workerManager, configurationService, modeService, this._modelService)));
}

public dispose(): void {
super.dispose();
}

public canComputeDiff(original: URI, modified: URI): boolean {
return (canSyncModel(this._modelService, original) && canSyncModel(this._modelService, modified));
}

public computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): TPromise<editorCommon.ILineChange[]> {
return this._workerManager.withWorker().then(client => client.computeDiff(original, modified, ignoreTrimWhitespace));
}

public canComputeDirtyDiff(original: URI, modified: URI): boolean {
return (canSyncModel(this._modelService, original) && canSyncModel(this._modelService, modified));
}

public computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): TPromise<editorCommon.IChange[]> {
return this._workerManager.withWorker().then(client => client.computeDirtyDiff(original, modified, ignoreTrimWhitespace));
}
Expand All @@ -70,10 +94,17 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker
if (!Array.isArray(edits) || edits.length === 0) {
return TPromise.as(edits);
} else {
if (!canSyncModel(this._modelService, resource)) {
return TPromise.as(edits); // File too large
}
return this._workerManager.withWorker().then(client => client.computeMoreMinimalEdits(resource, edits, ranges));
}
}

public canNavigateValueSet(resource: URI): boolean {
return (canSyncModel(this._modelService, resource));
}

public navigateValueSet(resource: URI, range: IRange, up: boolean): TPromise<modes.IInplaceReplaceSupportResult> {
return this._workerManager.withWorker().then(client => client.navigateValueSet(resource, range, up));
}
Expand All @@ -84,18 +115,28 @@ class WordBasedCompletionItemProvider implements modes.ISuggestSupport {
private readonly _workerManager: WorkerManager;
private readonly _configurationService: ITextResourceConfigurationService;
private readonly _modeService: IModeService;
private readonly _modelService: IModelService;

constructor(workerManager: WorkerManager, configurationService: ITextResourceConfigurationService, modeService: IModeService) {
constructor(
workerManager: WorkerManager,
configurationService: ITextResourceConfigurationService,
modeService: IModeService,
modelService: IModelService
) {
this._workerManager = workerManager;
this._configurationService = configurationService;
this._modeService = modeService;
this._modelService = modelService;
}

provideCompletionItems(model: editorCommon.IModel, position: Position): TPromise<modes.ISuggestResult> {
const { wordBasedSuggestions } = this._configurationService.getConfiguration<IEditorOptions>(model.uri, position, 'editor');
if (!wordBasedSuggestions) {
return undefined;
}
if (!canSyncModel(this._modelService, model.uri)) {
return undefined; // File too large
}
return this._workerManager.withWorker().then(client => client.textualSuggest(model.uri, position));
}
}
Expand Down Expand Up @@ -228,6 +269,9 @@ class EditorModelManager extends Disposable {
if (!model) {
return;
}
if (model.isTooLargeForTokenization()) {
return;
}

let modelUrl = resource.toString();

Expand Down
18 changes: 11 additions & 7 deletions src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,17 @@ class InPlaceReplaceController implements IEditorContribution {

var state = new EditorState(this.editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);

this.currentRequest = this.editorWorkerService.navigateValueSet(modelURI, selection, up);
this.currentRequest = this.currentRequest.then((basicResult) => {
if (basicResult && basicResult.range && basicResult.value) {
return basicResult;
}
return null;
});
if (!this.editorWorkerService.canNavigateValueSet(modelURI)) {
this.currentRequest = TPromise.as(null);
} else {
this.currentRequest = this.editorWorkerService.navigateValueSet(modelURI, selection, up);
this.currentRequest = this.currentRequest.then((basicResult) => {
if (basicResult && basicResult.range && basicResult.value) {
return basicResult;
}
return null;
});
}

return this.currentRequest.then((result: IInplaceReplaceSupportResult) => {

Expand Down
6 changes: 4 additions & 2 deletions src/vs/editor/standalone/browser/standaloneCodeEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { MenuId, MenuRegistry, IMenuItem } from 'vs/platform/actions/common/acti
import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import * as aria from 'vs/base/browser/ui/aria/aria';
import { IMessageService } from "vs/platform/message/common/message";

/**
* The options to create an editor.
Expand Down Expand Up @@ -301,14 +302,15 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon
@IContextViewService contextViewService: IContextViewService,
@IEditorWorkerService editorWorkerService: IEditorWorkerService,
@ICodeEditorService codeEditorService: ICodeEditorService,
@IStandaloneThemeService themeService: IStandaloneThemeService
@IStandaloneThemeService themeService: IStandaloneThemeService,
@IMessageService messageService: IMessageService
) {
options = options || {};
if (typeof options.theme === 'string') {
options.theme = themeService.setTheme(options.theme);
}

super(domElement, options, editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService);
super(domElement, options, editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService, messageService);

if (keybindingService instanceof StandaloneKeybindingService) {
this._standaloneKeybindingService = keybindingService;
Expand Down
4 changes: 3 additions & 1 deletion src/vs/editor/standalone/browser/standaloneEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { Token } from 'vs/editor/common/core/token';
import { FontInfo, BareFontInfo } from 'vs/editor/common/config/fontInfo';
import * as editorOptions from 'vs/editor/common/config/editorOptions';
import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
import { IMessageService } from "vs/platform/message/common/message";

/**
* @internal
Expand Down Expand Up @@ -127,7 +128,8 @@ export function createDiffEditor(domElement: HTMLElement, options?: IDiffEditorC
services.get(IContextViewService),
services.get(IEditorWorkerService),
services.get(ICodeEditorService),
services.get(IStandaloneThemeService)
services.get(IStandaloneThemeService),
services.get(IMessageService)
);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ class DirtyDiffModelDecorator {
return winjs.TPromise.as([]); // disposed
}

if (!this.editorWorkerService.canComputeDirtyDiff(originalURI, this.model.uri)) {
return winjs.TPromise.as([]); // Files too large
}

return this.editorWorkerService.computeDirtyDiff(originalURI, this.model.uri, true);
});
}
Expand Down

0 comments on commit e633f3b

Please sign in to comment.