Skip to content

Commit

Permalink
fix #4909: hook monaco cancellation into plugin language services
Browse files Browse the repository at this point in the history
Signed-off-by: Anton Kosyakov <[email protected]>
  • Loading branch information
akosyakov committed Apr 15, 2019
1 parent a4a71ca commit c58678e
Show file tree
Hide file tree
Showing 26 changed files with 222 additions and 215 deletions.
60 changes: 33 additions & 27 deletions packages/plugin-ext/src/api/plugin-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -862,47 +862,53 @@ export interface ProcessTaskDto extends TaskDto, CommandProperties {
}

export interface LanguagesExt {
$provideCompletionItems(handle: number, resource: UriComponents, position: Position, context: CompletionContext): Promise<CompletionResultDto | undefined>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: Position, completion: Completion): Promise<Completion>;
$provideCompletionItems(handle: number, resource: UriComponents, position: Position,
context: CompletionContext, token?: CancellationToken): Promise<CompletionResultDto | undefined>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: Position, completion: Completion, token?: CancellationToken): Promise<Completion>;
$releaseCompletionItems(handle: number, id: number): void;
$provideImplementation(handle: number, resource: UriComponents, position: Position): Promise<Definition | DefinitionLink[] | undefined>;
$provideTypeDefinition(handle: number, resource: UriComponents, position: Position): Promise<Definition | DefinitionLink[] | undefined>;
$provideDefinition(handle: number, resource: UriComponents, position: Position): Promise<Definition | DefinitionLink[] | undefined>;
$provideReferences(handle: number, resource: UriComponents, position: Position, context: ReferenceContext): Promise<Location[] | undefined>;
$provideSignatureHelp(handle: number, resource: UriComponents, position: Position): Promise<SignatureHelp | undefined>;
$provideHover(handle: number, resource: UriComponents, position: Position): Promise<Hover | undefined>;
$provideDocumentHighlights(handle: number, resource: UriComponents, position: Position): Promise<DocumentHighlight[] | undefined>;
$provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: FormattingOptions): Promise<ModelSingleEditOperation[] | undefined>;
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: Range, options: FormattingOptions): Promise<ModelSingleEditOperation[] | undefined>;
$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>;
$provideReferences(handle: number, resource: UriComponents, position: Position, context: ReferenceContext, token?: CancellationToken): Promise<Location[] | undefined>;
$provideSignatureHelp(handle: number, resource: UriComponents, position: Position, token?: CancellationToken): Promise<SignatureHelp | undefined>;
$provideHover(handle: number, resource: UriComponents, position: Position, token?: CancellationToken): Promise<Hover | undefined>;
$provideDocumentHighlights(handle: number, resource: UriComponents, position: Position, token?: CancellationToken): Promise<DocumentHighlight[] | undefined>;
$provideDocumentFormattingEdits(handle: number, resource: UriComponents,
options: FormattingOptions, token?: CancellationToken): Promise<ModelSingleEditOperation[] | undefined>;
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: Range,
options: FormattingOptions, token?: CancellationToken): Promise<ModelSingleEditOperation[] | undefined>;
$provideOnTypeFormattingEdits(
handle: number,
resource: UriComponents,
position: Position,
ch: string,
options: FormattingOptions
options: FormattingOptions,
token?: CancellationToken
): Promise<ModelSingleEditOperation[] | undefined>;
$provideDocumentLinks(handle: number, resource: UriComponents): Promise<DocumentLink[] | undefined>;
$resolveDocumentLink(handle: number, link: DocumentLink): Promise<DocumentLink | undefined>;
$provideCodeLenses(handle: number, resource: UriComponents): Promise<CodeLensSymbol[] | undefined>;
$resolveCodeLens(handle: number, resource: UriComponents, symbol: CodeLensSymbol): Promise<CodeLensSymbol | undefined>;
$provideDocumentLinks(handle: number, resource: UriComponents, token?: CancellationToken): Promise<DocumentLink[] | undefined>;
$resolveDocumentLink(handle: number, link: DocumentLink, token?: CancellationToken): Promise<DocumentLink | undefined>;
$provideCodeLenses(handle: number, resource: UriComponents, token?: CancellationToken): Promise<CodeLensSymbol[] | undefined>;
$resolveCodeLens(handle: number, resource: UriComponents, symbol: CodeLensSymbol, token?: CancellationToken): Promise<CodeLensSymbol | undefined>;
$provideCodeActions(
handle: number,
resource: UriComponents,
rangeOrSelection: Range | Selection,
context: monaco.languages.CodeActionContext
context: monaco.languages.CodeActionContext,
token?: CancellationToken
): Promise<monaco.languages.CodeAction[]>;
$provideDocumentSymbols(handle: number, resource: UriComponents): Promise<DocumentSymbol[] | undefined>;
$provideWorkspaceSymbols(handle: number, query: string): PromiseLike<SymbolInformation[]>;
$resolveWorkspaceSymbol(handle: number, symbol: SymbolInformation): PromiseLike<SymbolInformation>;
$provideDocumentSymbols(handle: number, resource: UriComponents, token?: CancellationToken): Promise<DocumentSymbol[] | undefined>;
$provideWorkspaceSymbols(handle: number, query: string, token?: CancellationToken): PromiseLike<SymbolInformation[]>;
$resolveWorkspaceSymbol(handle: number, symbol: SymbolInformation, token?: CancellationToken): PromiseLike<SymbolInformation>;
$provideFoldingRange(
handle: number,
resource: UriComponents,
context: monaco.languages.FoldingContext
context: monaco.languages.FoldingContext,
token?: CancellationToken
): PromiseLike<monaco.languages.FoldingRange[] | undefined>;
$provideDocumentColors(handle: number, resource: UriComponents): PromiseLike<RawColorInfo[]>;
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: RawColorInfo): PromiseLike<ColorPresentation[]>;
$provideRenameEdits(handle: number, resource: UriComponents, position: Position, newName: string): PromiseLike<WorkspaceEditDto | undefined>;
$resolveRenameLocation(handle: number, resource: UriComponents, position: Position): PromiseLike<RenameLocation | undefined>;
$provideDocumentColors(handle: number, resource: UriComponents, token?: CancellationToken): PromiseLike<RawColorInfo[]>;
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: RawColorInfo, token?: CancellationToken): PromiseLike<ColorPresentation[]>;
$provideRenameEdits(handle: number, resource: UriComponents, position: Position, newName: string, token?: CancellationToken): PromiseLike<WorkspaceEditDto | undefined>;
$resolveRenameLocation(handle: number, resource: UriComponents, position: Position, token?: CancellationToken): PromiseLike<RenameLocation | undefined>;
}

