mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-09 02:30:15 -06:00
Merge branch 'master' into JSDocCommentScaffolding
This commit is contained in:
commit
355db2351e
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,6 +25,7 @@ tests/baselines/reference/projectOutput/*
|
||||
tests/baselines/local/projectOutput/*
|
||||
tests/services/baselines/prototyping/local/*
|
||||
tests/services/browser/typescriptServices.js
|
||||
scripts/configureNightly.js
|
||||
scripts/processDiagnosticMessages.d.ts
|
||||
scripts/processDiagnosticMessages.js
|
||||
scripts/importDefinitelyTypedTests.js
|
||||
|
||||
57
Jakefile.js
57
Jakefile.js
@ -138,6 +138,7 @@ var harnessSources = harnessCoreSources.concat([
|
||||
"services/documentRegistry.ts",
|
||||
"services/preProcessFile.ts",
|
||||
"services/patternMatcher.ts",
|
||||
"session.ts",
|
||||
"versionCache.ts",
|
||||
"convertToBase64.ts",
|
||||
"transpile.ts"
|
||||
@ -313,7 +314,7 @@ var processDiagnosticMessagesTs = path.join(scriptsDirectory, "processDiagnostic
|
||||
var diagnosticMessagesJson = path.join(compilerDirectory, "diagnosticMessages.json");
|
||||
var diagnosticInfoMapTs = path.join(compilerDirectory, "diagnosticInformationMap.generated.ts");
|
||||
|
||||
file(processDiagnosticMessagesTs)
|
||||
file(processDiagnosticMessagesTs);
|
||||
|
||||
// processDiagnosticMessages script
|
||||
compileFile(processDiagnosticMessagesJs,
|
||||
@ -338,12 +339,50 @@ file(diagnosticInfoMapTs, [processDiagnosticMessagesJs, diagnosticMessagesJson],
|
||||
complete();
|
||||
});
|
||||
ex.run();
|
||||
}, {async: true})
|
||||
}, {async: true});
|
||||
|
||||
desc("Generates a diagnostic file in TypeScript based on an input JSON file");
|
||||
task("generate-diagnostics", [diagnosticInfoMapTs])
|
||||
task("generate-diagnostics", [diagnosticInfoMapTs]);
|
||||
|
||||
|
||||
// Publish nightly
|
||||
var configureNightlyJs = path.join(scriptsDirectory, "configureNightly.js");
|
||||
var configureNightlyTs = path.join(scriptsDirectory, "configureNightly.ts");
|
||||
var packageJson = "package.json";
|
||||
var programTs = path.join(compilerDirectory, "program.ts");
|
||||
|
||||
file(configureNightlyTs);
|
||||
|
||||
compileFile(/*outfile*/configureNightlyJs,
|
||||
/*sources*/ [configureNightlyTs],
|
||||
/*prereqs*/ [configureNightlyTs],
|
||||
/*prefixes*/ [],
|
||||
/*useBuiltCompiler*/ false,
|
||||
/*noOutFile*/ false,
|
||||
/*generateDeclarations*/ false,
|
||||
/*outDir*/ undefined,
|
||||
/*preserveConstEnums*/ undefined,
|
||||
/*keepComments*/ false,
|
||||
/*noResolve*/ false,
|
||||
/*stripInternal*/ false);
|
||||
|
||||
task("setDebugMode", function() {
|
||||
useDebugMode = true;
|
||||
});
|
||||
|
||||
task("configure-nightly", [configureNightlyJs], function() {
|
||||
var cmd = "node " + configureNightlyJs + " " + packageJson + " " + programTs;
|
||||
console.log(cmd);
|
||||
exec(cmd);
|
||||
}, { async: true });
|
||||
|
||||
desc("Configure, build, test, and publish the nightly release.");
|
||||
task("publish-nightly", ["configure-nightly", "LKG", "clean", "setDebugMode", "runtests"], function () {
|
||||
var cmd = "npm publish --tag next";
|
||||
console.log(cmd);
|
||||
exec(cmd);
|
||||
});
|
||||
|
||||
// Local target to build the compiler and services
|
||||
var tscFile = path.join(builtLocalDirectory, compilerFilename);
|
||||
compileFile(tscFile, compilerSources, [builtLocalDirectory, copyright].concat(compilerSources), [copyright], /*useBuiltCompiler:*/ false);
|
||||
@ -440,11 +479,11 @@ file(specMd, [word2mdJs, specWord], function () {
|
||||
child_process.exec(cmd, function () {
|
||||
complete();
|
||||
});
|
||||
}, {async: true})
|
||||
}, {async: true});
|
||||
|
||||
|
||||
desc("Generates a Markdown version of the Language Specification");
|
||||
task("generate-spec", [specMd])
|
||||
task("generate-spec", [specMd]);
|
||||
|
||||
|
||||
// Makes a new LKG. This target does not build anything, but errors if not all the outputs are present in the built/local directory
|
||||
@ -576,7 +615,7 @@ task("runtests", ["tests", builtLocalDirectory], function() {
|
||||
exec(cmd, deleteTemporaryProjectOutput);
|
||||
}, {async: true});
|
||||
|
||||
desc("Generates code coverage data via instanbul")
|
||||
desc("Generates code coverage data via instanbul");
|
||||
task("generate-code-coverage", ["tests", builtLocalDirectory], function () {
|
||||
var cmd = 'istanbul cover node_modules/mocha/bin/_mocha -- -R min -t ' + testTimeout + ' ' + run;
|
||||
console.log(cmd);
|
||||
@ -619,7 +658,7 @@ task("runtests-browser", ["tests", "browserify", builtLocalDirectory], function(
|
||||
function getDiffTool() {
|
||||
var program = process.env['DIFF']
|
||||
if (!program) {
|
||||
fail("Add the 'DIFF' environment variable to the path of the program you want to use.")
|
||||
fail("Add the 'DIFF' environment variable to the path of the program you want to use.");
|
||||
}
|
||||
return program;
|
||||
}
|
||||
@ -628,14 +667,14 @@ function getDiffTool() {
|
||||
desc("Diffs the compiler baselines using the diff tool specified by the 'DIFF' environment variable");
|
||||
task('diff', function () {
|
||||
var cmd = '"' + getDiffTool() + '" ' + refBaseline + ' ' + localBaseline;
|
||||
console.log(cmd)
|
||||
console.log(cmd);
|
||||
exec(cmd);
|
||||
}, {async: true});
|
||||
|
||||
desc("Diffs the RWC baselines using the diff tool specified by the 'DIFF' environment variable");
|
||||
task('diff-rwc', function () {
|
||||
var cmd = '"' + getDiffTool() + '" ' + refRwcBaseline + ' ' + localRwcBaseline;
|
||||
console.log(cmd)
|
||||
console.log(cmd);
|
||||
exec(cmd);
|
||||
}, {async: true});
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"name": "typescript",
|
||||
"author": "Microsoft Corp.",
|
||||
"homepage": "http://typescriptlang.org/",
|
||||
"version": "1.5.3",
|
||||
"version": "1.6.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "TypeScript is a language for application scale JavaScript development",
|
||||
"keywords": [
|
||||
|
||||
73
scripts/configureNightly.ts
Normal file
73
scripts/configureNightly.ts
Normal file
@ -0,0 +1,73 @@
|
||||
/// <reference path="../src/compiler/sys.ts" />
|
||||
|
||||
/**
|
||||
* A minimal description for a parsed package.json object.
|
||||
*/
|
||||
interface PackageJson {
|
||||
name: string;
|
||||
version: string;
|
||||
keywords: string[];
|
||||
}
|
||||
|
||||
function main(): void {
|
||||
const sys = ts.sys;
|
||||
if (sys.args.length < 2) {
|
||||
sys.write("Usage:" + sys.newLine)
|
||||
sys.write("\tnode configureNightly.js <package.json location> <file containing version>" + sys.newLine);
|
||||
return;
|
||||
}
|
||||
|
||||
// Acquire the version from the package.json file and modify it appropriately.
|
||||
const packageJsonFilePath = ts.normalizePath(sys.args[0]);
|
||||
const packageJsonContents = sys.readFile(packageJsonFilePath);
|
||||
const packageJsonValue: PackageJson = JSON.parse(packageJsonContents);
|
||||
|
||||
const nightlyVersion = getNightlyVersionString(packageJsonValue.version);
|
||||
|
||||
// Modify the package.json structure
|
||||
packageJsonValue.version = nightlyVersion;
|
||||
|
||||
// Acquire and modify the source file that exposes the version string.
|
||||
const tsFilePath = ts.normalizePath(sys.args[1]);
|
||||
const tsFileContents = sys.readFile(tsFilePath);
|
||||
const versionAssignmentRegExp = /export\s+const\s+version\s+=\s+".*";/;
|
||||
const modifiedTsFileContents = tsFileContents.replace(versionAssignmentRegExp, `export const version = "${nightlyVersion}";`);
|
||||
|
||||
// Ensure we are actually changing something - the user probably wants to know that the update failed.
|
||||
if (tsFileContents === modifiedTsFileContents) {
|
||||
let err = `\n '${tsFilePath}' was not updated while configuring for a nightly publish.\n `;
|
||||
|
||||
if (tsFileContents.match(versionAssignmentRegExp)) {
|
||||
err += `Ensure that you have not already run this script; otherwise, erase your changes using 'git checkout -- "${tsFilePath}"'.`;
|
||||
}
|
||||
else {
|
||||
err += `The file seems to no longer have a string matching '${versionAssignmentRegExp}'.`;
|
||||
}
|
||||
|
||||
throw err + "\n";
|
||||
}
|
||||
|
||||
// Finally write the changes to disk.
|
||||
sys.writeFile(packageJsonFilePath, JSON.stringify(packageJsonValue, /*replacer:*/ undefined, /*space:*/ 4))
|
||||
sys.writeFile(tsFilePath, modifiedTsFileContents);
|
||||
}
|
||||
|
||||
function getNightlyVersionString(versionString: string): string {
|
||||
// If the version string already contains "-nightly",
|
||||
// then get the base string and update based on that.
|
||||
const dashNightlyPos = versionString.indexOf("-dev");
|
||||
if (dashNightlyPos >= 0) {
|
||||
versionString = versionString.slice(0, dashNightlyPos);
|
||||
}
|
||||
|
||||
// We're going to append a representation of the current time at the end of the current version.
|
||||
// String.prototype.toISOString() returns a 24-character string formatted as 'YYYY-MM-DDTHH:mm:ss.sssZ',
|
||||
// but we'd prefer to just remove separators and limit ourselves to YYYYMMDD.
|
||||
// UTC time will always be implicit here.
|
||||
const now = new Date();
|
||||
const timeStr = now.toISOString().replace(/:|T|\.|-/g, "").slice(0, 8);
|
||||
|
||||
return `${versionString}-dev.${timeStr}`;
|
||||
}
|
||||
|
||||
main();
|
||||
@ -68,6 +68,7 @@ namespace ts {
|
||||
getPropertyOfType,
|
||||
getSignaturesOfType,
|
||||
getIndexTypeOfType,
|
||||
getBaseTypes,
|
||||
getReturnTypeOfSignature,
|
||||
getSymbolsInScope,
|
||||
getSymbolAtLocation,
|
||||
|
||||
@ -8,7 +8,7 @@ namespace ts {
|
||||
/* @internal */ export let ioWriteTime = 0;
|
||||
|
||||
/** The version of the TypeScript compiler release */
|
||||
export const version = "1.5.3";
|
||||
export const version = "1.6.0";
|
||||
|
||||
export function findConfigFile(searchPath: string): string {
|
||||
let fileName = "tsconfig.json";
|
||||
@ -341,7 +341,7 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] {
|
||||
function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] {
|
||||
return runWithCancellationToken(() => {
|
||||
if (!isDeclarationFile(sourceFile)) {
|
||||
let resolver = getDiagnosticsProducingTypeChecker().getEmitResolver(sourceFile, cancellationToken);
|
||||
@ -350,7 +350,7 @@ namespace ts {
|
||||
return ts.getDeclarationDiagnostics(getEmitHost(writeFile), resolver, sourceFile);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getOptionsDiagnostics(): Diagnostic[] {
|
||||
let allDiagnostics: Diagnostic[] = [];
|
||||
|
||||
@ -1404,6 +1404,7 @@ namespace ts {
|
||||
getPropertyOfType(type: Type, propertyName: string): Symbol;
|
||||
getSignaturesOfType(type: Type, kind: SignatureKind): Signature[];
|
||||
getIndexTypeOfType(type: Type, kind: IndexKind): Type;
|
||||
getBaseTypes(type: InterfaceType): ObjectType[];
|
||||
getReturnTypeOfSignature(signature: Signature): Type;
|
||||
|
||||
getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[];
|
||||
@ -1808,7 +1809,9 @@ namespace ts {
|
||||
typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic)
|
||||
outerTypeParameters: TypeParameter[]; // Outer type parameters (undefined if none)
|
||||
localTypeParameters: TypeParameter[]; // Local type parameters (undefined if none)
|
||||
/* @internal */
|
||||
resolvedBaseConstructorType?: Type; // Resolved base constructor type of class
|
||||
/* @internal */
|
||||
resolvedBaseTypes: ObjectType[]; // Resolved base types
|
||||
}
|
||||
|
||||
|
||||
@ -29,6 +29,7 @@ var Buffer: BufferConstructor = require('buffer').Buffer;
|
||||
// this will work in the browser via browserify
|
||||
var _chai: typeof chai = require('chai');
|
||||
var assert: typeof _chai.assert = _chai.assert;
|
||||
var expect: typeof _chai.expect = _chai.expect;
|
||||
declare var __dirname: string; // Node-specific
|
||||
var global = <any>Function("return this").call(null);
|
||||
|
||||
|
||||
@ -109,13 +109,13 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
export class Session {
|
||||
projectService: ProjectService;
|
||||
pendingOperation = false;
|
||||
fileHash: ts.Map<number> = {};
|
||||
nextFileId = 1;
|
||||
errorTimer: any; /*NodeJS.Timer | number*/
|
||||
immediateId: any;
|
||||
changeSeq = 0;
|
||||
protected projectService: ProjectService;
|
||||
private pendingOperation = false;
|
||||
private fileHash: ts.Map<number> = {};
|
||||
private nextFileId = 1;
|
||||
private errorTimer: any; /*NodeJS.Timer | number*/
|
||||
private immediateId: any;
|
||||
private changeSeq = 0;
|
||||
|
||||
constructor(
|
||||
private host: ServerHost,
|
||||
@ -129,7 +129,7 @@ namespace ts.server {
|
||||
});
|
||||
}
|
||||
|
||||
handleEvent(eventName: string, project: Project, fileName: string) {
|
||||
private handleEvent(eventName: string, project: Project, fileName: string) {
|
||||
if (eventName == "context") {
|
||||
this.projectService.log("got context event, updating diagnostics for" + fileName, "Info");
|
||||
this.updateErrorCheck([{ fileName, project }], this.changeSeq,
|
||||
@ -137,7 +137,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
logError(err: Error, cmd: string) {
|
||||
public logError(err: Error, cmd: string) {
|
||||
var typedErr = <StackTraceError>err;
|
||||
var msg = "Exception on executing command " + cmd;
|
||||
if (typedErr.message) {
|
||||
@ -149,11 +149,11 @@ namespace ts.server {
|
||||
this.projectService.log(msg);
|
||||
}
|
||||
|
||||
sendLineToClient(line: string) {
|
||||
private sendLineToClient(line: string) {
|
||||
this.host.write(line + this.host.newLine);
|
||||
}
|
||||
|
||||
send(msg: protocol.Message) {
|
||||
public send(msg: protocol.Message) {
|
||||
var json = JSON.stringify(msg);
|
||||
if (this.logger.isVerbose()) {
|
||||
this.logger.info(msg.type + ": " + json);
|
||||
@ -162,7 +162,7 @@ namespace ts.server {
|
||||
'\r\n\r\n' + json);
|
||||
}
|
||||
|
||||
event(info: any, eventName: string) {
|
||||
public event(info: any, eventName: string) {
|
||||
var ev: protocol.Event = {
|
||||
seq: 0,
|
||||
type: "event",
|
||||
@ -172,7 +172,7 @@ namespace ts.server {
|
||||
this.send(ev);
|
||||
}
|
||||
|
||||
response(info: any, cmdName: string, reqSeq = 0, errorMsg?: string) {
|
||||
private response(info: any, cmdName: string, reqSeq = 0, errorMsg?: string) {
|
||||
var res: protocol.Response = {
|
||||
seq: 0,
|
||||
type: "response",
|
||||
@ -189,11 +189,11 @@ namespace ts.server {
|
||||
this.send(res);
|
||||
}
|
||||
|
||||
output(body: any, commandName: string, requestSequence = 0, errorMessage?: string) {
|
||||
public output(body: any, commandName: string, requestSequence = 0, errorMessage?: string) {
|
||||
this.response(body, commandName, requestSequence, errorMessage);
|
||||
}
|
||||
|
||||
semanticCheck(file: string, project: Project) {
|
||||
private semanticCheck(file: string, project: Project) {
|
||||
try {
|
||||
var diags = project.compilerService.languageService.getSemanticDiagnostics(file);
|
||||
|
||||
@ -207,7 +207,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
syntacticCheck(file: string, project: Project) {
|
||||
private syntacticCheck(file: string, project: Project) {
|
||||
try {
|
||||
var diags = project.compilerService.languageService.getSyntacticDiagnostics(file);
|
||||
if (diags) {
|
||||
@ -220,12 +220,12 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
errorCheck(file: string, project: Project) {
|
||||
private errorCheck(file: string, project: Project) {
|
||||
this.syntacticCheck(file, project);
|
||||
this.semanticCheck(file, project);
|
||||
}
|
||||
|
||||
updateProjectStructure(seq: number, matchSeq: (seq: number) => boolean, ms = 1500) {
|
||||
private updateProjectStructure(seq: number, matchSeq: (seq: number) => boolean, ms = 1500) {
|
||||
setTimeout(() => {
|
||||
if (matchSeq(seq)) {
|
||||
this.projectService.updateProjectStructure();
|
||||
@ -233,7 +233,7 @@ namespace ts.server {
|
||||
}, ms);
|
||||
}
|
||||
|
||||
updateErrorCheck(checkList: PendingErrorCheck[], seq: number,
|
||||
private updateErrorCheck(checkList: PendingErrorCheck[], seq: number,
|
||||
matchSeq: (seq: number) => boolean, ms = 1500, followMs = 200) {
|
||||
if (followMs > ms) {
|
||||
followMs = ms;
|
||||
@ -269,7 +269,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
getDefinition(line: number, offset: number, fileName: string): protocol.FileSpan[] {
|
||||
private getDefinition(line: number, offset: number, fileName: string): protocol.FileSpan[] {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
@ -291,7 +291,7 @@ namespace ts.server {
|
||||
}));
|
||||
}
|
||||
|
||||
getTypeDefinition(line: number, offset: number, fileName: string): protocol.FileSpan[] {
|
||||
private getTypeDefinition(line: number, offset: number, fileName: string): protocol.FileSpan[] {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
@ -313,7 +313,7 @@ namespace ts.server {
|
||||
}));
|
||||
}
|
||||
|
||||
getOccurrences(line: number, offset: number, fileName: string): protocol.OccurrencesResponseItem[]{
|
||||
private getOccurrences(line: number, offset: number, fileName: string): protocol.OccurrencesResponseItem[]{
|
||||
fileName = ts.normalizePath(fileName);
|
||||
let project = this.projectService.getProjectForFile(fileName);
|
||||
|
||||
@ -343,7 +343,7 @@ namespace ts.server {
|
||||
});
|
||||
}
|
||||
|
||||
getProjectInfo(fileName: string, needFileNameList: boolean): protocol.ProjectInfo {
|
||||
private getProjectInfo(fileName: string, needFileNameList: boolean): protocol.ProjectInfo {
|
||||
fileName = ts.normalizePath(fileName)
|
||||
let project = this.projectService.getProjectForFile(fileName)
|
||||
|
||||
@ -358,7 +358,7 @@ namespace ts.server {
|
||||
return projectInfo;
|
||||
}
|
||||
|
||||
getRenameLocations(line: number, offset: number, fileName: string,findInComments: boolean, findInStrings: boolean): protocol.RenameResponseBody {
|
||||
private getRenameLocations(line: number, offset: number, fileName: string,findInComments: boolean, findInStrings: boolean): protocol.RenameResponseBody {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
@ -426,7 +426,7 @@ namespace ts.server {
|
||||
return { info: renameInfo, locs: bakedRenameLocs };
|
||||
}
|
||||
|
||||
getReferences(line: number, offset: number, fileName: string): protocol.ReferencesResponseBody {
|
||||
private getReferences(line: number, offset: number, fileName: string): protocol.ReferencesResponseBody {
|
||||
// TODO: get all projects for this file; report refs for all projects deleting duplicates
|
||||
// can avoid duplicates by eliminating same ref file from subsequent projects
|
||||
var file = ts.normalizePath(fileName);
|
||||
@ -473,12 +473,12 @@ namespace ts.server {
|
||||
};
|
||||
}
|
||||
|
||||
openClientFile(fileName: string) {
|
||||
private openClientFile(fileName: string) {
|
||||
var file = ts.normalizePath(fileName);
|
||||
this.projectService.openClientFile(file);
|
||||
}
|
||||
|
||||
getQuickInfo(line: number, offset: number, fileName: string): protocol.QuickInfoResponseBody {
|
||||
private getQuickInfo(line: number, offset: number, fileName: string): protocol.QuickInfoResponseBody {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
@ -504,7 +504,7 @@ namespace ts.server {
|
||||
};
|
||||
}
|
||||
|
||||
getFormattingEditsForRange(line: number, offset: number, endLine: number, endOffset: number, fileName: string): protocol.CodeEdit[] {
|
||||
private getFormattingEditsForRange(line: number, offset: number, endLine: number, endOffset: number, fileName: string): protocol.CodeEdit[] {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
@ -531,7 +531,7 @@ namespace ts.server {
|
||||
});
|
||||
}
|
||||
|
||||
getFormattingEditsAfterKeystroke(line: number, offset: number, key: string, fileName: string): protocol.CodeEdit[] {
|
||||
private getFormattingEditsAfterKeystroke(line: number, offset: number, key: string, fileName: string): protocol.CodeEdit[] {
|
||||
var file = ts.normalizePath(fileName);
|
||||
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
@ -607,7 +607,7 @@ namespace ts.server {
|
||||
});
|
||||
}
|
||||
|
||||
getCompletions(line: number, offset: number, prefix: string, fileName: string): protocol.CompletionEntry[] {
|
||||
private getCompletions(line: number, offset: number, prefix: string, fileName: string): protocol.CompletionEntry[] {
|
||||
if (!prefix) {
|
||||
prefix = "";
|
||||
}
|
||||
@ -633,7 +633,7 @@ namespace ts.server {
|
||||
}, []).sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
getCompletionEntryDetails(line: number, offset: number,
|
||||
private getCompletionEntryDetails(line: number, offset: number,
|
||||
entryNames: string[], fileName: string): protocol.CompletionEntryDetails[] {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
@ -653,7 +653,7 @@ namespace ts.server {
|
||||
}, []);
|
||||
}
|
||||
|
||||
getSignatureHelpItems(line: number, offset: number, fileName: string): protocol.SignatureHelpItems {
|
||||
private getSignatureHelpItems(line: number, offset: number, fileName: string): protocol.SignatureHelpItems {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
@ -682,7 +682,7 @@ namespace ts.server {
|
||||
return result;
|
||||
}
|
||||
|
||||
getDiagnostics(delay: number, fileNames: string[]) {
|
||||
private getDiagnostics(delay: number, fileNames: string[]) {
|
||||
var checkList = fileNames.reduce((accum: PendingErrorCheck[], fileName: string) => {
|
||||
fileName = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(fileName);
|
||||
@ -697,7 +697,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
change(line: number, offset: number, endLine: number, endOffset: number, insertString: string, fileName: string) {
|
||||
private change(line: number, offset: number, endLine: number, endOffset: number, insertString: string, fileName: string) {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (project) {
|
||||
@ -712,7 +712,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
reload(fileName: string, tempFileName: string, reqSeq = 0) {
|
||||
private reload(fileName: string, tempFileName: string, reqSeq = 0) {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var tmpfile = ts.normalizePath(tempFileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
@ -725,7 +725,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
saveToTmp(fileName: string, tempFileName: string) {
|
||||
private saveToTmp(fileName: string, tempFileName: string) {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var tmpfile = ts.normalizePath(tempFileName);
|
||||
|
||||
@ -735,12 +735,12 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
closeClientFile(fileName: string) {
|
||||
private closeClientFile(fileName: string) {
|
||||
var file = ts.normalizePath(fileName);
|
||||
this.projectService.closeClientFile(file);
|
||||
}
|
||||
|
||||
decorateNavigationBarItem(project: Project, fileName: string, items: ts.NavigationBarItem[]): protocol.NavigationBarItem[] {
|
||||
private decorateNavigationBarItem(project: Project, fileName: string, items: ts.NavigationBarItem[]): protocol.NavigationBarItem[] {
|
||||
if (!items) {
|
||||
return undefined;
|
||||
}
|
||||
@ -759,7 +759,7 @@ namespace ts.server {
|
||||
}));
|
||||
}
|
||||
|
||||
getNavigationBarItems(fileName: string): protocol.NavigationBarItem[] {
|
||||
private getNavigationBarItems(fileName: string): protocol.NavigationBarItem[] {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
@ -775,7 +775,7 @@ namespace ts.server {
|
||||
return this.decorateNavigationBarItem(project, fileName, items);
|
||||
}
|
||||
|
||||
getNavigateToItems(searchValue: string, fileName: string, maxResultCount?: number): protocol.NavtoItem[] {
|
||||
private getNavigateToItems(searchValue: string, fileName: string, maxResultCount?: number): protocol.NavtoItem[] {
|
||||
var file = ts.normalizePath(fileName);
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
if (!project) {
|
||||
@ -814,7 +814,7 @@ namespace ts.server {
|
||||
});
|
||||
}
|
||||
|
||||
getBraceMatching(line: number, offset: number, fileName: string): protocol.TextSpan[] {
|
||||
private getBraceMatching(line: number, offset: number, fileName: string): protocol.TextSpan[] {
|
||||
var file = ts.normalizePath(fileName);
|
||||
|
||||
var project = this.projectService.getProjectForFile(file);
|
||||
@ -836,7 +836,7 @@ namespace ts.server {
|
||||
}));
|
||||
}
|
||||
|
||||
exit() {
|
||||
public exit() {
|
||||
}
|
||||
|
||||
private handlers : Map<(request: protocol.Request) => {response?: any, responseRequired?: boolean}> = {
|
||||
@ -942,14 +942,14 @@ namespace ts.server {
|
||||
return {response: this.getProjectInfo(file, needFileNameList), responseRequired: true};
|
||||
},
|
||||
};
|
||||
addProtocolHandler(command: string, handler: (request: protocol.Request) => {response?: any, responseRequired: boolean}) {
|
||||
public addProtocolHandler(command: string, handler: (request: protocol.Request) => {response?: any, responseRequired: boolean}) {
|
||||
if (this.handlers[command]) {
|
||||
throw new Error(`Protocol handler already exists for command "${command}"`);
|
||||
}
|
||||
this.handlers[command] = handler;
|
||||
}
|
||||
|
||||
executeCommand(request: protocol.Request) : {response?: any, responseRequired?: boolean} {
|
||||
public executeCommand(request: protocol.Request) : {response?: any, responseRequired?: boolean} {
|
||||
var handler = this.handlers[request.command];
|
||||
if (handler) {
|
||||
return handler(request);
|
||||
@ -960,7 +960,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
onMessage(message: string) {
|
||||
public onMessage(message: string) {
|
||||
if (this.logger.isVerbose()) {
|
||||
this.logger.info("request: " + message);
|
||||
var start = this.hrtime();
|
||||
|
||||
@ -48,6 +48,7 @@ namespace ts {
|
||||
getConstructSignatures(): Signature[];
|
||||
getStringIndexType(): Type;
|
||||
getNumberIndexType(): Type;
|
||||
getBaseTypes(): ObjectType[]
|
||||
}
|
||||
|
||||
export interface Signature {
|
||||
@ -682,6 +683,11 @@ namespace ts {
|
||||
getNumberIndexType(): Type {
|
||||
return this.checker.getIndexTypeOfType(this, IndexKind.Number);
|
||||
}
|
||||
getBaseTypes(): ObjectType[] {
|
||||
return this.flags & (TypeFlags.Class | TypeFlags.Interface)
|
||||
? this.checker.getBaseTypes(<TypeObject & InterfaceType>this)
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
class SignatureObject implements Signature {
|
||||
|
||||
447
tests/cases/unittests/session.ts
Normal file
447
tests/cases/unittests/session.ts
Normal file
@ -0,0 +1,447 @@
|
||||
/// <reference path="..\..\..\src\harness\harness.ts" />
|
||||
|
||||
namespace ts.server {
|
||||
let lastWrittenToHost: string;
|
||||
const mockHost: ServerHost = {
|
||||
args: [],
|
||||
newLine: "\n",
|
||||
useCaseSensitiveFileNames: true,
|
||||
write(s): void { lastWrittenToHost = s; },
|
||||
readFile(): string { return void 0; },
|
||||
writeFile(): void {},
|
||||
resolvePath(): string { return void 0; },
|
||||
fileExists: () => false,
|
||||
directoryExists: () => false,
|
||||
createDirectory(): void {},
|
||||
getExecutingFilePath(): string { return void 0; },
|
||||
getCurrentDirectory(): string { return void 0; },
|
||||
readDirectory(): string[] { return []; },
|
||||
exit(): void {}
|
||||
};
|
||||
const mockLogger: Logger = {
|
||||
close(): void {},
|
||||
isVerbose(): boolean { return false; },
|
||||
loggingEnabled(): boolean { return false; },
|
||||
perftrc(s: string): void {},
|
||||
info(s: string): void {},
|
||||
startGroup(): void {},
|
||||
endGroup(): void {},
|
||||
msg(s: string, type?: string): void {},
|
||||
};
|
||||
|
||||
describe("the Session class", () => {
|
||||
let session: Session;
|
||||
let lastSent: protocol.Message;
|
||||
|
||||
beforeEach(() => {
|
||||
session = new Session(mockHost, Buffer.byteLength, process.hrtime, mockLogger);
|
||||
session.send = (msg: protocol.Message) => {
|
||||
lastSent = msg;
|
||||
};
|
||||
});
|
||||
|
||||
describe("executeCommand", () => {
|
||||
it("should throw when commands are executed with invalid arguments", () => {
|
||||
const req: protocol.FileRequest = {
|
||||
command: CommandNames.Open,
|
||||
seq: 0,
|
||||
type: "command",
|
||||
arguments: {
|
||||
file: undefined
|
||||
}
|
||||
};
|
||||
|
||||
expect(() => session.executeCommand(req)).to.throw();
|
||||
});
|
||||
it("should output an error response when a command does not exist", () => {
|
||||
const req: protocol.Request = {
|
||||
command: "foobar",
|
||||
seq: 0,
|
||||
type: "command"
|
||||
};
|
||||
|
||||
session.executeCommand(req);
|
||||
|
||||
expect(lastSent).to.deep.equal(<protocol.Response>{
|
||||
command: CommandNames.Unknown,
|
||||
type: "response",
|
||||
seq: 0,
|
||||
message: "Unrecognized JSON command: foobar",
|
||||
request_seq: 0,
|
||||
success: false
|
||||
});
|
||||
});
|
||||
it("should return a tuple containing the response and if a response is required on success", () => {
|
||||
const req: protocol.ConfigureRequest = {
|
||||
command: CommandNames.Configure,
|
||||
seq: 0,
|
||||
type: "command",
|
||||
arguments: {
|
||||
hostInfo: "unit test",
|
||||
formatOptions: {
|
||||
newLineCharacter: "`n"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
expect(session.executeCommand(req)).to.deep.equal({
|
||||
responseRequired: false
|
||||
});
|
||||
expect(lastSent).to.deep.equal({
|
||||
command: CommandNames.Configure,
|
||||
type: "response",
|
||||
success: true,
|
||||
request_seq: 0,
|
||||
seq: 0,
|
||||
body: undefined
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("onMessage", () => {
|
||||
it("should not throw when commands are executed with invalid arguments", () => {
|
||||
let i = 0;
|
||||
for (name in CommandNames) {
|
||||
if (!Object.prototype.hasOwnProperty.call(CommandNames, name)) {
|
||||
continue;
|
||||
}
|
||||
const req: protocol.Request = {
|
||||
command: name,
|
||||
seq: i++,
|
||||
type: "command"
|
||||
};
|
||||
session.onMessage(JSON.stringify(req));
|
||||
req.seq = i++;
|
||||
req.arguments = {};
|
||||
session.onMessage(JSON.stringify(req));
|
||||
req.seq = i++;
|
||||
req.arguments = null;
|
||||
session.onMessage(JSON.stringify(req));
|
||||
req.seq = i++;
|
||||
req.arguments = "";
|
||||
session.onMessage(JSON.stringify(req));
|
||||
req.seq = i++;
|
||||
req.arguments = 0;
|
||||
session.onMessage(JSON.stringify(req));
|
||||
req.seq = i++;
|
||||
req.arguments = [];
|
||||
session.onMessage(JSON.stringify(req));
|
||||
}
|
||||
session.onMessage("GARBAGE NON_JSON DATA");
|
||||
});
|
||||
it("should output the response for a correctly handled message", () => {
|
||||
const req: protocol.ConfigureRequest = {
|
||||
command: CommandNames.Configure,
|
||||
seq: 0,
|
||||
type: "command",
|
||||
arguments: {
|
||||
hostInfo: "unit test",
|
||||
formatOptions: {
|
||||
newLineCharacter: "`n"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
session.onMessage(JSON.stringify(req));
|
||||
|
||||
expect(lastSent).to.deep.equal(<protocol.ConfigureResponse>{
|
||||
command: CommandNames.Configure,
|
||||
type: "response",
|
||||
success: true,
|
||||
request_seq: 0,
|
||||
seq: 0,
|
||||
body: undefined
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("send", () => {
|
||||
it("is an overrideable handle which sends protocol messages over the wire", () => {
|
||||
const msg = {seq: 0, type: "none"};
|
||||
const strmsg = JSON.stringify(msg);
|
||||
const len = 1 + Buffer.byteLength(strmsg, "utf8");
|
||||
const resultMsg = `Content-Length: ${len}\r\n\r\n${strmsg}\n`;
|
||||
|
||||
session.send = Session.prototype.send;
|
||||
assert(session.send);
|
||||
expect(session.send(msg)).to.not.exist;
|
||||
expect(lastWrittenToHost).to.equal(resultMsg);
|
||||
});
|
||||
});
|
||||
|
||||
describe("addProtocolHandler", () => {
|
||||
it("can add protocol handlers", () => {
|
||||
const respBody = {
|
||||
item: false
|
||||
};
|
||||
const command = "newhandle";
|
||||
const result = {
|
||||
response: respBody,
|
||||
responseRequired: true
|
||||
};
|
||||
|
||||
session.addProtocolHandler(command, (req) => result);
|
||||
|
||||
expect(session.executeCommand({
|
||||
command,
|
||||
seq: 0,
|
||||
type: "command"
|
||||
})).to.deep.equal(result);
|
||||
});
|
||||
it("throws when a duplicate handler is passed", () => {
|
||||
const respBody = {
|
||||
item: false
|
||||
};
|
||||
const resp = {
|
||||
response: respBody,
|
||||
responseRequired: true
|
||||
};
|
||||
const command = "newhandle";
|
||||
|
||||
session.addProtocolHandler(command, (req) => resp);
|
||||
|
||||
expect(() => session.addProtocolHandler(command, (req) => resp))
|
||||
.to.throw(`Protocol handler already exists for command "${command}"`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("event", () => {
|
||||
it("can format event responses and send them", () => {
|
||||
const evt = "notify-test";
|
||||
const info = {
|
||||
test: true
|
||||
};
|
||||
|
||||
session.event(info, evt);
|
||||
|
||||
expect(lastSent).to.deep.equal({
|
||||
type: "event",
|
||||
seq: 0,
|
||||
event: evt,
|
||||
body: info
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("output", () => {
|
||||
it("can format command responses and send them", () => {
|
||||
const body = {
|
||||
block: {
|
||||
key: "value"
|
||||
}
|
||||
};
|
||||
const command = "test";
|
||||
|
||||
session.output(body, command);
|
||||
|
||||
expect(lastSent).to.deep.equal({
|
||||
seq: 0,
|
||||
request_seq: 0,
|
||||
type: "response",
|
||||
command,
|
||||
body: body,
|
||||
success: true
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("how Session is extendable via subclassing", () => {
|
||||
class TestSession extends Session {
|
||||
lastSent: protocol.Message;
|
||||
customHandler = "testhandler";
|
||||
constructor() {
|
||||
super(mockHost, Buffer.byteLength, process.hrtime, mockLogger);
|
||||
this.addProtocolHandler(this.customHandler, () => {
|
||||
return {response: undefined, responseRequired: true};
|
||||
});
|
||||
}
|
||||
send(msg: protocol.Message) {
|
||||
this.lastSent = msg;
|
||||
}
|
||||
};
|
||||
|
||||
it("can override methods such as send", () => {
|
||||
const session = new TestSession();
|
||||
const body = {
|
||||
block: {
|
||||
key: "value"
|
||||
}
|
||||
};
|
||||
const command = "test";
|
||||
|
||||
session.output(body, command);
|
||||
|
||||
expect(session.lastSent).to.deep.equal({
|
||||
seq: 0,
|
||||
request_seq: 0,
|
||||
type: "response",
|
||||
command,
|
||||
body: body,
|
||||
success: true
|
||||
});
|
||||
});
|
||||
it("can add and respond to new protocol handlers", () => {
|
||||
const session = new TestSession();
|
||||
|
||||
expect(session.executeCommand({
|
||||
seq: 0,
|
||||
type: "command",
|
||||
command: session.customHandler
|
||||
})).to.deep.equal({
|
||||
response: undefined,
|
||||
responseRequired: true
|
||||
});
|
||||
});
|
||||
it("has access to the project service", () => {
|
||||
class ServiceSession extends TestSession {
|
||||
constructor() {
|
||||
super();
|
||||
assert(this.projectService);
|
||||
expect(this.projectService).to.be.instanceOf(ProjectService);
|
||||
}
|
||||
};
|
||||
new ServiceSession();
|
||||
});
|
||||
});
|
||||
|
||||
describe("an example of using the Session API to create an in-process server", () => {
|
||||
class InProcSession extends Session {
|
||||
private queue: protocol.Request[] = [];
|
||||
constructor(private client: InProcClient) {
|
||||
super(mockHost, Buffer.byteLength, process.hrtime, mockLogger);
|
||||
this.addProtocolHandler("echo", (req: protocol.Request) => ({
|
||||
response: req.arguments,
|
||||
responseRequired: true
|
||||
}));
|
||||
}
|
||||
|
||||
send(msg: protocol.Message) {
|
||||
this.client.handle(msg);
|
||||
}
|
||||
|
||||
enqueue(msg: protocol.Request) {
|
||||
this.queue.unshift(msg);
|
||||
}
|
||||
|
||||
handleRequest(msg: protocol.Request) {
|
||||
let response: protocol.Response;
|
||||
try {
|
||||
({response} = this.executeCommand(msg));
|
||||
}
|
||||
catch (e) {
|
||||
this.output(undefined, msg.command, msg.seq, e.toString());
|
||||
return;
|
||||
}
|
||||
if (response) {
|
||||
this.output(response, msg.command, msg.seq);
|
||||
}
|
||||
}
|
||||
|
||||
consumeQueue() {
|
||||
while (this.queue.length > 0) {
|
||||
const elem = this.queue.pop();
|
||||
this.handleRequest(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InProcClient {
|
||||
private server: InProcSession;
|
||||
private seq = 0;
|
||||
private callbacks: ts.Map<(resp: protocol.Response) => void> = {};
|
||||
private eventHandlers: ts.Map<(args: any) => void> = {};
|
||||
|
||||
handle(msg: protocol.Message): void {
|
||||
if (msg.type === "response") {
|
||||
const response = <protocol.Response>msg;
|
||||
if (this.callbacks[response.request_seq]) {
|
||||
this.callbacks[response.request_seq](response);
|
||||
delete this.callbacks[response.request_seq];
|
||||
}
|
||||
}
|
||||
else if (msg.type === "event") {
|
||||
const event = <protocol.Event>msg;
|
||||
this.emit(event.event, event.body);
|
||||
}
|
||||
}
|
||||
|
||||
emit(name: string, args: any): void {
|
||||
if (this.eventHandlers[name]) {
|
||||
this.eventHandlers[name](args);
|
||||
}
|
||||
}
|
||||
|
||||
on(name: string, handler: (args: any) => void): void {
|
||||
this.eventHandlers[name] = handler;
|
||||
}
|
||||
|
||||
connect(session: InProcSession): void {
|
||||
this.server = session;
|
||||
}
|
||||
|
||||
execute(command: string, args: any, callback: (resp: protocol.Response) => void): void {
|
||||
if (!this.server) {
|
||||
return;
|
||||
}
|
||||
this.seq++;
|
||||
this.server.enqueue({
|
||||
seq: this.seq,
|
||||
type: "command",
|
||||
command,
|
||||
arguments: args
|
||||
});
|
||||
this.callbacks[this.seq] = callback;
|
||||
}
|
||||
};
|
||||
|
||||
it("can be constructed and respond to commands", (done) => {
|
||||
const cli = new InProcClient();
|
||||
const session = new InProcSession(cli);
|
||||
const toEcho = {
|
||||
data: true
|
||||
};
|
||||
const toEvent = {
|
||||
data: false
|
||||
};
|
||||
let responses = 0;
|
||||
|
||||
// Connect the client
|
||||
cli.connect(session);
|
||||
|
||||
// Add an event handler
|
||||
cli.on("testevent", (eventinfo) => {
|
||||
expect(eventinfo).to.equal(toEvent);
|
||||
responses++;
|
||||
expect(responses).to.equal(1);
|
||||
});
|
||||
|
||||
// Trigger said event from the server
|
||||
session.event(toEvent, "testevent");
|
||||
|
||||
// Queue an echo command
|
||||
cli.execute("echo", toEcho, (resp) => {
|
||||
assert(resp.success, resp.message);
|
||||
responses++;
|
||||
expect(responses).to.equal(2);
|
||||
expect(resp.body).to.deep.equal(toEcho);
|
||||
});
|
||||
|
||||
// Queue a configure command
|
||||
cli.execute("configure", {
|
||||
hostInfo: "unit test",
|
||||
formatOptions: {
|
||||
newLineCharacter: "`n"
|
||||
}
|
||||
}, (resp) => {
|
||||
assert(resp.success, resp.message);
|
||||
responses++;
|
||||
expect(responses).to.equal(3);
|
||||
done();
|
||||
});
|
||||
|
||||
// Consume the queue and trigger the callbacks
|
||||
session.consumeQueue();
|
||||
});
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user