Merge #3191

3191: Remove two stage constuction r=matklad a=matklad



Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 1189497..aaf2ef4 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -2,18 +2,14 @@
 import * as vscode from 'vscode';
 
 import { Config } from './config';
-import { ensureServerBinary } from './installation/server';
 import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed';
 
-export async function createClient(config: Config): Promise<null | lc.LanguageClient> {
+export async function createClient(config: Config, serverPath: string): Promise<lc.LanguageClient> {
     // '.' Is the fallback if no folder is open
     // TODO?: Workspace folders support Uri's (eg: file://test.txt).
     // It might be a good idea to test if the uri points to a file.
     const workspaceFolderPath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? '.';
 
-    const serverPath = await ensureServerBinary(config.serverSource);
-    if (!serverPath) return null;
-
     const run: lc.Executable = {
         command: serverPath,
         options: { cwd: workspaceFolderPath },
diff --git a/editors/code/src/commands/analyzer_status.ts b/editors/code/src/commands/analyzer_status.ts
index cfe7d1a..6631e8d 100644
--- a/editors/code/src/commands/analyzer_status.ts
+++ b/editors/code/src/commands/analyzer_status.ts
@@ -37,12 +37,10 @@
 
 class TextDocumentContentProvider
     implements vscode.TextDocumentContentProvider {
-    private ctx: Ctx;
     uri = vscode.Uri.parse('rust-analyzer-status://status');
     eventEmitter = new vscode.EventEmitter<vscode.Uri>();
 
-    constructor(ctx: Ctx) {
-        this.ctx = ctx;
+    constructor(private readonly ctx: Ctx) {
     }
 
     provideTextDocumentContent(
diff --git a/editors/code/src/commands/expand_macro.ts b/editors/code/src/commands/expand_macro.ts
index dcdde78..6fee6eb 100644
--- a/editors/code/src/commands/expand_macro.ts
+++ b/editors/code/src/commands/expand_macro.ts
@@ -42,12 +42,10 @@
 
 class TextDocumentContentProvider
     implements vscode.TextDocumentContentProvider {
-    private ctx: Ctx;
     uri = vscode.Uri.parse('rust-analyzer://expandMacro/[EXPANSION].rs');
     eventEmitter = new vscode.EventEmitter<vscode.Uri>();
 
-    constructor(ctx: Ctx) {
-        this.ctx = ctx;
+    constructor(private readonly ctx: Ctx) {
     }
 
     async provideTextDocumentContent(_uri: vscode.Uri): Promise<string> {
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts
index 7dde66a..2887c96 100644
--- a/editors/code/src/commands/syntax_tree.ts
+++ b/editors/code/src/commands/syntax_tree.ts
@@ -68,12 +68,10 @@
 
 class TextDocumentContentProvider
     implements vscode.TextDocumentContentProvider {
-    private ctx: Ctx;
     uri = vscode.Uri.parse('rust-analyzer://syntaxtree');
     eventEmitter = new vscode.EventEmitter<vscode.Uri>();
 
-    constructor(ctx: Ctx) {
-        this.ctx = ctx;
+    constructor(private readonly ctx: Ctx) {
     }
 
     provideTextDocumentContent(uri: vscode.Uri): vscode.ProviderResult<string> {
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index c06d8ac..dfc8aa7 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -1,43 +1,24 @@
 import * as vscode from 'vscode';
 import * as lc from 'vscode-languageclient';
-import { strict as assert } from "assert";
 
 import { Config } from './config';
 import { createClient } from './client';
 
 export class Ctx {
-    readonly config: Config;
-    // 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.
-    // FIXME: this actually needs syncronization of some kind (check how
-    // vscode deals with `deactivate()` call when extension has some work scheduled
-    // on the event loop to get a better picture of what we can do here)
-    client: lc.LanguageClient | null = null;
-    private extCtx: vscode.ExtensionContext;
+    private constructor(
+        readonly config: Config,
+        private readonly extCtx: vscode.ExtensionContext,
+        readonly client: lc.LanguageClient
+    ) {
 
-    constructor(extCtx: vscode.ExtensionContext) {
-        this.config = new Config(extCtx);
-        this.extCtx = extCtx;
     }
 
-    async startServer() {
-        assert(this.client == null);
-
-        const client = await createClient(this.config);
-        if (!client) {
-            throw new Error(
-                "Rust Analyzer Language Server is not available. " +
-                "Please, ensure its [proper installation](https://github.com/rust-analyzer/rust-analyzer/tree/master/docs/user#vs-code)."
-            );
-        }
-
-        this.pushCleanup(client.start());
+    static async create(config: Config, extCtx: vscode.ExtensionContext, serverPath: string): Promise<Ctx> {
+        const client = await createClient(config, serverPath);
+        const res = new Ctx(config, extCtx, client);
+        res.pushCleanup(client.start());
         await client.onReady();
-
-        this.client = client;
+        return res;
     }
 
     get activeRustEditor(): vscode.TextEditor | undefined {
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts
index 55bbd7f..f82df66 100644
--- a/editors/code/src/inlay_hints.ts
+++ b/editors/code/src/inlay_hints.ts
@@ -92,8 +92,6 @@
 
     async refresh() {
         if (!this.enabled) return;
-        console.log("freshin!");
-
         await Promise.all(this.allEditors.map(it => this.refreshEditor(it)));
     }
 
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 0bf2c48..0ad7ef1 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -5,21 +5,27 @@
 import { activateStatusDisplay } from './status_display';
 import { Ctx } from './ctx';
 import { activateHighlighting } from './highlighting';
+import { ensureServerBinary } from './installation/server';
+import { Config } from './config';
 
 let ctx: Ctx | undefined;
 
 export async function activate(context: vscode.ExtensionContext) {
-    ctx = new Ctx(context);
+    const config = new Config(context)
+
+    const serverPath = await ensureServerBinary(config.serverSource);
+    if (serverPath == null) {
+        throw new Error(
+            "Rust Analyzer Language Server is not available. " +
+            "Please, ensure its [proper installation](https://rust-analyzer.github.io/manual.html#installation)."
+        );
+    }
 
     // Note: we try to start the server before we activate type hints so that it
     // registers its `onDidChangeDocument` handler before us.
     //
     // This a horribly, horribly wrong way to deal with this problem.
-    try {
-        await ctx.startServer();
-    } catch (e) {
-        vscode.window.showErrorMessage(e.message);
-    }
+    ctx = await Ctx.create(config, context, serverPath);
 
     // Commands which invokes manually via command palette, shortcut, etc.
     ctx.registerCommand('reload', (ctx) => {