提交 afc3556c 编写于 作者: J Johannes Rieken

reuse proper model sync logic

上级 2d68b24a
......@@ -14,7 +14,13 @@ export interface IRawModelData {
value:editorCommon.IRawText;
}
export abstract class EditorSimpleWorker {
export interface IEditorModelWorker {
acceptNewModel(data: IRawModelData): void;
acceptModelChanged(modelUrl: string, events: editorCommon.IModelContentChangedEvent2[]);
acceptRemovedModel(modelUrl: string): void;
}
export abstract class EditorSimpleWorker implements IEditorModelWorker {
public acceptNewModel(data:IRawModelData): void {
throw new Error('Not implemented!');
......
......@@ -13,7 +13,7 @@ import {DefaultWorkerFactory} from 'vs/base/worker/defaultWorkerFactory';
import * as editorCommon from 'vs/editor/common/editorCommon';
import {WordHelper} from 'vs/editor/common/model/textModelWithTokensHelpers';
import {IInplaceReplaceSupportResult, ILink, ISuggestResult} from 'vs/editor/common/modes';
import {EditorSimpleWorker} from 'vs/editor/common/services/editorSimpleWorkerCommon';
import {IEditorModelWorker, EditorSimpleWorker} from 'vs/editor/common/services/editorSimpleWorkerCommon';
import {IEditorWorkerService} from 'vs/editor/common/services/editorWorkerService';
import {IModelService} from 'vs/editor/common/services/modelService';
......@@ -102,28 +102,23 @@ class WorkerManager extends Disposable {
}
}
class EditorWorkerClient extends Disposable {
export class EditorModelManager extends Disposable {
private _worker: SimpleWorkerClient<EditorSimpleWorker>;
private _proxy: EditorSimpleWorker;
private _modelService:IModelService;
private _syncedModels: {[modelUrl:string]:IDisposable[];};
private _syncedModelsLastUsedTime: {[modelUrl:string]:number;};
private _proxy: IEditorModelWorker;
private _modelService: IModelService;
private _syncedModels: { [modelUrl: string]: IDisposable[]; } = Object.create(null);
private _syncedModelsLastUsedTime: { [modelUrl: string]: number; } = Object.create(null);
constructor(modelService:IModelService) {
constructor(proxy: IEditorModelWorker, modelService: IModelService, keepIdleModels: boolean) {
super();
this._proxy = proxy;
this._modelService = modelService;
this._syncedModels = Object.create(null);
this._syncedModelsLastUsedTime = Object.create(null);
this._worker = this._register(new SimpleWorkerClient<EditorSimpleWorker>(
new DefaultWorkerFactory(),
'vs/editor/common/services/editorSimpleWorker',
EditorSimpleWorker
));
this._proxy = this._worker.get();
let stopModelSyncInterval = this._register(new IntervalTimer());
stopModelSyncInterval.cancelAndSet(() => this._checkStopModelSync(), Math.round(STOP_SYNC_MODEL_DELTA_TIME_MS / 2));
if (!keepIdleModels) {
let timer = new IntervalTimer();
timer.cancelAndSet(() => this._checkStopModelSync(), Math.round(STOP_SYNC_MODEL_DELTA_TIME_MS / 2));
this._register(timer);
}
}
public dispose(): void {
......@@ -135,67 +130,7 @@ class EditorWorkerClient extends Disposable {
super.dispose();
}
private _checkStopModelSync(): void {
let currentTime = (new Date()).getTime();
let toRemove:string[] = [];
for (let modelUrl in this._syncedModelsLastUsedTime) {
let elapsedTime = currentTime - this._syncedModelsLastUsedTime[modelUrl];
if (elapsedTime > STOP_SYNC_MODEL_DELTA_TIME_MS) {
toRemove.push(modelUrl);
}
}
for (let i = 0; i < toRemove.length; i++) {
this._stopModelSync(toRemove[i]);
}
}
public computeDiff(original:URI, modified:URI, ignoreTrimWhitespace:boolean):TPromise<editorCommon.ILineChange[]> {
return this._withSyncedResources([original, modified]).then(_ => {
return this._proxy.computeDiff(original.toString(), modified.toString(), ignoreTrimWhitespace);
});
}
public computeDirtyDiff(original:URI, modified:URI, ignoreTrimWhitespace:boolean):TPromise<editorCommon.IChange[]> {
return this._withSyncedResources([original, modified]).then(_ => {
return this._proxy.computeDirtyDiff(original.toString(), modified.toString(), ignoreTrimWhitespace);
});
}
public computeLinks(resource:URI):TPromise<ILink[]> {
return this._withSyncedResources([resource]).then(_ => {
return this._proxy.computeLinks(resource.toString());
});
}
public textualSuggest(resource: URI, position: editorCommon.IPosition): TPromise<ISuggestResult[]> {
return this._withSyncedResources([resource]).then(_ => {
let model = this._modelService.getModel(resource);
if (!model) {
return null;
}
let wordDefRegExp = WordHelper.massageWordDefinitionOf(model.getMode());
let wordDef = wordDefRegExp.source;
let wordDefFlags = (wordDefRegExp.global ? 'g' : '') + (wordDefRegExp.ignoreCase ? 'i' : '') + (wordDefRegExp.multiline ? 'm' : '');
return this._proxy.textualSuggest(resource.toString(), position, wordDef, wordDefFlags);
});
}
public navigateValueSet(resource: URI, range:editorCommon.IRange, up:boolean): TPromise<IInplaceReplaceSupportResult> {
return this._withSyncedResources([resource]).then(_ => {
let model = this._modelService.getModel(resource);
if (!model) {
return null;
}
let wordDefRegExp = WordHelper.massageWordDefinitionOf(model.getMode());
let wordDef = wordDefRegExp.source;
let wordDefFlags = (wordDefRegExp.global ? 'g' : '') + (wordDefRegExp.ignoreCase ? 'i' : '') + (wordDefRegExp.multiline ? 'm' : '');
return this._proxy.navigateValueSet(resource.toString(), range, up, wordDef, wordDefFlags);
});
}
private _withSyncedResources(resources:URI[]): TPromise<void> {
public withSyncedResources(resources:URI[]): TPromise<void> {
for (let i = 0; i < resources.length; i++) {
let resource = resources[i];
......@@ -212,6 +147,22 @@ class EditorWorkerClient extends Disposable {
return TPromise.as(null);
}
private _checkStopModelSync(): void {
let currentTime = (new Date()).getTime();
let toRemove:string[] = [];
for (let modelUrl in this._syncedModelsLastUsedTime) {
let elapsedTime = currentTime - this._syncedModelsLastUsedTime[modelUrl];
if (elapsedTime > STOP_SYNC_MODEL_DELTA_TIME_MS) {
toRemove.push(modelUrl);
}
}
for (let i = 0; i < toRemove.length; i++) {
this._stopModelSync(toRemove[i]);
}
}
private _beginModelSync(resource:URI): void {
let modelUrl = resource.toString();
let model = this._modelService.getModel(resource);
......@@ -262,3 +213,67 @@ class EditorWorkerClient extends Disposable {
disposeAll(toDispose);
}
}
class EditorWorkerClient extends Disposable {
private _modelService: IModelService;
private _worker: SimpleWorkerClient<EditorSimpleWorker>;
private _proxy: EditorSimpleWorker;
private _modelManager: EditorModelManager;
constructor(modelService: IModelService) {
super();
this._modelService = modelService;
this._worker = this._register(new SimpleWorkerClient<EditorSimpleWorker>(
new DefaultWorkerFactory(),
'vs/editor/common/services/editorSimpleWorker',
EditorSimpleWorker
));
this._proxy = this._worker.get();
this._modelManager = new EditorModelManager(this._proxy, this._modelService, false);
}
public computeDiff(original:URI, modified:URI, ignoreTrimWhitespace:boolean):TPromise<editorCommon.ILineChange[]> {
return this._modelManager.withSyncedResources([original, modified]).then(_ => {
return this._proxy.computeDiff(original.toString(), modified.toString(), ignoreTrimWhitespace);
});
}
public computeDirtyDiff(original:URI, modified:URI, ignoreTrimWhitespace:boolean):TPromise<editorCommon.IChange[]> {
return this._modelManager.withSyncedResources([original, modified]).then(_ => {
return this._proxy.computeDirtyDiff(original.toString(), modified.toString(), ignoreTrimWhitespace);
});
}
public computeLinks(resource:URI):TPromise<ILink[]> {
return this._modelManager.withSyncedResources([resource]).then(_ => {
return this._proxy.computeLinks(resource.toString());
});
}
public textualSuggest(resource: URI, position: editorCommon.IPosition): TPromise<ISuggestResult[]> {
return this._modelManager.withSyncedResources([resource]).then(_ => {
let model = this._modelService.getModel(resource);
if (!model) {
return null;
}
let wordDefRegExp = WordHelper.massageWordDefinitionOf(model.getMode());
let wordDef = wordDefRegExp.source;
let wordDefFlags = (wordDefRegExp.global ? 'g' : '') + (wordDefRegExp.ignoreCase ? 'i' : '') + (wordDefRegExp.multiline ? 'm' : '');
return this._proxy.textualSuggest(resource.toString(), position, wordDef, wordDefFlags);
});
}
public navigateValueSet(resource: URI, range:editorCommon.IRange, up:boolean): TPromise<IInplaceReplaceSupportResult> {
return this._modelManager.withSyncedResources([resource]).then(_ => {
let model = this._modelService.getModel(resource);
if (!model) {
return null;
}
let wordDefRegExp = WordHelper.massageWordDefinitionOf(model.getMode());
let wordDef = wordDefRegExp.source;
let wordDefFlags = (wordDefRegExp.global ? 'g' : '') + (wordDefRegExp.ignoreCase ? 'i' : '') + (wordDefRegExp.multiline ? 'm' : '');
return this._proxy.navigateValueSet(resource.toString(), range, up, wordDef, wordDefFlags);
});
}
}
......@@ -19,7 +19,7 @@ import {ExtraInfoRegistry} from 'vs/editor/contrib/hover/common/hover';
import {ReferenceRegistry} from 'vs/editor/contrib/referenceSearch/common/referenceSearch';
import {DeclarationRegistry} from 'vs/editor/contrib/goToDeclaration/common/goToDeclaration';
export default function registerLanguageFeatures(selector: string, modelService: IModelService, worker: () => TPromise<AbstractWorker>): lifecycle.IDisposable {
export default function registerLanguageFeatures(selector: string, modelService: IModelService, worker: (first:URI, ...more:URI[]) => TPromise<AbstractWorker>): lifecycle.IDisposable {
const disposables: lifecycle.IDisposable[] = [];
disposables.push(SuggestRegistry.register(selector, new SuggestAdapter(modelService, worker)));
disposables.push(ParameterHintsRegistry.register(selector, new ParameterHintsAdapter(modelService, worker)));
......@@ -32,7 +32,7 @@ export default function registerLanguageFeatures(selector: string, modelService:
abstract class Adapter {
constructor(protected _modelService: IModelService, protected _worker: () => TPromise<AbstractWorker>) {
constructor(protected _modelService: IModelService, protected _worker: (first:URI, ...more:URI[]) => TPromise<AbstractWorker>) {
}
......@@ -78,7 +78,7 @@ class SuggestAdapter extends Adapter implements modes.ISuggestSupport {
const wordInfo = model.getWordUntilPosition(position);
const offset = this._positionToOffset(resource, position);
return this._worker().then(worker => {
return this._worker(resource).then(worker => {
return worker.getCompletionsAtPosition(resource.toString(), offset);
}).then(info => {
if (!info) {
......@@ -101,7 +101,7 @@ class SuggestAdapter extends Adapter implements modes.ISuggestSupport {
getSuggestionDetails(resource: URI, position: editor.IPosition, suggestion: modes.ISuggestion) {
return this._worker().then(worker => {
return this._worker(resource).then(worker => {
return worker.getCompletionEntryDetails(resource.toString(),
this._positionToOffset(resource, position),
suggestion.label);
......@@ -145,7 +145,7 @@ class ParameterHintsAdapter extends Adapter implements modes.IParameterHintsSupp
return true;
}
getParameterHints(resource: URI, position: editor.IPosition, triggerCharacter?: string): TPromise<modes.IParameterHints> {
return this._worker().then(worker => worker.getSignatureHelpItems(resource.toString(), this._positionToOffset(resource, position))).then(info => {
return this._worker(resource).then(worker => worker.getSignatureHelpItems(resource.toString(), this._positionToOffset(resource, position))).then(info => {
if (!info) {
return;
......@@ -195,7 +195,7 @@ class ParameterHintsAdapter extends Adapter implements modes.IParameterHintsSupp
class QuickInfoAdapter extends Adapter implements modes.IExtraInfoSupport {
computeInfo(resource: URI, position: editor.IPosition): TPromise<modes.IComputeExtraInfoResult> {
return this._worker().then(worker => {
return this._worker(resource).then(worker => {
return worker.getQuickInfoAtPosition(resource.toString(), this._positionToOffset(resource, position));
}).then(info => {
if (!info) {
......@@ -214,7 +214,7 @@ class QuickInfoAdapter extends Adapter implements modes.IExtraInfoSupport {
class OccurrencesAdapter extends Adapter implements modes.IOccurrencesSupport {
findOccurrences(resource: URI, position: editor.IPosition, strict?: boolean): TPromise<modes.IOccurence[]> {
return this._worker().then(worker => {
return this._worker(resource).then(worker => {
return worker.getOccurrencesAtPosition(resource.toString(), this._positionToOffset(resource, position));
}).then(entries => {
if (!entries) {
......@@ -239,7 +239,7 @@ class DeclarationAdapter extends Adapter implements modes.IDeclarationSupport {
}
findDeclaration(resource: URI, position: editor.IPosition): TPromise<modes.IReference[]> {
return this._worker().then(worker => {
return this._worker(resource).then(worker => {
return worker.getDefinitionAtPosition(resource.toString(), this._positionToOffset(resource, position));
}).then(entries => {
if (!entries) {
......@@ -268,12 +268,8 @@ class ReferenceAdapter extends Adapter implements modes.IReferenceSupport {
return true;
}
/**
* @returns a list of reference of the symbol at the position in the
* given resource.
*/
findReferences(resource: URI, position: editor.IPosition, includeDeclaration: boolean): TPromise<modes.IReference[]> {
return this._worker().then(worker => {
return this._worker(resource).then(worker => {
return worker.getReferencesAtPosition(resource.toString(), this._positionToOffset(resource, position));
}).then(entries => {
if (!entries) {
......
......@@ -4,20 +4,21 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import {TPromise} from 'vs/base/common/winjs.base';
import {IDisposable, disposeAll} from 'vs/base/common/lifecycle';
import {DefaultWorkerFactory} from 'vs/base/worker/defaultWorkerFactory';
import {SimpleWorkerClient} from 'vs/base/common/worker/simpleWorker';
import {IModelService} from 'vs/editor/common/services/modelService';
import {EditorModelManager} from 'vs/editor/common/services/editorWorkerServiceImpl';
import {Defaults} from '../typescript';
import registerLanguageFeatures from '../languageFeatures';
import AbstractWorker from './worker';
class Client {
private _client: TPromise<AbstractWorker> = null;
private _clientDispose: IDisposable[];
private _client: TPromise<{ worker: AbstractWorker; manager: EditorModelManager }> = null;
private _clientDispose: IDisposable[] = [];
private _factory = new DefaultWorkerFactory();
private _modelService: IModelService;
......@@ -25,18 +26,17 @@ class Client {
this._modelService = modelService;
}
private _createClient(): TPromise<AbstractWorker> {
this._clientDispose = [];
private _createClient(): TPromise<{ worker: AbstractWorker; manager: EditorModelManager; }> {
const client = new SimpleWorkerClient<AbstractWorker>(this._factory, 'vs/languages/typescript/common/worker/workerImpl', AbstractWorker);
const manager = new EditorModelManager(client.get(), this._modelService, true);
let client = new SimpleWorkerClient<AbstractWorker>(this._factory,
'vs/languages/typescript/common/worker/workerImpl',
AbstractWorker);
this._clientDispose.push(manager);
this._clientDispose.push(client);
const stopWorker = () => {
this._clientDispose = disposeAll(this._clientDispose);
this._client = null;
client = null;
};
// stop worker after being idle
......@@ -50,47 +50,34 @@ class Client {
// stop worker when defaults change
this._clientDispose.push(Defaults.onDidChange(() => stopWorker()));
// send default to worker right away
const worker = client.get();
const {compilerOptions, extraLibs} = Defaults;
return worker.acceptDefaults(compilerOptions, extraLibs).then(() => worker);
return worker.acceptDefaults(compilerOptions, extraLibs).then(() =>({ worker, manager }));
}
dispose(): void {
this._clientDispose = disposeAll(this._clientDispose);
this._client = null;
}
get(): TPromise<AbstractWorker> {
get(resources:URI[]): TPromise<AbstractWorker> {
if (!this._client) {
this._client = this._createClient();
}
// TODO@joh use proper model sync
return this._client.then(worker => {
let promises = this._modelService.getModels().map(model => {
return TPromise.as(worker.acceptNewModel({
url: model.getAssociatedResource().toString(),
versionId: model.getVersionId(),
value: {
EOL: model.getEOL(),
lines: model.getLinesContent(),
length: model.getValueLength(),
BOM: undefined,
options: undefined
}
}));
});
return TPromise.join(promises).then(() => worker);
});
return this._client
.then(data => data.manager.withSyncedResources(resources)
.then(_ => data.worker));
}
}
export function create(selector: string, modelService: IModelService) {
const client = new Client(modelService);
const registration = registerLanguageFeatures(selector, modelService, () => client.get());
const registration = registerLanguageFeatures(selector,
modelService,
(first: URI, ...more: URI[]) => client.get([first].concat(more)));
return {
dispose() {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册