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