Merge pull request #2877 from Microsoft/tsconfigSupportInLS

This adds a method to the language service to parse a tsconfig file and correctly calculate the initial collection of files to consider. 

We expect the 'host' to find the files in the file system.
This commit is contained in:
Paul van Brenk 2015-04-23 15:21:08 -07:00
commit 3907caec68
8 changed files with 95 additions and 16 deletions

View File

@ -285,12 +285,27 @@ module ts {
* Read tsconfig.json file
* @param fileName The path to the config file
*/
export function readConfigFile(fileName: string): any {
export function readConfigFile(fileName: string): { config?: any; error?: Diagnostic } {
try {
var text = sys.readFile(fileName);
return /\S/.test(text) ? JSON.parse(text) : {};
}
catch (e) {
return { error: createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e.message) };
}
return parseConfigFileText(fileName, text);
}
/**
* Parse the text of the tsconfig.json file
* @param fileName The path to the config file
* @param jsonText The text of the config file
*/
export function parseConfigFileText(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } {
try {
return { config: /\S/.test(jsonText) ? JSON.parse(jsonText) : {} };
}
catch (e) {
return { error: createCompilerDiagnostic(Diagnostics.Failed_to_parse_file_0_Colon_1, fileName, e.message) };
}
}
@ -300,7 +315,7 @@ module ts {
* @param basePath A root directory to resolve relative path entries in the config
* file to. e.g. outDir
*/
export function parseConfigFile(json: any, basePath?: string): ParsedCommandLine {
export function parseConfigFile(json: any, host: ParseConfigHost, basePath: string): ParsedCommandLine {
var errors: Diagnostic[] = [];
return {
@ -359,7 +374,7 @@ module ts {
}
}
else {
var sysFiles = sys.readDirectory(basePath, ".ts");
var sysFiles = host.readDirectory(basePath, ".ts");
for (var i = 0; i < sysFiles.length; i++) {
var name = sysFiles[i];
if (!fileExtensionIs(name, ".d.ts") || !contains(sysFiles, name.substr(0, name.length - 5) + ".ts")) {

View File

@ -439,6 +439,7 @@ module ts {
Cannot_find_the_common_subdirectory_path_for_the_input_files: { code: 5009, category: DiagnosticCategory.Error, key: "Cannot find the common subdirectory path for the input files." },
Cannot_read_file_0_Colon_1: { code: 5012, category: DiagnosticCategory.Error, key: "Cannot read file '{0}': {1}" },
Unsupported_file_encoding: { code: 5013, category: DiagnosticCategory.Error, key: "Unsupported file encoding." },
Failed_to_parse_file_0_Colon_1: { code: 5014, category: DiagnosticCategory.Error, key: "Failed to parse file '{0}': {1}." },
Unknown_compiler_option_0: { code: 5023, category: DiagnosticCategory.Error, key: "Unknown compiler option '{0}'." },
Compiler_option_0_requires_a_value_of_type_1: { code: 5024, category: DiagnosticCategory.Error, key: "Compiler option '{0}' requires a value of type {1}." },
Could_not_write_file_0_Colon_1: { code: 5033, category: DiagnosticCategory.Error, key: "Could not write file '{0}': {1}" },

View File

@ -1744,6 +1744,10 @@
"category": "Error",
"code": 5013
},
"Failed to parse file '{0}': {1}.": {
"category": "Error",
"code": 5014
},
"Unknown compiler option '{0}'.": {
"category": "Error",
"code": 5023

View File

@ -208,12 +208,15 @@ module ts {
if (!cachedProgram) {
if (configFileName) {
var configObject = readConfigFile(configFileName);
if (!configObject) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Unable_to_open_file_0, configFileName));
let result = readConfigFile(configFileName);
if (result.error) {
reportDiagnostic(result.error);
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
}
var configParseResult = parseConfigFile(configObject, getDirectoryPath(configFileName));
let configObject = result.config;
let configParseResult = parseConfigFile(configObject, sys, getDirectoryPath(configFileName));
if (configParseResult.errors.length > 0) {
reportDiagnostics(configParseResult.errors);
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
@ -230,7 +233,7 @@ module ts {
compilerHost.getSourceFile = getSourceFile;
}
var compileResult = compile(rootFileNames, compilerOptions, compilerHost);
let compileResult = compile(rootFileNames, compilerOptions, compilerHost);
if (!compilerOptions.watch) {
return sys.exit(compileResult.exitStatus);

View File

@ -1032,6 +1032,10 @@ module ts {
getCurrentDirectory(): string;
}
export interface ParseConfigHost {
readDirectory(rootDir: string, extension: string): string[];
}
export interface WriteFileCallback {
(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void): void;
}

View File

@ -186,6 +186,7 @@ module Harness.LanguageService {
var script = this.getScriptInfo(fileName);
return script ? script.version.toString() : undefined;
}
log(s: string): void { }
trace(s: string): void { }
error(s: string): void { }
@ -203,7 +204,7 @@ module Harness.LanguageService {
}
/// Shim adapter
class ShimLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceShimHost {
class ShimLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceShimHost, ts.CoreServicesShimHost {
private nativeHost: NativeLanguageServiceHost;
constructor(cancellationToken?: ts.CancellationToken, options?: ts.CompilerOptions) {
super(cancellationToken, options);
@ -227,6 +228,11 @@ module Harness.LanguageService {
}
getScriptVersion(fileName: string): string { return this.nativeHost.getScriptVersion(fileName); }
getLocalizedDiagnosticMessages(): string { return JSON.stringify({}); }
readDirectory(rootDir: string, extension: string): string {
throw new Error("NYI");
}
log(s: string): void { this.nativeHost.log(s); }
trace(s: string): void { this.nativeHost.trace(s); }
error(s: string): void { this.nativeHost.error(s); }

View File

@ -910,7 +910,7 @@ module ts.server {
return { errorMsg: "tsconfig syntax error" };
}
else {
var parsedCommandLine = ts.parseConfigFile(rawConfig, dirPath);
var parsedCommandLine = ts.parseConfigFile(rawConfig, ts.sys, dirPath);
if (parsedCommandLine.errors && (parsedCommandLine.errors.length > 0)) {
return { errorMsg: "tsconfig option errors" };
}

View File

@ -57,6 +57,12 @@ module ts {
getNewLine?(): string;
}
/** Public interface of the the of a config service shim instance.*/
export interface CoreServicesShimHost extends Logger {
/** Returns a JSON-encoded value of the type: string[] */
readDirectory(rootDir: string, extension: string): string;
}
///
/// Pre-processing
///
@ -77,7 +83,7 @@ module ts {
export interface Shim {
dispose(dummy: any): void;
}
export interface LanguageServiceShim extends Shim {
languageService: LanguageService;
@ -188,6 +194,7 @@ module ts {
export interface CoreServicesShim extends Shim {
getPreProcessedFileInfo(fileName: string, sourceText: IScriptSnapshot): string;
getTSConfigFileInfo(fileName: string, sourceText: IScriptSnapshot): string;
getDefaultCompilationSettings(): string;
}
@ -302,6 +309,17 @@ module ts {
}
}
}
export class CoreServicesShimHostAdapter implements ParseConfigHost {
constructor(private shimHost: CoreServicesShimHost) {
}
public readDirectory(rootDir: string, extension: string): string[] {
var encoded = this.shimHost.readDirectory(rootDir, extension);
return JSON.parse(encoded);
}
}
function simpleForwardCall(logger: Logger, actionDescription: string, action: () => any): any {
logger.log(actionDescription);
@ -741,7 +759,8 @@ module ts {
}
class CoreServicesShimObject extends ShimBase implements CoreServicesShim {
constructor(factory: ShimFactory, public logger: Logger) {
constructor(factory: ShimFactory, public logger: Logger, private host: CoreServicesShimHostAdapter) {
super(factory);
}
@ -779,6 +798,32 @@ module ts {
});
}
public getTSConfigFileInfo(fileName: string, sourceTextSnapshot: IScriptSnapshot): string {
return this.forwardJSONCall(
"getTSConfigFileInfo('" + fileName + "')",
() => {
let text = sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength());
let result = parseConfigFileText(fileName, text);
if (result.error) {
return {
options: {},
files: [],
errors: [realizeDiagnostic(result.error, '\r\n')]
};
}
var configFile = parseConfigFile(result.config, this.host, getDirectoryPath(normalizeSlashes(fileName)));
return {
options: configFile.options,
files: configFile.fileNames,
errors: realizeDiagnostics(configFile.errors, '\r\n')
};
});
}
public getDefaultCompilationSettings(): string {
return this.forwardJSONCall(
"getDefaultCompilationSettings()",
@ -821,12 +866,13 @@ module ts {
}
}
public createCoreServicesShim(logger: Logger): CoreServicesShim {
public createCoreServicesShim(host: CoreServicesShimHost): CoreServicesShim {
try {
return new CoreServicesShimObject(this, logger);
var adapter = new CoreServicesShimHostAdapter(host);
return new CoreServicesShimObject(this, <Logger>host, adapter);
}
catch (err) {
logInternalError(logger, err);
logInternalError(<Logger>host, err);
throw err;
}
}