-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathserver.ts
More file actions
370 lines (308 loc) · 16.4 KB
/
server.ts
File metadata and controls
370 lines (308 loc) · 16.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
// Copyright 2022 - 2025 The MathWorks, Inc.
import { TextDocument } from 'vscode-languageserver-textdocument'
import { ClientCapabilities, InitializeParams, InitializeResult, TextDocuments } from 'vscode-languageserver/node'
import DocumentIndexer from './indexing/DocumentIndexer'
import WorkspaceIndexer from './indexing/WorkspaceIndexer'
import ConfigurationManager, { ConnectionTiming } from './lifecycle/ConfigurationManager'
import MatlabLifecycleManager from './lifecycle/MatlabLifecycleManager'
import Logger from './logging/Logger'
import { Actions, reportTelemetryAction } from './logging/TelemetryUtils'
import NotificationService, { Notification } from './notifications/NotificationService'
import CompletionSupportProvider from './providers/completion/CompletionSupportProvider'
import FormatSupportProvider from './providers/formatting/FormatSupportProvider'
import LintingSupportProvider from './providers/linting/LintingSupportProvider'
import ExecuteCommandProvider, { MatlabLSCommands } from './providers/lspCommands/ExecuteCommandProvider'
import NavigationSupportProvider from './providers/navigation/NavigationSupportProvider'
import LifecycleNotificationHelper from './lifecycle/LifecycleNotificationHelper'
import MVM, { IMVM, MatlabMVMConnectionState } from './mvm/impl/MVM'
import FoldingSupportProvider from './providers/folding/FoldingSupportProvider'
import ClientConnection from './ClientConnection'
import PathResolver from './providers/navigation/PathResolver'
import Indexer from './indexing/Indexer'
import RenameSymbolProvider from './providers/rename/RenameSymbolProvider'
import HighlightSymbolProvider from './providers/highlighting/HighlightSymbolProvider'
import { RequestType } from './indexing/SymbolSearchService'
import { cacheAndClearProxyEnvironmentVariables } from './utils/ProxyUtils'
import MatlabDebugAdaptorServer from './debug/MatlabDebugAdaptorServer'
import { DebugServices } from './debug/DebugServices'
import MVMServer from './mvm/MVMServer'
import { stopLicensingServer } from './licensing/server'
import { setInstallPath } from './licensing/config'
import { handleInstallPathSettingChanged, handleSignInChanged, setupLicensingNotificationListenersAndUpdateClient } from './utils/LicensingUtils'
import PathSynchronizer from './lifecycle/PathSynchronizer'
import { URI } from 'vscode-uri'
import GraphicsPrewarmService from './lifecycle/GraphicsPrewarmService'
import { handleDefaultEditorConfigChange, setDefaultEditorVsCode } from './utils/DefaultEditorUtils'
import FileInfoIndex from './indexing/FileInfoIndex'
export async function startServer (): Promise<void> {
cacheAndClearProxyEnvironmentVariables()
// Create a connection for the server
const connection = ClientConnection.getConnection()
// Initialize Logger
Logger.initialize(connection.console)
// Instantiate services
const matlabLifecycleManager = new MatlabLifecycleManager()
const mvm = new MVM(matlabLifecycleManager, Logger);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const mvmServer = new MVMServer(mvm, NotificationService);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const matlabDebugAdaptor = new MatlabDebugAdaptorServer(mvm, new DebugServices(mvm));
const pathResolver = new PathResolver(mvm)
const fileInfoIndex = new FileInfoIndex()
const indexer = new Indexer(matlabLifecycleManager, mvm, fileInfoIndex)
const workspaceIndexer = new WorkspaceIndexer(indexer)
const documentIndexer = new DocumentIndexer(indexer, fileInfoIndex)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const graphicsPrewarmService = new GraphicsPrewarmService(mvm, ConfigurationManager)
const formatSupportProvider = new FormatSupportProvider(matlabLifecycleManager, mvm)
const foldingSupportProvider = new FoldingSupportProvider(matlabLifecycleManager, mvm)
const lintingSupportProvider = new LintingSupportProvider(matlabLifecycleManager, mvm)
const executeCommandProvider = new ExecuteCommandProvider(lintingSupportProvider)
const completionSupportProvider = new CompletionSupportProvider(matlabLifecycleManager, mvm)
const navigationSupportProvider = new NavigationSupportProvider(matlabLifecycleManager, fileInfoIndex, indexer, documentIndexer, pathResolver)
const renameSymbolProvider = new RenameSymbolProvider(matlabLifecycleManager, documentIndexer, fileInfoIndex)
const highlightSymbolProvider = new HighlightSymbolProvider(matlabLifecycleManager, documentIndexer, indexer, fileInfoIndex)
let pathSynchronizer: PathSynchronizer | null
// Create basic text document manager
const documentManager: TextDocuments<TextDocument> = new TextDocuments(TextDocument)
let hasMatlabBeenRequested: boolean = false
matlabLifecycleManager.eventEmitter.on('connected', () => {
// Handle things after MATLAB® has launched
hasMatlabBeenRequested = false
})
mvm.on(IMVM.Events.stateChange, (state: MatlabMVMConnectionState) => {
if (state === MatlabMVMConnectionState.CONNECTED) {
// Handle when the MVM has connected
mvm.feval('matlabls.utils.startupHelper', 0, [])
// Initiate workspace indexing
void workspaceIndexer.indexWorkspace()
documentManager.all().forEach(textDocument => {
// Lint the open documents
void lintingSupportProvider.lintDocument(textDocument)
// Index the open document
void documentIndexer.indexDocument(textDocument)
void navigationSupportProvider.handleDocumentSymbol(textDocument.uri, documentManager, RequestType.DocumentSymbol)
})
}
})
let capabilities: ClientCapabilities
// Handles an initialization request
connection.onInitialize((params: InitializeParams) => {
capabilities = params.capabilities
// Defines the capabilities supported by this language server
const initResult: InitializeResult = {
capabilities: {
codeActionProvider: true,
completionProvider: {
triggerCharacters: [
'.', // Struct/class properties, package names, etc.
'(', // Function call
' ', // Command-style function call
',', // Function arguments
'/', // File path
'\\' // File path
]
},
definitionProvider: true,
documentFormattingProvider: true,
documentRangeFormattingProvider: true,
executeCommandProvider: {
commands: Object.values(MatlabLSCommands)
},
foldingRangeProvider: true,
referencesProvider: true,
signatureHelpProvider: {
triggerCharacters: ['(', ',']
},
documentSymbolProvider: true,
renameProvider: {
prepareProvider: true
},
documentHighlightProvider: true
}
}
return initResult
})
// Handles the initialized notification
/* eslint-disable @typescript-eslint/no-explicit-any */
connection.onInitialized(async () => {
ConfigurationManager.setup(capabilities)
// Add callbacks when settings change.
ConfigurationManager.addSettingCallback('signIn', handleSignInChanged)
ConfigurationManager.addSettingCallback('installPath', handleInstallPathSettingChanged)
ConfigurationManager.addSettingCallback('defaultEditor', configuration => handleDefaultEditorConfigChange(configuration, mvm))
const configuration = await ConfigurationManager.getConfiguration()
// If "signIn" setting is checked, setup notification listeners for it.
if (configuration.signIn) {
await setupLicensingNotificationListenersAndUpdateClient(matlabLifecycleManager)
// If installPath setting is not empty, update installPath in licensing config required for its workflows.
if (configuration.installPath !== '') {
setInstallPath(configuration.installPath)
}
}
workspaceIndexer.setupCallbacks(capabilities)
if (capabilities.workspace?.workspaceFolders != null) {
// If workspace folders are supported, try to synchronize the MATLAB path with the user's workspace.
pathSynchronizer = new PathSynchronizer(matlabLifecycleManager, mvm)
pathSynchronizer.initialize()
}
void startMatlabIfOnStartLaunch()
})
async function startMatlabIfOnStartLaunch (): Promise<void> {
// Launch MATLAB if it should be launched early
const connectionTiming = (await ConfigurationManager.getConfiguration()).matlabConnectionTiming
if (connectionTiming === ConnectionTiming.OnStart) {
void matlabLifecycleManager.connectToMatlab().catch(reason => {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
Logger.error(`MATLAB onStart connection failed: ${reason}`)
})
}
}
// Handles a shutdown request
connection.onShutdown(async () => {
// Shut down MATLAB
matlabLifecycleManager.disconnectFromMatlab()
// If licensing workflows are enabled, shutdown the licensing server too.
if ((await ConfigurationManager.getConfiguration()).signIn) {
stopLicensingServer();
}
})
interface MatlabConnectionStatusParam {
connectionAction: 'connect' | 'disconnect'
}
// Set up connection notification listeners
NotificationService.registerNotificationListener(
Notification.MatlabConnectionClientUpdate,
(data: MatlabConnectionStatusParam) => {
switch (data.connectionAction) {
case 'connect':
void matlabLifecycleManager.connectToMatlab().catch(reason => {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
Logger.error(`Connection request failed: ${reason}`)
})
break
case 'disconnect':
matlabLifecycleManager.disconnectFromMatlab()
}
}
)
// Set up MATLAB startup request listener
NotificationService.registerNotificationListener(
Notification.MatlabRequestInstance,
async () => { // eslint-disable-line @typescript-eslint/no-misused-promises
if (hasMatlabBeenRequested) {
return;
}
hasMatlabBeenRequested = true;
const matlabConnection = await matlabLifecycleManager.getMatlabConnection(true);
if (matlabConnection === null) {
LifecycleNotificationHelper.notifyMatlabRequirement()
}
}
)
NotificationService.registerNotificationListener(
Notification.TerminalCompletionRequest,
async (data) => { // eslint-disable-line @typescript-eslint/no-misused-promises
NotificationService.sendNotification(Notification.TerminalCompletionResponse, {
requestId: data.requestId,
result: await completionSupportProvider.getCompletions(data.code, data.offset)
});
}
)
// Notification service to recieve VS Code executable path during in-session config changes and trigger updating MATLAB settings
NotificationService.registerNotificationListener(
Notification.EditorExecutablePath,
async (data) => {
const configuration = await ConfigurationManager.getConfiguration()
await setDefaultEditorVsCode(configuration, mvm, data)
}
)
// Handles files opened
documentManager.onDidOpen(params => {
reportFileOpened(params.document)
void lintingSupportProvider.lintDocument(params.document)
void documentIndexer.indexDocument(params.document)
void navigationSupportProvider.handleDocumentSymbol(params.document.uri, documentManager, RequestType.DocumentSymbol)
})
documentManager.onDidClose(params => {
lintingSupportProvider.clearDiagnosticsForDocument(params.document)
})
// Handles files saved
documentManager.onDidSave(params => {
// Trigger any post-save operations
if (mvm.isReady()) {
const filePath = URI.parse(params.document.uri).fsPath
void mvm.feval('matlabls.utils.saveHelper', 0, [filePath])
}
void lintingSupportProvider.lintDocument(params.document)
})
// Handles changes to the text document
documentManager.onDidChangeContent(params => {
if (matlabLifecycleManager.isMatlabConnected()) {
// Only want to lint on content changes when linting is being backed by MATLAB
lintingSupportProvider.queueLintingForDocument(params.document)
documentIndexer.queueIndexingForDocument(params.document)
}
})
// Handle execute command requests
connection.onExecuteCommand(params => {
void executeCommandProvider.handleExecuteCommand(params, documentManager)
})
/** -------------------- COMPLETION SUPPORT -------------------- **/
connection.onCompletion(async params => {
// Gather a list of possible completions to be displayed by the IDE
return await completionSupportProvider.handleCompletionRequest(params, documentManager)
})
connection.onSignatureHelp(async params => {
// Gather a list of possible function signatures to be displayed by the IDE
return await completionSupportProvider.handleSignatureHelpRequest(params, documentManager)
})
/** -------------------- FOLDING SUPPORT -------------------- **/
connection.onFoldingRanges(async params => {
// Retrieve the folding ranges
// If there are valid folding ranges, hand them back to the IDE
// Else, return null, so the IDE falls back to indent-based folding
return await foldingSupportProvider.handleFoldingRangeRequest(params, documentManager)
})
/** -------------------- FORMATTING SUPPORT -------------------- **/
connection.onDocumentFormatting(async params => {
// Gather a set of document edits required for formatting, which the IDE will execute
return await formatSupportProvider.handleDocumentFormatRequest(params, documentManager)
})
connection.onDocumentRangeFormatting(async params => {
return await formatSupportProvider.handleDocumentRangeFormatRequest(params, documentManager)
})
/** -------------------- LINTING SUPPORT -------------------- **/
connection.onCodeAction(params => {
// Retrieve a list of possible code actions to be displayed by the IDE
return lintingSupportProvider.handleCodeActionRequest(params)
})
/** -------------------- NAVIGATION SUPPORT -------------------- **/
connection.onDefinition(async params => {
return await navigationSupportProvider.handleDefOrRefRequest(params, documentManager, RequestType.Definition)
})
connection.onReferences(async params => {
return await navigationSupportProvider.handleDefOrRefRequest(params, documentManager, RequestType.References)
})
connection.onDocumentSymbol(async params => {
return await navigationSupportProvider.handleDocumentSymbol(params.textDocument.uri, documentManager, RequestType.DocumentSymbol)
})
// Start listening to open/change/close text document events
documentManager.listen(connection)
/** -------------------- RENAME SUPPORT -------------------- **/
connection.onPrepareRename(async params => {
return await renameSymbolProvider.prepareRename(params, documentManager)
})
connection.onRenameRequest(async params => {
return await renameSymbolProvider.handleRenameRequest(params, documentManager)
})
/** -------------- VARIABLE HIGHLIGHTING SUPPORT --------------- **/
connection.onDocumentHighlight(async params => {
return await highlightSymbolProvider.handleDocumentHighlightRequest(params, documentManager)
})
}
/** -------------------- Helper Functions -------------------- **/
function reportFileOpened (document: TextDocument): void {
const roughSize = Math.ceil(document.getText().length / 1024) // in KB
reportTelemetryAction(Actions.OpenFile, roughSize.toString())
}