import * as vscode from "vscode"; import * as lc from "vscode-languageclient/node"; import * as commands from "./commands"; import { type CommandFactory, Ctx, fetchWorkspace } from "./ctx"; import * as diagnostics from "./diagnostics"; import { activateTaskProvider } from "./tasks"; import { setContextValue } from "./util"; import { initializeDebugSessionTrackingAndRebuild } from "./debug"; const RUST_PROJECT_CONTEXT_NAME = "inRustProject"; export interface RustAnalyzerExtensionApi { // FIXME: this should be non-optional readonly client?: lc.LanguageClient; } export async function deactivate() { await setContextValue(RUST_PROJECT_CONTEXT_NAME, undefined); } export async function activate( context: vscode.ExtensionContext, ): Promise { checkConflictingExtensions(); const ctx = new Ctx(context, createCommands(), fetchWorkspace()); // VS Code doesn't show a notification when an extension fails to activate // so we do it ourselves. const api = await activateServer(ctx).catch((err) => { void vscode.window.showErrorMessage( `Cannot activate rust-analyzer extension: ${err.message}`, ); throw err; }); await setContextValue(RUST_PROJECT_CONTEXT_NAME, true); return api; } async function activateServer(ctx: Ctx): Promise { if (ctx.workspace.kind === "Workspace Folder") { ctx.pushExtCleanup(activateTaskProvider(ctx.config)); } const diagnosticProvider = new diagnostics.TextDocumentProvider(ctx); ctx.pushExtCleanup( vscode.workspace.registerTextDocumentContentProvider( diagnostics.URI_SCHEME, diagnosticProvider, ), ); const decorationProvider = new diagnostics.AnsiDecorationProvider(ctx); ctx.pushExtCleanup(decorationProvider); async function decorateVisibleEditors(document: vscode.TextDocument) { for (const editor of vscode.window.visibleTextEditors) { if (document === editor.document) { await decorationProvider.provideDecorations(editor); } } } vscode.workspace.onDidChangeTextDocument( async (event) => await decorateVisibleEditors(event.document), null, ctx.subscriptions, ); vscode.workspace.onDidOpenTextDocument(decorateVisibleEditors, null, ctx.subscriptions); vscode.window.onDidChangeActiveTextEditor( async (editor) => { if (editor) { diagnosticProvider.triggerUpdate(editor.document.uri); await decorateVisibleEditors(editor.document); } }, null, ctx.subscriptions, ); vscode.window.onDidChangeVisibleTextEditors( async (visibleEditors) => { for (const editor of visibleEditors) { diagnosticProvider.triggerUpdate(editor.document.uri); await decorationProvider.provideDecorations(editor); } }, null, ctx.subscriptions, ); vscode.workspace.onDidChangeWorkspaceFolders( async (_) => ctx.onWorkspaceFolderChanges(), null, ctx.subscriptions, ); vscode.workspace.onDidChangeConfiguration( async (_) => { await ctx.client?.sendNotification(lc.DidChangeConfigurationNotification.type, { settings: "", }); }, null, ctx.subscriptions, ); if (ctx.config.debug.buildBeforeRestart) { initializeDebugSessionTrackingAndRebuild(ctx); } if (ctx.config.initializeStopped) { ctx.setServerStatus({ health: "stopped", }); } else { await ctx.start(); } return ctx; } function createCommands(): Record { return { onEnter: { enabled: commands.onEnter, disabled: (_) => () => vscode.commands.executeCommand("default:type", { text: "\n" }), }, restartServer: { enabled: (ctx) => async () => { await ctx.restart(); }, disabled: (ctx) => async () => { await ctx.start(); }, }, startServer: { enabled: (ctx) => async () => { await ctx.start(); }, disabled: (ctx) => async () => { await ctx.start(); }, }, stopServer: { enabled: (ctx) => async () => { // FIXME: We should re-use the client, that is ctx.deactivate() if none of the configs have changed await ctx.stopAndDispose(); ctx.setServerStatus({ health: "stopped", }); }, disabled: (_) => async () => {}, }, analyzerStatus: { enabled: commands.analyzerStatus }, memoryUsage: { enabled: commands.memoryUsage }, reloadWorkspace: { enabled: commands.reloadWorkspace }, rebuildProcMacros: { enabled: commands.rebuildProcMacros }, matchingBrace: { enabled: commands.matchingBrace }, joinLines: { enabled: commands.joinLines }, parentModule: { enabled: commands.parentModule }, syntaxTree: { enabled: commands.syntaxTree }, viewHir: { enabled: commands.viewHir }, viewMir: { enabled: commands.viewMir }, interpretFunction: { enabled: commands.interpretFunction }, viewFileText: { enabled: commands.viewFileText }, viewItemTree: { enabled: commands.viewItemTree }, viewCrateGraph: { enabled: commands.viewCrateGraph }, viewFullCrateGraph: { enabled: commands.viewFullCrateGraph }, expandMacro: { enabled: commands.expandMacro }, run: { enabled: commands.run }, copyRunCommandLine: { enabled: commands.copyRunCommandLine }, debug: { enabled: commands.debug }, newDebugConfig: { enabled: commands.newDebugConfig }, openDocs: { enabled: commands.openDocs }, openExternalDocs: { enabled: commands.openExternalDocs }, openCargoToml: { enabled: commands.openCargoToml }, peekTests: { enabled: commands.peekTests }, moveItemUp: { enabled: commands.moveItemUp }, moveItemDown: { enabled: commands.moveItemDown }, cancelFlycheck: { enabled: commands.cancelFlycheck }, clearFlycheck: { enabled: commands.clearFlycheck }, runFlycheck: { enabled: commands.runFlycheck }, ssr: { enabled: commands.ssr }, serverVersion: { enabled: commands.serverVersion }, viewMemoryLayout: { enabled: commands.viewMemoryLayout }, toggleCheckOnSave: { enabled: commands.toggleCheckOnSave }, toggleLSPLogs: { enabled: commands.toggleLSPLogs }, openWalkthrough: { enabled: commands.openWalkthrough }, // Internal commands which are invoked by the server. applyActionGroup: { enabled: commands.applyActionGroup }, applySnippetWorkspaceEdit: { enabled: commands.applySnippetWorkspaceEditCommand }, debugSingle: { enabled: commands.debugSingle }, gotoLocation: { enabled: commands.gotoLocation }, hoverRefCommandProxy: { enabled: commands.hoverRefCommandProxy }, resolveCodeAction: { enabled: commands.resolveCodeAction }, runSingle: { enabled: commands.runSingle }, showReferences: { enabled: commands.showReferences }, triggerParameterHints: { enabled: commands.triggerParameterHints }, rename: { enabled: commands.rename }, openLogs: { enabled: commands.openLogs }, revealDependency: { enabled: commands.revealDependency }, }; } function checkConflictingExtensions() { if (vscode.extensions.getExtension("rust-lang.rust")) { vscode.window .showWarningMessage( `You have both the rust-analyzer (rust-lang.rust-analyzer) and Rust (rust-lang.rust) ` + "plugins enabled. These are known to conflict and cause various functions of " + "both plugins to not work correctly. You should disable one of them.", "Got it", ) .then(() => {}, console.error); } }