Merge branch 'master' into outFile

Conflicts:
	src/harness/fourslash.ts
This commit is contained in:
Mohamed Hegazy 2015-08-20 17:46:38 -07:00
commit b395fb9f45
12 changed files with 352 additions and 51 deletions

View file

@ -85,7 +85,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
else {
// targetSourceFile is specified (e.g calling emitter from language service or calling getSemanticDiagnostic from language service)
if (shouldEmitToOwnFile(targetSourceFile, compilerOptions)) {
let jsFilePath = getOwnEmitOutputFilePath(targetSourceFile, host, forEach(host.getSourceFiles(), shouldEmitJsx) ? ".jsx" : ".js");
let jsFilePath = getOwnEmitOutputFilePath(targetSourceFile, host, shouldEmitJsx(targetSourceFile) ? ".jsx" : ".js");
emitFile(jsFilePath, targetSourceFile);
}
else if (!isDeclarationFile(targetSourceFile) && (compilerOptions.outFile || compilerOptions.out)) {

View file

@ -129,13 +129,14 @@ module FourSlash {
sourceRoot: "sourceRoot",
allowNonTsExtensions: "allowNonTsExtensions",
resolveReference: "ResolveReference", // This flag is used to specify entry file for resolve file references. The flag is only allow once per test file
jsx: "jsx",
};
// List of allowed metadata names
let fileMetadataNames = [metadataOptionNames.fileName, metadataOptionNames.emitThisFile, metadataOptionNames.resolveReference];
let globalMetadataNames = [metadataOptionNames.allowNonTsExtensions, metadataOptionNames.baselineFile, metadataOptionNames.declaration,
metadataOptionNames.mapRoot, metadataOptionNames.module, metadataOptionNames.out, metadataOptionNames.outFile,
metadataOptionNames.outDir, metadataOptionNames.sourceMap, metadataOptionNames.sourceRoot];
metadataOptionNames.mapRoot, metadataOptionNames.module, metadataOptionNames.out,metadataOptionNames.outFile,
metadataOptionNames.outDir, metadataOptionNames.sourceMap, metadataOptionNames.sourceRoot, metadataOptionNames.jsx];
function convertGlobalOptionsToCompilerOptions(globalOptions: { [idx: string]: string }): ts.CompilerOptions {
let settings: ts.CompilerOptions = { target: ts.ScriptTarget.ES5 };
@ -182,6 +183,20 @@ module FourSlash {
case metadataOptionNames.sourceRoot:
settings.sourceRoot = globalOptions[prop];
break;
case metadataOptionNames.jsx:
switch (globalOptions[prop].toLowerCase()) {
case "react":
settings.jsx = ts.JsxEmit.React;
break;
case "preserve":
settings.jsx = ts.JsxEmit.Preserve;
break;
default:
ts.Debug.assert(globalOptions[prop] === undefined || globalOptions[prop] === "None");
settings.jsx = ts.JsxEmit.None;
break;
}
break;
}
}
}

View file

