]> git.lizzy.rs Git - rust.git/blob - editors/code/src/extension.ts
8654b603007bcbf3f30fd80fb6c7c7d0843f263b
[rust.git] / editors / code / src / extension.ts
1 import * as vscode from 'vscode';
2 import * as lc from 'vscode-languageclient';
3
4 import * as commands from './commands';
5 import { CargoWatchProvider } from './commands/cargo_watch';
6 import { ExpandMacroHoverProvider } from './commands/expand_macro';
7 import { HintsUpdater } from './commands/inlay_hints';
8 import {
9     interactivelyStartCargoWatch,
10     startCargoWatch
11 } from './commands/runnables';
12 import { SyntaxTreeContentProvider } from './commands/syntaxTree';
13 import * as events from './events';
14 import * as notifications from './notifications';
15 import { Server } from './server';
16
17 export function activate(context: vscode.ExtensionContext) {
18     function disposeOnDeactivation(disposable: vscode.Disposable) {
19         context.subscriptions.push(disposable);
20     }
21
22     function registerCommand(name: string, f: any) {
23         disposeOnDeactivation(vscode.commands.registerCommand(name, f));
24     }
25     function overrideCommand(
26         name: string,
27         f: (...args: any[]) => Promise<boolean>
28     ) {
29         const defaultCmd = `default:${name}`;
30         const original = (...args: any[]) =>
31             vscode.commands.executeCommand(defaultCmd, ...args);
32
33         try {
34             registerCommand(name, async (...args: any[]) => {
35                 const editor = vscode.window.activeTextEditor;
36                 if (
37                     !editor ||
38                     !editor.document ||
39                     editor.document.languageId !== 'rust'
40                 ) {
41                     return await original(...args);
42                 }
43                 if (!(await f(...args))) {
44                     return await original(...args);
45                 }
46             });
47         } catch (_) {
48             vscode.window.showWarningMessage(
49                 'Enhanced typing feature is disabled because of incompatibility with VIM extension, consider turning off rust-analyzer.enableEnhancedTyping: https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/README.md#settings'
50             );
51         }
52     }
53
54     // Commands are requests from vscode to the language server
55     registerCommand(
56         'rust-analyzer.analyzerStatus',
57         commands.analyzerStatus.makeCommand(context)
58     );
59     registerCommand('rust-analyzer.collectGarbage', () =>
60         Server.client.sendRequest<null>('rust-analyzer/collectGarbage', null)
61     );
62     registerCommand(
63         'rust-analyzer.matchingBrace',
64         commands.matchingBrace.handle
65     );
66     registerCommand('rust-analyzer.joinLines', commands.joinLines.handle);
67     registerCommand('rust-analyzer.parentModule', commands.parentModule.handle);
68     registerCommand('rust-analyzer.run', commands.runnables.handle);
69     // Unlike the above this does not send requests to the language server
70     registerCommand('rust-analyzer.runSingle', commands.runnables.handleSingle);
71     registerCommand(
72         'rust-analyzer.applySourceChange',
73         commands.applySourceChange.handle
74     );
75     registerCommand(
76         'rust-analyzer.showReferences',
77         (uri: string, position: lc.Position, locations: lc.Location[]) => {
78             vscode.commands.executeCommand(
79                 'editor.action.showReferences',
80                 vscode.Uri.parse(uri),
81                 Server.client.protocol2CodeConverter.asPosition(position),
82                 locations.map(Server.client.protocol2CodeConverter.asLocation)
83             );
84         }
85     );
86
87     if (Server.config.enableEnhancedTyping) {
88         overrideCommand('type', commands.onEnter.handle);
89     }
90
91     // Notifications are events triggered by the language server
92     const allNotifications: Iterable<
93         [string, lc.GenericNotificationHandler]
94     > = [
95         [
96             'rust-analyzer/publishDecorations',
97             notifications.publishDecorations.handle
98         ]
99     ];
100     const syntaxTreeContentProvider = new SyntaxTreeContentProvider();
101
102     // The events below are plain old javascript events, triggered and handled by vscode
103     vscode.window.onDidChangeActiveTextEditor(
104         events.changeActiveTextEditor.makeHandler(syntaxTreeContentProvider)
105     );
106
107     disposeOnDeactivation(
108         vscode.workspace.registerTextDocumentContentProvider(
109             'rust-analyzer',
110             syntaxTreeContentProvider
111         )
112     );
113
114     registerCommand(
115         'rust-analyzer.syntaxTree',
116         commands.syntaxTree.createHandle(syntaxTreeContentProvider)
117     );
118
119     vscode.workspace.onDidChangeTextDocument(
120         events.changeTextDocument.createHandler(syntaxTreeContentProvider),
121         null,
122         context.subscriptions
123     );
124
125     const expandMacroContentProvider = new ExpandMacroHoverProvider();
126
127     disposeOnDeactivation(
128         vscode.languages.registerHoverProvider(
129             'rust',
130             expandMacroContentProvider
131         )
132     );
133
134     const startServer = () => Server.start(allNotifications);
135     const reloadCommand = () => reloadServer(startServer);
136
137     vscode.commands.registerCommand('rust-analyzer.reload', reloadCommand);
138
139     // Executing `cargo watch` provides us with inline diagnostics on save
140     let provider: CargoWatchProvider | undefined;
141     interactivelyStartCargoWatch(context).then(p => {
142         provider = p;
143     });
144     registerCommand('rust-analyzer.startCargoWatch', () => {
145         if (provider) {
146             provider.start();
147         } else {
148             startCargoWatch(context).then(p => {
149                 provider = p;
150             });
151         }
152     });
153     registerCommand('rust-analyzer.stopCargoWatch', () => {
154         if (provider) {
155             provider.stop();
156         }
157     });
158
159     // Start the language server, finally!
160     startServer();
161
162     if (Server.config.displayInlayHints) {
163         const hintsUpdater = new HintsUpdater();
164         hintsUpdater.refreshHintsForVisibleEditors().then(() => {
165             // vscode may ignore top level hintsUpdater.refreshHintsForVisibleEditors()
166             // so update the hints once when the focus changes to guarantee their presence
167             let editorChangeDisposable: vscode.Disposable | null = null;
168             editorChangeDisposable = vscode.window.onDidChangeActiveTextEditor(
169                 _ => {
170                     if (editorChangeDisposable !== null) {
171                         editorChangeDisposable.dispose();
172                     }
173                     return hintsUpdater.refreshHintsForVisibleEditors();
174                 }
175             );
176
177             disposeOnDeactivation(
178                 vscode.window.onDidChangeVisibleTextEditors(_ =>
179                     hintsUpdater.refreshHintsForVisibleEditors()
180                 )
181             );
182             disposeOnDeactivation(
183                 vscode.workspace.onDidChangeTextDocument(e =>
184                     hintsUpdater.refreshHintsForVisibleEditors(e)
185                 )
186             );
187             disposeOnDeactivation(
188                 vscode.workspace.onDidChangeConfiguration(_ =>
189                     hintsUpdater.toggleHintsDisplay(
190                         Server.config.displayInlayHints
191                     )
192                 )
193             );
194         });
195     }
196 }
197
198 export function deactivate(): Thenable<void> {
199     if (!Server.client) {
200         return Promise.resolve();
201     }
202     return Server.client.stop();
203 }
204
205 async function reloadServer(startServer: () => void) {
206     if (Server.client != null) {
207         vscode.window.showInformationMessage('Reloading rust-analyzer...');
208         await Server.client.stop();
209         startServer();
210     }
211 }