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