Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Added caching, more tests
  • Loading branch information
rbuckton committed Dec 7, 2015
commit 30575dbd7cc4ac62c044e6d1eef77de3affd11ce
3 changes: 2 additions & 1 deletion Jakefile.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ var harnessSources = harnessCoreSources.concat([
"reuseProgramStructure.ts",
"cachingInServerLSHost.ts",
"moduleResolution.ts",
"tsconfigParsing.ts"
"tsconfigParsing.ts",
"expandFiles.ts"
].map(function (f) {
return path.join(unittestsDirectory, f);
})).concat([
Expand Down
474 changes: 360 additions & 114 deletions src/compiler/commandLineParser.ts

Large diffs are not rendered by default.

88 changes: 87 additions & 1 deletion src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ namespace ts {
remove,
forEachValue: forEachValueInMap,
reduce,
clear
clear,
mergeFrom
};

function forEachValueInMap(f: (key: Path, value: T) => void) {
Expand Down Expand Up @@ -61,6 +62,16 @@ namespace ts {
files = {};
}

function mergeFrom(other: FileMap<T>) {
other.forEachValue(mergeFromOther);
}

function mergeFromOther(key: Path, value: T) {
if (!contains(key)) {
set(key, value);
}
}

function toKey(path: Path): string {
return keyMapper ? keyMapper(path) : path;
}
Expand Down Expand Up @@ -822,6 +833,28 @@ namespace ts {
return compareValues(aComponents.length, bComponents.length);
}

export function containsPath(parent: string, child: string, currentDirectory: string, ignoreCase?: boolean) {
if (parent === undefined || child === undefined) return false;
if (parent === child) return true;
parent = removeTrailingDirectorySeparator(parent);
child = removeTrailingDirectorySeparator(child);
if (parent === child) return true;
const parentComponents = getNormalizedPathComponents(parent, currentDirectory);
const childComponents = getNormalizedPathComponents(child, currentDirectory);
if (childComponents.length < parentComponents.length) {
return false;
}

for (let i = 0; i < parentComponents.length; ++i) {
const result = compareStrings(parentComponents[i], childComponents[i], ignoreCase);
if (result !== Comparison.EqualTo) {
return false;
}
}

return true;
}

export function fileExtensionIs(path: string, extension: string): boolean {
const pathLen = path.length;
const extLen = extension.length;
Expand Down Expand Up @@ -850,6 +883,59 @@ namespace ts {
return false;
}

/**
* Extension boundaries by priority. Lower numbers indicate higher priorities, and are
* aligned to the offset of the highest priority extension in the
* allSupportedExtensions array.
*/
export const enum ExtensionPriority {
TypeScriptFiles = 0,
DeclarationAndJavaScriptFiles = 2,
Limit = 5,

Highest = TypeScriptFiles,
Lowest = DeclarationAndJavaScriptFiles,
}

export function getExtensionPriority(path: string, supportedExtensions: string[]): ExtensionPriority {
for (let i = supportedExtensions.length - 1; i >= 0; i--) {
if (fileExtensionIs(path, supportedExtensions[i])) {
return adjustExtensionPriority(<ExtensionPriority>i);
}
}

// If its not in the list of supported extensions, this is likely a
// TypeScript file with a non-ts extension
return ExtensionPriority.Highest;
}

/**
* Adjusts an extension priority to be the highest priority within the same range.
*/
export function adjustExtensionPriority(extensionPriority: ExtensionPriority): ExtensionPriority {
if (extensionPriority < ExtensionPriority.DeclarationAndJavaScriptFiles) {
return ExtensionPriority.TypeScriptFiles;
}
else if (extensionPriority < ExtensionPriority.Limit) {
return ExtensionPriority.DeclarationAndJavaScriptFiles;
}
else {
return ExtensionPriority.Limit;
}
}

/**
* Gets the next lowest extension priority for a given priority.
*/
export function getNextLowestExtensionPriority(extensionPriority: ExtensionPriority): ExtensionPriority {
if (extensionPriority < ExtensionPriority.DeclarationAndJavaScriptFiles) {
return ExtensionPriority.DeclarationAndJavaScriptFiles;
}
else {
return ExtensionPriority.Limit;
}
}

const extensionsToRemove = [".d.ts", ".ts", ".js", ".tsx", ".jsx"];
export function removeFileExtension(path: string): string {
for (const ext of extensionsToRemove) {
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2020,6 +2020,10 @@
"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
Expand Down
20 changes: 19 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace ts {

forEachValue(f: (key: Path, v: T) => void): void;
reduce<U>(f: (memo: U, value: T, key: Path) => U, initial: U): U;
mergeFrom(other: FileMap<T>): void;
clear(): void;
}

Expand Down Expand Up @@ -1588,11 +1589,17 @@ namespace ts {
readDirectory(rootDir: string, extension: string, exclude: string[]): string[];

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

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

/**
* Reads the files names in the directory.
* @param rootDir The directory path.
Expand Down Expand Up @@ -2460,6 +2467,17 @@ namespace ts {
options: CompilerOptions;
fileNames: string[];
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
6 changes: 4 additions & 2 deletions src/harness/external/chai.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,14 @@ 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(actual: any, expected: any, message?: string): void;
function notDeepEqual(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 isNull(value: any, message?: string): void;
function isNotNull(value: any, message?: string): void;
function isUndefined(value: any, message?: string): void;
function isDefined(value: any, message?: string): void;
}
}
1 change: 1 addition & 0 deletions src/harness/harnessLanguageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ namespace Harness.LanguageService {
throw new Error("Not implemented.");
}
fileExists(fileName: string) { return this.getScriptInfo(fileName) !== undefined; }
directoryExists(directoryName: string) { return false; }
readFile(fileName: string) {
const snapshot = this.nativeHost.getScriptSnapshot(fileName);
return snapshot && snapshot.getText(0, snapshot.getLength());
Expand Down
5 changes: 5 additions & 0 deletions src/harness/projectsRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ class ProjectRunner extends RunnerBase {
const configParseHost: ts.ParseConfigHost = {
useCaseSensitiveFileNames: Harness.IO.useCaseSensitiveFileNames(),
fileExists,
directoryExists,
readDirectory,
readDirectoryNames,
readFileNames
Expand Down Expand Up @@ -297,6 +298,10 @@ class ProjectRunner extends RunnerBase {
return Harness.IO.fileExists(getFileNameInTheProjectTest(fileName));
}

function directoryExists(directoryName: string): boolean {
return Harness.IO.directoryExists(getFileNameInTheProjectTest(directoryName));
}

function getSourceFileText(fileName: string): string {
let text: string = undefined;
try {
Expand Down
1 change: 1 addition & 0 deletions src/harness/rwcRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ namespace RWC {
const configParseHost: ts.ParseConfigHost = {
useCaseSensitiveFileNames: Harness.IO.useCaseSensitiveFileNames(),
fileExists: Harness.IO.fileExists,
directoryExists: Harness.IO.directoryExists,
readDirectory: Harness.IO.readDirectory,
readDirectoryNames: Harness.IO.readDirectoryNames,
readFileNames: Harness.IO.readFileNames,
Expand Down
57 changes: 40 additions & 17 deletions src/server/editorServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ namespace ts.server {
if (!resolution) {
const existingResolution = currentResolutionsInFile && ts.lookUp(currentResolutionsInFile, moduleName);
if (moduleResolutionIsValid(existingResolution)) {
// ok, it is safe to use existing module resolution results
// ok, it is safe to use existing module resolution results
resolution = existingResolution;
}
else {
Expand All @@ -144,8 +144,8 @@ namespace ts.server {
}

if (resolution.resolvedModule) {
// TODO: consider checking failedLookupLocations
// TODO: use lastCheckTime to track expiration for module name resolution
// TODO: consider checking failedLookupLocations
// TODO: use lastCheckTime to track expiration for module name resolution
return true;
}

Expand Down Expand Up @@ -354,6 +354,7 @@ namespace ts.server {
export interface ProjectOptions {
// these fields can be present in the project file
files?: string[];
wildcardDirectories?: ts.Map<ts.WatchDirectoryFlags>;
compilerOptions?: ts.CompilerOptions;
}

Expand All @@ -362,6 +363,7 @@ namespace ts.server {
projectFilename: string;
projectFileWatcher: FileWatcher;
directoryWatcher: FileWatcher;
directoriesWatchedForWildcards: Map<FileWatcher>;
// Used to keep track of what directories are watched for this project
directoriesWatchedForTsconfig: string[] = [];
program: ts.Program;
Expand Down Expand Up @@ -510,7 +512,7 @@ namespace ts.server {
openFileRootsConfigured: ScriptInfo[] = [];
// a path to directory watcher map that detects added tsconfig files
directoryWatchersForTsconfig: ts.Map<FileWatcher> = {};
// count of how many projects are using the directory watcher. If the
// count of how many projects are using the directory watcher. If the
// number becomes 0 for a watcher, then we should close it.
directoryWatchersRefCount: ts.Map<number> = {};
hostConfiguration: HostConfiguration;
Expand Down Expand Up @@ -590,11 +592,11 @@ namespace ts.server {
// We check if the project file list has changed. If so, we update the project.
if (!arrayIsEqualTo(currentRootFiles && currentRootFiles.sort(), newRootFiles && newRootFiles.sort())) {
// For configured projects, the change is made outside the tsconfig file, and
// it is not likely to affect the project for other files opened by the client. We can
// it is not likely to affect the project for other files opened by the client. We can
// just update the current project.
this.updateConfiguredProject(project);

// Call updateProjectStructure to clean up inferred projects we may have
// Call updateProjectStructure to clean up inferred projects we may have
// created for the new files
this.updateProjectStructure();
}
Expand Down Expand Up @@ -739,6 +741,8 @@ namespace ts.server {
if (project.isConfiguredProject()) {
project.projectFileWatcher.close();
project.directoryWatcher.close();
forEachValue(project.directoriesWatchedForWildcards, watcher => { watcher.close(); });
delete project.directoriesWatchedForWildcards;
this.configuredProjects = copyListRemovingItem(project, this.configuredProjects);
}
else {
Expand Down Expand Up @@ -816,8 +820,8 @@ namespace ts.server {
* @param info The file that has been closed or newly configured
*/
closeOpenFile(info: ScriptInfo) {
// Closing file should trigger re-reading the file content from disk. This is
// because the user may chose to discard the buffer content before saving
// Closing file should trigger re-reading the file content from disk. This is
// because the user may chose to discard the buffer content before saving
// to the disk, and the server's version of the file can be out of sync.
info.svc.reloadFromFile(info.fileName);

Expand Down Expand Up @@ -915,8 +919,8 @@ namespace ts.server {
}

/**
* This function is to update the project structure for every projects.
* It is called on the premise that all the configured projects are
* This function is to update the project structure for every projects.
* It is called on the premise that all the configured projects are
* up to date.
*/
updateProjectStructure() {
Expand Down Expand Up @@ -970,7 +974,7 @@ namespace ts.server {

if (rootFile.defaultProject && rootFile.defaultProject.isConfiguredProject()) {
// If the root file has already been added into a configured project,
// meaning the original inferred project is gone already.
// meaning the original inferred project is gone already.
if (!rootedProject.isConfiguredProject()) {
this.removeProject(rootedProject);
}
Expand Down Expand Up @@ -1075,9 +1079,9 @@ namespace ts.server {
}

/**
* This function tries to search for a tsconfig.json for the given file. If we found it,
* This function tries to search for a tsconfig.json for the given file. If we found it,
* we first detect if there is already a configured project created for it: if so, we re-read
* the tsconfig file content and update the project; otherwise we create a new one.
* the tsconfig file content and update the project; otherwise we create a new one.
*/
openOrUpdateConfiguredProjectForFile(fileName: string) {
const searchPath = ts.normalizePath(getDirectoryPath(fileName));
Expand Down Expand Up @@ -1215,7 +1219,8 @@ namespace ts.server {
else {
const projectOptions: ProjectOptions = {
files: parsedCommandLine.fileNames,
compilerOptions: parsedCommandLine.options
wildcardDirectories: parsedCommandLine.wildcardDirectories,
compilerOptions: parsedCommandLine.options,
};
return { succeeded: true, projectOptions };
}
Expand All @@ -1241,12 +1246,30 @@ namespace ts.server {
}
project.finishGraph();
project.projectFileWatcher = this.host.watchFile(configFilename, _ => this.watchedProjectConfigFileChanged(project));
this.log("Add recursive watcher for: " + ts.getDirectoryPath(configFilename));

const configDirectoryPath = ts.getDirectoryPath(configFilename);

this.log("Add recursive watcher for: " + configDirectoryPath);
project.directoryWatcher = this.host.watchDirectory(
ts.getDirectoryPath(configFilename),
configDirectoryPath,
path => this.directoryWatchedForSourceFilesChanged(project, path),
/*recursive*/ true
);

project.directoriesWatchedForWildcards = reduceProperties(projectOptions.wildcardDirectories, (watchers, flag, directory) => {
if (comparePaths(configDirectoryPath, directory, ".", !this.host.useCaseSensitiveFileNames) !== Comparison.EqualTo) {
const recursive = (flag & WatchDirectoryFlags.Recursive) !== 0;
this.log(`Add ${ recursive ? "recursive " : ""}watcher for: ${directory}`);
watchers[directory] = this.host.watchDirectory(
directory,
path => this.directoryWatchedForSourceFilesChanged(project, path),
recursive
);
}

return watchers;
}, <Map<FileWatcher>>{});

return { success: true, project: project };
}
}
Expand Down Expand Up @@ -1280,7 +1303,7 @@ namespace ts.server {
info = this.openFile(fileName, /*openedByClient*/ false);
}
else {
// if the root file was opened by client, it would belong to either
// if the root file was opened by client, it would belong to either
// openFileRoots or openFileReferenced.
if (info.isOpen) {
if (this.openFileRoots.indexOf(info) >= 0) {
Expand Down
Loading