Skip to content

Commit fc1eca4

Browse files
dklilleyGitHub Enterprise
authored andcommitted
Merge pull request mathworks#38 from development/dlilley.refactor.reverse-dependencies
Refactor to improve dependencies and testability of code
2 parents ab85188 + 2eedaed commit fc1eca4

19 files changed

+396
-285
lines changed

src/ClientConnection.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2024 The MathWorks, Inc.
2+
import { _Connection, createConnection, ProposedFeatures } from "vscode-languageserver/node"
3+
4+
export type Connection = _Connection
5+
6+
export default class ClientConnection {
7+
private static connection: Connection
8+
9+
/**
10+
* Retrieves the connection to the client. If no connection currently exists,
11+
* a new connection is created.
12+
*
13+
* @returns The connection to the client
14+
*/
15+
public static getConnection (): Connection {
16+
if (ClientConnection.connection == null) {
17+
ClientConnection.connection = createConnection(ProposedFeatures.all)
18+
}
19+
20+
return ClientConnection.connection
21+
}
22+
23+
/**
24+
* Sets the ClientConnection to a given object.
25+
* This API is primarily meant for testing purposes.
26+
*
27+
* @param connection The connection object to set
28+
*/
29+
public static setConnection (connection: Connection): void {
30+
ClientConnection.connection = connection
31+
}
32+
}

src/index.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
// Copyright 2022 - 2023 The MathWorks, Inc.
1+
// Copyright 2022 - 2024 The MathWorks, Inc.
22

33
// Start up the LSP server
4-
import { connection } from './server'
4+
import ClientConnection from './ClientConnection'
5+
import * as server from './server'
56

6-
// Listen on the connection
7-
connection.listen()
7+
// Start up the language server
8+
server.startServer()
9+
10+
// Listen on the client connection
11+
ClientConnection.getConnection().listen()

