diff --git a/.gitignore b/.gitignore index 5125de360b8..2e90e788faa 100644 --- a/.gitignore +++ b/.gitignore @@ -39,7 +39,7 @@ scripts/word2md.js scripts/buildProtocol.js scripts/ior.js scripts/authors.js -scripts/configureNightly.js +scripts/configurePrerelease.js scripts/processDiagnosticMessages.d.ts scripts/processDiagnosticMessages.js scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js diff --git a/Jakefile.js b/Jakefile.js index fa600250649..9bef7946722 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -556,16 +556,16 @@ desc("Generates a diagnostic file in TypeScript based on an input JSON file"); task("generate-diagnostics", [diagnosticInfoMapTs]); // Publish nightly -var configureNightlyJs = path.join(scriptsDirectory, "configureNightly.js"); -var configureNightlyTs = path.join(scriptsDirectory, "configureNightly.ts"); +var configurePrereleaseJs = path.join(scriptsDirectory, "configurePrerelease.js"); +var configurePrereleaseTs = path.join(scriptsDirectory, "configurePrerelease.ts"); var packageJson = "package.json"; var versionFile = path.join(compilerDirectory, "core.ts"); -file(configureNightlyTs); +file(configurePrereleaseTs); -compileFile(/*outfile*/configureNightlyJs, - /*sources*/[configureNightlyTs], - /*prereqs*/[configureNightlyTs], +compileFile(/*outfile*/configurePrereleaseJs, + /*sources*/[configurePrereleaseTs], + /*prereqs*/[configurePrereleaseTs], /*prefixes*/[], /*useBuiltCompiler*/ false, { noOutFile: false, generateDeclarations: false, keepComments: false, noResolve: false, stripInternal: false }); @@ -574,8 +574,8 @@ task("setDebugMode", function () { useDebugMode = true; }); -task("configure-nightly", [configureNightlyJs], function () { - var cmd = host + " " + configureNightlyJs + " " + packageJson + " " + versionFile; +task("configure-nightly", [configurePrereleaseJs], function () { + var cmd = host + " " + configurePrereleaseJs + " dev " + packageJson + " " + versionFile; console.log(cmd); exec(cmd); }, { async: true }); @@ -587,6 +587,19 @@ task("publish-nightly", ["configure-nightly", "LKG", "clean", "setDebugMode", "r exec(cmd); }); +task("configure-insiders", [configurePrereleaseJs], function () { + var cmd = host + " " + configurePrereleaseJs + " insiders " + packageJson + " " + versionFile; + console.log(cmd); + exec(cmd); +}, { async: true }); + +desc("Configure, build, test, and publish the insiders release."); +task("publish-insiders", ["configure-nightly", "LKG", "clean", "setDebugMode", "runtests-parallel"], function () { + var cmd = "npm publish --tag insiders"; + console.log(cmd); + exec(cmd); +}); + var importDefinitelyTypedTestsDirectory = path.join(scriptsDirectory, "importDefinitelyTypedTests"); var importDefinitelyTypedTestsJs = path.join(importDefinitelyTypedTestsDirectory, "importDefinitelyTypedTests.js"); var importDefinitelyTypedTestsTs = path.join(importDefinitelyTypedTestsDirectory, "importDefinitelyTypedTests.ts"); diff --git a/scripts/configureNightly.ts b/scripts/configurePrerelease.ts similarity index 79% rename from scripts/configureNightly.ts rename to scripts/configurePrerelease.ts index 778f5ce3727..a63490ca051 100644 --- a/scripts/configureNightly.ts +++ b/scripts/configurePrerelease.ts @@ -11,34 +11,39 @@ interface PackageJson { function main(): void { const sys = ts.sys; - if (sys.args.length < 2) { + if (sys.args.length < 3) { sys.write("Usage:" + sys.newLine) - sys.write("\tnode configureNightly.js " + sys.newLine); + sys.write("\tnode configureNightly.js " + sys.newLine); return; } + const tag = sys.args[0]; + if (tag !== "dev" && tag !== "insiders") { + throw new Error(`Unexpected tag name '${tag}'.`); + } + // Acquire the version from the package.json file and modify it appropriately. - const packageJsonFilePath = ts.normalizePath(sys.args[0]); + const packageJsonFilePath = ts.normalizePath(sys.args[1]); const packageJsonValue: PackageJson = JSON.parse(sys.readFile(packageJsonFilePath)); const { majorMinor, patch } = parsePackageJsonVersion(packageJsonValue.version); - const nightlyPatch = getNightlyPatch(patch); + const prereleasePatch = getPrereleasePatch(tag, patch); // Acquire and modify the source file that exposes the version string. - const tsFilePath = ts.normalizePath(sys.args[1]); + const tsFilePath = ts.normalizePath(sys.args[2]); const tsFileContents = ts.sys.readFile(tsFilePath); - const modifiedTsFileContents = updateTsFile(tsFilePath, tsFileContents, majorMinor, patch, nightlyPatch); + const modifiedTsFileContents = updateTsFile(tsFilePath, tsFileContents, majorMinor, patch, prereleasePatch); // 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 `; + let err = `\n '${tsFilePath}' was not updated while configuring for a prerelease publish for '${tag}'.\n `; err += `Ensure that you have not already run this script; otherwise, erase your changes using 'git checkout -- "${tsFilePath}"'.`; - throw err + "\n"; + throw new Error(err + "\n"); } // Finally write the changes to disk. // Modify the package.json structure - packageJsonValue.version = `${majorMinor}.${nightlyPatch}`; + packageJsonValue.version = `${majorMinor}.${prereleasePatch}`; sys.writeFile(packageJsonFilePath, JSON.stringify(packageJsonValue, /*replacer:*/ undefined, /*space:*/ 4)) sys.writeFile(tsFilePath, modifiedTsFileContents); } @@ -69,7 +74,7 @@ function parsePackageJsonVersion(versionString: string): { majorMinor: string, p } /** e.g. 0-dev.20170707 */ -function getNightlyPatch(plainPatch: string): string { +function getPrereleasePatch(tag: string, plainPatch: string): string { // 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. @@ -77,7 +82,7 @@ function getNightlyPatch(plainPatch: string): string { const now = new Date(); const timeStr = now.toISOString().replace(/:|T|\.|-/g, "").slice(0, 8); - return `${plainPatch}-dev.${timeStr}`; + return `${plainPatch}-${tag}.${timeStr}`; } main(); \ No newline at end of file diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 405fd11f138..14631fa69cb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5995,7 +5995,9 @@ namespace ts { for (const memberType of types) { for (const { escapedName } of getAugmentedPropertiesOfType(memberType)) { if (!props.has(escapedName)) { - props.set(escapedName, createUnionOrIntersectionProperty(unionType as UnionType, escapedName)); + const prop = createUnionOrIntersectionProperty(unionType as UnionType, escapedName); + // May be undefined if the property is private + if (prop) props.set(escapedName, prop); } } } @@ -6177,7 +6179,7 @@ namespace ts { t; } - function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String): Symbol { + function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String): Symbol | undefined { let props: Symbol[]; const isUnion = containingType.flags & TypeFlags.Union; const excludeModifiers = isUnion ? ModifierFlags.NonPublicAccessibilityModifier : 0; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 6a421bf6948..bf6a086967c 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4704,7 +4704,7 @@ namespace ts { } export function isTypeOfExpression(node: Node): node is TypeOfExpression { - return node.kind === SyntaxKind.AwaitExpression; + return node.kind === SyntaxKind.TypeOfExpression; } export function isVoidExpression(node: Node): node is VoidExpression { diff --git a/src/harness/externalCompileRunner.ts b/src/harness/externalCompileRunner.ts index c829a6490bb..1f945cfcc9c 100644 --- a/src/harness/externalCompileRunner.ts +++ b/src/harness/externalCompileRunner.ts @@ -131,13 +131,13 @@ function removeExpectedErrors(errors: string, cwd: string): string { function isUnexpectedError(cwd: string) { return (error: string[]) => { ts.Debug.assertGreaterThanOrEqual(error.length, 1); - const match = error[0].match(/(.+\.ts)\((\d+),\d+\): error TS/); + const match = error[0].match(/(.+\.tsx?)\((\d+),\d+\): error TS/); if (!match) { return true; } const [, errorFile, lineNumberString] = match; const lines = fs.readFileSync(path.join(cwd, errorFile), { encoding: "utf8" }).split("\n"); - const lineNumber = parseInt(lineNumberString); + const lineNumber = parseInt(lineNumberString) - 1; ts.Debug.assertGreaterThanOrEqual(lineNumber, 0); ts.Debug.assertLessThan(lineNumber, lines.length); const previousLine = lineNumber - 1 > 0 ? lines[lineNumber - 1] : ""; diff --git a/src/services/completions.ts b/src/services/completions.ts index 82e7065228a..80fdf6c280e 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -20,6 +20,7 @@ namespace ts.Completions { None, ClassElementKeywords, // Keywords at class keyword ConstructorParameterKeywords, // Keywords at constructor parameter + FunctionLikeBodyKeywords // Keywords at function like body } export function getCompletionsAtPosition( @@ -1060,6 +1061,10 @@ namespace ts.Completions { return true; } + if (tryGetFunctionLikeBodyCompletionContainer(contextToken)) { + keywordFilters = KeywordCompletionFilters.FunctionLikeBodyKeywords; + } + if (classLikeContainer = tryGetClassLikeCompletionContainer(contextToken)) { // cursor inside class declaration getGetClassLikeCompletionSymbols(classLikeContainer); @@ -1688,6 +1693,22 @@ namespace ts.Completions { return undefined; } + function tryGetFunctionLikeBodyCompletionContainer(contextToken: Node): FunctionLikeDeclaration { + if (contextToken) { + let prev: Node; + const container = findAncestor(contextToken.parent, (node: Node) => { + if (isClassLike(node)) { + return "quit"; + } + if (isFunctionLikeDeclaration(node) && prev === node.body) { + return true; + } + prev = node; + }); + return container && container as FunctionLikeDeclaration; + } + } + function tryGetContainingJsxElement(contextToken: Node): JsxOpeningLikeElement { if (contextToken) { const parent = contextToken.parent; @@ -2126,6 +2147,8 @@ namespace ts.Completions { return getFilteredKeywordCompletions(isClassMemberCompletionKeywordText); case KeywordCompletionFilters.ConstructorParameterKeywords: return getFilteredKeywordCompletions(isConstructorParameterCompletionKeywordText); + case KeywordCompletionFilters.FunctionLikeBodyKeywords: + return getFilteredKeywordCompletions(isFunctionLikeBodyCompletionKeywordText); default: Debug.assertNever(keywordFilter); } @@ -2188,6 +2211,26 @@ namespace ts.Completions { return isConstructorParameterCompletionKeyword(stringToToken(text)); } + function isFunctionLikeBodyCompletionKeyword(kind: SyntaxKind) { + switch (kind) { + case SyntaxKind.PublicKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.ProtectedKeyword: + case SyntaxKind.ReadonlyKeyword: + case SyntaxKind.ConstructorKeyword: + case SyntaxKind.StaticKeyword: + case SyntaxKind.AbstractKeyword: + case SyntaxKind.GetKeyword: + case SyntaxKind.SetKeyword: + return false; + } + return true; + } + + function isFunctionLikeBodyCompletionKeywordText(text: string) { + return isFunctionLikeBodyCompletionKeyword(stringToToken(text)); + } + function isEqualityOperatorKind(kind: ts.SyntaxKind): kind is EqualityOperator { switch (kind) { case ts.SyntaxKind.EqualsEqualsEqualsToken: diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 16270eda479..f6a9dee4cfe 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -321,6 +321,9 @@ namespace ts.formatting { rule("NoSpaceAfterCloseBracket", SyntaxKind.CloseBracketToken, anyToken, [isNonJsxSameLineTokenContext, isNotBeforeBlockInFunctionDeclarationContext], RuleAction.Delete), rule("SpaceAfterSemicolon", SyntaxKind.SemicolonToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Space), + // Remove extra space between for and await + rule("SpaceBetweenForAndAwaitKeyword", SyntaxKind.ForKeyword, SyntaxKind.AwaitKeyword, [isNonJsxSameLineTokenContext], RuleAction.Space), + // Add a space between statements. All keywords except (do,else,case) has open/close parens after them. // So, we have a rule to add a space for [),Any], [do,Any], [else,Any], and [case,Any] rule( diff --git a/tests/cases/fourslash/completionInFunctionLikeBody.ts b/tests/cases/fourslash/completionInFunctionLikeBody.ts new file mode 100644 index 00000000000..a4c5e92dde9 --- /dev/null +++ b/tests/cases/fourslash/completionInFunctionLikeBody.ts @@ -0,0 +1,43 @@ +/// + +//// class Foo { +//// bar () { +//// /*1*/ +//// class Foo1 { +//// bar1 () { +//// /*2*/ +//// } +//// /*3*/ +//// } +//// } +//// /*4*/ +//// } + + +goTo.marker("1"); +verify.not.completionListContains("public", "public", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("private", "private", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("protected", "protected", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("constructor", "constructor", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("readonly", "readonly", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("static", "static", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("abstract", "abstract", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("get", "get", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("set", "set", /*documentation*/ undefined, "keyword"); + +goTo.marker("2"); +verify.not.completionListContains("public", "public", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("private", "private", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("protected", "protected", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("constructor", "constructor", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("readonly", "readonly", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("static", "static", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("abstract", "abstract", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("get", "get", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("set", "set", /*documentation*/ undefined, "keyword"); + +goTo.marker("3"); +verify.completionListContainsClassElementKeywords(); + +goTo.marker("4"); +verify.completionListContainsClassElementKeywords(); diff --git a/tests/cases/fourslash/completionInJSDocFunctionNew.ts b/tests/cases/fourslash/completionInJSDocFunctionNew.ts index 6a06ec76fe5..1266eb5fad3 100644 --- a/tests/cases/fourslash/completionInJSDocFunctionNew.ts +++ b/tests/cases/fourslash/completionInJSDocFunctionNew.ts @@ -6,5 +6,5 @@ ////var f = function () { return new/**/; } goTo.marker(); -verify.completionListCount(116); +verify.completionListCount(107); verify.completionListContains('new'); diff --git a/tests/cases/fourslash/completionInJSDocFunctionThis.ts b/tests/cases/fourslash/completionInJSDocFunctionThis.ts index 0fd771f272b..57cf08c9ff3 100644 --- a/tests/cases/fourslash/completionInJSDocFunctionThis.ts +++ b/tests/cases/fourslash/completionInJSDocFunctionThis.ts @@ -5,5 +5,5 @@ ////var f = function (s) { return this/**/; } goTo.marker(); -verify.completionListCount(117); +verify.completionListCount(108); verify.completionListContains('this') diff --git a/tests/cases/fourslash/completionsUnion.ts b/tests/cases/fourslash/completionsUnion.ts index ed449936680..d9d1d3c792f 100644 --- a/tests/cases/fourslash/completionsUnion.ts +++ b/tests/cases/fourslash/completionsUnion.ts @@ -2,7 +2,9 @@ ////interface I { x: number; } ////interface Many extends ReadonlyArray { extra: number; } -////const x: I | I[] | Many = { /**/ }; +////class C { private priv: number; } +////const x: I | I[] | Many | C = { /**/ }; // We specifically filter out any array-like types. +// Private members will be excluded by `createUnionOrIntersectionProperty`. verify.completionsAt("", ["x"]); diff --git a/tests/cases/fourslash/formattingAwait.ts b/tests/cases/fourslash/formattingAwait.ts new file mode 100644 index 00000000000..e6a86f92d08 --- /dev/null +++ b/tests/cases/fourslash/formattingAwait.ts @@ -0,0 +1,18 @@ +/// + +////async function f() { +//// for await (const x of g()) { +//// console.log(x); +//// } +////} + + +format.document(); + +verify.currentFileContentIs( +`async function f() { + for await (const x of g()) { + console.log(x); + } +}` +); \ No newline at end of file