export interface LanguagesMain {
Expand Down Expand Up @@ -1066,8 +1072,8 @@ export const MAIN_RPC_CONTEXT = {
};

export interface TasksExt {
$provideTasks(handle: number): Promise<TaskDto[] | undefined>;
$resolveTask(handle: number, task: TaskDto): Promise<TaskDto | undefined>;
$provideTasks(handle: number, token?: CancellationToken): Promise<TaskDto[] | undefined>;
$resolveTask(handle: number, task: TaskDto, token?: CancellationToken): Promise<TaskDto | undefined>;
$onDidStartTask(execution: TaskExecutionDto): void;
$onDidEndTask(id: number): void;
}
Expand Down
45 changes: 43 additions & 2 deletions packages/plugin-ext/src/api/rpc-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { Event } from '@theia/core/lib/common/event';
import { Deferred } from '@theia/core/lib/common/promise-util';
import VSCodeURI from 'vscode-uri';
import URI from '@theia/core/lib/common/uri';
import { CancellationToken, CancellationTokenSource } from 'vscode-languageserver-protocol';

export interface MessageConnection {
send(msg: {}): void;
Expand Down Expand Up @@ -64,6 +65,7 @@ export class RPCProtocolImpl implements RPCProtocol {
private readonly proxies: { [id: string]: any; };
private lastMessageId: number;
private readonly invokedHandlers: { [req: string]: Promise<any>; };
private readonly cancellationTokenSources: { [req: string]: CancellationTokenSource } = {};
private readonly pendingRPCReplies: { [msgId: string]: Deferred<any>; };
private readonly multiplexor: RPCMultiplexer;
private messageToSendHostId: string | undefined;
Expand Down Expand Up @@ -110,10 +112,20 @@ export class RPCProtocolImpl implements RPCProtocol {
if (this.isDisposed) {
return Promise.reject(canceled());
}
const cancellationToken: CancellationToken | undefined = args.length && CancellationToken.is(args[args.length - 1]) ? args.pop() : undefined;
if (cancellationToken && cancellationToken.isCancellationRequested) {
return Promise.reject(canceled());
}

const callId = String(++this.lastMessageId);
const result = new Deferred();

if (cancellationToken) {
cancellationToken.onCancellationRequested(() =>
this.multiplexor.send(MessageFactory.cancel(callId, this.messageToSendHostId))
);
}

this.pendingRPCReplies[callId] = result;
this.multiplexor.send(MessageFactory.request(callId, proxyId, methodName, args, this.messageToSendHostId));
return result.promise;
Expand Down Expand Up @@ -147,20 +159,35 @@ export class RPCProtocolImpl implements RPCProtocol {
case MessageType.ReplyErr:
this.receiveReplyErr(msg);
break;
case MessageType.Cancel:
this.receiveCancel(msg);
break;
}
}

private receiveCancel(msg: CancelMessage): void {
const cancellationTokenSource = this.cancellationTokenSources[msg.id];
if (cancellationTokenSource) {
cancellationTokenSource.cancel();
}
}

private receiveRequest(msg: RequestMessage): void {
const callId = msg.id;
const proxyId = msg.proxyId;

const tokenSource = new CancellationTokenSource();
this.cancellationTokenSources[callId] = tokenSource;
msg.args.push(tokenSource.token);
this.invokedHandlers[callId] = this.invokeHandler(proxyId, msg.method, msg.args);

this.invokedHandlers[callId].then(r => {
delete this.invokedHandlers[callId];
delete this.cancellationTokenSources[callId];
this.multiplexor.send(MessageFactory.replyOK(callId, r, this.messageToSendHostId));
}, err => {
delete this.invokedHandlers[callId];
delete this.cancellationTokenSources[callId];
this.multiplexor.send(MessageFactory.replyErr(callId, err, this.messageToSendHostId));
});
}
Expand Down Expand Up @@ -272,6 +299,14 @@ class RPCMultiplexer {

class MessageFactory {

static cancel(req: string, messageToSendHostId?: string): string {
let prefix = '';
if (messageToSendHostId) {
prefix = `"hostID":"${messageToSendHostId}",`;
}
return `{${prefix}"type":${MessageType.Cancel},"id":"${req}"}`;
}

public static request(req: string, rpcId: string, method: string, args: any[], messageToSendHostId?: string): string {
let prefix = '';
if (messageToSendHostId) {
Expand Down Expand Up @@ -368,7 +403,13 @@ function isSerializedObject(obj: any): obj is SerializedObject {
const enum MessageType {
Request = 1,
Reply = 2,
ReplyErr = 3
ReplyErr = 3,
Cancel = 4
}

class CancelMessage {
type: MessageType.Cancel;
id: string;
}

class RequestMessage {
Expand All @@ -391,7 +432,7 @@ class ReplyErrMessage {
err: SerializedError;
}

type RPCMessage = RequestMessage | ReplyMessage | ReplyErrMessage;
type RPCMessage = RequestMessage | ReplyMessage | ReplyErrMessage | CancelMessage;

export interface SerializedError {
readonly $isError: true;
Expand Down
Loading

0 comments on commit c58678e

Please sign in to comment.