提交 35643d80 编写于 作者: J Johannes Rieken

add lifecycle to code lens, #74846

上级 be46e973
......@@ -1461,15 +1461,21 @@ export interface IWebviewPanelOptions {
readonly retainContextWhenHidden?: boolean;
}
export interface ICodeLensSymbol {
export interface CodeLens {
range: IRange;
id?: string;
command?: Command;
}
export interface CodeLensList {
lenses: CodeLens[];
dispose(): void;
}
export interface CodeLensProvider {
onDidChange?: Event<this>;
provideCodeLenses(model: model.ITextModel, token: CancellationToken): ProviderResult<ICodeLensSymbol[]>;
resolveCodeLens?(model: model.ITextModel, codeLens: ICodeLensSymbol, token: CancellationToken): ProviderResult<ICodeLensSymbol>;
provideCodeLenses(model: model.ITextModel, token: CancellationToken): ProviderResult<CodeLensList>;
resolveCodeLens?(model: model.ITextModel, codeLens: CodeLens, token: CancellationToken): ProviderResult<CodeLens>;
}
// --- feature registries ------
......
......@@ -6,18 +6,19 @@
import { ITextModel } from 'vs/editor/common/model';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ICodeLensData } from 'vs/editor/contrib/codelens/codelens';
import { CodeLensModel } from 'vs/editor/contrib/codelens/codelens';
import { LRUCache, values } from 'vs/base/common/map';
import { ICodeLensSymbol, CodeLensProvider } from 'vs/editor/common/modes';
import { CodeLensProvider, CodeLensList, CodeLens } from 'vs/editor/common/modes';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { Range } from 'vs/editor/common/core/range';
import { runWhenIdle } from 'vs/base/common/async';
export const ICodeLensCache = createDecorator<ICodeLensCache>('ICodeLensCache');
export interface ICodeLensCache {
_serviceBrand: any;
put(model: ITextModel, data: ICodeLensData[]): void;
get(model: ITextModel): ICodeLensData[] | undefined;
put(model: ITextModel, data: CodeLensModel): void;
get(model: ITextModel): CodeLensModel | undefined;
delete(model: ITextModel): void;
}
......@@ -30,7 +31,7 @@ class CacheItem {
constructor(
readonly lineCount: number,
readonly data: ICodeLensData[]
readonly data: CodeLensModel
) { }
}
......@@ -39,7 +40,7 @@ export class CodeLensCache implements ICodeLensCache {
_serviceBrand: any;
private readonly _fakeProvider = new class implements CodeLensProvider {
provideCodeLenses(): ICodeLensSymbol[] {
provideCodeLenses(): CodeLensList {
throw new Error('not supported');
}
};
......@@ -48,9 +49,12 @@ export class CodeLensCache implements ICodeLensCache {
constructor(@IStorageService storageService: IStorageService) {
const key = 'codelens/cache';
// remove old data
const oldkey = 'codelens/cache';
runWhenIdle(() => storageService.remove(oldkey, StorageScope.WORKSPACE));
// restore lens data on start
const key = 'codelens/cache2';
const raw = storageService.get(key, StorageScope.WORKSPACE, '{}');
this._deserialize(raw);
......@@ -61,13 +65,12 @@ export class CodeLensCache implements ICodeLensCache {
});
}
put(model: ITextModel, data: ICodeLensData[]): void {
const item = new CacheItem(model.getLineCount(), data.map(item => {
return {
symbol: item.symbol,
provider: this._fakeProvider
};
}));
put(model: ITextModel, data: CodeLensModel): void {
const lensModel = new CodeLensModel();
lensModel.add({ lenses: data.lenses.map(v => v.symbol), dispose() { } }, this._fakeProvider, 0);
const item = new CacheItem(model.getLineCount(), lensModel);
this._cache.set(model.uri.toString(), item);
}
......@@ -86,7 +89,7 @@ export class CodeLensCache implements ICodeLensCache {
const data: Record<string, ISerializedCacheData> = Object.create(null);
this._cache.forEach((value, key) => {
const lines = new Set<number>();
for (const d of value.data) {
for (const d of value.data.lenses) {
lines.add(d.symbol.range.startLineNumber);
}
data[key] = {
......@@ -102,14 +105,14 @@ export class CodeLensCache implements ICodeLensCache {
const data: Record<string, ISerializedCacheData> = JSON.parse(raw);
for (const key in data) {
const element = data[key];
const symbols: ICodeLensData[] = [];
const lenses: CodeLens[] = [];
for (const line of element.lines) {
symbols.push({
provider: this._fakeProvider,
symbol: { range: new Range(line, 1, line, 11) }
});
lenses.push({ range: new Range(line, 1, line, 11) });
}
this._cache.set(key, new CacheItem(element.lineCount, symbols));
const model = new CodeLensModel();
model.add({ lenses, dispose() { } }, this._fakeProvider, 0);
this._cache.set(key, new CacheItem(element.lineCount, model));
}
} catch {
// ignore...
......
......@@ -9,38 +9,43 @@ import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/error
import { URI } from 'vs/base/common/uri';
import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { ITextModel } from 'vs/editor/common/model';
import { CodeLensProvider, CodeLensProviderRegistry, ICodeLensSymbol } from 'vs/editor/common/modes';
import { CodeLensProvider, CodeLensProviderRegistry, CodeLens, CodeLensList } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { DisposableStore } from 'vs/base/common/lifecycle';
export interface ICodeLensData {
symbol: ICodeLensSymbol;
export interface CodeLensItem {
symbol: CodeLens;
provider: CodeLensProvider;
providerRank: number;
}
export function getCodeLensData(model: ITextModel, token: CancellationToken): Promise<ICodeLensData[]> {
export class CodeLensModel {
const symbols: ICodeLensData[] = [];
const provider = CodeLensProviderRegistry.ordered(model);
lenses: CodeLensItem[] = [];
const promises = provider.map(provider => Promise.resolve(provider.provideCodeLenses(model, token)).then(result => {
if (Array.isArray(result)) {
for (let symbol of result) {
symbols.push({ symbol, provider });
}
}
}).catch(onUnexpectedExternalError));
private readonly _dispoables = new DisposableStore();
return Promise.all(promises).then(() => {
dispose(): void {
this._dispoables.dispose();
}
return mergeSort(symbols, (a, b) => {
add(list: CodeLensList, provider: CodeLensProvider, providerIdx: number): void {
this._dispoables.add(list);
for (const symbol of list.lenses) {
this.lenses.push({ symbol, provider, providerRank: providerIdx });
}
}
seal(): void {
this.lenses = mergeSort(this.lenses, (a, b) => {
// sort by lineNumber, provider-rank, and column
if (a.symbol.range.startLineNumber < b.symbol.range.startLineNumber) {
return -1;
} else if (a.symbol.range.startLineNumber > b.symbol.range.startLineNumber) {
return 1;
} else if (provider.indexOf(a.provider) < provider.indexOf(b.provider)) {
} else if (a.providerRank < b.providerRank) {
return -1;
} else if (provider.indexOf(a.provider) > provider.indexOf(b.provider)) {
} else if (a.providerRank > b.providerRank) {
return 1;
} else if (a.symbol.range.startColumn < b.symbol.range.startColumn) {
return -1;
......@@ -50,6 +55,21 @@ export function getCodeLensData(model: ITextModel, token: CancellationToken): Pr
return 0;
}
});
}
}
export function getCodeLensData(model: ITextModel, token: CancellationToken): Promise<CodeLensModel> {
const provider = CodeLensProviderRegistry.ordered(model);
const result = new CodeLensModel();
const promises = provider.map((provider, i) => Promise.resolve(provider.provideCodeLenses(model, token))
.then(list => list && result.add(list, provider, i))
.catch(onUnexpectedExternalError));
return Promise.all(promises).then(() => {
result.seal();
return result;
});
}
......@@ -65,12 +85,12 @@ registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) {
throw illegalArgument();
}
const result: ICodeLensSymbol[] = [];
const result: CodeLens[] = [];
return getCodeLensData(model, CancellationToken.None).then(value => {
let resolve: Promise<any>[] = [];
for (const item of value) {
for (const item of value.lenses) {
if (typeof itemResolveCount === 'undefined' || Boolean(item.symbol.command)) {
result.push(item.symbol);
} else if (itemResolveCount-- > 0 && item.provider.resolveCodeLens) {
......@@ -78,7 +98,7 @@ registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) {
}
}
return Promise.all(resolve);
return Promise.all(resolve).finally(() => value.dispose());
}).then(() => {
return result;
......
......@@ -12,9 +12,9 @@ import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model';
import { CodeLensProviderRegistry, ICodeLensSymbol } from 'vs/editor/common/modes';
import { ICodeLensData, getCodeLensData } from 'vs/editor/contrib/codelens/codelens';
import { CodeLens, CodeLensHelper } from 'vs/editor/contrib/codelens/codelensWidget';
import { CodeLensProviderRegistry, CodeLens } from 'vs/editor/common/modes';
import { CodeLensModel, getCodeLensData, CodeLensItem } from 'vs/editor/contrib/codelens/codelens';
import { CodeLensWidget, CodeLensHelper } from 'vs/editor/contrib/codelens/codelensWidget';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ICodeLensCache } from 'vs/editor/contrib/codelens/codeLensCache';
......@@ -27,8 +27,9 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
private _globalToDispose: IDisposable[];
private _localToDispose: IDisposable[];
private _lenses: CodeLens[];
private _currentFindCodeLensSymbolsPromise: CancelablePromise<ICodeLensData[]> | null;
private _lenses: CodeLensWidget[];
private _currentFindCodeLensSymbolsPromise: CancelablePromise<CodeLensModel> | null;
private _currentCodeLensModel: CodeLensModel | undefined;
private _modelChangeCounter: number;
private _currentResolveCodeLensSymbolsPromise: CancelablePromise<any> | null;
private _detectVisibleLenses: RunOnceScheduler;
......@@ -76,6 +77,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
this._currentResolveCodeLensSymbolsPromise = null;
}
this._localToDispose = dispose(this._localToDispose);
dispose(this._currentCodeLensModel);
}
getId(): string {
......@@ -136,7 +138,14 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
this._currentFindCodeLensSymbolsPromise.then(result => {
if (counterValue === this._modelChangeCounter) { // only the last one wins
// lifecycle -> dispose old model
dispose(this._currentCodeLensModel);
this._currentCodeLensModel = result;
// cache model to reduce flicker
this._codeLensCache.put(model, result);
// render lenses
this._renderCodeLensSymbols(result);
this._detectVisibleLenses.schedule();
}
......@@ -147,7 +156,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
this._localToDispose.push(this._editor.onDidChangeModelContent((e) => {
this._editor.changeDecorations((changeAccessor) => {
this._editor.changeViewZones((viewAccessor) => {
let toDispose: CodeLens[] = [];
let toDispose: CodeLensWidget[] = [];
let lastLensLineNumber: number = -1;
this._lenses.forEach((lens) => {
......@@ -228,16 +237,16 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
this._lenses = [];
}
private _renderCodeLensSymbols(symbols: ICodeLensData[]): void {
private _renderCodeLensSymbols(symbols: CodeLensModel): void {
if (!this._editor.hasModel()) {
return;
}
let maxLineNumber = this._editor.getModel().getLineCount();
let groups: ICodeLensData[][] = [];
let lastGroup: ICodeLensData[] | undefined;
let groups: CodeLensItem[][] = [];
let lastGroup: CodeLensItem[] | undefined;
for (let symbol of symbols) {
for (let symbol of symbols.lenses) {
let line = symbol.symbol.range.startLineNumber;
if (line < 1 || line > maxLineNumber) {
// invalid code lens
......@@ -272,7 +281,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
groupsIndex++;
codeLensIndex++;
} else {
this._lenses.splice(codeLensIndex, 0, new CodeLens(groups[groupsIndex], this._editor, helper, accessor, () => this._detectVisibleLenses.schedule()));
this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, helper, accessor, () => this._detectVisibleLenses.schedule()));
codeLensIndex++;
groupsIndex++;
}
......@@ -286,7 +295,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
// Create extra symbols
while (groupsIndex < groups.length) {
this._lenses.push(new CodeLens(groups[groupsIndex], this._editor, helper, accessor, () => this._detectVisibleLenses.schedule()));
this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, helper, accessor, () => this._detectVisibleLenses.schedule()));
groupsIndex++;
}
......@@ -308,8 +317,8 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
return;
}
const toResolve: ICodeLensData[][] = [];
const lenses: CodeLens[] = [];
const toResolve: CodeLensItem[][] = [];
const lenses: CodeLensWidget[] = [];
this._lenses.forEach((lens) => {
const request = lens.computeIfNecessary(model);
if (request) {
......@@ -326,7 +335,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
const promises = toResolve.map((request, i) => {
const resolvedSymbols = new Array<ICodeLensSymbol | undefined | null>(request.length);
const resolvedSymbols = new Array<CodeLens | undefined | null>(request.length);
const promises = request.map((request, i) => {
if (!request.symbol.command && typeof request.provider.resolveCodeLens === 'function') {
return Promise.resolve(request.provider.resolveCodeLens(model, request.symbol, token)).then(symbol => {
......
......@@ -11,9 +11,9 @@ import * as editorBrowser from 'vs/editor/browser/editorBrowser';
import { Range } from 'vs/editor/common/core/range';
import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { Command, ICodeLensSymbol } from 'vs/editor/common/modes';
import { Command, CodeLens } from 'vs/editor/common/modes';
import { editorCodeLensForeground } from 'vs/editor/common/view/editorColorRegistry';
import { ICodeLensData } from 'vs/editor/contrib/codelens/codelens';
import { CodeLensItem } from 'vs/editor/contrib/codelens/codelens';
import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
......@@ -65,7 +65,7 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
constructor(
editor: editorBrowser.ICodeEditor,
symbolRange: Range,
data: ICodeLensData[]
data: CodeLensItem[]
) {
this._id = 'codeLensWidget' + (++CodeLensContentWidget._idPool);
this._editor = editor;
......@@ -88,7 +88,7 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
this._domNode.innerHTML = '&nbsp;';
}
withCommands(inSymbols: Array<ICodeLensSymbol | undefined | null>, animate: boolean): void {
withCommands(inSymbols: Array<CodeLens | undefined | null>, animate: boolean): void {
this._commands.clear();
const symbols = coalesce(inSymbols);
......@@ -189,17 +189,17 @@ export class CodeLensHelper {
}
}
export class CodeLens {
export class CodeLensWidget {
private readonly _editor: editorBrowser.ICodeEditor;
private readonly _viewZone: CodeLensViewZone;
private readonly _viewZoneId: number;
private readonly _contentWidget: CodeLensContentWidget;
private _decorationIds: string[];
private _data: ICodeLensData[];
private _data: CodeLensItem[];
constructor(
data: ICodeLensData[],
data: CodeLensItem[],
editor: editorBrowser.ICodeEditor,
helper: CodeLensHelper,
viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor,
......@@ -256,7 +256,7 @@ export class CodeLens {
});
}
updateCodeLensSymbols(data: ICodeLensData[], helper: CodeLensHelper): void {
updateCodeLensSymbols(data: CodeLensItem[], helper: CodeLensHelper): void {
while (this._decorationIds.length) {
helper.removeDecoration(this._decorationIds.pop()!);
}
......@@ -270,7 +270,7 @@ export class CodeLens {
});
}
computeIfNecessary(model: ITextModel): ICodeLensData[] | null {
computeIfNecessary(model: ITextModel): CodeLensItem[] | null {
if (!this._contentWidget.isVisible()) {
return null;
}
......@@ -285,7 +285,7 @@ export class CodeLens {
return this._data;
}
updateCommands(symbols: Array<ICodeLensSymbol | undefined | null>): void {
updateCommands(symbols: Array<CodeLens | undefined | null>): void {
this._contentWidget.withCommands(symbols, true);
for (let i = 0; i < this._data.length; i++) {
const resolved = symbols[i];
......
......@@ -5449,16 +5449,21 @@ declare namespace monaco.languages {
arguments?: any[];
}
export interface ICodeLensSymbol {
export interface CodeLens {
range: IRange;
id?: string;
command?: Command;
}
export interface CodeLensList {
lenses: CodeLens[];
dispose(): void;
}
export interface CodeLensProvider {
onDidChange?: IEvent<this>;
provideCodeLenses(model: editor.ITextModel, token: CancellationToken): ProviderResult<ICodeLensSymbol[]>;
resolveCodeLens?(model: editor.ITextModel, codeLens: ICodeLensSymbol, token: CancellationToken): ProviderResult<ICodeLensSymbol>;
provideCodeLenses(model: editor.ITextModel, token: CancellationToken): ProviderResult<CodeLensList>;
resolveCodeLens?(model: editor.ITextModel, codeLens: CodeLens, token: CancellationToken): ProviderResult<CodeLens>;
}
export interface ILanguageExtensionPoint {
......
......@@ -139,25 +139,19 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number | undefined): void {
const provider = <modes.CodeLensProvider>{
provideCodeLenses: (model: ITextModel, token: CancellationToken): modes.ICodeLensSymbol[] | Promise<modes.ICodeLensSymbol[]> => {
return this._proxy.$provideCodeLenses(handle, model.uri, token).then(dto => {
if (dto) {
dto.forEach(obj => {
this._heapService.trackObject(obj);
this._heapService.trackObject(obj.command);
});
provideCodeLenses: (model: ITextModel, token: CancellationToken): Promise<modes.CodeLensList | undefined> => {
return this._proxy.$provideCodeLenses(handle, model.uri, token).then(listDto => {
if (!listDto) {
return undefined;
}
return dto;
return {
lenses: listDto.lenses,
dispose: () => listDto.cacheId && this._proxy.$releaseCodeLenses(handle, listDto.cacheId)
};
});
},
resolveCodeLens: (_model: ITextModel, codeLens: modes.ICodeLensSymbol, token: CancellationToken): Promise<modes.ICodeLensSymbol | undefined> => {
return this._proxy.$resolveCodeLens(handle, codeLens, token).then(obj => {
if (obj) {
this._heapService.trackObject(obj);
this._heapService.trackObject(obj.command);
}
return obj;
});
resolveCodeLens: (_model: ITextModel, codeLens: modes.CodeLens, token: CancellationToken): Promise<modes.CodeLens | undefined> => {
return this._proxy.$resolveCodeLens(handle, codeLens, token);
}
};
......
......@@ -1008,9 +1008,14 @@ export interface LinkDto {
tooltip?: string;
}
export interface CodeLensDto extends ObjectIdentifier {
export interface CodeLensListDto {
cacheId?: number;
lenses: CodeLensDto[];
}
export interface CodeLensDto {
cacheId?: ChainedCacheId;
range: IRange;
id?: string;
command?: CommandDto;
}
......@@ -1026,8 +1031,9 @@ export interface CallHierarchyDto {
export interface ExtHostLanguageFeaturesShape {
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeLensDto[]>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeLensListDto | undefined>;
$resolveCodeLens(handle: number, symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined>;
$releaseCodeLenses(handle: number, id: number): void;
$provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
$provideDeclaration(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
$provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
......
......@@ -504,7 +504,7 @@ export class ExtHostApiCommands {
private _executeCodeLensProvider(resource: URI, itemResolveCount: number): Promise<vscode.CodeLens[] | undefined> {
const args = { resource, itemResolveCount };
return this._commands.executeCommand<modes.ICodeLensSymbol[]>('_executeCodeLensProvider', args)
return this._commands.executeCommand<modes.CodeLens[]>('_executeCodeLensProvider', args)
.then(tryMapWith(item => {
return new types.CodeLens(
typeConverters.Range.to(item.range),
......
......@@ -18,6 +18,7 @@ import { revive } from 'vs/base/common/marshalling';
import { Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
import { URI } from 'vs/base/common/uri';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
interface CommandHandler {
callback: Function;
......@@ -211,6 +212,35 @@ export class CommandsConverter {
this._commands.registerCommand(true, this._delegatingCommandId, this._executeConvertedCommand, this);
}
toInternal2(command: vscode.Command | undefined, disposables: DisposableStore): CommandDto | undefined {
if (!command) {
return undefined;
}
const result: CommandDto = {
$ident: undefined,
id: command.command,
title: command.title,
tooltip: command.tooltip
};
if (command.command && isNonEmptyArray(command.arguments)) {
// we have a contributed command with arguments. that
// means we don't want to send the arguments around
const id = this._heap.keep(command);
disposables.add(toDisposable(() => this._heap.delete(id)));
result.$ident = id;
result.id = this._delegatingCommandId;
result.arguments = [id];
}
return result;
}
toInternal(command: vscode.Command): CommandDto;
toInternal(command: undefined): undefined;
toInternal(command: vscode.Command | undefined): CommandDto | undefined;
......
......@@ -15,7 +15,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics';
import { asPromise } from 'vs/base/common/async';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, SuggestDataDto, LinksListDto, ChainedCacheId } from './extHost.protocol';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, SuggestDataDto, LinksListDto, ChainedCacheId, CodeLensListDto } from './extHost.protocol';
import { regExpLeadsToEndlessLoop, regExpFlags } from 'vs/base/common/strings';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange, Range as EditorRange } from 'vs/editor/common/core/range';
......@@ -28,6 +28,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio
import * as callHierarchy from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { LRUCache } from 'vs/base/common/map';
import { IURITransformer } from 'vs/base/common/uriIpc';
import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
// --- adapter
......@@ -102,34 +103,48 @@ class CodeLensAdapter {
private static _badCmd: vscode.Command = { command: 'missing', title: '!!MISSING: command!!' };
private readonly _cache = new Cache<vscode.CodeLens>();
private readonly _disposables = new Map<number, DisposableStore>();
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _commands: CommandsConverter,
private readonly _heapService: ExtHostHeapService,
private readonly _provider: vscode.CodeLensProvider
) { }
provideCodeLenses(resource: URI, token: CancellationToken): Promise<CodeLensDto[]> {
provideCodeLenses(resource: URI, token: CancellationToken): Promise<CodeLensListDto | undefined> {
const doc = this._documents.getDocument(resource);
return asPromise(() => this._provider.provideCodeLenses(doc, token)).then(lenses => {
const result: CodeLensDto[] = [];
if (isNonEmptyArray(lenses)) {
for (const lens of lenses) {
const id = this._heapService.keep(lens);
result.push(ObjectIdentifier.mixin({
range: typeConvert.Range.from(lens.range),
command: this._commands.toInternal(lens.command)
}, id));
}
if (!lenses || token.isCancellationRequested) {
return undefined;
}
const cacheId = this._cache.add(lenses);
const disposables = new DisposableStore();
this._disposables.set(cacheId, disposables);
const result: CodeLensListDto = {
cacheId,
lenses: [],
};
for (let i = 0; i < lenses.length; i++) {
result.lenses.push({
cacheId: [cacheId, i],
range: typeConvert.Range.from(lenses[i].range),
command: this._commands.toInternal2(lenses[i].command, disposables)
});
}
return result;
});
}
resolveCodeLens(symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined> {
const lens = this._heapService.get<vscode.CodeLens>(ObjectIdentifier.of(symbol));
const lens = symbol.cacheId && this._cache.get(...symbol.cacheId);
if (!lens) {
return Promise.resolve(undefined);
}
......@@ -147,6 +162,12 @@ class CodeLensAdapter {
return symbol;
});
}
releaseCodeLenses(cachedId: number): void {
dispose(this._disposables.get(cachedId));
this._disposables.delete(cachedId);
this._cache.delete(cachedId);
}
}
function convertToLocationLinks(value: vscode.Definition): modes.LocationLink[] {
......@@ -1136,7 +1157,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
const handle = this._nextHandle();
const eventHandle = typeof provider.onDidChangeCodeLenses === 'function' ? this._nextHandle() : undefined;
this._adapter.set(handle, new AdapterData(new CodeLensAdapter(this._documents, this._commands.converter, this._heapService, provider), extension));
this._adapter.set(handle, new AdapterData(new CodeLensAdapter(this._documents, this._commands.converter, provider), extension));
this._proxy.$registerCodeLensSupport(handle, this._transformDocumentSelector(selector), eventHandle);
let result = this._createDisposable(handle);
......@@ -1148,14 +1169,18 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return result;
}
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.ICodeLensSymbol[]> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token), []);
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeLensListDto | undefined> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token), undefined);
}
$resolveCodeLens(handle: number, symbol: modes.ICodeLensSymbol, token: CancellationToken): Promise<modes.ICodeLensSymbol | undefined> {
$resolveCodeLens(handle: number, symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(symbol, token), undefined);
}
$releaseCodeLenses(handle: number, cacheId: number): void {
this._withAdapter(handle, CodeLensAdapter, adapter => Promise.resolve(adapter.releaseCodeLenses(cacheId)), undefined);
}
// --- declaration
registerDefinitionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
......
......@@ -194,7 +194,7 @@ suite('ExtHostLanguageFeatures', function () {
await rpcProtocol.sync();
const value = await getCodeLensData(model, CancellationToken.None);
assert.equal(value.length, 1);
assert.equal(value.lenses.length, 1);
});
test('CodeLens, do not resolve a resolved lens', async () => {
......@@ -212,8 +212,8 @@ suite('ExtHostLanguageFeatures', function () {
await rpcProtocol.sync();
const value = await getCodeLensData(model, CancellationToken.None);
assert.equal(value.length, 1);
const data = value[0];
assert.equal(value.lenses.length, 1);
const [data] = value.lenses;
const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None));
assert.equal(symbol!.command!.id, 'id');
assert.equal(symbol!.command!.title, 'Title');
......@@ -229,8 +229,8 @@ suite('ExtHostLanguageFeatures', function () {
await rpcProtocol.sync();
const value = await getCodeLensData(model, CancellationToken.None);
assert.equal(value.length, 1);
let data = value[0];
assert.equal(value.lenses.length, 1);
let [data] = value.lenses;
const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None));
assert.equal(symbol!.command!.id, 'missing');
assert.equal(symbol!.command!.title, '!!MISSING: command!!');
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册