Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[plugin] Implement registerDeclarationProvider #6173

Merged
merged 2 commits into from
Sep 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/core/src/common/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export interface Event<T> {

export namespace Event {
const _disposable = { dispose(): void { } };
export const None: Event<any> = Object.assign(function (): { dispose(): void } { return _disposable; }, {
export const None: Event<any> = Object.assign(function (): { dispose(): void } { return _disposable; }, {
get maxListeners(): number { return 0; },
set maxListeners(maxListeners: number) { }
});
Expand Down Expand Up @@ -195,8 +195,8 @@ export class Emitter<T> {

return result;
}, {
maxListeners: 30
}
maxListeners: 30
}
);
}
return this._event;
Expand All @@ -208,7 +208,7 @@ export class Emitter<T> {
}
const count = this._callbacks.length;
if (count > maxListeners) {
console.warn(new Error(`Possible Emitter memory leak detected. ${maxListeners} exit listeners added. Use event.maxListeners to increase limit`));
console.warn(new Error(`Possible Emitter memory leak detected. ${count} listeners added. Use event.maxListeners to increase the limit (${maxListeners})`));
}
}

Expand Down
3 changes: 1 addition & 2 deletions packages/monaco/src/typings/monaco/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,6 @@ declare module monaco.services {
readonly languageIdentifier: LanguageIdentifier;
}


export interface ServiceCollection {
set<T>(id: any, instanceOrDescriptor: T): T;
}
Expand Down Expand Up @@ -958,6 +957,6 @@ declare module monaco.languages {
export function registerCompletionItemProvider(selector: monaco.modes.LanguageSelector, provider: CompletionItemProvider): IDisposable;
export function registerColorProvider(selector: monaco.modes.LanguageSelector, provider: DocumentColorProvider): IDisposable;
export function registerFoldingRangeProvider(selector: monaco.modes.LanguageSelector, provider: FoldingRangeProvider): IDisposable;
export function registerDeclarationProvider(selector: monaco.modes.LanguageSelector, provider: FoldingRangeProvider): IDisposable;
export function registerDeclarationProvider(selector: monaco.modes.LanguageSelector, provider: DeclarationProvider): IDisposable;
export function registerSelectionRangeProvider(selector: monaco.modes.LanguageSelector, provider: SelectionRangeProvider): IDisposable;
}
4 changes: 4 additions & 0 deletions packages/plugin-ext/src/common/plugin-api-rpc-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,10 @@ export interface DefinitionProvider {
provideDefinition(model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken): Definition | DefinitionLink[] | undefined;
}

export interface DeclarationProvider {
provideDeclaration(model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken): Definition | DefinitionLink[] | undefined;
}

