Skip to content

Update of glob-style pattern matching #8841

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Jun 20, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f9ae3e4
Initial support for globs in tsconfig.json
rbuckton Dec 3, 2015
30575db
Added caching, more tests
rbuckton Dec 7, 2015
5de2fcc
Merge branch 'master' into glob2
rbuckton Dec 7, 2015
def3ba1
Added stubs to ChakraHost interface for readDirectoryNames/readFileNames
rbuckton Dec 7, 2015
c3c3bca
Fixed typos in comments
rbuckton Dec 8, 2015
bf9af46
Changed name of 'reduce' method added to FileSet
rbuckton Dec 8, 2015
d857250
Heavily revised implementation that relies on an updated 'readDirecto…
rbuckton Dec 14, 2015
247657f
Merge branch 'master' into glob2
rbuckton Dec 15, 2015
94a5327
more tests
rbuckton Dec 16, 2015
6f85fe9
Minor update to shims.ts for forthcoming VS support for globs.
rbuckton Dec 16, 2015
d23df34
Detailed comments for regular expressions and renamed some files.
rbuckton Dec 16, 2015
c224917
Comment cleanup
rbuckton Jan 4, 2016
cde12ef
Merge branch 'master' into glob2
rbuckton Jan 4, 2016
c1205eb
Fixed new linter warnings
rbuckton Jan 5, 2016
084b94c
Merge branch 'master' into glob2
riknoll May 26, 2016
db85643
Fixing linter and test errors
riknoll May 26, 2016
c340c88
Bringing back excludes error and fixing faulty test
riknoll May 26, 2016
50f0033
Merge branch 'master' into glob2_merged
riknoll May 26, 2016
aa5c51c
Fixing lint errors
riknoll May 26, 2016
0415b95
Passing regular expressions to native hosts
riknoll May 31, 2016
08ca1c0
Merge branch 'master' into glob2_merged
riknoll Jun 17, 2016
86cde9e
Updating readDirectory for tsserverProjectSystem unit tests
riknoll Jun 17, 2016
95072aa
Responding to PR feedback
riknoll Jun 18, 2016
f817ffa
Merge branch 'master' into glob2_merged
riknoll Jun 18, 2016
f73ed59
Adding more matchFiles test cases
riknoll Jun 20, 2016
b49acd5
Merge branch 'master' into glob2_merged
riknoll Jun 20, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Jakefile.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ var languageServiceLibrarySources = [

var harnessCoreSources = [
"harness.ts",
"virtualFileSystem.ts",
"sourceMapRecorder.ts",
"harnessLanguageService.ts",
"fourslash.ts",
Expand Down Expand Up @@ -155,7 +156,8 @@ var harnessSources = harnessCoreSources.concat([
"commandLineParsing.ts",
"convertCompilerOptionsFromJson.ts",
"convertTypingOptionsFromJson.ts",
"tsserverProjectSystem.ts"
"tsserverProjectSystem.ts",
"matchFiles.ts"
].map(function (f) {
return path.join(unittestsDirectory, f);
})).concat([
Expand Down
380 changes: 329 additions & 51 deletions src/compiler/commandLineParser.ts

Large diffs are not rendered by default.

353 changes: 352 additions & 1 deletion src/compiler/core.ts

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2232,6 +2232,14 @@
"category": "Error",
"code": 5009
},
"File specification cannot end in a recursive directory wildcard ('**'): '{0}'.": {
"category": "Error",
"code": 5010
},
"File specification cannot contain multiple recursive directory wildcards ('**'): '{0}'.": {
"category": "Error",
"code": 5011
},
"Cannot read file '{0}': {1}": {
"category": "Error",
"code": 5012
Expand Down
118 changes: 56 additions & 62 deletions src/compiler/sys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace ts {
getExecutingFilePath(): string;
getCurrentDirectory(): string;
getDirectories(path: string): string[];
readDirectory(path: string, extension?: string, exclude?: string[]): string[];
readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[];
getModifiedTime?(path: string): Date;
createHash?(data: string): string;
getMemoryUsage?(): number;
Expand Down Expand Up @@ -74,7 +74,7 @@ namespace ts {
readFile(path: string): string;
writeFile(path: string, contents: string): void;
getDirectories(path: string): string[];
readDirectory(path: string, extension?: string, exclude?: string[]): string[];
readDirectory(path: string, extensions?: string[], basePaths?: string[], excludeEx?: string, includeFileEx?: string, includeDirEx?: string): string[];
watchFile?(path: string, callback: FileWatcherCallback): FileWatcher;
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
realpath(path: string): string;
Expand All @@ -85,6 +85,7 @@ namespace ts {
function getWScriptSystem(): System {

const fso = new ActiveXObject("Scripting.FileSystemObject");
const shell = new ActiveXObject("WScript.Shell");

const fileStream = new ActiveXObject("ADODB.Stream");
fileStream.Type = 2 /*text*/;
Expand Down Expand Up @@ -152,10 +153,6 @@ namespace ts {
}
}

function getCanonicalPath(path: string): string {
return path.toLowerCase();
}

function getNames(collection: any): string[] {
const result: string[] = [];
for (let e = new Enumerator(collection); !e.atEnd(); e.moveNext()) {
Expand All @@ -169,30 +166,22 @@ namespace ts {
return getNames(folder.subfolders);
}

function readDirectory(path: string, extension?: string, exclude?: string[]): string[] {
const result: string[] = [];
exclude = map(exclude, s => getCanonicalPath(combinePaths(path, s)));
visitDirectory(path);
return result;
function visitDirectory(path: string) {
function getAccessibleFileSystemEntries(path: string): FileSystemEntries {
try {
const folder = fso.GetFolder(path || ".");
const files = getNames(folder.files);
for (const current of files) {
const name = combinePaths(path, current);
if ((!extension || fileExtensionIs(name, extension)) && !contains(exclude, getCanonicalPath(name))) {
result.push(name);
}
}
const subfolders = getNames(folder.subfolders);
for (const current of subfolders) {
const name = combinePaths(path, current);
if (!contains(exclude, getCanonicalPath(name))) {
visitDirectory(name);
}
}
const directories = getNames(folder.subfolders);
return { files, directories };
}
catch (e) {
return { files: [], directories: [] };
}
}

function readDirectory(path: string, extensions?: string[], excludes?: string[], includes?: string[]): string[] {
return matchFiles(path, extensions, excludes, includes, /*useCaseSensitiveFileNames*/ false, shell.CurrentDirectory, getAccessibleFileSystemEntries);
}

return {
args,
newLine: "\r\n",
Expand Down Expand Up @@ -220,7 +209,7 @@ namespace ts {
return WScript.ScriptFullName;
},
getCurrentDirectory() {
return new ActiveXObject("WScript.Shell").CurrentDirectory;
return shell.CurrentDirectory;
},
getDirectories,
readDirectory,
Expand Down Expand Up @@ -381,8 +370,43 @@ namespace ts {
}
}

function getCanonicalPath(path: string): string {
return useCaseSensitiveFileNames ? path : path.toLowerCase();
function getAccessibleFileSystemEntries(path: string): FileSystemEntries {
try {
const entries = _fs.readdirSync(path || ".").sort();
const files: string[] = [];
const directories: string[] = [];
for (const entry of entries) {
// This is necessary because on some file system node fails to exclude
// "." and "..". See https://github.com./nodejs/node/issues/4002
if (entry === "." || entry === "..") {
continue;
}
const name = combinePaths(path, entry);

let stat: any;
try {
stat = _fs.statSync(name);
}
catch (e) {
continue;
}

if (stat.isFile()) {
files.push(entry);
}
else if (stat.isDirectory()) {
directories.push(entry);
}
}
return { files, directories };
}
catch (e) {
return { files: [], directories: [] };
}
}

function readDirectory(path: string, extensions?: string[], excludes?: string[], includes?: string[]): string[] {
return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, process.cwd(), getAccessibleFileSystemEntries);
}

const enum FileSystemEntryKind {
Expand Down Expand Up @@ -415,39 +439,6 @@ namespace ts {
return filter<string>(_fs.readdirSync(path), p => fileSystemEntryExists(combinePaths(path, p), FileSystemEntryKind.Directory));
}

function readDirectory(path: string, extension?: string, exclude?: string[]): string[] {
const result: string[] = [];
exclude = map(exclude, s => getCanonicalPath(combinePaths(path, s)));
visitDirectory(path);
return result;
function visitDirectory(path: string) {
const files = _fs.readdirSync(path || ".").sort();
const directories: string[] = [];
for (const current of files) {
// This is necessary because on some file system node fails to exclude
// "." and "..". See https://github.com./nodejs/node/issues/4002
if (current === "." || current === "..") {
continue;
}
const name = combinePaths(path, current);
if (!contains(exclude, getCanonicalPath(name))) {
const stat = _fs.statSync(name);
if (stat.isFile()) {
if (!extension || fileExtensionIs(name, extension)) {
result.push(name);
}
}
else if (stat.isDirectory()) {
directories.push(name);
}
}
}
for (const current of directories) {
visitDirectory(current);
}
}
}

return {
args: process.argv.slice(2),
newLine: _os.EOL,
Expand Down Expand Up @@ -586,7 +577,10 @@ namespace ts {
getExecutingFilePath: () => ChakraHost.executingFile,
getCurrentDirectory: () => ChakraHost.currentDirectory,
getDirectories: ChakraHost.getDirectories,
readDirectory: ChakraHost.readDirectory,
readDirectory: (path: string, extensions?: string[], excludes?: string[], includes?: string[]) => {
const pattern = getFileMatcherPatterns(path, extensions, excludes, includes, !!ChakraHost.useCaseSensitiveFileNames, ChakraHost.currentDirectory);
return ChakraHost.readDirectory(path, extensions, pattern.basePaths, pattern.excludePattern, pattern.includeFilePattern, pattern.includeDirectoryPattern);
},
exit: ChakraHost.quit,
realpath
};
Expand Down
21 changes: 20 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1677,7 +1677,15 @@ namespace ts {
}

export interface ParseConfigHost {
readDirectory(rootDir: string, extension: string, exclude: string[]): string[];
useCaseSensitiveFileNames: boolean;

readDirectory(rootDir: string, extensions: string[], excludes: string[], includes: string[]): string[];

/**
* Gets a value indicating whether the specified path exists and is a file.
* @param path The path to test.
*/
fileExists(path: string): boolean;
}

export interface WriteFileCallback {
Expand Down Expand Up @@ -2660,6 +2668,17 @@ namespace ts {
fileNames: string[];
raw?: any;
errors: Diagnostic[];
wildcardDirectories?: Map<WatchDirectoryFlags>;
}

export const enum WatchDirectoryFlags {
None = 0,
Recursive = 1 << 0,
}

export interface ExpandResult {
fileNames: string[];
wildcardDirectories: Map<WatchDirectoryFlags>;
}

/* @internal */
Expand Down
5 changes: 5 additions & 0 deletions src/harness/external/chai.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,13 @@ declare module chai {
module assert {
function equal(actual: any, expected: any, message?: string): void;
function notEqual(actual: any, expected: any, message?: string): void;
function deepEqual<T>(actual: T, expected: T, message?: string): void;
function notDeepEqual<T>(actual: T, expected: T, message?: string): void;
function lengthOf(object: any[], length: number, message?: string): void;
function isTrue(value: any, message?: string): void;
function isFalse(value: any, message?: string): void;
function isOk(actual: any, message?: string): void;
function isUndefined(value: any, message?: string): void;
function isDefined(value: any, message?: string): void;
}
}
29 changes: 23 additions & 6 deletions src/harness/harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
/// <reference path="external\chai.d.ts"/>
/// <reference path="sourceMapRecorder.ts"/>
/// <reference path="runnerbase.ts"/>
/// <reference path="virtualFileSystem.ts" />

// Block scoped definitions work poorly for global variables, temporarily enable var
/* tslint:disable:no-var-keyword */
Expand Down Expand Up @@ -443,7 +444,7 @@ namespace Harness {
args(): string[];
getExecutingFilePath(): string;
exit(exitCode?: number): void;
readDirectory(path: string, extension?: string, exclude?: string[]): string[];
readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[]): string[];
}
export var IO: IO;

Expand Down Expand Up @@ -482,7 +483,7 @@ namespace Harness {
export const directoryExists: typeof IO.directoryExists = fso.FolderExists;
export const fileExists: typeof IO.fileExists = fso.FileExists;
export const log: typeof IO.log = global.WScript && global.WScript.StdOut.WriteLine;
export const readDirectory: typeof IO.readDirectory = (path, extension, exclude) => ts.sys.readDirectory(path, extension, exclude);
export const readDirectory: typeof IO.readDirectory = (path, extension, exclude, include) => ts.sys.readDirectory(path, extension, exclude, include);

export function createDirectory(path: string) {
if (directoryExists(path)) {
Expand Down Expand Up @@ -552,7 +553,7 @@ namespace Harness {
export const fileExists: typeof IO.fileExists = fs.existsSync;
export const log: typeof IO.log = s => console.log(s);

export const readDirectory: typeof IO.readDirectory = (path, extension, exclude) => ts.sys.readDirectory(path, extension, exclude);
export const readDirectory: typeof IO.readDirectory = (path, extension, exclude, include) => ts.sys.readDirectory(path, extension, exclude, include);

export function createDirectory(path: string) {
if (!directoryExists(path)) {
Expand Down Expand Up @@ -740,8 +741,22 @@ namespace Harness {
Http.writeToServerSync(serverRoot + path, "WRITE", contents);
}

export function readDirectory(path: string, extension?: string, exclude?: string[]) {
return listFiles(path).filter(f => !extension || ts.fileExtensionIs(f, extension));
export function readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[]) {
const fs = new Utils.VirtualFileSystem(path, useCaseSensitiveFileNames());
for (const file in listFiles(path)) {
fs.addFile(file);
}
return ts.matchFiles(path, extension, exclude, include, useCaseSensitiveFileNames(), getCurrentDirectory(), path => {
const entry = fs.traversePath(path);
if (entry && entry.isDirectory()) {
const directory = <Utils.VirtualDirectory>entry;
return {
files: ts.map(directory.getFiles(), f => f.name),
directories: ts.map(directory.getDirectories(), d => d.name)
};
}
return { files: [], directories: [] };
});
}
}
}
Expand Down Expand Up @@ -1531,7 +1546,9 @@ namespace Harness {

// unit tests always list files explicitly
const parseConfigHost: ts.ParseConfigHost = {
readDirectory: (name) => []
useCaseSensitiveFileNames: false,
readDirectory: (name) => [],
fileExists: (name) => true
};

// check if project has tsconfig.json in the list of files
Expand Down
9 changes: 7 additions & 2 deletions src/harness/harnessLanguageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,12 @@ namespace Harness.LanguageService {
readDirectory(rootDir: string, extension: string): string {
throw new Error("NYI");
}
readDirectoryNames(path: string): string {
throw new Error("Not implemented.");
}
readFileNames(path: string): string {
throw new Error("Not implemented.");
}
fileExists(fileName: string) { return this.getScriptInfo(fileName) !== undefined; }
readFile(fileName: string) {
const snapshot = this.nativeHost.getScriptSnapshot(fileName);
Expand Down Expand Up @@ -611,7 +617,7 @@ namespace Harness.LanguageService {
return [];
}

readDirectory(path: string, extension?: string): string[] {
readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[]): string[] {
throw new Error("Not implemented Yet.");
}

Expand Down Expand Up @@ -695,4 +701,3 @@ namespace Harness.LanguageService {
getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo { throw new Error("getPreProcessedFileInfo is not available using the server interface."); }
}
}

9 changes: 5 additions & 4 deletions src/harness/loggedIO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ interface IOLog {
}[];
directoriesRead: {
path: string,
extension: string,
extension: string[],
exclude: string[],
include: string[],
result: string[]
}[];
}
Expand Down Expand Up @@ -217,9 +218,9 @@ namespace Playback {
memoize(path => findResultByPath(wrapper, replayLog.filesRead, path).contents));

wrapper.readDirectory = recordReplay(wrapper.readDirectory, underlying)(
(path, extension, exclude) => {
const result = (<ts.System>underlying).readDirectory(path, extension, exclude);
const logEntry = { path, extension, exclude, result };
(path, extension, exclude, include) => {
const result = (<ts.System>underlying).readDirectory(path, extension, exclude, include);
const logEntry = { path, extension, exclude, include, result };
recordLog.directoriesRead.push(logEntry);
return result;
},
Expand Down
Loading