Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/extensions/common/workspaceContains.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as resources from '../../../../base/common/resources.js';
7
import { URI, UriComponents } from '../../../../base/common/uri.js';
8
import { CancellationTokenSource, CancellationToken } from '../../../../base/common/cancellation.js';
9
import * as errors from '../../../../base/common/errors.js';
10
import { ExtensionIdentifier, IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';
11
import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
12
import { QueryBuilder } from '../../search/common/queryBuilder.js';
13
import { ISearchService } from '../../search/common/search.js';
14
import { toWorkspaceFolder } from '../../../../platform/workspace/common/workspace.js';
15
import { ILogService } from '../../../../platform/log/common/log.js';
16
import { promiseWithResolvers } from '../../../../base/common/async.js';
17
18
const WORKSPACE_CONTAINS_TIMEOUT = 7000;
19
20
export interface IExtensionActivationHost {
21
readonly logService: ILogService;
22
readonly folders: readonly UriComponents[];
23
readonly forceUsingSearch: boolean;
24
25
exists(uri: URI): Promise<boolean>;
26
checkExists(folders: readonly UriComponents[], includes: string[], token: CancellationToken): Promise<boolean>;
27
}
28
29
export interface IExtensionActivationResult {
30
activationEvent: string;
31
}
32
33
export function checkActivateWorkspaceContainsExtension(host: IExtensionActivationHost, desc: IExtensionDescription): Promise<IExtensionActivationResult | undefined> {
34
const activationEvents = desc.activationEvents;
35
if (!activationEvents) {
36
return Promise.resolve(undefined);
37
}
38
39
const fileNames: string[] = [];
40
const globPatterns: string[] = [];
41
42
for (const activationEvent of activationEvents) {
43
if (/^workspaceContains:/.test(activationEvent)) {
44
const fileNameOrGlob = activationEvent.substr('workspaceContains:'.length);
45
if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0 || host.forceUsingSearch) {
46
globPatterns.push(fileNameOrGlob);
47
} else {
48
fileNames.push(fileNameOrGlob);
49
}
50
}
51
}
52
53
if (fileNames.length === 0 && globPatterns.length === 0) {
54
return Promise.resolve(undefined);
55
}
56
57
const { promise, resolve } = promiseWithResolvers<IExtensionActivationResult | undefined>();
58
const activate = (activationEvent: string) => resolve({ activationEvent });
59
60
const fileNamePromise = Promise.all(fileNames.map((fileName) => _activateIfFileName(host, fileName, activate))).then(() => { });
61
const globPatternPromise = _activateIfGlobPatterns(host, desc.identifier, globPatterns, activate);
62
63
Promise.all([fileNamePromise, globPatternPromise]).then(() => {
64
// when all are done, resolve with undefined (relevant only if it was not activated so far)
65
resolve(undefined);
66
});
67
68
return promise;
69
}
70
71
async function _activateIfFileName(host: IExtensionActivationHost, fileName: string, activate: (activationEvent: string) => void): Promise<void> {
72
// find exact path
73
for (const uri of host.folders) {
74
if (await host.exists(resources.joinPath(URI.revive(uri), fileName))) {
75
// the file was found
76
activate(`workspaceContains:${fileName}`);
77
return;
78
}
79
}
80
}
81
82
async function _activateIfGlobPatterns(host: IExtensionActivationHost, extensionId: ExtensionIdentifier, globPatterns: string[], activate: (activationEvent: string) => void): Promise<void> {
83
if (globPatterns.length === 0) {
84
return Promise.resolve(undefined);
85
}
86
87
const tokenSource = new CancellationTokenSource();
88
const searchP = host.checkExists(host.folders, globPatterns, tokenSource.token);
89
90
const timer = setTimeout(async () => {
91
tokenSource.cancel();
92
host.logService.info(`Not activating extension '${extensionId.value}': Timed out while searching for 'workspaceContains' pattern ${globPatterns.join(',')}`);
93
}, WORKSPACE_CONTAINS_TIMEOUT);
94
95
let exists: boolean = false;
96
try {
97
exists = await searchP;
98
} catch (err) {
99
if (!errors.isCancellationError(err)) {
100
errors.onUnexpectedError(err);
101
}
102
}
103
104
tokenSource.dispose();
105
clearTimeout(timer);
106
107
if (exists) {
108
// a file was found matching one of the glob patterns
109
activate(`workspaceContains:${globPatterns.join(',')}`);
110
}
111
}
112
113
export function checkGlobFileExists(
114
accessor: ServicesAccessor,
115
folders: readonly UriComponents[],
116
includes: string[],
117
token: CancellationToken,
118
): Promise<boolean> {
119
const instantiationService = accessor.get(IInstantiationService);
120
const searchService = accessor.get(ISearchService);
121
const queryBuilder = instantiationService.createInstance(QueryBuilder);
122
const query = queryBuilder.file(folders.map(folder => toWorkspaceFolder(URI.revive(folder))), {
123
_reason: 'checkExists',
124
includePattern: includes,
125
exists: true
126
});
127
128
return searchService.fileSearch(query, token).then(
129
result => {
130
return !!result.limitHit;
131
},
132
err => {
133
if (!errors.isCancellationError(err)) {
134
return Promise.reject(err);
135
}
136
137
return false;
138
});
139
}
140
141