/**
* Value-object that contains additional information when
* requesting references.
Expand Down
2 changes: 2 additions & 0 deletions packages/plugin-ext/src/common/plugin-api-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,7 @@ export interface LanguagesExt {
$provideImplementation(handle: number, resource: UriComponents, position: Position, token: CancellationToken): Promise<Definition | DefinitionLink[] | undefined>;
$provideTypeDefinition(handle: number, resource: UriComponents, position: Position, token: CancellationToken): Promise<Definition | DefinitionLink[] | undefined>;
$provideDefinition(handle: number, resource: UriComponents, position: Position, token: CancellationToken): Promise<Definition | DefinitionLink[] | undefined>;
$provideDeclaration(handle: number, resource: UriComponents, position: Position, token: CancellationToken): Promise<Definition | DefinitionLink[] | undefined>;
$provideReferences(handle: number, resource: UriComponents, position: Position, context: ReferenceContext, token: CancellationToken): Promise<Location[] | undefined>;
$provideSignatureHelp(
handle: number, resource: UriComponents, position: Position, context: SignatureHelpContext, token: CancellationToken
Expand Down Expand Up @@ -1149,6 +1150,7 @@ export interface LanguagesMain {
$registerImplementationProvider(handle: number, selector: SerializedDocumentFilter[]): void;
$registerTypeDefinitionProvider(handle: number, selector: SerializedDocumentFilter[]): void;
$registerDefinitionProvider(handle: number, selector: SerializedDocumentFilter[]): void;
$registerDeclarationProvider(handle: number, selector: SerializedDocumentFilter[]): void;
$registerReferenceProvider(handle: number, selector: SerializedDocumentFilter[]): void;
$registerSignatureHelpProvider(handle: number, selector: SerializedDocumentFilter[], metadata: theia.SignatureHelpProviderMetadata): void;
$registerHoverProvider(handle: number, selector: SerializedDocumentFilter[]): void;
Expand Down
34 changes: 34 additions & 0 deletions packages/plugin-ext/src/main/browser/languages-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ export class LanguagesMainImpl implements LanguagesMain {
this.disposables.set(handle, disposable);
}

$registerDeclarationProvider(handle: number, selector: SerializedDocumentFilter[]): void {
const languageSelector = fromLanguageSelector(selector);
const declarationProvider = this.createDeclarationProvider(handle);
const disposable = new DisposableCollection();
disposable.push(monaco.languages.registerDeclarationProvider(languageSelector, declarationProvider));
this.disposables.set(handle, disposable);
}

$registerReferenceProvider(handle: number, selector: SerializedDocumentFilter[]): void {
const languageSelector = fromLanguageSelector(selector);
const referenceProvider = this.createReferenceProvider(handle);
Expand Down Expand Up @@ -425,6 +433,32 @@ export class LanguagesMainImpl implements LanguagesMain {
};
}

protected createDeclarationProvider(handle: number): monaco.languages.DeclarationProvider {
return {
provideDeclaration: (model, position, token) =>
this.proxy.$provideDeclaration(handle, model.uri, position, token).then(result => {
if (!result) {
return undefined;
}

if (Array.isArray(result)) {
// using DefinitionLink because Location is mandatory part of DefinitionLink
const definitionLinks: monaco.languages.LocationLink[] = [];
for (const item of result) {
definitionLinks.push({ ...item, uri: monaco.Uri.revive(item.uri) });
}
return definitionLinks;
} else {
// single Location
return <monaco.languages.Location>{
uri: monaco.Uri.revive(result.uri),
range: result.range
};
}
})
};
}

protected createSignatureHelpProvider(handle: number, metadata: theia.SignatureHelpProviderMetadata): monaco.languages.SignatureHelpProvider {
return {
signatureHelpTriggerCharacters: metadata.triggerCharacters,
Expand Down
14 changes: 14 additions & 0 deletions packages/plugin-ext/src/plugin/languages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import { ColorProviderAdapter } from './languages/color';
import { RenameAdapter } from './languages/rename';
import { Event } from '@theia/core/lib/common/event';
import { CommandRegistryImpl } from './command-registry';
import { DeclarationAdapter } from './languages/declaration';

type Adapter = CompletionAdapter |
SignatureHelpAdapter |
Expand All @@ -89,6 +90,7 @@ type Adapter = CompletionAdapter |
RangeFormattingAdapter |
OnTypeFormattingAdapter |
DefinitionAdapter |
DeclarationAdapter |
ImplementationAdapter |
TypeDefinitionAdapter |
LinkProviderAdapter |
Expand Down Expand Up @@ -248,6 +250,18 @@ export class LanguagesExtImpl implements LanguagesExt {
}
// ### Definition provider end

// ### Declaration provider begin
$provideDeclaration(handle: number, resource: UriComponents, position: Position, token: theia.CancellationToken): Promise<Definition | DefinitionLink[] | undefined> {
return this.withAdapter(handle, DeclarationAdapter, adapter => adapter.provideDeclaration(URI.revive(resource), position, token));
}

registerDeclarationProvider(selector: theia.DocumentSelector, provider: theia.DeclarationProvider): theia.Disposable {
const callId = this.addNewAdapter(new DeclarationAdapter(provider, this.documents));
this.proxy.$registerDeclarationProvider(callId, this.transformDocumentSelector(selector));
return this.createDisposable(callId);
}
// ### Declaration provider end

// ### Signature help begin
$provideSignatureHelp(
handle: number, resource: UriComponents, position: Position, context: SignatureHelpContext, token: theia.CancellationToken
Expand Down
72 changes: 72 additions & 0 deletions packages/plugin-ext/src/plugin/languages/declaration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/********************************************************************************
* Copyright (C) 2019 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import URI from 'vscode-uri/lib/umd';
import * as theia from '@theia/plugin';
import { DocumentsExtImpl } from '../documents';
import * as types from '../types-impl';
import * as Converter from '../type-converters';
import { Position } from '../../common/plugin-api-rpc';
import { Definition, DefinitionLink, Location } from '../../common/plugin-api-rpc-model';
import { isDefinitionLinkArray, isLocationArray } from './util';

export class DeclarationAdapter {

constructor(
private readonly provider: theia.DeclarationProvider,
private readonly documents: DocumentsExtImpl) {
}

provideDeclaration(resource: URI, position: Position, token: theia.CancellationToken): Promise<Definition | DefinitionLink[] | undefined> {
const documentData = this.documents.getDocumentData(resource);
if (!documentData) {
return Promise.reject(new Error(`There is no document for ${resource}`));
}

const document = documentData.document;
const zeroBasedPosition = Converter.toPosition(position);

return Promise.resolve(this.provider.provideDeclaration(document, zeroBasedPosition, token)).then(definition => {
if (!definition) {
return undefined;
}

if (definition instanceof types.Location) {
return Converter.fromLocation(definition);
}

if (isLocationArray(definition)) {
const locations: Location[] = [];

for (const location of definition) {
locations.push(Converter.fromLocation(location));
}

return locations;
}

if (isDefinitionLinkArray(definition)) {
const definitionLinks: DefinitionLink[] = [];

for (const definitionLink of definition) {
definitionLinks.push(Converter.fromDefinitionLink(definitionLink));
}

return definitionLinks;
}
});
}
}
3 changes: 3 additions & 0 deletions packages/plugin-ext/src/plugin/plugin-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,9 @@ export function createAPIFactory(
registerDefinitionProvider(selector: theia.DocumentSelector, provider: theia.DefinitionProvider): theia.Disposable {
return languagesExt.registerDefinitionProvider(selector, provider);
},
registerDeclarationProvider(selector: theia.DocumentSelector, provider: theia.DeclarationProvider): theia.Disposable {
return languagesExt.registerDeclarationProvider(selector, provider);
},
registerSignatureHelpProvider(
selector: theia.DocumentSelector, provider: theia.SignatureHelpProvider, first?: string | theia.SignatureHelpProviderMetadata, ...remaining: string[]
): theia.Disposable {
Expand Down
21 changes: 21 additions & 0 deletions packages/plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,27 @@ function provideDefinitionHandler(document: theia.TextDocument, position: theia.
The handler will be invoked each time when a user executes `Go To Definition` command.
It is possible to return a few sources, but for most cases only one is enough. Return `undefined` to provide nothing.

#### Declaration provider

It is possible to provide a declaration for a symbol from within a plugin.
To do this one may register corresponding provider. For example:

```typescript
const documentsSelector: theia.DocumentSelector = { scheme: 'file', language: 'typescript' };
const handler: theia.DeclarationProvider = { provideDeclaration: provideDeclarationHandler };

const disposable = theia.languages.registerDeclarationProvider(documentsSelector, handler);

...

function provideDeclarationHandler(document: theia.TextDocument, position: theia.Position): theia.ProviderResult<theia.Definition | theia.DefinitionLink[]> {
// code here
}
```

The handler will be invoked each time when a user executes `Go To Declaration` command.
It is possible to return a few sources, but for most cases only one is enough. Return `undefined` to provide nothing.

#### Implementation provider

It is possible to provide implementation source for a symbol from within plugin.
Expand Down
31 changes: 31 additions & 0 deletions packages/plugin/src/theia.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,24 @@ declare module '@theia/plugin' {
provideDefinition(document: TextDocument, position: Position, token: CancellationToken | undefined): ProviderResult<Definition | DefinitionLink[]>;
}

/**
* The declaration provider interface defines the contract between extensions and
* the [go to declaration](https://code.visualstudio.com/api/references/vscode-api#DeclarationProvider)
* feature.
*/
export interface DeclarationProvider {
/**
* Provide the declaration of the symbol at the given position and document.
*
* @param document The document in which the command was invoked.
* @param position The position at which the command was invoked.
* @param token A cancellation token.
* @return A declaration or a thenable that resolves to such. The lack of a result can be
* signaled by returning `undefined` or `null`.
*/
provideDeclaration(document: TextDocument, position: Position, token: CancellationToken | undefined): ProviderResult<Definition | DefinitionLink[]>;
}

/**
* The implementation provider interface defines the contract between extensions and
* the go to implementation feature.
Expand Down Expand Up @@ -6825,6 +6843,19 @@ declare module '@theia/plugin' {
*/
export function registerDefinitionProvider(selector: DocumentSelector, provider: DefinitionProvider): Disposable;

/**
* Register a declaration provider.
*
* Multiple providers can be registered for a language. In that case providers are asked in
* parallel and the results are merged. A failing provider (rejected promise or exception) will
* not cause a failure of the whole operation.
*
* @param selector A selector that defines the documents this provider is applicable to.
* @param provider A declaration provider.
* @return A [disposable](#Disposable) that unregisters this provider when being disposed.
*/
export function registerDeclarationProvider(selector: DocumentSelector, provider: DeclarationProvider): Disposable;

/**
* Register a signature help provider.
*
Expand Down