Skip to content

Commit

Permalink
eclipse-theia#10027 WIP Support EvaluatableExpressions
Browse files Browse the repository at this point in the history
- Implement support for plugins providing evalutable epressions

Contributed on behalf of STMicroelectronics

Signed-off-by: Nina Doschek <ndoschek@eclipsesource.com>

Fixes eclipse-theia#10027
  • Loading branch information
ndoschek committed Mar 31, 2022
1 parent f89e826 commit a28d87c
Show file tree
Hide file tree
Showing 11 changed files with 166 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import * as monaco from '@theia/monaco-editor-core';
*/
@injectable()
export class DebugExpressionProvider {
get(model: monaco.editor.IModel, selection: monaco.IRange): string {
get(model: monaco.editor.ITextModel, selection: monaco.IRange): string {
const lineContent = model.getLineContent(selection.startLineNumber);
const { start, end } = this.getExactExpressionStartAndEnd(lineContent, selection.startColumn, selection.endColumn);
return lineContent.substring(start - 1, end);
Expand Down
8 changes: 7 additions & 1 deletion packages/debug/src/browser/editor/debug-hover-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,13 @@ export class DebugHoverWidget extends SourceTreeWidget implements monaco.editor.
}

this.options = options;
const matchingExpression = this.expressionProvider.get(this.editor.getControl().getModel()!, options.selection);

const textModel = this.editor.getControl().getModel();
if (!textModel) {
return;
}

const matchingExpression = this.expressionProvider.get(textModel, options.selection);
if (!matchingExpression) {
this.hide();
return;
Expand Down
10 changes: 10 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 @@ -258,6 +258,16 @@ export interface HoverProvider {
provideHover(model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken): Hover | undefined | Thenable<Hover | undefined>;
}

export interface EvaluatableExpression {
range: Range;
expression?: string;
}

export interface EvaluatableExpressionProvider {
provideEvaluatableExpression(model: monaco.editor.ITextModel, position: monaco.Position,
token: monaco.CancellationToken): EvaluatableExpression | undefined | Thenable<EvaluatableExpression | undefined>;
}

export enum DocumentHighlightKind {
Text = 0,
Read = 1,
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 @@ -1428,6 +1428,7 @@ export interface LanguagesExt {
): Promise<SignatureHelp | undefined>;
$releaseSignatureHelp(handle: number, id: number): void;
$provideHover(handle: number, resource: UriComponents, position: Position, token: CancellationToken): Promise<Hover | undefined>;
$provideEvaluatableExpression(handle: number, resource: UriComponents, position: theia.Position, token: CancellationToken): Promise<theia.EvaluatableExpression | undefined>;
$provideDocumentHighlights(handle: number, resource: UriComponents, position: Position, token: CancellationToken): Promise<DocumentHighlight[] | undefined>;
$provideDocumentFormattingEdits(handle: number, resource: UriComponents,
options: FormattingOptions, token: CancellationToken): Promise<TextEdit[] | undefined>;
Expand Down Expand Up @@ -1502,6 +1503,7 @@ export interface LanguagesMain {
$registerReferenceProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[]): void;
$registerSignatureHelpProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[], metadata: theia.SignatureHelpProviderMetadata): void;
$registerHoverProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[]): void;
$registerEvaluatableExpressionProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[]): void;
$registerDocumentHighlightProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[]): void;
$registerQuickFixProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[], codeActionKinds?: string[], documentation?: CodeActionProviderDocumentation): void;
$clearDiagnostics(id: string): void;
Expand Down
20 changes: 19 additions & 1 deletion packages/plugin-ext/src/main/browser/languages-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,25 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable {
return this.proxy.$provideHover(handle, model.uri, position, token);
}

$registerDocumentHighlightProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[]): void {
$registerEvaluatableExpressionProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[]): void {
const languageSelector = this.toLanguageSelector(selector);
const evaluatableExpressionProvider = this.createEvaluatableExpressionProvider(handle);
this.register(handle, (theia.languages.registerEvaluatableExpressionProvider as RegistrationFunction<theia.EvaluatableExpressionProvider>)
(languageSelector, evaluatableExpressionProvider));
}

protected createEvaluatableExpressionProvider(handle: number): theia.EvaluatableExpressionProvider {
return {
provideEvaluatableExpression: (model, position, token) => this.provideEvaluatableExpression(handle, model, position, token)
};
}

