From a6435b1cbd1417b0d334ea9018f5371df198898f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 29 Dec 2016 15:53:31 +0100 Subject: [PATCH] add CodeLensProvider#onDidUpdateCodeLenses, #10648 --- src/vs/editor/common/modes.ts | 1 + .../contrib/codelens/browser/codelens.ts | 73 +++++++------------ src/vs/monaco.d.ts | 1 + src/vs/vscode.d.ts | 5 ++ src/vs/workbench/api/node/extHost.protocol.ts | 3 +- .../api/node/extHostLanguageFeatures.ts | 13 +++- .../api/node/mainThreadLanguageFeatures.ts | 24 +++++- 7 files changed, 68 insertions(+), 52 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 8d9f31a71ea..9299e8e6ca0 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -647,6 +647,7 @@ export interface ICodeLensSymbol { command?: Command; } export interface CodeLensProvider { + onDidChange?: Event; provideCodeLenses(model: editorCommon.IReadOnlyModel, token: CancellationToken): ICodeLensSymbol[] | Thenable; resolveCodeLens?(model: editorCommon.IReadOnlyModel, codeLens: ICodeLensSymbol, token: CancellationToken): ICodeLensSymbol | Thenable; } diff --git a/src/vs/editor/contrib/codelens/browser/codelens.ts b/src/vs/editor/contrib/codelens/browser/codelens.ts index 7367224b89a..80896f67608 100644 --- a/src/vs/editor/contrib/codelens/browser/codelens.ts +++ b/src/vs/editor/contrib/codelens/browser/codelens.ts @@ -8,7 +8,7 @@ import 'vs/css!./codelens'; import { RunOnceScheduler, asWinJsPromise } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposables, IDisposable, dispose } from 'vs/base/common/lifecycle'; import Severity from 'vs/base/common/severity'; import { format, escape } from 'vs/base/common/strings'; import { TPromise } from 'vs/base/common/winjs.base'; @@ -17,8 +17,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IMessageService } from 'vs/platform/message/common/message'; import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { CodeLensProviderRegistry, ICodeLensSymbol, Command } from 'vs/editor/common/modes'; -import { IModelService } from 'vs/editor/common/services/modelService'; +import { CodeLensProviderRegistry, CodeLensProvider, ICodeLensSymbol, Command } from 'vs/editor/common/modes'; import * as editorBrowser from 'vs/editor/browser/editorBrowser'; import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; import { ICodeLensData, getCodeLensData } from '../common/codelens'; @@ -157,33 +156,6 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget { } } -function modelsVersionId(modelService: IModelService, modeId: string): number { - let result = 1; - let models = modelService.getModels() - .filter(model => model.getMode().getId() === modeId) - .map((model) => { - return { - url: model.uri.toString(), - versionId: model.getVersionId() - }; - }) - .sort((a, b) => { - if (a.url < b.url) { - return -1; - } - if (a.url > b.url) { - return 1; - } - return 0; - }); - - for (let i = 0; i < models.length; i++) { - result = (((31 * result) | 0) + models[i].versionId) | 0; - } - - return result; -} - interface IDecorationIdCallback { (decorationId: string): void; } @@ -215,7 +187,6 @@ class CodeLensHelper { this._addDecorationsCallbacks[i](resultingDecorations[i]); } } - } class CodeLens { @@ -226,7 +197,6 @@ class CodeLens { private _decorationIds: string[]; private _data: ICodeLensData[]; private _editor: editorBrowser.ICodeEditor; - private _lastUpdateModelsVersionId: number; public constructor(data: ICodeLensData[], editor: editorBrowser.ICodeEditor, helper: CodeLensHelper, @@ -258,8 +228,6 @@ class CodeLens { this._viewZoneId = viewZoneChangeAccessor.addZone(this._viewZone); this._editor.addContentWidget(this._contentWidget); - - this._lastUpdateModelsVersionId = -1; } public dispose(helper: CodeLensHelper, viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor): void { @@ -296,16 +264,12 @@ class CodeLens { }); } - public computeIfNecessary(currentModelsVersionId: number, model: editorCommon.IModel): ICodeLensData[] { + public computeIfNecessary(model: editorCommon.IModel): ICodeLensData[] { this._contentWidget.updateVisibility(); // trigger the fade in if (!this._contentWidget.isVisible()) { return null; } - if (this._lastUpdateModelsVersionId === currentModelsVersionId) { - return null; - } - // Read editor current state for (let i = 0; i < this._decorationIds.length; i++) { this._data[i].symbol.range = model.getDecorationRange(this._decorationIds[i]); @@ -313,9 +277,8 @@ class CodeLens { return this._data; } - public updateCommands(symbols: ICodeLensSymbol[], currentModelsVersionId: number): void { + public updateCommands(symbols: ICodeLensSymbol[]): void { this._contentWidget.withCommands(symbols); - this._lastUpdateModelsVersionId = currentModelsVersionId; } public getLineNumber(): number { @@ -355,7 +318,6 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { constructor( private _editor: editorBrowser.ICodeEditor, - @IModelService private _modelService: IModelService, @ICommandService private _commandService: ICommandService, @IMessageService private _messageService: IMessageService ) { @@ -419,9 +381,29 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { return; } + const providerSubscriptions = new Disposables(); + this._localToDispose.push(providerSubscriptions); + + const onEvent = () => { + providerSubscriptions.dispose(); + scheduler.schedule(); + }; + + const subscribeToProviders = (result: ICodeLensData[]) => { + const seen = new Set(); + + for (const {provider} of result) { + if (provider.onDidChange && !seen.has(provider)) { + providerSubscriptions.add(provider.onDidChange(onEvent)); + seen.add(provider); + } + } + }; + const detectVisible = new RunOnceScheduler(() => { this._onViewportChanged(model.getMode().getId()); }, 500); + const scheduler = new RunOnceScheduler(() => { if (this._currentFindCodeLensSymbolsPromise) { this._currentFindCodeLensSymbolsPromise.cancel(); @@ -433,6 +415,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { this._currentFindCodeLensSymbolsPromise.then((result) => { if (counterValue === this._modelChangeCounter) { // only the last one wins this.renderCodeLensSymbols(result); + subscribeToProviders(result); detectVisible.schedule(); } }, (error) => { @@ -590,12 +573,10 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { return; } - const currentModelsVersionId = modelsVersionId(this._modelService, modeId); - const toResolve: ICodeLensData[][] = []; const lenses: CodeLens[] = []; this._lenses.forEach((lens) => { - const request = lens.computeIfNecessary(currentModelsVersionId, model); + const request = lens.computeIfNecessary(model); if (request) { toResolve.push(request); lenses.push(lens); @@ -618,7 +599,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { }); return TPromise.join(promises).then(() => { - lenses[i].updateCommands(resolvedSymbols, currentModelsVersionId); + lenses[i].updateCommands(resolvedSymbols); }); }); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 849ca65962b..cdc84c0621a 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4658,6 +4658,7 @@ declare module monaco.languages { } export interface CodeLensProvider { + onDidChange?: IEvent; provideCodeLenses(model: editor.IReadOnlyModel, token: CancellationToken): ICodeLensSymbol[] | Thenable; resolveCodeLens?(model: editor.IReadOnlyModel, codeLens: ICodeLensSymbol, token: CancellationToken): ICodeLensSymbol | Thenable; } diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 671cedebbd9..756c99025d1 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -1613,6 +1613,11 @@ declare module 'vscode' { */ export interface CodeLensProvider { + /** + * An optional event to signal that the code lenses from this provider have changed. + */ + onDidChangeCodeLenses?: Event; + /** * Compute a list of [lenses](#CodeLens). This call should return as fast as possible and if * computing the commands is expensive implementors should only return code lens objects with the diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 4fd0df5a734..3ab2941a954 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -150,7 +150,8 @@ export abstract class MainThreadErrorsShape { export abstract class MainThreadLanguageFeaturesShape { $unregister(handle: number): TPromise { throw ni(); } $registerOutlineSupport(handle: number, selector: vscode.DocumentSelector): TPromise { throw ni(); } - $registerCodeLensSupport(handle: number, selector: vscode.DocumentSelector): TPromise { throw ni(); } + $registerCodeLensSupport(handle: number, selector: vscode.DocumentSelector, eventHandle: number): TPromise { throw ni(); } + $emitCodeLensEvent(eventHandle: number, event?: any): TPromise { throw ni(); } $registerDeclaractionSupport(handle: number, selector: vscode.DocumentSelector): TPromise { throw ni(); } $registerHoverProvider(handle: number, selector: vscode.DocumentSelector): TPromise { throw ni(); } $registerDocumentHighlightProvider(handle: number, selector: vscode.DocumentSelector): TPromise { throw ni(); } diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index dfce64cc6fb..038eb9e0138 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -677,9 +677,18 @@ export class ExtHostLanguageFeatures extends ExtHostLanguageFeaturesShape { registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable { const handle = this._nextHandle(); + const eventHandle = typeof provider.onDidChangeCodeLenses === 'function' ? this._nextHandle() : undefined; + this._adapter[handle] = new CodeLensAdapter(this._documents, this._commands.converter, this._heapService, provider); - this._proxy.$registerCodeLensSupport(handle, selector); - return this._createDisposable(handle); + this._proxy.$registerCodeLensSupport(handle, selector, eventHandle); + let result = this._createDisposable(handle); + + if (eventHandle !== undefined) { + const subscription = provider.onDidChangeCodeLenses(_ => this._proxy.$emitCodeLensEvent(eventHandle)); + result = Disposable.from(result, subscription); + } + + return result; } $provideCodeLenses(handle: number, resource: URI): TPromise { diff --git a/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts index 275a9350c95..362ca4e7928 100644 --- a/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts @@ -6,6 +6,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { Emitter } from 'vs/base/common/event'; import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; import * as vscode from 'vscode'; import { IReadOnlyModel, ISingleEditOperation } from 'vs/editor/common/editorCommon'; @@ -57,15 +58,32 @@ export class MainThreadLanguageFeatures extends MainThreadLanguageFeaturesShape // --- code lens - $registerCodeLensSupport(handle: number, selector: vscode.DocumentSelector): TPromise { - this._registrations[handle] = modes.CodeLensProviderRegistry.register(selector, { + $registerCodeLensSupport(handle: number, selector: vscode.DocumentSelector, eventHandle: number): TPromise { + + const provider = { provideCodeLenses: (model: IReadOnlyModel, token: CancellationToken): modes.ICodeLensSymbol[] | Thenable => { return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$provideCodeLenses(handle, model.uri))); }, resolveCodeLens: (model: IReadOnlyModel, codeLens: modes.ICodeLensSymbol, token: CancellationToken): modes.ICodeLensSymbol | Thenable => { return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$resolveCodeLens(handle, model.uri, codeLens))); } - }); + }; + + if (typeof eventHandle === 'number') { + const emitter = new Emitter(); + this._registrations[eventHandle] = emitter; + provider.onDidChange = emitter.event; + } + + this._registrations[handle] = modes.CodeLensProviderRegistry.register(selector, provider); + return undefined; + } + + $emitCodeLensEvent(eventHandle: number, event?: any): TPromise { + const obj = this._registrations[eventHandle]; + if (obj instanceof Emitter) { + obj.fire(event); + } return undefined; } -- GitLab