mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into preserveTypeAliases
# Conflicts: # tests/baselines/reference/modularizeLibrary_UsingES5LibAndES6FeatureLibs.types
This commit is contained in:
commit
4c9675cc44
95 files changed
+2228
-494
No files matched your search
@@ -5933,7 +5933,7 @@ namespace ts {
|
||||
return factory.createPropertySignature(
|
||||
/*modifiers*/ undefined,
|
||||
name,
|
||||
t.typeExpression && isJSDocOptionalType(t.typeExpression.type) ? factory.createToken(SyntaxKind.QuestionToken) : undefined,
|
||||
t.isBracketed || t.typeExpression && isJSDocOptionalType(t.typeExpression.type) ? factory.createToken(SyntaxKind.QuestionToken) : undefined,
|
||||
overrideTypeNode || (t.typeExpression && visitNode(t.typeExpression.type, visitExistingNodeTreeSymbols)) || factory.createKeywordTypeNode(SyntaxKind.AnyKeyword)
|
||||
);
|
||||
}));
|
||||
@@ -33470,7 +33470,7 @@ namespace ts {
|
||||
if (isInJSFile(node)) {
|
||||
const typeTag = getJSDocTypeTag(node);
|
||||
if (typeTag && typeTag.typeExpression && !getContextualCallSignature(getTypeFromTypeNode(typeTag.typeExpression), node)) {
|
||||
error(typeTag, Diagnostics.The_type_of_a_function_declaration_must_match_the_function_s_signature);
|
||||
error(typeTag.typeExpression.type, Diagnostics.The_type_of_a_function_declaration_must_match_the_function_s_signature);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40222,7 +40222,9 @@ namespace ts {
|
||||
if (node.exclamationToken && (node.parent.parent.kind !== SyntaxKind.VariableStatement || !node.type || node.initializer || node.flags & NodeFlags.Ambient)) {
|
||||
const message = node.initializer
|
||||
? Diagnostics.Declarations_with_initializers_cannot_also_have_definite_assignment_assertions
|
||||
: Diagnostics.Declarations_with_definite_assignment_assertions_must_also_have_type_annotations;
|
||||
: !node.type
|
||||
? Diagnostics.Declarations_with_definite_assignment_assertions_must_also_have_type_annotations
|
||||
: Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context;
|
||||
return grammarErrorOnNode(node.exclamationToken, message);
|
||||
}
|
||||
|
||||
|
||||
@@ -6143,6 +6143,34 @@
|
||||
"category": "Message",
|
||||
"code": 95148
|
||||
},
|
||||
"Return type must be inferred from a function": {
|
||||
"category": "Message",
|
||||
"code": 95149
|
||||
},
|
||||
"Could not determine function return type": {
|
||||
"category": "Message",
|
||||
"code": 95150
|
||||
},
|
||||
"Could not convert to arrow function": {
|
||||
"category": "Message",
|
||||
"code": 95151
|
||||
},
|
||||
"Could not convert to named function": {
|
||||
"category": "Message",
|
||||
"code": 95152
|
||||
},
|
||||
"Could not convert to anonymous function": {
|
||||
"category": "Message",
|
||||
"code": 95153
|
||||
},
|
||||
"Can only convert string concatenation": {
|
||||
"category": "Message",
|
||||
"code": 95154
|
||||
},
|
||||
"Selection is not a valid statement or statements": {
|
||||
"category": "Message",
|
||||
"code": 95155
|
||||
},
|
||||
|
||||
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
|
||||
"category": "Error",
|
||||
|
||||
@@ -2992,7 +2992,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function getEffectiveImplementsTypeNodes(node: ClassLikeDeclaration): undefined | readonly ExpressionWithTypeArguments[]{
|
||||
if(isInJSFile(node)) {
|
||||
if (isInJSFile(node)) {
|
||||
return getJSDocImplementsTags(node).map(n => n.class);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -3420,6 +3420,12 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public verifyRefactorKindsAvailable(kind: string, expected: string[], preferences = ts.emptyOptions) {
|
||||
const refactors = this.getApplicableRefactorsAtSelection("invoked", kind, preferences);
|
||||
const availableKinds = ts.flatMap(refactors, refactor => refactor.actions).map(action => action.kind);
|
||||
assert.deepEqual(availableKinds.sort(), expected.sort(), `Expected kinds to be equal`);
|
||||
}
|
||||
|
||||
public verifyRefactorsAvailable(names: readonly string[]): void {
|
||||
assert.deepEqual(unique(this.getApplicableRefactorsAtSelection(), r => r.name), names);
|
||||
}
|
||||
@@ -3833,14 +3839,14 @@ namespace FourSlash {
|
||||
test(renameKeys(newFileContents, key => pathUpdater(key) || key), "with file moved");
|
||||
}
|
||||
|
||||
private getApplicableRefactorsAtSelection(triggerReason: ts.RefactorTriggerReason = "implicit") {
|
||||
return this.getApplicableRefactorsWorker(this.getSelection(), this.activeFile.fileName, ts.emptyOptions, triggerReason);
|
||||
private getApplicableRefactorsAtSelection(triggerReason: ts.RefactorTriggerReason = "implicit", kind?: string, preferences = ts.emptyOptions) {
|
||||
return this.getApplicableRefactorsWorker(this.getSelection(), this.activeFile.fileName, preferences, triggerReason, kind);
|
||||
}
|
||||
private getApplicableRefactors(rangeOrMarker: Range | Marker, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason = "implicit"): readonly ts.ApplicableRefactorInfo[] {
|
||||
return this.getApplicableRefactorsWorker("position" in rangeOrMarker ? rangeOrMarker.position : rangeOrMarker, rangeOrMarker.fileName, preferences, triggerReason); // eslint-disable-line no-in-operator
|
||||
private getApplicableRefactors(rangeOrMarker: Range | Marker, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason = "implicit", kind?: string): readonly ts.ApplicableRefactorInfo[] {
|
||||
return this.getApplicableRefactorsWorker("position" in rangeOrMarker ? rangeOrMarker.position : rangeOrMarker, rangeOrMarker.fileName, preferences, triggerReason, kind); // eslint-disable-line no-in-operator
|
||||
}
|
||||
private getApplicableRefactorsWorker(positionOrRange: number | ts.TextRange, fileName: string, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason): readonly ts.ApplicableRefactorInfo[] {
|
||||
return this.languageService.getApplicableRefactors(fileName, positionOrRange, preferences, triggerReason) || ts.emptyArray;
|
||||
private getApplicableRefactorsWorker(positionOrRange: number | ts.TextRange, fileName: string, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason, kind?: string): readonly ts.ApplicableRefactorInfo[] {
|
||||
return this.languageService.getApplicableRefactors(fileName, positionOrRange, preferences, triggerReason, kind) || ts.emptyArray;
|
||||
}
|
||||
|
||||
public configurePlugin(pluginName: string, configuration: any): void {
|
||||
|
||||
@@ -215,6 +215,10 @@ namespace FourSlashInterface {
|
||||
this.state.verifyRefactorAvailable(this.negative, triggerReason, name, actionName);
|
||||
}
|
||||
|
||||
public refactorKindAvailable(kind: string, expected: string[], preferences = ts.emptyOptions) {
|
||||
this.state.verifyRefactorKindsAvailable(kind, expected, preferences);
|
||||
}
|
||||
|
||||
public toggleLineComment(newFileContent: string) {
|
||||
this.state.toggleLineComment(newFileContent);
|
||||
}
|
||||
|
||||
Vendored
+92
-4
@@ -1,15 +1,103 @@
|
||||
declare namespace Reflect {
|
||||
/**
|
||||
* Calls the function with the specified object as the this value
|
||||
* and the elements of specified array as the arguments.
|
||||
* @param target The function to call.
|
||||
* @param thisArgument The object to be used as the this object.
|
||||
* @param argumentsList An array of argument values to be passed to the function.
|
||||
*/
|
||||
function apply(target: Function, thisArgument: any, argumentsList: ArrayLike<any>): any;
|
||||
function construct(target: Function, argumentsList: ArrayLike<any>, newTarget?: any): any;
|
||||
|
||||
/**
|
||||
* Constructs the target with the elements of specified array as the arguments
|
||||
* and the specified constructor as the `new.target` value.
|
||||
* @param target The constructor to invoke.
|
||||
* @param argumentsList An array of argument values to be passed to the constructor.
|
||||
* @param newTarget The constructor to be used as the `new.target` object.
|
||||
*/
|
||||
function construct(target: Function, argumentsList: ArrayLike<any>, newTarget?: Function): any;
|
||||
|
||||
/**
|
||||
* Adds a property to an object, or modifies attributes of an existing property.
|
||||
* @param target Object on which to add or modify the property. This can be a native JavaScript object
|
||||
* (that is, a user-defined object or a built in object) or a DOM object.
|
||||
* @param propertyKey The property name.
|
||||
* @param attributes Descriptor for the property. It can be for a data property or an accessor property.
|
||||
*/
|
||||
function defineProperty(target: object, propertyKey: PropertyKey, attributes: PropertyDescriptor): boolean;
|
||||
|
||||
/**
|
||||
* Removes a property from an object, equivalent to `delete target[propertyKey]`,
|
||||
* except it won't throw if `target[propertyKey]` is non-configurable.
|
||||
* @param target Object from which to remove the own property.
|
||||
* @param propertyKey The property name.
|
||||
*/
|
||||
function deleteProperty(target: object, propertyKey: PropertyKey): boolean;
|
||||
|
||||
/**
|
||||
* Gets the property of target, equivalent to `target[propertyKey]` when `receiver === target`.
|
||||
* @param target Object that contains the property on itself or in its prototype chain.
|
||||
* @param propertyKey The property name.
|
||||
* @param receiver The reference to use as the `this` value in the getter function,
|
||||
* if `target[propertyKey]` is an accessor property.
|
||||
*/
|
||||
function get(target: object, propertyKey: PropertyKey, receiver?: any): any;
|
||||
|
||||
/**
|
||||
* Gets the own property descriptor of the specified object.
|
||||
* An own property descriptor is one that is defined directly on the object and is not inherited from the object's prototype.
|
||||
* @param target Object that contains the property.
|
||||
* @param propertyKey The property name.
|
||||
*/
|
||||
function getOwnPropertyDescriptor(target: object, propertyKey: PropertyKey): PropertyDescriptor | undefined;
|
||||
function getPrototypeOf(target: object): object;
|
||||
|
||||
/**
|
||||
* Returns the prototype of an object.
|
||||
* @param target The object that references the prototype.
|
||||
*/
|
||||
function getPrototypeOf(target: object): object | null;
|
||||
|
||||
/**
|
||||
* Equivalent to `propertyKey in target`.
|
||||
* @param target Object that contains the property on itself or in its prototype chain.
|
||||
* @param propertyKey Name of the property.
|
||||
*/
|
||||
function has(target: object, propertyKey: PropertyKey): boolean;
|
||||
|
||||
/**
|
||||
* Returns a value that indicates whether new properties can be added to an object.
|
||||
* @param target Object to test.
|
||||
*/
|
||||
function isExtensible(target: object): boolean;
|
||||
function ownKeys(target: object): PropertyKey[];
|
||||
|
||||
/**
|
||||
* Returns the string and symbol keys of the own properties of an object. The own properties of an object
|
||||
* are those that are defined directly on that object, and are not inherited from the object's prototype.
|
||||
* @param target Object that contains the own properties.
|
||||
*/
|
||||
function ownKeys(target: object): (string | symbol)[];
|
||||
|
||||
/**
|
||||
* Prevents the addition of new properties to an object.
|
||||
* @param target Object to make non-extensible.
|
||||
* @return Whether the object has been made non-extensible.
|
||||
*/
|
||||
function preventExtensions(target: object): boolean;
|
||||
|
||||
/**
|
||||
* Sets the property of target, equivalent to `target[propertyKey] = value` when `receiver === target`.
|
||||
* @param target Object that contains the property on itself or in its prototype chain.
|
||||
* @param propertyKey Name of the property.
|
||||
* @param receiver The reference to use as the `this` value in the setter function,
|
||||
* if `target[propertyKey]` is an accessor property.
|
||||
*/
|
||||
function set(target: object, propertyKey: PropertyKey, value: any, receiver?: any): boolean;
|
||||
function setPrototypeOf(target: object, proto: any): boolean;
|
||||
|
||||
/**
|
||||
* Sets the prototype of a specified object o to object proto or null.
|
||||
* @param target The object to change its prototype.
|
||||
* @param proto The value of the new prototype or null.
|
||||
* @return Whether setting the prototype was successful.
|
||||
*/
|
||||
function setPrototypeOf(target: object, proto: object | null): boolean;
|
||||
}
|
||||
Vendored
+236
-16
@@ -54,6 +54,239 @@ declare namespace Intl {
|
||||
*/
|
||||
type RelativeTimeFormatStyle = "long" | "short" | "narrow";
|
||||
|
||||
/**
|
||||
* Unicode Calendar Identifier
|
||||
*
|
||||
* [Reference](http://unicode.org/reports/tr35/#UnicodeCalendarIdentifier)
|
||||
*
|
||||
* [Source](https://github.com/unicode-org/cldr/blob/b805d0b/common/bcp47/calendar.xml)
|
||||
*/
|
||||
type Calendar =
|
||||
// Thai Buddhist calendar
|
||||
| "buddhist"
|
||||
// Traditional Chinese calendar
|
||||
| "chinese"
|
||||
// Coptic calendar
|
||||
| "coptic"
|
||||
// Traditional Korean calendar
|
||||
| "dangi"
|
||||
// Ethiopic calendar, Amete Alem (epoch approx. 5493 B.C.E)
|
||||
| "ethiopic-amete-alem" | "ethioaa"
|
||||
// Ethiopic calendar, Amete Mihret (epoch approx, 8 C.E.)
|
||||
| "ethiopic"
|
||||
// Gregorian calendar
|
||||
| "gregorian"
|
||||
// Gregorian calendar (alias)
|
||||
| "gregory"
|
||||
// Traditional Hebrew calendar
|
||||
| "hebrew"
|
||||
// Indian calendar
|
||||
| "indian"
|
||||
// Islamic calendar
|
||||
| "islamic"
|
||||
// Islamic calendar, Umm al-Qura
|
||||
| "islamic-umalqura"
|
||||
// Islamic calendar, tabular (intercalary years [2,5,7,10,13,16,18,21,24,26,29] - astronomical epoch)
|
||||
| "islamic-tbla"
|
||||
// Islamic calendar, tabular (intercalary years [2,5,7,10,13,16,18,21,24,26,29] - civil epoch)
|
||||
| "islamic-civil"
|
||||
| "islamicc" // Deprecated alias
|
||||
// Islamic calendar, Saudi Arabia sighting
|
||||
| "islamic-rgsa"
|
||||
// ISO calendar (Gregorian calendar using the ISO 8601 calendar week rules)
|
||||
| "iso8601"
|
||||
// Japanese Imperial calendar
|
||||
| "japanese"
|
||||
// Persian calendar
|
||||
| "persian"
|
||||
// Republic of China calendar
|
||||
| "roc"
|
||||
;
|
||||
|
||||
/**
|
||||
* Unicode Number System Identifier
|
||||
*
|
||||
* [Reference](http://unicode.org/reports/tr35/#UnicodeNumberSystemIdentifier)
|
||||
*
|
||||
* [Source](https://github.com/unicode-org/cldr/blob/be7a953/common/bcp47/number.xml)
|
||||
*/
|
||||
type NumberingSystem =
|
||||
// Adlam digits
|
||||
| "adlm"
|
||||
// Ahom digits
|
||||
| "ahom"
|
||||
// Arabic-Indic digits
|
||||
| "arab"
|
||||
// Extended Arabic-Indic digits
|
||||
| "arabext"
|
||||
// Armenian upper case numerals — algorithmic
|
||||
| "armn"
|
||||
// Armenian lower case numerals — algorithmic
|
||||
| "armnlow"
|
||||
// Balinese digits
|
||||
| "bali"
|
||||
// Bengali digits
|
||||
| "beng"
|
||||
// Bhaiksuki digits
|
||||
| "bhks"
|
||||
// Brahmi digits
|
||||
| "brah"
|
||||
// Chakma digits
|
||||
| "cakm"
|
||||
// Cham digits
|
||||
| "cham"
|
||||
// Cyrillic numerals — algorithmic
|
||||
| "cyrl"
|
||||
// Devanagari digits
|
||||
| "deva"
|
||||
// Dives Akuru digits
|
||||
| "diak"
|
||||
// Ethiopic numerals — algorithmic
|
||||
| "ethi"
|
||||
// Financial numerals — may be algorithmic
|
||||
| "finance"
|
||||
// Full width digits
|
||||
| "fullwide"
|
||||
// Georgian numerals — algorithmic
|
||||
| "geor"
|
||||
// Gunjala Gondi digits
|
||||
| "gong"
|
||||
// Masaram Gondi digits
|
||||
| "gonm"
|
||||
// Greek upper case numerals — algorithmic
|
||||
| "grek"
|
||||
// Greek lower case numerals — algorithmic
|
||||
| "greklow"
|
||||
// Gujarati digits
|
||||
| "gujr"
|
||||
// Gurmukhi digits
|
||||
| "guru"
|
||||
// Han-character day-of-month numbering for lunar/other traditional calendars"
|
||||
| "hanidays"
|
||||
// Positional decimal system using Chinese number ideographs as digits
|
||||
| "hanidec"
|
||||
// Simplified Chinese numerals — algorithmic
|
||||
| "hans"
|
||||
// Simplified Chinese financial numerals — algorithmic
|
||||
| "hansfin"
|
||||
// Traditional Chinese numerals — algorithmic
|
||||
| "hant"
|
||||
// Traditional Chinese financial numerals — algorithmic
|
||||
| "hantfin"
|
||||
// Hebrew numerals — algorithmic
|
||||
| "hebr"
|
||||
// Pahawh Hmong digits
|
||||
| "hmng"
|
||||
// Nyiakeng Puachue Hmong digits
|
||||
| "hmnp"
|
||||
// Javanese digits
|
||||
| "java"
|
||||
// Japanese numerals — algorithmic
|
||||
| "jpan"
|
||||
// Japanese financial numerals — algorithmic
|
||||
| "jpanfin"
|
||||
// Japanese first-year Gannen numbering for Japanese calendar
|
||||
| "jpanyear"
|
||||
// Kayah Li digits
|
||||
| "kali"
|
||||
// Khmer digits
|
||||
| "khmr"
|
||||
// Kannada digits
|
||||
| "knda"
|
||||
// Tai Tham Hora (secular) digits
|
||||
| "lana"
|
||||
// Tai Tham Tham (ecclesiastical) digits
|
||||
| "lanatham"
|
||||
// Lao digits
|
||||
| "laoo"
|
||||
// Latin digits
|
||||
| "latn"
|
||||
// Lepcha digits
|
||||
| "lepc"
|
||||
// Limbu digits
|
||||
| "limb"
|
||||
// Mathematical bold digits
|
||||
| "mathbold"
|
||||
// Mathematical double-struck digits
|
||||
| "mathdbl"
|
||||
// Mathematical monospace digits
|
||||
| "mathmono"
|
||||
// Mathematical sans-serif bold digits
|
||||
| "mathsanb"
|
||||
// Mathematical sans-serif digits
|
||||
| "mathsans"
|
||||
// Malayalam digits
|
||||
| "mlym"
|
||||
// Modi digits
|
||||
| "modi"
|
||||
// Mongolian digits
|
||||
| "mong"
|
||||
// Mro digits
|
||||
| "mroo"
|
||||
// Meetei Mayek digits
|
||||
| "mtei"
|
||||
// Myanmar digits
|
||||
| "mymr"
|
||||
// Myanmar Shan digits
|
||||
| "mymrshan"
|
||||
// Myanmar Tai Laing digits
|
||||
| "mymrtlng"
|
||||
// Native digits
|
||||
| "native"
|
||||
// Newa digits
|
||||
| "newa"
|
||||
// N'Ko digits
|
||||
| "nkoo"
|
||||
// Ol Chiki digits
|
||||
| "olck"
|
||||
// Oriya digits
|
||||
| "orya"
|
||||
// Osmanya digits
|
||||
| "osma"
|
||||
// Hanifi Rohingya digits
|
||||
| "rohg"
|
||||
// Roman upper case numerals — algorithmic
|
||||
| "roman"
|
||||
// Roman lowercase numerals — algorithmic
|
||||
| "romanlow"
|
||||
// Saurashtra digits
|
||||
| "saur"
|
||||
// Sharada digits
|
||||
| "shrd"
|
||||
// Khudawadi digits
|
||||
| "sind"
|
||||
// Sinhala Lith digits
|
||||
| "sinh"
|
||||
// Sora_Sompeng digits
|
||||
| "sora"
|
||||
// Sundanese digits
|
||||
| "sund"
|
||||
// Takri digits
|
||||
| "takr"
|
||||
// New Tai Lue digits
|
||||
| "talu"
|
||||
// Tamil numerals — algorithmic
|
||||
| "taml"
|
||||
// Modern Tamil decimal digits
|
||||
| "tamldec"
|
||||
// Telugu digits
|
||||
| "telu"
|
||||
// Thai digits
|
||||
| "thai"
|
||||
// Tirhuta digits
|
||||
| "tirh"
|
||||
// Tibetan digits
|
||||
| "tibt"
|
||||
// Traditional numerals — may be algorithmic
|
||||
| "traditional" | "traditio"
|
||||
// Vai digits
|
||||
| "vaii"
|
||||
// Warang Citi digits
|
||||
| "wara"
|
||||
// Wancho digits
|
||||
| "wcho"
|
||||
;
|
||||
|
||||
/**
|
||||
* An object with some or all of properties of `options` parameter
|
||||
* of `Intl.RelativeTimeFormat` constructor.
|
||||
@@ -281,23 +514,10 @@ declare namespace Intl {
|
||||
interface DateTimeFormatOptions {
|
||||
dateStyle?: "full" | "long" | "medium" | "short";
|
||||
timeStyle?: "full" | "long" | "medium" | "short";
|
||||
calendar?: "buddhist" | "chinese" | " coptic" | "ethiopia" | "ethiopic" | "gregory" | " hebrew" | "indian" | "islamic" | "iso8601" | " japanese" | "persian" | "roc";
|
||||
dayPeriod?: "narrow" | "short" | " long";
|
||||
numberingSystem?: "arab" | "arabext" | " bali" | "beng" | "deva" | "fullwide" | " gujr" | "guru" | "hanidec" | "khmr" | " knda" | "laoo" | "latn" | "limb" | "mlym" | " mong" | "mymr" | "orya" | "tamldec" | " telu" | "thai" | "tibt";
|
||||
localeMatcher?: "best fit" | "lookup";
|
||||
timeZone?: string;
|
||||
hour12?: boolean;
|
||||
calendar?: Calendar;
|
||||
dayPeriod?: "narrow" | "short" | "long";
|
||||
numberingSystem?: NumberingSystem;
|
||||
hourCycle?: "h11" | "h12" | "h23" | "h24";
|
||||
formatMatcher?: "best fit" | "basic";
|
||||
weekday?: "long" | "short" | "narrow";
|
||||
era?: "long" | "short" | "narrow";
|
||||
year?: "numeric" | "2-digit";
|
||||
month?: "numeric" | "2-digit" | "long" | "short" | "narrow";
|
||||
day?: "numeric" | "2-digit";
|
||||
hour?: "numeric" | "2-digit";
|
||||
minute?: "numeric" | "2-digit";
|
||||
second?: "numeric" | "2-digit";
|
||||
fractionalSecondDigits?: 0 | 1 | 2 | 3;
|
||||
timeZoneName?: "long" | "short";
|
||||
}
|
||||
}
|
||||
Vendored
+40
-28
@@ -1199,7 +1199,7 @@ interface ConcatArray<T> {
|
||||
|
||||
interface Array<T> {
|
||||
/**
|
||||
* Gets or sets the length of the array. This is a number one higher than the highest element defined in an array.
|
||||
* Gets or sets the length of the array. This is a number one higher than the highest index in the array.
|
||||
*/
|
||||
length: number;
|
||||
/**
|
||||
@@ -1212,44 +1212,54 @@ interface Array<T> {
|
||||
toLocaleString(): string;
|
||||
/**
|
||||
* Removes the last element from an array and returns it.
|
||||
* If the array is empty, undefined is returned and the array is not modified.
|
||||
*/
|
||||
pop(): T | undefined;
|
||||
/**
|
||||
* Appends new elements to an array, and returns the new length of the array.
|
||||
* @param items New elements of the Array.
|
||||
* Appends new elements to the end of an array, and returns the new length of the array.
|
||||
* @param items New elements to add to the array.
|
||||
*/
|
||||
push(...items: T[]): number;
|
||||
/**
|
||||
* Combines two or more arrays.
|
||||
* @param items Additional items to add to the end of array1.
|
||||
* This method returns a new array without modifying any existing arrays.
|
||||
* @param items Additional arrays and/or items to add to the end of the array.
|
||||
*/
|
||||
concat(...items: ConcatArray<T>[]): T[];
|
||||
/**
|
||||
* Combines two or more arrays.
|
||||
* @param items Additional items to add to the end of array1.
|
||||
* This method returns a new array without modifying any existing arrays.
|
||||
* @param items Additional arrays and/or items to add to the end of the array.
|
||||
*/
|
||||
concat(...items: (T | ConcatArray<T>)[]): T[];
|
||||
/**
|
||||
* Adds all the elements of an array separated by the specified separator string.
|
||||
* @param separator A string used to separate one element of an array from the next in the resulting String. If omitted, the array elements are separated with a comma.
|
||||
* Adds all the elements of an array into a string, separated by the specified separator string.
|
||||
* @param separator A string used to separate one element of the array from the next in the resulting string. If omitted, the array elements are separated with a comma.
|
||||
*/
|
||||
join(separator?: string): string;
|
||||
/**
|
||||
* Reverses the elements in an Array.
|
||||
* Reverses the elements in an array in place.
|
||||
* This method mutates the array and returns a reference to the same array.
|
||||
*/
|
||||
reverse(): T[];
|
||||
/**
|
||||
* Removes the first element from an array and returns it.
|
||||
* If the array is empty, undefined is returned and the array is not modified.
|
||||
*/
|
||||
shift(): T | undefined;
|
||||
/**
|
||||
* Returns a section of an array.
|
||||
* @param start The beginning of the specified portion of the array.
|
||||
* @param end The end of the specified portion of the array. This is exclusive of the element at the index 'end'.
|
||||
* Returns a copy of a section of an array.
|
||||
* For both start and end, a negative index can be used to indicate an offset from the end of the array.
|
||||
* For example, -2 refers to the second to last element of the array.
|
||||
* @param start The beginning index of the specified portion of the array.
|
||||
* If start is undefined, then the slice begins at index 0.
|
||||
* @param end The end index of the specified portion of the array. This is exclusive of the element at the index 'end'.
|
||||
* If end is undefined, then the slice extends to the end of the array.
|
||||
*/
|
||||
slice(start?: number, end?: number): T[];
|
||||
/**
|
||||
* Sorts an array.
|
||||
* Sorts an array in place.
|
||||
* This method mutates the array and returns a reference to the same array.
|
||||
* @param compareFn Function used to determine the order of the elements. It is expected to return
|
||||
* a negative value if first argument is less than second argument, zero if they're equal and a positive
|
||||
* value otherwise. If omitted, the elements are sorted in ascending, ASCII character order.
|
||||
@@ -1262,6 +1272,7 @@ interface Array<T> {
|
||||
* Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements.
|
||||
* @param start The zero-based location in the array from which to start removing elements.
|
||||
* @param deleteCount The number of elements to remove.
|
||||
* @returns An array containing the elements that were deleted.
|
||||
*/
|
||||
splice(start: number, deleteCount?: number): T[];
|
||||
/**
|
||||
@@ -1269,23 +1280,24 @@ interface Array<T> {
|
||||
* @param start The zero-based location in the array from which to start removing elements.
|
||||
* @param deleteCount The number of elements to remove.
|
||||
* @param items Elements to insert into the array in place of the deleted elements.
|
||||
* @returns An array containing the elements that were deleted.
|
||||
*/
|
||||
splice(start: number, deleteCount: number, ...items: T[]): T[];
|
||||
/**
|
||||
* Inserts new elements at the start of an array.
|
||||
* @param items Elements to insert at the start of the Array.
|
||||
* Inserts new elements at the start of an array, and returns the new length of the array.
|
||||
* @param items Elements to insert at the start of the array.
|
||||
*/
|
||||
unshift(...items: T[]): number;
|
||||
/**
|
||||
* Returns the index of the first occurrence of a value in an array.
|
||||
* Returns the index of the first occurrence of a value in an array, or -1 if it is not present.
|
||||
* @param searchElement The value to locate in the array.
|
||||
* @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at index 0.
|
||||
*/
|
||||
indexOf(searchElement: T, fromIndex?: number): number;
|
||||
/**
|
||||
* Returns the index of the last occurrence of a specified value in an array.
|
||||
* Returns the index of the last occurrence of a specified value in an array, or -1 if it is not present.
|
||||
* @param searchElement The value to locate in the array.
|
||||
* @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at the last index in the array.
|
||||
* @param fromIndex The array index at which to begin searching backward. If fromIndex is omitted, the search starts at the last index in the array.
|
||||
*/
|
||||
lastIndexOf(searchElement: T, fromIndex?: number): number;
|
||||
/**
|
||||
@@ -4318,17 +4330,17 @@ declare namespace Intl {
|
||||
};
|
||||
|
||||
interface DateTimeFormatOptions {
|
||||
localeMatcher?: string;
|
||||
weekday?: string;
|
||||
era?: string;
|
||||
year?: string;
|
||||
month?: string;
|
||||
day?: string;
|
||||
hour?: string;
|
||||
minute?: string;
|
||||
second?: string;
|
||||
timeZoneName?: string;
|
||||
formatMatcher?: string;
|
||||
localeMatcher?: "best fit" | "lookup";
|
||||
weekday?: "long" | "short" | "narrow";
|
||||
era?: "long" | "short" | "narrow";
|
||||
year?: "numeric" | "2-digit";
|
||||
month?: "numeric" | "2-digit" | "long" | "short" | "narrow";
|
||||
day?: "numeric" | "2-digit";
|
||||
hour?: "numeric" | "2-digit";
|
||||
minute?: "numeric" | "2-digit";
|
||||
second?: "numeric" | "2-digit";
|
||||
timeZoneName?: "long" | "short";
|
||||
formatMatcher?: "best fit" | "basic";
|
||||
hour12?: boolean;
|
||||
timeZone?: string;
|
||||
}
|
||||
|
||||
@@ -566,7 +566,8 @@ namespace ts.server.protocol {
|
||||
arguments: GetApplicableRefactorsRequestArgs;
|
||||
}
|
||||
export type GetApplicableRefactorsRequestArgs = FileLocationOrRangeRequestArgs & {
|
||||
triggerReason?: RefactorTriggerReason
|
||||
triggerReason?: RefactorTriggerReason;
|
||||
kind?: string;
|
||||
};
|
||||
|
||||
export type RefactorTriggerReason = "implicit" | "invoked";
|
||||
@@ -626,6 +627,11 @@ namespace ts.server.protocol {
|
||||
* the current context.
|
||||
*/
|
||||
notApplicableReason?: string;
|
||||
|
||||
/**
|
||||
* The hierarchical dotted name of the refactor action.
|
||||
*/
|
||||
kind?: string;
|
||||
}
|
||||
|
||||
export interface GetEditsForRefactorRequest extends Request {
|
||||
|
||||
@@ -2129,7 +2129,7 @@ namespace ts.server {
|
||||
private getApplicableRefactors(args: protocol.GetApplicableRefactorsRequestArgs): protocol.ApplicableRefactorInfo[] {
|
||||
const { file, project } = this.getFileAndProject(args);
|
||||
const scriptInfo = project.getScriptInfoForNormalizedPath(file)!;
|
||||
return project.getLanguageService().getApplicableRefactors(file, this.extractPositionOrRange(args, scriptInfo), this.getPreferences(file), args.triggerReason);
|
||||
return project.getLanguageService().getApplicableRefactors(file, this.extractPositionOrRange(args, scriptInfo), this.getPreferences(file), args.triggerReason, args.kind);
|
||||
}
|
||||
|
||||
private getEditsForRefactor(args: protocol.GetEditsForRefactorRequestArgs, simplifiedResult: boolean): RefactorEditInfo | protocol.RefactorEditInfo {
|
||||
|
||||
@@ -235,7 +235,6 @@ namespace ts.codefix {
|
||||
return transformThen(node, transformer, prevArgName);
|
||||
}
|
||||
if (isPromiseReturningCallExpression(node, transformer.checker, "catch")) {
|
||||
if (node.arguments.length === 0) return silentFail();
|
||||
return transformCatch(node, transformer, prevArgName);
|
||||
}
|
||||
if (isPropertyAccessExpression(node)) {
|
||||
@@ -252,15 +251,13 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function transformCatch(node: CallExpression, transformer: Transformer, prevArgName?: SynthBindingName): readonly Statement[] {
|
||||
const func = node.arguments[0];
|
||||
const argName = getArgBindingName(func, transformer);
|
||||
const func = singleOrUndefined(node.arguments);
|
||||
const argName = func ? getArgBindingName(func, transformer) : undefined;
|
||||
let possibleNameForVarDecl: SynthIdentifier | undefined;
|
||||
|
||||
/*
|
||||
If there is another call in the chain after the .catch() we are transforming, we will need to save the result of both paths (try block and catch block)
|
||||
To do this, we will need to synthesize a variable that we were not aware of while we were adding identifiers to the synthNamesMap
|
||||
We will use the prevArgName and then update the synthNamesMap with a new variable name for the next transformation step
|
||||
*/
|
||||
// If there is another call in the chain after the .catch() we are transforming, we will need to save the result of both paths (try block and catch block)
|
||||
// To do this, we will need to synthesize a variable that we were not aware of while we were adding identifiers to the synthNamesMap
|
||||
// We will use the prevArgName and then update the synthNamesMap with a new variable name for the next transformation step
|
||||
if (prevArgName && !shouldReturn(node, transformer)) {
|
||||
if (isSynthIdentifier(prevArgName)) {
|
||||
possibleNameForVarDecl = prevArgName;
|
||||
@@ -282,14 +279,12 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
const tryBlock = factory.createBlock(transformExpression(node.expression, transformer, possibleNameForVarDecl));
|
||||
const transformationBody = getTransformationBody(func, possibleNameForVarDecl, argName, node, transformer);
|
||||
const transformationBody = func ? getTransformationBody(func, possibleNameForVarDecl, argName, node, transformer) : emptyArray;
|
||||
const catchArg = argName ? isSynthIdentifier(argName) ? argName.identifier.text : argName.bindingPattern : "e";
|
||||
const catchVariableDeclaration = factory.createVariableDeclaration(catchArg);
|
||||
const catchClause = factory.createCatchClause(catchVariableDeclaration, factory.createBlock(transformationBody));
|
||||
|
||||
/*
|
||||
In order to avoid an implicit any, we will synthesize a type for the declaration using the unions of the types of both paths (try block and catch block)
|
||||
*/
|
||||
// In order to avoid an implicit any, we will synthesize a type for the declaration using the unions of the types of both paths (try block and catch block)
|
||||
let varDeclList: VariableStatement | undefined;
|
||||
let varDeclIdentifier: Identifier | undefined;
|
||||
if (possibleNameForVarDecl && !shouldReturn(node, transformer)) {
|
||||
|
||||
@@ -52,6 +52,6 @@ namespace ts.codefix {
|
||||
function getTypesPackageNameToInstall(packageName: string, host: LanguageServiceHost, diagCode: number): string | undefined {
|
||||
return diagCode === errorCodeCannotFindModule
|
||||
? (JsTyping.nodeCoreModules.has(packageName) ? "@types/node" : undefined)
|
||||
: (host.isKnownTypesPackageName!(packageName) ? getTypesPackageName(packageName) : undefined); // TODO: GH#18217
|
||||
: (host.isKnownTypesPackageName?.(packageName) ? getTypesPackageName(packageName) : undefined);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,8 @@ namespace ts.codefix {
|
||||
type AcceptedNameType = Identifier | StringLiteral;
|
||||
type ContainerDeclaration = ClassLikeDeclaration | ObjectLiteralExpression;
|
||||
|
||||
interface Info {
|
||||
type Info = AccessorInfo | refactor.RefactorErrorInfo;
|
||||
interface AccessorInfo {
|
||||
readonly container: ContainerDeclaration;
|
||||
readonly isStatic: boolean;
|
||||
readonly isReadonly: boolean;
|
||||
@@ -16,20 +17,12 @@ namespace ts.codefix {
|
||||
readonly renameAccessor: boolean;
|
||||
}
|
||||
|
||||
type InfoOrError = {
|
||||
info: Info,
|
||||
error?: never
|
||||
} | {
|
||||
info?: never,
|
||||
error: string
|
||||
};
|
||||
|
||||
export function generateAccessorFromProperty(file: SourceFile, program: Program, start: number, end: number, context: textChanges.TextChangesContext, _actionName: string): FileTextChanges[] | undefined {
|
||||
const fieldInfo = getAccessorConvertiblePropertyAtPosition(file, program, start, end);
|
||||
if (!fieldInfo || !fieldInfo.info) return undefined;
|
||||
if (!fieldInfo || refactor.isRefactorErrorInfo(fieldInfo)) return undefined;
|
||||
|
||||
const changeTracker = textChanges.ChangeTracker.fromContext(context);
|
||||
const { isStatic, isReadonly, fieldName, accessorName, originalName, type, container, declaration } = fieldInfo.info;
|
||||
const { isStatic, isReadonly, fieldName, accessorName, originalName, type, container, declaration } = fieldInfo;
|
||||
|
||||
suppressLeadingAndTrailingTrivia(fieldName);
|
||||
suppressLeadingAndTrailingTrivia(accessorName);
|
||||
@@ -112,7 +105,7 @@ namespace ts.codefix {
|
||||
return modifierFlags;
|
||||
}
|
||||
|
||||
export function getAccessorConvertiblePropertyAtPosition(file: SourceFile, program: Program, start: number, end: number, considerEmptySpans = true): InfoOrError | undefined {
|
||||
export function getAccessorConvertiblePropertyAtPosition(file: SourceFile, program: Program, start: number, end: number, considerEmptySpans = true): Info | undefined {
|
||||
const node = getTokenAtPosition(file, start);
|
||||
const cursorRequest = start === end && considerEmptySpans;
|
||||
const declaration = findAncestor(node.parent, isAcceptedDeclaration);
|
||||
@@ -142,17 +135,15 @@ namespace ts.codefix {
|
||||
const fieldName = createPropertyName(startWithUnderscore ? name : getUniqueName(`_${name}`, file), declaration.name);
|
||||
const accessorName = createPropertyName(startWithUnderscore ? getUniqueName(name.substring(1), file) : name, declaration.name);
|
||||
return {
|
||||
info: {
|
||||
isStatic: hasStaticModifier(declaration),
|
||||
isReadonly: hasEffectiveReadonlyModifier(declaration),
|
||||
type: getDeclarationType(declaration, program),
|
||||
container: declaration.kind === SyntaxKind.Parameter ? declaration.parent.parent : declaration.parent,
|
||||
originalName: (<AcceptedNameType>declaration.name).text,
|
||||
declaration,
|
||||
fieldName,
|
||||
accessorName,
|
||||
renameAccessor: startWithUnderscore
|
||||
}
|
||||
isStatic: hasStaticModifier(declaration),
|
||||
isReadonly: hasEffectiveReadonlyModifier(declaration),
|
||||
type: getDeclarationType(declaration, program),
|
||||
container: declaration.kind === SyntaxKind.Parameter ? declaration.parent.parent : declaration.parent,
|
||||
originalName: (<AcceptedNameType>declaration.name).text,
|
||||
declaration,
|
||||
fieldName,
|
||||
accessorName,
|
||||
renameAccessor: startWithUnderscore
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@ namespace ts.refactor {
|
||||
|
||||
export function getApplicableRefactors(context: RefactorContext): ApplicableRefactorInfo[] {
|
||||
return arrayFrom(flatMapIterator(refactors.values(), refactor =>
|
||||
context.cancellationToken && context.cancellationToken.isCancellationRequested() ? undefined : refactor.getAvailableActions(context)));
|
||||
context.cancellationToken && context.cancellationToken.isCancellationRequested() ||
|
||||
!refactor.kinds?.some(kind => refactorKindBeginsWith(kind, context.kind)) ? undefined :
|
||||
refactor.getAvailableActions(context)));
|
||||
}
|
||||
|
||||
export function getEditsForRefactor(context: RefactorContext, refactorName: string, actionName: string): RefactorEditInfo | undefined {
|
||||
|
||||
@@ -2,45 +2,40 @@
|
||||
namespace ts.refactor.addOrRemoveBracesToArrowFunction {
|
||||
const refactorName = "Add or remove braces in an arrow function";
|
||||
const refactorDescription = Diagnostics.Add_or_remove_braces_in_an_arrow_function.message;
|
||||
const addBracesActionName = "Add braces to arrow function";
|
||||
const removeBracesActionName = "Remove braces from arrow function";
|
||||
const addBracesActionDescription = Diagnostics.Add_braces_to_arrow_function.message;
|
||||
const removeBracesActionDescription = Diagnostics.Remove_braces_from_arrow_function.message;
|
||||
registerRefactor(refactorName, { getEditsForAction, getAvailableActions });
|
||||
|
||||
interface Info {
|
||||
const addBracesAction = {
|
||||
name: "Add braces to arrow function",
|
||||
description: Diagnostics.Add_braces_to_arrow_function.message,
|
||||
kind: "refactor.rewrite.arrow.braces.add",
|
||||
};
|
||||
const removeBracesAction = {
|
||||
name: "Remove braces from arrow function",
|
||||
description: Diagnostics.Remove_braces_from_arrow_function.message,
|
||||
kind: "refactor.rewrite.arrow.braces.remove"
|
||||
};
|
||||
registerRefactor(refactorName, {
|
||||
kinds: [removeBracesAction.kind],
|
||||
getEditsForAction,
|
||||
getAvailableActions });
|
||||
|
||||
interface FunctionBracesInfo {
|
||||
func: ArrowFunction;
|
||||
expression: Expression | undefined;
|
||||
returnStatement?: ReturnStatement;
|
||||
addBraces: boolean;
|
||||
}
|
||||
|
||||
type InfoOrError = {
|
||||
info: Info,
|
||||
error?: never
|
||||
} | {
|
||||
info?: never,
|
||||
error: string
|
||||
};
|
||||
|
||||
function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] {
|
||||
const { file, startPosition, triggerReason } = context;
|
||||
const info = getConvertibleArrowFunctionAtPosition(file, startPosition, triggerReason === "invoked");
|
||||
if (!info) return emptyArray;
|
||||
|
||||
if (info.error === undefined) {
|
||||
if (!isRefactorErrorInfo(info)) {
|
||||
return [{
|
||||
name: refactorName,
|
||||
description: refactorDescription,
|
||||
actions: [
|
||||
info.info.addBraces ?
|
||||
{
|
||||
name: addBracesActionName,
|
||||
description: addBracesActionDescription
|
||||
} : {
|
||||
name: removeBracesActionName,
|
||||
description: removeBracesActionDescription
|
||||
}
|
||||
info.addBraces ? addBracesAction : removeBracesAction
|
||||
]
|
||||
}];
|
||||
}
|
||||
@@ -49,15 +44,10 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction {
|
||||
return [{
|
||||
name: refactorName,
|
||||
description: refactorDescription,
|
||||
actions: [{
|
||||
name: addBracesActionName,
|
||||
description: addBracesActionDescription,
|
||||
notApplicableReason: info.error
|
||||
}, {
|
||||
name: removeBracesActionName,
|
||||
description: removeBracesActionDescription,
|
||||
notApplicableReason: info.error
|
||||
}]
|
||||
actions: [
|
||||
{ ...addBracesAction, notApplicableReason: info.error },
|
||||
{ ...removeBracesAction, notApplicableReason: info.error },
|
||||
]
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -67,19 +57,19 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction {
|
||||
function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined {
|
||||
const { file, startPosition } = context;
|
||||
const info = getConvertibleArrowFunctionAtPosition(file, startPosition);
|
||||
if (!info || !info.info) return undefined;
|
||||
Debug.assert(info && !isRefactorErrorInfo(info), "Expected applicable refactor info");
|
||||
|
||||
const { expression, returnStatement, func } = info.info;
|
||||
const { expression, returnStatement, func } = info;
|
||||
|
||||
let body: ConciseBody;
|
||||
|
||||
if (actionName === addBracesActionName) {
|
||||
if (actionName === addBracesAction.name) {
|
||||
const returnStatement = factory.createReturnStatement(expression);
|
||||
body = factory.createBlock([returnStatement], /* multiLine */ true);
|
||||
suppressLeadingAndTrailingTrivia(body);
|
||||
copyLeadingComments(expression!, returnStatement, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ true);
|
||||
}
|
||||
else if (actionName === removeBracesActionName && returnStatement) {
|
||||
else if (actionName === removeBracesAction.name && returnStatement) {
|
||||
const actualExpression = expression || factory.createVoidZero();
|
||||
body = needsParentheses(actualExpression) ? factory.createParenthesizedExpression(actualExpression) : actualExpression;
|
||||
suppressLeadingAndTrailingTrivia(body);
|
||||
@@ -98,7 +88,7 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction {
|
||||
return { renameFilename: undefined, renameLocation: undefined, edits };
|
||||
}
|
||||
|
||||
function getConvertibleArrowFunctionAtPosition(file: SourceFile, startPosition: number, considerFunctionBodies = true): InfoOrError | undefined {
|
||||
function getConvertibleArrowFunctionAtPosition(file: SourceFile, startPosition: number, considerFunctionBodies = true, kind?: string): FunctionBracesInfo | RefactorErrorInfo | undefined {
|
||||
const node = getTokenAtPosition(file, startPosition);
|
||||
const func = getContainingFunction(node);
|
||||
|
||||
@@ -118,26 +108,13 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (isExpression(func.body)) {
|
||||
return {
|
||||
info: {
|
||||
func,
|
||||
addBraces: true,
|
||||
expression: func.body
|
||||
}
|
||||
};
|
||||
if (refactorKindBeginsWith(addBracesAction.kind, kind) && isExpression(func.body)) {
|
||||
return { func, addBraces: true, expression: func.body };
|
||||
}
|
||||
else if (func.body.statements.length === 1) {
|
||||
else if (refactorKindBeginsWith(removeBracesAction.kind, kind) && isBlock(func.body) && func.body.statements.length === 1) {
|
||||
const firstStatement = first(func.body.statements);
|
||||
if (isReturnStatement(firstStatement)) {
|
||||
return {
|
||||
info: {
|
||||
func,
|
||||
addBraces: false,
|
||||
expression: firstStatement.expression,
|
||||
returnStatement: firstStatement
|
||||
}
|
||||
};
|
||||
return { func, addBraces: false, expression: firstStatement.expression, returnStatement: firstStatement };
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
|
||||
@@ -3,15 +3,30 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression {
|
||||
const refactorName = "Convert arrow function or function expression";
|
||||
const refactorDescription = getLocaleSpecificMessage(Diagnostics.Convert_arrow_function_or_function_expression);
|
||||
|
||||
const toAnonymousFunctionActionName = "Convert to anonymous function";
|
||||
const toNamedFunctionActionName = "Convert to named function";
|
||||
const toArrowFunctionActionName = "Convert to arrow function";
|
||||
|
||||
const toAnonymousFunctionActionDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_anonymous_function);
|
||||
const toNamedFunctionActionDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_named_function);
|
||||
const toArrowFunctionActionDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_arrow_function);
|
||||
|
||||
registerRefactor(refactorName, { getEditsForAction, getAvailableActions });
|
||||
const toAnonymousFunctionAction = {
|
||||
name: "Convert to anonymous function",
|
||||
description: getLocaleSpecificMessage(Diagnostics.Convert_to_anonymous_function),
|
||||
kind: "refactor.rewrite.function.anonymous",
|
||||
};
|
||||
const toNamedFunctionAction = {
|
||||
name: "Convert to named function",
|
||||
description: getLocaleSpecificMessage(Diagnostics.Convert_to_named_function),
|
||||
kind: "refactor.rewrite.function.named",
|
||||
};
|
||||
const toArrowFunctionAction = {
|
||||
name: "Convert to arrow function",
|
||||
description: getLocaleSpecificMessage(Diagnostics.Convert_to_arrow_function),
|
||||
kind: "refactor.rewrite.function.arrow",
|
||||
};
|
||||
registerRefactor(refactorName, {
|
||||
kinds: [
|
||||
toAnonymousFunctionAction.kind,
|
||||
toNamedFunctionAction.kind,
|
||||
toArrowFunctionAction.kind
|
||||
],
|
||||
getEditsForAction,
|
||||
getAvailableActions
|
||||
});
|
||||
|
||||
interface FunctionInfo {
|
||||
readonly selectedVariableDeclaration: boolean;
|
||||
@@ -26,38 +41,50 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression {
|
||||
}
|
||||
|
||||
function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] {
|
||||
const { file, startPosition, program } = context;
|
||||
const { file, startPosition, program, kind } = context;
|
||||
const info = getFunctionInfo(file, startPosition, program);
|
||||
|
||||
if (!info) return emptyArray;
|
||||
const { selectedVariableDeclaration, func } = info;
|
||||
const possibleActions: RefactorActionInfo[] = [];
|
||||
|
||||
if (selectedVariableDeclaration || (isArrowFunction(func) && isVariableDeclaration(func.parent))) {
|
||||
possibleActions.push({
|
||||
name: toNamedFunctionActionName,
|
||||
description: toNamedFunctionActionDescription
|
||||
});
|
||||
const errors: RefactorActionInfo[] = [];
|
||||
if (refactorKindBeginsWith(toNamedFunctionAction.kind, kind)) {
|
||||
const error = selectedVariableDeclaration || (isArrowFunction(func) && isVariableDeclaration(func.parent)) ?
|
||||
undefined : getLocaleSpecificMessage(Diagnostics.Could_not_convert_to_named_function);
|
||||
if (error) {
|
||||
errors.push({ ...toNamedFunctionAction, notApplicableReason: error });
|
||||
}
|
||||
else {
|
||||
possibleActions.push(toNamedFunctionAction);
|
||||
}
|
||||
}
|
||||
|
||||
if (!selectedVariableDeclaration && isArrowFunction(func)) {
|
||||
possibleActions.push({
|
||||
name: toAnonymousFunctionActionName,
|
||||
description: toAnonymousFunctionActionDescription
|
||||
});
|
||||
if (refactorKindBeginsWith(toAnonymousFunctionAction.kind, kind)) {
|
||||
const error = !selectedVariableDeclaration && isArrowFunction(func) ?
|
||||
undefined: getLocaleSpecificMessage(Diagnostics.Could_not_convert_to_anonymous_function);
|
||||
if (error) {
|
||||
errors.push({ ...toAnonymousFunctionAction, notApplicableReason: error });
|
||||
}
|
||||
else {
|
||||
possibleActions.push(toAnonymousFunctionAction);
|
||||
}
|
||||
}
|
||||
|
||||
if (isFunctionExpression(func)) {
|
||||
possibleActions.push({
|
||||
name: toArrowFunctionActionName,
|
||||
description: toArrowFunctionActionDescription
|
||||
});
|
||||
if (refactorKindBeginsWith(toArrowFunctionAction.kind, kind)) {
|
||||
const error = isFunctionExpression(func) ? undefined : getLocaleSpecificMessage(Diagnostics.Could_not_convert_to_arrow_function);
|
||||
if (error) {
|
||||
errors.push({ ...toArrowFunctionAction, notApplicableReason: error });
|
||||
}
|
||||
else {
|
||||
possibleActions.push(toArrowFunctionAction);
|
||||
}
|
||||
}
|
||||
|
||||
return [{
|
||||
name: refactorName,
|
||||
description: refactorDescription,
|
||||
actions: possibleActions
|
||||
actions: possibleActions.length === 0 && context.preferences.provideRefactorNotApplicableReason ?
|
||||
errors : possibleActions
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -70,18 +97,18 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression {
|
||||
const edits: FileTextChanges[] = [];
|
||||
|
||||
switch (actionName) {
|
||||
case toAnonymousFunctionActionName:
|
||||
case toAnonymousFunctionAction.name:
|
||||
edits.push(...getEditInfoForConvertToAnonymousFunction(context, func));
|
||||
break;
|
||||
|
||||
case toNamedFunctionActionName:
|
||||
case toNamedFunctionAction.name:
|
||||
const variableInfo = getVariableInfo(func);
|
||||
if (!variableInfo) return undefined;
|
||||
|
||||
edits.push(...getEditInfoForConvertToNamedFunction(context, func, variableInfo));
|
||||
break;
|
||||
|
||||
case toArrowFunctionActionName:
|
||||
case toArrowFunctionAction.name:
|
||||
if (!isFunctionExpression(func)) return undefined;
|
||||
edits.push(...getEditInfoForConvertToArrowFunction(context, func));
|
||||
break;
|
||||
|
||||
@@ -1,54 +1,62 @@
|
||||
/* @internal */
|
||||
namespace ts.refactor {
|
||||
const refactorName = "Convert export";
|
||||
const actionNameDefaultToNamed = "Convert default export to named export";
|
||||
const actionNameNamedToDefault = "Convert named export to default export";
|
||||
|
||||
const defaultToNamedAction = {
|
||||
name: "Convert default export to named export",
|
||||
description: Diagnostics.Convert_default_export_to_named_export.message,
|
||||
kind: "refactor.rewrite.export.named"
|
||||
};
|
||||
const namedToDefaultAction = {
|
||||
name: "Convert named export to default export",
|
||||
description: Diagnostics.Convert_named_export_to_default_export.message,
|
||||
kind: "refactor.rewrite.export.default"
|
||||
};
|
||||
|
||||
registerRefactor(refactorName, {
|
||||
kinds: [
|
||||
defaultToNamedAction.kind,
|
||||
namedToDefaultAction.kind
|
||||
],
|
||||
getAvailableActions(context): readonly ApplicableRefactorInfo[] {
|
||||
const info = getInfo(context, context.triggerReason === "invoked");
|
||||
if (!info) return emptyArray;
|
||||
|
||||
if (info.error === undefined) {
|
||||
const description = info.info.wasDefault ? Diagnostics.Convert_default_export_to_named_export.message : Diagnostics.Convert_named_export_to_default_export.message;
|
||||
const actionName = info.info.wasDefault ? actionNameDefaultToNamed : actionNameNamedToDefault;
|
||||
return [{ name: refactorName, description, actions: [{ name: actionName, description }] }];
|
||||
if (!isRefactorErrorInfo(info)) {
|
||||
const action = info.wasDefault ? defaultToNamedAction : namedToDefaultAction;
|
||||
return [{ name: refactorName, description: action.description, actions: [action] }];
|
||||
}
|
||||
|
||||
if (context.preferences.provideRefactorNotApplicableReason) {
|
||||
return [
|
||||
{ name: refactorName, description: Diagnostics.Convert_default_export_to_named_export.message, actions: [{ name: actionNameDefaultToNamed, description: Diagnostics.Convert_default_export_to_named_export.message, notApplicableReason: info.error }] },
|
||||
{ name: refactorName, description: Diagnostics.Convert_named_export_to_default_export.message, actions: [{ name: actionNameNamedToDefault, description: Diagnostics.Convert_named_export_to_default_export.message, notApplicableReason: info.error }] },
|
||||
{ name: refactorName, description: Diagnostics.Convert_default_export_to_named_export.message, actions: [
|
||||
{ ...defaultToNamedAction, notApplicableReason: info.error },
|
||||
{ ...namedToDefaultAction, notApplicableReason: info.error },
|
||||
]}
|
||||
];
|
||||
}
|
||||
|
||||
return emptyArray;
|
||||
},
|
||||
getEditsForAction(context, actionName): RefactorEditInfo {
|
||||
Debug.assert(actionName === actionNameDefaultToNamed || actionName === actionNameNamedToDefault, "Unexpected action name");
|
||||
const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, context.program, Debug.checkDefined(getInfo(context)?.info, "context must have info"), t, context.cancellationToken));
|
||||
Debug.assert(actionName === defaultToNamedAction.name || actionName === namedToDefaultAction.name, "Unexpected action name");
|
||||
const info = getInfo(context);
|
||||
Debug.assert(info && !isRefactorErrorInfo(info), "Expected applicable refactor info");
|
||||
const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, context.program, info, t, context.cancellationToken));
|
||||
return { edits, renameFilename: undefined, renameLocation: undefined };
|
||||
},
|
||||
});
|
||||
|
||||
// If a VariableStatement, will have exactly one VariableDeclaration, with an Identifier for a name.
|
||||
type ExportToConvert = FunctionDeclaration | ClassDeclaration | InterfaceDeclaration | EnumDeclaration | NamespaceDeclaration | TypeAliasDeclaration | VariableStatement;
|
||||
interface Info {
|
||||
interface ExportInfo {
|
||||
readonly exportNode: ExportToConvert;
|
||||
readonly exportName: Identifier; // This is exportNode.name except for VariableStatement_s.
|
||||
readonly wasDefault: boolean;
|
||||
readonly exportingModuleSymbol: Symbol;
|
||||
}
|
||||
|
||||
type InfoOrError = {
|
||||
info: Info,
|
||||
error?: never
|
||||
} | {
|
||||
info?: never,
|
||||
error: string
|
||||
};
|
||||
|
||||
function getInfo(context: RefactorContext, considerPartialSpans = true): InfoOrError | undefined {
|
||||
function getInfo(context: RefactorContext, considerPartialSpans = true): ExportInfo | RefactorErrorInfo | undefined {
|
||||
const { file } = context;
|
||||
const span = getRefactorContextSpan(context);
|
||||
const token = getTokenAtPosition(file, span.start);
|
||||
@@ -74,7 +82,7 @@ namespace ts.refactor {
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
case SyntaxKind.ModuleDeclaration: {
|
||||
const node = exportNode as FunctionDeclaration | ClassDeclaration | InterfaceDeclaration | EnumDeclaration | TypeAliasDeclaration | NamespaceDeclaration;
|
||||
return node.name && isIdentifier(node.name) ? { info: { exportNode: node, exportName: node.name, wasDefault, exportingModuleSymbol } } : undefined;
|
||||
return node.name && isIdentifier(node.name) ? { exportNode: node, exportName: node.name, wasDefault, exportingModuleSymbol } : undefined;
|
||||
}
|
||||
case SyntaxKind.VariableStatement: {
|
||||
const vs = exportNode as VariableStatement;
|
||||
@@ -85,19 +93,19 @@ namespace ts.refactor {
|
||||
const decl = first(vs.declarationList.declarations);
|
||||
if (!decl.initializer) return undefined;
|
||||
Debug.assert(!wasDefault, "Can't have a default flag here");
|
||||
return isIdentifier(decl.name) ? { info: { exportNode: vs, exportName: decl.name, wasDefault, exportingModuleSymbol } } : undefined;
|
||||
return isIdentifier(decl.name) ? { exportNode: vs, exportName: decl.name, wasDefault, exportingModuleSymbol } : undefined;
|
||||
}
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function doChange(exportingSourceFile: SourceFile, program: Program, info: Info, changes: textChanges.ChangeTracker, cancellationToken: CancellationToken | undefined): void {
|
||||
function doChange(exportingSourceFile: SourceFile, program: Program, info: ExportInfo, changes: textChanges.ChangeTracker, cancellationToken: CancellationToken | undefined): void {
|
||||
changeExport(exportingSourceFile, info, changes, program.getTypeChecker());
|
||||
changeImports(program, info, changes, cancellationToken);
|
||||
}
|
||||
|
||||
function changeExport(exportingSourceFile: SourceFile, { wasDefault, exportNode, exportName }: Info, changes: textChanges.ChangeTracker, checker: TypeChecker): void {
|
||||
function changeExport(exportingSourceFile: SourceFile, { wasDefault, exportNode, exportName }: ExportInfo, changes: textChanges.ChangeTracker, checker: TypeChecker): void {
|
||||
if (wasDefault) {
|
||||
changes.delete(exportingSourceFile, Debug.checkDefined(findModifier(exportNode, SyntaxKind.DefaultKeyword), "Should find a default keyword in modifier list"));
|
||||
}
|
||||
@@ -131,7 +139,7 @@ namespace ts.refactor {
|
||||
}
|
||||
}
|
||||
|
||||
function changeImports(program: Program, { wasDefault, exportName, exportingModuleSymbol }: Info, changes: textChanges.ChangeTracker, cancellationToken: CancellationToken | undefined): void {
|
||||
function changeImports(program: Program, { wasDefault, exportName, exportingModuleSymbol }: ExportInfo, changes: textChanges.ChangeTracker, cancellationToken: CancellationToken | undefined): void {
|
||||
const checker = program.getTypeChecker();
|
||||
const exportSymbol = Debug.checkDefined(checker.getSymbolAtLocation(exportName), "Export name should resolve to a symbol");
|
||||
FindAllReferences.Core.eachExportReference(program.getSourceFiles(), checker, cancellationToken, exportSymbol, exportingModuleSymbol, exportName.text, wasDefault, ref => {
|
||||
|
||||
@@ -1,46 +1,55 @@
|
||||
/* @internal */
|
||||
namespace ts.refactor {
|
||||
const refactorName = "Convert import";
|
||||
const actionNameNamespaceToNamed = "Convert namespace import to named imports";
|
||||
const actionNameNamedToNamespace = "Convert named imports to namespace import";
|
||||
|
||||
type NamedImportBindingsOrError = {
|
||||
info: NamedImportBindings,
|
||||
error?: never
|
||||
} | {
|
||||
info?: never,
|
||||
error: string
|
||||
const namespaceToNamedAction = {
|
||||
name: "Convert namespace import to named imports",
|
||||
description: Diagnostics.Convert_namespace_import_to_named_imports.message,
|
||||
kind: "refactor.rewrite.import.named",
|
||||
};
|
||||
const namedToNamespaceAction = {
|
||||
name: "Convert named imports to namespace import",
|
||||
description: Diagnostics.Convert_named_imports_to_namespace_import.message,
|
||||
kind: "refactor.rewrite.import.namespace",
|
||||
};
|
||||
|
||||
registerRefactor(refactorName, {
|
||||
kinds: [
|
||||
namespaceToNamedAction.kind,
|
||||
namedToNamespaceAction.kind
|
||||
],
|
||||
getAvailableActions(context): readonly ApplicableRefactorInfo[] {
|
||||
const i = getImportToConvert(context, context.triggerReason === "invoked");
|
||||
if (!i) return emptyArray;
|
||||
const info = getImportToConvert(context, context.triggerReason === "invoked");
|
||||
if (!info) return emptyArray;
|
||||
|
||||
if (i.error === undefined) {
|
||||
const description = i.info.kind === SyntaxKind.NamespaceImport ? Diagnostics.Convert_namespace_import_to_named_imports.message : Diagnostics.Convert_named_imports_to_namespace_import.message;
|
||||
const actionName = i.info.kind === SyntaxKind.NamespaceImport ? actionNameNamespaceToNamed : actionNameNamedToNamespace;
|
||||
return [{ name: refactorName, description, actions: [{ name: actionName, description }] }];
|
||||
if (!isRefactorErrorInfo(info)) {
|
||||
const namespaceImport = info.kind === SyntaxKind.NamespaceImport;
|
||||
const action = namespaceImport ? namespaceToNamedAction : namedToNamespaceAction;
|
||||
return [{ name: refactorName, description: action.description, actions: [action] }];
|
||||
}
|
||||
|
||||
if (context.preferences.provideRefactorNotApplicableReason) {
|
||||
return [
|
||||
{ name: refactorName, description: Diagnostics.Convert_namespace_import_to_named_imports.message, actions: [{ name: actionNameNamespaceToNamed, description: Diagnostics.Convert_namespace_import_to_named_imports.message, notApplicableReason: i.error }] },
|
||||
{ name: refactorName, description: Diagnostics.Convert_named_imports_to_namespace_import.message, actions: [{ name: actionNameNamedToNamespace, description: Diagnostics.Convert_named_imports_to_namespace_import.message, notApplicableReason: i.error }] }
|
||||
{ name: refactorName, description: namespaceToNamedAction.description,
|
||||
actions: [{ ...namespaceToNamedAction, notApplicableReason: info.error }] },
|
||||
{ name: refactorName, description: namedToNamespaceAction.description,
|
||||
actions: [{ ...namedToNamespaceAction, notApplicableReason: info.error }] }
|
||||
];
|
||||
}
|
||||
|
||||
return emptyArray;
|
||||
},
|
||||
getEditsForAction(context, actionName): RefactorEditInfo {
|
||||
Debug.assert(actionName === actionNameNamespaceToNamed || actionName === actionNameNamedToNamespace, "Unexpected action name");
|
||||
const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, context.program, t, Debug.checkDefined(getImportToConvert(context)?.info, "Context must provide an import to convert")));
|
||||
Debug.assert(actionName === namespaceToNamedAction.name || actionName === namedToNamespaceAction.name, "Unexpected action name");
|
||||
const info = getImportToConvert(context);
|
||||
Debug.assert(info && !isRefactorErrorInfo(info), "Expected applicable refactor info");
|
||||
const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, context.program, t, info));
|
||||
return { edits, renameFilename: undefined, renameLocation: undefined };
|
||||
}
|
||||
});
|
||||
|
||||
// Can convert imports of the form `import * as m from "m";` or `import d, { x, y } from "m";`.
|
||||
function getImportToConvert(context: RefactorContext, considerPartialSpans = true): NamedImportBindingsOrError | undefined {
|
||||
function getImportToConvert(context: RefactorContext, considerPartialSpans = true): NamedImportBindings | RefactorErrorInfo | undefined {
|
||||
const { file } = context;
|
||||
const span = getRefactorContextSpan(context);
|
||||
const token = getTokenAtPosition(file, span.start);
|
||||
@@ -57,7 +66,7 @@ namespace ts.refactor {
|
||||
return { error: getLocaleSpecificMessage(Diagnostics.Could_not_find_namespace_import_or_named_imports) };
|
||||
}
|
||||
|
||||
return { info: importClause.namedBindings };
|
||||
return importClause.namedBindings;
|
||||
}
|
||||
|
||||
function doChange(sourceFile: SourceFile, program: Program, changes: textChanges.ChangeTracker, toConvert: NamedImportBindings): void {
|
||||
|
||||
@@ -2,8 +2,17 @@
|
||||
namespace ts.refactor.addOrRemoveBracesToArrowFunction {
|
||||
const refactorName = "Convert overload list to single signature";
|
||||
const refactorDescription = Diagnostics.Convert_overload_list_to_single_signature.message;
|
||||
registerRefactor(refactorName, { getEditsForAction, getAvailableActions });
|
||||
|
||||
const functionOverloadAction = {
|
||||
name: refactorName,
|
||||
description: refactorDescription,
|
||||
kind: "refactor.rewrite.function.overloadList",
|
||||
};
|
||||
registerRefactor(refactorName, {
|
||||
kinds: [functionOverloadAction.kind],
|
||||
getEditsForAction,
|
||||
getAvailableActions
|
||||
});
|
||||
|
||||
function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] {
|
||||
const { file, startPosition, program } = context;
|
||||
@@ -13,10 +22,7 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction {
|
||||
return [{
|
||||
name: refactorName,
|
||||
description: refactorDescription,
|
||||
actions: [{
|
||||
name: refactorName,
|
||||
description: refactorDescription
|
||||
}]
|
||||
actions: [functionOverloadAction]
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,18 @@
|
||||
namespace ts.refactor.convertParamsToDestructuredObject {
|
||||
const refactorName = "Convert parameters to destructured object";
|
||||
const minimumParameterLength = 2;
|
||||
registerRefactor(refactorName, { getEditsForAction, getAvailableActions });
|
||||
const refactorDescription = getLocaleSpecificMessage(Diagnostics.Convert_parameters_to_destructured_object);
|
||||
|
||||
const toDestructuredAction = {
|
||||
name: refactorName,
|
||||
description: refactorDescription,
|
||||
kind: "refactor.rewrite.parameters.toDestructured"
|
||||
};
|
||||
registerRefactor(refactorName, {
|
||||
kinds: [toDestructuredAction.kind],
|
||||
getEditsForAction,
|
||||
getAvailableActions
|
||||
});
|
||||
|
||||
function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] {
|
||||
const { file, startPosition } = context;
|
||||
@@ -12,14 +22,10 @@ namespace ts.refactor.convertParamsToDestructuredObject {
|
||||
const functionDeclaration = getFunctionDeclarationAtPosition(file, startPosition, context.program.getTypeChecker());
|
||||
if (!functionDeclaration) return emptyArray;
|
||||
|
||||
const description = getLocaleSpecificMessage(Diagnostics.Convert_parameters_to_destructured_object);
|
||||
return [{
|
||||
name: refactorName,
|
||||
description,
|
||||
actions: [{
|
||||
name: refactorName,
|
||||
description
|
||||
}]
|
||||
description: refactorDescription,
|
||||
actions: [toDestructuredAction]
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,16 @@ namespace ts.refactor.convertStringOrTemplateLiteral {
|
||||
const refactorName = "Convert to template string";
|
||||
const refactorDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_template_string);
|
||||
|
||||
registerRefactor(refactorName, { getEditsForAction, getAvailableActions });
|
||||
const convertStringAction = {
|
||||
name: refactorName,
|
||||
description: refactorDescription,
|
||||
kind: "refactor.rewrite.string"
|
||||
};
|
||||
registerRefactor(refactorName, {
|
||||
kinds: [convertStringAction.kind],
|
||||
getEditsForAction,
|
||||
getAvailableActions
|
||||
});
|
||||
|
||||
function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] {
|
||||
const { file, startPosition } = context;
|
||||
@@ -12,7 +21,13 @@ namespace ts.refactor.convertStringOrTemplateLiteral {
|
||||
const refactorInfo: ApplicableRefactorInfo = { name: refactorName, description: refactorDescription, actions: [] };
|
||||
|
||||
if (isBinaryExpression(maybeBinary) && isStringConcatenationValid(maybeBinary)) {
|
||||
refactorInfo.actions.push({ name: refactorName, description: refactorDescription });
|
||||
refactorInfo.actions.push(convertStringAction);
|
||||
return [refactorInfo];
|
||||
}
|
||||
else if (context.preferences.provideRefactorNotApplicableReason) {
|
||||
refactorInfo.actions.push({ ...convertStringAction,
|
||||
notApplicableReason: getLocaleSpecificMessage(Diagnostics.Can_only_convert_string_concatenation)
|
||||
});
|
||||
return [refactorInfo];
|
||||
}
|
||||
return emptyArray;
|
||||
|
||||
@@ -3,20 +3,26 @@ namespace ts.refactor.convertToOptionalChainExpression {
|
||||
const refactorName = "Convert to optional chain expression";
|
||||
const convertToOptionalChainExpressionMessage = getLocaleSpecificMessage(Diagnostics.Convert_to_optional_chain_expression);
|
||||
|
||||
registerRefactor(refactorName, { getAvailableActions, getEditsForAction });
|
||||
const toOptionalChainAction = {
|
||||
name: refactorName,
|
||||
description: convertToOptionalChainExpressionMessage,
|
||||
kind: "refactor.rewrite.expression.optionalChain",
|
||||
};
|
||||
registerRefactor(refactorName, {
|
||||
kinds: [toOptionalChainAction.kind],
|
||||
getAvailableActions,
|
||||
getEditsForAction
|
||||
});
|
||||
|
||||
function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] {
|
||||
const info = getInfo(context, context.triggerReason === "invoked");
|
||||
if (!info) return emptyArray;
|
||||
|
||||
if (!info.error) {
|
||||
if (!isRefactorErrorInfo(info)) {
|
||||
return [{
|
||||
name: refactorName,
|
||||
description: convertToOptionalChainExpressionMessage,
|
||||
actions: [{
|
||||
name: refactorName,
|
||||
description: convertToOptionalChainExpressionMessage
|
||||
}]
|
||||
actions: [toOptionalChainAction],
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -24,11 +30,7 @@ namespace ts.refactor.convertToOptionalChainExpression {
|
||||
return [{
|
||||
name: refactorName,
|
||||
description: convertToOptionalChainExpressionMessage,
|
||||
actions: [{
|
||||
name: refactorName,
|
||||
description: convertToOptionalChainExpressionMessage,
|
||||
notApplicableReason: info.error
|
||||
}]
|
||||
actions: [{ ...toOptionalChainAction, notApplicableReason: info.error }],
|
||||
}];
|
||||
}
|
||||
return emptyArray;
|
||||
@@ -36,24 +38,16 @@ namespace ts.refactor.convertToOptionalChainExpression {
|
||||
|
||||
function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined {
|
||||
const info = getInfo(context);
|
||||
if (!info || !info.info) return undefined;
|
||||
Debug.assert(info && !isRefactorErrorInfo(info), "Expected applicable refactor info");
|
||||
const edits = textChanges.ChangeTracker.with(context, t =>
|
||||
doChange(context.file, context.program.getTypeChecker(), t, Debug.checkDefined(info.info, "context must have info"), actionName)
|
||||
doChange(context.file, context.program.getTypeChecker(), t, info, actionName)
|
||||
);
|
||||
return { edits, renameFilename: undefined, renameLocation: undefined };
|
||||
}
|
||||
|
||||
type InfoOrError = {
|
||||
info: Info,
|
||||
error?: never;
|
||||
} | {
|
||||
info?: never,
|
||||
error: string;
|
||||
};
|
||||
|
||||
type Occurrence = PropertyAccessExpression | ElementAccessExpression | Identifier;
|
||||
|
||||
interface Info {
|
||||
interface OptionalChainInfo {
|
||||
finalExpression: PropertyAccessExpression | ElementAccessExpression | CallExpression,
|
||||
occurrences: Occurrence[],
|
||||
expression: ValidExpression,
|
||||
@@ -83,7 +77,7 @@ namespace ts.refactor.convertToOptionalChainExpression {
|
||||
return isValidExpression(node) || isValidStatement(node);
|
||||
}
|
||||
|
||||
function getInfo(context: RefactorContext, considerEmptySpans = true): InfoOrError | undefined {
|
||||
function getInfo(context: RefactorContext, considerEmptySpans = true): OptionalChainInfo | RefactorErrorInfo | undefined {
|
||||
const { file, program } = context;
|
||||
const span = getRefactorContextSpan(context);
|
||||
|
||||
@@ -103,7 +97,7 @@ namespace ts.refactor.convertToOptionalChainExpression {
|
||||
return isConditionalExpression(expression) ? getConditionalInfo(expression, checker) : getBinaryInfo(expression);
|
||||
}
|
||||
|
||||
function getConditionalInfo(expression: ConditionalExpression, checker: TypeChecker): InfoOrError | undefined {
|
||||
function getConditionalInfo(expression: ConditionalExpression, checker: TypeChecker): OptionalChainInfo | RefactorErrorInfo | undefined {
|
||||
const condition = expression.condition;
|
||||
const finalExpression = getFinalExpressionInChain(expression.whenTrue);
|
||||
|
||||
@@ -113,16 +107,16 @@ namespace ts.refactor.convertToOptionalChainExpression {
|
||||
|
||||
if ((isPropertyAccessExpression(condition) || isIdentifier(condition))
|
||||
&& getMatchingStart(condition, finalExpression.expression)) {
|
||||
return { info: { finalExpression, occurrences: [condition], expression } };
|
||||
return { finalExpression, occurrences: [condition], expression };
|
||||
}
|
||||
else if (isBinaryExpression(condition)) {
|
||||
const occurrences = getOccurrencesInExpression(finalExpression.expression, condition);
|
||||
return occurrences ? { info: { finalExpression, occurrences, expression } } :
|
||||
return occurrences ? { finalExpression, occurrences, expression } :
|
||||
{ error: getLocaleSpecificMessage(Diagnostics.Could_not_find_matching_access_expressions) };
|
||||
}
|
||||
}
|
||||
|
||||
function getBinaryInfo(expression: BinaryExpression): InfoOrError | undefined {
|
||||
function getBinaryInfo(expression: BinaryExpression): OptionalChainInfo | RefactorErrorInfo | undefined {
|
||||
if (expression.operatorToken.kind !== SyntaxKind.AmpersandAmpersandToken) {
|
||||
return { error: getLocaleSpecificMessage(Diagnostics.Can_only_convert_logical_AND_access_chains) };
|
||||
};
|
||||
@@ -131,7 +125,7 @@ namespace ts.refactor.convertToOptionalChainExpression {
|
||||
if (!finalExpression) return { error: getLocaleSpecificMessage(Diagnostics.Could_not_find_convertible_access_expression) };
|
||||
|
||||
const occurrences = getOccurrencesInExpression(finalExpression.expression, expression.left);
|
||||
return occurrences ? { info: { finalExpression, occurrences, expression } } :
|
||||
return occurrences ? { finalExpression, occurrences, expression } :
|
||||
{ error: getLocaleSpecificMessage(Diagnostics.Could_not_find_matching_access_expressions) };
|
||||
}
|
||||
|
||||
@@ -288,7 +282,7 @@ namespace ts.refactor.convertToOptionalChainExpression {
|
||||
return toConvert;
|
||||
}
|
||||
|
||||
function doChange(sourceFile: SourceFile, checker: TypeChecker, changes: textChanges.ChangeTracker, info: Info, _actionName: string): void {
|
||||
function doChange(sourceFile: SourceFile, checker: TypeChecker, changes: textChanges.ChangeTracker, info: OptionalChainInfo, _actionName: string): void {
|
||||
const { finalExpression, occurrences, expression } = info;
|
||||
const firstOccurrence = occurrences[occurrences.length - 1];
|
||||
const convertedChain = convertOccurrences(checker, finalExpression, occurrences);
|
||||
|
||||
@@ -1,39 +1,56 @@
|
||||
/* @internal */
|
||||
namespace ts.refactor.extractSymbol {
|
||||
const refactorName = "Extract Symbol";
|
||||
registerRefactor(refactorName, { getAvailableActions, getEditsForAction });
|
||||
|
||||
const extractConstantAction = {
|
||||
name: "Extract Constant",
|
||||
description: getLocaleSpecificMessage(Diagnostics.Extract_constant),
|
||||
kind: "refactor.extract.constant",
|
||||
};
|
||||
const extractFunctionAction = {
|
||||
name: "Extract Function",
|
||||
description: getLocaleSpecificMessage(Diagnostics.Extract_function),
|
||||
kind: "refactor.extract.function",
|
||||
};
|
||||
registerRefactor(refactorName, {
|
||||
kinds: [
|
||||
extractConstantAction.kind,
|
||||
extractFunctionAction.kind
|
||||
],
|
||||
getAvailableActions,
|
||||
getEditsForAction
|
||||
});
|
||||
|
||||
/**
|
||||
* Compute the associated code actions
|
||||
* Exported for tests.
|
||||
*/
|
||||
export function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] {
|
||||
const requestedRefactor = context.kind;
|
||||
const rangeToExtract = getRangeToExtract(context.file, getRefactorContextSpan(context), context.triggerReason === "invoked");
|
||||
|
||||
const targetRange = rangeToExtract.targetRange;
|
||||
|
||||
if (targetRange === undefined) {
|
||||
if (!rangeToExtract.errors || rangeToExtract.errors.length === 0 || !context.preferences.provideRefactorNotApplicableReason) {
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
return [{
|
||||
name: refactorName,
|
||||
description: getLocaleSpecificMessage(Diagnostics.Extract_function),
|
||||
actions: [{
|
||||
description: getLocaleSpecificMessage(Diagnostics.Extract_function),
|
||||
name: "function_extract_error",
|
||||
notApplicableReason: getStringError(rangeToExtract.errors)
|
||||
}]
|
||||
},
|
||||
{
|
||||
name: refactorName,
|
||||
description: getLocaleSpecificMessage(Diagnostics.Extract_constant),
|
||||
actions: [{
|
||||
description: getLocaleSpecificMessage(Diagnostics.Extract_constant),
|
||||
name: "constant_extract_error",
|
||||
notApplicableReason: getStringError(rangeToExtract.errors)
|
||||
}]
|
||||
}];
|
||||
const errors = [];
|
||||
if (refactorKindBeginsWith(extractFunctionAction.kind, requestedRefactor)) {
|
||||
errors.push({
|
||||
name: refactorName,
|
||||
description: extractFunctionAction.description,
|
||||
actions: [{ ...extractFunctionAction, notApplicableReason: getStringError(rangeToExtract.errors) }]
|
||||
});
|
||||
}
|
||||
if (refactorKindBeginsWith(extractConstantAction.kind, requestedRefactor)) {
|
||||
errors.push({
|
||||
name: refactorName,
|
||||
description: extractConstantAction.description,
|
||||
actions: [{ ...extractConstantAction, notApplicableReason: getStringError(rangeToExtract.errors) }]
|
||||
});
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
const extractions = getPossibleExtractions(targetRange, context);
|
||||
@@ -53,46 +70,54 @@ namespace ts.refactor.extractSymbol {
|
||||
let i = 0;
|
||||
for (const { functionExtraction, constantExtraction } of extractions) {
|
||||
const description = functionExtraction.description;
|
||||
if (functionExtraction.errors.length === 0) {
|
||||
// Don't issue refactorings with duplicated names.
|
||||
// Scopes come back in "innermost first" order, so extractions will
|
||||
// preferentially go into nearer scopes
|
||||
if (!usedFunctionNames.has(description)) {
|
||||
usedFunctionNames.set(description, true);
|
||||
functionActions.push({
|
||||
if(refactorKindBeginsWith(extractFunctionAction.kind, requestedRefactor)){
|
||||
if (functionExtraction.errors.length === 0) {
|
||||
// Don't issue refactorings with duplicated names.
|
||||
// Scopes come back in "innermost first" order, so extractions will
|
||||
// preferentially go into nearer scopes
|
||||
if (!usedFunctionNames.has(description)) {
|
||||
usedFunctionNames.set(description, true);
|
||||
functionActions.push({
|
||||
description,
|
||||
name: `function_scope_${i}`,
|
||||
kind: extractFunctionAction.kind
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (!innermostErrorFunctionAction) {
|
||||
innermostErrorFunctionAction = {
|
||||
description,
|
||||
name: `function_scope_${i}`
|
||||
});
|
||||
name: `function_scope_${i}`,
|
||||
notApplicableReason: getStringError(functionExtraction.errors),
|
||||
kind: extractFunctionAction.kind
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (!innermostErrorFunctionAction) {
|
||||
innermostErrorFunctionAction = {
|
||||
description,
|
||||
name: `function_scope_${i}`,
|
||||
notApplicableReason: getStringError(functionExtraction.errors)
|
||||
};
|
||||
}
|
||||
|
||||
// Skip these since we don't have a way to report errors yet
|
||||
if (constantExtraction.errors.length === 0) {
|
||||
// Don't issue refactorings with duplicated names.
|
||||
// Scopes come back in "innermost first" order, so extractions will
|
||||
// preferentially go into nearer scopes
|
||||
const description = constantExtraction.description;
|
||||
if (!usedConstantNames.has(description)) {
|
||||
usedConstantNames.set(description, true);
|
||||
constantActions.push({
|
||||
if(refactorKindBeginsWith(extractConstantAction.kind, requestedRefactor)) {
|
||||
if (constantExtraction.errors.length === 0) {
|
||||
// Don't issue refactorings with duplicated names.
|
||||
// Scopes come back in "innermost first" order, so extractions will
|
||||
// preferentially go into nearer scopes
|
||||
const description = constantExtraction.description;
|
||||
if (!usedConstantNames.has(description)) {
|
||||
usedConstantNames.set(description, true);
|
||||
constantActions.push({
|
||||
description,
|
||||
name: `constant_scope_${i}`,
|
||||
kind: extractConstantAction.kind
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (!innermostErrorConstantAction) {
|
||||
innermostErrorConstantAction = {
|
||||
description,
|
||||
name: `constant_scope_${i}`
|
||||
});
|
||||
name: `constant_scope_${i}`,
|
||||
notApplicableReason: getStringError(constantExtraction.errors),
|
||||
kind: extractConstantAction.kind
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (!innermostErrorConstantAction) {
|
||||
innermostErrorConstantAction = {
|
||||
description,
|
||||
name: `constant_scope_${i}`,
|
||||
notApplicableReason: getStringError(constantExtraction.errors)
|
||||
};
|
||||
}
|
||||
|
||||
// *do* increment i anyway because we'll look for the i-th scope
|
||||
@@ -106,7 +131,7 @@ namespace ts.refactor.extractSymbol {
|
||||
infos.push({
|
||||
name: refactorName,
|
||||
description: getLocaleSpecificMessage(Diagnostics.Extract_function),
|
||||
actions: functionActions
|
||||
actions: functionActions,
|
||||
});
|
||||
}
|
||||
else if (context.preferences.provideRefactorNotApplicableReason && innermostErrorFunctionAction) {
|
||||
|
||||
@@ -1,25 +1,39 @@
|
||||
/* @internal */
|
||||
namespace ts.refactor {
|
||||
const refactorName = "Extract type";
|
||||
const extractToTypeAlias = "Extract to type alias";
|
||||
const extractToInterface = "Extract to interface";
|
||||
const extractToTypeDef = "Extract to typedef";
|
||||
|
||||
const extractToTypeAliasAction = {
|
||||
name: "Extract to type alias",
|
||||
description: getLocaleSpecificMessage(Diagnostics.Extract_to_type_alias),
|
||||
kind: "refactor.extract.type",
|
||||
};
|
||||
const extractToInterfaceAction = {
|
||||
name: "Extract to interface",
|
||||
description: getLocaleSpecificMessage(Diagnostics.Extract_to_interface),
|
||||
kind: "refactor.extract.interface",
|
||||
};
|
||||
const extractToTypeDefAction = {
|
||||
name: "Extract to typedef",
|
||||
description: getLocaleSpecificMessage(Diagnostics.Extract_to_typedef),
|
||||
kind: "refactor.extract.typedef"
|
||||
};
|
||||
|
||||
registerRefactor(refactorName, {
|
||||
kinds: [
|
||||
extractToTypeAliasAction.kind,
|
||||
extractToInterfaceAction.kind,
|
||||
extractToTypeDefAction.kind
|
||||
],
|
||||
getAvailableActions(context): readonly ApplicableRefactorInfo[] {
|
||||
const info = getRangeToExtract(context, context.triggerReason === "invoked");
|
||||
if (!info) return emptyArray;
|
||||
|
||||
if (info.error === undefined) {
|
||||
if (!isRefactorErrorInfo(info)) {
|
||||
return [{
|
||||
name: refactorName,
|
||||
description: getLocaleSpecificMessage(Diagnostics.Extract_type),
|
||||
actions: info.info.isJS ? [{
|
||||
name: extractToTypeDef, description: getLocaleSpecificMessage(Diagnostics.Extract_to_typedef)
|
||||
}] : append([{
|
||||
name: extractToTypeAlias, description: getLocaleSpecificMessage(Diagnostics.Extract_to_type_alias)
|
||||
}], info.info.typeElements && {
|
||||
name: extractToInterface, description: getLocaleSpecificMessage(Diagnostics.Extract_to_interface)
|
||||
})
|
||||
actions: info.isJS ?
|
||||
[extractToTypeDefAction] : append([extractToTypeAliasAction], info.typeElements && extractToInterfaceAction)
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -28,9 +42,9 @@ namespace ts.refactor {
|
||||
name: refactorName,
|
||||
description: getLocaleSpecificMessage(Diagnostics.Extract_type),
|
||||
actions: [
|
||||
{ name: extractToTypeDef, description: getLocaleSpecificMessage(Diagnostics.Extract_to_typedef), notApplicableReason: info.error },
|
||||
{ name: extractToTypeAlias, description: getLocaleSpecificMessage(Diagnostics.Extract_to_type_alias), notApplicableReason: info.error },
|
||||
{ name: extractToInterface, description: getLocaleSpecificMessage(Diagnostics.Extract_to_interface), notApplicableReason: info.error },
|
||||
{ ...extractToTypeDefAction, notApplicableReason: info.error },
|
||||
{ ...extractToTypeAliasAction, notApplicableReason: info.error },
|
||||
{ ...extractToInterfaceAction, notApplicableReason: info.error },
|
||||
]
|
||||
}];
|
||||
}
|
||||
@@ -39,18 +53,19 @@ namespace ts.refactor {
|
||||
},
|
||||
getEditsForAction(context, actionName): RefactorEditInfo {
|
||||
const { file, } = context;
|
||||
const info = Debug.checkDefined(getRangeToExtract(context)?.info, "Expected to find a range to extract");
|
||||
const info = getRangeToExtract(context);
|
||||
Debug.assert(info && !isRefactorErrorInfo(info), "Expected to find a range to extract");
|
||||
|
||||
const name = getUniqueName("NewType", file);
|
||||
const edits = textChanges.ChangeTracker.with(context, changes => {
|
||||
switch (actionName) {
|
||||
case extractToTypeAlias:
|
||||
case extractToTypeAliasAction.name:
|
||||
Debug.assert(!info.isJS, "Invalid actionName/JS combo");
|
||||
return doTypeAliasChange(changes, file, name, info);
|
||||
case extractToTypeDef:
|
||||
case extractToTypeDefAction.name:
|
||||
Debug.assert(info.isJS, "Invalid actionName/JS combo");
|
||||
return doTypedefChange(changes, file, name, info);
|
||||
case extractToInterface:
|
||||
case extractToInterfaceAction.name:
|
||||
Debug.assert(!info.isJS && !!info.typeElements, "Invalid actionName/JS combo");
|
||||
return doInterfaceChange(changes, file, name, info as InterfaceInfo);
|
||||
default:
|
||||
@@ -72,16 +87,9 @@ namespace ts.refactor {
|
||||
isJS: boolean; selection: TypeNode; firstStatement: Statement; typeParameters: readonly TypeParameterDeclaration[]; typeElements: readonly TypeElement[];
|
||||
}
|
||||
|
||||
type Info = TypeAliasInfo | InterfaceInfo;
|
||||
type InfoOrError = {
|
||||
info: Info,
|
||||
error?: never
|
||||
} | {
|
||||
info?: never,
|
||||
error: string
|
||||
};
|
||||
type ExtractInfo = TypeAliasInfo | InterfaceInfo;
|
||||
|
||||
function getRangeToExtract(context: RefactorContext, considerEmptySpans = true): InfoOrError | undefined {
|
||||
function getRangeToExtract(context: RefactorContext, considerEmptySpans = true): ExtractInfo | RefactorErrorInfo | undefined {
|
||||
const { file, startPosition } = context;
|
||||
const isJS = isSourceFileJS(file);
|
||||
const current = getTokenAtPosition(file, startPosition);
|
||||
@@ -98,7 +106,7 @@ namespace ts.refactor {
|
||||
if (!typeParameters) return { error: getLocaleSpecificMessage(Diagnostics.No_type_could_be_extracted_from_this_type_node) };
|
||||
|
||||
const typeElements = flattenTypeLiteralNodeReference(checker, selection);
|
||||
return { info: { isJS, selection, firstStatement, typeParameters, typeElements } };
|
||||
return { isJS, selection, firstStatement, typeParameters, typeElements };
|
||||
}
|
||||
|
||||
function flattenTypeLiteralNodeReference(checker: TypeChecker, node: TypeNode | undefined): readonly TypeElement[] | undefined {
|
||||
@@ -209,7 +217,7 @@ namespace ts.refactor {
|
||||
changes.replaceNode(file, selection, factory.createTypeReferenceNode(name, typeParameters.map(id => factory.createTypeReferenceNode(id.name, /* typeArguments */ undefined))), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: textChanges.TrailingTriviaOption.ExcludeWhitespace });
|
||||
}
|
||||
|
||||
function doTypedefChange(changes: textChanges.ChangeTracker, file: SourceFile, name: string, info: Info) {
|
||||
function doTypedefChange(changes: textChanges.ChangeTracker, file: SourceFile, name: string, info: ExtractInfo) {
|
||||
const { firstStatement, selection, typeParameters } = info;
|
||||
|
||||
const node = factory.createJSDocTypedefTag(
|
||||
|
||||
@@ -2,18 +2,25 @@
|
||||
namespace ts.refactor.generateGetAccessorAndSetAccessor {
|
||||
const actionName = "Generate 'get' and 'set' accessors";
|
||||
const actionDescription = Diagnostics.Generate_get_and_set_accessors.message;
|
||||
|
||||
const generateGetSetAction = {
|
||||
name: actionName,
|
||||
description: actionDescription,
|
||||
kind: "refactor.rewrite.property.generateAccessors",
|
||||
};
|
||||
registerRefactor(actionName, {
|
||||
kinds: [generateGetSetAction.kind],
|
||||
getEditsForAction(context, actionName) {
|
||||
if (!context.endPosition) return undefined;
|
||||
const info = codefix.getAccessorConvertiblePropertyAtPosition(context.file, context.program, context.startPosition, context.endPosition);
|
||||
if (!info || !info.info) return undefined;
|
||||
Debug.assert(info && !isRefactorErrorInfo(info), "Expected applicable refactor info");
|
||||
const edits = codefix.generateAccessorFromProperty(context.file, context.program, context.startPosition, context.endPosition, context, actionName);
|
||||
if (!edits) return undefined;
|
||||
|
||||
const renameFilename = context.file.fileName;
|
||||
const nameNeedRename = info.info.renameAccessor ? info.info.accessorName : info.info.fieldName;
|
||||
const nameNeedRename = info.renameAccessor ? info.accessorName : info.fieldName;
|
||||
const renameLocationOffset = isIdentifier(nameNeedRename) ? 0 : -1;
|
||||
const renameLocation = renameLocationOffset + getRenameLocation(edits, renameFilename, nameNeedRename.text, /*preferLastLocation*/ isParameter(info.info.declaration));
|
||||
const renameLocation = renameLocationOffset + getRenameLocation(edits, renameFilename, nameNeedRename.text, /*preferLastLocation*/ isParameter(info.declaration));
|
||||
|
||||
return { renameFilename, renameLocation, edits };
|
||||
},
|
||||
@@ -22,16 +29,11 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
|
||||
const info = codefix.getAccessorConvertiblePropertyAtPosition(context.file, context.program, context.startPosition, context.endPosition, context.triggerReason === "invoked");
|
||||
if (!info) return emptyArray;
|
||||
|
||||
if (!info.error) {
|
||||
if (!isRefactorErrorInfo(info)) {
|
||||
return [{
|
||||
name: actionName,
|
||||
description: actionDescription,
|
||||
actions: [
|
||||
{
|
||||
name: actionName,
|
||||
description: actionDescription
|
||||
}
|
||||
]
|
||||
actions: [generateGetSetAction],
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -39,11 +41,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
|
||||
return [{
|
||||
name: actionName,
|
||||
description: actionDescription,
|
||||
actions: [{
|
||||
name: actionName,
|
||||
description: actionDescription,
|
||||
notApplicableReason: info.error
|
||||
}]
|
||||
actions: [{ ...generateGetSetAction, notApplicableReason: info.error }],
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/* @internal */
|
||||
namespace ts.refactor {
|
||||
/**
|
||||
* Returned by refactor funtions when some error message needs to be surfaced to users.
|
||||
*/
|
||||
export interface RefactorErrorInfo {
|
||||
error: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if some refactor info has refactor error info.
|
||||
*/
|
||||
export function isRefactorErrorInfo(info: unknown): info is RefactorErrorInfo {
|
||||
return (info as RefactorErrorInfo).error !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if string "known" begins with string "requested".
|
||||
* Used to match requested kinds with a known kind.
|
||||
*/
|
||||
export function refactorKindBeginsWith(known: string, requested: string | undefined): boolean {
|
||||
if(!requested) return true;
|
||||
return known.substr(0, requested.length) === requested;
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,21 @@
|
||||
namespace ts.refactor.inferFunctionReturnType {
|
||||
const refactorName = "Infer function return type";
|
||||
const refactorDescription = Diagnostics.Infer_function_return_type.message;
|
||||
registerRefactor(refactorName, { getEditsForAction, getAvailableActions });
|
||||
|
||||
const inferReturnTypeAction = {
|
||||
name: refactorName,
|
||||
description: refactorDescription,
|
||||
kind: "refactor.rewrite.function.returnType"
|
||||
};
|
||||
registerRefactor(refactorName, {
|
||||
kinds: [inferReturnTypeAction.kind],
|
||||
getEditsForAction,
|
||||
getAvailableActions
|
||||
});
|
||||
|
||||
function getEditsForAction(context: RefactorContext): RefactorEditInfo | undefined {
|
||||
const info = getInfo(context);
|
||||
if (info) {
|
||||
if (info && !isRefactorErrorInfo(info)) {
|
||||
const edits = textChanges.ChangeTracker.with(context, t =>
|
||||
t.tryInsertTypeAnnotation(context.file, info.declaration, info.returnTypeNode));
|
||||
return { renameFilename: undefined, renameLocation: undefined, edits };
|
||||
@@ -16,14 +26,19 @@ namespace ts.refactor.inferFunctionReturnType {
|
||||
|
||||
function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] {
|
||||
const info = getInfo(context);
|
||||
if (info) {
|
||||
if (!info) return emptyArray;
|
||||
if (!isRefactorErrorInfo(info)) {
|
||||
return [{
|
||||
name: refactorName,
|
||||
description: refactorDescription,
|
||||
actions: [{
|
||||
name: refactorName,
|
||||
description: refactorDescription
|
||||
}]
|
||||
actions: [inferReturnTypeAction]
|
||||
}];
|
||||
}
|
||||
if (context.preferences.provideRefactorNotApplicableReason) {
|
||||
return [{
|
||||
name: refactorName,
|
||||
description: refactorDescription,
|
||||
actions: [{ ...inferReturnTypeAction, notApplicableReason: info.error }]
|
||||
}];
|
||||
}
|
||||
return emptyArray;
|
||||
@@ -35,21 +50,25 @@ namespace ts.refactor.inferFunctionReturnType {
|
||||
| ArrowFunction
|
||||
| MethodDeclaration;
|
||||
|
||||
interface Info {
|
||||
interface FunctionInfo {
|
||||
declaration: ConvertibleDeclaration;
|
||||
returnTypeNode: TypeNode;
|
||||
}
|
||||
|
||||
function getInfo(context: RefactorContext): Info | undefined {
|
||||
if (isInJSFile(context.file)) return;
|
||||
function getInfo(context: RefactorContext): FunctionInfo | RefactorErrorInfo | undefined {
|
||||
if (isInJSFile(context.file) || !refactorKindBeginsWith(inferReturnTypeAction.kind, context.kind)) return;
|
||||
|
||||
const token = getTokenAtPosition(context.file, context.startPosition);
|
||||
const declaration = findAncestor(token, isConvertibleDeclaration);
|
||||
if (!declaration || !declaration.body || declaration.type) return;
|
||||
if (!declaration || !declaration.body || declaration.type) {
|
||||
return { error: getLocaleSpecificMessage(Diagnostics.Return_type_must_be_inferred_from_a_function) };
|
||||
}
|
||||
|
||||
const typeChecker = context.program.getTypeChecker();
|
||||
const returnType = tryGetReturnType(typeChecker, declaration);
|
||||
if (!returnType) return;
|
||||
if (!returnType) {
|
||||
return { error: getLocaleSpecificMessage(Diagnostics.Could_not_determine_function_return_type) };
|
||||
};
|
||||
|
||||
const returnTypeNode = typeChecker.typeToTypeNode(returnType, declaration, NodeBuilderFlags.NoTruncation);
|
||||
if (returnTypeNode) {
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
/* @internal */
|
||||
namespace ts.refactor {
|
||||
const refactorName = "Move to a new file";
|
||||
const description = getLocaleSpecificMessage(Diagnostics.Move_to_a_new_file);
|
||||
|
||||
const moveToNewFileAction = {
|
||||
name: refactorName,
|
||||
description,
|
||||
kind: "refactor.move.newFile",
|
||||
};
|
||||
registerRefactor(refactorName, {
|
||||
kinds: [moveToNewFileAction.kind],
|
||||
getAvailableActions(context): readonly ApplicableRefactorInfo[] {
|
||||
if (!context.preferences.allowTextChangesInNewFiles || getStatementsToMove(context) === undefined) return emptyArray;
|
||||
const description = getLocaleSpecificMessage(Diagnostics.Move_to_a_new_file);
|
||||
return [{ name: refactorName, description, actions: [{ name: refactorName, description }] }];
|
||||
const statements = getStatementsToMove(context);
|
||||
if (context.preferences.allowTextChangesInNewFiles && statements) {
|
||||
return [{ name: refactorName, description, actions: [moveToNewFileAction] }];
|
||||
}
|
||||
if (context.preferences.provideRefactorNotApplicableReason) {
|
||||
return [{ name: refactorName, description, actions:
|
||||
[{ ...moveToNewFileAction, notApplicableReason: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_statement_or_statements) }]
|
||||
}];
|
||||
}
|
||||
return emptyArray;
|
||||
},
|
||||
getEditsForAction(context, actionName): RefactorEditInfo {
|
||||
Debug.assert(actionName === refactorName, "Wrong refactor invoked");
|
||||
|
||||
+24
-21
@@ -550,9 +550,8 @@ namespace ts {
|
||||
|
||||
getJsDocTags(): JSDocTagInfo[] {
|
||||
if (this.jsDocTags === undefined) {
|
||||
this.jsDocTags = this.declaration ? JsDoc.getJsDocTagsFromDeclarations([this.declaration]) : [];
|
||||
this.jsDocTags = this.declaration ? getJsDocTags([this.declaration], this.checker) : [];
|
||||
}
|
||||
|
||||
return this.jsDocTags;
|
||||
}
|
||||
}
|
||||
@@ -566,13 +565,26 @@ namespace ts {
|
||||
return getJSDocTags(node).some(tag => tag.tagName.text === "inheritDoc");
|
||||
}
|
||||
|
||||
function getJsDocTags(declarations: Declaration[], checker: TypeChecker): JSDocTagInfo[] {
|
||||
let tags = JsDoc.getJsDocTagsFromDeclarations(declarations);
|
||||
if (tags.length === 0 || declarations.some(hasJSDocInheritDocTag)) {
|
||||
forEachUnique(declarations, declaration => {
|
||||
const inheritedTags = findBaseOfDeclaration(checker, declaration, symbol => symbol.getJsDocTags());
|
||||
if (inheritedTags) {
|
||||
tags = [...inheritedTags, ...tags];
|
||||
}
|
||||
});
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
function getDocumentationComment(declarations: readonly Declaration[] | undefined, checker: TypeChecker | undefined): SymbolDisplayPart[] {
|
||||
if (!declarations) return emptyArray;
|
||||
|
||||
let doc = JsDoc.getJsDocCommentsFromDeclarations(declarations);
|
||||
if (doc.length === 0 || declarations.some(hasJSDocInheritDocTag)) {
|
||||
if (checker && (doc.length === 0 || declarations.some(hasJSDocInheritDocTag))) {
|
||||
forEachUnique(declarations, declaration => {
|
||||
const inheritedDocs = findInheritedJSDocComments(declaration, declaration.symbol.name, checker!); // TODO: GH#18217
|
||||
const inheritedDocs = findBaseOfDeclaration(checker, declaration, symbol => symbol.getDocumentationComment(checker));
|
||||
// TODO: GH#16312 Return a ReadonlyArray, avoid copying inheritedDocs
|
||||
if (inheritedDocs) doc = doc.length === 0 ? inheritedDocs.slice() : inheritedDocs.concat(lineBreakPart(), doc);
|
||||
});
|
||||
@@ -580,20 +592,10 @@ namespace ts {
|
||||
return doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to find JSDoc comments for possibly-inherited properties. Checks superclasses then traverses
|
||||
* implemented interfaces until a symbol is found with the same name and with documentation.
|
||||
* @param declaration The possibly-inherited declaration to find comments for.
|
||||
* @param propertyName The name of the possibly-inherited property.
|
||||
* @param typeChecker A TypeChecker, used to find inherited properties.
|
||||
* @returns A filled array of documentation comments if any were found, otherwise an empty array.
|
||||
*/
|
||||
function findInheritedJSDocComments(declaration: Declaration, propertyName: string, typeChecker: TypeChecker): readonly SymbolDisplayPart[] | undefined {
|
||||
function findBaseOfDeclaration<T>(checker: TypeChecker, declaration: Declaration, cb: (symbol: Symbol) => T[]): T[] | undefined {
|
||||
return firstDefined(declaration.parent ? getAllSuperTypeNodes(declaration.parent) : emptyArray, superTypeNode => {
|
||||
const superType = typeChecker.getTypeAtLocation(superTypeNode);
|
||||
const baseProperty = superType && typeChecker.getPropertyOfType(superType, propertyName);
|
||||
const inheritedDocs = baseProperty && baseProperty.getDocumentationComment(typeChecker);
|
||||
return inheritedDocs && inheritedDocs.length ? inheritedDocs : undefined;
|
||||
const symbol = checker.getPropertyOfType(checker.getTypeAtLocation(superTypeNode), declaration.symbol.name);
|
||||
return symbol ? cb(symbol) : undefined;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2451,7 +2453,7 @@ namespace ts {
|
||||
return Rename.getRenameInfo(program, getValidSourceFile(fileName), position, options);
|
||||
}
|
||||
|
||||
function getRefactorContext(file: SourceFile, positionOrRange: number | TextRange, preferences: UserPreferences, formatOptions?: FormatCodeSettings, triggerReason?: RefactorTriggerReason): RefactorContext {
|
||||
function getRefactorContext(file: SourceFile, positionOrRange: number | TextRange, preferences: UserPreferences, formatOptions?: FormatCodeSettings, triggerReason?: RefactorTriggerReason, kind?: string): RefactorContext {
|
||||
const [startPosition, endPosition] = typeof positionOrRange === "number" ? [positionOrRange, undefined] : [positionOrRange.pos, positionOrRange.end];
|
||||
return {
|
||||
file,
|
||||
@@ -2463,6 +2465,7 @@ namespace ts {
|
||||
cancellationToken,
|
||||
preferences,
|
||||
triggerReason,
|
||||
kind
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2470,10 +2473,10 @@ namespace ts {
|
||||
return SmartSelectionRange.getSmartSelectionRange(position, syntaxTreeCache.getCurrentSourceFile(fileName));
|
||||
}
|
||||
|
||||
function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions, triggerReason: RefactorTriggerReason): ApplicableRefactorInfo[] {
|
||||
function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions, triggerReason: RefactorTriggerReason, kind: string): ApplicableRefactorInfo[] {
|
||||
synchronizeHostData();
|
||||
const file = getValidSourceFile(fileName);
|
||||
return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, preferences, emptyOptions, triggerReason));
|
||||
return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, preferences, emptyOptions, triggerReason, kind));
|
||||
}
|
||||
|
||||
function getEditsForRefactor(
|
||||
@@ -2694,7 +2697,7 @@ namespace ts {
|
||||
return symbol ? [symbol] : emptyArray;
|
||||
}
|
||||
|
||||
const discriminatedPropertySymbols = mapDefined(contextualType.types, t => isObjectLiteralExpression(node.parent) && checker.isTypeInvalidDueToUnionDiscriminant(t, node.parent) ? undefined : t.getProperty(name));
|
||||
const discriminatedPropertySymbols = mapDefined(contextualType.types, t => (isObjectLiteralExpression(node.parent)|| isJsxAttributes(node.parent)) && checker.isTypeInvalidDueToUnionDiscriminant(t, node.parent) ? undefined : t.getProperty(name));
|
||||
if (unionSymbolOk && (discriminatedPropertySymbols.length === 0 || discriminatedPropertySymbols.length === contextualType.types.length)) {
|
||||
const symbol = contextualType.getProperty(name);
|
||||
if (symbol) return [symbol];
|
||||
|
||||
@@ -100,31 +100,42 @@ namespace ts.SymbolDisplay {
|
||||
return ScriptElementKind.unknown;
|
||||
}
|
||||
|
||||
function isDeprecatedDeclaration(decl: Declaration) {
|
||||
return !!(getCombinedNodeFlagsAlwaysIncludeJSDoc(decl) & ModifierFlags.Deprecated);
|
||||
}
|
||||
|
||||
function getNormalizedSymbolModifiers(symbol: Symbol) {
|
||||
if (symbol.declarations && symbol.declarations.length) {
|
||||
const [declaration, ...declarations] = symbol.declarations;
|
||||
// omit deprecated flag if some declarations are not deprecated
|
||||
const excludeFlags = length(declarations) && isDeprecatedDeclaration(declaration) && some(declarations, d => !isDeprecatedDeclaration(d))
|
||||
? ModifierFlags.Deprecated
|
||||
: ModifierFlags.None;
|
||||
const modifiers = getNodeModifiers(declaration, excludeFlags);
|
||||
if (modifiers) {
|
||||
return modifiers.split(",");
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export function getSymbolModifiers(typeChecker: TypeChecker, symbol: Symbol): string {
|
||||
if (!symbol) {
|
||||
return ScriptElementKindModifier.none;
|
||||
}
|
||||
|
||||
const modifiers = new Set<string>();
|
||||
if (symbol.declarations && symbol.declarations.length > 0) {
|
||||
const kindModifiers = getNodeModifiers(symbol.declarations[0]);
|
||||
if (kindModifiers !== ScriptElementKindModifier.none) {
|
||||
kindModifiers.split(",").forEach(m => modifiers.add(m));
|
||||
}
|
||||
}
|
||||
const modifiers = new Set(getNormalizedSymbolModifiers(symbol));
|
||||
if (symbol.flags & SymbolFlags.Alias) {
|
||||
const resolvedSymbol = typeChecker.getAliasedSymbol(symbol);
|
||||
if (resolvedSymbol !== symbol && resolvedSymbol.declarations && resolvedSymbol.declarations.length > 0) {
|
||||
const kindModifiers = getNodeModifiers(resolvedSymbol.declarations[0]);
|
||||
if (kindModifiers !== ScriptElementKindModifier.none) {
|
||||
kindModifiers.split(",").forEach(m => modifiers.add(m));
|
||||
}
|
||||
if (resolvedSymbol !== symbol) {
|
||||
forEach(getNormalizedSymbolModifiers(resolvedSymbol), modifier => {
|
||||
modifiers.add(modifier);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (symbol.flags & SymbolFlags.Optional) {
|
||||
modifiers.add(ScriptElementKindModifier.optionalModifier);
|
||||
}
|
||||
|
||||
return modifiers.size > 0 ? arrayFrom(modifiers.values()).join(",") : ScriptElementKindModifier.none;
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
"refactors/extractSymbol.ts",
|
||||
"refactors/extractType.ts",
|
||||
"refactors/generateGetAccessorAndSetAccessor.ts",
|
||||
"refactors/helpers.ts",
|
||||
"refactors/moveToNewFile.ts",
|
||||
"refactors/addOrRemoveBracesToArrowFunction.ts",
|
||||
"refactors/convertParamsToDestructuredObject.ts",
|
||||
|
||||
+11
-1
@@ -520,7 +520,7 @@ namespace ts {
|
||||
/** @deprecated `fileName` will be ignored */
|
||||
applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise<ApplyCodeActionCommandResult | ApplyCodeActionCommandResult[]>;
|
||||
|
||||
getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason): ApplicableRefactorInfo[];
|
||||
getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, kind?: string): ApplicableRefactorInfo[];
|
||||
getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined): RefactorEditInfo | undefined;
|
||||
organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[];
|
||||
getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[];
|
||||
@@ -792,6 +792,11 @@ namespace ts {
|
||||
* the current context.
|
||||
*/
|
||||
notApplicableReason?: string;
|
||||
|
||||
/**
|
||||
* The hierarchical dotted name of the refactor action.
|
||||
*/
|
||||
kind?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1464,6 +1469,10 @@ namespace ts {
|
||||
|
||||
/** @internal */
|
||||
export interface Refactor {
|
||||
/** List of action kinds a refactor can provide.
|
||||
* Used to skip unnecessary calculation when specific refactors are requested. */
|
||||
kinds?: string[];
|
||||
|
||||
/** Compute the associated code actions */
|
||||
getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined;
|
||||
|
||||
@@ -1480,5 +1489,6 @@ namespace ts {
|
||||
cancellationToken?: CancellationToken;
|
||||
preferences: UserPreferences;
|
||||
triggerReason?: RefactorTriggerReason;
|
||||
kind?: string;
|
||||
}
|
||||
}
|
||||
@@ -1588,9 +1588,11 @@ namespace ts {
|
||||
return n.kind === SyntaxKind.EndOfFileToken ? !!(n as EndOfFileToken).jsDoc : n.getWidth(sourceFile) !== 0;
|
||||
}
|
||||
|
||||
export function getNodeModifiers(node: Node): string {
|
||||
const flags = isDeclaration(node) ? getCombinedNodeFlagsAlwaysIncludeJSDoc(node) : ModifierFlags.None;
|
||||
export function getNodeModifiers(node: Node, excludeFlags = ModifierFlags.None): string {
|
||||
const result: string[] = [];
|
||||
const flags = isDeclaration(node)
|
||||
? getCombinedNodeFlagsAlwaysIncludeJSDoc(node) & ~excludeFlags
|
||||
: ModifierFlags.None;
|
||||
|
||||
if (flags & ModifierFlags.Private) result.push(ScriptElementKindModifier.privateMemberModifier);
|
||||
if (flags & ModifierFlags.Protected) result.push(ScriptElementKindModifier.protectedMemberModifier);
|
||||
|
||||
@@ -1577,6 +1577,18 @@ const fn = (): Promise<(message: string) => void> =>
|
||||
function [#|f|]() {
|
||||
return fn().then(res => res("test"));
|
||||
}
|
||||
`);
|
||||
|
||||
_testConvertToAsyncFunction("convertToAsyncFunction_emptyCatch1", `
|
||||
function [#|f|]() {
|
||||
return Promise.resolve().catch();
|
||||
}
|
||||
`);
|
||||
|
||||
_testConvertToAsyncFunction("convertToAsyncFunction_emptyCatch2", `
|
||||
function [#|f|]() {
|
||||
return Promise.resolve(0).then(x => x).catch();
|
||||
}
|
||||
`);
|
||||
|
||||
_testConvertToAsyncFunctionWithModule("convertToAsyncFunction_importedFunction", `
|
||||
|
||||
Reference in new issue
Block a user