diff options
Diffstat (limited to 'editors/code/src')
-rw-r--r-- | editors/code/src/client.ts | 23 | ||||
-rw-r--r-- | editors/code/src/commands.ts | 32 | ||||
-rw-r--r-- | editors/code/src/config.ts | 1 | ||||
-rw-r--r-- | editors/code/src/ctx.ts | 6 | ||||
-rw-r--r-- | editors/code/src/debug.ts | 12 | ||||
-rw-r--r-- | editors/code/src/inlay_hints.ts | 2 | ||||
-rw-r--r-- | editors/code/src/lsp_ext.ts | 2 | ||||
-rw-r--r-- | editors/code/src/main.ts | 36 | ||||
-rw-r--r-- | editors/code/src/net.ts | 25 | ||||
-rw-r--r-- | editors/code/src/run.ts | 26 | ||||
-rw-r--r-- | editors/code/src/snippets.ts | 4 |
11 files changed, 93 insertions, 76 deletions
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 539e487ec..0771ca3b6 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts | |||
@@ -2,7 +2,6 @@ import * as lc from 'vscode-languageclient/node'; | |||
2 | import * as vscode from 'vscode'; | 2 | import * as vscode from 'vscode'; |
3 | import * as ra from '../src/lsp_ext'; | 3 | import * as ra from '../src/lsp_ext'; |
4 | import * as Is from 'vscode-languageclient/lib/common/utils/is'; | 4 | import * as Is from 'vscode-languageclient/lib/common/utils/is'; |
5 | import { DocumentSemanticsTokensSignature, DocumentSemanticsTokensEditsSignature, DocumentRangeSemanticTokensSignature } from 'vscode-languageclient/lib/common/semanticTokens'; | ||
6 | import { assert } from './util'; | 5 | import { assert } from './util'; |
7 | import { WorkspaceEdit } from 'vscode'; | 6 | import { WorkspaceEdit } from 'vscode'; |
8 | 7 | ||
@@ -11,7 +10,7 @@ export interface Env { | |||
11 | } | 10 | } |
12 | 11 | ||
13 | function renderCommand(cmd: ra.CommandLink) { | 12 | function renderCommand(cmd: ra.CommandLink) { |
14 | return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`; | 13 | return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip}')`; |
15 | } | 14 | } |
16 | 15 | ||
17 | function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownString { | 16 | function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownString { |
@@ -24,13 +23,6 @@ function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownStri | |||
24 | return result; | 23 | return result; |
25 | } | 24 | } |
26 | 25 | ||
27 | // Workaround for https://github.com/microsoft/vscode-languageserver-node/issues/576 | ||
28 | async function semanticHighlightingWorkaround<R, F extends (...args: any[]) => vscode.ProviderResult<R>>(next: F, ...args: Parameters<F>): Promise<R> { | ||
29 | const res = await next(...args); | ||
30 | if (res == null) throw new Error('busy'); | ||
31 | return res; | ||
32 | } | ||
33 | |||
34 | export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc.LanguageClient { | 26 | export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc.LanguageClient { |
35 | // '.' Is the fallback if no folder is open | 27 | // '.' Is the fallback if no folder is open |
36 | // TODO?: Workspace folders support Uri's (eg: file://test.txt). | 28 | // TODO?: Workspace folders support Uri's (eg: file://test.txt). |
@@ -57,15 +49,6 @@ export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc | |||
57 | diagnosticCollectionName: "rustc", | 49 | diagnosticCollectionName: "rustc", |
58 | traceOutputChannel, | 50 | traceOutputChannel, |
59 | middleware: { | 51 | middleware: { |
60 | provideDocumentSemanticTokens(document: vscode.TextDocument, token: vscode.CancellationToken, next: DocumentSemanticsTokensSignature): vscode.ProviderResult<vscode.SemanticTokens> { | ||
61 | return semanticHighlightingWorkaround(next, document, token); | ||
62 | }, | ||
63 | provideDocumentSemanticTokensEdits(document: vscode.TextDocument, previousResultId: string, token: vscode.CancellationToken, next: DocumentSemanticsTokensEditsSignature): vscode.ProviderResult<vscode.SemanticTokensEdits | vscode.SemanticTokens> { | ||
64 | return semanticHighlightingWorkaround(next, document, previousResultId, token); | ||
65 | }, | ||
66 | provideDocumentRangeSemanticTokens(document: vscode.TextDocument, range: vscode.Range, token: vscode.CancellationToken, next: DocumentRangeSemanticTokensSignature): vscode.ProviderResult<vscode.SemanticTokens> { | ||
67 | return semanticHighlightingWorkaround(next, document, range, token); | ||
68 | }, | ||
69 | async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, _next: lc.ProvideHoverSignature) { | 52 | async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, _next: lc.ProvideHoverSignature) { |
70 | return client.sendRequest(lc.HoverRequest.type, client.code2ProtocolConverter.asTextDocumentPositionParams(document, position), token).then( | 53 | return client.sendRequest(lc.HoverRequest.type, client.code2ProtocolConverter.asTextDocumentPositionParams(document, position), token).then( |
71 | (result) => { | 54 | (result) => { |
@@ -79,7 +62,7 @@ export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc | |||
79 | return hover; | 62 | return hover; |
80 | }, | 63 | }, |
81 | (error) => { | 64 | (error) => { |
82 | client.handleFailedRequest(lc.HoverRequest.type, error, null); | 65 | client.handleFailedRequest(lc.HoverRequest.type, token, error, null); |
83 | return Promise.resolve(null); | 66 | return Promise.resolve(null); |
84 | }); | 67 | }); |
85 | }, | 68 | }, |
@@ -138,7 +121,7 @@ export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc | |||
138 | command: "rust-analyzer.applyActionGroup", | 121 | command: "rust-analyzer.applyActionGroup", |
139 | title: "", | 122 | title: "", |
140 | arguments: [items.map((item) => { | 123 | arguments: [items.map((item) => { |
141 | return { label: item.title, arguments: item.command!!.arguments!![0] }; | 124 | return { label: item.title, arguments: item.command!.arguments![0] }; |
142 | })], | 125 | })], |
143 | }; | 126 | }; |
144 | 127 | ||
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index c1c9f9754..283b9a160 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts | |||
@@ -5,7 +5,7 @@ import * as ra from './lsp_ext'; | |||
5 | import { Ctx, Cmd } from './ctx'; | 5 | import { Ctx, Cmd } from './ctx'; |
6 | import { applySnippetWorkspaceEdit, applySnippetTextEdits } from './snippets'; | 6 | import { applySnippetWorkspaceEdit, applySnippetTextEdits } from './snippets'; |
7 | import { spawnSync } from 'child_process'; | 7 | import { spawnSync } from 'child_process'; |
8 | import { RunnableQuickPick, selectRunnable, createTask } from './run'; | 8 | import { RunnableQuickPick, selectRunnable, createTask, createArgs } from './run'; |
9 | import { AstInspector } from './ast_inspector'; | 9 | import { AstInspector } from './ast_inspector'; |
10 | import { isRustDocument, sleep, isRustEditor } from './util'; | 10 | import { isRustDocument, sleep, isRustEditor } from './util'; |
11 | import { startDebugSession, makeDebugConfig } from './debug'; | 11 | import { startDebugSession, makeDebugConfig } from './debug'; |
@@ -125,7 +125,7 @@ export function joinLines(ctx: Ctx): Cmd { | |||
125 | ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)), | 125 | ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)), |
126 | textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), | 126 | textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), |
127 | }); | 127 | }); |
128 | editor.edit((builder) => { | 128 | await editor.edit((builder) => { |
129 | client.protocol2CodeConverter.asTextEdits(items).forEach((edit: any) => { | 129 | client.protocol2CodeConverter.asTextEdits(items).forEach((edit: any) => { |
130 | builder.replace(edit.range, edit.newText); | 130 | builder.replace(edit.range, edit.newText); |
131 | }); | 131 | }); |
@@ -236,7 +236,7 @@ export function ssr(ctx: Ctx): Cmd { | |||
236 | const request = await vscode.window.showInputBox(options); | 236 | const request = await vscode.window.showInputBox(options); |
237 | if (!request) return; | 237 | if (!request) return; |
238 | 238 | ||
239 | vscode.window.withProgress({ | 239 | await vscode.window.withProgress({ |
240 | location: vscode.ProgressLocation.Notification, | 240 | location: vscode.ProgressLocation.Notification, |
241 | title: "Structured search replace in progress...", | 241 | title: "Structured search replace in progress...", |
242 | cancellable: false, | 242 | cancellable: false, |
@@ -457,10 +457,10 @@ export function reloadWorkspace(ctx: Ctx): Cmd { | |||
457 | } | 457 | } |
458 | 458 | ||
459 | export function showReferences(ctx: Ctx): Cmd { | 459 | export function showReferences(ctx: Ctx): Cmd { |
460 | return (uri: string, position: lc.Position, locations: lc.Location[]) => { | 460 | return async (uri: string, position: lc.Position, locations: lc.Location[]) => { |
461 | const client = ctx.client; | 461 | const client = ctx.client; |
462 | if (client) { | 462 | if (client) { |
463 | vscode.commands.executeCommand( | 463 | await vscode.commands.executeCommand( |
464 | 'editor.action.showReferences', | 464 | 'editor.action.showReferences', |
465 | vscode.Uri.parse(uri), | 465 | vscode.Uri.parse(uri), |
466 | client.protocol2CodeConverter.asPosition(position), | 466 | client.protocol2CodeConverter.asPosition(position), |
@@ -474,7 +474,7 @@ export function applyActionGroup(_ctx: Ctx): Cmd { | |||
474 | return async (actions: { label: string; arguments: lc.CodeAction }[]) => { | 474 | return async (actions: { label: string; arguments: lc.CodeAction }[]) => { |
475 | const selectedAction = await vscode.window.showQuickPick(actions); | 475 | const selectedAction = await vscode.window.showQuickPick(actions); |
476 | if (!selectedAction) return; | 476 | if (!selectedAction) return; |
477 | vscode.commands.executeCommand( | 477 | await vscode.commands.executeCommand( |
478 | 'rust-analyzer.resolveCodeAction', | 478 | 'rust-analyzer.resolveCodeAction', |
479 | selectedAction.arguments, | 479 | selectedAction.arguments, |
480 | ); | 480 | ); |
@@ -510,7 +510,7 @@ export function openDocs(ctx: Ctx): Cmd { | |||
510 | const doclink = await client.sendRequest(ra.openDocs, { position, textDocument }); | 510 | const doclink = await client.sendRequest(ra.openDocs, { position, textDocument }); |
511 | 511 | ||
512 | if (doclink != null) { | 512 | if (doclink != null) { |
513 | vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(doclink)); | 513 | await vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(doclink)); |
514 | } | 514 | } |
515 | }; | 515 | }; |
516 | 516 | ||
@@ -528,10 +528,10 @@ export function resolveCodeAction(ctx: Ctx): Cmd { | |||
528 | const edit = client.protocol2CodeConverter.asWorkspaceEdit(itemEdit); | 528 | const edit = client.protocol2CodeConverter.asWorkspaceEdit(itemEdit); |
529 | // filter out all text edits and recreate the WorkspaceEdit without them so we can apply | 529 | // filter out all text edits and recreate the WorkspaceEdit without them so we can apply |
530 | // snippet edits on our own | 530 | // snippet edits on our own |
531 | const itemEditWithoutTextEdits = { ...item, documentChanges: itemEdit.documentChanges?.filter(change => "kind" in change) }; | 531 | const lcFileSystemEdit = { ...itemEdit, documentChanges: itemEdit.documentChanges?.filter(change => "kind" in change) }; |
532 | const editWithoutTextEdits = client.protocol2CodeConverter.asWorkspaceEdit(itemEditWithoutTextEdits); | 532 | const fileSystemEdit = client.protocol2CodeConverter.asWorkspaceEdit(lcFileSystemEdit); |
533 | await vscode.workspace.applyEdit(fileSystemEdit); | ||
533 | await applySnippetWorkspaceEdit(edit); | 534 | await applySnippetWorkspaceEdit(edit); |
534 | await vscode.workspace.applyEdit(editWithoutTextEdits); | ||
535 | }; | 535 | }; |
536 | } | 536 | } |
537 | 537 | ||
@@ -572,6 +572,18 @@ export function runSingle(ctx: Ctx): Cmd { | |||
572 | }; | 572 | }; |
573 | } | 573 | } |
574 | 574 | ||
575 | export function copyRunCommandLine(ctx: Ctx) { | ||
576 | let prevRunnable: RunnableQuickPick | undefined; | ||
577 | return async () => { | ||
578 | const item = await selectRunnable(ctx, prevRunnable); | ||
579 | if (!item) return; | ||
580 | const args = createArgs(item.runnable); | ||
581 | const commandLine = ["cargo", ...args].join(" "); | ||
582 | await vscode.env.clipboard.writeText(commandLine); | ||
583 | await vscode.window.showInformationMessage("Cargo invocation copied to the clipboard."); | ||
584 | }; | ||
585 | } | ||
586 | |||
575 | export function debug(ctx: Ctx): Cmd { | 587 | export function debug(ctx: Ctx): Cmd { |
576 | let prevDebuggee: RunnableQuickPick | undefined; | 588 | let prevDebuggee: RunnableQuickPick | undefined; |
577 | 589 | ||
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index ebe4de1ea..ddb5cfbd3 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -144,6 +144,7 @@ export class Config { | |||
144 | debug: this.get<boolean>("lens.debug"), | 144 | debug: this.get<boolean>("lens.debug"), |
145 | implementations: this.get<boolean>("lens.implementations"), | 145 | implementations: this.get<boolean>("lens.implementations"), |
146 | methodReferences: this.get<boolean>("lens.methodReferences"), | 146 | methodReferences: this.get<boolean>("lens.methodReferences"), |
147 | references: this.get<boolean>("lens.references"), | ||
147 | }; | 148 | }; |
148 | } | 149 | } |
149 | 150 | ||
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index e7585184b..c07583cfa 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts | |||
@@ -74,6 +74,12 @@ export class Ctx { | |||
74 | this.statusBar.command = undefined; | 74 | this.statusBar.command = undefined; |
75 | this.statusBar.color = undefined; | 75 | this.statusBar.color = undefined; |
76 | break; | 76 | break; |
77 | case "readyPartial": | ||
78 | this.statusBar.text = "rust-analyzer"; | ||
79 | this.statusBar.tooltip = "Ready (Partial)"; | ||
80 | this.statusBar.command = undefined; | ||
81 | this.statusBar.color = undefined; | ||
82 | break; | ||
77 | case "ready": | 83 | case "ready": |
78 | this.statusBar.text = "rust-analyzer"; | 84 | this.statusBar.text = "rust-analyzer"; |
79 | this.statusBar.tooltip = "Ready"; | 85 | this.statusBar.tooltip = "Ready"; |
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index 925126a16..3889a2773 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts | |||
@@ -77,7 +77,7 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v | |||
77 | } | 77 | } |
78 | 78 | ||
79 | if (!debugEngine) { | 79 | if (!debugEngine) { |
80 | vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)` | 80 | await vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)` |
81 | + ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extension for debugging.`); | 81 | + ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extension for debugging.`); |
82 | return; | 82 | return; |
83 | } | 83 | } |
@@ -86,12 +86,14 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v | |||
86 | if (ctx.config.debug.openDebugPane) { | 86 | if (ctx.config.debug.openDebugPane) { |
87 | debugOutput.show(true); | 87 | debugOutput.show(true); |
88 | } | 88 | } |
89 | 89 | // folder exists or RA is not active. | |
90 | const isMultiFolderWorkspace = vscode.workspace.workspaceFolders!.length > 1; | 90 | // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion |
91 | const firstWorkspace = vscode.workspace.workspaceFolders![0]; // folder exists or RA is not active. | 91 | const workspaceFolders = vscode.workspace.workspaceFolders!; |
92 | const isMultiFolderWorkspace = workspaceFolders.length > 1; | ||
93 | const firstWorkspace = workspaceFolders[0]; | ||
92 | const workspace = !isMultiFolderWorkspace || !runnable.args.workspaceRoot ? | 94 | const workspace = !isMultiFolderWorkspace || !runnable.args.workspaceRoot ? |
93 | firstWorkspace : | 95 | firstWorkspace : |
94 | vscode.workspace.workspaceFolders!.find(w => runnable.args.workspaceRoot?.includes(w.uri.fsPath)) || firstWorkspace; | 96 | workspaceFolders.find(w => runnable.args.workspaceRoot?.includes(w.uri.fsPath)) || firstWorkspace; |
95 | 97 | ||
96 | const wsFolder = path.normalize(workspace.uri.fsPath); | 98 | const wsFolder = path.normalize(workspace.uri.fsPath); |
97 | const workspaceQualifier = isMultiFolderWorkspace ? `:${workspace.name}` : ''; | 99 | const workspaceQualifier = isMultiFolderWorkspace ? `:${workspace.name}` : ''; |
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts index 38eb1c15b..61db6b8d0 100644 --- a/editors/code/src/inlay_hints.ts +++ b/editors/code/src/inlay_hints.ts | |||
@@ -36,7 +36,7 @@ export function activateInlayHints(ctx: Ctx) { | |||
36 | maybeUpdater.onConfigChange, maybeUpdater, ctx.subscriptions | 36 | maybeUpdater.onConfigChange, maybeUpdater, ctx.subscriptions |
37 | ); | 37 | ); |
38 | 38 | ||
39 | maybeUpdater.onConfigChange(); | 39 | maybeUpdater.onConfigChange().catch(console.error); |
40 | } | 40 | } |
41 | 41 | ||
42 | const typeHints = createHintStyle("type"); | 42 | const typeHints = createHintStyle("type"); |
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index d21a3db86..2de1e427d 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts | |||
@@ -10,7 +10,7 @@ export interface AnalyzerStatusParams { | |||
10 | export const analyzerStatus = new lc.RequestType<AnalyzerStatusParams, string, void>("rust-analyzer/analyzerStatus"); | 10 | export const analyzerStatus = new lc.RequestType<AnalyzerStatusParams, string, void>("rust-analyzer/analyzerStatus"); |
11 | export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage"); | 11 | export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage"); |
12 | 12 | ||
13 | export type Status = "loading" | "ready" | "invalid" | "needsReload"; | 13 | export type Status = "loading" | "ready" | "readyPartial" | "invalid" | "needsReload"; |
14 | export interface StatusParams { | 14 | export interface StatusParams { |
15 | status: Status; | 15 | status: Status; |
16 | } | 16 | } |
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 1edb7713d..00393d6e8 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -12,7 +12,7 @@ import { PersistentState } from './persistent_state'; | |||
12 | import { fetchRelease, download } from './net'; | 12 | import { fetchRelease, download } from './net'; |
13 | import { activateTaskProvider } from './tasks'; | 13 | import { activateTaskProvider } from './tasks'; |
14 | import { setContextValue } from './util'; | 14 | import { setContextValue } from './util'; |
15 | import { exec } from 'child_process'; | 15 | import { exec, spawnSync } from 'child_process'; |
16 | 16 | ||
17 | let ctx: Ctx | undefined; | 17 | let ctx: Ctx | undefined; |
18 | 18 | ||
@@ -76,7 +76,7 @@ async function tryActivate(context: vscode.ExtensionContext) { | |||
76 | // This a horribly, horribly wrong way to deal with this problem. | 76 | // This a horribly, horribly wrong way to deal with this problem. |
77 | ctx = await Ctx.create(config, context, serverPath, workspaceFolder.uri.fsPath); | 77 | ctx = await Ctx.create(config, context, serverPath, workspaceFolder.uri.fsPath); |
78 | 78 | ||
79 | setContextValue(RUST_PROJECT_CONTEXT_NAME, true); | 79 | await setContextValue(RUST_PROJECT_CONTEXT_NAME, true); |
80 | 80 | ||
81 | // Commands which invokes manually via command palette, shortcut, etc. | 81 | // Commands which invokes manually via command palette, shortcut, etc. |
82 | 82 | ||
@@ -108,6 +108,7 @@ async function tryActivate(context: vscode.ExtensionContext) { | |||
108 | ctx.registerCommand('viewHir', commands.viewHir); | 108 | ctx.registerCommand('viewHir', commands.viewHir); |
109 | ctx.registerCommand('expandMacro', commands.expandMacro); | 109 | ctx.registerCommand('expandMacro', commands.expandMacro); |
110 | ctx.registerCommand('run', commands.run); | 110 | ctx.registerCommand('run', commands.run); |
111 | ctx.registerCommand('copyRunCommandLine', commands.copyRunCommandLine); | ||
111 | ctx.registerCommand('debug', commands.debug); | 112 | ctx.registerCommand('debug', commands.debug); |
112 | ctx.registerCommand('newDebugConfig', commands.newDebugConfig); | 113 | ctx.registerCommand('newDebugConfig', commands.newDebugConfig); |
113 | ctx.registerCommand('openDocs', commands.openDocs); | 114 | ctx.registerCommand('openDocs', commands.openDocs); |
@@ -142,7 +143,7 @@ async function tryActivate(context: vscode.ExtensionContext) { | |||
142 | } | 143 | } |
143 | 144 | ||
144 | export async function deactivate() { | 145 | export async function deactivate() { |
145 | setContextValue(RUST_PROJECT_CONTEXT_NAME, undefined); | 146 | await setContextValue(RUST_PROJECT_CONTEXT_NAME, undefined); |
146 | await ctx?.client.stop(); | 147 | await ctx?.client.stop(); |
147 | ctx = undefined; | 148 | ctx = undefined; |
148 | } | 149 | } |
@@ -183,10 +184,10 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi | |||
183 | 184 | ||
184 | const release = await downloadWithRetryDialog(state, async () => { | 185 | const release = await downloadWithRetryDialog(state, async () => { |
185 | return await fetchRelease("nightly", state.githubToken); | 186 | return await fetchRelease("nightly", state.githubToken); |
186 | }).catch((e) => { | 187 | }).catch(async (e) => { |
187 | log.error(e); | 188 | log.error(e); |
188 | if (state.releaseId === undefined) { // Show error only for the initial download | 189 | if (state.releaseId === undefined) { // Show error only for the initial download |
189 | vscode.window.showErrorMessage(`Failed to download rust-analyzer nightly ${e}`); | 190 | await vscode.window.showErrorMessage(`Failed to download rust-analyzer nightly ${e}`); |
190 | } | 191 | } |
191 | return undefined; | 192 | return undefined; |
192 | }); | 193 | }); |
@@ -208,7 +209,6 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi | |||
208 | url: artifact.browser_download_url, | 209 | url: artifact.browser_download_url, |
209 | dest, | 210 | dest, |
210 | progressTitle: "Downloading rust-analyzer extension", | 211 | progressTitle: "Downloading rust-analyzer extension", |
211 | overwrite: true, | ||
212 | }); | 212 | }); |
213 | }); | 213 | }); |
214 | 214 | ||
@@ -246,10 +246,10 @@ async function patchelf(dest: PathLike): Promise<void> { | |||
246 | }, | 246 | }, |
247 | async (progress, _) => { | 247 | async (progress, _) => { |
248 | const expression = ` | 248 | const expression = ` |
249 | {src, pkgs ? import <nixpkgs> {}}: | 249 | {srcStr, pkgs ? import <nixpkgs> {}}: |
250 | pkgs.stdenv.mkDerivation { | 250 | pkgs.stdenv.mkDerivation { |
251 | name = "rust-analyzer"; | 251 | name = "rust-analyzer"; |
252 | inherit src; | 252 | src = /. + srcStr; |
253 | phases = [ "installPhase" "fixupPhase" ]; | 253 | phases = [ "installPhase" "fixupPhase" ]; |
254 | installPhase = "cp $src $out"; | 254 | installPhase = "cp $src $out"; |
255 | fixupPhase = '' | 255 | fixupPhase = '' |
@@ -262,7 +262,7 @@ async function patchelf(dest: PathLike): Promise<void> { | |||
262 | await fs.rename(dest, origFile); | 262 | await fs.rename(dest, origFile); |
263 | progress.report({ message: "Patching executable", increment: 20 }); | 263 | progress.report({ message: "Patching executable", increment: 20 }); |
264 | await new Promise((resolve, reject) => { | 264 | await new Promise((resolve, reject) => { |
265 | const handle = exec(`nix-build -E - --arg src '${origFile}' -o ${dest}`, | 265 | const handle = exec(`nix-build -E - --argstr srcStr '${origFile}' -o '${dest}'`, |
266 | (err, stdout, stderr) => { | 266 | (err, stdout, stderr) => { |
267 | if (err != null) { | 267 | if (err != null) { |
268 | reject(Error(stderr)); | 268 | reject(Error(stderr)); |
@@ -297,9 +297,9 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
297 | "arm64 linux": "aarch64-unknown-linux-gnu", | 297 | "arm64 linux": "aarch64-unknown-linux-gnu", |
298 | "arm64 darwin": "aarch64-apple-darwin", | 298 | "arm64 darwin": "aarch64-apple-darwin", |
299 | }; | 299 | }; |
300 | const platform = platforms[`${process.arch} ${process.platform}`]; | 300 | let platform = platforms[`${process.arch} ${process.platform}`]; |
301 | if (platform === undefined) { | 301 | if (platform === undefined) { |
302 | vscode.window.showErrorMessage( | 302 | await vscode.window.showErrorMessage( |
303 | "Unfortunately we don't ship binaries for your platform yet. " + | 303 | "Unfortunately we don't ship binaries for your platform yet. " + |
304 | "You need to manually clone rust-analyzer repository and " + | 304 | "You need to manually clone rust-analyzer repository and " + |
305 | "run `cargo xtask install --server` to build the language server from sources. " + | 305 | "run `cargo xtask install --server` to build the language server from sources. " + |
@@ -309,6 +309,9 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
309 | ); | 309 | ); |
310 | return undefined; | 310 | return undefined; |
311 | } | 311 | } |
312 | if (platform === "x86_64-unknown-linux-gnu" && isMusl()) { | ||
313 | platform = "x86_64-unknown-linux-musl"; | ||
314 | } | ||
312 | const ext = platform.indexOf("-windows-") !== -1 ? ".exe" : ""; | 315 | const ext = platform.indexOf("-windows-") !== -1 ? ".exe" : ""; |
313 | const dest = path.join(config.globalStoragePath, `rust-analyzer-${platform}${ext}`); | 316 | const dest = path.join(config.globalStoragePath, `rust-analyzer-${platform}${ext}`); |
314 | const exists = await fs.stat(dest).then(() => true, () => false); | 317 | const exists = await fs.stat(dest).then(() => true, () => false); |
@@ -340,7 +343,6 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
340 | progressTitle: "Downloading rust-analyzer server", | 343 | progressTitle: "Downloading rust-analyzer server", |
341 | gunzip: true, | 344 | gunzip: true, |
342 | mode: 0o755, | 345 | mode: 0o755, |
343 | overwrite: true, | ||
344 | }); | 346 | }); |
345 | }); | 347 | }); |
346 | 348 | ||
@@ -366,6 +368,13 @@ async function isNixOs(): Promise<boolean> { | |||
366 | } | 368 | } |
367 | } | 369 | } |
368 | 370 | ||
371 | function isMusl(): boolean { | ||
372 | // We can detect Alpine by checking `/etc/os-release` but not Void Linux musl. | ||
373 | // Instead, we run `ldd` since it advertises the libc which it belongs to. | ||
374 | const res = spawnSync("ldd", ["--version"]); | ||
375 | return res.stderr != null && res.stderr.indexOf("musl libc") >= 0; | ||
376 | } | ||
377 | |||
369 | async function downloadWithRetryDialog<T>(state: PersistentState, downloadFunc: () => Promise<T>): Promise<T> { | 378 | async function downloadWithRetryDialog<T>(state: PersistentState, downloadFunc: () => Promise<T>): Promise<T> { |
370 | while (true) { | 379 | while (true) { |
371 | try { | 380 | try { |
@@ -435,6 +444,7 @@ function warnAboutExtensionConflicts() { | |||
435 | vscode.window.showWarningMessage( | 444 | vscode.window.showWarningMessage( |
436 | `You have both the ${fst[0]} (${fst[1]}) and ${sec[0]} (${sec[1]}) ` + | 445 | `You have both the ${fst[0]} (${fst[1]}) and ${sec[0]} (${sec[1]}) ` + |
437 | "plugins enabled. These are known to conflict and cause various functions of " + | 446 | "plugins enabled. These are known to conflict and cause various functions of " + |
438 | "both plugins to not work correctly. You should disable one of them.", "Got it"); | 447 | "both plugins to not work correctly. You should disable one of them.", "Got it") |
448 | .then(() => { }, console.error); | ||
439 | }; | 449 | }; |
440 | } | 450 | } |
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts index 1ab21e726..d39dc1baf 100644 --- a/editors/code/src/net.ts +++ b/editors/code/src/net.ts | |||
@@ -73,23 +73,16 @@ interface DownloadOpts { | |||
73 | dest: string; | 73 | dest: string; |
74 | mode?: number; | 74 | mode?: number; |
75 | gunzip?: boolean; | 75 | gunzip?: boolean; |
76 | overwrite?: boolean; | ||
77 | } | 76 | } |
78 | 77 | ||
79 | export async function download(opts: DownloadOpts) { | 78 | export async function download(opts: DownloadOpts) { |
80 | // Put artifact into a temporary file (in the same dir for simplicity) | 79 | // Put artifact into a temporary file (in the same dir for simplicity) |
81 | // to prevent partially downloaded files when user kills vscode | 80 | // to prevent partially downloaded files when user kills vscode |
81 | // This also avoids overwriting running executables | ||
82 | const dest = path.parse(opts.dest); | 82 | const dest = path.parse(opts.dest); |
83 | const randomHex = crypto.randomBytes(5).toString("hex"); | 83 | const randomHex = crypto.randomBytes(5).toString("hex"); |
84 | const tempFile = path.join(dest.dir, `${dest.name}${randomHex}`); | 84 | const tempFile = path.join(dest.dir, `${dest.name}${randomHex}`); |
85 | 85 | ||
86 | if (opts.overwrite) { | ||
87 | // Unlinking the exe file before moving new one on its place should prevent ETXTBSY error. | ||
88 | await fs.promises.unlink(opts.dest).catch(err => { | ||
89 | if (err.code !== "ENOENT") throw err; | ||
90 | }); | ||
91 | } | ||
92 | |||
93 | await vscode.window.withProgress( | 86 | await vscode.window.withProgress( |
94 | { | 87 | { |
95 | location: vscode.ProgressLocation.Notification, | 88 | location: vscode.ProgressLocation.Notification, |
@@ -99,13 +92,15 @@ export async function download(opts: DownloadOpts) { | |||
99 | async (progress, _cancellationToken) => { | 92 | async (progress, _cancellationToken) => { |
100 | let lastPercentage = 0; | 93 | let lastPercentage = 0; |
101 | await downloadFile(opts.url, tempFile, opts.mode, !!opts.gunzip, (readBytes, totalBytes) => { | 94 | await downloadFile(opts.url, tempFile, opts.mode, !!opts.gunzip, (readBytes, totalBytes) => { |
102 | const newPercentage = (readBytes / totalBytes) * 100; | 95 | const newPercentage = Math.round((readBytes / totalBytes) * 100); |
103 | progress.report({ | 96 | if (newPercentage !== lastPercentage) { |
104 | message: newPercentage.toFixed(0) + "%", | 97 | progress.report({ |
105 | increment: newPercentage - lastPercentage | 98 | message: `${newPercentage.toFixed(0)}%`, |
106 | }); | 99 | increment: newPercentage - lastPercentage |
107 | 100 | }); | |
108 | lastPercentage = newPercentage; | 101 | |
102 | lastPercentage = newPercentage; | ||
103 | } | ||
109 | }); | 104 | }); |
110 | } | 105 | } |
111 | ); | 106 | ); |
diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index 17573cd82..138e3f686 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts | |||
@@ -45,7 +45,7 @@ export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, | |||
45 | if (items.length === 0) { | 45 | if (items.length === 0) { |
46 | // it is the debug case, run always has at least 'cargo check ...' | 46 | // it is the debug case, run always has at least 'cargo check ...' |
47 | // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_runnables | 47 | // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_runnables |
48 | vscode.window.showErrorMessage("There's no debug target!"); | 48 | await vscode.window.showErrorMessage("There's no debug target!"); |
49 | return; | 49 | return; |
50 | } | 50 | } |
51 | 51 | ||
@@ -65,8 +65,8 @@ export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, | |||
65 | disposables.push( | 65 | disposables.push( |
66 | quickPick.onDidHide(() => close()), | 66 | quickPick.onDidHide(() => close()), |
67 | quickPick.onDidAccept(() => close(quickPick.selectedItems[0])), | 67 | quickPick.onDidAccept(() => close(quickPick.selectedItems[0])), |
68 | quickPick.onDidTriggerButton((_button) => { | 68 | quickPick.onDidTriggerButton(async (_button) => { |
69 | (async () => await makeDebugConfig(ctx, quickPick.activeItems[0].runnable))(); | 69 | await makeDebugConfig(ctx, quickPick.activeItems[0].runnable); |
70 | close(); | 70 | close(); |
71 | }), | 71 | }), |
72 | quickPick.onDidChangeActive((active) => { | 72 | quickPick.onDidChangeActive((active) => { |
@@ -128,13 +128,7 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise | |||
128 | throw `Unexpected runnable kind: ${runnable.kind}`; | 128 | throw `Unexpected runnable kind: ${runnable.kind}`; |
129 | } | 129 | } |
130 | 130 | ||
131 | const args = [...runnable.args.cargoArgs]; // should be a copy! | 131 | const args = createArgs(runnable); |
132 | if (runnable.args.cargoExtraArgs) { | ||
133 | args.push(...runnable.args.cargoExtraArgs); // Append user-specified cargo options. | ||
134 | } | ||
135 | if (runnable.args.executableArgs.length > 0) { | ||
136 | args.push('--', ...runnable.args.executableArgs); | ||
137 | } | ||
138 | 132 | ||
139 | const definition: tasks.CargoTaskDefinition = { | 133 | const definition: tasks.CargoTaskDefinition = { |
140 | type: tasks.TASK_TYPE, | 134 | type: tasks.TASK_TYPE, |
@@ -145,9 +139,21 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise | |||
145 | overrideCargo: runnable.args.overrideCargo, | 139 | overrideCargo: runnable.args.overrideCargo, |
146 | }; | 140 | }; |
147 | 141 | ||
142 | // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion | ||
148 | const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate() | 143 | const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate() |
149 | const cargoTask = await tasks.buildCargoTask(target, definition, runnable.label, args, config.cargoRunner, true); | 144 | const cargoTask = await tasks.buildCargoTask(target, definition, runnable.label, args, config.cargoRunner, true); |
150 | cargoTask.presentationOptions.clear = true; | 145 | cargoTask.presentationOptions.clear = true; |
151 | 146 | ||
152 | return cargoTask; | 147 | return cargoTask; |
153 | } | 148 | } |
149 | |||
150 | export function createArgs(runnable: ra.Runnable): string[] { | ||
151 | const args = [...runnable.args.cargoArgs]; // should be a copy! | ||
152 | if (runnable.args.cargoExtraArgs) { | ||
153 | args.push(...runnable.args.cargoExtraArgs); // Append user-specified cargo options. | ||
154 | } | ||
155 | if (runnable.args.executableArgs.length > 0) { | ||
156 | args.push('--', ...runnable.args.executableArgs); | ||
157 | } | ||
158 | return args; | ||
159 | } | ||
diff --git a/editors/code/src/snippets.ts b/editors/code/src/snippets.ts index fee736e7d..dc53ebe2e 100644 --- a/editors/code/src/snippets.ts +++ b/editors/code/src/snippets.ts | |||
@@ -62,7 +62,9 @@ function parseSnippet(snip: string): [string, [number, number]] | undefined { | |||
62 | const m = snip.match(/\$(0|\{0:([^}]*)\})/); | 62 | const m = snip.match(/\$(0|\{0:([^}]*)\})/); |
63 | if (!m) return undefined; | 63 | if (!m) return undefined; |
64 | const placeholder = m[2] ?? ""; | 64 | const placeholder = m[2] ?? ""; |
65 | const range: [number, number] = [m.index!!, placeholder.length]; | 65 | if (m.index == null) |
66 | return undefined; | ||
67 | const range: [number, number] = [m.index, placeholder.length]; | ||
66 | const insert = snip.replace(m[0], placeholder); | 68 | const insert = snip.replace(m[0], placeholder); |
67 | return [insert, range]; | 69 | return [insert, range]; |
68 | } | 70 | } |