2019-12-30 13:42:59 +00:00
import * as vscode from 'vscode' ;
import * as lc from 'vscode-languageclient' ;
2019-12-30 19:46:14 +00:00
import { Config } from './config' ;
2019-12-31 17:55:34 +00:00
import { createClient } from './client' ;
2019-12-30 13:42:59 +00:00
export class Ctx {
2019-12-31 16:34:52 +00:00
readonly config : Config ;
2019-12-31 17:14:00 +00:00
// Because we have "reload server" action, various listeners **will** face a
// situation where the client is not ready yet, and should be prepared to
// deal with it.
//
// Ideally, this should be replaced with async getter though.
2019-12-31 17:55:34 +00:00
client : lc.LanguageClient | null = null ;
2019-12-30 14:11:30 +00:00
private extCtx : vscode.ExtensionContext ;
2019-12-31 17:14:00 +00:00
private onDidRestartHooks : Array < ( client : lc.LanguageClient ) = > void > = [ ] ;
2019-12-30 13:42:59 +00:00
constructor ( extCtx : vscode.ExtensionContext ) {
2019-12-31 17:55:34 +00:00
this . config = new Config ( extCtx ) ;
2019-12-30 14:11:30 +00:00
this . extCtx = extCtx ;
2019-12-30 13:42:59 +00:00
}
2019-12-31 17:14:00 +00:00
async restartServer() {
2020-02-02 19:12:59 +00:00
const old = this . client ;
2019-12-31 17:14:00 +00:00
if ( old ) {
2019-12-31 17:55:34 +00:00
await old . stop ( ) ;
2019-12-31 17:14:00 +00:00
}
this . client = null ;
const client = createClient ( this . config ) ;
this . pushCleanup ( client . start ( ) ) ;
await client . onReady ( ) ;
2019-12-31 17:55:34 +00:00
this . client = client ;
2019-12-31 17:14:00 +00:00
for ( const hook of this . onDidRestartHooks ) {
2019-12-31 17:55:34 +00:00
hook ( client ) ;
2019-12-31 17:14:00 +00:00
}
2019-12-30 13:42:59 +00:00
}
2019-12-30 14:20:13 +00:00
get activeRustEditor ( ) : vscode . TextEditor | undefined {
const editor = vscode . window . activeTextEditor ;
return editor && editor . document . languageId === 'rust'
? editor
: undefined ;
}
2019-12-30 14:11:30 +00:00
registerCommand ( name : string , factory : ( ctx : Ctx ) = > Cmd ) {
const fullName = ` rust-analyzer. ${ name } ` ;
2019-12-30 13:42:59 +00:00
const cmd = factory ( this ) ;
const d = vscode . commands . registerCommand ( fullName , cmd ) ;
this . pushCleanup ( d ) ;
}
2019-12-30 15:43:34 +00:00
overrideCommand ( name : string , factory : ( ctx : Ctx ) = > Cmd ) {
const defaultCmd = ` default: ${ name } ` ;
const override = factory ( this ) ;
const original = ( . . . args : any [ ] ) = >
vscode . commands . executeCommand ( defaultCmd , . . . args ) ;
try {
const d = vscode . commands . registerCommand (
name ,
async ( . . . args : any [ ] ) = > {
if ( ! ( await override ( . . . args ) ) ) {
return await original ( . . . args ) ;
}
} ,
) ;
this . pushCleanup ( d ) ;
} catch ( _ ) {
vscode . window . showWarningMessage (
'Enhanced typing feature is disabled because of incompatibility with VIM extension, consider turning off rust-analyzer.enableEnhancedTyping: https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/README.md#settings' ,
) ;
}
}
2019-12-30 18:05:41 +00:00
get subscriptions ( ) : { dispose ( ) : any } [ ] {
return this . extCtx . subscriptions ;
}
2019-12-30 13:42:59 +00:00
pushCleanup ( d : { dispose ( ) : any } ) {
2019-12-30 14:11:30 +00:00
this . extCtx . subscriptions . push ( d ) ;
2019-12-30 13:42:59 +00:00
}
2019-12-30 21:18:16 +00:00
2019-12-31 17:14:00 +00:00
onDidRestart ( hook : ( client : lc.LanguageClient ) = > void ) {
2019-12-31 17:55:34 +00:00
this . onDidRestartHooks . push ( hook ) ;
2019-12-31 16:22:43 +00:00
}
2019-12-30 13:42:59 +00:00
}
2019-12-30 13:53:43 +00:00
export type Cmd = ( . . . args : any [ ] ) = > any ;
2019-12-30 21:53:21 +00:00
2019-12-31 17:14:00 +00:00
export async function sendRequestWithRetry < R > (
client : lc.LanguageClient ,
method : string ,
param : any ,
token? : vscode.CancellationToken ,
) : Promise < R > {
for ( const delay of [ 2 , 4 , 6 , 8 , 10 , null ] ) {
try {
return await ( token ? client . sendRequest ( method , param , token ) : client . sendRequest ( method , param ) ) ;
} catch ( e ) {
if (
e . code === lc . ErrorCodes . ContentModified &&
delay !== null
) {
await sleep ( 10 * ( 1 << delay ) ) ;
continue ;
}
throw e ;
}
}
throw 'unreachable' ;
}
2019-12-30 22:12:33 +00:00
const sleep = ( ms : number ) = > new Promise ( resolve = > setTimeout ( resolve , ms ) ) ;