]> git.lizzy.rs Git - rust.git/blob - editors/code/src/config.ts
Merge #4501
[rust.git] / editors / code / src / config.ts
1 import * as vscode from 'vscode';
2 import { log } from "./util";
3
4 export type UpdatesChannel = "stable" | "nightly";
5
6 export const NIGHTLY_TAG = "nightly";
7
8 export class Config {
9     readonly extensionId = "matklad.rust-analyzer";
10
11     private readonly rootSection = "rust-analyzer";
12     private readonly requiresReloadOpts = [
13         "serverPath",
14         "cargo",
15         "procMacro",
16         "files",
17         "highlighting",
18         "updates.channel",
19         "lens.enable",
20         "lens.run",
21         "lens.debug",
22         "lens.implementations",
23     ]
24         .map(opt => `${this.rootSection}.${opt}`);
25
26     readonly package: {
27         version: string;
28         releaseTag: string | null;
29         enableProposedApi: boolean | undefined;
30     } = vscode.extensions.getExtension(this.extensionId)!.packageJSON;
31
32     readonly globalStoragePath: string;
33
34     constructor(ctx: vscode.ExtensionContext) {
35         this.globalStoragePath = ctx.globalStoragePath;
36         vscode.workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, ctx.subscriptions);
37         this.refreshLogging();
38     }
39
40     private refreshLogging() {
41         log.setEnabled(this.traceExtension);
42         log.debug(
43             "Extension version:", this.package.version,
44             "using configuration:", this.cfg
45         );
46     }
47
48     private async onDidChangeConfiguration(event: vscode.ConfigurationChangeEvent) {
49         this.refreshLogging();
50
51         const requiresReloadOpt = this.requiresReloadOpts.find(
52             opt => event.affectsConfiguration(opt)
53         );
54
55         if (!requiresReloadOpt) return;
56
57         const userResponse = await vscode.window.showInformationMessage(
58             `Changing "${requiresReloadOpt}" requires a reload`,
59             "Reload now"
60         );
61
62         if (userResponse === "Reload now") {
63             await vscode.commands.executeCommand("workbench.action.reloadWindow");
64         }
65     }
66
67     // We don't do runtime config validation here for simplicity. More on stackoverflow:
68     // https://stackoverflow.com/questions/60135780/what-is-the-best-way-to-type-check-the-configuration-for-vscode-extension
69
70     private get cfg(): vscode.WorkspaceConfiguration {
71         return vscode.workspace.getConfiguration(this.rootSection);
72     }
73
74     /**
75      * Beware that postfix `!` operator erases both `null` and `undefined`.
76      * This is why the following doesn't work as expected:
77      *
78      * ```ts
79      * const nullableNum = vscode
80      *  .workspace
81      *  .getConfiguration
82      *  .getConfiguration("rust-analyer")
83      *  .get<number | null>(path)!;
84      *
85      * // What happens is that type of `nullableNum` is `number` but not `null | number`:
86      * const fullFledgedNum: number = nullableNum;
87      * ```
88      * So this getter handles this quirk by not requiring the caller to use postfix `!`
89      */
90     private get<T>(path: string): T {
91         return this.cfg.get<T>(path)!;
92     }
93
94     get serverPath() { return this.get<null | string>("serverPath"); }
95     get channel() { return this.get<UpdatesChannel>("updates.channel"); }
96     get askBeforeDownload() { return this.get<boolean>("updates.askBeforeDownload"); }
97     get traceExtension() { return this.get<boolean>("trace.extension"); }
98
99     get inlayHints() {
100         return {
101             enable: this.get<boolean>("inlayHints.enable"),
102             typeHints: this.get<boolean>("inlayHints.typeHints"),
103             parameterHints: this.get<boolean>("inlayHints.parameterHints"),
104             chainingHints: this.get<boolean>("inlayHints.chainingHints"),
105             maxLength: this.get<null | number>("inlayHints.maxLength"),
106         };
107     }
108
109     get checkOnSave() {
110         return {
111             command: this.get<string>("checkOnSave.command"),
112         };
113     }
114
115     get debug() {
116         // "/rustc/<id>" used by suggestions only.
117         const { ["/rustc/<id>"]: _, ...sourceFileMap } = this.get<Record<string, string>>("debug.sourceFileMap");
118
119         return {
120             engine: this.get<string>("debug.engine"),
121             engineSettings: this.get<object>("debug.engineSettings"),
122             openUpDebugPane: this.get<boolean>("debug.openUpDebugPane"),
123             sourceFileMap: sourceFileMap
124         };
125     }
126
127     get lens() {
128         return {
129             enable: this.get<boolean>("lens.enable"),
130             run: this.get<boolean>("lens.run"),
131             debug: this.get<boolean>("lens.debug"),
132             implementations: this.get<boolean>("lens.implementations"),
133         };
134     }
135 }