(
logger: Logger,
getExcludedFolders: ((f: WorkspaceFolder | undefined) => string[]) | undefined,
options: { sort?: boolean; requirePubspec?: boolean, searchDepth: number, workspaceFolders?: WorkspaceFolder[], onlyWorkspaceRoots?: boolean },
)
| 130 | } |
| 131 | |
| 132 | export async function getAllProjectFoldersAndExclusions( |
| 133 | logger: Logger, |
| 134 | getExcludedFolders: ((f: WorkspaceFolder | undefined) => string[]) | undefined, |
| 135 | options: { sort?: boolean; requirePubspec?: boolean, searchDepth: number, workspaceFolders?: WorkspaceFolder[], onlyWorkspaceRoots?: boolean }, |
| 136 | ): Promise<ProjectFolderSearchResults> { |
| 137 | const workspaceFolders = options.workspaceFolders ?? getDartWorkspaceFolders(); |
| 138 | |
| 139 | // If an existing search is in progress, wait because it might populate the cache with the results |
| 140 | // we want. |
| 141 | if (inProgressProjectFolderSearch) { |
| 142 | await inProgressProjectFolderSearch; |
| 143 | } |
| 144 | |
| 145 | const cacheKey = `folders_${workspaceFolders.map((f) => f.uri.toString()).join(path.sep)}_${!!options.sort}_${!!options.onlyWorkspaceRoots}`; |
| 146 | const cachedFolders = projectFolderCache.get(cacheKey); |
| 147 | if (cachedFolders) { |
| 148 | logger.info(`Returning cached results for project search`); |
| 149 | return cachedFolders; |
| 150 | } |
| 151 | |
| 152 | // Track this search so other searches can wait on it. |
| 153 | const completer = new PromiseCompleter<void>(); |
| 154 | inProgressProjectFolderSearch = completer.promise; |
| 155 | const tokenSource = new vs.CancellationTokenSource(); |
| 156 | try { |
| 157 | let startTimeMs = new Date().getTime(); |
| 158 | |
| 159 | const topLevelFolders = workspaceFolders.map((w) => fsPath(w.uri)); |
| 160 | let allExcludedFolders = getExcludedFolders ? flatMap(workspaceFolders, getExcludedFolders) : []; |
| 161 | const resultsPromise = findProjectFolders(logger, topLevelFolders, allExcludedFolders, options, tokenSource.token); |
| 162 | |
| 163 | tokenSource.token.onCancellationRequested((_) => { |
| 164 | logger.info(`Project search was cancelled after ${new Date().getTime() - startTimeMs}ms (was searching ${options.searchDepth} levels)`); |
| 165 | }); |
| 166 | await withProgressIfSlow( |
| 167 | resultsPromise, |
| 168 | tokenSource, |
| 169 | projectSearchProgressText, |
| 170 | { showAfterMs: projectSearchProgressNotificationDelayInMs }, |
| 171 | ); |
| 172 | tokenSource.dispose(); |
| 173 | |
| 174 | let projectFolders = await resultsPromise; |
| 175 | logger.info(`Took ${new Date().getTime() - startTimeMs}ms to search for ${projectFolders.length} projects (${options.searchDepth} levels)`); |
| 176 | logger.info(` Found projects:`); |
| 177 | for (const projectFolder of projectFolders) |
| 178 | logger.info(` ${projectFolder}`); |
| 179 | startTimeMs = new Date().getTime(); |
| 180 | |
| 181 | // Filter out any folders excluded by analysis_options. |
| 182 | try { |
| 183 | const excludedFolders = getAnalysisOptionsExcludedFolders(logger, projectFolders); |
| 184 | projectFolders = projectFolders.filter((p) => !excludedFolders.find((ex) => isWithinPathOrEqual(p, ex))); |
| 185 | logger.info(`Took ${new Date().getTime() - startTimeMs}ms to filter out excluded projects (${excludedFolders.length} exclusion rules). ${projectFolders.length} projects remain.`); |
| 186 | |
| 187 | allExcludedFolders = allExcludedFolders.concat(excludedFolders); |
| 188 | } catch (e) { |
| 189 | logger.error(`Failed to filter out analysis_options exclusions: ${e}`); |
no test coverage detected