Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/microsoft/vscode.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBabak K. Shandiz <babak.k.shandiz@gmail.com>2022-11-10 02:50:24 +0300
committerGitHub <noreply@github.com>2022-11-10 02:50:24 +0300
commit7fc3b1fbda9e3bde2049ac1aa48a8133984b2c47 (patch)
tree8fe16548e097d7a41e652b62a6228b9f08ad40b0
parent245c0c781c96f7f39b170b0090f16390071f407d (diff)
🎁 Add `search` to file location methods in tasks (#165156)
-rw-r--r--src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts25
-rw-r--r--src/vs/workbench/contrib/tasks/common/problemMatcher.ts137
2 files changed, 151 insertions, 11 deletions
diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts
index 0a35133ca7d..6e004ffba4d 100644
--- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts
+++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts
@@ -16,6 +16,7 @@ import * as resources from 'vs/base/common/resources';
import Severity from 'vs/base/common/severity';
import * as Types from 'vs/base/common/types';
import * as nls from 'vs/nls';
+import { asArray } from 'vs/base/common/arrays';
import { IModelService } from 'vs/editor/common/services/model';
import { IFileService } from 'vs/platform/files/common/files';
@@ -1648,7 +1649,13 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
matcher = value;
}
if (matcher && matcher.filePrefix) {
- this._collectVariables(variables, matcher.filePrefix);
+ if (Types.isString(matcher.filePrefix)) {
+ this._collectVariables(variables, matcher.filePrefix);
+ } else {
+ for (const fp of [...asArray(matcher.filePrefix.include || []), ...asArray(matcher.filePrefix.exclude || [])]) {
+ this._collectVariables(variables, fp);
+ }
+ }
}
});
}
@@ -1710,7 +1717,21 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
copy.uriProvider = taskSystemInfo.uriProvider;
}
if (hasFilePrefix) {
- copy.filePrefix = await this._resolveVariable(resolver, copy.filePrefix);
+ const filePrefix = copy.filePrefix;
+ if (Types.isString(filePrefix)) {
+ copy.filePrefix = await this._resolveVariable(resolver, filePrefix);
+ } else if (filePrefix !== undefined) {
+ if (filePrefix.include) {
+ filePrefix.include = Array.isArray(filePrefix.include)
+ ? await Promise.all(filePrefix.include.map(x => this._resolveVariable(resolver, x)))
+ : await this._resolveVariable(resolver, filePrefix.include);
+ }
+ if (filePrefix.exclude) {
+ filePrefix.exclude = Array.isArray(filePrefix.exclude)
+ ? await Promise.all(filePrefix.exclude.map(x => this._resolveVariable(resolver, x)))
+ : await this._resolveVariable(resolver, filePrefix.exclude);
+ }
+ }
}
result.push(copy);
}
diff --git a/src/vs/workbench/contrib/tasks/common/problemMatcher.ts b/src/vs/workbench/contrib/tasks/common/problemMatcher.ts
index de370e64892..f67b71187ed 100644
--- a/src/vs/workbench/contrib/tasks/common/problemMatcher.ts
+++ b/src/vs/workbench/contrib/tasks/common/problemMatcher.ts
@@ -17,17 +17,20 @@ import { URI } from 'vs/base/common/uri';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { ValidationStatus, ValidationState, IProblemReporter, Parser } from 'vs/base/common/parsers';
import { IStringDictionary } from 'vs/base/common/collections';
+import { asArray } from 'vs/base/common/arrays';
+import { Schemas as NetworkSchemas } from 'vs/base/common/network';
import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { Event, Emitter } from 'vs/base/common/event';
-import { IFileService, IFileStatWithPartialMetadata } from 'vs/platform/files/common/files';
+import { FileType, IFileService, IFileStatWithPartialMetadata, IFileSystemProvider } from 'vs/platform/files/common/files';
export enum FileLocationKind {
Default,
Relative,
Absolute,
- AutoDetect
+ AutoDetect,
+ Search
}
export module FileLocationKind {
@@ -39,6 +42,8 @@ export module FileLocationKind {
return FileLocationKind.Relative;
} else if (value === 'autodetect') {
return FileLocationKind.AutoDetect;
+ } else if (value === 'search') {
+ return FileLocationKind.Search;
} else {
return undefined;
}
@@ -132,7 +137,7 @@ export interface ProblemMatcher {
source?: string;
applyTo: ApplyToKind;
fileLocation: FileLocationKind;
- filePrefix?: string;
+ filePrefix?: string | Config.SearchFileLocationArgs;
pattern: IProblemPattern | IProblemPattern[];
severity?: Severity;
watching?: IWatchingMatcher;
@@ -192,7 +197,7 @@ export async function getResource(filename: string, matcher: ProblemMatcher, fil
let fullPath: string | undefined;
if (kind === FileLocationKind.Absolute) {
fullPath = filename;
- } else if ((kind === FileLocationKind.Relative) && matcher.filePrefix) {
+ } else if ((kind === FileLocationKind.Relative) && matcher.filePrefix && Types.isString(matcher.filePrefix)) {
fullPath = join(matcher.filePrefix, filename);
} else if (kind === FileLocationKind.AutoDetect) {
const matcherClone = Objects.deepClone(matcher);
@@ -212,6 +217,18 @@ export async function getResource(filename: string, matcher: ProblemMatcher, fil
matcherClone.fileLocation = FileLocationKind.Absolute;
return getResource(filename, matcherClone);
+ } else if (kind === FileLocationKind.Search && fileService) {
+ const fsProvider = fileService.getProvider(NetworkSchemas.file);
+ if (fsProvider) {
+ const uri = await searchForFileLocation(filename, fsProvider, matcher.filePrefix as Config.SearchFileLocationArgs);
+ fullPath = uri?.path;
+ }
+
+ if (!fullPath) {
+ const absoluteMatcher = Objects.deepClone(matcher);
+ absoluteMatcher.fileLocation = FileLocationKind.Absolute;
+ return getResource(filename, absoluteMatcher);
+ }
}
if (fullPath === undefined) {
throw new Error('FileLocationKind is not actionable. Does the matcher have a filePrefix? This should never happen.');
@@ -228,6 +245,56 @@ export async function getResource(filename: string, matcher: ProblemMatcher, fil
}
}
+async function searchForFileLocation(filename: string, fsProvider: IFileSystemProvider, args: Config.SearchFileLocationArgs): Promise<URI | undefined> {
+ const exclusions = new Set(asArray(args.exclude || []).map(x => URI.file(x).path));
+ async function search(dir: URI): Promise<URI | undefined> {
+ if (exclusions.has(dir.path)) {
+ return undefined;
+ }
+
+ const entries = await fsProvider.readdir(dir);
+ const subdirs: URI[] = [];
+
+ for (const [name, fileType] of entries) {
+ if (fileType === FileType.Directory) {
+ subdirs.push(URI.joinPath(dir, name));
+ continue;
+ }
+
+ if (fileType === FileType.File) {
+ /**
+ * Note that sometimes the given `filename` could be a relative
+ * path (not just the "name.ext" part). For example, the
+ * `filename` can be "/subdir/name.ext". So, just comparing
+ * `name` as `filename` is not sufficient. The workaround here
+ * is to form the URI with `dir` and `name` and check if it ends
+ * with the given `filename`.
+ */
+ const fullUri = URI.joinPath(dir, name);
+ if (fullUri.path.endsWith(filename)) {
+ return fullUri;
+ }
+ }
+ }
+
+ for (const subdir of subdirs) {
+ const result = await search(subdir);
+ if (result) {
+ return result;
+ }
+ }
+ return undefined;
+ }
+
+ for (const dir of asArray(args.include || [])) {
+ const hit = await search(URI.file(dir));
+ if (hit) {
+ return hit;
+ }
+ }
+ return undefined;
+}
+
export interface ILineMatcher {
matchLength: number;
next(line: string): IProblemMatch | null;
@@ -796,8 +863,14 @@ export namespace Config {
* - ["autodetect", "path value"]: the filename is treated
* relative to the given path value, and if it does not
* exist, it is treated as absolute.
+ * - ["search", { include?: "" | []; exclude?: "" | [] }]: The filename
+ * needs to be searched under the directories named by the "include"
+ * property and their nested subdirectories. With "exclude" property
+ * present, the directories should be removed from the search. When
+ * `include` is not unprovided, the current workspace directory should
+ * be used as the default.
*/
- fileLocation?: string | string[];
+ fileLocation?: string | string[] | ['search', SearchFileLocationArgs];
/**
* The name of a predefined problem pattern, the inline definition
@@ -824,6 +897,11 @@ export namespace Config {
background?: IBackgroundMonitor;
}
+ export type SearchFileLocationArgs = {
+ include?: string | string[];
+ exclude?: string | string[];
+ };
+
export type ProblemMatcherType = string | ProblemMatcher | Array<string | ProblemMatcher>;
export interface INamedProblemMatcher extends ProblemMatcher {
@@ -1367,7 +1445,7 @@ export class ProblemMatcherParser extends Parser {
applyTo = ApplyToKind.allDocuments;
}
let fileLocation: FileLocationKind | undefined = undefined;
- let filePrefix: string | undefined = undefined;
+ let filePrefix: string | Config.SearchFileLocationArgs | undefined = undefined;
let kind: FileLocationKind | undefined;
if (Types.isUndefined(description.fileLocation)) {
@@ -1379,6 +1457,8 @@ export class ProblemMatcherParser extends Parser {
fileLocation = kind;
if ((kind === FileLocationKind.Relative) || (kind === FileLocationKind.AutoDetect)) {
filePrefix = '${workspaceFolder}';
+ } else if (kind === FileLocationKind.Search) {
+ filePrefix = { include: ['${workspaceFolder}'] };
}
}
} else if (Types.isStringArray(description.fileLocation)) {
@@ -1392,6 +1472,12 @@ export class ProblemMatcherParser extends Parser {
filePrefix = values[1];
}
}
+ } else if (Array.isArray(description.fileLocation)) {
+ const kind = FileLocationKind.fromString(description.fileLocation[0]);
+ if (kind === FileLocationKind.Search) {
+ fileLocation = FileLocationKind.Search;
+ filePrefix = description.fileLocation[1] ?? { include: ['${workspaceFolder}'] };
+ }
}
const pattern = description.pattern ? this.createProblemPattern(description.pattern) : undefined;
@@ -1605,16 +1691,49 @@ export namespace Schemas {
oneOf: [
{
type: 'string',
- enum: ['absolute', 'relative', 'autoDetect']
+ enum: ['absolute', 'relative', 'autoDetect', 'search']
},
{
type: 'array',
items: {
type: 'string'
- }
+ },
+ examples: [
+ ['relative', '${workspaceFolder}'],
+ ['autoDetect', '${workspaceFolder}'],
+ ]
+ },
+ {
+ type: 'array',
+ items: [
+ { type: 'string', enum: ['search'] },
+ {
+ type: 'object',
+ properties: {
+ 'include': {
+ oneOf: [
+ { type: 'string' },
+ { type: 'array', items: { type: 'string' } }
+ ]
+ },
+ 'exclude': {
+ oneOf: [
+ { type: 'string' },
+ { type: 'array', items: { type: 'string' } }
+ ]
+ },
+ },
+ required: ['include']
+ }
+ ],
+ additionalItems: false,
+ examples: [
+ ['search', { 'include': ['${workspaceFolder}'] }],
+ ['search', { 'include': ['${workspaceFolder}'], 'exclude': [] }]
+ ],
}
],
- description: localize('ProblemMatcherSchema.fileLocation', 'Defines how file names reported in a problem pattern should be interpreted. A relative fileLocation may be an array, where the second element of the array is the path the relative file location.')
+ description: localize('ProblemMatcherSchema.fileLocation', 'Defines how file names reported in a problem pattern should be interpreted. A relative fileLocation may be an array, where the second element of the array is the path of the relative file location. The search fileLocation mode, performs a deep (and, possibly, heavy) file system search within the directories specified by the include/exclude properties of the second element (or the current workspace directory if not specified).')
},
background: {
type: 'object',