-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathIndexer.ts
More file actions
194 lines (163 loc) · 6.71 KB
/
Indexer.ts
File metadata and controls
194 lines (163 loc) · 6.71 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
// Copyright 2022 - 2025 The MathWorks, Inc.
import { TextDocument } from 'vscode-languageserver-textdocument'
import { URI } from 'vscode-uri'
import MatlabLifecycleManager from '../lifecycle/MatlabLifecycleManager'
import FileInfoIndex, { MatlabCodeData, RawCodeData } from './FileInfoIndex'
import * as fs from 'fs/promises'
import PathResolver from '../providers/navigation/PathResolver'
import ConfigurationManager from '../lifecycle/ConfigurationManager'
import MVM from '../mvm/impl/MVM'
import Logger from '../logging/Logger'
import parse from '../mvm/MdaParser'
import * as FileNameUtils from '../utils/FileNameUtils'
interface WorkspaceFileIndexedResponse {
isDone: boolean
filePath: string
codeData: RawCodeData
}
export default class Indexer {
private readonly INDEX_FOLDERS_RESPONSE_CHANNEL = '/matlabls/indexFolders/response'
constructor (
private readonly matlabLifecycleManager: MatlabLifecycleManager,
private readonly mvm: MVM,
private readonly pathResolver: PathResolver
) {}
/**
* Indexes the given TextDocument and caches the data.
*
* @param textDocument The document being indexed
*/
async indexDocument (textDocument: TextDocument): Promise<void> {
if (!this.mvm.isReady()) {
// MVM not yet ready
return
}
const rawCodeData = await this.getCodeData(textDocument.getText(), textDocument.uri)
if (rawCodeData === null) {
return
}
const parsedCodeData = FileInfoIndex.parseAndStoreCodeData(textDocument.uri, rawCodeData)
void this.indexAdditionalClassData(parsedCodeData, textDocument.uri)
}
/**
* Indexes all M files within the given list of folders.
*
* @param folders A list of folder URIs to be indexed
*/
async indexFolders (folders: string[]): Promise<void> {
const matlabConnection = await this.matlabLifecycleManager.getMatlabConnection()
if (matlabConnection == null || !this.mvm.isReady()) {
return
}
const channelId = matlabConnection.getChannelId()
const responseChannel = `${this.INDEX_FOLDERS_RESPONSE_CHANNEL}/${channelId}`
const analysisLimit = (await ConfigurationManager.getConfiguration()).maxFileSizeForAnalysis
const responseSub = matlabConnection.subscribe(responseChannel, message => {
const fileResults = message as WorkspaceFileIndexedResponse
if (fileResults.isDone) {
// No more files being indexed - safe to unsubscribe
matlabConnection.unsubscribe(responseSub)
}
// Convert file path to URI, which is used as an index when storing the code data
const fileUri = URI.file(fileResults.filePath).toString()
FileInfoIndex.parseAndStoreCodeData(fileUri, fileResults.codeData)
})
try {
const mdaFolders = {
mwtype: 'string',
mwsize: [1, folders.length],
mwdata: folders
}
const response = await this.mvm.feval(
'matlabls.handlers.indexing.parseInfoFromFolder',
0,
[mdaFolders, analysisLimit, responseChannel]
)
if ('error' in response) {
Logger.error('Error received while indexing folders:')
Logger.error(response.error.msg)
Logger.warn('Not all files may have been indexed successfully.')
matlabConnection.unsubscribe(responseSub)
}
} catch (err) {
Logger.error('Error caught while indexing folders:')
Logger.error(err as string)
Logger.warn('Not all files may have been indexed successfully.')
}
}
/**
* Indexes the file for the given URI and caches the data.
*
* @param uri The URI for the file being indexed
*/
async indexFile (uri: string): Promise<void> {
if (!this.mvm.isReady()) {
// MVM not yet ready
return
}
const filePath = FileNameUtils.getFilePathFromUri(uri)
const fileContentBuffer = await fs.readFile(filePath)
const code = fileContentBuffer.toString()
const rawCodeData = await this.getCodeData(code, uri)
if (rawCodeData === null) {
return
}
FileInfoIndex.parseAndStoreCodeData(uri, rawCodeData)
}
/**
* Retrieves data about classes, functions, and variables from the given document.
*
* @param code The code being parsed
* @param uri The URI associated with the code
* @param matlabConnection The connection to MATLAB®
*
* @returns The raw data extracted from the document
*/
private async getCodeData (code: string, uri: string): Promise<RawCodeData | null> {
const filePath = FileNameUtils.getFilePathFromUri(uri)
const analysisLimit = (await ConfigurationManager.getConfiguration()).maxFileSizeForAnalysis
try {
const response = await this.mvm.feval(
'matlabls.handlers.indexing.parseInfoFromDocument',
1,
[code, filePath, analysisLimit]
)
if ('error' in response) {
Logger.error('Error received while parsing file:')
Logger.error(response.error.msg)
return null
}
return parse(response.result[0]) as RawCodeData
} catch (err) {
Logger.error('Error caught while parsing file:')
Logger.error(err as string)
return null
}
}
/**
* Indexes any supplemental files if the parsed code data represents a class.
* This will index any other files in a @ directory, as well as any direct base classes.
*
* @param parsedCodeData The parsed code data
* @param matlabConnection The connection to MATLAB
* @param uri The document's URI
*/
private async indexAdditionalClassData (parsedCodeData: MatlabCodeData, uri: string): Promise<void> {
if (parsedCodeData.classInfo == null) {
return
}
// Queue indexing for other files in @ class directory
const classDefFolder = parsedCodeData.classInfo.classDefFolder
if (classDefFolder !== '') {
void this.indexFolders([classDefFolder])
}
// Find and queue indexing for parent classes
const baseClasses = parsedCodeData.classInfo.baseClasses
baseClasses.forEach(async baseClass => {
const resolvedUri = await this.pathResolver.resolvePath(baseClass, uri)
if (resolvedUri !== '' && resolvedUri !== null) {
void this.indexFile(resolvedUri)
}
})
}
}