diff options
Diffstat (limited to 'editors/code/src/util.ts')
-rw-r--r-- | editors/code/src/util.ts | 64 |
1 files changed, 54 insertions, 10 deletions
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 352ef9162..970fedb37 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts | |||
@@ -2,6 +2,7 @@ import * as lc from "vscode-languageclient"; | |||
2 | import * as vscode from "vscode"; | 2 | import * as vscode from "vscode"; |
3 | import { strict as nativeAssert } from "assert"; | 3 | import { strict as nativeAssert } from "assert"; |
4 | import { spawnSync } from "child_process"; | 4 | import { spawnSync } from "child_process"; |
5 | import { inspect } from "util"; | ||
5 | 6 | ||
6 | export function assert(condition: boolean, explanation: string): asserts condition { | 7 | export function assert(condition: boolean, explanation: string): asserts condition { |
7 | try { | 8 | try { |
@@ -14,22 +15,46 @@ export function assert(condition: boolean, explanation: string): asserts conditi | |||
14 | 15 | ||
15 | export const log = new class { | 16 | export const log = new class { |
16 | private enabled = true; | 17 | private enabled = true; |
18 | private readonly output = vscode.window.createOutputChannel("Rust Analyzer Client"); | ||
17 | 19 | ||
18 | setEnabled(yes: boolean): void { | 20 | setEnabled(yes: boolean): void { |
19 | log.enabled = yes; | 21 | log.enabled = yes; |
20 | } | 22 | } |
21 | 23 | ||
22 | debug(message?: any, ...optionalParams: any[]): void { | 24 | // Hint: the type [T, ...T[]] means a non-empty array |
25 | debug(...msg: [unknown, ...unknown[]]): void { | ||
23 | if (!log.enabled) return; | 26 | if (!log.enabled) return; |
24 | // eslint-disable-next-line no-console | 27 | log.write("DEBUG", ...msg); |
25 | console.log(message, ...optionalParams); | 28 | log.output.toString(); |
26 | } | 29 | } |
27 | 30 | ||
28 | error(message?: any, ...optionalParams: any[]): void { | 31 | info(...msg: [unknown, ...unknown[]]): void { |
29 | if (!log.enabled) return; | 32 | log.write("INFO", ...msg); |
33 | } | ||
34 | |||
35 | warn(...msg: [unknown, ...unknown[]]): void { | ||
36 | debugger; | ||
37 | log.write("WARN", ...msg); | ||
38 | } | ||
39 | |||
40 | error(...msg: [unknown, ...unknown[]]): void { | ||
30 | debugger; | 41 | debugger; |
31 | // eslint-disable-next-line no-console | 42 | log.write("ERROR", ...msg); |
32 | console.error(message, ...optionalParams); | 43 | log.output.show(true); |
44 | } | ||
45 | |||
46 | private write(label: string, ...messageParts: unknown[]): void { | ||
47 | const message = messageParts.map(log.stringify).join(" "); | ||
48 | const dateTime = new Date().toLocaleString(); | ||
49 | log.output.appendLine(`${label} [${dateTime}]: ${message}`); | ||
50 | } | ||
51 | |||
52 | private stringify(val: unknown): string { | ||
53 | if (typeof val === "string") return val; | ||
54 | return inspect(val, { | ||
55 | colors: false, | ||
56 | depth: 6, // heuristic | ||
57 | }); | ||
33 | } | 58 | } |
34 | }; | 59 | }; |
35 | 60 | ||
@@ -47,7 +72,7 @@ export async function sendRequestWithRetry<TParam, TRet>( | |||
47 | ); | 72 | ); |
48 | } catch (error) { | 73 | } catch (error) { |
49 | if (delay === null) { | 74 | if (delay === null) { |
50 | log.error("LSP request timed out", { method: reqType.method, param, error }); | 75 | log.warn("LSP request timed out", { method: reqType.method, param, error }); |
51 | throw error; | 76 | throw error; |
52 | } | 77 | } |
53 | 78 | ||
@@ -56,7 +81,7 @@ export async function sendRequestWithRetry<TParam, TRet>( | |||
56 | } | 81 | } |
57 | 82 | ||
58 | if (error.code !== lc.ErrorCodes.ContentModified) { | 83 | if (error.code !== lc.ErrorCodes.ContentModified) { |
59 | log.error("LSP request failed", { method: reqType.method, param, error }); | 84 | log.warn("LSP request failed", { method: reqType.method, param, error }); |
60 | throw error; | 85 | throw error; |
61 | } | 86 | } |
62 | 87 | ||
@@ -90,7 +115,8 @@ export function isValidExecutable(path: string): boolean { | |||
90 | 115 | ||
91 | const res = spawnSync(path, ["--version"], { encoding: 'utf8' }); | 116 | const res = spawnSync(path, ["--version"], { encoding: 'utf8' }); |
92 | 117 | ||
93 | log.debug(res, "--version output:", res.output); | 118 | const printOutput = res.error && (res.error as any).code !== 'ENOENT' ? log.warn : log.debug; |
119 | printOutput(path, "--version:", res); | ||
94 | 120 | ||
95 | return res.status === 0; | 121 | return res.status === 0; |
96 | } | 122 | } |
@@ -99,3 +125,21 @@ export function isValidExecutable(path: string): boolean { | |||
99 | export function setContextValue(key: string, value: any): Thenable<void> { | 125 | export function setContextValue(key: string, value: any): Thenable<void> { |
100 | return vscode.commands.executeCommand('setContext', key, value); | 126 | return vscode.commands.executeCommand('setContext', key, value); |
101 | } | 127 | } |
128 | |||
129 | /** | ||
130 | * Returns a higher-order function that caches the results of invoking the | ||
131 | * underlying function. | ||
132 | */ | ||
133 | export function memoize<Ret, TThis, Param extends string>(func: (this: TThis, arg: Param) => Ret) { | ||
134 | const cache = new Map<string, Ret>(); | ||
135 | |||
136 | return function(this: TThis, arg: Param) { | ||
137 | const cached = cache.get(arg); | ||
138 | if (cached) return cached; | ||
139 | |||
140 | const result = func.call(this, arg); | ||
141 | cache.set(arg, result); | ||
142 | |||
143 | return result; | ||
144 | }; | ||
145 | } | ||