mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' of https://github.com/Microsoft/TypeScript into bug/29880
This commit is contained in:
+23
-17
@@ -2515,7 +2515,7 @@ namespace ts {
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug.fail(Debug.showSyntaxKind(thisContainer));
|
||||
Debug.failBadSyntaxKind(thisContainer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2581,7 +2581,7 @@ namespace ts {
|
||||
// Fix up parent pointers since we're going to use these nodes before we bind into them
|
||||
node.left.parent = node;
|
||||
node.right.parent = node;
|
||||
if (isIdentifier(lhs.expression) && container === file && isNameOfExportsOrModuleExportsAliasDeclaration(file, lhs.expression)) {
|
||||
if (isIdentifier(lhs.expression) && container === file && isExportsOrModuleExportsOrAlias(file, lhs.expression)) {
|
||||
// This can be an alias for the 'exports' or 'module.exports' names, e.g.
|
||||
// var util = module.exports;
|
||||
// util.property = function ...
|
||||
@@ -2975,21 +2975,27 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function isExportsOrModuleExportsOrAlias(sourceFile: SourceFile, node: Expression): boolean {
|
||||
return isExportsIdentifier(node) ||
|
||||
isModuleExportsPropertyAccessExpression(node) ||
|
||||
isIdentifier(node) && isNameOfExportsOrModuleExportsAliasDeclaration(sourceFile, node);
|
||||
}
|
||||
|
||||
function isNameOfExportsOrModuleExportsAliasDeclaration(sourceFile: SourceFile, node: Identifier): boolean {
|
||||
const symbol = lookupSymbolForNameWorker(sourceFile, node.escapedText);
|
||||
return !!symbol && !!symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) &&
|
||||
!!symbol.valueDeclaration.initializer && isExportsOrModuleExportsOrAliasOrAssignment(sourceFile, symbol.valueDeclaration.initializer);
|
||||
}
|
||||
|
||||
function isExportsOrModuleExportsOrAliasOrAssignment(sourceFile: SourceFile, node: Expression): boolean {
|
||||
return isExportsOrModuleExportsOrAlias(sourceFile, node) ||
|
||||
(isAssignmentExpression(node, /*excludeCompoundAssignment*/ true) && (
|
||||
isExportsOrModuleExportsOrAliasOrAssignment(sourceFile, node.left) || isExportsOrModuleExportsOrAliasOrAssignment(sourceFile, node.right)));
|
||||
let i = 0;
|
||||
const q = [node];
|
||||
while (q.length && i < 100) {
|
||||
i++;
|
||||
node = q.shift()!;
|
||||
if (isExportsIdentifier(node) || isModuleExportsPropertyAccessExpression(node)) {
|
||||
return true;
|
||||
}
|
||||
else if (isIdentifier(node)) {
|
||||
const symbol = lookupSymbolForNameWorker(sourceFile, node.escapedText);
|
||||
if (!!symbol && !!symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) && !!symbol.valueDeclaration.initializer) {
|
||||
const init = symbol.valueDeclaration.initializer;
|
||||
q.push(init);
|
||||
if (isAssignmentExpression(init, /*excludeCompoundAssignment*/ true)) {
|
||||
q.push(init.left);
|
||||
q.push(init.right);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function lookupSymbolForNameWorker(container: Node, name: __String): Symbol | undefined {
|
||||
|
||||
+376
-234
File diff suppressed because it is too large
Load Diff
@@ -2361,7 +2361,7 @@ namespace ts {
|
||||
if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, Extension.Json)) {
|
||||
extendedConfigPath = `${extendedConfigPath}.json`;
|
||||
if (!host.fileExists(extendedConfigPath)) {
|
||||
errors.push(createDiagnostic(Diagnostics.File_0_does_not_exist, extendedConfig));
|
||||
errors.push(createDiagnostic(Diagnostics.File_0_not_found, extendedConfig));
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -2372,7 +2372,7 @@ namespace ts {
|
||||
if (resolved.resolvedModule) {
|
||||
return resolved.resolvedModule.resolvedFileName;
|
||||
}
|
||||
errors.push(createDiagnostic(Diagnostics.File_0_does_not_exist, extendedConfig));
|
||||
errors.push(createDiagnostic(Diagnostics.File_0_not_found, extendedConfig));
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
+4
-87
@@ -239,7 +239,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
// When the deleted entry was the last one, we need to
|
||||
// adust the lastEntry reference.
|
||||
// adjust the lastEntry reference.
|
||||
if (this.lastEntry === entry) {
|
||||
this.lastEntry = previousEntry;
|
||||
}
|
||||
@@ -1255,7 +1255,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the only element of an array if it contains only one element; otheriwse, returns the
|
||||
* Returns the only element of an array if it contains only one element; otherwise, returns the
|
||||
* array.
|
||||
*/
|
||||
export function singleOrMany<T>(array: T[]): T | T[];
|
||||
@@ -1400,8 +1400,8 @@ namespace ts {
|
||||
/** Shims `Array.from`. */
|
||||
export function arrayFrom<T, U>(iterator: Iterator<T> | IterableIterator<T>, map: (t: T) => U): U[];
|
||||
export function arrayFrom<T>(iterator: Iterator<T> | IterableIterator<T>): T[];
|
||||
export function arrayFrom(iterator: Iterator<any> | IterableIterator<any>, map?: (t: any) => any): any[] {
|
||||
const result: any[] = [];
|
||||
export function arrayFrom<T, U>(iterator: Iterator<T> | IterableIterator<T>, map?: (t: T) => U): (T | U)[] {
|
||||
const result: (T | U)[] = [];
|
||||
for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) {
|
||||
result.push(map ? map(value) : value);
|
||||
}
|
||||
@@ -1686,89 +1686,6 @@ namespace ts {
|
||||
export type AnyFunction = (...args: never[]) => void;
|
||||
export type AnyConstructor = new (...args: unknown[]) => unknown;
|
||||
|
||||
export namespace Debug {
|
||||
export let currentAssertionLevel = AssertionLevel.None;
|
||||
export let isDebugging = false;
|
||||
|
||||
export function shouldAssert(level: AssertionLevel): boolean {
|
||||
return currentAssertionLevel >= level;
|
||||
}
|
||||
|
||||
export function assert(expression: boolean, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: AnyFunction): void {
|
||||
if (!expression) {
|
||||
if (verboseDebugInfo) {
|
||||
message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo());
|
||||
}
|
||||
fail(message ? "False expression: " + message : "False expression.", stackCrawlMark || assert);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertEqual<T>(a: T, b: T, msg?: string, msg2?: string): void {
|
||||
if (a !== b) {
|
||||
const message = msg ? msg2 ? `${msg} ${msg2}` : msg : "";
|
||||
fail(`Expected ${a} === ${b}. ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertLessThan(a: number, b: number, msg?: string): void {
|
||||
if (a >= b) {
|
||||
fail(`Expected ${a} < ${b}. ${msg || ""}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertLessThanOrEqual(a: number, b: number): void {
|
||||
if (a > b) {
|
||||
fail(`Expected ${a} <= ${b}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertGreaterThanOrEqual(a: number, b: number): void {
|
||||
if (a < b) {
|
||||
fail(`Expected ${a} >= ${b}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function fail(message?: string, stackCrawlMark?: AnyFunction): never {
|
||||
debugger;
|
||||
const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure.");
|
||||
if ((<any>Error).captureStackTrace) {
|
||||
(<any>Error).captureStackTrace(e, stackCrawlMark || fail);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
export function assertDefined<T>(value: T | null | undefined, message?: string): T {
|
||||
if (value === undefined || value === null) return fail(message);
|
||||
return value;
|
||||
}
|
||||
|
||||
export function assertEachDefined<T, A extends ReadonlyArray<T>>(value: A, message?: string): A {
|
||||
for (const v of value) {
|
||||
assertDefined(v, message);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never {
|
||||
const detail = typeof member === "object" && "kind" in member && "pos" in member ? "SyntaxKind: " + showSyntaxKind(member as Node) : JSON.stringify(member);
|
||||
return fail(`${message} ${detail}`, stackCrawlMark || assertNever);
|
||||
}
|
||||
|
||||
export function getFunctionName(func: AnyFunction) {
|
||||
if (typeof func !== "function") {
|
||||
return "";
|
||||
}
|
||||
else if (func.hasOwnProperty("name")) {
|
||||
return (<any>func).name;
|
||||
}
|
||||
else {
|
||||
const text = Function.prototype.toString.call(func);
|
||||
const match = /^function\s+([\w\$]+)\s*\(/.exec(text);
|
||||
return match ? match[1] : "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function equateValues<T>(a: T, b: T) {
|
||||
return a === b;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
export namespace Debug {
|
||||
export let currentAssertionLevel = AssertionLevel.None;
|
||||
export let isDebugging = false;
|
||||
|
||||
export function shouldAssert(level: AssertionLevel): boolean {
|
||||
return currentAssertionLevel >= level;
|
||||
}
|
||||
|
||||
export function assert(expression: boolean, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: AnyFunction): void {
|
||||
if (!expression) {
|
||||
if (verboseDebugInfo) {
|
||||
message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo());
|
||||
}
|
||||
fail(message ? "False expression: " + message : "False expression.", stackCrawlMark || assert);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertEqual<T>(a: T, b: T, msg?: string, msg2?: string): void {
|
||||
if (a !== b) {
|
||||
const message = msg ? msg2 ? `${msg} ${msg2}` : msg : "";
|
||||
fail(`Expected ${a} === ${b}. ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertLessThan(a: number, b: number, msg?: string): void {
|
||||
if (a >= b) {
|
||||
fail(`Expected ${a} < ${b}. ${msg || ""}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertLessThanOrEqual(a: number, b: number): void {
|
||||
if (a > b) {
|
||||
fail(`Expected ${a} <= ${b}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertGreaterThanOrEqual(a: number, b: number): void {
|
||||
if (a < b) {
|
||||
fail(`Expected ${a} >= ${b}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function fail(message?: string, stackCrawlMark?: AnyFunction): never {
|
||||
debugger;
|
||||
const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure.");
|
||||
if ((<any>Error).captureStackTrace) {
|
||||
(<any>Error).captureStackTrace(e, stackCrawlMark || fail);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
export function assertDefined<T>(value: T | null | undefined, message?: string): T {
|
||||
if (value === undefined || value === null) return fail(message);
|
||||
return value;
|
||||
}
|
||||
|
||||
export function assertEachDefined<T, A extends ReadonlyArray<T>>(value: A, message?: string): A {
|
||||
for (const v of value) {
|
||||
assertDefined(v, message);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never {
|
||||
const detail = typeof member === "object" && "kind" in member && "pos" in member && formatSyntaxKind ? "SyntaxKind: " + formatSyntaxKind((member as Node).kind) : JSON.stringify(member);
|
||||
return fail(`${message} ${detail}`, stackCrawlMark || assertNever);
|
||||
}
|
||||
|
||||
export function getFunctionName(func: AnyFunction) {
|
||||
if (typeof func !== "function") {
|
||||
return "";
|
||||
}
|
||||
else if (func.hasOwnProperty("name")) {
|
||||
return (<any>func).name;
|
||||
}
|
||||
else {
|
||||
const text = Function.prototype.toString.call(func);
|
||||
const match = /^function\s+([\w\$]+)\s*\(/.exec(text);
|
||||
return match ? match[1] : "";
|
||||
}
|
||||
}
|
||||
|
||||
export function formatSymbol(symbol: Symbol): string {
|
||||
return `{ name: ${unescapeLeadingUnderscores(symbol.escapedName)}; flags: ${formatSymbolFlags(symbol.flags)}; declarations: ${map(symbol.declarations, node => formatSyntaxKind(node.kind))} }`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an enum value as a string for debugging and debug assertions.
|
||||
*/
|
||||
export function formatEnum(value = 0, enumObject: any, isFlags?: boolean) {
|
||||
const members = getEnumMembers(enumObject);
|
||||
if (value === 0) {
|
||||
return members.length > 0 && members[0][0] === 0 ? members[0][1] : "0";
|
||||
}
|
||||
if (isFlags) {
|
||||
let result = "";
|
||||
let remainingFlags = value;
|
||||
for (let i = members.length - 1; i >= 0 && remainingFlags !== 0; i--) {
|
||||
const [enumValue, enumName] = members[i];
|
||||
if (enumValue !== 0 && (remainingFlags & enumValue) === enumValue) {
|
||||
remainingFlags &= ~enumValue;
|
||||
result = `${enumName}${result ? "|" : ""}${result}`;
|
||||
}
|
||||
}
|
||||
if (remainingFlags === 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const [enumValue, enumName] of members) {
|
||||
if (enumValue === value) {
|
||||
return enumName;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
function getEnumMembers(enumObject: any) {
|
||||
const result: [number, string][] = [];
|
||||
for (const name in enumObject) {
|
||||
const value = enumObject[name];
|
||||
if (typeof value === "number") {
|
||||
result.push([value, name]);
|
||||
}
|
||||
}
|
||||
|
||||
return stableSort<[number, string]>(result, (x, y) => compareValues(x[0], y[0]));
|
||||
}
|
||||
|
||||
export function formatSyntaxKind(kind: SyntaxKind | undefined): string {
|
||||
return formatEnum(kind, (<any>ts).SyntaxKind, /*isFlags*/ false);
|
||||
}
|
||||
|
||||
export function formatNodeFlags(flags: NodeFlags | undefined): string {
|
||||
return formatEnum(flags, (<any>ts).NodeFlags, /*isFlags*/ true);
|
||||
}
|
||||
|
||||
export function formatModifierFlags(flags: ModifierFlags | undefined): string {
|
||||
return formatEnum(flags, (<any>ts).ModifierFlags, /*isFlags*/ true);
|
||||
}
|
||||
|
||||
export function formatTransformFlags(flags: TransformFlags | undefined): string {
|
||||
return formatEnum(flags, (<any>ts).TransformFlags, /*isFlags*/ true);
|
||||
}
|
||||
|
||||
export function formatEmitFlags(flags: EmitFlags | undefined): string {
|
||||
return formatEnum(flags, (<any>ts).EmitFlags, /*isFlags*/ true);
|
||||
}
|
||||
|
||||
export function formatSymbolFlags(flags: SymbolFlags | undefined): string {
|
||||
return formatEnum(flags, (<any>ts).SymbolFlags, /*isFlags*/ true);
|
||||
}
|
||||
|
||||
export function formatTypeFlags(flags: TypeFlags | undefined): string {
|
||||
return formatEnum(flags, (<any>ts).TypeFlags, /*isFlags*/ true);
|
||||
}
|
||||
|
||||
export function formatObjectFlags(flags: ObjectFlags | undefined): string {
|
||||
return formatEnum(flags, (<any>ts).ObjectFlags, /*isFlags*/ true);
|
||||
}
|
||||
|
||||
export function failBadSyntaxKind(node: Node, message?: string): never {
|
||||
return fail(
|
||||
`${message || "Unexpected node."}\r\nNode ${formatSyntaxKind(node.kind)} was unexpected.`,
|
||||
failBadSyntaxKind);
|
||||
}
|
||||
|
||||
export const assertEachNode = shouldAssert(AssertionLevel.Normal)
|
||||
? (nodes: Node[], test: (node: Node) => boolean, message?: string): void => assert(
|
||||
test === undefined || every(nodes, test),
|
||||
message || "Unexpected node.",
|
||||
() => `Node array did not pass test '${getFunctionName(test)}'.`,
|
||||
assertEachNode)
|
||||
: noop;
|
||||
|
||||
export const assertNode = shouldAssert(AssertionLevel.Normal)
|
||||
? (node: Node | undefined, test: ((node: Node | undefined) => boolean) | undefined, message?: string): void => assert(
|
||||
test === undefined || test(node),
|
||||
message || "Unexpected node.",
|
||||
() => `Node ${formatSyntaxKind(node!.kind)} did not pass test '${getFunctionName(test!)}'.`,
|
||||
assertNode)
|
||||
: noop;
|
||||
|
||||
export const assertOptionalNode = shouldAssert(AssertionLevel.Normal)
|
||||
? (node: Node, test: (node: Node) => boolean, message?: string): void => assert(
|
||||
test === undefined || node === undefined || test(node),
|
||||
message || "Unexpected node.",
|
||||
() => `Node ${formatSyntaxKind(node.kind)} did not pass test '${getFunctionName(test)}'.`,
|
||||
assertOptionalNode)
|
||||
: noop;
|
||||
|
||||
export const assertOptionalToken = shouldAssert(AssertionLevel.Normal)
|
||||
? (node: Node, kind: SyntaxKind, message?: string): void => assert(
|
||||
kind === undefined || node === undefined || node.kind === kind,
|
||||
message || "Unexpected node.",
|
||||
() => `Node ${formatSyntaxKind(node.kind)} was not a '${formatSyntaxKind(kind)}' token.`,
|
||||
assertOptionalToken)
|
||||
: noop;
|
||||
|
||||
export const assertMissingNode = shouldAssert(AssertionLevel.Normal)
|
||||
? (node: Node, message?: string): void => assert(
|
||||
node === undefined,
|
||||
message || "Unexpected node.",
|
||||
() => `Node ${formatSyntaxKind(node.kind)} was unexpected'.`,
|
||||
assertMissingNode)
|
||||
: noop;
|
||||
|
||||
let isDebugInfoEnabled = false;
|
||||
|
||||
/**
|
||||
* Injects debug information into frequently used types.
|
||||
*/
|
||||
export function enableDebugInfo() {
|
||||
if (isDebugInfoEnabled) return;
|
||||
|
||||
// Add additional properties in debug mode to assist with debugging.
|
||||
Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, {
|
||||
__debugFlags: { get(this: Symbol) { return formatSymbolFlags(this.flags); } }
|
||||
});
|
||||
|
||||
Object.defineProperties(objectAllocator.getTypeConstructor().prototype, {
|
||||
__debugFlags: { get(this: Type) { return formatTypeFlags(this.flags); } },
|
||||
__debugObjectFlags: { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((<ObjectType>this).objectFlags) : ""; } },
|
||||
__debugTypeToString: { value(this: Type) { return this.checker.typeToString(this); } },
|
||||
});
|
||||
|
||||
const nodeConstructors = [
|
||||
objectAllocator.getNodeConstructor(),
|
||||
objectAllocator.getIdentifierConstructor(),
|
||||
objectAllocator.getTokenConstructor(),
|
||||
objectAllocator.getSourceFileConstructor()
|
||||
];
|
||||
|
||||
for (const ctor of nodeConstructors) {
|
||||
if (!ctor.prototype.hasOwnProperty("__debugKind")) {
|
||||
Object.defineProperties(ctor.prototype, {
|
||||
__debugKind: { get(this: Node) { return formatSyntaxKind(this.kind); } },
|
||||
__debugNodeFlags: { get(this: Node) { return formatNodeFlags(this.flags); } },
|
||||
__debugModifierFlags: { get(this: Node) { return formatModifierFlags(getModifierFlagsNoCache(this)); } },
|
||||
__debugTransformFlags: { get(this: Node) { return formatTransformFlags(this.transformFlags); } },
|
||||
__debugIsParseTreeNode: { get(this: Node) { return isParseTreeNode(this); } },
|
||||
__debugEmitFlags: { get(this: Node) { return formatEmitFlags(getEmitFlags(this)); } },
|
||||
__debugGetText: {
|
||||
value(this: Node, includeTrivia?: boolean) {
|
||||
if (nodeIsSynthesized(this)) return "";
|
||||
const parseNode = getParseTreeNode(this);
|
||||
const sourceFile = parseNode && getSourceFileOfNode(parseNode);
|
||||
return sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : "";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isDebugInfoEnabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -655,7 +655,7 @@
|
||||
"category": "Error",
|
||||
"code": 1207
|
||||
},
|
||||
"Cannot compile namespaces when the '--isolatedModules' flag is provided.": {
|
||||
"All files must be modules when the '--isolatedModules' flag is provided.": {
|
||||
"category": "Error",
|
||||
"code": 1208
|
||||
},
|
||||
@@ -3811,10 +3811,6 @@
|
||||
"category": "Error",
|
||||
"code": 6189
|
||||
},
|
||||
"Found 'package.json' at '{0}'. Package ID is '{1}'.": {
|
||||
"category": "Message",
|
||||
"code": 6190
|
||||
},
|
||||
"Whether to keep outdated console output in watch mode instead of clearing the screen.": {
|
||||
"category": "Message",
|
||||
"code": 6191
|
||||
@@ -3923,6 +3919,18 @@
|
||||
"category": "Message",
|
||||
"code": 6217
|
||||
},
|
||||
"======== Module name '{0}' was successfully resolved to '{1}' with Package ID '{2}'. ========": {
|
||||
"category": "Message",
|
||||
"code": 6218
|
||||
},
|
||||
"======== Type reference directive '{0}' was successfully resolved to '{1}' with Package ID '{2}', primary: {3}. ========": {
|
||||
"category": "Message",
|
||||
"code": 6219
|
||||
},
|
||||
"'package.json' had a falsy '{0}' field.": {
|
||||
"category": "Message",
|
||||
"code": 6220
|
||||
},
|
||||
|
||||
"Projects to reference": {
|
||||
"category": "Message",
|
||||
@@ -4284,6 +4292,14 @@
|
||||
"category": "Error",
|
||||
"code": 7052
|
||||
},
|
||||
"Element implicitly has an 'any' type because expression of type '{0}' can't be used to index type '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 7053
|
||||
},
|
||||
"No index signature with a parameter of type '{0}' was found on type '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 7054
|
||||
},
|
||||
"You cannot rename this element.": {
|
||||
"category": "Error",
|
||||
"code": 8000
|
||||
@@ -4962,7 +4978,20 @@
|
||||
"category": "Message",
|
||||
"code": 95076
|
||||
},
|
||||
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer." :{
|
||||
"Extract type": {
|
||||
"category": "Message",
|
||||
"code": 95077
|
||||
},
|
||||
"Extract to type alias": {
|
||||
"category": "Message",
|
||||
"code": 95078
|
||||
},
|
||||
"Extract to typedef": {
|
||||
"category": "Message",
|
||||
"code": 95079
|
||||
},
|
||||
|
||||
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
|
||||
"category": "Error",
|
||||
"code": 18004
|
||||
},
|
||||
|
||||
@@ -222,7 +222,7 @@ namespace ts {
|
||||
|
||||
/*@internal*/
|
||||
// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
|
||||
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile | undefined, emitOnlyDtsFiles?: boolean, transformers?: TransformerFactory<Bundle | SourceFile>[], declarationTransformers?: TransformerFactory<Bundle | SourceFile>[], onlyBuildInfo?: boolean): EmitResult {
|
||||
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile | undefined, { scriptTransformers, declarationTransformers }: EmitTransformers, emitOnlyDtsFiles?: boolean, onlyBuildInfo?: boolean): EmitResult {
|
||||
const compilerOptions = host.getCompilerOptions();
|
||||
const sourceMapDataList: SourceMapEmitResult[] | undefined = (compilerOptions.sourceMap || compilerOptions.inlineSourceMap || getAreDeclarationMapsEnabled(compilerOptions)) ? [] : undefined;
|
||||
const emittedFilesList: string[] | undefined = compilerOptions.listEmittedFiles ? [] : undefined;
|
||||
@@ -303,7 +303,7 @@ namespace ts {
|
||||
return;
|
||||
}
|
||||
// Transform the source files
|
||||
const transform = transformNodes(resolver, host, compilerOptions, [sourceFileOrBundle], transformers!, /*allowDtsFiles*/ false);
|
||||
const transform = transformNodes(resolver, host, compilerOptions, [sourceFileOrBundle], scriptTransformers, /*allowDtsFiles*/ false);
|
||||
|
||||
const printerOptions: PrinterOptions = {
|
||||
removeComments: compilerOptions.removeComments,
|
||||
@@ -349,7 +349,7 @@ namespace ts {
|
||||
// Do that here when emitting only dts files
|
||||
nonJsFiles.forEach(collectLinkedAliases);
|
||||
}
|
||||
const declarationTransform = transformNodes(resolver, host, compilerOptions, inputListOrBundle, concatenate([transformDeclarations], declarationTransformers), /*allowDtsFiles*/ false);
|
||||
const declarationTransform = transformNodes(resolver, host, compilerOptions, inputListOrBundle, declarationTransformers, /*allowDtsFiles*/ false);
|
||||
if (length(declarationTransform.diagnostics)) {
|
||||
for (const diagnostic of declarationTransform.diagnostics!) {
|
||||
emitterDiagnostics.add(diagnostic);
|
||||
@@ -723,7 +723,7 @@ namespace ts {
|
||||
useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
|
||||
getProgramBuildInfo: returnUndefined
|
||||
};
|
||||
emitFiles(notImplementedResolver, emitHost, /*targetSourceFile*/ undefined, /*emitOnlyDtsFiles*/ false, getTransformers(config.options));
|
||||
emitFiles(notImplementedResolver, emitHost, /*targetSourceFile*/ undefined, getTransformers(config.options), /*emitOnlyDtsFiles*/ false);
|
||||
return outputFiles;
|
||||
}
|
||||
|
||||
@@ -4527,6 +4527,8 @@ namespace ts {
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
return generateNameForMethodOrAccessor(<MethodDeclaration | AccessorDeclaration>node);
|
||||
case SyntaxKind.ComputedPropertyName:
|
||||
return makeTempVariableName(TempFlags.Auto, /*reserveInNestedScopes*/ true);
|
||||
default:
|
||||
return makeTempVariableName(TempFlags.Auto);
|
||||
}
|
||||
|
||||
@@ -1004,10 +1004,10 @@ namespace ts {
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createPropertyAccess(expression: Expression, name: string | Identifier | undefined) {
|
||||
export function createPropertyAccess(expression: Expression, name: string | Identifier) {
|
||||
const node = <PropertyAccessExpression>createSynthesizedNode(SyntaxKind.PropertyAccessExpression);
|
||||
node.expression = parenthesizeForAccess(expression);
|
||||
node.name = asName(name)!; // TODO: GH#18217
|
||||
node.name = asName(name);
|
||||
setEmitFlags(node, EmitFlags.NoIndentation);
|
||||
return node;
|
||||
}
|
||||
@@ -2468,7 +2468,7 @@ namespace ts {
|
||||
|
||||
export function createSpreadAssignment(expression: Expression) {
|
||||
const node = <SpreadAssignment>createSynthesizedNode(SyntaxKind.SpreadAssignment);
|
||||
node.expression = expression !== undefined ? parenthesizeExpressionForList(expression) : undefined!; // TODO: GH#18217
|
||||
node.expression = parenthesizeExpressionForList(expression);
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -2949,7 +2949,7 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateBundle(node: Bundle, sourceFiles: ReadonlyArray<SourceFile>, prepends: ReadonlyArray<UnparsedSource> = emptyArray) {
|
||||
export function updateBundle(node: Bundle, sourceFiles: ReadonlyArray<SourceFile>, prepends: ReadonlyArray<UnparsedSource | InputFiles> = emptyArray) {
|
||||
if (node.sourceFiles !== sourceFiles || node.prepends !== prepends) {
|
||||
return createBundle(sourceFiles, prepends);
|
||||
}
|
||||
|
||||
@@ -16,12 +16,23 @@ namespace ts {
|
||||
push(value: T): void;
|
||||
}
|
||||
|
||||
function withPackageId(packageId: PackageId | undefined, r: PathAndExtension | undefined): Resolved | undefined {
|
||||
function withPackageId(packageInfo: PackageJsonInfo | undefined, r: PathAndExtension | undefined): Resolved | undefined {
|
||||
let packageId: PackageId | undefined;
|
||||
if (r && packageInfo) {
|
||||
const packageJsonContent = packageInfo.packageJsonContent as PackageJson;
|
||||
if (typeof packageJsonContent.name === "string" && typeof packageJsonContent.version === "string") {
|
||||
packageId = {
|
||||
name: packageJsonContent.name,
|
||||
subModuleName: r.path.slice(packageInfo.packageDirectory.length + directorySeparator.length),
|
||||
version: packageJsonContent.version
|
||||
};
|
||||
}
|
||||
}
|
||||
return r && { path: r.path, extension: r.ext, packageId };
|
||||
}
|
||||
|
||||
function noPackageId(r: PathAndExtension | undefined): Resolved | undefined {
|
||||
return withPackageId(/*packageId*/ undefined, r);
|
||||
return withPackageId(/*packageInfo*/ undefined, r);
|
||||
}
|
||||
|
||||
function removeIgnoredPackageId(r: Resolved | undefined): PathAndExtension | undefined {
|
||||
@@ -130,7 +141,15 @@ namespace ts {
|
||||
|
||||
function readPackageJsonPathField<K extends "typings" | "types" | "main" | "tsconfig">(jsonContent: PackageJson, fieldName: K, baseDirectory: string, state: ModuleResolutionState): PackageJson[K] | undefined {
|
||||
const fileName = readPackageJsonField(jsonContent, fieldName, "string", state);
|
||||
if (fileName === undefined) return;
|
||||
if (fileName === undefined) {
|
||||
return;
|
||||
}
|
||||
if (!fileName) {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.package_json_had_a_falsy_0_field, fieldName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const path = normalizePath(combinePaths(baseDirectory, fileName));
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, fieldName, fileName, path);
|
||||
@@ -307,7 +326,12 @@ namespace ts {
|
||||
const { fileName, packageId } = resolved;
|
||||
const resolvedFileName = options.preserveSymlinks ? fileName : realPath(fileName, host, traceEnabled);
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFileName, primary);
|
||||
if (packageId) {
|
||||
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_with_Package_ID_2_primary_Colon_3, typeReferenceDirectiveName, resolvedFileName, packageIdToString(packageId), primary);
|
||||
}
|
||||
else {
|
||||
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFileName, primary);
|
||||
}
|
||||
}
|
||||
resolvedTypeReferenceDirective = { primary, resolvedFileName, packageId, isExternalLibraryImport: pathContainsNodeModules(fileName) };
|
||||
}
|
||||
@@ -663,7 +687,12 @@ namespace ts {
|
||||
|
||||
if (traceEnabled) {
|
||||
if (result.resolvedModule) {
|
||||
trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1, moduleName, result.resolvedModule.resolvedFileName);
|
||||
if (result.resolvedModule.packageId) {
|
||||
trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1_with_Package_ID_2, moduleName, result.resolvedModule.resolvedFileName, packageIdToString(result.resolvedModule.packageId));
|
||||
}
|
||||
else {
|
||||
trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1, moduleName, result.resolvedModule.resolvedFileName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
trace(host, Diagnostics.Module_name_0_was_not_resolved, moduleName);
|
||||
@@ -968,10 +997,9 @@ namespace ts {
|
||||
}
|
||||
const resolvedFromFile = loadModuleFromFile(extensions, candidate, onlyRecordFailures, state);
|
||||
if (resolvedFromFile) {
|
||||
const nm = considerPackageJson ? parseNodeModuleFromPath(resolvedFromFile) : undefined;
|
||||
const packageInfo = nm && getPackageJsonInfo(nm.packageDirectory, nm.subModuleName, /*onlyRecordFailures*/ false, state);
|
||||
const packageId = packageInfo && packageInfo.packageId;
|
||||
return withPackageId(packageId, resolvedFromFile);
|
||||
const packageDirectory = considerPackageJson ? parseNodeModuleFromPath(resolvedFromFile) : undefined;
|
||||
const packageInfo = packageDirectory ? getPackageJsonInfo(packageDirectory, /*onlyRecordFailures*/ false, state) : undefined;
|
||||
return withPackageId(packageInfo, resolvedFromFile);
|
||||
}
|
||||
}
|
||||
if (!onlyRecordFailures) {
|
||||
@@ -998,13 +1026,12 @@ namespace ts {
|
||||
* (Not neeeded for `loadModuleFromNodeModules` as that looks up the `package.json` as part of resolution.)
|
||||
*
|
||||
* packageDirectory is the directory of the package itself.
|
||||
* subModuleName is the path within the package.
|
||||
* For `blah/node_modules/foo/index.d.ts` this is { packageDirectory: "foo", subModuleName: "index.d.ts" }. (Part before "/node_modules/" is ignored.)
|
||||
* For `/node_modules/foo/bar.d.ts` this is { packageDirectory: "foo", subModuleName": "bar/index.d.ts" }.
|
||||
* For `/node_modules/@types/foo/bar/index.d.ts` this is { packageDirectory: "@types/foo", subModuleName: "bar/index.d.ts" }.
|
||||
* For `/node_modules/foo/bar/index.d.ts` this is { packageDirectory: "foo", subModuleName": "bar/index.d.ts" }.
|
||||
* For `blah/node_modules/foo/index.d.ts` this is packageDirectory: "foo"
|
||||
* For `/node_modules/foo/bar.d.ts` this is packageDirectory: "foo"
|
||||
* For `/node_modules/@types/foo/bar/index.d.ts` this is packageDirectory: "@types/foo"
|
||||
* For `/node_modules/foo/bar/index.d.ts` this is packageDirectory: "foo"
|
||||
*/
|
||||
function parseNodeModuleFromPath(resolved: PathAndExtension): { packageDirectory: string, subModuleName: string } | undefined {
|
||||
function parseNodeModuleFromPath(resolved: PathAndExtension): string | undefined {
|
||||
const path = normalizePath(resolved.path);
|
||||
const idx = path.lastIndexOf(nodeModulesPathPart);
|
||||
if (idx === -1) {
|
||||
@@ -1016,9 +1043,7 @@ namespace ts {
|
||||
if (path.charCodeAt(indexAfterNodeModules) === CharacterCodes.at) {
|
||||
indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterPackageName);
|
||||
}
|
||||
const packageDirectory = path.slice(0, indexAfterPackageName);
|
||||
const subModuleName = removeExtension(path.slice(indexAfterPackageName + 1), resolved.ext) + Extension.Dts;
|
||||
return { packageDirectory, subModuleName };
|
||||
return path.slice(0, indexAfterPackageName);
|
||||
}
|
||||
|
||||
function moveToNextDirectorySeparatorIfAvailable(path: string, prevSeparatorIndex: number): number {
|
||||
@@ -1026,19 +1051,6 @@ namespace ts {
|
||||
return nextSeparatorIndex === -1 ? prevSeparatorIndex : nextSeparatorIndex;
|
||||
}
|
||||
|
||||
function addExtensionAndIndex(path: string): string {
|
||||
if (path === "") {
|
||||
return "index.d.ts";
|
||||
}
|
||||
if (endsWith(path, ".d.ts")) {
|
||||
return path;
|
||||
}
|
||||
if (path === "index" || endsWith(path, "/index")) {
|
||||
return path + ".d.ts";
|
||||
}
|
||||
return path + "/index.d.ts";
|
||||
}
|
||||
|
||||
function loadModuleFromFileNoPackageId(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined {
|
||||
return noPackageId(loadModuleFromFile(extensions, candidate, onlyRecordFailures, state));
|
||||
}
|
||||
@@ -1119,61 +1131,29 @@ namespace ts {
|
||||
}
|
||||
|
||||
function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true) {
|
||||
const packageInfo = considerPackageJson ? getPackageJsonInfo(candidate, "", onlyRecordFailures, state) : undefined;
|
||||
const packageId = packageInfo && packageInfo.packageId;
|
||||
const packageInfo = considerPackageJson ? getPackageJsonInfo(candidate, onlyRecordFailures, state) : undefined;
|
||||
const packageJsonContent = packageInfo && packageInfo.packageJsonContent;
|
||||
const versionPaths = packageInfo && packageInfo.versionPaths;
|
||||
return withPackageId(packageId, loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, state, packageJsonContent, versionPaths));
|
||||
return withPackageId(packageInfo, loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, state, packageJsonContent, versionPaths));
|
||||
}
|
||||
|
||||
interface PackageJsonInfo {
|
||||
packageJsonContent: PackageJsonPathFields | undefined;
|
||||
packageId: PackageId | undefined;
|
||||
packageDirectory: string;
|
||||
packageJsonContent: PackageJsonPathFields;
|
||||
versionPaths: VersionPaths | undefined;
|
||||
}
|
||||
|
||||
function getPackageJsonInfo(packageDirectory: string, subModuleName: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PackageJsonInfo | undefined {
|
||||
function getPackageJsonInfo(packageDirectory: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PackageJsonInfo | undefined {
|
||||
const { host, traceEnabled } = state;
|
||||
const directoryExists = !onlyRecordFailures && directoryProbablyExists(packageDirectory, host);
|
||||
const packageJsonPath = combinePaths(packageDirectory, "package.json");
|
||||
if (directoryExists && host.fileExists(packageJsonPath)) {
|
||||
const packageJsonContent = readJson(packageJsonPath, host) as PackageJson;
|
||||
if (subModuleName === "") { // looking up the root - need to handle types/typings/main redirects for subModuleName
|
||||
const path = readPackageJsonTypesFields(packageJsonContent, packageDirectory, state);
|
||||
if (typeof path === "string") {
|
||||
subModuleName = addExtensionAndIndex(path.substring(packageDirectory.length + 1));
|
||||
}
|
||||
else {
|
||||
const jsPath = readPackageJsonMainField(packageJsonContent, packageDirectory, state);
|
||||
if (typeof jsPath === "string" && jsPath.length > packageDirectory.length) {
|
||||
const potentialSubModule = jsPath.substring(packageDirectory.length + 1);
|
||||
subModuleName = (forEach(supportedJSExtensions, extension =>
|
||||
tryRemoveExtension(potentialSubModule, extension)) || potentialSubModule) + Extension.Dts;
|
||||
}
|
||||
else {
|
||||
subModuleName = "index.d.ts";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!endsWith(subModuleName, Extension.Dts)) {
|
||||
subModuleName = addExtensionAndIndex(subModuleName);
|
||||
}
|
||||
|
||||
const versionPaths = readPackageJsonTypesVersionPaths(packageJsonContent, state);
|
||||
const packageId: PackageId | undefined = typeof packageJsonContent.name === "string" && typeof packageJsonContent.version === "string"
|
||||
? { name: packageJsonContent.name, subModuleName, version: packageJsonContent.version }
|
||||
: undefined;
|
||||
if (traceEnabled) {
|
||||
if (packageId) {
|
||||
trace(host, Diagnostics.Found_package_json_at_0_Package_ID_is_1, packageJsonPath, packageIdToString(packageId));
|
||||
}
|
||||
else {
|
||||
trace(host, Diagnostics.Found_package_json_at_0, packageJsonPath);
|
||||
}
|
||||
trace(host, Diagnostics.Found_package_json_at_0, packageJsonPath);
|
||||
}
|
||||
|
||||
return { packageJsonContent, packageId, versionPaths };
|
||||
const versionPaths = readPackageJsonTypesVersionPaths(packageJsonContent, state);
|
||||
return { packageDirectory, packageJsonContent, versionPaths };
|
||||
}
|
||||
else {
|
||||
if (directoryExists && traceEnabled) {
|
||||
@@ -1328,27 +1308,36 @@ namespace ts {
|
||||
const candidate = normalizePath(combinePaths(nodeModulesDirectory, moduleName));
|
||||
|
||||
// First look for a nested package.json, as in `node_modules/foo/bar/package.json`.
|
||||
let packageJsonContent: PackageJsonPathFields | undefined;
|
||||
let packageId: PackageId | undefined;
|
||||
let versionPaths: VersionPaths | undefined;
|
||||
|
||||
const packageInfo = getPackageJsonInfo(candidate, "", !nodeModulesDirectoryExists, state);
|
||||
let packageInfo = getPackageJsonInfo(candidate, !nodeModulesDirectoryExists, state);
|
||||
if (packageInfo) {
|
||||
({ packageJsonContent, packageId, versionPaths } = packageInfo);
|
||||
const fromFile = loadModuleFromFile(extensions, candidate, !nodeModulesDirectoryExists, state);
|
||||
if (fromFile) {
|
||||
return noPackageId(fromFile);
|
||||
}
|
||||
|
||||
const fromDirectory = loadNodeModuleFromDirectoryWorker(extensions, candidate, !nodeModulesDirectoryExists, state, packageJsonContent, versionPaths);
|
||||
return withPackageId(packageId, fromDirectory);
|
||||
const fromDirectory = loadNodeModuleFromDirectoryWorker(
|
||||
extensions,
|
||||
candidate,
|
||||
!nodeModulesDirectoryExists,
|
||||
state,
|
||||
packageInfo.packageJsonContent,
|
||||
packageInfo.versionPaths
|
||||
);
|
||||
return withPackageId(packageInfo, fromDirectory);
|
||||
}
|
||||
|
||||
const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => {
|
||||
const pathAndExtension =
|
||||
loadModuleFromFile(extensions, candidate, onlyRecordFailures, state) ||
|
||||
loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, state, packageJsonContent, versionPaths);
|
||||
return withPackageId(packageId, pathAndExtension);
|
||||
loadNodeModuleFromDirectoryWorker(
|
||||
extensions,
|
||||
candidate,
|
||||
onlyRecordFailures,
|
||||
state,
|
||||
packageInfo && packageInfo.packageJsonContent,
|
||||
packageInfo && packageInfo.versionPaths
|
||||
);
|
||||
return withPackageId(packageInfo, pathAndExtension);
|
||||
};
|
||||
|
||||
const { packageName, rest } = parsePackageName(moduleName);
|
||||
@@ -1356,14 +1345,13 @@ namespace ts {
|
||||
const packageDirectory = combinePaths(nodeModulesDirectory, packageName);
|
||||
|
||||
// Don't use a "types" or "main" from here because we're not loading the root, but a subdirectory -- just here for the packageId and path mappings.
|
||||
const packageInfo = getPackageJsonInfo(packageDirectory, rest, !nodeModulesDirectoryExists, state);
|
||||
if (packageInfo) ({ packageId, versionPaths } = packageInfo);
|
||||
if (versionPaths) {
|
||||
packageInfo = getPackageJsonInfo(packageDirectory, !nodeModulesDirectoryExists, state);
|
||||
if (packageInfo && packageInfo.versionPaths) {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.package_json_has_a_typesVersions_entry_0_that_matches_compiler_version_1_looking_for_a_pattern_to_match_module_name_2, versionPaths.version, version, rest);
|
||||
trace(state.host, Diagnostics.package_json_has_a_typesVersions_entry_0_that_matches_compiler_version_1_looking_for_a_pattern_to_match_module_name_2, packageInfo.versionPaths.version, version, rest);
|
||||
}
|
||||
const packageDirectoryExists = nodeModulesDirectoryExists && directoryProbablyExists(packageDirectory, state.host);
|
||||
const fromPaths = tryLoadModuleUsingPaths(extensions, rest, packageDirectory, versionPaths.paths, loader, !packageDirectoryExists, state);
|
||||
const fromPaths = tryLoadModuleUsingPaths(extensions, rest, packageDirectory, packageInfo.versionPaths.paths, loader, !packageDirectoryExists, state);
|
||||
if (fromPaths) {
|
||||
return fromPaths.value;
|
||||
}
|
||||
|
||||
@@ -175,9 +175,9 @@ namespace ts.moduleSpecifiers {
|
||||
|
||||
function discoverProbableSymlinks(files: ReadonlyArray<SourceFile>, getCanonicalFileName: GetCanonicalFileName, cwd: string): ReadonlyMap<string> {
|
||||
const result = createMap<string>();
|
||||
const symlinks = mapDefined(files, sf =>
|
||||
sf.resolvedModules && firstDefinedIterator(sf.resolvedModules.values(), res =>
|
||||
res && res.originalPath && res.resolvedFileName !== res.originalPath ? [res.resolvedFileName, res.originalPath] : undefined));
|
||||
const symlinks = flatten<readonly [string, string]>(mapDefined(files, sf =>
|
||||
sf.resolvedModules && compact(arrayFrom(mapIterator(sf.resolvedModules.values(), res =>
|
||||
res && res.originalPath && res.resolvedFileName !== res.originalPath ? [res.resolvedFileName, res.originalPath] as const : undefined)))));
|
||||
for (const [resolvedPath, originalPath] of symlinks) {
|
||||
const [commonResolved, commonOriginal] = guessDirectorySymlink(resolvedPath, originalPath, cwd, getCanonicalFileName);
|
||||
result.set(commonOriginal, commonResolved);
|
||||
|
||||
@@ -1328,6 +1328,8 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
function createMissingNode<T extends Node>(kind: T["kind"], reportAtCurrentPosition: false, diagnosticMessage?: DiagnosticMessage, arg0?: any): T;
|
||||
function createMissingNode<T extends Node>(kind: T["kind"], reportAtCurrentPosition: boolean, diagnosticMessage: DiagnosticMessage, arg0?: any): T;
|
||||
function createMissingNode<T extends Node>(kind: T["kind"], reportAtCurrentPosition: boolean, diagnosticMessage: DiagnosticMessage, arg0?: any): T {
|
||||
if (reportAtCurrentPosition) {
|
||||
parseErrorAtPosition(scanner.getStartPos(), 0, diagnosticMessage, arg0);
|
||||
@@ -3122,15 +3124,16 @@ namespace ts {
|
||||
}
|
||||
|
||||
function parseUnionOrIntersectionType(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, parseConstituentType: () => TypeNode, operator: SyntaxKind.BarToken | SyntaxKind.AmpersandToken): TypeNode {
|
||||
parseOptional(operator);
|
||||
const start = scanner.getStartPos();
|
||||
const hasLeadingOperator = parseOptional(operator);
|
||||
let type = parseConstituentType();
|
||||
if (token() === operator) {
|
||||
if (token() === operator || hasLeadingOperator) {
|
||||
const types = [type];
|
||||
while (parseOptional(operator)) {
|
||||
types.push(parseConstituentType());
|
||||
}
|
||||
const node = <UnionOrIntersectionTypeNode>createNode(kind, type.pos);
|
||||
node.types = createNodeArray(types, type.pos);
|
||||
const node = <UnionOrIntersectionTypeNode>createNode(kind, start);
|
||||
node.types = createNodeArray(types, start);
|
||||
type = finishNode(node);
|
||||
}
|
||||
return type;
|
||||
@@ -4290,7 +4293,7 @@ namespace ts {
|
||||
badNode.end = invalidElement.end;
|
||||
badNode.left = result;
|
||||
badNode.right = invalidElement;
|
||||
badNode.operatorToken = createMissingNode(SyntaxKind.CommaToken, /*reportAtCurrentPosition*/ false, /*diagnosticMessage*/ undefined!); // TODO: GH#18217
|
||||
badNode.operatorToken = createMissingNode(SyntaxKind.CommaToken, /*reportAtCurrentPosition*/ false);
|
||||
badNode.operatorToken.pos = badNode.operatorToken.end = badNode.right.pos;
|
||||
return <JsxElement><Node>badNode;
|
||||
}
|
||||
|
||||
+13
-11
@@ -1453,9 +1453,8 @@ namespace ts {
|
||||
notImplementedResolver,
|
||||
getEmitHost(writeFileCallback),
|
||||
/*targetSourceFile*/ undefined,
|
||||
/*transformers*/ noTransformers,
|
||||
/*emitOnlyDtsFiles*/ false,
|
||||
/*transformers*/ undefined,
|
||||
/*declaraitonTransformers*/ undefined,
|
||||
/*onlyBuildInfo*/ true
|
||||
);
|
||||
|
||||
@@ -1574,14 +1573,12 @@ namespace ts {
|
||||
|
||||
performance.mark("beforeEmit");
|
||||
|
||||
const transformers = emitOnlyDtsFiles ? [] : getTransformers(options, customTransformers);
|
||||
const emitResult = emitFiles(
|
||||
emitResolver,
|
||||
getEmitHost(writeFileCallback),
|
||||
sourceFile,
|
||||
getTransformers(options, customTransformers, emitOnlyDtsFiles),
|
||||
emitOnlyDtsFiles,
|
||||
transformers,
|
||||
customTransformers && customTransformers.afterDeclarations
|
||||
);
|
||||
|
||||
performance.mark("afterEmit");
|
||||
@@ -2235,7 +2232,10 @@ namespace ts {
|
||||
if (isRedirect) {
|
||||
inputName = getProjectReferenceRedirect(fileName) || fileName;
|
||||
}
|
||||
if (getNormalizedAbsolutePath(checkedName, currentDirectory) !== getNormalizedAbsolutePath(inputName, currentDirectory)) {
|
||||
// Check if it differs only in drive letters its ok to ignore that error:
|
||||
const checkedAbsolutePath = getNormalizedAbsolutePathWithoutRoot(checkedName, currentDirectory);
|
||||
const inputAbsolutePath = getNormalizedAbsolutePathWithoutRoot(inputName, currentDirectory);
|
||||
if (checkedAbsolutePath !== inputAbsolutePath) {
|
||||
reportFileNamesDifferOnlyInCasingError(inputName, checkedName, refFile, refPos, refEnd);
|
||||
}
|
||||
}
|
||||
@@ -2688,6 +2688,7 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
sourceFile = Debug.assertDefined(commandLine.options.configFile);
|
||||
Debug.assert(!sourceFile.path || sourceFile.path === sourceFilePath);
|
||||
addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined);
|
||||
}
|
||||
else {
|
||||
@@ -2699,11 +2700,12 @@ namespace ts {
|
||||
projectReferenceRedirects.set(sourceFilePath, false);
|
||||
return undefined;
|
||||
}
|
||||
sourceFile.path = sourceFilePath;
|
||||
sourceFile.resolvedPath = sourceFilePath;
|
||||
sourceFile.originalFileName = refPath;
|
||||
commandLine = parseJsonSourceFileConfigFileContent(sourceFile, configParsingHost, basePath, /*existingOptions*/ undefined, refPath);
|
||||
}
|
||||
sourceFile.path = sourceFilePath;
|
||||
sourceFile.resolvedPath = sourceFilePath;
|
||||
sourceFile.originalFileName = refPath;
|
||||
|
||||
const resolvedRef: ResolvedProjectReference = { commandLine, sourceFile };
|
||||
projectReferenceRedirects.set(sourceFilePath, resolvedRef);
|
||||
if (commandLine.projectReferences) {
|
||||
@@ -2856,10 +2858,10 @@ namespace ts {
|
||||
createDiagnosticForOptionName(Diagnostics.Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher, "isolatedModules", "target");
|
||||
}
|
||||
|
||||
const firstNonExternalModuleSourceFile = find(files, f => !isExternalModule(f) && !f.isDeclarationFile && f.scriptKind !== ScriptKind.JSON);
|
||||
const firstNonExternalModuleSourceFile = find(files, f => !isExternalModule(f) && !isSourceFileJS(f) && !f.isDeclarationFile && f.scriptKind !== ScriptKind.JSON);
|
||||
if (firstNonExternalModuleSourceFile) {
|
||||
const span = getErrorSpanForNode(firstNonExternalModuleSourceFile, firstNonExternalModuleSourceFile);
|
||||
programDiagnostics.add(createFileDiagnostic(firstNonExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_compile_namespaces_when_the_isolatedModules_flag_is_provided));
|
||||
programDiagnostics.add(createFileDiagnostic(firstNonExternalModuleSourceFile, span.start, span.length, Diagnostics.All_files_must_be_modules_when_the_isolatedModules_flag_is_provided));
|
||||
}
|
||||
}
|
||||
else if (firstNonAmbientExternalModuleSourceFile && languageVersion < ScriptTarget.ES2015 && options.module === ModuleKind.None) {
|
||||
|
||||
@@ -24,13 +24,24 @@ namespace ts {
|
||||
EmitNotifications = 1 << 1,
|
||||
}
|
||||
|
||||
export function getTransformers(compilerOptions: CompilerOptions, customTransformers?: CustomTransformers) {
|
||||
export const noTransformers: EmitTransformers = { scriptTransformers: emptyArray, declarationTransformers: emptyArray };
|
||||
|
||||
export function getTransformers(compilerOptions: CompilerOptions, customTransformers?: CustomTransformers, emitOnlyDtsFiles?: boolean): EmitTransformers {
|
||||
return {
|
||||
scriptTransformers: getScriptTransformers(compilerOptions, customTransformers, emitOnlyDtsFiles),
|
||||
declarationTransformers: getDeclarationTransformers(customTransformers),
|
||||
};
|
||||
}
|
||||
|
||||
function getScriptTransformers(compilerOptions: CompilerOptions, customTransformers?: CustomTransformers, emitOnlyDtsFiles?: boolean) {
|
||||
if (emitOnlyDtsFiles) return emptyArray;
|
||||
|
||||
const jsx = compilerOptions.jsx;
|
||||
const languageVersion = getEmitScriptTarget(compilerOptions);
|
||||
const moduleKind = getEmitModuleKind(compilerOptions);
|
||||
const transformers: TransformerFactory<SourceFile | Bundle>[] = [];
|
||||
|
||||
addRange(transformers, customTransformers && customTransformers.before);
|
||||
addRange(transformers, customTransformers && map(customTransformers.before, wrapScriptTransformerFactory));
|
||||
|
||||
transformers.push(transformTypeScript);
|
||||
|
||||
@@ -71,11 +82,44 @@ namespace ts {
|
||||
transformers.push(transformES5);
|
||||
}
|
||||
|
||||
addRange(transformers, customTransformers && customTransformers.after);
|
||||
|
||||
addRange(transformers, customTransformers && map(customTransformers.after, wrapScriptTransformerFactory));
|
||||
return transformers;
|
||||
}
|
||||
|
||||
function getDeclarationTransformers(customTransformers?: CustomTransformers) {
|
||||
const transformers: TransformerFactory<SourceFile | Bundle>[] = [];
|
||||
transformers.push(transformDeclarations);
|
||||
addRange(transformers, customTransformers && map(customTransformers.afterDeclarations, wrapDeclarationTransformerFactory));
|
||||
return transformers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a custom script or declaration transformer object in a `Transformer` callback with fallback support for transforming bundles.
|
||||
*/
|
||||
function wrapCustomTransformer(transformer: CustomTransformer): Transformer<Bundle | SourceFile> {
|
||||
return node => isBundle(node) ? transformer.transformBundle(node) : transformer.transformSourceFile(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a transformer factory that may return a custom script or declaration transformer object.
|
||||
*/
|
||||
function wrapCustomTransformerFactory<T extends SourceFile | Bundle>(transformer: TransformerFactory<T> | CustomTransformerFactory, handleDefault: (node: Transformer<T>) => Transformer<Bundle | SourceFile>): TransformerFactory<Bundle | SourceFile> {
|
||||
return context => {
|
||||
const customTransformer = transformer(context);
|
||||
return typeof customTransformer === "function"
|
||||
? handleDefault(customTransformer)
|
||||
: wrapCustomTransformer(customTransformer);
|
||||
};
|
||||
}
|
||||
|
||||
function wrapScriptTransformerFactory(transformer: TransformerFactory<SourceFile> | CustomTransformerFactory): TransformerFactory<Bundle | SourceFile> {
|
||||
return wrapCustomTransformerFactory(transformer, chainBundle);
|
||||
}
|
||||
|
||||
function wrapDeclarationTransformerFactory(transformer: TransformerFactory<Bundle | SourceFile> | CustomTransformerFactory): TransformerFactory<Bundle | SourceFile> {
|
||||
return wrapCustomTransformerFactory(transformer, identity);
|
||||
}
|
||||
|
||||
export function noEmitSubstitution(_hint: EmitHint, node: Node) {
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -521,8 +521,10 @@ namespace ts {
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
||||
t[p] = s[p];
|
||||
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
||||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
|
||||
t[p[i]] = s[p[i]];
|
||||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
||||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
||||
t[p[i]] = s[p[i]];
|
||||
}
|
||||
return t;
|
||||
};`
|
||||
};
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace ts {
|
||||
loopOutParameters: LoopOutParameter[];
|
||||
}
|
||||
|
||||
type LoopConverter = (node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convertedLoopBodyStatements: Statement[] | undefined) => Statement;
|
||||
type LoopConverter = (node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convertedLoopBodyStatements: Statement[] | undefined, ancestorFacts: HierarchyFacts) => Statement;
|
||||
|
||||
// Facts we track as we traverse the tree
|
||||
const enum HierarchyFacts {
|
||||
@@ -163,11 +163,12 @@ namespace ts {
|
||||
ExportedVariableStatement = 1 << 5, // Enclosed in an exported variable statement in the current scope
|
||||
TopLevel = 1 << 6, // Enclosing block-scoped container is a top-level container
|
||||
Block = 1 << 7, // Enclosing block-scoped container is a Block
|
||||
IterationStatement = 1 << 8, // Enclosed in an IterationStatement
|
||||
IterationStatement = 1 << 8, // Immediately enclosed in an IterationStatement
|
||||
IterationStatementBlock = 1 << 9, // Enclosing Block is enclosed in an IterationStatement
|
||||
ForStatement = 1 << 10, // Enclosing block-scoped container is a ForStatement
|
||||
ForInOrForOfStatement = 1 << 11, // Enclosing block-scoped container is a ForInStatement or ForOfStatement
|
||||
ConstructorWithCapturedSuper = 1 << 12, // Enclosed in a constructor that captures 'this' for use with 'super'
|
||||
IterationContainer = 1 << 10, // Enclosed in an outer IterationStatement
|
||||
ForStatement = 1 << 11, // Enclosing block-scoped container is a ForStatement
|
||||
ForInOrForOfStatement = 1 << 12, // Enclosing block-scoped container is a ForInStatement or ForOfStatement
|
||||
ConstructorWithCapturedSuper = 1 << 13, // Enclosed in a constructor that captures 'this' for use with 'super'
|
||||
// NOTE: do not add more ancestor flags without also updating AncestorFactsMask below.
|
||||
// NOTE: when adding a new ancestor flag, be sure to update the subtree flags below.
|
||||
|
||||
@@ -184,11 +185,11 @@ namespace ts {
|
||||
|
||||
// A source file is a top-level block scope.
|
||||
SourceFileIncludes = TopLevel,
|
||||
SourceFileExcludes = BlockScopeExcludes & ~TopLevel,
|
||||
SourceFileExcludes = BlockScopeExcludes & ~TopLevel | IterationContainer,
|
||||
|
||||
// Functions, methods, and accessors are both new lexical scopes and new block scopes.
|
||||
FunctionIncludes = Function | TopLevel,
|
||||
FunctionExcludes = BlockScopeExcludes & ~TopLevel | ArrowFunction | AsyncFunctionBody | CapturesThis | NonStaticClassElement | ConstructorWithCapturedSuper,
|
||||
FunctionExcludes = BlockScopeExcludes & ~TopLevel | ArrowFunction | AsyncFunctionBody | CapturesThis | NonStaticClassElement | ConstructorWithCapturedSuper | IterationContainer,
|
||||
|
||||
AsyncFunctionBodyIncludes = FunctionIncludes | AsyncFunctionBody,
|
||||
AsyncFunctionBodyExcludes = FunctionExcludes & ~NonStaticClassElement,
|
||||
@@ -205,16 +206,16 @@ namespace ts {
|
||||
// 'do' and 'while' statements are not block scopes. We track that the subtree is contained
|
||||
// within an IterationStatement to indicate whether the embedded statement is an
|
||||
// IterationStatementBlock.
|
||||
DoOrWhileStatementIncludes = IterationStatement,
|
||||
DoOrWhileStatementIncludes = IterationStatement | IterationContainer,
|
||||
DoOrWhileStatementExcludes = None,
|
||||
|
||||
// 'for' statements are new block scopes and have special handling for 'let' declarations.
|
||||
ForStatementIncludes = IterationStatement | ForStatement,
|
||||
ForStatementIncludes = IterationStatement | ForStatement | IterationContainer,
|
||||
ForStatementExcludes = BlockScopeExcludes & ~ForStatement,
|
||||
|
||||
// 'for-in' and 'for-of' statements are new block scopes and have special handling for
|
||||
// 'let' declarations.
|
||||
ForInOrForOfStatementIncludes = IterationStatement | ForInOrForOfStatement,
|
||||
ForInOrForOfStatementIncludes = IterationStatement | ForInOrForOfStatement | IterationContainer,
|
||||
ForInOrForOfStatementExcludes = BlockScopeExcludes & ~ForInOrForOfStatement,
|
||||
|
||||
// Blocks (other than function bodies) are new block scopes.
|
||||
@@ -228,8 +229,8 @@ namespace ts {
|
||||
// Subtree facts
|
||||
//
|
||||
|
||||
NewTarget = 1 << 13, // Contains a 'new.target' meta-property
|
||||
CapturedLexicalThis = 1 << 14, // Contains a lexical `this` reference captured by an arrow function.
|
||||
NewTarget = 1 << 14, // Contains a 'new.target' meta-property
|
||||
CapturedLexicalThis = 1 << 15, // Contains a lexical `this` reference captured by an arrow function.
|
||||
|
||||
//
|
||||
// Subtree masks
|
||||
@@ -1639,7 +1640,7 @@ namespace ts {
|
||||
// arguments are both mapped contiguously to the accessor name.
|
||||
const target = getMutableClone(receiver);
|
||||
setEmitFlags(target, EmitFlags.NoComments | EmitFlags.NoTrailingSourceMap);
|
||||
setSourceMapRange(target, firstAccessor.name); // TODO: GH#18217
|
||||
setSourceMapRange(target, firstAccessor.name);
|
||||
|
||||
const propertyName = createExpressionForPropertyName(visitNode(firstAccessor.name, visitor, isPropertyName));
|
||||
setEmitFlags(propertyName, EmitFlags.NoComments | EmitFlags.NoLeadingSourceMap);
|
||||
@@ -2227,7 +2228,7 @@ namespace ts {
|
||||
|
||||
function visitIterationStatementWithFacts(excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts, node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convert?: LoopConverter) {
|
||||
const ancestorFacts = enterSubtree(excludeFacts, includeFacts);
|
||||
const updated = convertIterationStatementBodyIfNecessary(node, outermostLabeledStatement, convert);
|
||||
const updated = convertIterationStatementBodyIfNecessary(node, outermostLabeledStatement, ancestorFacts, convert);
|
||||
exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None);
|
||||
return updated;
|
||||
}
|
||||
@@ -2434,7 +2435,7 @@ namespace ts {
|
||||
return restoreEnclosingLabel(forStatement, outermostLabeledStatement, convertedLoopState && resetLabel);
|
||||
}
|
||||
|
||||
function convertForOfStatementForIterable(node: ForOfStatement, outermostLabeledStatement: LabeledStatement, convertedLoopBodyStatements: Statement[]): Statement {
|
||||
function convertForOfStatementForIterable(node: ForOfStatement, outermostLabeledStatement: LabeledStatement, convertedLoopBodyStatements: Statement[], ancestorFacts: HierarchyFacts): Statement {
|
||||
const expression = visitNode(node.expression, visitor, isExpression);
|
||||
const iterator = isIdentifier(expression) ? getGeneratedNameForNode(expression) : createTempVariable(/*recordTempVariable*/ undefined);
|
||||
const result = isIdentifier(expression) ? getGeneratedNameForNode(iterator) : createTempVariable(/*recordTempVariable*/ undefined);
|
||||
@@ -2447,13 +2448,18 @@ namespace ts {
|
||||
hoistVariableDeclaration(errorRecord);
|
||||
hoistVariableDeclaration(returnMethod);
|
||||
|
||||
// if we are enclosed in an outer loop ensure we reset 'errorRecord' per each iteration
|
||||
const initializer = ancestorFacts & HierarchyFacts.IterationContainer
|
||||
? inlineExpressions([createAssignment(errorRecord, createVoidZero()), values])
|
||||
: values;
|
||||
|
||||
const forStatement = setEmitFlags(
|
||||
setTextRange(
|
||||
createFor(
|
||||
/*initializer*/ setEmitFlags(
|
||||
setTextRange(
|
||||
createVariableDeclarationList([
|
||||
setTextRange(createVariableDeclaration(iterator, /*type*/ undefined, values), node.expression),
|
||||
setTextRange(createVariableDeclaration(iterator, /*type*/ undefined, initializer), node.expression),
|
||||
createVariableDeclaration(result, /*type*/ undefined, next)
|
||||
]),
|
||||
node.expression
|
||||
@@ -2665,7 +2671,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function convertIterationStatementBodyIfNecessary(node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convert?: LoopConverter): VisitResult<Statement> {
|
||||
function convertIterationStatementBodyIfNecessary(node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, ancestorFacts: HierarchyFacts, convert?: LoopConverter): VisitResult<Statement> {
|
||||
if (!shouldConvertIterationStatement(node)) {
|
||||
let saveAllowedNonLabeledJumps: Jump | undefined;
|
||||
if (convertedLoopState) {
|
||||
@@ -2676,7 +2682,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
const result = convert
|
||||
? convert(node, outermostLabeledStatement, /*convertedLoopBodyStatements*/ undefined)
|
||||
? convert(node, outermostLabeledStatement, /*convertedLoopBodyStatements*/ undefined, ancestorFacts)
|
||||
: restoreEnclosingLabel(visitEachChild(node, visitor, context), outermostLabeledStatement, convertedLoopState && resetLabel);
|
||||
|
||||
if (convertedLoopState) {
|
||||
@@ -2708,7 +2714,7 @@ namespace ts {
|
||||
let loop: Statement;
|
||||
if (bodyFunction) {
|
||||
if (convert) {
|
||||
loop = convert(node, outermostLabeledStatement, bodyFunction.part);
|
||||
loop = convert(node, outermostLabeledStatement, bodyFunction.part, ancestorFacts);
|
||||
}
|
||||
else {
|
||||
const clone = convertIterationStatementCore(node, initializerFunction, createBlock(bodyFunction.part, /*multiLine*/ true));
|
||||
|
||||
@@ -77,6 +77,8 @@ namespace ts {
|
||||
return visitObjectLiteralExpression(node as ObjectLiteralExpression);
|
||||
case SyntaxKind.BinaryExpression:
|
||||
return visitBinaryExpression(node as BinaryExpression, noDestructuringValue);
|
||||
case SyntaxKind.CatchClause:
|
||||
return visitCatchClause(node as CatchClause);
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
return visitVariableDeclaration(node as VariableDeclaration);
|
||||
case SyntaxKind.ForOfStatement:
|
||||
@@ -272,6 +274,28 @@ namespace ts {
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
function visitCatchClause(node: CatchClause) {
|
||||
if (node.variableDeclaration &&
|
||||
isBindingPattern(node.variableDeclaration.name) &&
|
||||
node.variableDeclaration.name.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
|
||||
const name = getGeneratedNameForNode(node.variableDeclaration.name);
|
||||
const updatedDecl = updateVariableDeclaration(node.variableDeclaration, node.variableDeclaration.name, /*type*/ undefined, name);
|
||||
const visitedBindings = flattenDestructuringBinding(updatedDecl, visitor, context, FlattenLevel.ObjectRest);
|
||||
let block = visitNode(node.block, visitor, isBlock);
|
||||
if (some(visitedBindings)) {
|
||||
block = updateBlock(block, [
|
||||
createVariableStatement(/*modifiers*/ undefined, visitedBindings),
|
||||
...block.statements,
|
||||
]);
|
||||
}
|
||||
return updateCatchClause(
|
||||
node,
|
||||
updateVariableDeclaration(node.variableDeclaration, name, /*type*/ undefined, /*initializer*/ undefined),
|
||||
block);
|
||||
}
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a VariableDeclaration node with a binding pattern.
|
||||
*
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
"files": [
|
||||
"core.ts",
|
||||
"debug.ts",
|
||||
"performance.ts",
|
||||
"semver.ts",
|
||||
|
||||
|
||||
+46
-23
@@ -2546,6 +2546,8 @@ namespace ts {
|
||||
Shared = 1 << 10, // Referenced as antecedent more than once
|
||||
PreFinally = 1 << 11, // Injected edge that links pre-finally label and pre-try flow
|
||||
AfterFinally = 1 << 12, // Injected edge that links post-finally flow with the rest of the graph
|
||||
/** @internal */
|
||||
Cached = 1 << 13, // Indicates that at least one cross-call cache entry exists for this node, even if not a loop participant
|
||||
Label = BranchLabel | LoopLabel,
|
||||
Condition = TrueCondition | FalseCondition
|
||||
}
|
||||
@@ -2614,7 +2616,7 @@ namespace ts {
|
||||
|
||||
// Incomplete types occur during control flow analysis of loops. An IncompleteType
|
||||
// is distinguished from a regular type by a flags value of zero. Incomplete type
|
||||
// objects are internal to the getFlowTypeOfRefecence function and never escape it.
|
||||
// objects are internal to the getFlowTypeOfReference function and never escape it.
|
||||
export interface IncompleteType {
|
||||
flags: TypeFlags; // No flags set
|
||||
type: Type; // The type marked incomplete
|
||||
@@ -3011,13 +3013,26 @@ namespace ts {
|
||||
Completely = 1 << 1,
|
||||
}
|
||||
|
||||
export type CustomTransformerFactory = (context: TransformationContext) => CustomTransformer;
|
||||
|
||||
export interface CustomTransformer {
|
||||
transformSourceFile(node: SourceFile): SourceFile;
|
||||
transformBundle(node: Bundle): Bundle;
|
||||
}
|
||||
|
||||
export interface CustomTransformers {
|
||||
/** Custom transformers to evaluate before built-in .js transformations. */
|
||||
before?: TransformerFactory<SourceFile>[];
|
||||
before?: (TransformerFactory<SourceFile> | CustomTransformerFactory)[];
|
||||
/** Custom transformers to evaluate after built-in .js transformations. */
|
||||
after?: TransformerFactory<SourceFile>[];
|
||||
after?: (TransformerFactory<SourceFile> | CustomTransformerFactory)[];
|
||||
/** Custom transformers to evaluate after built-in .d.ts transformations. */
|
||||
afterDeclarations?: TransformerFactory<Bundle | SourceFile>[];
|
||||
afterDeclarations?: (TransformerFactory<Bundle | SourceFile> | CustomTransformerFactory)[];
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export interface EmitTransformers {
|
||||
scriptTransformers: readonly TransformerFactory<SourceFile | Bundle>[];
|
||||
declarationTransformers: readonly TransformerFactory<SourceFile | Bundle>[];
|
||||
}
|
||||
|
||||
export interface SourceMapSpan {
|
||||
@@ -3139,6 +3154,7 @@ namespace ts {
|
||||
*/
|
||||
getExportSymbolOfSymbol(symbol: Symbol): Symbol;
|
||||
getPropertySymbolOfDestructuringAssignment(location: Identifier): Symbol | undefined;
|
||||
getTypeOfAssignmentPattern(pattern: AssignmentPattern): Type;
|
||||
getTypeAtLocation(node: Node): Type;
|
||||
getTypeFromTypeNode(node: TypeNode): Type;
|
||||
|
||||
@@ -3735,7 +3751,7 @@ namespace ts {
|
||||
specifierCache?: Map<string>; // For symbols corresponding to external modules, a cache of incoming path -> module specifier name mappings
|
||||
extendedContainers?: Symbol[]; // Containers (other than the parent) which this symbol is aliased in
|
||||
extendedContainersByFile?: Map<Symbol[]>; // Containers (other than the parent) which this symbol is aliased in
|
||||
variances?: Variance[]; // Alias symbol type argument variance cache
|
||||
variances?: VarianceFlags[]; // Alias symbol type argument variance cache
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -3951,6 +3967,8 @@ namespace ts {
|
||||
StructuredOrInstantiable = StructuredType | Instantiable,
|
||||
/* @internal */
|
||||
ObjectFlagsType = Nullable | Never | Object | Union | Intersection,
|
||||
/* @internal */
|
||||
Simplifiable = IndexedAccess | Conditional,
|
||||
// 'Narrowable' types are types where narrowing actually narrows.
|
||||
// This *should* be every type other than null, undefined, void, and never
|
||||
Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive,
|
||||
@@ -4131,12 +4149,16 @@ namespace ts {
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export const enum Variance {
|
||||
Invariant = 0, // Neither covariant nor contravariant
|
||||
Covariant = 1, // Covariant
|
||||
Contravariant = 2, // Contravariant
|
||||
Bivariant = 3, // Both covariant and contravariant
|
||||
Independent = 4, // Unwitnessed type parameter
|
||||
export const enum VarianceFlags {
|
||||
Invariant = 0, // Neither covariant nor contravariant
|
||||
Covariant = 1 << 0, // Covariant
|
||||
Contravariant = 1 << 1, // Contravariant
|
||||
Bivariant = Covariant | Contravariant, // Both covariant and contravariant
|
||||
Independent = 1 << 2, // Unwitnessed type parameter
|
||||
VarianceMask = Invariant | Covariant | Contravariant | Independent, // Mask containing all measured variances without the unmeasurable flag
|
||||
Unmeasurable = 1 << 3, // Variance result is unusable - relationship relies on structural comparisons which are not reflected in generic relationships
|
||||
Unreliable = 1 << 4, // Variance result is unreliable - relationship relies on structural comparisons which are not reflected in generic relationships
|
||||
AllowsStructuralFallback = Unmeasurable | Unreliable,
|
||||
}
|
||||
|
||||
// Generic class and interface types
|
||||
@@ -4144,7 +4166,7 @@ namespace ts {
|
||||
/* @internal */
|
||||
instantiations: Map<TypeReference>; // Generic instantiation cache
|
||||
/* @internal */
|
||||
variances?: Variance[]; // Variance of each type parameter
|
||||
variances?: VarianceFlags[]; // Variance of each type parameter
|
||||
}
|
||||
|
||||
export interface TupleType extends GenericType {
|
||||
@@ -4319,8 +4341,8 @@ namespace ts {
|
||||
root: ConditionalRoot;
|
||||
checkType: Type;
|
||||
extendsType: Type;
|
||||
trueType: Type;
|
||||
falseType: Type;
|
||||
resolvedTrueType: Type;
|
||||
resolvedFalseType: Type;
|
||||
/* @internal */
|
||||
resolvedInferredTrueType?: Type; // The `trueType` instantiated with the `combinedMapper`, if present
|
||||
/* @internal */
|
||||
@@ -4405,15 +4427,16 @@ namespace ts {
|
||||
export type TypeMapper = (t: TypeParameter) => Type;
|
||||
|
||||
export const enum InferencePriority {
|
||||
NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
|
||||
HomomorphicMappedType = 1 << 1, // Reverse inference for homomorphic mapped type
|
||||
MappedTypeConstraint = 1 << 2, // Reverse inference for mapped type
|
||||
ReturnType = 1 << 3, // Inference made from return type of generic function
|
||||
LiteralKeyof = 1 << 4, // Inference made from a string literal to a keyof T
|
||||
NoConstraints = 1 << 5, // Don't infer from constraints of instantiable types
|
||||
AlwaysStrict = 1 << 6, // Always use strict rules for contravariant inferences
|
||||
NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
|
||||
HomomorphicMappedType = 1 << 1, // Reverse inference for homomorphic mapped type
|
||||
PartialHomomorphicMappedType = 1 << 2, // Partial reverse inference for homomorphic mapped type
|
||||
MappedTypeConstraint = 1 << 3, // Reverse inference for mapped type
|
||||
ReturnType = 1 << 4, // Inference made from return type of generic function
|
||||
LiteralKeyof = 1 << 5, // Inference made from a string literal to a keyof T
|
||||
NoConstraints = 1 << 6, // Don't infer from constraints of instantiable types
|
||||
AlwaysStrict = 1 << 7, // Always use strict rules for contravariant inferences
|
||||
|
||||
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
|
||||
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -5133,7 +5156,7 @@ namespace ts {
|
||||
createHash?(data: string): string;
|
||||
getParsedCommandLine?(fileName: string): ParsedCommandLine | undefined;
|
||||
|
||||
// TODO: later handle this in better way in builder host instead once the api for tsbuild finalizes and doesnt use compilerHost as base
|
||||
// TODO: later handle this in better way in builder host instead once the api for tsbuild finalizes and doesn't use compilerHost as base
|
||||
/*@internal*/createDirectory?(directory: string): void;
|
||||
}
|
||||
|
||||
|
||||
@@ -2073,7 +2073,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function importFromModuleSpecifier(node: StringLiteralLike): AnyValidImportOrReExport {
|
||||
return tryGetImportFromModuleSpecifier(node) || Debug.fail(Debug.showSyntaxKind(node.parent));
|
||||
return tryGetImportFromModuleSpecifier(node) || Debug.failBadSyntaxKind(node.parent);
|
||||
}
|
||||
|
||||
export function tryGetImportFromModuleSpecifier(node: StringLiteralLike): AnyValidImportOrReExport | undefined {
|
||||
@@ -4186,78 +4186,6 @@ namespace ts {
|
||||
return getNewLine ? getNewLine() : sys ? sys.newLine : carriageReturnLineFeed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an enum value as a string for debugging and debug assertions.
|
||||
*/
|
||||
function formatEnum(value = 0, enumObject: any, isFlags?: boolean) {
|
||||
const members = getEnumMembers(enumObject);
|
||||
if (value === 0) {
|
||||
return members.length > 0 && members[0][0] === 0 ? members[0][1] : "0";
|
||||
}
|
||||
if (isFlags) {
|
||||
let result = "";
|
||||
let remainingFlags = value;
|
||||
for (let i = members.length - 1; i >= 0 && remainingFlags !== 0; i--) {
|
||||
const [enumValue, enumName] = members[i];
|
||||
if (enumValue !== 0 && (remainingFlags & enumValue) === enumValue) {
|
||||
remainingFlags &= ~enumValue;
|
||||
result = `${enumName}${result ? ", " : ""}${result}`;
|
||||
}
|
||||
}
|
||||
if (remainingFlags === 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const [enumValue, enumName] of members) {
|
||||
if (enumValue === value) {
|
||||
return enumName;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
function getEnumMembers(enumObject: any) {
|
||||
const result: [number, string][] = [];
|
||||
for (const name in enumObject) {
|
||||
const value = enumObject[name];
|
||||
if (typeof value === "number") {
|
||||
result.push([value, name]);
|
||||
}
|
||||
}
|
||||
|
||||
return stableSort<[number, string]>(result, (x, y) => compareValues(x[0], y[0]));
|
||||
}
|
||||
|
||||
export function formatSyntaxKind(kind: SyntaxKind | undefined): string {
|
||||
return formatEnum(kind, (<any>ts).SyntaxKind, /*isFlags*/ false);
|
||||
}
|
||||
|
||||
export function formatModifierFlags(flags: ModifierFlags | undefined): string {
|
||||
return formatEnum(flags, (<any>ts).ModifierFlags, /*isFlags*/ true);
|
||||
}
|
||||
|
||||
export function formatTransformFlags(flags: TransformFlags | undefined): string {
|
||||
return formatEnum(flags, (<any>ts).TransformFlags, /*isFlags*/ true);
|
||||
}
|
||||
|
||||
export function formatEmitFlags(flags: EmitFlags | undefined): string {
|
||||
return formatEnum(flags, (<any>ts).EmitFlags, /*isFlags*/ true);
|
||||
}
|
||||
|
||||
export function formatSymbolFlags(flags: SymbolFlags | undefined): string {
|
||||
return formatEnum(flags, (<any>ts).SymbolFlags, /*isFlags*/ true);
|
||||
}
|
||||
|
||||
export function formatTypeFlags(flags: TypeFlags | undefined): string {
|
||||
return formatEnum(flags, (<any>ts).TypeFlags, /*isFlags*/ true);
|
||||
}
|
||||
|
||||
export function formatObjectFlags(flags: ObjectFlags | undefined): string {
|
||||
return formatEnum(flags, (<any>ts).ObjectFlags, /*isFlags*/ true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new TextRange from the provided pos and end.
|
||||
*
|
||||
@@ -7646,6 +7574,14 @@ namespace ts {
|
||||
return root + pathComponents.slice(1).join(directorySeparator);
|
||||
}
|
||||
|
||||
export function getNormalizedAbsolutePathWithoutRoot(fileName: string, currentDirectory: string | undefined) {
|
||||
return getPathWithoutRoot(getNormalizedPathComponents(fileName, currentDirectory));
|
||||
}
|
||||
|
||||
function getPathWithoutRoot(pathComponents: ReadonlyArray<string>) {
|
||||
if (pathComponents.length === 0) return "";
|
||||
return pathComponents.slice(1).join(directorySeparator);
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -8420,29 +8356,6 @@ namespace ts {
|
||||
return pathext ? path.slice(0, path.length - pathext.length) + (startsWith(ext, ".") ? ext : "." + ext) : path;
|
||||
}
|
||||
|
||||
export namespace Debug {
|
||||
export function showSymbol(symbol: Symbol): string {
|
||||
const symbolFlags = (ts as any).SymbolFlags;
|
||||
return `{ flags: ${symbolFlags ? showFlags(symbol.flags, symbolFlags) : symbol.flags}; declarations: ${map(symbol.declarations, showSyntaxKind)} }`;
|
||||
}
|
||||
|
||||
function showFlags(flags: number, flagsEnum: { [flag: number]: string }): string {
|
||||
const out: string[] = [];
|
||||
for (let pow = 0; pow <= 30; pow++) {
|
||||
const n = 1 << pow;
|
||||
if (flags & n) {
|
||||
out.push(flagsEnum[n]);
|
||||
}
|
||||
}
|
||||
return out.join("|");
|
||||
}
|
||||
|
||||
export function showSyntaxKind(node: Node): string {
|
||||
const syntaxKind = (ts as any).SyntaxKind;
|
||||
return syntaxKind ? syntaxKind[node.kind] : node.kind.toString();
|
||||
}
|
||||
}
|
||||
|
||||
export function tryParsePattern(pattern: string): Pattern | undefined {
|
||||
// This should be verified outside of here and a proper error thrown.
|
||||
Debug.assert(hasZeroOrOneAsteriskCharacter(pattern));
|
||||
|
||||
@@ -1558,100 +1558,4 @@ namespace ts {
|
||||
function aggregateTransformFlagsForChildNodes(transformFlags: TransformFlags, nodes: NodeArray<Node>): TransformFlags {
|
||||
return transformFlags | aggregateTransformFlagsForNodeArray(nodes);
|
||||
}
|
||||
|
||||
export namespace Debug {
|
||||
let isDebugInfoEnabled = false;
|
||||
|
||||
export function failBadSyntaxKind(node: Node, message?: string): never {
|
||||
return fail(
|
||||
`${message || "Unexpected node."}\r\nNode ${formatSyntaxKind(node.kind)} was unexpected.`,
|
||||
failBadSyntaxKind);
|
||||
}
|
||||
|
||||
export const assertEachNode = shouldAssert(AssertionLevel.Normal)
|
||||
? (nodes: Node[], test: (node: Node) => boolean, message?: string): void => assert(
|
||||
test === undefined || every(nodes, test),
|
||||
message || "Unexpected node.",
|
||||
() => `Node array did not pass test '${getFunctionName(test)}'.`,
|
||||
assertEachNode)
|
||||
: noop;
|
||||
|
||||
export const assertNode = shouldAssert(AssertionLevel.Normal)
|
||||
? (node: Node | undefined, test: ((node: Node | undefined) => boolean) | undefined, message?: string): void => assert(
|
||||
test === undefined || test(node),
|
||||
message || "Unexpected node.",
|
||||
() => `Node ${formatSyntaxKind(node!.kind)} did not pass test '${getFunctionName(test!)}'.`,
|
||||
assertNode)
|
||||
: noop;
|
||||
|
||||
export const assertOptionalNode = shouldAssert(AssertionLevel.Normal)
|
||||
? (node: Node, test: (node: Node) => boolean, message?: string): void => assert(
|
||||
test === undefined || node === undefined || test(node),
|
||||
message || "Unexpected node.",
|
||||
() => `Node ${formatSyntaxKind(node.kind)} did not pass test '${getFunctionName(test)}'.`,
|
||||
assertOptionalNode)
|
||||
: noop;
|
||||
|
||||
export const assertOptionalToken = shouldAssert(AssertionLevel.Normal)
|
||||
? (node: Node, kind: SyntaxKind, message?: string): void => assert(
|
||||
kind === undefined || node === undefined || node.kind === kind,
|
||||
message || "Unexpected node.",
|
||||
() => `Node ${formatSyntaxKind(node.kind)} was not a '${formatSyntaxKind(kind)}' token.`,
|
||||
assertOptionalToken)
|
||||
: noop;
|
||||
|
||||
export const assertMissingNode = shouldAssert(AssertionLevel.Normal)
|
||||
? (node: Node, message?: string): void => assert(
|
||||
node === undefined,
|
||||
message || "Unexpected node.",
|
||||
() => `Node ${formatSyntaxKind(node.kind)} was unexpected'.`,
|
||||
assertMissingNode)
|
||||
: noop;
|
||||
|
||||
/**
|
||||
* Injects debug information into frequently used types.
|
||||
*/
|
||||
export function enableDebugInfo() {
|
||||
if (isDebugInfoEnabled) return;
|
||||
|
||||
// Add additional properties in debug mode to assist with debugging.
|
||||
Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, {
|
||||
__debugFlags: { get(this: Symbol) { return formatSymbolFlags(this.flags); } }
|
||||
});
|
||||
|
||||
Object.defineProperties(objectAllocator.getTypeConstructor().prototype, {
|
||||
__debugFlags: { get(this: Type) { return formatTypeFlags(this.flags); } },
|
||||
__debugObjectFlags: { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((<ObjectType>this).objectFlags) : ""; } },
|
||||
__debugTypeToString: { value(this: Type) { return this.checker.typeToString(this); } },
|
||||
});
|
||||
|
||||
const nodeConstructors = [
|
||||
objectAllocator.getNodeConstructor(),
|
||||
objectAllocator.getIdentifierConstructor(),
|
||||
objectAllocator.getTokenConstructor(),
|
||||
objectAllocator.getSourceFileConstructor()
|
||||
];
|
||||
|
||||
for (const ctor of nodeConstructors) {
|
||||
if (!ctor.prototype.hasOwnProperty("__debugKind")) {
|
||||
Object.defineProperties(ctor.prototype, {
|
||||
__debugKind: { get(this: Node) { return formatSyntaxKind(this.kind); } },
|
||||
__debugModifierFlags: { get(this: Node) { return formatModifierFlags(getModifierFlagsNoCache(this)); } },
|
||||
__debugTransformFlags: { get(this: Node) { return formatTransformFlags(this.transformFlags); } },
|
||||
__debugEmitFlags: { get(this: Node) { return formatEmitFlags(getEmitFlags(this)); } },
|
||||
__debugGetText: {
|
||||
value(this: Node, includeTrivia?: boolean) {
|
||||
if (nodeIsSynthesized(this)) return "";
|
||||
const parseNode = getParseTreeNode(this);
|
||||
const sourceFile = parseNode && getSourceFileOfNode(parseNode);
|
||||
return sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : "";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isDebugInfoEnabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,6 +424,10 @@ namespace ts.server {
|
||||
return renameInfo;
|
||||
}
|
||||
|
||||
getSmartSelectionRange() {
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[] {
|
||||
if (!this.lastRenameEntry ||
|
||||
this.lastRenameEntry.inputs.fileName !== fileName ||
|
||||
|
||||
+130
-34
@@ -798,8 +798,8 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
private verifyCompletionEntry(actual: ts.CompletionEntry, expected: FourSlashInterface.ExpectedCompletionEntry) {
|
||||
const { insertText, replacementSpan, hasAction, isRecommended, kind, kindModifiers, text, documentation, tags, source, sourceDisplay } = typeof expected === "string"
|
||||
? { insertText: undefined, replacementSpan: undefined, hasAction: undefined, isRecommended: undefined, kind: undefined, kindModifiers: undefined, text: undefined, documentation: undefined, tags: undefined, source: undefined, sourceDisplay: undefined }
|
||||
const { insertText, replacementSpan, hasAction, isRecommended, kind, kindModifiers, text, documentation, tags, source, sourceDisplay, sortText } = typeof expected === "string"
|
||||
? { insertText: undefined, replacementSpan: undefined, hasAction: undefined, isRecommended: undefined, kind: undefined, kindModifiers: undefined, text: undefined, documentation: undefined, tags: undefined, source: undefined, sourceDisplay: undefined, sortText: undefined }
|
||||
: expected;
|
||||
|
||||
if (actual.insertText !== insertText) {
|
||||
@@ -825,6 +825,7 @@ namespace FourSlash {
|
||||
assert.equal(actual.hasAction, hasAction);
|
||||
assert.equal(actual.isRecommended, isRecommended);
|
||||
assert.equal(actual.source, source);
|
||||
assert.equal(actual.sortText, sortText || ts.Completions.SortText.LocationPriority, this.messageAtLastKnownMarker(`Actual entry: ${JSON.stringify(actual)}`));
|
||||
|
||||
if (text !== undefined) {
|
||||
const actualDetails = this.getCompletionEntryDetails(actual.name, actual.source)!;
|
||||
@@ -1417,12 +1418,7 @@ Actual: ${stringify(fullActual)}`);
|
||||
}
|
||||
|
||||
public baselineCurrentFileBreakpointLocations() {
|
||||
let baselineFile = this.testData.globalOptions[MetadataOptionNames.baselineFile];
|
||||
if (!baselineFile) {
|
||||
baselineFile = this.activeFile.fileName.replace(this.basePath + "/breakpointValidation", "bpSpan");
|
||||
baselineFile = baselineFile.replace(ts.Extension.Ts, ".baseline");
|
||||
|
||||
}
|
||||
const baselineFile = this.getBaselineFileName().replace("breakpointValidation", "bpSpan");
|
||||
Harness.Baseline.runBaseline(baselineFile, this.baselineCurrentFileLocations(pos => this.getBreakpointStatementLocation(pos)!));
|
||||
}
|
||||
|
||||
@@ -1497,8 +1493,7 @@ Actual: ${stringify(fullActual)}`);
|
||||
}
|
||||
|
||||
public baselineQuickInfo() {
|
||||
const baselineFile = this.testData.globalOptions[MetadataOptionNames.baselineFile] ||
|
||||
ts.getBaseFileName(this.activeFile.fileName).replace(ts.Extension.Ts, ".baseline");
|
||||
const baselineFile = this.getBaselineFileName();
|
||||
Harness.Baseline.runBaseline(
|
||||
baselineFile,
|
||||
stringify(
|
||||
@@ -1508,6 +1503,39 @@ Actual: ${stringify(fullActual)}`);
|
||||
}))));
|
||||
}
|
||||
|
||||
public baselineSmartSelection() {
|
||||
const n = "\n";
|
||||
const baselineFile = this.getBaselineFileName();
|
||||
const markers = this.getMarkers();
|
||||
const fileContent = this.activeFile.content;
|
||||
const text = markers.map(marker => {
|
||||
const baselineContent = [fileContent.slice(0, marker.position) + "/**/" + fileContent.slice(marker.position) + n];
|
||||
let selectionRange: ts.SelectionRange | undefined = this.languageService.getSmartSelectionRange(this.activeFile.fileName, marker.position);
|
||||
while (selectionRange) {
|
||||
const { textSpan } = selectionRange;
|
||||
let masked = Array.from(fileContent).map((char, index) => {
|
||||
const charCode = char.charCodeAt(0);
|
||||
if (index >= textSpan.start && index < ts.textSpanEnd(textSpan)) {
|
||||
return char === " " ? "•" : ts.isLineBreak(charCode) ? `↲${n}` : char;
|
||||
}
|
||||
return ts.isLineBreak(charCode) ? char : " ";
|
||||
}).join("");
|
||||
masked = masked.replace(/^\s*$\r?\n?/gm, ""); // Remove blank lines
|
||||
const isRealCharacter = (char: string) => char !== "•" && char !== "↲" && !ts.isWhiteSpaceLike(char.charCodeAt(0));
|
||||
const leadingWidth = Array.from(masked).findIndex(isRealCharacter);
|
||||
const trailingWidth = ts.findLastIndex(Array.from(masked), isRealCharacter);
|
||||
masked = masked.slice(0, leadingWidth)
|
||||
+ masked.slice(leadingWidth, trailingWidth).replace(/•/g, " ").replace(/↲/g, "")
|
||||
+ masked.slice(trailingWidth);
|
||||
baselineContent.push(masked);
|
||||
selectionRange = selectionRange.parent;
|
||||
}
|
||||
return baselineContent.join(fileContent.includes("\n") ? n + n : n);
|
||||
}).join(n.repeat(2) + "=".repeat(80) + n.repeat(2));
|
||||
|
||||
Harness.Baseline.runBaseline(baselineFile, text);
|
||||
}
|
||||
|
||||
public printBreakpointLocation(pos: number) {
|
||||
Harness.IO.log("\n**Pos: " + pos + " " + this.spanInfoToString(this.getBreakpointStatementLocation(pos)!, " "));
|
||||
}
|
||||
@@ -1562,6 +1590,11 @@ Actual: ${stringify(fullActual)}`);
|
||||
Harness.IO.log(stringify(help.items[help.selectedItemIndex]));
|
||||
}
|
||||
|
||||
private getBaselineFileName() {
|
||||
return this.testData.globalOptions[MetadataOptionNames.baselineFile] ||
|
||||
ts.getBaseFileName(this.activeFile.fileName).replace(ts.Extension.Ts, ".baseline");
|
||||
}
|
||||
|
||||
private getSignatureHelp({ triggerReason }: FourSlashInterface.VerifySignatureHelpOptions): ts.SignatureHelpItems | undefined {
|
||||
return this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition, {
|
||||
triggerReason
|
||||
@@ -3960,6 +3993,10 @@ namespace FourSlashInterface {
|
||||
this.state.baselineQuickInfo();
|
||||
}
|
||||
|
||||
public baselineSmartSelection() {
|
||||
this.state.baselineSmartSelection();
|
||||
}
|
||||
|
||||
public nameOrDottedNameSpanTextIs(text: string) {
|
||||
this.state.verifyCurrentNameOrDottedNameSpanText(text);
|
||||
}
|
||||
@@ -4398,18 +4435,63 @@ namespace FourSlashInterface {
|
||||
}
|
||||
}
|
||||
export namespace Completion {
|
||||
const functionEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "function", kindModifiers: "declare" });
|
||||
const varEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "var", kindModifiers: "declare" });
|
||||
const moduleEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "module", kindModifiers: "declare" });
|
||||
const keywordEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "keyword" });
|
||||
const methodEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "method", kindModifiers: "declare" });
|
||||
const propertyEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "property", kindModifiers: "declare" });
|
||||
const interfaceEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "interface", kindModifiers: "declare" });
|
||||
const typeEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "type", kindModifiers: "declare" });
|
||||
export import SortText = ts.Completions.SortText;
|
||||
|
||||
const functionEntry = (name: string): ExpectedCompletionEntryObject => ({
|
||||
name,
|
||||
kind: "function",
|
||||
kindModifiers: "declare",
|
||||
sortText: SortText.GlobalsOrKeywords
|
||||
});
|
||||
const varEntry = (name: string): ExpectedCompletionEntryObject => ({
|
||||
name,
|
||||
kind: "var",
|
||||
kindModifiers: "declare",
|
||||
sortText: SortText.GlobalsOrKeywords
|
||||
});
|
||||
const moduleEntry = (name: string): ExpectedCompletionEntryObject => ({
|
||||
name,
|
||||
kind: "module",
|
||||
kindModifiers: "declare",
|
||||
sortText: SortText.GlobalsOrKeywords
|
||||
});
|
||||
const keywordEntry = (name: string): ExpectedCompletionEntryObject => ({
|
||||
name,
|
||||
kind: "keyword",
|
||||
sortText: SortText.GlobalsOrKeywords
|
||||
});
|
||||
const methodEntry = (name: string): ExpectedCompletionEntryObject => ({
|
||||
name,
|
||||
kind: "method",
|
||||
kindModifiers: "declare",
|
||||
sortText: SortText.LocationPriority
|
||||
});
|
||||
const propertyEntry = (name: string): ExpectedCompletionEntryObject => ({
|
||||
name,
|
||||
kind: "property",
|
||||
kindModifiers: "declare",
|
||||
sortText: SortText.LocationPriority
|
||||
});
|
||||
const interfaceEntry = (name: string): ExpectedCompletionEntryObject => ({
|
||||
name,
|
||||
kind: "interface",
|
||||
kindModifiers: "declare",
|
||||
sortText: SortText.GlobalsOrKeywords
|
||||
});
|
||||
const typeEntry = (name: string): ExpectedCompletionEntryObject => ({
|
||||
name,
|
||||
kind: "type",
|
||||
kindModifiers: "declare",
|
||||
sortText: SortText.GlobalsOrKeywords
|
||||
});
|
||||
|
||||
const res: ExpectedCompletionEntryObject[] = [];
|
||||
for (let i = ts.SyntaxKind.FirstKeyword; i <= ts.SyntaxKind.LastKeyword; i++) {
|
||||
res.push({ name: ts.Debug.assertDefined(ts.tokenToString(i)), kind: "keyword" });
|
||||
res.push({
|
||||
name: ts.Debug.assertDefined(ts.tokenToString(i)),
|
||||
kind: "keyword",
|
||||
sortText: SortText.GlobalsOrKeywords
|
||||
});
|
||||
}
|
||||
export const keywordsWithUndefined: ReadonlyArray<ExpectedCompletionEntryObject> = res;
|
||||
export const keywords: ReadonlyArray<ExpectedCompletionEntryObject> = keywordsWithUndefined.filter(k => k.name !== "undefined");
|
||||
@@ -4516,11 +4598,15 @@ namespace FourSlashInterface {
|
||||
moduleEntry("Intl"),
|
||||
];
|
||||
|
||||
export const globalThisEntry: ExpectedCompletionEntry = {
|
||||
name: "globalThis",
|
||||
kind: "module",
|
||||
sortText: SortText.GlobalsOrKeywords
|
||||
};
|
||||
export const globalTypes = globalTypesPlus([]);
|
||||
|
||||
export function globalTypesPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
|
||||
return [
|
||||
{ name: "globalThis", kind: "module" },
|
||||
globalThisEntry,
|
||||
...globalTypeDecls,
|
||||
...plus,
|
||||
...typeKeywords,
|
||||
@@ -4569,7 +4655,11 @@ namespace FourSlashInterface {
|
||||
export const classElementInJsKeywords = getInJsKeywords(classElementKeywords);
|
||||
|
||||
export const constructorParameterKeywords: ReadonlyArray<ExpectedCompletionEntryObject> =
|
||||
["private", "protected", "public", "readonly"].map((name): ExpectedCompletionEntryObject => ({ name, kind: "keyword" }));
|
||||
["private", "protected", "public", "readonly"].map((name): ExpectedCompletionEntryObject => ({
|
||||
name,
|
||||
kind: "keyword",
|
||||
sortText: SortText.GlobalsOrKeywords
|
||||
}));
|
||||
|
||||
export const functionMembers: ReadonlyArray<ExpectedCompletionEntryObject> = [
|
||||
methodEntry("apply"),
|
||||
@@ -4798,13 +4888,18 @@ namespace FourSlashInterface {
|
||||
"await",
|
||||
].map(keywordEntry);
|
||||
|
||||
export const undefinedVarEntry: ExpectedCompletionEntry = {
|
||||
name: "undefined",
|
||||
kind: "var",
|
||||
sortText: SortText.GlobalsOrKeywords
|
||||
};
|
||||
// TODO: many of these are inappropriate to always provide
|
||||
export const globalsInsideFunction = (plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> => [
|
||||
{ name: "arguments", kind: "local var" },
|
||||
...plus,
|
||||
{ name: "globalThis", kind: "module" },
|
||||
globalThisEntry,
|
||||
...globalsVars,
|
||||
{ name: "undefined", kind: "var" },
|
||||
undefinedVarEntry,
|
||||
...globalKeywordsInsideFunction,
|
||||
];
|
||||
|
||||
@@ -4813,10 +4908,10 @@ namespace FourSlashInterface {
|
||||
// TODO: many of these are inappropriate to always provide
|
||||
export const globalsInJsInsideFunction = (plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> => [
|
||||
{ name: "arguments", kind: "local var" },
|
||||
{ name: "globalThis", kind: "module" },
|
||||
globalThisEntry,
|
||||
...globalsVars,
|
||||
...plus,
|
||||
{ name: "undefined", kind: "var" },
|
||||
undefinedVarEntry,
|
||||
...globalInJsKeywordsInsideFunction,
|
||||
];
|
||||
|
||||
@@ -4954,34 +5049,34 @@ namespace FourSlashInterface {
|
||||
})();
|
||||
|
||||
export const globals: ReadonlyArray<ExpectedCompletionEntryObject> = [
|
||||
{ name: "globalThis", kind: "module" },
|
||||
globalThisEntry,
|
||||
...globalsVars,
|
||||
{ name: "undefined", kind: "var" },
|
||||
undefinedVarEntry,
|
||||
...globalKeywords
|
||||
];
|
||||
|
||||
export const globalsInJs: ReadonlyArray<ExpectedCompletionEntryObject> = [
|
||||
{ name: "globalThis", kind: "module" },
|
||||
globalThisEntry,
|
||||
...globalsVars,
|
||||
{ name: "undefined", kind: "var" },
|
||||
undefinedVarEntry,
|
||||
...globalInJsKeywords
|
||||
];
|
||||
|
||||
export function globalsPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
|
||||
return [
|
||||
{ name: "globalThis", kind: "module" },
|
||||
globalThisEntry,
|
||||
...globalsVars,
|
||||
...plus,
|
||||
{ name: "undefined", kind: "var" },
|
||||
undefinedVarEntry,
|
||||
...globalKeywords];
|
||||
}
|
||||
|
||||
export function globalsInJsPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
|
||||
return [
|
||||
{ name: "globalThis", kind: "module" },
|
||||
globalThisEntry,
|
||||
...globalsVars,
|
||||
...plus,
|
||||
{ name: "undefined", kind: "var" },
|
||||
undefinedVarEntry,
|
||||
...globalInJsKeywords];
|
||||
}
|
||||
}
|
||||
@@ -5014,6 +5109,7 @@ namespace FourSlashInterface {
|
||||
readonly documentation?: string;
|
||||
readonly sourceDisplay?: string;
|
||||
readonly tags?: ReadonlyArray<ts.JSDocTagInfo>;
|
||||
readonly sortText?: ts.Completions.SortText;
|
||||
}
|
||||
|
||||
export interface VerifyCompletionsOptions {
|
||||
|
||||
@@ -472,6 +472,9 @@ namespace Harness.LanguageService {
|
||||
getRenameInfo(fileName: string, position: number, options?: ts.RenameInfoOptions): ts.RenameInfo {
|
||||
return unwrapJSONCallResult(this.shim.getRenameInfo(fileName, position, options));
|
||||
}
|
||||
getSmartSelectionRange(fileName: string, position: number): ts.SelectionRange {
|
||||
return unwrapJSONCallResult(this.shim.getSmartSelectionRange(fileName, position));
|
||||
}
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): ts.RenameLocation[] {
|
||||
return unwrapJSONCallResult(this.shim.findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename));
|
||||
}
|
||||
|
||||
Vendored
+2
-1
@@ -17328,7 +17328,8 @@ interface WorkerEventMap extends AbstractWorkerEventMap {
|
||||
/** An interface of the Web Workers API represents a background task that can be easily created and can send messages back to its creator. Creating a worker is as simple as calling the Worker() constructor and specifying a script to be run in the worker thread. */
|
||||
interface Worker extends EventTarget, AbstractWorker {
|
||||
onmessage: ((this: Worker, ev: MessageEvent) => any) | null;
|
||||
postMessage(message: any, transfer?: Transferable[]): void;
|
||||
postMessage(message: any, transfer: Transferable[]): void;
|
||||
postMessage(message: any, options?: PostMessageOptions): void;
|
||||
terminate(): void;
|
||||
addEventListener<K extends keyof WorkerEventMap>(type: K, listener: (this: Worker, ev: WorkerEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
||||
|
||||
Vendored
+2
-4
@@ -513,7 +513,7 @@ interface Boolean {
|
||||
|
||||
interface BooleanConstructor {
|
||||
new(value?: any): Boolean;
|
||||
<T>(value?: T): value is Exclude<T, false | null | undefined | '' | 0>;
|
||||
<T>(value?: T): boolean;
|
||||
readonly prototype: Boolean;
|
||||
}
|
||||
|
||||
@@ -1446,9 +1446,7 @@ type Extract<T, U> = T extends U ? T : never;
|
||||
/**
|
||||
* Construct a type with the properties of T except for those in type K.
|
||||
*/
|
||||
type Omit<T, K extends keyof any> = {
|
||||
[P in Exclude<keyof T, K>]: T[P]
|
||||
};
|
||||
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
|
||||
|
||||
/**
|
||||
* Exclude null and undefined from T
|
||||
|
||||
Vendored
+2
-1
@@ -4231,7 +4231,8 @@ interface WorkerEventMap extends AbstractWorkerEventMap {
|
||||
/** An interface of the Web Workers API represents a background task that can be easily created and can send messages back to its creator. Creating a worker is as simple as calling the Worker() constructor and specifying a script to be run in the worker thread. */
|
||||
interface Worker extends EventTarget, AbstractWorker {
|
||||
onmessage: ((this: Worker, ev: MessageEvent) => any) | null;
|
||||
postMessage(message: any, transfer?: Transferable[]): void;
|
||||
postMessage(message: any, transfer: Transferable[]): void;
|
||||
postMessage(message: any, options?: PostMessageOptions): void;
|
||||
terminate(): void;
|
||||
addEventListener<K extends keyof WorkerEventMap>(type: K, listener: (this: Worker, ev: WorkerEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
||||
|
||||
+22
-1
@@ -130,7 +130,10 @@ namespace ts.server.protocol {
|
||||
GetEditsForFileRename = "getEditsForFileRename",
|
||||
/* @internal */
|
||||
GetEditsForFileRenameFull = "getEditsForFileRename-full",
|
||||
ConfigurePlugin = "configurePlugin"
|
||||
ConfigurePlugin = "configurePlugin",
|
||||
SelectionRange = "selectionRange",
|
||||
/* @internal */
|
||||
SelectionRangeFull = "selectionRange-full",
|
||||
|
||||
// NOTE: If updating this, be sure to also update `allCommandNames` in `harness/unittests/session.ts`.
|
||||
}
|
||||
@@ -1395,6 +1398,24 @@ namespace ts.server.protocol {
|
||||
export interface ConfigurePluginResponse extends Response {
|
||||
}
|
||||
|
||||
export interface SelectionRangeRequest extends FileRequest {
|
||||
command: CommandTypes.SelectionRange;
|
||||
arguments: SelectionRangeRequestArgs;
|
||||
}
|
||||
|
||||
export interface SelectionRangeRequestArgs extends FileRequestArgs {
|
||||
locations: Location[];
|
||||
}
|
||||
|
||||
export interface SelectionRangeResponse extends Response {
|
||||
body?: SelectionRange[];
|
||||
}
|
||||
|
||||
export interface SelectionRange {
|
||||
textSpan: TextSpan;
|
||||
parent?: SelectionRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information found in an "open" request.
|
||||
*/
|
||||
|
||||
+31
-3
@@ -1318,11 +1318,11 @@ namespace ts.server {
|
||||
this.projectService.openClientFileWithNormalizedPath(fileName, fileContent, scriptKind, /*hasMixedContent*/ false, projectRootPath);
|
||||
}
|
||||
|
||||
private getPosition(args: protocol.FileLocationRequestArgs, scriptInfo: ScriptInfo): number {
|
||||
private getPosition(args: protocol.Location & { position?: number }, scriptInfo: ScriptInfo): number {
|
||||
return args.position !== undefined ? args.position : scriptInfo.lineOffsetToPosition(args.line, args.offset);
|
||||
}
|
||||
|
||||
private getPositionInFile(args: protocol.FileLocationRequestArgs, file: NormalizedPath): number {
|
||||
private getPositionInFile(args: protocol.Location & { position?: number }, file: NormalizedPath): number {
|
||||
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file)!;
|
||||
return this.getPosition(args, scriptInfo);
|
||||
}
|
||||
@@ -2059,6 +2059,28 @@ namespace ts.server {
|
||||
this.projectService.configurePlugin(args);
|
||||
}
|
||||
|
||||
private getSmartSelectionRange(args: protocol.SelectionRangeRequestArgs, simplifiedResult: boolean) {
|
||||
const { locations } = args;
|
||||
const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args);
|
||||
const scriptInfo = Debug.assertDefined(this.projectService.getScriptInfo(file));
|
||||
|
||||
return map(locations, location => {
|
||||
const pos = this.getPosition(location, scriptInfo);
|
||||
const selectionRange = languageService.getSmartSelectionRange(file, pos);
|
||||
return simplifiedResult ? this.mapSelectionRange(selectionRange, scriptInfo) : selectionRange;
|
||||
});
|
||||
}
|
||||
|
||||
private mapSelectionRange(selectionRange: SelectionRange, scriptInfo: ScriptInfo): protocol.SelectionRange {
|
||||
const result: protocol.SelectionRange = {
|
||||
textSpan: this.toLocationTextSpan(selectionRange.textSpan, scriptInfo),
|
||||
};
|
||||
if (selectionRange.parent) {
|
||||
result.parent = this.mapSelectionRange(selectionRange.parent, scriptInfo);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
getCanonicalFileName(fileName: string) {
|
||||
const name = this.host.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
|
||||
return normalizePath(name);
|
||||
@@ -2414,7 +2436,13 @@ namespace ts.server {
|
||||
this.configurePlugin(request.arguments);
|
||||
this.doOutput(/*info*/ undefined, CommandNames.ConfigurePlugin, request.seq, /*success*/ true);
|
||||
return this.notRequired();
|
||||
}
|
||||
},
|
||||
[CommandNames.SelectionRange]: (request: protocol.SelectionRangeRequest) => {
|
||||
return this.requiredResponse(this.getSmartSelectionRange(request.arguments, /*simplifiedResult*/ true));
|
||||
},
|
||||
[CommandNames.SelectionRangeFull]: (request: protocol.SelectionRangeRequest) => {
|
||||
return this.requiredResponse(this.getSmartSelectionRange(request.arguments, /*simplifiedResult*/ false));
|
||||
},
|
||||
});
|
||||
|
||||
public addProtocolHandler(command: string, handler: (request: protocol.Request) => HandlerResponse) {
|
||||
|
||||
@@ -15,11 +15,11 @@ namespace ts.codefix {
|
||||
function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number) {
|
||||
const token = getTokenAtPosition(sourceFile, pos);
|
||||
if (!isIdentifier(token)) {
|
||||
return Debug.fail("add-name-to-nameless-parameter operates on identifiers, but got a " + formatSyntaxKind(token.kind));
|
||||
return Debug.fail("add-name-to-nameless-parameter operates on identifiers, but got a " + Debug.formatSyntaxKind(token.kind));
|
||||
}
|
||||
const param = token.parent;
|
||||
if (!isParameter(param)) {
|
||||
return Debug.fail("Tried to add a parameter name to a non-parameter: " + formatSyntaxKind(token.kind));
|
||||
return Debug.fail("Tried to add a parameter name to a non-parameter: " + Debug.formatSyntaxKind(token.kind));
|
||||
}
|
||||
const i = param.parent.parameters.indexOf(param);
|
||||
Debug.assert(!param.type, "Tried to add a parameter name to a parameter that already had one.");
|
||||
|
||||
+101
-23
@@ -1,5 +1,12 @@
|
||||
/* @internal */
|
||||
namespace ts.Completions {
|
||||
export enum SortText {
|
||||
LocationPriority = "0",
|
||||
SuggestedClassMembers = "1",
|
||||
GlobalsOrKeywords = "2",
|
||||
AutoImportSuggestions = "3",
|
||||
JavascriptIdentifiers = "4"
|
||||
}
|
||||
export type Log = (message: string) => void;
|
||||
|
||||
const enum SymbolOriginInfoKind { ThisType, SymbolMemberNoExport, SymbolMemberExport, Export }
|
||||
@@ -22,6 +29,8 @@ namespace ts.Completions {
|
||||
*/
|
||||
type SymbolOriginInfoMap = (SymbolOriginInfo | undefined)[];
|
||||
|
||||
type SymbolSortTextMap = (SortText | undefined)[];
|
||||
|
||||
const enum KeywordCompletionFilters {
|
||||
None, // No keywords
|
||||
All, // Every possible keyword (TODO: This is never appropriate)
|
||||
@@ -78,7 +87,21 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
function completionInfoFromData(sourceFile: SourceFile, typeChecker: TypeChecker, compilerOptions: CompilerOptions, log: Log, completionData: CompletionData, preferences: UserPreferences): CompletionInfo | undefined {
|
||||
const { symbols, completionKind, isInSnippetScope, isNewIdentifierLocation, location, propertyAccessToConvert, keywordFilters, literals, symbolToOriginInfoMap, recommendedCompletion, isJsxInitializer, insideJsDocTagTypeExpression } = completionData;
|
||||
const {
|
||||
symbols,
|
||||
completionKind,
|
||||
isInSnippetScope,
|
||||
isNewIdentifierLocation,
|
||||
location,
|
||||
propertyAccessToConvert,
|
||||
keywordFilters,
|
||||
literals,
|
||||
symbolToOriginInfoMap,
|
||||
recommendedCompletion,
|
||||
isJsxInitializer,
|
||||
insideJsDocTagTypeExpression,
|
||||
symbolToSortTextMap,
|
||||
} = completionData;
|
||||
|
||||
if (location && location.parent && isJsxClosingElement(location.parent)) {
|
||||
// In the TypeScript JSX element, if such element is not defined. When users query for completion at closing tag,
|
||||
@@ -93,7 +116,7 @@ namespace ts.Completions {
|
||||
name: tagName.getFullText(sourceFile) + (hasClosingAngleBracket ? "" : ">"),
|
||||
kind: ScriptElementKind.classElement,
|
||||
kindModifiers: undefined,
|
||||
sortText: "0",
|
||||
sortText: SortText.LocationPriority,
|
||||
};
|
||||
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: false, entries: [entry] };
|
||||
}
|
||||
@@ -101,7 +124,22 @@ namespace ts.Completions {
|
||||
const entries: CompletionEntry[] = [];
|
||||
|
||||
if (isUncheckedFile(sourceFile, compilerOptions)) {
|
||||
const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target!, log, completionKind, preferences, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap);
|
||||
const uniqueNames = getCompletionEntriesFromSymbols(
|
||||
symbols,
|
||||
entries,
|
||||
location,
|
||||
sourceFile,
|
||||
typeChecker,
|
||||
compilerOptions.target!,
|
||||
log,
|
||||
completionKind,
|
||||
preferences,
|
||||
propertyAccessToConvert,
|
||||
isJsxInitializer,
|
||||
recommendedCompletion,
|
||||
symbolToOriginInfoMap,
|
||||
symbolToSortTextMap
|
||||
);
|
||||
getJSCompletionEntries(sourceFile, location!.pos, uniqueNames, compilerOptions.target!, entries); // TODO: GH#18217
|
||||
}
|
||||
else {
|
||||
@@ -109,7 +147,22 @@ namespace ts.Completions {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target!, log, completionKind, preferences, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap);
|
||||
getCompletionEntriesFromSymbols(
|
||||
symbols,
|
||||
entries,
|
||||
location,
|
||||
sourceFile,
|
||||
typeChecker,
|
||||
compilerOptions.target!,
|
||||
log,
|
||||
completionKind,
|
||||
preferences,
|
||||
propertyAccessToConvert,
|
||||
isJsxInitializer,
|
||||
recommendedCompletion,
|
||||
symbolToOriginInfoMap,
|
||||
symbolToSortTextMap
|
||||
);
|
||||
}
|
||||
|
||||
if (keywordFilters !== KeywordCompletionFilters.None) {
|
||||
@@ -160,7 +213,7 @@ namespace ts.Completions {
|
||||
name: realName,
|
||||
kind: ScriptElementKind.warning,
|
||||
kindModifiers: "",
|
||||
sortText: "1"
|
||||
sortText: SortText.JavascriptIdentifiers
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -169,28 +222,23 @@ namespace ts.Completions {
|
||||
const completionNameForLiteral = (literal: string | number | PseudoBigInt) =>
|
||||
typeof literal === "object" ? pseudoBigIntToString(literal) + "n" : JSON.stringify(literal);
|
||||
function createCompletionEntryForLiteral(literal: string | number | PseudoBigInt): CompletionEntry {
|
||||
return { name: completionNameForLiteral(literal), kind: ScriptElementKind.string, kindModifiers: ScriptElementKindModifier.none, sortText: "0" };
|
||||
return { name: completionNameForLiteral(literal), kind: ScriptElementKind.string, kindModifiers: ScriptElementKindModifier.none, sortText: SortText.LocationPriority };
|
||||
}
|
||||
|
||||
function createCompletionEntry(
|
||||
symbol: Symbol,
|
||||
sortText: SortText,
|
||||
location: Node | undefined,
|
||||
sourceFile: SourceFile,
|
||||
typeChecker: TypeChecker,
|
||||
target: ScriptTarget,
|
||||
kind: CompletionKind,
|
||||
name: string,
|
||||
needsConvertPropertyAccess: boolean,
|
||||
origin: SymbolOriginInfo | undefined,
|
||||
recommendedCompletion: Symbol | undefined,
|
||||
propertyAccessToConvert: PropertyAccessExpression | undefined,
|
||||
isJsxInitializer: IsJsxInitializer | undefined,
|
||||
preferences: UserPreferences,
|
||||
): CompletionEntry | undefined {
|
||||
const info = getCompletionEntryDisplayNameForSymbol(symbol, target, origin, kind);
|
||||
if (!info) {
|
||||
return undefined;
|
||||
}
|
||||
const { name, needsConvertPropertyAccess } = info;
|
||||
|
||||
let insertText: string | undefined;
|
||||
let replacementSpan: TextSpan | undefined;
|
||||
if (origin && origin.kind === SymbolOriginInfoKind.ThisType) {
|
||||
@@ -230,7 +278,7 @@ namespace ts.Completions {
|
||||
name,
|
||||
kind: SymbolDisplay.getSymbolKind(typeChecker, symbol, location!), // TODO: GH#18217
|
||||
kindModifiers: SymbolDisplay.getSymbolModifiers(symbol),
|
||||
sortText: "0",
|
||||
sortText,
|
||||
source: getSourceFromOrigin(origin),
|
||||
hasAction: trueOrUndefined(!!origin && originIsExport(origin)),
|
||||
isRecommended: trueOrUndefined(isRecommendedCompletionMatch(symbol, recommendedCompletion, typeChecker)),
|
||||
@@ -266,6 +314,7 @@ namespace ts.Completions {
|
||||
isJsxInitializer?: IsJsxInitializer,
|
||||
recommendedCompletion?: Symbol,
|
||||
symbolToOriginInfoMap?: SymbolOriginInfoMap,
|
||||
symbolToSortTextMap?: SymbolSortTextMap,
|
||||
): Map<true> {
|
||||
const start = timestamp();
|
||||
// Tracks unique names.
|
||||
@@ -275,13 +324,30 @@ namespace ts.Completions {
|
||||
const uniques = createMap<true>();
|
||||
for (const symbol of symbols) {
|
||||
const origin = symbolToOriginInfoMap ? symbolToOriginInfoMap[getSymbolId(symbol)] : undefined;
|
||||
const entry = createCompletionEntry(symbol, location, sourceFile, typeChecker, target, kind, origin, recommendedCompletion, propertyAccessToConvert, isJsxInitializer, preferences);
|
||||
if (!entry) {
|
||||
const info = getCompletionEntryDisplayNameForSymbol(symbol, target, origin, kind);
|
||||
if (!info) {
|
||||
continue;
|
||||
}
|
||||
const { name, needsConvertPropertyAccess } = info;
|
||||
if (uniques.has(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { name } = entry;
|
||||
if (uniques.has(name)) {
|
||||
const entry = createCompletionEntry(
|
||||
symbol,
|
||||
symbolToSortTextMap && symbolToSortTextMap[getSymbolId(symbol)] || SortText.LocationPriority,
|
||||
location,
|
||||
sourceFile,
|
||||
typeChecker,
|
||||
name,
|
||||
needsConvertPropertyAccess,
|
||||
origin,
|
||||
recommendedCompletion,
|
||||
propertyAccessToConvert,
|
||||
isJsxInitializer,
|
||||
preferences
|
||||
);
|
||||
if (!entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -321,7 +387,7 @@ namespace ts.Completions {
|
||||
name,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
kind: ScriptElementKind.label,
|
||||
sortText: "0"
|
||||
sortText: SortText.LocationPriority
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -358,7 +424,7 @@ namespace ts.Completions {
|
||||
// We don't need to perform character checks here because we're only comparing the
|
||||
// name against 'entryName' (which is known to be good), not building a new
|
||||
// completion entry.
|
||||
return firstDefined<Symbol, SymbolCompletion>(symbols, (symbol): SymbolCompletion | undefined => { // TODO: Shouldn't need return type annotation (GH#12632)
|
||||
return firstDefined(symbols, (symbol): SymbolCompletion | undefined => {
|
||||
const origin = symbolToOriginInfoMap[getSymbolId(symbol)];
|
||||
const info = getCompletionEntryDisplayNameForSymbol(symbol, compilerOptions.target!, origin, completionKind);
|
||||
return info && info.name === entryId.name && getSourceFromOrigin(origin) === entryId.source
|
||||
@@ -512,6 +578,7 @@ namespace ts.Completions {
|
||||
readonly previousToken: Node | undefined;
|
||||
readonly isJsxInitializer: IsJsxInitializer;
|
||||
readonly insideJsDocTagTypeExpression: boolean;
|
||||
readonly symbolToSortTextMap: SymbolSortTextMap;
|
||||
}
|
||||
type Request = { readonly kind: CompletionDataKind.JsDocTagName | CompletionDataKind.JsDocTag } | { readonly kind: CompletionDataKind.JsDocParameterName, tag: JSDocParameterTag };
|
||||
|
||||
@@ -804,6 +871,7 @@ namespace ts.Completions {
|
||||
let keywordFilters = KeywordCompletionFilters.None;
|
||||
let symbols: Symbol[] = [];
|
||||
const symbolToOriginInfoMap: SymbolOriginInfoMap = [];
|
||||
const symbolToSortTextMap: SymbolSortTextMap = [];
|
||||
|
||||
if (isRightOfDot) {
|
||||
getTypeScriptMemberSymbols();
|
||||
@@ -853,7 +921,8 @@ namespace ts.Completions {
|
||||
recommendedCompletion,
|
||||
previousToken,
|
||||
isJsxInitializer,
|
||||
insideJsDocTagTypeExpression
|
||||
insideJsDocTagTypeExpression,
|
||||
symbolToSortTextMap
|
||||
};
|
||||
|
||||
type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;
|
||||
@@ -1057,6 +1126,12 @@ namespace ts.Completions {
|
||||
const symbolMeanings = (isTypeOnly ? SymbolFlags.None : SymbolFlags.Value) | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias;
|
||||
|
||||
symbols = Debug.assertEachDefined(typeChecker.getSymbolsInScope(scopeNode, symbolMeanings), "getSymbolsInScope() should all be defined");
|
||||
for (const symbol of symbols) {
|
||||
if (!typeChecker.isArgumentsSymbol(symbol) &&
|
||||
!some(symbol.declarations, d => d.getSourceFile() === sourceFile)) {
|
||||
symbolToSortTextMap[getSymbolId(symbol)] = SortText.GlobalsOrKeywords;
|
||||
}
|
||||
}
|
||||
|
||||
// Need to insert 'this.' before properties of `this` type, so only do that if `includeInsertTextCompletions`
|
||||
if (preferences.includeCompletionsWithInsertText && scopeNode.kind !== SyntaxKind.SourceFile) {
|
||||
@@ -1065,6 +1140,7 @@ namespace ts.Completions {
|
||||
for (const symbol of getPropertiesForCompletion(thisType, typeChecker)) {
|
||||
symbolToOriginInfoMap[getSymbolId(symbol)] = { kind: SymbolOriginInfoKind.ThisType };
|
||||
symbols.push(symbol);
|
||||
symbolToSortTextMap[getSymbolId(symbol)] = SortText.SuggestedClassMembers;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1198,6 +1274,7 @@ namespace ts.Completions {
|
||||
// So in `declare namespace foo {} declare module "foo" { export = foo; }`, there will just be the global completion for `foo`.
|
||||
some(resolvedModuleSymbol.declarations, d => !!d.getSourceFile().externalModuleIndicator)) {
|
||||
symbols.push(resolvedModuleSymbol);
|
||||
symbolToSortTextMap[getSymbolId(resolvedModuleSymbol)] = SortText.AutoImportSuggestions;
|
||||
symbolToOriginInfoMap[getSymbolId(resolvedModuleSymbol)] = { kind: SymbolOriginInfoKind.Export, moduleSymbol, isDefaultExport: false };
|
||||
}
|
||||
|
||||
@@ -1223,6 +1300,7 @@ namespace ts.Completions {
|
||||
const origin: SymbolOriginInfoExport = { kind: SymbolOriginInfoKind.Export, moduleSymbol, isDefaultExport };
|
||||
if (detailsEntryId || stringContainsCharactersInOrder(getSymbolName(symbol, origin, target).toLowerCase(), tokenTextLowerCase)) {
|
||||
symbols.push(symbol);
|
||||
symbolToSortTextMap[getSymbolId(symbol)] = SortText.AutoImportSuggestions;
|
||||
symbolToOriginInfoMap[getSymbolId(symbol)] = origin;
|
||||
}
|
||||
}
|
||||
@@ -1944,7 +2022,7 @@ namespace ts.Completions {
|
||||
name: tokenToString(i)!,
|
||||
kind: ScriptElementKind.keyword,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
sortText: "0"
|
||||
sortText: SortText.GlobalsOrKeywords
|
||||
});
|
||||
}
|
||||
return res;
|
||||
|
||||
@@ -635,7 +635,7 @@ namespace ts.FindAllReferences.Core {
|
||||
// Ignore UMD module and global merge
|
||||
if (symbol.flags & SymbolFlags.Transient) return undefined;
|
||||
// Assertions for GH#21814. We should be handling SourceFile symbols in `getReferencedSymbolsForModule` instead of getting here.
|
||||
Debug.fail(`Unexpected symbol at ${Debug.showSyntaxKind(node)}: ${Debug.showSymbol(symbol)}`);
|
||||
Debug.fail(`Unexpected symbol at ${Debug.formatSyntaxKind(node.kind)}: ${Debug.formatSymbol(symbol)}`);
|
||||
}
|
||||
return isTypeLiteralNode(decl.parent) && isUnionTypeNode(decl.parent.parent)
|
||||
? checker.getPropertyOfType(checker.getTypeFromTypeNode(decl.parent.parent), symbol.name)
|
||||
|
||||
@@ -133,7 +133,7 @@ namespace ts.FindAllReferences {
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug.assertNever(direct, `Unexpected import kind: ${Debug.showSyntaxKind(direct)}`);
|
||||
Debug.failBadSyntaxKind(direct, "Unexpected import kind.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -515,7 +515,7 @@ namespace ts.FindAllReferences {
|
||||
const sym = useLhsSymbol ? checker.getSymbolAtLocation(cast(node.left, isPropertyAccessExpression).name) : symbol;
|
||||
// Better detection for GH#20803
|
||||
if (sym && !(checker.getMergedSymbol(sym.parent!).flags & SymbolFlags.Module)) {
|
||||
Debug.fail(`Special property assignment kind does not have a module as its parent. Assignment is ${Debug.showSymbol(sym)}, parent is ${Debug.showSymbol(sym.parent!)}`);
|
||||
Debug.fail(`Special property assignment kind does not have a module as its parent. Assignment is ${Debug.formatSymbol(sym)}, parent is ${Debug.formatSymbol(sym.parent!)}`);
|
||||
}
|
||||
return sym && exportInfo(sym, kind);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
/* @internal */
|
||||
namespace ts.refactor {
|
||||
const refactorName = "Extract type";
|
||||
const extractToTypeAlias = "Extract to type alias";
|
||||
const extractToTypeDef = "Extract to typedef";
|
||||
registerRefactor(refactorName, {
|
||||
getAvailableActions(context): ReadonlyArray<ApplicableRefactorInfo> {
|
||||
const info = getRangeToExtract(context);
|
||||
if (!info) return emptyArray;
|
||||
|
||||
return [{
|
||||
name: refactorName,
|
||||
description: getLocaleSpecificMessage(Diagnostics.Extract_type),
|
||||
actions: [info.isJS ? {
|
||||
name: extractToTypeDef, description: getLocaleSpecificMessage(Diagnostics.Extract_to_typedef)
|
||||
} : {
|
||||
name: extractToTypeAlias, description: getLocaleSpecificMessage(Diagnostics.Extract_to_type_alias)
|
||||
}]
|
||||
}];
|
||||
},
|
||||
getEditsForAction(context, actionName): RefactorEditInfo {
|
||||
Debug.assert(actionName === extractToTypeAlias || actionName === extractToTypeDef);
|
||||
const { file } = context;
|
||||
const info = Debug.assertDefined(getRangeToExtract(context));
|
||||
Debug.assert(actionName === extractToTypeAlias && !info.isJS || actionName === extractToTypeDef && info.isJS);
|
||||
|
||||
const name = getUniqueName("NewType", file);
|
||||
const edits = textChanges.ChangeTracker.with(context, changes => info.isJS ?
|
||||
doTypedefChange(changes, file, name, info.firstStatement, info.selection, info.typeParameters) :
|
||||
doTypeAliasChange(changes, file, name, info.firstStatement, info.selection, info.typeParameters));
|
||||
|
||||
const renameFilename = file.fileName;
|
||||
const renameLocation = getRenameLocation(edits, renameFilename, name, /*preferLastLocation*/ false);
|
||||
return { edits, renameFilename, renameLocation };
|
||||
}
|
||||
});
|
||||
|
||||
interface Info { isJS: boolean; selection: TypeNode; firstStatement: Statement; typeParameters: ReadonlyArray<TypeParameterDeclaration>; }
|
||||
|
||||
function getRangeToExtract(context: RefactorContext): Info | undefined {
|
||||
const { file, startPosition } = context;
|
||||
const isJS = isSourceFileJS(file);
|
||||
const current = getTokenAtPosition(file, startPosition);
|
||||
const range = createTextRangeFromSpan(getRefactorContextSpan(context));
|
||||
|
||||
const selection = findAncestor(current, (node => node.parent && rangeContainsSkipTrivia(range, node, file) && !rangeContainsSkipTrivia(range, node.parent, file)));
|
||||
if (!selection || !isTypeNode(selection)) return undefined;
|
||||
|
||||
const checker = context.program.getTypeChecker();
|
||||
const firstStatement = Debug.assertDefined(isJS ? findAncestor(selection, isStatementAndHasJSDoc) : findAncestor(selection, isStatement));
|
||||
const typeParameters = collectTypeParameters(checker, selection, firstStatement, file);
|
||||
if (!typeParameters) return undefined;
|
||||
|
||||
return { isJS, selection, firstStatement, typeParameters };
|
||||
}
|
||||
|
||||
function isStatementAndHasJSDoc(n: Node): n is (Statement & HasJSDoc) {
|
||||
return isStatement(n) && hasJSDocNodes(n);
|
||||
}
|
||||
|
||||
function rangeContainsSkipTrivia(r1: TextRange, node: Node, file: SourceFile): boolean {
|
||||
return rangeContainsStartEnd(r1, skipTrivia(file.text, node.pos), node.end);
|
||||
}
|
||||
|
||||
function collectTypeParameters(checker: TypeChecker, selection: TypeNode, statement: Statement, file: SourceFile): TypeParameterDeclaration[] | undefined {
|
||||
const result: TypeParameterDeclaration[] = [];
|
||||
return visitor(selection) ? undefined : result;
|
||||
|
||||
function visitor(node: Node): true | undefined {
|
||||
if (isTypeReferenceNode(node)) {
|
||||
if (isIdentifier(node.typeName)) {
|
||||
const symbol = checker.resolveName(node.typeName.text, node.typeName, SymbolFlags.TypeParameter, /* excludeGlobals */ true);
|
||||
if (symbol) {
|
||||
const declaration = cast(first(symbol.declarations), isTypeParameterDeclaration);
|
||||
if (rangeContainsSkipTrivia(statement, declaration, file) && !rangeContainsSkipTrivia(selection, declaration, file)) {
|
||||
result.push(declaration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isInferTypeNode(node)) {
|
||||
const conditionalTypeNode = findAncestor(node, n => isConditionalTypeNode(n) && rangeContainsSkipTrivia(n.extendsType, node, file));
|
||||
if (!conditionalTypeNode || !rangeContainsSkipTrivia(selection, conditionalTypeNode, file)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if ((isTypePredicateNode(node) || isThisTypeNode(node))) {
|
||||
const functionLikeNode = findAncestor(node.parent, isFunctionLike);
|
||||
if (functionLikeNode && functionLikeNode.type && rangeContainsSkipTrivia(functionLikeNode.type, node, file) && !rangeContainsSkipTrivia(selection, functionLikeNode, file)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (isTypeQueryNode(node)) {
|
||||
if (isIdentifier(node.exprName)) {
|
||||
const symbol = checker.resolveName(node.exprName.text, node.exprName, SymbolFlags.Value, /* excludeGlobals */ false);
|
||||
if (symbol && rangeContainsSkipTrivia(statement, symbol.valueDeclaration, file) && !rangeContainsSkipTrivia(selection, symbol.valueDeclaration, file)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (isThisIdentifier(node.exprName.left) && !rangeContainsSkipTrivia(selection, node.parent, file)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return forEachChild(node, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
function doTypeAliasChange(changes: textChanges.ChangeTracker, file: SourceFile, name: string, firstStatement: Statement, selection: TypeNode, typeParameters: ReadonlyArray<TypeParameterDeclaration>) {
|
||||
const newTypeNode = createTypeAliasDeclaration(
|
||||
/* decorators */ undefined,
|
||||
/* modifiers */ undefined,
|
||||
name,
|
||||
typeParameters.map(id => updateTypeParameterDeclaration(id, id.name, id.constraint, /* defaultType */ undefined)),
|
||||
selection
|
||||
);
|
||||
changes.insertNodeBefore(file, firstStatement, newTypeNode, /* blankLineBetween */ true);
|
||||
changes.replaceNode(file, selection, createTypeReferenceNode(name, typeParameters.map(id => createTypeReferenceNode(id.name, /* typeArguments */ undefined))));
|
||||
}
|
||||
|
||||
function doTypedefChange(changes: textChanges.ChangeTracker, file: SourceFile, name: string, firstStatement: Statement, selection: TypeNode, typeParameters: ReadonlyArray<TypeParameterDeclaration>) {
|
||||
const node = <JSDocTypedefTag>createNode(SyntaxKind.JSDocTypedefTag);
|
||||
node.tagName = createIdentifier("typedef"); // TODO: jsdoc factory https://github.com/Microsoft/TypeScript/pull/29539
|
||||
node.fullName = createIdentifier(name);
|
||||
node.name = node.fullName;
|
||||
node.typeExpression = createJSDocTypeExpression(selection);
|
||||
|
||||
const templates: JSDocTemplateTag[] = [];
|
||||
forEach(typeParameters, typeParameter => {
|
||||
const constraint = getEffectiveConstraintOfTypeParameter(typeParameter);
|
||||
|
||||
const template = <JSDocTemplateTag>createNode(SyntaxKind.JSDocTemplateTag);
|
||||
template.tagName = createIdentifier("template");
|
||||
template.constraint = constraint && cast(constraint, isJSDocTypeExpression);
|
||||
|
||||
const parameter = <TypeParameterDeclaration>createNode(SyntaxKind.TypeParameter);
|
||||
parameter.name = typeParameter.name;
|
||||
template.typeParameters = createNodeArray([parameter]);
|
||||
|
||||
templates.push(template);
|
||||
});
|
||||
|
||||
changes.insertNodeBefore(file, firstStatement, createJSDocComment(/* comment */ undefined, createNodeArray(concatenate<JSDocTag>(templates, [node]))), /* blankLineBetween */ true);
|
||||
changes.replaceNode(file, selection, createTypeReferenceNode(name, typeParameters.map(id => createTypeReferenceNode(id.name, /* typeArguments */ undefined))));
|
||||
}
|
||||
}
|
||||
@@ -173,7 +173,7 @@ namespace ts {
|
||||
const textPos = scanner.getTextPos();
|
||||
if (textPos <= end) {
|
||||
if (token === SyntaxKind.Identifier) {
|
||||
Debug.fail(`Did not expect ${Debug.showSyntaxKind(parent)} to have an Identifier in its trivia`);
|
||||
Debug.fail(`Did not expect ${Debug.formatSyntaxKind(parent.kind)} to have an Identifier in its trivia`);
|
||||
}
|
||||
nodes.push(createNode(token, pos, textPos, parent));
|
||||
}
|
||||
@@ -1467,33 +1467,41 @@ namespace ts {
|
||||
}
|
||||
|
||||
const typeChecker = program.getTypeChecker();
|
||||
const symbol = getSymbolAtLocationForQuickInfo(node, typeChecker);
|
||||
const nodeForQuickInfo = getNodeForQuickInfo(node);
|
||||
const symbol = getSymbolAtLocationForQuickInfo(nodeForQuickInfo, typeChecker);
|
||||
|
||||
if (!symbol || typeChecker.isUnknownSymbol(symbol)) {
|
||||
const type = shouldGetType(sourceFile, node, position) ? typeChecker.getTypeAtLocation(node) : undefined;
|
||||
const type = shouldGetType(sourceFile, nodeForQuickInfo, position) ? typeChecker.getTypeAtLocation(nodeForQuickInfo) : undefined;
|
||||
return type && {
|
||||
kind: ScriptElementKind.unknown,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
textSpan: createTextSpanFromNode(node, sourceFile),
|
||||
displayParts: typeChecker.runWithCancellationToken(cancellationToken, typeChecker => typeToDisplayParts(typeChecker, type, getContainerNode(node))),
|
||||
textSpan: createTextSpanFromNode(nodeForQuickInfo, sourceFile),
|
||||
displayParts: typeChecker.runWithCancellationToken(cancellationToken, typeChecker => typeToDisplayParts(typeChecker, type, getContainerNode(nodeForQuickInfo))),
|
||||
documentation: type.symbol ? type.symbol.getDocumentationComment(typeChecker) : undefined,
|
||||
tags: type.symbol ? type.symbol.getJsDocTags() : undefined
|
||||
};
|
||||
}
|
||||
|
||||
const { symbolKind, displayParts, documentation, tags } = typeChecker.runWithCancellationToken(cancellationToken, typeChecker =>
|
||||
SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, getContainerNode(node), node)
|
||||
SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, getContainerNode(nodeForQuickInfo), nodeForQuickInfo)
|
||||
);
|
||||
return {
|
||||
kind: symbolKind,
|
||||
kindModifiers: SymbolDisplay.getSymbolModifiers(symbol),
|
||||
textSpan: createTextSpanFromNode(node, sourceFile),
|
||||
textSpan: createTextSpanFromNode(nodeForQuickInfo, sourceFile),
|
||||
displayParts,
|
||||
documentation,
|
||||
tags,
|
||||
};
|
||||
}
|
||||
|
||||
function getNodeForQuickInfo(node: Node): Node {
|
||||
if (isNewExpression(node.parent) && node.pos === node.parent.pos) {
|
||||
return node.parent.expression;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
function shouldGetType(sourceFile: SourceFile, node: Node, position: number): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
@@ -2072,6 +2080,10 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
function getSmartSelectionRange(fileName: string, position: number): SelectionRange {
|
||||
return SmartSelectionRange.getSmartSelectionRange(position, syntaxTreeCache.getCurrentSourceFile(fileName));
|
||||
}
|
||||
|
||||
function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions): ApplicableRefactorInfo[] {
|
||||
synchronizeHostData();
|
||||
const file = getValidSourceFile(fileName);
|
||||
@@ -2119,6 +2131,7 @@ namespace ts {
|
||||
getBreakpointStatementAtPosition,
|
||||
getNavigateToItems,
|
||||
getRenameInfo,
|
||||
getSmartSelectionRange,
|
||||
findRenameLocations,
|
||||
getNavigationBarItems,
|
||||
getNavigationTree,
|
||||
|
||||
@@ -165,6 +165,7 @@ namespace ts {
|
||||
* { canRename: boolean, localizedErrorMessage: string, displayName: string, fullDisplayName: string, kind: string, kindModifiers: string, triggerSpan: { start; length } }
|
||||
*/
|
||||
getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): string;
|
||||
getSmartSelectionRange(fileName: string, position: number): string;
|
||||
|
||||
/**
|
||||
* Returns a JSON-encoded value of the type:
|
||||
@@ -838,6 +839,13 @@ namespace ts {
|
||||
);
|
||||
}
|
||||
|
||||
public getSmartSelectionRange(fileName: string, position: number): string {
|
||||
return this.forwardJSONCall(
|
||||
`getSmartSelectionRange('${fileName}', ${position})`,
|
||||
() => this.languageService.getSmartSelectionRange(fileName, position)
|
||||
);
|
||||
}
|
||||
|
||||
public findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): string {
|
||||
return this.forwardJSONCall(
|
||||
`findRenameLocations('${fileName}', ${position}, ${findInStrings}, ${findInComments}, ${providePrefixAndSuffixTextForRename})`,
|
||||
|
||||
@@ -455,7 +455,7 @@ namespace ts.SignatureHelp {
|
||||
for (let n = node; !isSourceFile(n) && (isManuallyInvoked || !isBlock(n)); n = n.parent) {
|
||||
// If the node is not a subspan of its parent, this is a big problem.
|
||||
// There have been crashes that might be caused by this violation.
|
||||
Debug.assert(rangeContainsRange(n.parent, n), "Not a subspan", () => `Child: ${Debug.showSyntaxKind(n)}, parent: ${Debug.showSyntaxKind(n.parent)}`);
|
||||
Debug.assert(rangeContainsRange(n.parent, n), "Not a subspan", () => `Child: ${Debug.formatSyntaxKind(n.kind)}, parent: ${Debug.formatSyntaxKind(n.parent.kind)}`);
|
||||
const argumentInfo = getImmediatelyContainingArgumentOrContextualParameterInfo(n, position, sourceFile, checker);
|
||||
if (argumentInfo) {
|
||||
return argumentInfo;
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
/* @internal */
|
||||
namespace ts.SmartSelectionRange {
|
||||
export function getSmartSelectionRange(pos: number, sourceFile: SourceFile): SelectionRange {
|
||||
let selectionRange: SelectionRange = {
|
||||
textSpan: createTextSpanFromBounds(sourceFile.getFullStart(), sourceFile.getEnd())
|
||||
};
|
||||
|
||||
let parentNode: Node = sourceFile;
|
||||
outer: while (true) {
|
||||
const children = getSelectionChildren(parentNode);
|
||||
if (!children.length) break;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const prevNode: Node | undefined = children[i - 1];
|
||||
const node: Node = children[i];
|
||||
const nextNode: Node | undefined = children[i + 1];
|
||||
if (node.getStart(sourceFile) > pos) {
|
||||
break outer;
|
||||
}
|
||||
|
||||
if (positionShouldSnapToNode(pos, node, nextNode)) {
|
||||
// 1. Blocks are effectively redundant with SyntaxLists.
|
||||
// 2. TemplateSpans, along with the SyntaxLists containing them, are a somewhat unintuitive grouping
|
||||
// of things that should be considered independently.
|
||||
// 3. A VariableStatement’s children are just a VaraiableDeclarationList and a semicolon.
|
||||
// 4. A lone VariableDeclaration in a VaraibleDeclaration feels redundant with the VariableStatement.
|
||||
//
|
||||
// Dive in without pushing a selection range.
|
||||
if (isBlock(node)
|
||||
|| isTemplateSpan(node) || isTemplateHead(node)
|
||||
|| prevNode && isTemplateHead(prevNode)
|
||||
|| isVariableDeclarationList(node) && isVariableStatement(parentNode)
|
||||
|| isSyntaxList(node) && isVariableDeclarationList(parentNode)
|
||||
|| isVariableDeclaration(node) && isSyntaxList(parentNode) && children.length === 1) {
|
||||
parentNode = node;
|
||||
break;
|
||||
}
|
||||
|
||||
// Synthesize a stop for '${ ... }' since '${' and '}' actually belong to siblings.
|
||||
if (isTemplateSpan(parentNode) && nextNode && isTemplateMiddleOrTemplateTail(nextNode)) {
|
||||
const start = node.getFullStart() - "${".length;
|
||||
const end = nextNode.getStart() + "}".length;
|
||||
pushSelectionRange(start, end);
|
||||
}
|
||||
|
||||
// Blocks with braces, brackets, parens, or JSX tags on separate lines should be
|
||||
// selected from open to close, including whitespace but not including the braces/etc. themselves.
|
||||
const isBetweenMultiLineBookends = isSyntaxList(node)
|
||||
&& isListOpener(prevNode)
|
||||
&& isListCloser(nextNode)
|
||||
&& !positionsAreOnSameLine(prevNode.getStart(), nextNode.getStart(), sourceFile);
|
||||
const jsDocCommentStart = hasJSDocNodes(node) && node.jsDoc![0].getStart();
|
||||
const start = isBetweenMultiLineBookends ? prevNode.getEnd() : node.getStart();
|
||||
const end = isBetweenMultiLineBookends ? nextNode.getStart() : node.getEnd();
|
||||
if (isNumber(jsDocCommentStart)) {
|
||||
pushSelectionRange(jsDocCommentStart, end);
|
||||
}
|
||||
pushSelectionRange(start, end);
|
||||
|
||||
// String literals should have a stop both inside and outside their quotes.
|
||||
if (isStringLiteral(node) || isTemplateLiteral(node)) {
|
||||
pushSelectionRange(start + 1, end - 1);
|
||||
}
|
||||
|
||||
parentNode = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return selectionRange;
|
||||
|
||||
function pushSelectionRange(start: number, end: number): void {
|
||||
// Skip empty ranges
|
||||
if (start !== end) {
|
||||
// Skip ranges that are identical to the parent
|
||||
const textSpan = createTextSpanFromBounds(start, end);
|
||||
if (!selectionRange || !textSpansEqual(textSpan, selectionRange.textSpan)) {
|
||||
selectionRange = { textSpan, ...selectionRange && { parent: selectionRange } };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like `ts.positionBelongsToNode`, except positions immediately after nodes
|
||||
* count too, unless that position belongs to the next node. In effect, makes
|
||||
* selections able to snap to preceding tokens when the cursor is on the tail
|
||||
* end of them with only whitespace ahead.
|
||||
* @param pos The position to check.
|
||||
* @param node The candidate node to snap to.
|
||||
* @param nextNode The next sibling node in the tree.
|
||||
* @param sourceFile The source file containing the nodes.
|
||||
*/
|
||||
function positionShouldSnapToNode(pos: number, node: Node, nextNode: Node | undefined) {
|
||||
// Can’t use 'ts.positionBelongsToNode()' here because it cleverly accounts
|
||||
// for missing nodes, which can’t really be considered when deciding what
|
||||
// to select.
|
||||
Debug.assert(node.pos <= pos);
|
||||
if (pos < node.end) {
|
||||
return true;
|
||||
}
|
||||
const nodeEnd = node.getEnd();
|
||||
const nextNodeStart = nextNode && nextNode.getStart();
|
||||
if (nodeEnd === pos) {
|
||||
return pos !== nextNodeStart;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const isImport = or(isImportDeclaration, isImportEqualsDeclaration);
|
||||
|
||||
/**
|
||||
* Gets the children of a node to be considered for selection ranging,
|
||||
* transforming them into an artificial tree according to their intuitive
|
||||
* grouping where no grouping actually exists in the parse tree. For example,
|
||||
* top-level imports are grouped into their own SyntaxList so they can be
|
||||
* selected all together, even though in the AST they’re just siblings of each
|
||||
* other as well as of other top-level statements and declarations.
|
||||
*/
|
||||
function getSelectionChildren(node: Node): ReadonlyArray<Node> {
|
||||
// Group top-level imports
|
||||
if (isSourceFile(node)) {
|
||||
return groupChildren(node.getChildAt(0).getChildren(), isImport);
|
||||
}
|
||||
|
||||
// Mapped types _look_ like ObjectTypes with a single member,
|
||||
// but in fact don’t contain a SyntaxList or a node containing
|
||||
// the “key/value” pair like ObjectTypes do, but it seems intuitive
|
||||
// that the selection would snap to those points. The philosophy
|
||||
// of choosing a selection range is not so much about what the
|
||||
// syntax currently _is_ as what the syntax might easily become
|
||||
// if the user is making a selection; e.g., we synthesize a selection
|
||||
// around the “key/value” pair not because there’s a node there, but
|
||||
// because it allows the mapped type to become an object type with a
|
||||
// few keystrokes.
|
||||
if (isMappedTypeNode(node)) {
|
||||
const [openBraceToken, ...children] = node.getChildren();
|
||||
const closeBraceToken = Debug.assertDefined(children.pop());
|
||||
Debug.assertEqual(openBraceToken.kind, SyntaxKind.OpenBraceToken);
|
||||
Debug.assertEqual(closeBraceToken.kind, SyntaxKind.CloseBraceToken);
|
||||
// Group `-/+readonly` and `-/+?`
|
||||
const groupedWithPlusMinusTokens = groupChildren(children, child =>
|
||||
child === node.readonlyToken || child.kind === SyntaxKind.ReadonlyKeyword ||
|
||||
child === node.questionToken || child.kind === SyntaxKind.QuestionToken);
|
||||
// Group type parameter with surrounding brackets
|
||||
const groupedWithBrackets = groupChildren(groupedWithPlusMinusTokens, ({ kind }) =>
|
||||
kind === SyntaxKind.OpenBracketToken ||
|
||||
kind === SyntaxKind.TypeParameter ||
|
||||
kind === SyntaxKind.CloseBracketToken
|
||||
);
|
||||
return [
|
||||
openBraceToken,
|
||||
// Pivot on `:`
|
||||
createSyntaxList(splitChildren(groupedWithBrackets, ({ kind }) => kind === SyntaxKind.ColonToken)),
|
||||
closeBraceToken,
|
||||
];
|
||||
}
|
||||
|
||||
// Group modifiers and property name, then pivot on `:`.
|
||||
if (isPropertySignature(node)) {
|
||||
const children = groupChildren(node.getChildren(), child =>
|
||||
child === node.name || contains(node.modifiers, child));
|
||||
return splitChildren(children, ({ kind }) => kind === SyntaxKind.ColonToken);
|
||||
}
|
||||
|
||||
// Group the parameter name with its `...`, then that group with its `?`, then pivot on `=`.
|
||||
if (isParameter(node)) {
|
||||
const groupedDotDotDotAndName = groupChildren(node.getChildren(), child =>
|
||||
child === node.dotDotDotToken || child === node.name);
|
||||
const groupedWithQuestionToken = groupChildren(groupedDotDotDotAndName, child =>
|
||||
child === groupedDotDotDotAndName[0] || child === node.questionToken);
|
||||
return splitChildren(groupedWithQuestionToken, ({ kind }) => kind === SyntaxKind.EqualsToken);
|
||||
}
|
||||
|
||||
// Pivot on '='
|
||||
if (isBindingElement(node)) {
|
||||
return splitChildren(node.getChildren(), ({ kind }) => kind === SyntaxKind.EqualsToken);
|
||||
}
|
||||
|
||||
return node.getChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups sibling nodes together into their own SyntaxList if they
|
||||
* a) are adjacent, AND b) match a predicate function.
|
||||
*/
|
||||
function groupChildren(children: Node[], groupOn: (child: Node) => boolean): Node[] {
|
||||
const result: Node[] = [];
|
||||
let group: Node[] | undefined;
|
||||
for (const child of children) {
|
||||
if (groupOn(child)) {
|
||||
group = group || [];
|
||||
group.push(child);
|
||||
}
|
||||
else {
|
||||
if (group) {
|
||||
result.push(createSyntaxList(group));
|
||||
group = undefined;
|
||||
}
|
||||
result.push(child);
|
||||
}
|
||||
}
|
||||
if (group) {
|
||||
result.push(createSyntaxList(group));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits sibling nodes into up to four partitions:
|
||||
* 1) everything left of the first node matched by `pivotOn`,
|
||||
* 2) the first node matched by `pivotOn`,
|
||||
* 3) everything right of the first node matched by `pivotOn`,
|
||||
* 4) a trailing semicolon, if `separateTrailingSemicolon` is enabled.
|
||||
* The left and right groups, if not empty, will each be grouped into their own containing SyntaxList.
|
||||
* @param children The sibling nodes to split.
|
||||
* @param pivotOn The predicate function to match the node to be the pivot. The first node that matches
|
||||
* the predicate will be used; any others that may match will be included into the right-hand group.
|
||||
* @param separateTrailingSemicolon If the last token is a semicolon, it will be returned as a separate
|
||||
* child rather than be included in the right-hand group.
|
||||
*/
|
||||
function splitChildren(children: Node[], pivotOn: (child: Node) => boolean, separateTrailingSemicolon = true): Node[] {
|
||||
if (children.length < 2) {
|
||||
return children;
|
||||
}
|
||||
const splitTokenIndex = findIndex(children, pivotOn);
|
||||
if (splitTokenIndex === -1) {
|
||||
return children;
|
||||
}
|
||||
const leftChildren = children.slice(0, splitTokenIndex);
|
||||
const splitToken = children[splitTokenIndex];
|
||||
const lastToken = last(children);
|
||||
const separateLastToken = separateTrailingSemicolon && lastToken.kind === SyntaxKind.SemicolonToken;
|
||||
const rightChildren = children.slice(splitTokenIndex + 1, separateLastToken ? children.length - 1 : undefined);
|
||||
const result = compact([
|
||||
leftChildren.length ? createSyntaxList(leftChildren) : undefined,
|
||||
splitToken,
|
||||
rightChildren.length ? createSyntaxList(rightChildren) : undefined,
|
||||
]);
|
||||
return separateLastToken ? result.concat(lastToken) : result;
|
||||
}
|
||||
|
||||
function createSyntaxList(children: Node[]): SyntaxList {
|
||||
Debug.assertGreaterThanOrEqual(children.length, 1);
|
||||
const syntaxList = createNode(SyntaxKind.SyntaxList, children[0].pos, last(children).end) as SyntaxList;
|
||||
syntaxList._children = children;
|
||||
return syntaxList;
|
||||
}
|
||||
|
||||
function isListOpener(token: Node | undefined): token is Node {
|
||||
const kind = token && token.kind;
|
||||
return kind === SyntaxKind.OpenBraceToken
|
||||
|| kind === SyntaxKind.OpenBracketToken
|
||||
|| kind === SyntaxKind.OpenParenToken
|
||||
|| kind === SyntaxKind.JsxOpeningElement;
|
||||
}
|
||||
|
||||
function isListCloser(token: Node | undefined): token is Node {
|
||||
const kind = token && token.kind;
|
||||
return kind === SyntaxKind.CloseBraceToken
|
||||
|| kind === SyntaxKind.CloseBracketToken
|
||||
|| kind === SyntaxKind.CloseParenToken
|
||||
|| kind === SyntaxKind.JsxClosingElement;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,17 @@ namespace ts.Completions.StringCompletions {
|
||||
return convertPathCompletions(completion.paths);
|
||||
case StringLiteralCompletionKind.Properties: {
|
||||
const entries: CompletionEntry[] = [];
|
||||
getCompletionEntriesFromSymbols(completion.symbols, entries, sourceFile, sourceFile, checker, ScriptTarget.ESNext, log, CompletionKind.String, preferences); // Target will not be used, so arbitrary
|
||||
getCompletionEntriesFromSymbols(
|
||||
completion.symbols,
|
||||
entries,
|
||||
sourceFile,
|
||||
sourceFile,
|
||||
checker,
|
||||
ScriptTarget.ESNext,
|
||||
log,
|
||||
CompletionKind.String,
|
||||
preferences
|
||||
); // Target will not be used, so arbitrary
|
||||
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: completion.hasIndexSignature, entries };
|
||||
}
|
||||
case StringLiteralCompletionKind.Types: {
|
||||
@@ -60,7 +70,7 @@ namespace ts.Completions.StringCompletions {
|
||||
const isGlobalCompletion = false; // We don't want the editor to offer any other completions, such as snippets, inside a comment.
|
||||
const isNewIdentifierLocation = true; // The user may type in a path that doesn't yet exist, creating a "new identifier" with respect to the collection of identifiers the server is aware of.
|
||||
const entries = pathCompletions.map(({ name, kind, span, extension }): CompletionEntry =>
|
||||
({ name, kind, kindModifiers: kindModifiersFromExtension(extension), sortText: "0", replacementSpan: span }));
|
||||
({ name, kind, kindModifiers: kindModifiersFromExtension(extension), sortText: SortText.LocationPriority, replacementSpan: span }));
|
||||
return { isGlobalCompletion, isMemberCompletion: false, isNewIdentifierLocation, entries };
|
||||
}
|
||||
function kindModifiersFromExtension(extension: Extension | undefined): ScriptElementKindModifier {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"patternMatcher.ts",
|
||||
"preProcess.ts",
|
||||
"rename.ts",
|
||||
"smartSelection.ts",
|
||||
"signatureHelp.ts",
|
||||
"sourcemaps.ts",
|
||||
"suggestionDiagnostics.ts",
|
||||
@@ -80,6 +81,7 @@
|
||||
"refactors/convertExport.ts",
|
||||
"refactors/convertImport.ts",
|
||||
"refactors/extractSymbol.ts",
|
||||
"refactors/extractType.ts",
|
||||
"refactors/generateGetAccessorAndSetAccessor.ts",
|
||||
"refactors/moveToNewFile.ts",
|
||||
"refactors/addOrRemoveBracesToArrowFunction.ts",
|
||||
|
||||
@@ -296,6 +296,8 @@ namespace ts {
|
||||
getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): RenameInfo;
|
||||
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): ReadonlyArray<RenameLocation> | undefined;
|
||||
|
||||
getSmartSelectionRange(fileName: string, position: number): SelectionRange;
|
||||
|
||||
getDefinitionAtPosition(fileName: string, position: number): ReadonlyArray<DefinitionInfo> | undefined;
|
||||
getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan | undefined;
|
||||
getTypeDefinitionAtPosition(fileName: string, position: number): ReadonlyArray<DefinitionInfo> | undefined;
|
||||
@@ -848,6 +850,11 @@ namespace ts {
|
||||
isOptional: boolean;
|
||||
}
|
||||
|
||||
export interface SelectionRange {
|
||||
textSpan: TextSpan;
|
||||
parent?: SelectionRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single signature to show in signature help.
|
||||
* The id is used for subsequent calls into the language service to ask questions about the
|
||||
|
||||
@@ -142,6 +142,7 @@
|
||||
"unittests/tsserver/reload.ts",
|
||||
"unittests/tsserver/rename.ts",
|
||||
"unittests/tsserver/resolutionCache.ts",
|
||||
"unittests/tsserver/smartSelection.ts",
|
||||
"unittests/tsserver/session.ts",
|
||||
"unittests/tsserver/skipLibCheck.ts",
|
||||
"unittests/tsserver/symLinks.ts",
|
||||
|
||||
@@ -197,13 +197,13 @@ namespace ts {
|
||||
const caseSensitiveBasePath = "/dev/";
|
||||
const caseSensitiveHost = new fakes.ParseConfigHost(createFileSystem(/*ignoreCase*/ false, caseSensitiveBasePath, "/"));
|
||||
|
||||
function verifyDiagnostics(actual: Diagnostic[], expected: {code: number, category: DiagnosticCategory, messageText: string}[]) {
|
||||
function verifyDiagnostics(actual: Diagnostic[], expected: { code: number; messageText: string; }[]) {
|
||||
assert.isTrue(expected.length === actual.length, `Expected error: ${JSON.stringify(expected)}. Actual error: ${JSON.stringify(actual)}.`);
|
||||
for (let i = 0; i < actual.length; i++) {
|
||||
const actualError = actual[i];
|
||||
const expectedError = expected[i];
|
||||
assert.equal(actualError.code, expectedError.code, "Error code mismatch");
|
||||
assert.equal(actualError.category, expectedError.category, "Category mismatch");
|
||||
assert.equal(actualError.category, DiagnosticCategory.Error, "Category mismatch"); // Should always be error
|
||||
assert.equal(flattenDiagnosticMessageText(actualError.messageText, "\n"), expectedError.messageText);
|
||||
}
|
||||
}
|
||||
@@ -246,7 +246,7 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
function testFailure(name: string, entry: string, expectedDiagnostics: { code: number, category: DiagnosticCategory, messageText: string }[]) {
|
||||
function testFailure(name: string, entry: string, expectedDiagnostics: { code: number; messageText: string; }[]) {
|
||||
it(name, () => {
|
||||
const parsed = getParseCommandLine(entry);
|
||||
verifyDiagnostics(parsed.errors, expectedDiagnostics);
|
||||
@@ -280,26 +280,22 @@ namespace ts {
|
||||
testFailure("can report errors on circular imports", "circular.json", [
|
||||
{
|
||||
code: 18000,
|
||||
category: DiagnosticCategory.Error,
|
||||
messageText: `Circularity detected while resolving configuration: ${[combinePaths(basePath, "circular.json"), combinePaths(basePath, "circular2.json"), combinePaths(basePath, "circular.json")].join(" -> ")}`
|
||||
}
|
||||
]);
|
||||
|
||||
testFailure("can report missing configurations", "missing.json", [{
|
||||
code: 6096,
|
||||
category: DiagnosticCategory.Message,
|
||||
messageText: `File './missing2' does not exist.`
|
||||
code: 6053,
|
||||
messageText: `File './missing2' not found.`
|
||||
}]);
|
||||
|
||||
testFailure("can report errors in extended configs", "failure.json", [{
|
||||
code: 6114,
|
||||
category: DiagnosticCategory.Error,
|
||||
messageText: `Unknown option 'excludes'. Did you mean 'exclude'?`
|
||||
}]);
|
||||
|
||||
testFailure("can error when 'extends' is not a string", "extends.json", [{
|
||||
code: 5024,
|
||||
category: DiagnosticCategory.Error,
|
||||
messageText: `Compiler option 'extends' requires a value of type string.`
|
||||
}]);
|
||||
|
||||
|
||||
@@ -140,22 +140,28 @@ namespace ts {
|
||||
],
|
||||
{
|
||||
before: [
|
||||
context => node => visitNode(node, function visitor(node: Node): Node {
|
||||
if (isIdentifier(node) && node.text === "original") {
|
||||
const newNode = createIdentifier("changed");
|
||||
setSourceMapRange(newNode, {
|
||||
pos: 0,
|
||||
end: 7,
|
||||
// Do not provide a custom skipTrivia function for `source`.
|
||||
source: createSourceMapSource("another.html", "changed;")
|
||||
});
|
||||
return newNode;
|
||||
}
|
||||
return visitEachChild(node, visitor, context);
|
||||
})
|
||||
context => {
|
||||
const transformSourceFile: Transformer<SourceFile> = node => visitNode(node, function visitor(node: Node): Node {
|
||||
if (isIdentifier(node) && node.text === "original") {
|
||||
const newNode = createIdentifier("changed");
|
||||
setSourceMapRange(newNode, {
|
||||
pos: 0,
|
||||
end: 7,
|
||||
// Do not provide a custom skipTrivia function for `source`.
|
||||
source: createSourceMapSource("another.html", "changed;")
|
||||
});
|
||||
return newNode;
|
||||
}
|
||||
return visitEachChild(node, visitor, context);
|
||||
});
|
||||
return {
|
||||
transformSourceFile,
|
||||
transformBundle: node => createBundle(map(node.sourceFiles, transformSourceFile), node.prepends),
|
||||
};
|
||||
}
|
||||
]
|
||||
},
|
||||
{ sourceMap: true }
|
||||
{ sourceMap: true, outFile: "source.js" }
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ts {
|
||||
describe("unittests:: FactoryAPI", () => {
|
||||
function assertSyntaxKind(node: Node, expected: SyntaxKind) {
|
||||
assert.strictEqual(node.kind, expected, `Actual: ${Debug.showSyntaxKind(node)} Expected: ${(ts as any).SyntaxKind[expected]}`);
|
||||
assert.strictEqual(node.kind, expected, `Actual: ${Debug.formatSyntaxKind(node.kind)} Expected: ${Debug.formatSyntaxKind(expected)}`);
|
||||
}
|
||||
describe("createExportAssignment", () => {
|
||||
it("parenthesizes default export if necessary", () => {
|
||||
|
||||
@@ -37,6 +37,8 @@ namespace ts {
|
||||
parsesCorrectly("callSignatureInRecordType", "{{(): number}}");
|
||||
parsesCorrectly("methodInRecordType", "{{foo(): number}}");
|
||||
parsesCorrectly("unionType", "{(number|string)}");
|
||||
parsesCorrectly("unionTypeWithLeadingOperator", "{( | number | string )}");
|
||||
parsesCorrectly("unionTypeWithOneElementAndLeadingOperator", "{( | number )}");
|
||||
parsesCorrectly("topLevelNoParenUnionType", "{number|string}");
|
||||
parsesCorrectly("functionType1", "{function()}");
|
||||
parsesCorrectly("functionType2", "{function(string, boolean)}");
|
||||
|
||||
@@ -530,7 +530,7 @@ export = C;
|
||||
});
|
||||
});
|
||||
|
||||
describe("unittests:: moduleResolution:: Files with different casing", () => {
|
||||
describe("unittests:: moduleResolution:: Files with different casing with forceConsistentCasingInFileNames", () => {
|
||||
let library: SourceFile;
|
||||
function test(files: Map<string>, options: CompilerOptions, currentDirectory: string, useCaseSensitiveFileNames: boolean, rootFiles: string[], diagnosticCodes: number[]): void {
|
||||
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
@@ -649,6 +649,22 @@ import b = require("./moduleB");
|
||||
});
|
||||
test(files, { module: ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], []);
|
||||
});
|
||||
|
||||
it("should succeed when the two files in program differ only in drive letter in their names", () => {
|
||||
const files = createMapFromTemplate({
|
||||
"d:/someFolder/moduleA.ts": `import a = require("D:/someFolder/moduleC")`,
|
||||
"d:/someFolder/moduleB.ts": `import a = require("./moduleC")`,
|
||||
"D:/someFolder/moduleC.ts": "export const x = 10",
|
||||
});
|
||||
test(
|
||||
files,
|
||||
{ module: ModuleKind.CommonJS, forceConsistentCasingInFileNames: true },
|
||||
"d:/someFolder",
|
||||
/*useCaseSensitiveFileNames*/ false,
|
||||
["d:/someFolder/moduleA.ts", "d:/someFolder/moduleB.ts"],
|
||||
[]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("unittests:: moduleResolution:: baseUrl augmented module resolution", () => {
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace ts.projectSystem {
|
||||
kindModifiers: ScriptElementKindModifier.exportedModifier,
|
||||
name: "foo",
|
||||
replacementSpan: undefined,
|
||||
sortText: "0",
|
||||
sortText: Completions.SortText.AutoImportSuggestions,
|
||||
source: "/a",
|
||||
};
|
||||
assert.deepEqual<protocol.CompletionInfo | undefined>(response, {
|
||||
|
||||
@@ -264,6 +264,7 @@ namespace ts.server {
|
||||
CommandNames.OrganizeImportsFull,
|
||||
CommandNames.GetEditsForFileRename,
|
||||
CommandNames.GetEditsForFileRenameFull,
|
||||
CommandNames.SelectionRange,
|
||||
];
|
||||
|
||||
it("should not throw when commands are executed with invalid arguments", () => {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
namespace ts.projectSystem {
|
||||
function setup(fileName: string, content: string) {
|
||||
const file: File = { path: fileName, content };
|
||||
const host = createServerHost([file, libFile]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([file], session);
|
||||
return function getSmartSelectionRange(locations: protocol.SelectionRangeRequestArgs["locations"]) {
|
||||
return executeSessionRequest<protocol.SelectionRangeRequest, protocol.SelectionRangeResponse>(
|
||||
session,
|
||||
CommandNames.SelectionRange,
|
||||
{ file: fileName, locations });
|
||||
};
|
||||
}
|
||||
|
||||
// More tests in fourslash/smartSelection_*
|
||||
describe("unittests:: tsserver:: smartSelection", () => {
|
||||
it("works for simple JavaScript", () => {
|
||||
const getSmartSelectionRange = setup("/file.js", `
|
||||
class Foo {
|
||||
bar(a, b) {
|
||||
if (a === b) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}`);
|
||||
|
||||
const locations = getSmartSelectionRange([
|
||||
{ line: 4, offset: 13 }, // a === b
|
||||
]);
|
||||
|
||||
assert.deepEqual(locations, [{
|
||||
textSpan: { // a
|
||||
start: { line: 4, offset: 13 },
|
||||
end: { line: 4, offset: 14 } },
|
||||
parent: {
|
||||
textSpan: { // a === b
|
||||
start: { line: 4, offset: 13 },
|
||||
end: { line: 4, offset: 20 } },
|
||||
parent: {
|
||||
textSpan: { // IfStatement
|
||||
start: { line: 4, offset: 9 },
|
||||
end: { line: 6, offset: 10 } },
|
||||
parent: {
|
||||
textSpan: { // SyntaxList + whitespace (body of method)
|
||||
start: { line: 3, offset: 16 },
|
||||
end: { line: 8, offset: 5 } },
|
||||
parent: {
|
||||
textSpan: { // MethodDeclaration
|
||||
start: { line: 3, offset: 5 },
|
||||
end: { line: 8, offset: 6 } },
|
||||
parent: {
|
||||
textSpan: { // SyntaxList + whitespace (body of class)
|
||||
start: { line: 2, offset: 12 },
|
||||
end: { line: 9, offset: 1 } },
|
||||
parent: {
|
||||
textSpan: { // ClassDeclaration
|
||||
start: { line: 2, offset: 1 },
|
||||
end: { line: 9, offset: 2 } },
|
||||
parent: {
|
||||
textSpan: { // SourceFile (all text)
|
||||
start: { line: 1, offset: 1 },
|
||||
end: { line: 9, offset: 2 }, } } } } } } } } }]);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user