protected provideEvaluatableExpression(handle: number, model: monaco.editor.ITextModel, position: theia.Position,
token: monaco.CancellationToken): Promise<theia.EvaluatableExpression | undefined> {
return this.proxy.$provideEvaluatableExpression(handle, model.uri, position, token);
}

$registerDocumentHighlightProvider(handle: number, _pluginInfo: PluginInfo, selector: SerializedDocumentFilter[]): void {
const languageSelector = this.toLanguageSelector(selector);
const documentHighlightProvider = this.createDocumentHighlightProvider(handle);
this.register(handle, (monaco.languages.registerDocumentHighlightProvider as RegistrationFunction<monaco.languages.DocumentHighlightProvider>)
Expand Down
15 changes: 15 additions & 0 deletions packages/plugin-ext/src/plugin/languages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,12 @@ import { BinaryBuffer } from '@theia/core/lib/common/buffer';
import { DocumentSemanticTokensAdapter, DocumentRangeSemanticTokensAdapter } from './languages/semantic-highlighting';
import { isReadonlyArray } from '../common/arrays';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { EvaluatableExpressionAdapter } from './languages/evaluatable-expression';

type Adapter = CompletionAdapter |
SignatureHelpAdapter |
HoverAdapter |
EvaluatableExpressionAdapter |
DocumentHighlightAdapter |
DocumentFormattingAdapter |
RangeFormattingAdapter |
Expand Down Expand Up @@ -346,6 +348,19 @@ export class LanguagesExtImpl implements LanguagesExt {
}
// ### Hover Provider end

// ### EvaluatableExpression Provider begin
registerEvaluatableExpressionProvider(selector: theia.DocumentSelector, provider: theia.EvaluatableExpressionProvider, pluginInfo: PluginInfo): theia.Disposable {
const callId = this.addNewAdapter(new EvaluatableExpressionAdapter(provider, this.documents));
this.proxy.$registerEvaluatableExpressionProvider(callId, pluginInfo, this.transformDocumentSelector(selector));
return this.createDisposable(callId);
}

$provideEvaluatableExpression(handle: number, resource: UriComponents, position: theia.Position,
token: theia.CancellationToken): Promise<theia.EvaluatableExpression | undefined> {
return this.withAdapter(handle, EvaluatableExpressionAdapter, adapter => adapter.provideEvaluatableExpression(URI.revive(resource), position, token), undefined);
}
// ### EvaluatableExpression Provider end

// ### Document Highlight Provider begin
registerDocumentHighlightProvider(selector: theia.DocumentSelector, provider: theia.DocumentHighlightProvider, pluginInfo: PluginInfo): theia.Disposable {
const callId = this.addNewAdapter(new DocumentHighlightAdapter(provider, this.documents));
Expand Down
32 changes: 32 additions & 0 deletions packages/plugin-ext/src/plugin/languages/evaluatable-expression.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// *****************************************************************************
// Copyright (C) 2022 EclipseSource 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 '@theia/core/shared/vscode-uri';
import * as theia from '@theia/plugin';
import { DocumentsExtImpl } from '../documents';

export class EvaluatableExpressionAdapter {

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

async provideEvaluatableExpression(resource: URI, position: theia.Position, token: theia.CancellationToken): Promise<theia.EvaluatableExpression | undefined> {
const document = this.documents.getDocument(resource);
const value = await this.provider.provideEvaluatableExpression(document, position, token);
return value;
}
}
5 changes: 5 additions & 0 deletions packages/plugin-ext/src/plugin/plugin-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import {
SignatureHelp,
SignatureHelpTriggerKind,
Hover,
EvaluatableExpression,
DocumentHighlightKind,
DocumentHighlight,
DocumentLink,
Expand Down Expand Up @@ -721,6 +722,9 @@ export function createAPIFactory(
registerHoverProvider(selector: theia.DocumentSelector, provider: theia.HoverProvider): theia.Disposable {
return languagesExt.registerHoverProvider(selector, provider, pluginToPluginInfo(plugin));
},
registerEvaluatableExpressionProvider(selector: theia.DocumentSelector, provider: theia.EvaluatableExpressionProvider): theia.Disposable {
return languagesExt.registerEvaluatableExpressionProvider(selector, provider, pluginToPluginInfo(plugin));
},
registerDocumentHighlightProvider(selector: theia.DocumentSelector, provider: theia.DocumentHighlightProvider): theia.Disposable {
return languagesExt.registerDocumentHighlightProvider(selector, provider, pluginToPluginInfo(plugin));
},
Expand Down Expand Up @@ -971,6 +975,7 @@ export function createAPIFactory(
SignatureHelp,
SignatureHelpTriggerKind,
Hover,
EvaluatableExpression,
DocumentHighlightKind,
DocumentHighlight,
DocumentLink,
Expand Down
17 changes: 17 additions & 0 deletions packages/plugin-ext/src/plugin/types-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,23 @@ export class Hover {
this.range = range;
}
}
@es5ClassCompat
export class EvaluatableExpression {

public range: Range;
public expression?: string;

constructor(
range: Range,
expression?: string
) {
if (!range) {
illegalArgument('range must be defined');
}
this.range = range;
this.expression = expression;
}
}

export enum DocumentHighlightKind {
Text = 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ export class LanguagesMainPluginMetrics extends LanguagesMainImpl {
super.provideHover(handle, model, position, token));
}

// protected override provideEvaluatableExpression(handle: number, model: monaco.editor.ITextModel, position: monaco.Position,
// token: monaco.CancellationToken): monaco.languages.ProviderResult<theia.EvaluatableExpression> {
// return this.pluginMetricsResolver.resolveRequest(this.handleToExtensionName(handle),
// vst.HoverRequest.type.method,
// super.provideEvaluatableExpression(handle, model, position, token));
// }

protected override provideDocumentHighlights(handle: number, model: monaco.editor.ITextModel, position: monaco.Position,
token: monaco.CancellationToken): monaco.languages.ProviderResult<monaco.languages.DocumentHighlight[]> {
return this.pluginMetricsResolver.resolveRequest(this.handleToExtensionName(handle),
Expand Down Expand Up @@ -254,6 +261,11 @@ export class LanguagesMainPluginMetrics extends LanguagesMainImpl {
super.$registerHoverProvider(handle, pluginInfo, selector);
}

override $registerEvaluatableExpressionProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[]): void {
this.registerPluginWithFeatureHandle(handle, pluginInfo.id);
super.$registerEvaluatableExpressionProvider(handle, pluginInfo, selector);
}

override $registerDocumentHighlightProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[]): void {
this.registerPluginWithFeatureHandle(handle, pluginInfo.id);
super.$registerDocumentHighlightProvider(handle, pluginInfo, selector);
Expand Down
46 changes: 46 additions & 0 deletions packages/plugin/src/theia.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8913,6 +8913,18 @@ export module '@theia/plugin' {
*/
export function registerHoverProvider(selector: DocumentSelector, provider: HoverProvider): Disposable;

/**
* Register a provider that locates evaluatable expressions in text documents.
* The editor will evaluate the expression in the active debug session and will show the result in the debug hover.
*
* If multiple providers are registered for a language an arbitrary provider will be used.
*
* @param selector A selector that defines the documents this provider is applicable to.
* @param provider An evaluatable expression provider.
* @return A {@link Disposable} that unregisters this provider when being disposed.
*/
export function registerEvaluatableExpressionProvider(selector: DocumentSelector, provider: EvaluatableExpressionProvider): Disposable;

/**
* Register a workspace symbol provider.
*
Expand Down Expand Up @@ -9205,6 +9217,40 @@ export module '@theia/plugin' {
provideHover(document: TextDocument, position: Position, token: CancellationToken | undefined): ProviderResult<Hover>;
}

/**
* An evaluatable expression represents additional information for an expression in a document. Evaluatable expressions are
* evaluated by a debugger or runtime and their result is rendered in a tooltip-like widget.
* @internal
*/
export interface EvaluatableExpression {
/**
* The range to which this expression applies.
*/
range: Range;
/**
* This expression overrides the expression extracted from the range.
*/
expression?: string;
}

/**
* The evaluatable expression provider interface defines the contract between extensions and
* the debug hover.
* @internal
*/
export interface EvaluatableExpressionProvider {
/**
* Provide a hover for the given position and document. Multiple hovers at the same
* position will be merged by the editor. A hover can have a range which defaults
* to the word range at the position when omitted.
*
* @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.
*/
provideEvaluatableExpression(document: model.ITextModel, position: theia.Position, token: CancellationToken): ProviderResult<EvaluatableExpression>;
}

/**
* A document highlight kind.
*/
Expand Down

0 comments on commit a28d87c

Please sign in to comment.