diff options
author | Zac Pullar-Strecker <[email protected]> | 2020-07-31 03:12:44 +0100 |
---|---|---|
committer | Zac Pullar-Strecker <[email protected]> | 2020-07-31 03:12:44 +0100 |
commit | f05d7b41a719d848844b054a16477b29d0f063c6 (patch) | |
tree | 0a8a0946e8aef2ce64d4c13d0035ba41cce2daf3 /editors/code/src | |
parent | 73ff610e41959e3e7c78a2b4b25b086883132956 (diff) | |
parent | 6b7cb8b5ab539fc4333ce34bc29bf77c976f232a (diff) |
Merge remote-tracking branch 'upstream/master' into 503-hover-doc-links
Hasn't fixed tests yet.
Diffstat (limited to 'editors/code/src')
-rw-r--r-- | editors/code/src/client.ts | 8 | ||||
-rw-r--r-- | editors/code/src/commands.ts | 51 | ||||
-rw-r--r-- | editors/code/src/config.ts | 14 | ||||
-rw-r--r-- | editors/code/src/ctx.ts | 43 | ||||
-rw-r--r-- | editors/code/src/debug.ts | 16 | ||||
-rw-r--r-- | editors/code/src/lsp_ext.ts | 10 | ||||
-rw-r--r-- | editors/code/src/main.ts | 54 | ||||
-rw-r--r-- | editors/code/src/net.ts | 21 | ||||
-rw-r--r-- | editors/code/src/persistent_state.ts | 2 | ||||
-rw-r--r-- | editors/code/src/run.ts | 31 | ||||
-rw-r--r-- | editors/code/src/snippets.ts | 4 | ||||
-rw-r--r-- | editors/code/src/util.ts | 45 |
12 files changed, 238 insertions, 61 deletions
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 65ad573d8..18948cb3c 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts | |||
@@ -41,6 +41,7 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
41 | const clientOptions: lc.LanguageClientOptions = { | 41 | const clientOptions: lc.LanguageClientOptions = { |
42 | documentSelector: [{ scheme: 'file', language: 'rust' }], | 42 | documentSelector: [{ scheme: 'file', language: 'rust' }], |
43 | initializationOptions: vscode.workspace.getConfiguration("rust-analyzer"), | 43 | initializationOptions: vscode.workspace.getConfiguration("rust-analyzer"), |
44 | diagnosticCollectionName: "rustc", | ||
44 | traceOutputChannel, | 45 | traceOutputChannel, |
45 | middleware: { | 46 | middleware: { |
46 | // Workaround for https://github.com/microsoft/vscode-languageserver-node/issues/576 | 47 | // Workaround for https://github.com/microsoft/vscode-languageserver-node/issues/576 |
@@ -66,7 +67,7 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
66 | return Promise.resolve(null); | 67 | return Promise.resolve(null); |
67 | }); | 68 | }); |
68 | }, | 69 | }, |
69 | // Using custom handling of CodeActions where each code action is resloved lazily | 70 | // Using custom handling of CodeActions where each code action is resolved lazily |
70 | // That's why we are not waiting for any command or edits | 71 | // That's why we are not waiting for any command or edits |
71 | async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { | 72 | async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { |
72 | const params: lc.CodeActionParams = { | 73 | const params: lc.CodeActionParams = { |
@@ -87,7 +88,8 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
87 | continue; | 88 | continue; |
88 | } | 89 | } |
89 | assert(isCodeActionWithoutEditsAndCommands(item), "We don't expect edits or commands here"); | 90 | assert(isCodeActionWithoutEditsAndCommands(item), "We don't expect edits or commands here"); |
90 | const action = new vscode.CodeAction(item.title); | 91 | const kind = client.protocol2CodeConverter.asCodeActionKind((item as any).kind); |
92 | const action = new vscode.CodeAction(item.title, kind); | ||
91 | const group = (item as any).group; | 93 | const group = (item as any).group; |
92 | const id = (item as any).id; | 94 | const id = (item as any).id; |
93 | const resolveParams: ra.ResolveCodeActionParams = { | 95 | const resolveParams: ra.ResolveCodeActionParams = { |
@@ -116,6 +118,7 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
116 | result[index] = items[0]; | 118 | result[index] = items[0]; |
117 | } else { | 119 | } else { |
118 | const action = new vscode.CodeAction(group); | 120 | const action = new vscode.CodeAction(group); |
121 | action.kind = items[0].kind; | ||
119 | action.command = { | 122 | action.command = { |
120 | command: "rust-analyzer.applyActionGroup", | 123 | command: "rust-analyzer.applyActionGroup", |
121 | title: "", | 124 | title: "", |
@@ -161,6 +164,7 @@ class ExperimentalFeatures implements lc.StaticFeature { | |||
161 | caps.codeActionGroup = true; | 164 | caps.codeActionGroup = true; |
162 | caps.resolveCodeAction = true; | 165 | caps.resolveCodeAction = true; |
163 | caps.hoverActions = true; | 166 | caps.hoverActions = true; |
167 | caps.statusNotification = true; | ||
164 | capabilities.experimental = caps; | 168 | capabilities.experimental = caps; |
165 | } | 169 | } |
166 | initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { | 170 | initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { |
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 0e78f5101..d0faf4745 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts | |||
@@ -55,6 +55,38 @@ export function analyzerStatus(ctx: Ctx): Cmd { | |||
55 | }; | 55 | }; |
56 | } | 56 | } |
57 | 57 | ||
58 | export function memoryUsage(ctx: Ctx): Cmd { | ||
59 | const tdcp = new class implements vscode.TextDocumentContentProvider { | ||
60 | readonly uri = vscode.Uri.parse('rust-analyzer-memory://memory'); | ||
61 | readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>(); | ||
62 | |||
63 | provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> { | ||
64 | if (!vscode.window.activeTextEditor) return ''; | ||
65 | |||
66 | return ctx.client.sendRequest(ra.memoryUsage, null).then((mem) => { | ||
67 | return 'Per-query memory usage:\n' + mem + '\n(note: database has been cleared)'; | ||
68 | }); | ||
69 | } | ||
70 | |||
71 | get onDidChange(): vscode.Event<vscode.Uri> { | ||
72 | return this.eventEmitter.event; | ||
73 | } | ||
74 | }(); | ||
75 | |||
76 | ctx.pushCleanup( | ||
77 | vscode.workspace.registerTextDocumentContentProvider( | ||
78 | 'rust-analyzer-memory', | ||
79 | tdcp, | ||
80 | ), | ||
81 | ); | ||
82 | |||
83 | return async () => { | ||
84 | tdcp.eventEmitter.fire(tdcp.uri); | ||
85 | const document = await vscode.workspace.openTextDocument(tdcp.uri); | ||
86 | return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true); | ||
87 | }; | ||
88 | } | ||
89 | |||
58 | export function matchingBrace(ctx: Ctx): Cmd { | 90 | export function matchingBrace(ctx: Ctx): Cmd { |
59 | return async () => { | 91 | return async () => { |
60 | const editor = ctx.activeRustEditor; | 92 | const editor = ctx.activeRustEditor; |
@@ -153,15 +185,22 @@ export function parentModule(ctx: Ctx): Cmd { | |||
153 | 185 | ||
154 | export function ssr(ctx: Ctx): Cmd { | 186 | export function ssr(ctx: Ctx): Cmd { |
155 | return async () => { | 187 | return async () => { |
188 | const editor = vscode.window.activeTextEditor; | ||
156 | const client = ctx.client; | 189 | const client = ctx.client; |
157 | if (!client) return; | 190 | if (!editor || !client) return; |
191 | |||
192 | const position = editor.selection.active; | ||
193 | const selections = editor.selections; | ||
194 | const textDocument = { uri: editor.document.uri.toString() }; | ||
158 | 195 | ||
159 | const options: vscode.InputBoxOptions = { | 196 | const options: vscode.InputBoxOptions = { |
160 | value: "() ==>> ()", | 197 | value: "() ==>> ()", |
161 | prompt: "Enter request, for example 'Foo($a) ==> Foo::new($a)' ", | 198 | prompt: "Enter request, for example 'Foo($a) ==> Foo::new($a)' ", |
162 | validateInput: async (x: string) => { | 199 | validateInput: async (x: string) => { |
163 | try { | 200 | try { |
164 | await client.sendRequest(ra.ssr, { query: x, parseOnly: true }); | 201 | await client.sendRequest(ra.ssr, { |
202 | query: x, parseOnly: true, textDocument, position, selections, | ||
203 | }); | ||
165 | } catch (e) { | 204 | } catch (e) { |
166 | return e.toString(); | 205 | return e.toString(); |
167 | } | 206 | } |
@@ -176,7 +215,9 @@ export function ssr(ctx: Ctx): Cmd { | |||
176 | title: "Structured search replace in progress...", | 215 | title: "Structured search replace in progress...", |
177 | cancellable: false, | 216 | cancellable: false, |
178 | }, async (_progress, _token) => { | 217 | }, async (_progress, _token) => { |
179 | const edit = await client.sendRequest(ra.ssr, { query: request, parseOnly: false }); | 218 | const edit = await client.sendRequest(ra.ssr, { |
219 | query: request, parseOnly: false, textDocument, position, selections, | ||
220 | }); | ||
180 | 221 | ||
181 | await vscode.workspace.applyEdit(client.protocol2CodeConverter.asWorkspaceEdit(edit)); | 222 | await vscode.workspace.applyEdit(client.protocol2CodeConverter.asWorkspaceEdit(edit)); |
182 | }); | 223 | }); |
@@ -330,8 +371,8 @@ export function expandMacro(ctx: Ctx): Cmd { | |||
330 | }; | 371 | }; |
331 | } | 372 | } |
332 | 373 | ||
333 | export function collectGarbage(ctx: Ctx): Cmd { | 374 | export function reloadWorkspace(ctx: Ctx): Cmd { |
334 | return async () => ctx.client.sendRequest(ra.collectGarbage, null); | 375 | return async () => ctx.client.sendRequest(ra.reloadWorkspace, null); |
335 | } | 376 | } |
336 | 377 | ||
337 | export function showReferences(ctx: Ctx): Cmd { | 378 | export function showReferences(ctx: Ctx): Cmd { |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index fc95a7de6..033b04b60 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -5,6 +5,8 @@ export type UpdatesChannel = "stable" | "nightly"; | |||
5 | 5 | ||
6 | export const NIGHTLY_TAG = "nightly"; | 6 | export const NIGHTLY_TAG = "nightly"; |
7 | 7 | ||
8 | export type RunnableEnvCfg = undefined | Record<string, string> | { mask?: string; env: Record<string, string> }[]; | ||
9 | |||
8 | export class Config { | 10 | export class Config { |
9 | readonly extensionId = "matklad.rust-analyzer"; | 11 | readonly extensionId = "matklad.rust-analyzer"; |
10 | 12 | ||
@@ -37,10 +39,10 @@ export class Config { | |||
37 | 39 | ||
38 | private refreshLogging() { | 40 | private refreshLogging() { |
39 | log.setEnabled(this.traceExtension); | 41 | log.setEnabled(this.traceExtension); |
40 | log.debug( | 42 | log.info("Extension version:", this.package.version); |
41 | "Extension version:", this.package.version, | 43 | |
42 | "using configuration:", this.cfg | 44 | const cfg = Object.entries(this.cfg).filter(([_, val]) => !(val instanceof Function)); |
43 | ); | 45 | log.info("Using configuration", Object.fromEntries(cfg)); |
44 | } | 46 | } |
45 | 47 | ||
46 | private async onDidChangeConfiguration(event: vscode.ConfigurationChangeEvent) { | 48 | private async onDidChangeConfiguration(event: vscode.ConfigurationChangeEvent) { |
@@ -114,6 +116,10 @@ export class Config { | |||
114 | return this.get<string | undefined>("cargoRunner"); | 116 | return this.get<string | undefined>("cargoRunner"); |
115 | } | 117 | } |
116 | 118 | ||
119 | get runnableEnv() { | ||
120 | return this.get<RunnableEnvCfg>("runnableEnv"); | ||
121 | } | ||
122 | |||
117 | get debug() { | 123 | get debug() { |
118 | // "/rustc/<id>" used by suggestions only. | 124 | // "/rustc/<id>" used by suggestions only. |
119 | const { ["/rustc/<id>"]: _, ...sourceFileMap } = this.get<Record<string, string>>("debug.sourceFileMap"); | 125 | const { ["/rustc/<id>"]: _, ...sourceFileMap } = this.get<Record<string, string>>("debug.sourceFileMap"); |
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 41df11991..6e767babf 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts | |||
@@ -1,9 +1,11 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as lc from 'vscode-languageclient'; |
3 | import * as ra from './lsp_ext'; | ||
3 | 4 | ||
4 | import { Config } from './config'; | 5 | import { Config } from './config'; |
5 | import { createClient } from './client'; | 6 | import { createClient } from './client'; |
6 | import { isRustEditor, RustEditor } from './util'; | 7 | import { isRustEditor, RustEditor } from './util'; |
8 | import { Status } from './lsp_ext'; | ||
7 | 9 | ||
8 | export class Ctx { | 10 | export class Ctx { |
9 | private constructor( | 11 | private constructor( |
@@ -11,6 +13,7 @@ export class Ctx { | |||
11 | private readonly extCtx: vscode.ExtensionContext, | 13 | private readonly extCtx: vscode.ExtensionContext, |
12 | readonly client: lc.LanguageClient, | 14 | readonly client: lc.LanguageClient, |
13 | readonly serverPath: string, | 15 | readonly serverPath: string, |
16 | readonly statusBar: vscode.StatusBarItem, | ||
14 | ) { | 17 | ) { |
15 | 18 | ||
16 | } | 19 | } |
@@ -22,9 +25,18 @@ export class Ctx { | |||
22 | cwd: string, | 25 | cwd: string, |
23 | ): Promise<Ctx> { | 26 | ): Promise<Ctx> { |
24 | const client = createClient(serverPath, cwd); | 27 | const client = createClient(serverPath, cwd); |
25 | const res = new Ctx(config, extCtx, client, serverPath); | 28 | |
29 | const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); | ||
30 | extCtx.subscriptions.push(statusBar); | ||
31 | statusBar.text = "rust-analyzer"; | ||
32 | statusBar.tooltip = "ready"; | ||
33 | statusBar.show(); | ||
34 | |||
35 | const res = new Ctx(config, extCtx, client, serverPath, statusBar); | ||
36 | |||
26 | res.pushCleanup(client.start()); | 37 | res.pushCleanup(client.start()); |
27 | await client.onReady(); | 38 | await client.onReady(); |
39 | client.onNotification(ra.status, (status) => res.setStatus(status)); | ||
28 | return res; | 40 | return res; |
29 | } | 41 | } |
30 | 42 | ||
@@ -54,6 +66,35 @@ export class Ctx { | |||
54 | return this.extCtx.subscriptions; | 66 | return this.extCtx.subscriptions; |
55 | } | 67 | } |
56 | 68 | ||
69 | setStatus(status: Status) { | ||
70 | switch (status) { | ||
71 | case "loading": | ||
72 | this.statusBar.text = "$(sync~spin) rust-analyzer"; | ||
73 | this.statusBar.tooltip = "Loading the project"; | ||
74 | this.statusBar.command = undefined; | ||
75 | this.statusBar.color = undefined; | ||
76 | break; | ||
77 | case "ready": | ||
78 | this.statusBar.text = "rust-analyzer"; | ||
79 | this.statusBar.tooltip = "Ready"; | ||
80 | this.statusBar.command = undefined; | ||
81 | this.statusBar.color = undefined; | ||
82 | break; | ||
83 | case "invalid": | ||
84 | this.statusBar.text = "$(error) rust-analyzer"; | ||
85 | this.statusBar.tooltip = "Failed to load the project"; | ||
86 | this.statusBar.command = undefined; | ||
87 | this.statusBar.color = new vscode.ThemeColor("notificationsErrorIcon.foreground"); | ||
88 | break; | ||
89 | case "needsReload": | ||
90 | this.statusBar.text = "$(warning) rust-analyzer"; | ||
91 | this.statusBar.tooltip = "Click to reload"; | ||
92 | this.statusBar.command = "rust-analyzer.reloadWorkspace"; | ||
93 | this.statusBar.color = new vscode.ThemeColor("notificationsWarningIcon.foreground"); | ||
94 | break; | ||
95 | } | ||
96 | } | ||
97 | |||
57 | pushCleanup(d: Disposable) { | 98 | pushCleanup(d: Disposable) { |
58 | this.extCtx.subscriptions.push(d); | 99 | this.extCtx.subscriptions.push(d); |
59 | } | 100 | } |
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index 61c12dbe0..bd92c5b6d 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts | |||
@@ -5,9 +5,10 @@ import * as ra from './lsp_ext'; | |||
5 | 5 | ||
6 | import { Cargo } from './toolchain'; | 6 | import { Cargo } from './toolchain'; |
7 | import { Ctx } from "./ctx"; | 7 | import { Ctx } from "./ctx"; |
8 | import { prepareEnv } from "./run"; | ||
8 | 9 | ||
9 | const debugOutput = vscode.window.createOutputChannel("Debug"); | 10 | const debugOutput = vscode.window.createOutputChannel("Debug"); |
10 | type DebugConfigProvider = (config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>) => vscode.DebugConfiguration; | 11 | type DebugConfigProvider = (config: ra.Runnable, executable: string, env: Record<string, string>, sourceFileMap?: Record<string, string>) => vscode.DebugConfiguration; |
11 | 12 | ||
12 | export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise<void> { | 13 | export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise<void> { |
13 | const scope = ctx.activeRustEditor?.document.uri; | 14 | const scope = ctx.activeRustEditor?.document.uri; |
@@ -92,7 +93,8 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v | |||
92 | } | 93 | } |
93 | 94 | ||
94 | const executable = await getDebugExecutable(runnable); | 95 | const executable = await getDebugExecutable(runnable); |
95 | const debugConfig = knownEngines[debugEngine.id](runnable, simplifyPath(executable), debugOptions.sourceFileMap); | 96 | const env = prepareEnv(runnable, ctx.config.runnableEnv); |
97 | const debugConfig = knownEngines[debugEngine.id](runnable, simplifyPath(executable), env, debugOptions.sourceFileMap); | ||
96 | if (debugConfig.type in debugOptions.engineSettings) { | 98 | if (debugConfig.type in debugOptions.engineSettings) { |
97 | const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; | 99 | const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; |
98 | for (var key in settingsMap) { | 100 | for (var key in settingsMap) { |
@@ -121,7 +123,7 @@ async function getDebugExecutable(runnable: ra.Runnable): Promise<string> { | |||
121 | return executable; | 123 | return executable; |
122 | } | 124 | } |
123 | 125 | ||
124 | function getLldbDebugConfig(runnable: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration { | 126 | function getLldbDebugConfig(runnable: ra.Runnable, executable: string, env: Record<string, string>, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration { |
125 | return { | 127 | return { |
126 | type: "lldb", | 128 | type: "lldb", |
127 | request: "launch", | 129 | request: "launch", |
@@ -130,11 +132,12 @@ function getLldbDebugConfig(runnable: ra.Runnable, executable: string, sourceFil | |||
130 | args: runnable.args.executableArgs, | 132 | args: runnable.args.executableArgs, |
131 | cwd: runnable.args.workspaceRoot, | 133 | cwd: runnable.args.workspaceRoot, |
132 | sourceMap: sourceFileMap, | 134 | sourceMap: sourceFileMap, |
133 | sourceLanguages: ["rust"] | 135 | sourceLanguages: ["rust"], |
136 | env | ||
134 | }; | 137 | }; |
135 | } | 138 | } |
136 | 139 | ||
137 | function getCppvsDebugConfig(runnable: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration { | 140 | function getCppvsDebugConfig(runnable: ra.Runnable, executable: string, env: Record<string, string>, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration { |
138 | return { | 141 | return { |
139 | type: (os.platform() === "win32") ? "cppvsdbg" : "cppdbg", | 142 | type: (os.platform() === "win32") ? "cppvsdbg" : "cppdbg", |
140 | request: "launch", | 143 | request: "launch", |
@@ -142,6 +145,7 @@ function getCppvsDebugConfig(runnable: ra.Runnable, executable: string, sourceFi | |||
142 | program: executable, | 145 | program: executable, |
143 | args: runnable.args.executableArgs, | 146 | args: runnable.args.executableArgs, |
144 | cwd: runnable.args.workspaceRoot, | 147 | cwd: runnable.args.workspaceRoot, |
145 | sourceFileMap: sourceFileMap, | 148 | sourceFileMap, |
149 | env, | ||
146 | }; | 150 | }; |
147 | } | 151 | } |
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index e16ea799c..494d51c83 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts | |||
@@ -5,8 +5,12 @@ | |||
5 | import * as lc from "vscode-languageclient"; | 5 | import * as lc from "vscode-languageclient"; |
6 | 6 | ||
7 | export const analyzerStatus = new lc.RequestType<null, string, void>("rust-analyzer/analyzerStatus"); | 7 | export const analyzerStatus = new lc.RequestType<null, string, void>("rust-analyzer/analyzerStatus"); |
8 | export const memoryUsage = new lc.RequestType<null, string, void>("rust-analyzer/memoryUsage"); | ||
8 | 9 | ||
9 | export const collectGarbage = new lc.RequestType<null, null, void>("rust-analyzer/collectGarbage"); | 10 | export type Status = "loading" | "ready" | "invalid" | "needsReload"; |
11 | export const status = new lc.NotificationType<Status>("rust-analyzer/status"); | ||
12 | |||
13 | export const reloadWorkspace = new lc.RequestType<null, null, void>("rust-analyzer/reloadWorkspace"); | ||
10 | 14 | ||
11 | export interface SyntaxTreeParams { | 15 | export interface SyntaxTreeParams { |
12 | textDocument: lc.TextDocumentIdentifier; | 16 | textDocument: lc.TextDocumentIdentifier; |
@@ -60,6 +64,7 @@ export interface Runnable { | |||
60 | workspaceRoot?: string; | 64 | workspaceRoot?: string; |
61 | cargoArgs: string[]; | 65 | cargoArgs: string[]; |
62 | executableArgs: string[]; | 66 | executableArgs: string[]; |
67 | expectTest?: boolean; | ||
63 | }; | 68 | }; |
64 | } | 69 | } |
65 | export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>("experimental/runnables"); | 70 | export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>("experimental/runnables"); |
@@ -88,6 +93,9 @@ export const inlayHints = new lc.RequestType<InlayHintsParams, InlayHint[], void | |||
88 | export interface SsrParams { | 93 | export interface SsrParams { |
89 | query: string; | 94 | query: string; |
90 | parseOnly: boolean; | 95 | parseOnly: boolean; |
96 | textDocument: lc.TextDocumentIdentifier; | ||
97 | position: lc.Position; | ||
98 | selections: lc.Range[]; | ||
91 | } | 99 | } |
92 | export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>('experimental/ssr'); | 100 | export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>('experimental/ssr'); |
93 | 101 | ||
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 5ceab8b44..bd99d696a 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -19,6 +19,16 @@ let ctx: Ctx | undefined; | |||
19 | const RUST_PROJECT_CONTEXT_NAME = "inRustProject"; | 19 | const RUST_PROJECT_CONTEXT_NAME = "inRustProject"; |
20 | 20 | ||
21 | export async function activate(context: vscode.ExtensionContext) { | 21 | export async function activate(context: vscode.ExtensionContext) { |
22 | // For some reason vscode not always shows pop-up error notifications | ||
23 | // when an extension fails to activate, so we do it explicitly by ourselves. | ||
24 | // FIXME: remove this bit of code once vscode fixes this issue: https://github.com/microsoft/vscode/issues/101242 | ||
25 | await tryActivate(context).catch(err => { | ||
26 | void vscode.window.showErrorMessage(`Cannot activate rust-analyzer: ${err.message}`); | ||
27 | throw err; | ||
28 | }); | ||
29 | } | ||
30 | |||
31 | async function tryActivate(context: vscode.ExtensionContext) { | ||
22 | // Register a "dumb" onEnter command for the case where server fails to | 32 | // Register a "dumb" onEnter command for the case where server fails to |
23 | // start. | 33 | // start. |
24 | // | 34 | // |
@@ -44,13 +54,13 @@ export async function activate(context: vscode.ExtensionContext) { | |||
44 | const serverPath = await bootstrap(config, state).catch(err => { | 54 | const serverPath = await bootstrap(config, state).catch(err => { |
45 | let message = "bootstrap error. "; | 55 | let message = "bootstrap error. "; |
46 | 56 | ||
47 | if (err.code === "EBUSY" || err.code === "ETXTBSY") { | 57 | if (err.code === "EBUSY" || err.code === "ETXTBSY" || err.code === "EPERM") { |
48 | message += "Other vscode windows might be using rust-analyzer, "; | 58 | message += "Other vscode windows might be using rust-analyzer, "; |
49 | message += "you should close them and reload this window to retry. "; | 59 | message += "you should close them and reload this window to retry. "; |
50 | } | 60 | } |
51 | 61 | ||
52 | message += 'Open "Help > Toggle Developer Tools > Console" to see the logs '; | 62 | message += 'See the logs in "OUTPUT > Rust Analyzer Client" (should open automatically). '; |
53 | message += '(enable verbose logs with "rust-analyzer.trace.extension")'; | 63 | message += 'To enable verbose logs use { "rust-analyzer.trace.extension": true }'; |
54 | 64 | ||
55 | log.error("Bootstrap error", err); | 65 | log.error("Bootstrap error", err); |
56 | throw new Error(message); | 66 | throw new Error(message); |
@@ -58,9 +68,7 @@ export async function activate(context: vscode.ExtensionContext) { | |||
58 | 68 | ||
59 | const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; | 69 | const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; |
60 | if (workspaceFolder === undefined) { | 70 | if (workspaceFolder === undefined) { |
61 | const err = "Cannot activate rust-analyzer when no folder is opened"; | 71 | throw new Error("no folder is opened"); |
62 | void vscode.window.showErrorMessage(err); | ||
63 | throw new Error(err); | ||
64 | } | 72 | } |
65 | 73 | ||
66 | // Note: we try to start the server before we activate type hints so that it | 74 | // Note: we try to start the server before we activate type hints so that it |
@@ -88,7 +96,8 @@ export async function activate(context: vscode.ExtensionContext) { | |||
88 | }); | 96 | }); |
89 | 97 | ||
90 | ctx.registerCommand('analyzerStatus', commands.analyzerStatus); | 98 | ctx.registerCommand('analyzerStatus', commands.analyzerStatus); |
91 | ctx.registerCommand('collectGarbage', commands.collectGarbage); | 99 | ctx.registerCommand('memoryUsage', commands.memoryUsage); |
100 | ctx.registerCommand('reloadWorkspace', commands.reloadWorkspace); | ||
92 | ctx.registerCommand('matchingBrace', commands.matchingBrace); | 101 | ctx.registerCommand('matchingBrace', commands.matchingBrace); |
93 | ctx.registerCommand('joinLines', commands.joinLines); | 102 | ctx.registerCommand('joinLines', commands.joinLines); |
94 | ctx.registerCommand('parentModule', commands.parentModule); | 103 | ctx.registerCommand('parentModule', commands.parentModule); |
@@ -152,13 +161,17 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi | |||
152 | return; | 161 | return; |
153 | }; | 162 | }; |
154 | 163 | ||
155 | const lastCheck = state.lastCheck; | ||
156 | const now = Date.now(); | 164 | const now = Date.now(); |
165 | if (config.package.releaseTag === NIGHTLY_TAG) { | ||
166 | // Check if we should poll github api for the new nightly version | ||
167 | // if we haven't done it during the past hour | ||
168 | const lastCheck = state.lastCheck; | ||
157 | 169 | ||
158 | const anHour = 60 * 60 * 1000; | 170 | const anHour = 60 * 60 * 1000; |
159 | const shouldDownloadNightly = state.releaseId === undefined || (now - (lastCheck ?? 0)) > anHour; | 171 | const shouldCheckForNewNightly = state.releaseId === undefined || (now - (lastCheck ?? 0)) > anHour; |
160 | 172 | ||
161 | if (!shouldDownloadNightly) return; | 173 | if (!shouldCheckForNewNightly) return; |
174 | } | ||
162 | 175 | ||
163 | const release = await fetchRelease("nightly").catch((e) => { | 176 | const release = await fetchRelease("nightly").catch((e) => { |
164 | log.error(e); | 177 | log.error(e); |
@@ -202,7 +215,7 @@ async function bootstrapServer(config: Config, state: PersistentState): Promise< | |||
202 | ); | 215 | ); |
203 | } | 216 | } |
204 | 217 | ||
205 | log.debug("Using server binary at", path); | 218 | log.info("Using server binary at", path); |
206 | 219 | ||
207 | if (!isValidExecutable(path)) { | 220 | if (!isValidExecutable(path)) { |
208 | throw new Error(`Failed to execute ${path} --version`); | 221 | throw new Error(`Failed to execute ${path} --version`); |
@@ -261,13 +274,13 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
261 | }; | 274 | }; |
262 | if (config.package.releaseTag === null) return "rust-analyzer"; | 275 | if (config.package.releaseTag === null) return "rust-analyzer"; |
263 | 276 | ||
264 | let binaryName: string | undefined = undefined; | 277 | let platform: string | undefined; |
265 | if (process.arch === "x64" || process.arch === "ia32") { | 278 | if (process.arch === "x64" || process.arch === "ia32") { |
266 | if (process.platform === "linux") binaryName = "rust-analyzer-linux"; | 279 | if (process.platform === "linux") platform = "linux"; |
267 | if (process.platform === "darwin") binaryName = "rust-analyzer-mac"; | 280 | if (process.platform === "darwin") platform = "mac"; |
268 | if (process.platform === "win32") binaryName = "rust-analyzer-windows.exe"; | 281 | if (process.platform === "win32") platform = "windows"; |
269 | } | 282 | } |
270 | if (binaryName === undefined) { | 283 | if (platform === undefined) { |
271 | vscode.window.showErrorMessage( | 284 | vscode.window.showErrorMessage( |
272 | "Unfortunately we don't ship binaries for your platform yet. " + | 285 | "Unfortunately we don't ship binaries for your platform yet. " + |
273 | "You need to manually clone rust-analyzer repository and " + | 286 | "You need to manually clone rust-analyzer repository and " + |
@@ -278,8 +291,8 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
278 | ); | 291 | ); |
279 | return undefined; | 292 | return undefined; |
280 | } | 293 | } |
281 | 294 | const ext = platform === "windows" ? ".exe" : ""; | |
282 | const dest = path.join(config.globalStoragePath, binaryName); | 295 | const dest = path.join(config.globalStoragePath, `rust-analyzer-${platform}${ext}`); |
283 | const exists = await fs.stat(dest).then(() => true, () => false); | 296 | const exists = await fs.stat(dest).then(() => true, () => false); |
284 | if (!exists) { | 297 | if (!exists) { |
285 | await state.updateServerVersion(undefined); | 298 | await state.updateServerVersion(undefined); |
@@ -296,7 +309,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
296 | } | 309 | } |
297 | 310 | ||
298 | const release = await fetchRelease(config.package.releaseTag); | 311 | const release = await fetchRelease(config.package.releaseTag); |
299 | const artifact = release.assets.find(artifact => artifact.name === binaryName); | 312 | const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`); |
300 | assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); | 313 | assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); |
301 | 314 | ||
302 | // Unlinking the exe file before moving new one on its place should prevent ETXTBSY error. | 315 | // Unlinking the exe file before moving new one on its place should prevent ETXTBSY error. |
@@ -308,6 +321,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
308 | url: artifact.browser_download_url, | 321 | url: artifact.browser_download_url, |
309 | dest, | 322 | dest, |
310 | progressTitle: "Downloading rust-analyzer server", | 323 | progressTitle: "Downloading rust-analyzer server", |
324 | gunzip: true, | ||
311 | mode: 0o755 | 325 | mode: 0o755 |
312 | }); | 326 | }); |
313 | 327 | ||
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts index 866092882..681eaa9c9 100644 --- a/editors/code/src/net.ts +++ b/editors/code/src/net.ts | |||
@@ -1,8 +1,12 @@ | |||
1 | import fetch from "node-fetch"; | 1 | // Replace with `import fetch from "node-fetch"` once this is fixed in rollup: |
2 | // https://github.com/rollup/plugins/issues/491 | ||
3 | const fetch = require("node-fetch") as typeof import("node-fetch")["default"]; | ||
4 | |||
2 | import * as vscode from "vscode"; | 5 | import * as vscode from "vscode"; |
3 | import * as stream from "stream"; | 6 | import * as stream from "stream"; |
4 | import * as crypto from "crypto"; | 7 | import * as crypto from "crypto"; |
5 | import * as fs from "fs"; | 8 | import * as fs from "fs"; |
9 | import * as zlib from "zlib"; | ||
6 | import * as util from "util"; | 10 | import * as util from "util"; |
7 | import * as path from "path"; | 11 | import * as path from "path"; |
8 | import { log, assert } from "./util"; | 12 | import { log, assert } from "./util"; |
@@ -65,6 +69,7 @@ interface DownloadOpts { | |||
65 | url: string; | 69 | url: string; |
66 | dest: string; | 70 | dest: string; |
67 | mode?: number; | 71 | mode?: number; |
72 | gunzip?: boolean; | ||
68 | } | 73 | } |
69 | 74 | ||
70 | export async function download(opts: DownloadOpts) { | 75 | export async function download(opts: DownloadOpts) { |
@@ -82,7 +87,7 @@ export async function download(opts: DownloadOpts) { | |||
82 | }, | 87 | }, |
83 | async (progress, _cancellationToken) => { | 88 | async (progress, _cancellationToken) => { |
84 | let lastPercentage = 0; | 89 | let lastPercentage = 0; |
85 | await downloadFile(opts.url, tempFile, opts.mode, (readBytes, totalBytes) => { | 90 | await downloadFile(opts.url, tempFile, opts.mode, !!opts.gunzip, (readBytes, totalBytes) => { |
86 | const newPercentage = (readBytes / totalBytes) * 100; | 91 | const newPercentage = (readBytes / totalBytes) * 100; |
87 | progress.report({ | 92 | progress.report({ |
88 | message: newPercentage.toFixed(0) + "%", | 93 | message: newPercentage.toFixed(0) + "%", |
@@ -97,16 +102,11 @@ export async function download(opts: DownloadOpts) { | |||
97 | await fs.promises.rename(tempFile, opts.dest); | 102 | await fs.promises.rename(tempFile, opts.dest); |
98 | } | 103 | } |
99 | 104 | ||
100 | /** | ||
101 | * Downloads file from `url` and stores it at `destFilePath` with `mode` (unix permissions). | ||
102 | * `onProgress` callback is called on recieveing each chunk of bytes | ||
103 | * to track the progress of downloading, it gets the already read and total | ||
104 | * amount of bytes to read as its parameters. | ||
105 | */ | ||
106 | async function downloadFile( | 105 | async function downloadFile( |
107 | url: string, | 106 | url: string, |
108 | destFilePath: fs.PathLike, | 107 | destFilePath: fs.PathLike, |
109 | mode: number | undefined, | 108 | mode: number | undefined, |
109 | gunzip: boolean, | ||
110 | onProgress: (readBytes: number, totalBytes: number) => void | 110 | onProgress: (readBytes: number, totalBytes: number) => void |
111 | ): Promise<void> { | 111 | ): Promise<void> { |
112 | const res = await fetch(url); | 112 | const res = await fetch(url); |
@@ -130,7 +130,10 @@ async function downloadFile( | |||
130 | }); | 130 | }); |
131 | 131 | ||
132 | const destFileStream = fs.createWriteStream(destFilePath, { mode }); | 132 | const destFileStream = fs.createWriteStream(destFilePath, { mode }); |
133 | await pipeline(res.body, destFileStream); | 133 | const srcStream = gunzip ? res.body.pipe(zlib.createGunzip()) : res.body; |
134 | |||
135 | await pipeline(srcStream, destFileStream); | ||
136 | |||
134 | await new Promise<void>(resolve => { | 137 | await new Promise<void>(resolve => { |
135 | destFileStream.on("close", resolve); | 138 | destFileStream.on("close", resolve); |
136 | destFileStream.destroy(); | 139 | destFileStream.destroy(); |
diff --git a/editors/code/src/persistent_state.ts b/editors/code/src/persistent_state.ts index 138d11b89..5705eed81 100644 --- a/editors/code/src/persistent_state.ts +++ b/editors/code/src/persistent_state.ts | |||
@@ -4,7 +4,7 @@ import { log } from './util'; | |||
4 | export class PersistentState { | 4 | export class PersistentState { |
5 | constructor(private readonly globalState: vscode.Memento) { | 5 | constructor(private readonly globalState: vscode.Memento) { |
6 | const { lastCheck, releaseId, serverVersion } = this; | 6 | const { lastCheck, releaseId, serverVersion } = this; |
7 | log.debug("PersistentState: ", { lastCheck, releaseId, serverVersion }); | 7 | log.info("PersistentState:", { lastCheck, releaseId, serverVersion }); |
8 | } | 8 | } |
9 | 9 | ||
10 | /** | 10 | /** |
diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index 766b05112..de68f27ae 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts | |||
@@ -5,7 +5,7 @@ import * as tasks from './tasks'; | |||
5 | 5 | ||
6 | import { Ctx } from './ctx'; | 6 | import { Ctx } from './ctx'; |
7 | import { makeDebugConfig } from './debug'; | 7 | import { makeDebugConfig } from './debug'; |
8 | import { Config } from './config'; | 8 | import { Config, RunnableEnvCfg } from './config'; |
9 | 9 | ||
10 | const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; | 10 | const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; |
11 | 11 | ||
@@ -96,6 +96,30 @@ export class RunnableQuickPick implements vscode.QuickPickItem { | |||
96 | } | 96 | } |
97 | } | 97 | } |
98 | 98 | ||
99 | export function prepareEnv(runnable: ra.Runnable, runnableEnvCfg: RunnableEnvCfg): Record<string, string> { | ||
100 | const env: Record<string, string> = { "RUST_BACKTRACE": "short" }; | ||
101 | |||
102 | if (runnable.args.expectTest) { | ||
103 | env["UPDATE_EXPECT"] = "1"; | ||
104 | } | ||
105 | |||
106 | Object.assign(env, process.env as { [key: string]: string }); | ||
107 | |||
108 | if (runnableEnvCfg) { | ||
109 | if (Array.isArray(runnableEnvCfg)) { | ||
110 | for (const it of runnableEnvCfg) { | ||
111 | if (!it.mask || new RegExp(it.mask).test(runnable.label)) { | ||
112 | Object.assign(env, it.env); | ||
113 | } | ||
114 | } | ||
115 | } else { | ||
116 | Object.assign(env, runnableEnvCfg); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | return env; | ||
121 | } | ||
122 | |||
99 | export async function createTask(runnable: ra.Runnable, config: Config): Promise<vscode.Task> { | 123 | export async function createTask(runnable: ra.Runnable, config: Config): Promise<vscode.Task> { |
100 | if (runnable.kind !== "cargo") { | 124 | if (runnable.kind !== "cargo") { |
101 | // rust-analyzer supports only one kind, "cargo" | 125 | // rust-analyzer supports only one kind, "cargo" |
@@ -108,12 +132,13 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise | |||
108 | if (runnable.args.executableArgs.length > 0) { | 132 | if (runnable.args.executableArgs.length > 0) { |
109 | args.push('--', ...runnable.args.executableArgs); | 133 | args.push('--', ...runnable.args.executableArgs); |
110 | } | 134 | } |
135 | |||
111 | const definition: tasks.CargoTaskDefinition = { | 136 | const definition: tasks.CargoTaskDefinition = { |
112 | type: tasks.TASK_TYPE, | 137 | type: tasks.TASK_TYPE, |
113 | command: args[0], // run, test, etc... | 138 | command: args[0], // run, test, etc... |
114 | args: args.slice(1), | 139 | args: args.slice(1), |
115 | cwd: runnable.args.workspaceRoot, | 140 | cwd: runnable.args.workspaceRoot || ".", |
116 | env: Object.assign({}, process.env as { [key: string]: string }, { "RUST_BACKTRACE": "short" }), | 141 | env: prepareEnv(runnable, config.runnableEnv), |
117 | }; | 142 | }; |
118 | 143 | ||
119 | const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate() | 144 | const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate() |
diff --git a/editors/code/src/snippets.ts b/editors/code/src/snippets.ts index bcb3f2cc7..258b49982 100644 --- a/editors/code/src/snippets.ts +++ b/editors/code/src/snippets.ts | |||
@@ -6,6 +6,10 @@ export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) { | |||
6 | assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`); | 6 | assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`); |
7 | const [uri, edits] = edit.entries()[0]; | 7 | const [uri, edits] = edit.entries()[0]; |
8 | 8 | ||
9 | if (vscode.window.activeTextEditor?.document.uri !== uri) { | ||
10 | // `vscode.window.visibleTextEditors` only contains editors whose contents are being displayed | ||
11 | await vscode.window.showTextDocument(uri, {}); | ||
12 | } | ||
9 | const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString()); | 13 | const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString()); |
10 | if (!editor) return; | 14 | if (!editor) return; |
11 | await applySnippetTextEdits(editor, edits); | 15 | await applySnippetTextEdits(editor, edits); |
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index fec4c3295..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,21 +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 { |
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 { | ||
29 | debugger; | 41 | debugger; |
30 | // eslint-disable-next-line no-console | 42 | log.write("ERROR", ...msg); |
31 | 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 | }); | ||
32 | } | 58 | } |
33 | }; | 59 | }; |
34 | 60 | ||
@@ -46,7 +72,7 @@ export async function sendRequestWithRetry<TParam, TRet>( | |||
46 | ); | 72 | ); |
47 | } catch (error) { | 73 | } catch (error) { |
48 | if (delay === null) { | 74 | if (delay === null) { |
49 | log.error("LSP request timed out", { method: reqType.method, param, error }); | 75 | log.warn("LSP request timed out", { method: reqType.method, param, error }); |
50 | throw error; | 76 | throw error; |
51 | } | 77 | } |
52 | 78 | ||
@@ -55,7 +81,7 @@ export async function sendRequestWithRetry<TParam, TRet>( | |||
55 | } | 81 | } |
56 | 82 | ||
57 | if (error.code !== lc.ErrorCodes.ContentModified) { | 83 | if (error.code !== lc.ErrorCodes.ContentModified) { |
58 | log.error("LSP request failed", { method: reqType.method, param, error }); | 84 | log.warn("LSP request failed", { method: reqType.method, param, error }); |
59 | throw error; | 85 | throw error; |
60 | } | 86 | } |
61 | 87 | ||
@@ -89,7 +115,8 @@ export function isValidExecutable(path: string): boolean { | |||
89 | 115 | ||
90 | const res = spawnSync(path, ["--version"], { encoding: 'utf8' }); | 116 | const res = spawnSync(path, ["--version"], { encoding: 'utf8' }); |
91 | 117 | ||
92 | 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); | ||
93 | 120 | ||
94 | return res.status === 0; | 121 | return res.status === 0; |
95 | } | 122 | } |