src/indexing/DocumentIndexer.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ const INDEXING_DELAY = 500 // Delay (in ms) after keystroke before attempting to
1010
* Handles indexing a currently open document to gather data about classes,
1111
* functions, and variables.
1212
*/
13-
class DocumentIndexer {
13+
export default class DocumentIndexer {
1414
private readonly pendingFilesToIndex = new Map<string, NodeJS.Timeout>()
1515

16+
constructor (private indexer: Indexer) {}
17+
1618
/**
1719
* Queues a document to be indexed. This handles debouncing so that
1820
* indexing is not performed on every keystroke.
@@ -36,7 +38,7 @@ class DocumentIndexer {
3638
* @param textDocument The document being indexed
3739
*/
3840
indexDocument (textDocument: TextDocument): void {
39-
void Indexer.indexDocument(textDocument)
41+
void this.indexer.indexDocument(textDocument)
4042
}
4143

4244
/**
@@ -63,12 +65,10 @@ class DocumentIndexer {
6365
const uri = textDocument.uri
6466
if (this.pendingFilesToIndex.has(uri)) {
6567
this.clearTimerForDocumentUri(uri)
66-
await Indexer.indexDocument(textDocument)
68+
await this.indexer.indexDocument(textDocument)
6769
}
6870
if (!FileInfoIndex.codeDataCache.has(uri)) {
69-
await Indexer.indexDocument(textDocument)
71+
await this.indexer.indexDocument(textDocument)
7072
}
7173
}
7274
}
73-
74-
export default new DocumentIndexer()

src/indexing/FileInfoIndex.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ export enum FunctionVisibility {
9595
* Serves as an cache of data extracted from files
9696
*/
9797
class FileInfoIndex {
98+
private static instance: FileInfoIndex
99+
98100
/**
99101
* Maps document URI to the code data
100102
*/
@@ -105,6 +107,14 @@ class FileInfoIndex {
105107
*/
106108
readonly classInfoCache = new Map<string, MatlabClassInfo>()
107109

110+
public static getInstance (): FileInfoIndex {
111+
if (FileInfoIndex.instance == null) {
112+
FileInfoIndex.instance = new FileInfoIndex()
113+
}
114+
115+
return FileInfoIndex.instance
116+
}
117+
108118
/**
109119
* Parses the raw data into a more usable form. Caches the resulting data
110120
* in the code data index.
@@ -489,4 +499,4 @@ function convertRange (codeDataRange: CodeDataRange): Range {
489499
)
490500
}
491501

492-
export default new FileInfoIndex()
502+
export default FileInfoIndex.getInstance()

src/indexing/Indexer.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,22 @@ interface WorkspaceFileIndexedResponse {
1414
codeData: RawCodeData
1515
}
1616

17-
class Indexer {
17+
export default class Indexer {
1818
private readonly INDEX_DOCUMENT_REQUEST_CHANNEL = '/matlabls/indexDocument/request'
1919
private readonly INDEX_DOCUMENT_RESPONSE_CHANNEL = '/matlabls/indexDocument/response'
2020

2121
private readonly INDEX_FOLDERS_REQUEST_CHANNEL = '/matlabls/indexFolders/request'
2222
private readonly INDEX_FOLDERS_RESPONSE_CHANNEL = '/matlabls/indexFolders/response'
2323

24+
constructor (private matlabLifecycleManager: MatlabLifecycleManager, private pathResolver: PathResolver) {}
25+
2426
/**
2527
* Indexes the given TextDocument and caches the data.
2628
*
2729
* @param textDocument The document being indexed
2830
*/
2931
async indexDocument (textDocument: TextDocument): Promise<void> {
30-
const matlabConnection = await MatlabLifecycleManager.getMatlabConnection()
32+
const matlabConnection = await this.matlabLifecycleManager.getMatlabConnection()
3133

3234
if (matlabConnection == null) {
3335
return
@@ -46,7 +48,7 @@ class Indexer {
4648
* @param folders A list of folder URIs to be indexed
4749
*/
4850
async indexFolders (folders: string[]): Promise<void> {
49-
const matlabConnection = await MatlabLifecycleManager.getMatlabConnection()
51+
const matlabConnection = await this.matlabLifecycleManager.getMatlabConnection()
5052

5153
if (matlabConnection == null) {
5254
return
@@ -79,7 +81,7 @@ class Indexer {
7981
* @param uri The URI for the file being indexed
8082
*/
8183
async indexFile (uri: string): Promise<void> {
82-
const matlabConnection = await MatlabLifecycleManager.getMatlabConnection()
84+
const matlabConnection = await this.matlabLifecycleManager.getMatlabConnection()
8385

8486
if (matlabConnection == null) {
8587
return
@@ -143,7 +145,7 @@ class Indexer {
143145
// Find and queue indexing for parent classes
144146
const baseClasses = parsedCodeData.classInfo.baseClasses
145147

146-
const resolvedBaseClasses = await PathResolver.resolvePaths(baseClasses, uri, matlabConnection)
148+
const resolvedBaseClasses = await this.pathResolver.resolvePaths(baseClasses, uri, matlabConnection)
147149

148150
resolvedBaseClasses.forEach(resolvedBaseClass => {
149151
const uri = resolvedBaseClass.uri
@@ -153,5 +155,3 @@ class Indexer {
153155
})
154156
}
155157
}
156-
157-
export default new Indexer()

src/indexing/WorkspaceIndexer.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
// Copyright 2022 - 2023 The MathWorks, Inc.
1+
// Copyright 2022 - 2024 The MathWorks, Inc.
22

33
import { ClientCapabilities, WorkspaceFolder, WorkspaceFoldersChangeEvent } from 'vscode-languageserver'
44
import ConfigurationManager from '../lifecycle/ConfigurationManager'
5-
import { connection } from '../server'
65
import Indexer from './Indexer'
6+
import ClientConnection from '../ClientConnection'
77

88
/**
99
* Handles indexing files in the user's workspace to gather data about classes,
1010
* functions, and variables.
1111
*/
12-
class WorkspaceIndexer {
12+
export default class WorkspaceIndexer {
1313
private isWorkspaceIndexingSupported = false
1414

15+
constructor (private indexer: Indexer) {}
16+
1517
/**
1618
* Sets up workspace change listeners, if supported.
1719
*
@@ -26,7 +28,7 @@ class WorkspaceIndexer {
2628
return
2729
}
2830

29-
connection.workspace.onDidChangeWorkspaceFolders((params: WorkspaceFoldersChangeEvent) => {
31+
ClientConnection.getConnection().workspace.onDidChangeWorkspaceFolders((params: WorkspaceFoldersChangeEvent) => {
3032
void this.handleWorkspaceFoldersAdded(params.added)
3133
})
3234
}
@@ -39,13 +41,13 @@ class WorkspaceIndexer {
3941
return
4042
}
4143

42-
const folders = await connection.workspace.getWorkspaceFolders()
44+
const folders = await ClientConnection.getConnection().workspace.getWorkspaceFolders()
4345

4446
if (folders == null) {
4547
return
4648
}
4749

48-
void Indexer.indexFolders(folders.map(folder => folder.uri))
50+
void this.indexer.indexFolders(folders.map(folder => folder.uri))
4951
}
5052

5153
/**
@@ -58,7 +60,7 @@ class WorkspaceIndexer {
5860
return
5961
}
6062

61-
void Indexer.indexFolders(folders.map(folder => folder.uri))
63+
void this.indexer.indexFolders(folders.map(folder => folder.uri))
6264
}
6365

6466
/**
@@ -73,5 +75,3 @@ class WorkspaceIndexer {
7375
return this.isWorkspaceIndexingSupported && shouldIndexWorkspace
7476
}
7577
}
76-
77-
export default new WorkspaceIndexer()

src/lifecycle/ConfigurationManager.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
// Copyright 2022 - 2023 The MathWorks, Inc.
1+
// Copyright 2022 - 2024 The MathWorks, Inc.
22

33
import { ClientCapabilities, DidChangeConfigurationNotification, DidChangeConfigurationParams } from 'vscode-languageserver'
44
import { reportTelemetrySettingsChange } from '../logging/TelemetryUtils'
5-
import { connection } from '../server'
65
import { getCliArgs } from '../utils/CliUtils'
6+
import ClientConnection from '../ClientConnection'
77

88
export enum Argument {
99
// Basic arguments
@@ -45,6 +45,8 @@ const SETTING_NAMES: SettingName[] = [
4545
]
4646

4747
class ConfigurationManager {
48+
private static instance: ConfigurationManager
49+
4850
private configuration: Settings | null = null
4951
private readonly defaultConfiguration: Settings
5052
private globalSettings: Settings
@@ -77,12 +79,22 @@ class ConfigurationManager {
7779
}
7880
}
7981

82+
public static getInstance (): ConfigurationManager {
83+
if (ConfigurationManager.instance == null) {
84+
ConfigurationManager.instance = new ConfigurationManager
85+
}
86+
87+
return ConfigurationManager.instance
88+
}
89+
8090
/**
8191
* Sets up the configuration manager
8292
*
8393
* @param capabilities The client capabilities
8494
*/
8595
setup (capabilities: ClientCapabilities): void {
96+
const connection = ClientConnection.getConnection()
97+
8698
this.hasConfigurationCapability = capabilities.workspace?.configuration != null
8799

88100
if (this.hasConfigurationCapability) {
@@ -101,6 +113,7 @@ class ConfigurationManager {
101113
async getConfiguration (): Promise<Settings> {
102114
if (this.hasConfigurationCapability) {
103115
if (this.configuration == null) {
116+
const connection = ClientConnection.getConnection()
104117
this.configuration = await connection.workspace.getConfiguration('MATLAB') as Settings
105118
}
106119

@@ -164,4 +177,4 @@ class ConfigurationManager {
164177
}
165178
}
166179

167-
export default new ConfigurationManager()
180+
export default ConfigurationManager.getInstance()

src/lifecycle/LifecycleNotificationHelper.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,18 @@ import NotificationService, { Notification } from '../notifications/Notification
44
import { ConnectionState } from './MatlabSession'
55

66
class LifecycleNotificationHelper {
7+
private static instance: LifecycleNotificationHelper
8+
79
didMatlabLaunchFail = false
810

11+
public static getInstance (): LifecycleNotificationHelper {
12+
if (LifecycleNotificationHelper.instance == null) {
13+
LifecycleNotificationHelper.instance = new LifecycleNotificationHelper()
14+
}
15+
16+
return LifecycleNotificationHelper.instance
17+
}
18+
919
/**
1020
* Sends notification to the language client of a change in the MATLAB® connection state.
1121
*
@@ -27,4 +37,4 @@ class LifecycleNotificationHelper {
2737
}
2838
}
2939

30-
export default new LifecycleNotificationHelper()
40+
export default LifecycleNotificationHelper.getInstance()

src/lifecycle/MatlabLifecycleManager.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import ConfigurationManager, { Argument, ConnectionTiming } from "./Configuratio
66
import { MatlabConnection } from "./MatlabCommunicationManager"
77
import MatlabSession, { launchNewMatlab, connectToMatlab } from './MatlabSession'
88

9-
class MatlabLifecycleManager {
9+
export default class MatlabLifecycleManager {
1010
eventEmitter = new EventEmitter()
1111

1212
private matlabSession: MatlabSession | null = null
@@ -182,5 +182,3 @@ function shouldConnectToRemoteMatlab (): boolean {
182182
// Assume we should connect to existing MATLAB if the matlabUrl startup flag has been provided
183183
return Boolean(ConfigurationManager.getArgument(Argument.MatlabUrl))
184184
}
185-
186-
export default new MatlabLifecycleManager()

0 commit comments

Comments
 (0)