@ -224,6 +224,14 @@ namespace ts.server {
this.roots.push(info);
}
}
removeRoot(info: ScriptInfo) {
var scriptInfo = ts.lookUp(this.filenameToScript, info.fileName);
if (scriptInfo) {
this.filenameToScript[info.fileName] = undefined;
this.roots = copyListRemovingItem(info, this.roots);
}
}
saveTo(filename: string, tmpfilename: string) {
var script = this.getScriptInfo(filename);
@ -345,6 +353,7 @@ namespace ts.server {
export class Project {
compilerService: CompilerService;
projectFilename: string;
projectFileWatcher: FileWatcher;
program: ts.Program;
filenameToSourceFile: ts.Map<ts.SourceFile> = {};
updateGraphSeq = 0;
@ -352,7 +361,7 @@ namespace ts.server {
openRefCount = 0;
constructor(public projectService: ProjectService, public projectOptions?: ProjectOptions) {
this.compilerService = new CompilerService(this,projectOptions && projectOptions.compilerOptions);
this.compilerService = new CompilerService(this, projectOptions && projectOptions.compilerOptions);
}
addOpenRef() {
@ -423,6 +432,12 @@ namespace ts.server {
info.defaultProject = this;
this.compilerService.host.addRoot(info);
}
// remove a root file from project
removeRoot(info: ScriptInfo) {
info.defaultProject = undefined;
this.compilerService.host.removeRoot(info);
}
filesToString() {
var strBuilder = "";
@ -517,6 +532,12 @@ namespace ts.server {
}
}
watchedProjectConfigFileChanged(project: Project) {
this.log("Config File Changed: " + project.projectFilename);
this.updateConfiguredProject(project);
this.updateProjectStructure();
}
log(msg: string, type = "Err") {
this.psLogger.msg(msg, type);
}
@ -593,6 +614,19 @@ namespace ts.server {
}
this.configuredProjects = configuredProjects;
}
removeConfiguredProject(project: Project) {
project.projectFileWatcher.close();
this.configuredProjects = copyListRemovingItem(project, this.configuredProjects);
let fileNames = project.getFileNames();
for (let fileName of fileNames) {
let info = this.getScriptInfo(fileName);
if (info.defaultProject == project){
info.defaultProject = undefined;
}
}
}
setConfiguredProjectRoot(info: ScriptInfo) {
for (var i = 0, len = this.configuredProjects.length; i < len; i++) {
@ -647,7 +681,6 @@ namespace ts.server {
/**
* Remove this file from the set of open, non-configured files.
* @param info The file that has been closed or newly configured
* @param openedByConfig True if info has become a root of a configured project
*/
closeOpenFile(info: ScriptInfo) {
var openFileRoots: ScriptInfo[] = [];
@ -736,18 +769,42 @@ namespace ts.server {
return referencingProjects;
}
reloadProjects() {
// First check if there is new tsconfig file added for inferred project roots
for (let info of this.openFileRoots) {
this.openOrUpdateConfiguredProjectForFile(info.fileName);
}
this.updateProjectStructure();
}
/**
* 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() {
this.log("updating project structure from ...", "Info");
this.printProjects();
let unattachedOpenFiles: ScriptInfo[] = [];
let openFileRootsConfigured: ScriptInfo[] = [];
for (let info of this.openFileRootsConfigured) {
let project = info.defaultProject;
if (!project || !(project.getSourceFile(info))) {
info.defaultProject = undefined;
unattachedOpenFiles.push(info);
}
else {
openFileRootsConfigured.push(info);
}
}
this.openFileRootsConfigured = openFileRootsConfigured;
// First loop through all open files that are referenced by projects but are not
// project roots. For each referenced file, see if the default project still
// references that file. If so, then just keep the file in the referenced list.
// If not, add the file to an unattached list, to be rechecked later.
var openFilesReferenced: ScriptInfo[] = [];
var unattachedOpenFiles: ScriptInfo[] = [];
for (var i = 0, len = this.openFilesReferenced.length; i < len; i++) {
var referencedFile = this.openFilesReferenced[i];
referencedFile.defaultProject.updateGraph();
@ -857,31 +914,39 @@ namespace ts.server {
* Open file whose contents is managed by the client
* @param filename is absolute pathname
*/
openClientFile(fileName: string) {
var searchPath = ts.normalizePath(getDirectoryPath(fileName));
this.log("Search path: " + searchPath,"Info");
var configFileName = this.findConfigFile(searchPath);
if (configFileName) {
this.log("Config file name: " + configFileName, "Info");
} else {
this.log("no config file");
}
if (configFileName && (!this.configProjectIsActive(configFileName))) {
var configResult = this.openConfigFile(configFileName, fileName);
if (!configResult.success) {
this.log("Error opening config file " + configFileName + " " + configResult.errorMsg);
}
else {
this.log("Opened configuration file " + configFileName,"Info");
this.configuredProjects.push(configResult.project);
}
}
this.openOrUpdateConfiguredProjectForFile(fileName);
var info = this.openFile(fileName, true);
this.addOpenFile(info);
this.printProjects();
return info;
}
openOrUpdateConfiguredProjectForFile(fileName: string) {
let searchPath = ts.normalizePath(getDirectoryPath(fileName));
this.log("Search path: " + searchPath,"Info");
let configFileName = this.findConfigFile(searchPath);
if (configFileName) {
this.log("Config file name: " + configFileName, "Info");
let project = this.findConfiguredProjectByConfigFile(configFileName);
if (!project) {
var configResult = this.openConfigFile(configFileName, fileName);
if (!configResult.success) {
this.log("Error opening config file " + configFileName + " " + configResult.errorMsg);
}
else {
this.log("Opened configuration file " + configFileName,"Info");
this.configuredProjects.push(configResult.project);
}
}
else {
this.updateConfiguredProject(project);
}
}
else {
this.log("No config files found.");
}
}
/**
* Close file whose contents is managed by the client
@ -959,49 +1024,111 @@ namespace ts.server {
}
configProjectIsActive(fileName: string) {
for (var i = 0, len = this.configuredProjects.length; i < len; i++) {
if (this.configuredProjects[i].projectFilename == fileName) {
return true;
}
}
return false;
return this.findConfiguredProjectByConfigFile(fileName) === undefined;
}
openConfigFile(configFilename: string, clientFileName?: string): ProjectOpenResult {
findConfiguredProjectByConfigFile(configFileName: string) {
for (var i = 0, len = this.configuredProjects.length; i < len; i++) {
if (this.configuredProjects[i].projectFilename == configFileName) {
return this.configuredProjects[i];
}
}
return undefined;
}
configFileToProjectOptions(configFilename: string): { succeeded: boolean, projectOptions?: ProjectOptions, error?: ProjectOpenResult } {
configFilename = ts.normalizePath(configFilename);
// file references will be relative to dirPath (or absolute)
var dirPath = ts.getDirectoryPath(configFilename);
var contents = this.host.readFile(configFilename)
var rawConfig: { config?: ProjectOptions; error?: Diagnostic; } = ts.parseConfigFileText(configFilename, contents);
if (rawConfig.error) {
return rawConfig.error;
return { succeeded: false, error: rawConfig.error };
}
else {
var parsedCommandLine = ts.parseConfigFile(rawConfig.config, this.host, dirPath);
if (parsedCommandLine.errors && (parsedCommandLine.errors.length > 0)) {
return { errorMsg: "tsconfig option errors" };
return { succeeded: false, error: { errorMsg: "tsconfig option errors" }};
}
else if (parsedCommandLine.fileNames) {
else if (parsedCommandLine.fileNames == null) {
return { succeeded: false, error: { errorMsg: "no files found" }}
}
else {
var projectOptions: ProjectOptions = {
files: parsedCommandLine.fileNames,
compilerOptions: parsedCommandLine.options
};
var proj = this.createProject(configFilename, projectOptions);
for (var i = 0, len = parsedCommandLine.fileNames.length; i < len; i++) {
var rootFilename = parsedCommandLine.fileNames[i];
if (this.host.fileExists(rootFilename)) {
var info = this.openFile(rootFilename, clientFileName == rootFilename);
proj.addRoot(info);
}
else {
return { errorMsg: "specified file " + rootFilename + " not found" };
}
return { succeeded: true, projectOptions };
}
}
}
openConfigFile(configFilename: string, clientFileName?: string): ProjectOpenResult {
let { succeeded, projectOptions, error } = this.configFileToProjectOptions(configFilename);
if (!succeeded) {
return error;
}
else {
let proj = this.createProject(configFilename, projectOptions);
for (let i = 0, len = projectOptions.files.length; i < len; i++) {
let rootFilename = projectOptions.files[i];
if (this.host.fileExists(rootFilename)) {
let info = this.openFile(rootFilename, /*openedByClient*/ clientFileName == rootFilename);
proj.addRoot(info);
}
proj.finishGraph();
return { success: true, project: proj };
else {
return { errorMsg: "specified file " + rootFilename + " not found" };
}
}
proj.finishGraph();
proj.projectFileWatcher = this.host.watchFile(configFilename, _ => this.watchedProjectConfigFileChanged(proj));
return { success: true, project: proj };
}
}
updateConfiguredProject(project: Project) {
if (!this.host.fileExists(project.projectFilename)) {
this.log("Config file deleted");
this.removeConfiguredProject(project);
}
else {
let { succeeded, projectOptions, error } = this.configFileToProjectOptions(project.projectFilename);
if (!succeeded) {
return error;
}
else {
return { errorMsg: "no files found" };
let oldFileNames = project.compilerService.host.roots.map(info => info.fileName);
let newFileNames = projectOptions.files;
let fileNamesToRemove = oldFileNames.filter(f => newFileNames.indexOf(f) < 0);
let fileNamesToAdd = newFileNames.filter(f => oldFileNames.indexOf(f) < 0);
for (let fileName of fileNamesToRemove) {
let info = this.getScriptInfo(fileName);
project.removeRoot(info);
}
for (let fileName of fileNamesToAdd) {
let info = this.getScriptInfo(fileName);
if (!info) {
info = this.openFile(fileName, false);
}
else {
// if the root file was opened by client, it would belong to either
// openFileRoots or openFileReferenced.
if (this.openFileRoots.indexOf(info) >= 0) {
this.openFileRoots = copyListRemovingItem(info, this.openFileRoots);
}
if (this.openFilesReferenced.indexOf(info) >= 0) {
this.openFilesReferenced = copyListRemovingItem(info, this.openFilesReferenced);
}
this.openFileRootsConfigured.push(info);
}
project.addRoot(info);
}
project.setProjectOptions(projectOptions);
project.finishGraph();
}
}
}

View file

@ -31,6 +31,12 @@ declare namespace ts.server.protocol {
*/
arguments?: any;
}
/**
* Request to reload the project structure for all the opened files
*/
export interface ReloadProjectsRequest extends Message {
}
/**
* Server-initiated event message

View file

@ -100,6 +100,7 @@ namespace ts.server {
export const SignatureHelp = "signatureHelp";
export const TypeDefinition = "typeDefinition";
export const ProjectInfo = "projectInfo";
export const ReloadProjects = "reloadProjects";
export const Unknown = "unknown";
}
@ -226,6 +227,10 @@ namespace ts.server {
this.syntacticCheck(file, project);
this.semanticCheck(file, project);
}
private reloadProjects() {
this.projectService.reloadProjects();
}
private updateProjectStructure(seq: number, matchSeq: (seq: number) => boolean, ms = 1500) {
setTimeout(() => {
@ -1033,6 +1038,10 @@ namespace ts.server {
var { file, needFileNameList } = <protocol.ProjectInfoRequestArgs>request.arguments;
return {response: this.getProjectInfo(file, needFileNameList), responseRequired: true};
},
[CommandNames.ReloadProjects]: (request: protocol.ReloadProjectsRequest) => {
this.reloadProjects();
return {responseRequired: false};
}
};
public addProtocolHandler(command: string, handler: (request: protocol.Request) => {response?: any, responseRequired: boolean}) {
if (this.handlers[command]) {

View file

@ -1097,4 +1097,4 @@ module TypeScript.Services {
}
/* @internal */
let toolsVersion = "1.5";
const toolsVersion = "1.6";

View file

@ -0,0 +1,26 @@
EmitSkipped: false
FileName : outFile.js
var x = 5;
var Bar = (function () {
function Bar() {
}
return Bar;
})();
var x1 = "hello world";
var Foo = (function () {
function Foo() {
}
return Foo;
})();
FileName : outFile.d.ts
declare var x: number;
declare class Bar {
x: string;
y: number;
}
declare var x1: string;
declare class Foo {
x: string;
y: number;
}

View file

@ -0,0 +1,26 @@
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile1.js.map
{"version":3,"file":"inputFile1.js","sourceRoot":"","sources":["inputFile1.ts"],"names":["Bar","Bar.constructor"],"mappings":"AAAA,kBAAkB;AACjB,IAAI,CAAC,GAAW,CAAC,CAAC;AAClB;IAAAA;IAGAC,CAACA;IAADD,UAACA;AAADA,CAACA,AAHD,IAGC"}FileName : tests/cases/fourslash/inputFile1.js
// regular ts file
var t = 5;
var Bar = (function () {
function Bar() {
}
return Bar;
})();
//# sourceMappingURL=inputFile1.js.mapFileName : tests/cases/fourslash/inputFile1.d.ts
declare var t: number;
declare class Bar {
x: string;
y: number;
}
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile2.jsx.map
{"version":3,"file":"inputFile2.jsx","sourceRoot":"","sources":["inputFile2.tsx"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,QAAQ,CAAC;AACjB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,CAAC,CAAC,EAAG,CAAA"}FileName : tests/cases/fourslash/inputFile2.jsx
var y = "my div";
var x = <div name={y}/>;
//# sourceMappingURL=inputFile2.jsx.mapFileName : tests/cases/fourslash/inputFile2.d.ts
declare var y: string;
declare var x: any;

View file

@ -0,0 +1,26 @@
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile1.js.map
{"version":3,"file":"inputFile1.js","sourceRoot":"","sources":["inputFile1.ts"],"names":["Bar","Bar.constructor"],"mappings":"AAAA,kBAAkB;AACjB,IAAI,CAAC,GAAW,CAAC,CAAC;AAClB;IAAAA;IAGAC,CAACA;IAADD,UAACA;AAADA,CAACA,AAHD,IAGC"}FileName : tests/cases/fourslash/inputFile1.js
// regular ts file
var t = 5;
var Bar = (function () {
function Bar() {
}
return Bar;
})();
//# sourceMappingURL=inputFile1.js.mapFileName : tests/cases/fourslash/inputFile1.d.ts
declare var t: number;
declare class Bar {
x: string;
y: number;
}
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile2.js.map
{"version":3,"file":"inputFile2.js","sourceRoot":"","sources":["inputFile2.tsx"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,QAAQ,CAAC;AACjB,IAAI,CAAC,GAAG,qBAAC,GAAG,KAAC,IAAI,GAAG,CAAE,EAAG,CAAA"}FileName : tests/cases/fourslash/inputFile2.js
var y = "my div";
var x = React.createElement("div", {"name": y});
//# sourceMappingURL=inputFile2.js.mapFileName : tests/cases/fourslash/inputFile2.d.ts
declare var y: string;
declare var x: any;

View file

@ -0,0 +1,22 @@
/// <reference path="fourslash.ts" />
// @BaselineFile: getEmitOutputOutFile.baseline
// @declaration: true
// @outFile: outFile.js
// @Filename: inputFile1.ts
// @emitThisFile: true
//// var x: number = 5;
//// class Bar {
//// x : string;
//// y : number
//// }
// @Filename: inputFile2.ts
//// var x1: string = "hello world";
//// class Foo{
//// x : string;
//// y : number;
//// }
verify.baselineGetEmitOutput();

View file

@ -0,0 +1,22 @@
/// <reference path="fourslash.ts" />
// @BaselineFile: getEmitOutputTsxFile_Preserve.baseline
// @declaration: true
// @sourceMap: true
// @jsx: preserve
// @Filename: inputFile1.ts
// @emitThisFile: true
////// regular ts file
//// var t: number = 5;
//// class Bar {
//// x : string;
//// y : number
//// }
// @Filename: inputFile2.tsx
// @emitThisFile: true
//// var y = "my div";
//// var x = <div name= {y} />
verify.baselineGetEmitOutput();

View file

@ -0,0 +1,22 @@
/// <reference path="fourslash.ts" />
// @BaselineFile: getEmitOutputTsxFile_React.baseline
// @declaration: true
// @sourceMap: true
// @jsx: react
// @Filename: inputFile1.ts
// @emitThisFile: true
////// regular ts file
//// var t: number = 5;
//// class Bar {
//// x : string;
//// y : number
//// }
// @Filename: inputFile2.tsx
// @emitThisFile: true
//// var y = "my div";
//// var x = <div name= {y} />
verify.baselineGetEmitOutput();