-
Notifications
You must be signed in to change notification settings - Fork 378
Description
vscode-jsonrpc defines its own CancellationToken interface which is modeled after the .NET type by the same name. While this is a fine type to use, it's tucked inside an RPC library which doesn't make it particularly nice to use in other settings.
There is a cancellationtoken npm package that focuses just on such a token. It is a more capable token, offering throwIfCancelled(), timeouts, optional reasons for cancellation, an api to race a token against another promise, and combining multiple tokens into one as a race or as a union of all. While it can do everything the .NET version can, the API is designed to be more javascript-esque, so it feels more native.
Given these two options, in some libraries I would tend to prefer the latter token. But where I have to interop with vscode-jsonrpc this complicates the story and leads to potential bugs when the wrong token is given or expected.
Can vscode-jsonrpc be updated to support this cancellationtoken npm package?
In particular, I'm thinking you could do duck-type checking to detect which kind it is and work with both. When creating a token to pass to RPC servers, perhaps you could create a token which fulfills the API obligations of both so that the RPC server can be implemented to receive either one.
Here are adapters I wrote that makes either token look like the other, to demonstrate how simple this is. Note that creating a union type to provide to RPC server methods would probably be more complicated.
import CancellationToken from 'cancellationtoken';
import {
CancellationToken as vscodeCancellationToken,
CancellationTokenSource as vscodeCancellationTokenSource,
} from 'vscode-jsonrpc';
export class CancellationTokenAdapters {
/** Tests whether an object satisfies the {@link vscodeCancellationToken} interface. */
static IsVSCode(value: any): value is vscodeCancellationToken {
return vscodeCancellationToken.is(value);
}
/** Tests whether an object satisfies the {@link CancellationToken} interface. */
static IsCancellationToken(value: any): value is CancellationToken {
return value
&& typeof value.throwIfCancelled === 'function'
&& typeof value.onCancelled === 'function'
&& value.isCancelled !== undefined
&& value.canBeCancelled !== undefined;
}
/** Returns a {@link CancellationToken} that is linked to the given {@link vscodeCancellationToken}. */
static VSCodeToCancellationToken(cancellationToken: vscodeCancellationToken): CancellationToken {
if (cancellationToken.isCancellationRequested) {
return CancellationToken.CANCELLED;
} else if (cancellationToken === vscodeCancellationToken.None) {
return CancellationToken.CONTINUE;
} else {
const result = CancellationToken.create();
cancellationToken.onCancellationRequested(_ => result.cancel());
return result.token;
}
}
/** Returns a {@link vscodeCancellationToken} that is linked to the given {@link CancellationToken}. */
static CancellationTokenToVSCode(cancellationToken: CancellationToken): vscodeCancellationToken {
if (cancellationToken.isCancelled) {
return vscodeCancellationToken.Cancelled;
} else if (cancellationToken.canBeCancelled) {
const cts = new vscodeCancellationTokenSource();
cancellationToken.onCancelled(_ => cts.cancel());
return cts.token;
} else {
return vscodeCancellationToken.None;
}
}
}