mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Add 'prefixText' and 'suffixText' when renaming shorthand properties (#27356)
* Add 'prefixText' and 'suffixText' when renaming shorthand properties * Make prefixText and suffixText missing instead of undefined * Fix test
This commit is contained in:
@@ -390,8 +390,8 @@ namespace ts.server {
|
||||
const locations: RenameLocation[] = [];
|
||||
for (const entry of body.locs) {
|
||||
const fileName = entry.file;
|
||||
for (const loc of entry.locs) {
|
||||
locations.push({ textSpan: this.decodeSpan(loc, fileName), fileName });
|
||||
for (const { start, end, ...prefixSuffixText } of entry.locs) {
|
||||
locations.push({ textSpan: this.decodeSpan({ start, end }, fileName), fileName, ...prefixSuffixText });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1395,7 +1395,7 @@ Actual: ${stringify(fullActual)}`);
|
||||
}
|
||||
}
|
||||
|
||||
public verifyRenameLocations(startRanges: ArrayOrSingle<Range>, options: ReadonlyArray<Range> | { findInStrings?: boolean, findInComments?: boolean, ranges: ReadonlyArray<Range> }) {
|
||||
public verifyRenameLocations(startRanges: ArrayOrSingle<Range>, options: FourSlashInterface.RenameLocationsOptions) {
|
||||
const { findInStrings = false, findInComments = false, ranges = this.getRanges() } = ts.isArray(options) ? { findInStrings: false, findInComments: false, ranges: options } : options;
|
||||
|
||||
for (const startRange of toArray(startRanges)) {
|
||||
@@ -1412,7 +1412,10 @@ Actual: ${stringify(fullActual)}`);
|
||||
|
||||
const sort = (locations: ReadonlyArray<ts.RenameLocation> | undefined) =>
|
||||
locations && ts.sort(locations, (r1, r2) => ts.compareStringsCaseSensitive(r1.fileName, r2.fileName) || r1.textSpan.start - r2.textSpan.start);
|
||||
assert.deepEqual(sort(references), sort(ranges.map((r): ts.RenameLocation => ({ fileName: r.fileName, textSpan: ts.createTextSpanFromRange(r) }))));
|
||||
assert.deepEqual(sort(references), sort(ranges.map((rangeOrOptions): ts.RenameLocation => {
|
||||
const { range, ...prefixSuffixText } = "range" in rangeOrOptions ? rangeOrOptions : { range: rangeOrOptions };
|
||||
return { fileName: range.fileName, textSpan: ts.createTextSpanFromRange(range), ...prefixSuffixText };
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4484,7 +4487,7 @@ namespace FourSlashInterface {
|
||||
this.state.verifyRenameInfoFailed(message);
|
||||
}
|
||||
|
||||
public renameLocations(startRanges: ArrayOrSingle<FourSlash.Range>, options: FourSlash.Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges: FourSlash.Range[] }) {
|
||||
public renameLocations(startRanges: ArrayOrSingle<FourSlash.Range>, options: RenameLocationsOptions) {
|
||||
this.state.verifyRenameLocations(startRanges, options);
|
||||
}
|
||||
|
||||
@@ -4959,4 +4962,11 @@ namespace FourSlashInterface {
|
||||
readonly newFileContents: { readonly [fileName: string]: string };
|
||||
readonly preferences?: ts.UserPreferences;
|
||||
}
|
||||
|
||||
export type RenameLocationsOptions = ReadonlyArray<RenameLocationOptions> | {
|
||||
readonly findInStrings?: boolean;
|
||||
readonly findInComments?: boolean;
|
||||
readonly ranges: ReadonlyArray<RenameLocationOptions>;
|
||||
};
|
||||
export type RenameLocationOptions = FourSlash.Range | { readonly range: FourSlash.Range, readonly prefixText?: string, readonly suffixText?: string };
|
||||
}
|
||||
|
||||
@@ -1135,7 +1135,12 @@ namespace ts.server.protocol {
|
||||
/** The file to which the spans apply */
|
||||
file: string;
|
||||
/** The text spans in this group */
|
||||
locs: TextSpan[];
|
||||
locs: RenameTextSpan[];
|
||||
}
|
||||
|
||||
export interface RenameTextSpan extends TextSpan {
|
||||
readonly prefixText?: string;
|
||||
readonly suffixText?: string;
|
||||
}
|
||||
|
||||
export interface RenameResponseBody {
|
||||
|
||||
@@ -1190,10 +1190,11 @@ namespace ts.server {
|
||||
|
||||
private toSpanGroups(locations: ReadonlyArray<RenameLocation>): ReadonlyArray<protocol.SpanGroup> {
|
||||
const map = createMap<protocol.SpanGroup>();
|
||||
for (const { fileName, textSpan } of locations) {
|
||||
for (const { fileName, textSpan, originalTextSpan: _, originalFileName: _1, ...prefixSuffixText } of locations) {
|
||||
let group = map.get(fileName);
|
||||
if (!group) map.set(fileName, group = { file: fileName, locs: [] });
|
||||
group.locs.push(this.toLocationTextSpan(textSpan, Debug.assertDefined(this.projectService.getScriptInfo(fileName))));
|
||||
const scriptInfo = Debug.assertDefined(this.projectService.getScriptInfo(fileName));
|
||||
group.locs.push({ ...this.toLocationTextSpan(textSpan, scriptInfo), ...prefixSuffixText });
|
||||
}
|
||||
return arrayFrom(map.values());
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace ts.codefix {
|
||||
function getReferences(token: PropertyName | Token<SyntaxKind.ConstructorKeyword>, program: Program, cancellationToken: CancellationToken): ReadonlyArray<Identifier> {
|
||||
// Position shouldn't matter since token is not a SourceFile.
|
||||
return mapDefined(FindAllReferences.getReferenceEntriesForNode(-1, token, program, program.getSourceFiles(), cancellationToken), entry =>
|
||||
entry.type === "node" ? tryCast(entry.node, isIdentifier) : undefined);
|
||||
entry.kind !== FindAllReferences.EntryKind.Span ? tryCast(entry.node, isIdentifier) : undefined);
|
||||
}
|
||||
|
||||
function inferTypeForVariableFromUsage(token: Identifier, program: Program, cancellationToken: CancellationToken): Type | undefined {
|
||||
|
||||
@@ -13,19 +13,20 @@ namespace ts.FindAllReferences {
|
||||
| { readonly type: DefinitionKind.This; readonly node: Node }
|
||||
| { readonly type: DefinitionKind.String; readonly node: StringLiteral };
|
||||
|
||||
export const enum EntryKind { Span, Node, StringLiteral, SearchedLocalFoundProperty, SearchedPropertyFoundLocal }
|
||||
export type NodeEntryKind = EntryKind.Node | EntryKind.StringLiteral | EntryKind.SearchedLocalFoundProperty | EntryKind.SearchedPropertyFoundLocal;
|
||||
export type Entry = NodeEntry | SpanEntry;
|
||||
export interface NodeEntry {
|
||||
type: "node";
|
||||
node: Node;
|
||||
isInString?: true;
|
||||
readonly kind: NodeEntryKind;
|
||||
readonly node: Node;
|
||||
}
|
||||
export interface SpanEntry {
|
||||
type: "span";
|
||||
fileName: string;
|
||||
textSpan: TextSpan;
|
||||
readonly kind: EntryKind.Span;
|
||||
readonly fileName: string;
|
||||
readonly textSpan: TextSpan;
|
||||
}
|
||||
export function nodeEntry(node: Node, isInString?: true): NodeEntry {
|
||||
return { type: "node", node: (node as NamedDeclaration).name || node, isInString };
|
||||
export function nodeEntry(node: Node, kind: NodeEntryKind = EntryKind.Node): NodeEntry {
|
||||
return { kind, node: (node as NamedDeclaration).name || node };
|
||||
}
|
||||
|
||||
export interface Options {
|
||||
@@ -33,7 +34,7 @@ namespace ts.FindAllReferences {
|
||||
readonly findInComments?: boolean;
|
||||
/**
|
||||
* True if we are renaming the symbol.
|
||||
* If so, we will find fewer references -- if it is referenced by several different names, we sill only find references for the original name.
|
||||
* If so, we will find fewer references -- if it is referenced by several different names, we still only find references for the original name.
|
||||
*/
|
||||
readonly isForRename?: boolean;
|
||||
/** True if we are searching for implementations. We will have a different method of adding references if so. */
|
||||
@@ -84,10 +85,15 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
}
|
||||
|
||||
export function findReferencedEntries(program: Program, cancellationToken: CancellationToken, sourceFiles: ReadonlyArray<SourceFile>, node: Node, position: number, options: Options | undefined): ReferenceEntry[] | undefined {
|
||||
return map(flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options)), toReferenceEntry);
|
||||
export function findReferenceOrRenameEntries<T>(
|
||||
program: Program, cancellationToken: CancellationToken, sourceFiles: ReadonlyArray<SourceFile>, node: Node, position: number, options: Options | undefined,
|
||||
convertEntry: ToReferenceOrRenameEntry<T>,
|
||||
): T[] | undefined {
|
||||
return map(flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options)), entry => convertEntry(entry, node));
|
||||
}
|
||||
|
||||
export type ToReferenceOrRenameEntry<T> = (entry: Entry, originalNode: Node) => T;
|
||||
|
||||
export function getReferenceEntriesForNode(position: number, node: Node, program: Program, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken, options: Options = {}, sourceFilesSet: ReadonlyMap<true> = arrayToSet(sourceFiles, f => f.fileName)): Entry[] | undefined {
|
||||
return flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options, sourceFilesSet));
|
||||
}
|
||||
@@ -143,26 +149,64 @@ namespace ts.FindAllReferences {
|
||||
return { displayParts, kind: symbolKind };
|
||||
}
|
||||
|
||||
function toReferenceEntry(entry: Entry): ReferenceEntry {
|
||||
if (entry.type === "span") {
|
||||
return { textSpan: entry.textSpan, fileName: entry.fileName, isWriteAccess: false, isDefinition: false };
|
||||
}
|
||||
export function toRenameLocation(entry: Entry, originalNode: Node): RenameLocation {
|
||||
return { ...entryToDocumentSpan(entry), ...getPrefixAndSuffixText(entry, originalNode) };
|
||||
}
|
||||
|
||||
const { node, isInString } = entry;
|
||||
const sourceFile = node.getSourceFile();
|
||||
export function toReferenceEntry(entry: Entry): ReferenceEntry {
|
||||
const { textSpan, fileName } = entryToDocumentSpan(entry);
|
||||
if (entry.kind === EntryKind.Span) {
|
||||
return { textSpan, fileName, isWriteAccess: false, isDefinition: false };
|
||||
}
|
||||
const { kind, node } = entry;
|
||||
return {
|
||||
fileName: sourceFile.fileName,
|
||||
textSpan: getTextSpan(node, sourceFile),
|
||||
textSpan,
|
||||
fileName,
|
||||
isWriteAccess: isWriteAccessForReference(node),
|
||||
isDefinition: node.kind === SyntaxKind.DefaultKeyword
|
||||
|| !!getDeclarationFromName(node)
|
||||
|| isLiteralComputedPropertyDeclarationName(node),
|
||||
isInString,
|
||||
isInString: kind === EntryKind.StringLiteral ? true : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function entryToDocumentSpan(entry: Entry): DocumentSpan {
|
||||
if (entry.kind === EntryKind.Span) {
|
||||
return { textSpan: entry.textSpan, fileName: entry.fileName };
|
||||
}
|
||||
else {
|
||||
const sourceFile = entry.node.getSourceFile();
|
||||
return { textSpan: getTextSpan(entry.node, sourceFile), fileName: sourceFile.fileName };
|
||||
}
|
||||
}
|
||||
|
||||
function getPrefixAndSuffixText(entry: Entry, originalNode: Node): { readonly prefixText?: string, readonly suffixText?: string } {
|
||||
if (entry.kind !== EntryKind.Span && isIdentifier(originalNode)) {
|
||||
const { node, kind } = entry;
|
||||
const name = originalNode.text;
|
||||
const isShorthandAssignment = isShorthandPropertyAssignment(node.parent);
|
||||
if (isShorthandAssignment || isObjectBindingElementWithoutPropertyName(node.parent)) {
|
||||
if (kind === EntryKind.SearchedLocalFoundProperty) {
|
||||
return { prefixText: name + ": " };
|
||||
}
|
||||
else if (kind === EntryKind.SearchedPropertyFoundLocal) {
|
||||
return { suffixText: ": " + name };
|
||||
}
|
||||
else {
|
||||
return isShorthandAssignment
|
||||
// In `const o = { x }; o.x`, symbolAtLocation at `x` in `{ x }` is the property symbol.
|
||||
? { suffixText: ": " + name }
|
||||
// For a binding element `const { x } = o;`, symbolAtLocation at `x` is the property symbol.
|
||||
: { prefixText: name + ": " };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return emptyOptions;
|
||||
}
|
||||
|
||||
function toImplementationLocation(entry: Entry, checker: TypeChecker): ImplementationLocation {
|
||||
if (entry.type === "node") {
|
||||
if (entry.kind !== EntryKind.Span) {
|
||||
const { node } = entry;
|
||||
const sourceFile = node.getSourceFile();
|
||||
return { textSpan: getTextSpan(node, sourceFile), fileName: sourceFile.fileName, ...implementationKindDisplayParts(node, checker) };
|
||||
@@ -196,18 +240,18 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
|
||||
export function toHighlightSpan(entry: Entry): { fileName: string, span: HighlightSpan } {
|
||||
if (entry.type === "span") {
|
||||
if (entry.kind === EntryKind.Span) {
|
||||
const { fileName, textSpan } = entry;
|
||||
return { fileName, span: { textSpan, kind: HighlightSpanKind.reference } };
|
||||
}
|
||||
|
||||
const { node, isInString } = entry;
|
||||
const { node, kind } = entry;
|
||||
const sourceFile = node.getSourceFile();
|
||||
const writeAccess = isWriteAccessForReference(node);
|
||||
const span: HighlightSpan = {
|
||||
textSpan: getTextSpan(node, sourceFile),
|
||||
kind: writeAccess ? HighlightSpanKind.writtenReference : HighlightSpanKind.reference,
|
||||
isInString
|
||||
isInString: kind === EntryKind.StringLiteral ? true : undefined,
|
||||
};
|
||||
return { fileName: sourceFile.fileName, span };
|
||||
}
|
||||
@@ -348,11 +392,11 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
}
|
||||
// import("foo") with no qualifier will reference the `export =` of the module, which may be referenced anyway.
|
||||
return { type: "node", node: reference.literal };
|
||||
return nodeEntry(reference.literal);
|
||||
}
|
||||
else {
|
||||
return {
|
||||
type: "span",
|
||||
kind: EntryKind.Span,
|
||||
fileName: reference.referencingFile.fileName,
|
||||
textSpan: createTextSpanFromRange(reference.ref),
|
||||
};
|
||||
@@ -366,7 +410,7 @@ namespace ts.FindAllReferences.Core {
|
||||
break;
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
if (sourceFilesSet.has(decl.getSourceFile().fileName)) {
|
||||
references.push({ type: "node", node: (decl as ModuleDeclaration).name });
|
||||
references.push(nodeEntry((decl as ModuleDeclaration).name));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -422,7 +466,7 @@ namespace ts.FindAllReferences.Core {
|
||||
searchForImportsOfExport(node, symbol, { exportingModuleSymbol: Debug.assertDefined(symbol.parent, "Expected export symbol to have a parent"), exportKind: ExportKind.Default }, state);
|
||||
}
|
||||
else {
|
||||
const search = state.createSearch(node, symbol, /*comingFrom*/ undefined, { allSearchSymbols: node ? populateSearchSymbolSet(symbol, node, checker, !!options.implementations) : [symbol] });
|
||||
const search = state.createSearch(node, symbol, /*comingFrom*/ undefined, { allSearchSymbols: node ? populateSearchSymbolSet(symbol, node, checker, !!options.isForRename, !!options.implementations) : [symbol] });
|
||||
|
||||
// Try to get the smallest valid scope that we can limit our search to;
|
||||
// otherwise we'll need to search globally (i.e. include each file).
|
||||
@@ -581,21 +625,21 @@ namespace ts.FindAllReferences.Core {
|
||||
* Callback to add references for a particular searched symbol.
|
||||
* This initializes a reference group, so only call this if you will add at least one reference.
|
||||
*/
|
||||
referenceAdder(searchSymbol: Symbol): (node: Node) => void {
|
||||
referenceAdder(searchSymbol: Symbol): (node: Node, kind?: NodeEntryKind) => void {
|
||||
const symbolId = getSymbolId(searchSymbol);
|
||||
let references = this.symbolIdToReferences[symbolId];
|
||||
if (!references) {
|
||||
references = this.symbolIdToReferences[symbolId] = [];
|
||||
this.result.push({ definition: { type: DefinitionKind.Symbol, symbol: searchSymbol }, references });
|
||||
}
|
||||
return node => references.push(nodeEntry(node));
|
||||
return (node, kind) => references.push(nodeEntry(node, kind));
|
||||
}
|
||||
|
||||
/** Add a reference with no associated definition. */
|
||||
addStringOrCommentReference(fileName: string, textSpan: TextSpan): void {
|
||||
this.result.push({
|
||||
definition: undefined,
|
||||
references: [{ type: "span", fileName, textSpan }]
|
||||
references: [{ kind: EntryKind.Span, fileName, textSpan }]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -707,21 +751,6 @@ namespace ts.FindAllReferences.Core {
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function getObjectBindingElementWithoutPropertyName(symbol: Symbol): BindingElement & { name: Identifier } | undefined {
|
||||
const bindingElement = getDeclarationOfKind<BindingElement>(symbol, SyntaxKind.BindingElement);
|
||||
if (bindingElement &&
|
||||
bindingElement.parent.kind === SyntaxKind.ObjectBindingPattern &&
|
||||
isIdentifier(bindingElement.name) &&
|
||||
!bindingElement.propertyName) {
|
||||
return bindingElement as BindingElement & { name: Identifier };
|
||||
}
|
||||
}
|
||||
|
||||
function getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol: Symbol, checker: TypeChecker): Symbol | undefined {
|
||||
const bindingElement = getObjectBindingElementWithoutPropertyName(symbol);
|
||||
return bindingElement && getPropertySymbolFromBindingElement(checker, bindingElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the smallest scope in which a symbol may have named references.
|
||||
* Note that not every construct has been accounted for. This function can
|
||||
@@ -754,7 +783,7 @@ namespace ts.FindAllReferences.Core {
|
||||
|
||||
// If symbol is of object binding pattern element without property name we would want to
|
||||
// look for property too and that could be anywhere
|
||||
if (getObjectBindingElementWithoutPropertyName(symbol)) {
|
||||
if (declarations.some(isObjectBindingElementWithoutPropertyName)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1099,13 +1128,14 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
}
|
||||
|
||||
function addReference(referenceLocation: Node, relatedSymbol: Symbol, state: State): void {
|
||||
const addRef = state.referenceAdder(relatedSymbol);
|
||||
function addReference(referenceLocation: Node, relatedSymbol: Symbol | RelatedSymbol, state: State): void {
|
||||
const { kind, symbol } = "kind" in relatedSymbol ? relatedSymbol : { kind: undefined, symbol: relatedSymbol };
|
||||
const addRef = state.referenceAdder(symbol);
|
||||
if (state.options.implementations) {
|
||||
addImplementationReferences(referenceLocation, addRef, state);
|
||||
}
|
||||
else {
|
||||
addRef(referenceLocation);
|
||||
addRef(referenceLocation, kind);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1428,7 +1458,7 @@ namespace ts.FindAllReferences.Core {
|
||||
const references = flatMap(sourceFiles, sourceFile => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
return mapDefined(getPossibleSymbolReferenceNodes(sourceFile, node.text), ref =>
|
||||
isStringLiteral(ref) && ref.text === node.text ? nodeEntry(ref, /*isInString*/ true) : undefined);
|
||||
isStringLiteral(ref) && ref.text === node.text ? nodeEntry(ref, EntryKind.StringLiteral) : undefined);
|
||||
});
|
||||
|
||||
return [{
|
||||
@@ -1439,35 +1469,21 @@ namespace ts.FindAllReferences.Core {
|
||||
|
||||
// For certain symbol kinds, we need to include other symbols in the search set.
|
||||
// This is not needed when searching for re-exports.
|
||||
function populateSearchSymbolSet(symbol: Symbol, location: Node, checker: TypeChecker, implementations: boolean): Symbol[] {
|
||||
function populateSearchSymbolSet(symbol: Symbol, location: Node, checker: TypeChecker, isForRename: boolean, implementations: boolean): Symbol[] {
|
||||
const result: Symbol[] = [];
|
||||
forEachRelatedSymbol<void>(symbol, location, checker,
|
||||
forEachRelatedSymbol<void>(symbol, location, checker, isForRename,
|
||||
(sym, root, base) => { result.push(base || root || sym); },
|
||||
/*allowBaseTypes*/ () => !implementations);
|
||||
return result;
|
||||
}
|
||||
|
||||
function forEachRelatedSymbol<T>(
|
||||
symbol: Symbol, location: Node, checker: TypeChecker,
|
||||
cbSymbol: (symbol: Symbol, rootSymbol?: Symbol, baseSymbol?: Symbol) => T | undefined,
|
||||
symbol: Symbol, location: Node, checker: TypeChecker, isForRenamePopulateSearchSymbolSet: boolean,
|
||||
cbSymbol: (symbol: Symbol, rootSymbol?: Symbol, baseSymbol?: Symbol, kind?: NodeEntryKind) => T | undefined,
|
||||
allowBaseTypes: (rootSymbol: Symbol) => boolean,
|
||||
): T | undefined {
|
||||
const containingObjectLiteralElement = getContainingObjectLiteralElement(location);
|
||||
if (containingObjectLiteralElement) {
|
||||
// If the location is in a context sensitive location (i.e. in an object literal) try
|
||||
// to get a contextual type for it, and add the property symbol from the contextual
|
||||
// type to the search set
|
||||
const contextualType = checker.getContextualType(containingObjectLiteralElement.parent);
|
||||
const res = contextualType && firstDefined(getPropertySymbolsFromContextualType(containingObjectLiteralElement, checker, contextualType, /*unionSymbolOk*/ true), fromRoot);
|
||||
if (res) return res;
|
||||
|
||||
// If the location is name of property symbol from object literal destructuring pattern
|
||||
// Search the property symbol
|
||||
// for ( { property: p2 } of elems) { }
|
||||
const propertySymbol = getPropertySymbolOfDestructuringAssignment(location, checker);
|
||||
const res1 = propertySymbol && cbSymbol(propertySymbol);
|
||||
if (res1) return res1;
|
||||
|
||||
/* Because in short-hand property assignment, location has two meaning : property name and as value of the property
|
||||
* When we do findAllReference at the position of the short-hand property assignment, we would want to have references to position of
|
||||
* property name and variable declaration of the identifier.
|
||||
@@ -1479,8 +1495,29 @@ namespace ts.FindAllReferences.Core {
|
||||
* so that when matching with potential reference symbol, both symbols from property declaration and variable declaration
|
||||
* will be included correctly.
|
||||
*/
|
||||
const shorthandValueSymbol = checker.getShorthandAssignmentValueSymbol(location.parent);
|
||||
const res2 = shorthandValueSymbol && cbSymbol(shorthandValueSymbol);
|
||||
const shorthandValueSymbol = checker.getShorthandAssignmentValueSymbol(location.parent); // gets the local symbol
|
||||
if (shorthandValueSymbol && isForRenamePopulateSearchSymbolSet) {
|
||||
// When renaming 'x' in `const o = { x }`, just rename the local variable, not the property.
|
||||
return cbSymbol(shorthandValueSymbol, /*rootSymbol*/ undefined, /*baseSymbol*/ undefined, EntryKind.SearchedLocalFoundProperty);
|
||||
}
|
||||
|
||||
// If the location is in a context sensitive location (i.e. in an object literal) try
|
||||
// to get a contextual type for it, and add the property symbol from the contextual
|
||||
// type to the search set
|
||||
const contextualType = checker.getContextualType(containingObjectLiteralElement.parent);
|
||||
const res = contextualType && firstDefined(
|
||||
getPropertySymbolsFromContextualType(containingObjectLiteralElement, checker, contextualType, /*unionSymbolOk*/ true),
|
||||
sym => fromRoot(sym, EntryKind.SearchedPropertyFoundLocal));
|
||||
if (res) return res;
|
||||
|
||||
// If the location is name of property symbol from object literal destructuring pattern
|
||||
// Search the property symbol
|
||||
// for ( { property: p2 } of elems) { }
|
||||
const propertySymbol = getPropertySymbolOfDestructuringAssignment(location, checker);
|
||||
const res1 = propertySymbol && cbSymbol(propertySymbol, /*rootSymbol*/ undefined, /*baseSymbol*/ undefined, EntryKind.SearchedPropertyFoundLocal);
|
||||
if (res1) return res1;
|
||||
|
||||
const res2 = shorthandValueSymbol && cbSymbol(shorthandValueSymbol, /*rootSymbol*/ undefined, /*baseSymbol*/ undefined, EntryKind.SearchedLocalFoundProperty);
|
||||
if (res2) return res2;
|
||||
}
|
||||
|
||||
@@ -1494,12 +1531,14 @@ namespace ts.FindAllReferences.Core {
|
||||
return fromRoot(symbol.flags & SymbolFlags.FunctionScopedVariable ? paramProps[1] : paramProps[0]);
|
||||
}
|
||||
|
||||
// If this is symbol of binding element without propertyName declaration in Object binding pattern
|
||||
// Include the property in the search
|
||||
const bindingElementPropertySymbol = getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol, checker);
|
||||
return bindingElementPropertySymbol && fromRoot(bindingElementPropertySymbol);
|
||||
// symbolAtLocation for a binding element is the local symbol. See if the search symbol is the property.
|
||||
// Don't do this when populating search set for a rename -- just rename the local.
|
||||
if (!isForRenamePopulateSearchSymbolSet) {
|
||||
const bindingElementPropertySymbol = isObjectBindingElementWithoutPropertyName(location.parent) ? getPropertySymbolFromBindingElement(checker, location.parent) : undefined;
|
||||
return bindingElementPropertySymbol && fromRoot(bindingElementPropertySymbol, EntryKind.SearchedPropertyFoundLocal);
|
||||
}
|
||||
|
||||
function fromRoot(sym: Symbol): T | undefined {
|
||||
function fromRoot(sym: Symbol, kind?: NodeEntryKind): T | undefined {
|
||||
// If this is a union property:
|
||||
// - In populateSearchSymbolsSet we will add all the symbols from all its source symbols in all unioned types.
|
||||
// - In findRelatedSymbol, we will just use the union symbol if any source symbol is included in the search.
|
||||
@@ -1507,20 +1546,24 @@ namespace ts.FindAllReferences.Core {
|
||||
// - In populateSearchSymbolsSet, add the root the list
|
||||
// - In findRelatedSymbol, return the source symbol if that is in the search. (Do not return the instantiation symbol.)
|
||||
return firstDefined(checker.getRootSymbols(sym), rootSymbol =>
|
||||
cbSymbol(sym, rootSymbol)
|
||||
cbSymbol(sym, rootSymbol, /*baseSymbol*/ undefined, kind)
|
||||
// Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions
|
||||
|| (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface) && allowBaseTypes(rootSymbol)
|
||||
? getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, checker, base => cbSymbol(sym, rootSymbol, base))
|
||||
? getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, checker, base => cbSymbol(sym, rootSymbol, base, kind))
|
||||
: undefined));
|
||||
}
|
||||
}
|
||||
|
||||
function getRelatedSymbol(search: Search, referenceSymbol: Symbol, referenceLocation: Node, state: State): Symbol | undefined {
|
||||
interface RelatedSymbol {
|
||||
readonly symbol: Symbol;
|
||||
readonly kind: NodeEntryKind | undefined;
|
||||
}
|
||||
function getRelatedSymbol(search: Search, referenceSymbol: Symbol, referenceLocation: Node, state: State): RelatedSymbol | undefined {
|
||||
const { checker } = state;
|
||||
return forEachRelatedSymbol(referenceSymbol, referenceLocation, checker,
|
||||
(sym, rootSymbol, baseSymbol) => search.includes(baseSymbol || rootSymbol || sym)
|
||||
return forEachRelatedSymbol(referenceSymbol, referenceLocation, checker, /*isForRenamePopulateSearchSymbolSet*/ false,
|
||||
(sym, rootSymbol, baseSymbol, kind): RelatedSymbol | undefined => search.includes(baseSymbol || rootSymbol || sym)
|
||||
// For a base type, use the symbol for the derived type. For a synthetic (e.g. union) property, use the union symbol.
|
||||
? rootSymbol && !(getCheckFlags(sym) & CheckFlags.Synthetic) ? rootSymbol : sym
|
||||
? { symbol: rootSymbol && !(getCheckFlags(sym) & CheckFlags.Synthetic) ? rootSymbol : sym, kind }
|
||||
: undefined,
|
||||
/*allowBaseTypes*/ rootSymbol =>
|
||||
!(search.parents && !search.parents.some(parent => explicitlyInheritsFrom(rootSymbol.parent!, parent, state.inheritsFromCache, checker))));
|
||||
|
||||
@@ -226,7 +226,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
|
||||
const { file, program, cancellationToken } = context;
|
||||
|
||||
const referenceEntries = mapDefined(FindAllReferences.getReferenceEntriesForNode(originalName.parent.pos, originalName, program, [file], cancellationToken!), entry => // TODO: GH#18217
|
||||
(entry.type === "node" && rangeContainsRange(constructor, entry.node) && isIdentifier(entry.node) && isWriteAccess(entry.node)) ? entry.node : undefined);
|
||||
(entry.kind !== FindAllReferences.EntryKind.Span && rangeContainsRange(constructor, entry.node) && isIdentifier(entry.node) && isWriteAccess(entry.node)) ? entry.node : undefined);
|
||||
|
||||
forEach(referenceEntries, entry => {
|
||||
const parent = entry.parent;
|
||||
|
||||
@@ -158,7 +158,7 @@ namespace ts.refactor {
|
||||
|
||||
const shouldMove = (name: Identifier): boolean => {
|
||||
const symbol = isBindingElement(name.parent)
|
||||
? getPropertySymbolFromBindingElement(checker, name.parent as BindingElement & { name: Identifier })
|
||||
? getPropertySymbolFromBindingElement(checker, name.parent as ObjectBindingElementWithoutPropertyName)
|
||||
: skipAlias(checker.getSymbolAtLocation(name)!, checker); // TODO: GH#18217
|
||||
return !!symbol && movedSymbols.has(symbol);
|
||||
};
|
||||
|
||||
@@ -1544,20 +1544,20 @@ namespace ts {
|
||||
const node = getTouchingPropertyName(sourceFile, position);
|
||||
if (isIdentifier(node) && isJsxOpeningElement(node.parent) || isJsxClosingElement(node.parent)) {
|
||||
const { openingElement, closingElement } = node.parent.parent;
|
||||
return [openingElement, closingElement].map((node): RenameLocation => ({ fileName: sourceFile.fileName, textSpan: createTextSpanFromNode(node.tagName, sourceFile) }));
|
||||
return [openingElement, closingElement].map((node): RenameLocation =>
|
||||
({ fileName: sourceFile.fileName, textSpan: createTextSpanFromNode(node.tagName, sourceFile) }));
|
||||
}
|
||||
else {
|
||||
const refs = getReferences(node, position, { findInStrings, findInComments, isForRename: true });
|
||||
return refs && refs.map(({ fileName, textSpan }): RenameLocation => ({ fileName, textSpan }));
|
||||
return getReferencesWorker(node, position, { findInStrings, findInComments, isForRename: true }, FindAllReferences.toRenameLocation);
|
||||
}
|
||||
}
|
||||
|
||||
function getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] | undefined {
|
||||
synchronizeHostData();
|
||||
return getReferences(getTouchingPropertyName(getValidSourceFile(fileName), position), position);
|
||||
return getReferencesWorker(getTouchingPropertyName(getValidSourceFile(fileName), position), position, {}, FindAllReferences.toReferenceEntry);
|
||||
}
|
||||
|
||||
function getReferences(node: Node, position: number, options?: FindAllReferences.Options): ReferenceEntry[] | undefined {
|
||||
function getReferencesWorker<T>(node: Node, position: number, options: FindAllReferences.Options, cb: FindAllReferences.ToReferenceOrRenameEntry<T>): T[] | undefined {
|
||||
synchronizeHostData();
|
||||
|
||||
// Exclude default library when renaming as commonly user don't want to change that file.
|
||||
@@ -1565,7 +1565,7 @@ namespace ts {
|
||||
? program.getSourceFiles().filter(sourceFile => !program.isSourceFileDefaultLibrary(sourceFile))
|
||||
: program.getSourceFiles();
|
||||
|
||||
return FindAllReferences.findReferencedEntries(program, cancellationToken, sourceFiles, node, position, options);
|
||||
return FindAllReferences.findReferenceOrRenameEntries(program, cancellationToken, sourceFiles, node, position, options, cb);
|
||||
}
|
||||
|
||||
function findReferences(fileName: string, position: number): ReferencedSymbol[] | undefined {
|
||||
|
||||
@@ -619,6 +619,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
export interface RenameLocation extends DocumentSpan {
|
||||
readonly prefixText?: string;
|
||||
readonly suffixText?: string;
|
||||
}
|
||||
|
||||
export interface ReferenceEntry extends DocumentSpan {
|
||||
|
||||
@@ -1325,7 +1325,16 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
export function getPropertySymbolFromBindingElement(checker: TypeChecker, bindingElement: BindingElement & { name: Identifier }) {
|
||||
export type ObjectBindingElementWithoutPropertyName = BindingElement & { name: Identifier };
|
||||
|
||||
export function isObjectBindingElementWithoutPropertyName(bindingElement: Node): bindingElement is ObjectBindingElementWithoutPropertyName {
|
||||
return isBindingElement(bindingElement) &&
|
||||
isObjectBindingPattern(bindingElement.parent) &&
|
||||
isIdentifier(bindingElement.name) &&
|
||||
!bindingElement.propertyName;
|
||||
}
|
||||
|
||||
export function getPropertySymbolFromBindingElement(checker: TypeChecker, bindingElement: ObjectBindingElementWithoutPropertyName) {
|
||||
const typeOfPattern = checker.getTypeAtLocation(bindingElement.parent);
|
||||
const propSymbol = typeOfPattern && checker.getPropertyOfType(typeOfPattern, bindingElement.name.text);
|
||||
if (propSymbol && propSymbol.flags & SymbolFlags.Accessor) {
|
||||
|
||||
@@ -449,6 +449,14 @@ namespace ts.projectSystem {
|
||||
const toLocation = protocolToLocation(str);
|
||||
return { start: toLocation(span.start), end: toLocation(textSpanEnd(span)) };
|
||||
}
|
||||
function protocolRenameSpanFromSubstring(
|
||||
str: string,
|
||||
substring: string,
|
||||
options?: SpanFromSubstringOptions,
|
||||
prefixSuffixText?: { readonly prefixText?: string, readonly suffixText?: string },
|
||||
): protocol.RenameTextSpan {
|
||||
return { ...protocolTextSpanFromSubstring(str, substring, options), ...prefixSuffixText };
|
||||
}
|
||||
function textSpanFromSubstring(str: string, substring: string, options?: SpanFromSubstringOptions): TextSpan {
|
||||
const start = nthIndexOf(str, substring, options ? options.index : 0);
|
||||
Debug.assert(start !== -1);
|
||||
@@ -463,6 +471,9 @@ namespace ts.projectSystem {
|
||||
function documentSpanFromSubstring(file: File, substring: string, options?: SpanFromSubstringOptions): DocumentSpan {
|
||||
return { fileName: file.path, textSpan: textSpanFromSubstring(file.content, substring, options) };
|
||||
}
|
||||
function renameLocation(file: File, substring: string, options?: SpanFromSubstringOptions): RenameLocation {
|
||||
return documentSpanFromSubstring(file, substring, options);
|
||||
}
|
||||
interface SpanFromSubstringOptions {
|
||||
readonly index: number;
|
||||
}
|
||||
@@ -8284,12 +8295,12 @@ namespace ts.projectSystem {
|
||||
const response = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, { file: aFc, ...protocolLocationFromSubstring(cFile.content, "C") });
|
||||
|
||||
assert.equal(aFile.content, bFile.content);
|
||||
const abLocs: protocol.TextSpan[] = [
|
||||
protocolTextSpanFromSubstring(aFile.content, "C"),
|
||||
protocolTextSpanFromSubstring(aFile.content, "C", { index: 1 }),
|
||||
const abLocs: protocol.RenameTextSpan[] = [
|
||||
protocolRenameSpanFromSubstring(aFile.content, "C"),
|
||||
protocolRenameSpanFromSubstring(aFile.content, "C", { index: 1 }),
|
||||
];
|
||||
const span = protocolTextSpanFromSubstring(cFile.content, "C");
|
||||
const cLocs: protocol.TextSpan[] = [span];
|
||||
const span = protocolRenameSpanFromSubstring(cFile.content, "C");
|
||||
const cLocs: protocol.RenameTextSpan[] = [span];
|
||||
assert.deepEqual<protocol.RenameResponseBody | undefined>(response, {
|
||||
info: {
|
||||
canRename: true,
|
||||
@@ -8299,7 +8310,7 @@ namespace ts.projectSystem {
|
||||
kind: ScriptElementKind.constElement,
|
||||
kindModifiers: ScriptElementKindModifier.exportedModifier,
|
||||
localizedErrorMessage: undefined,
|
||||
triggerSpan: span,
|
||||
triggerSpan: protocolTextSpanFromSubstring(cFile.content, "C"),
|
||||
},
|
||||
locs: [
|
||||
{ file: aFc, locs: cLocs },
|
||||
@@ -9570,7 +9581,7 @@ export function Test2() {
|
||||
});
|
||||
|
||||
describe("tsserverProjectSystem rename", () => {
|
||||
it("works", () => {
|
||||
it("works with fileToRename", () => {
|
||||
const aTs: File = { path: "/a.ts", content: "export const a = 0;" };
|
||||
const bTs: File = { path: "/b.ts", content: 'import { a } from "./a";' };
|
||||
|
||||
@@ -9589,7 +9600,36 @@ export function Test2() {
|
||||
localizedErrorMessage: undefined,
|
||||
triggerSpan: protocolTextSpanFromSubstring(bTs.content, "a", { index: 1 }),
|
||||
},
|
||||
locs: [{ file: bTs.path, locs: [protocolTextSpanFromSubstring(bTs.content, "./a")] }],
|
||||
locs: [{ file: bTs.path, locs: [protocolRenameSpanFromSubstring(bTs.content, "./a")] }],
|
||||
});
|
||||
});
|
||||
|
||||
it("works with prefixText and suffixText", () => {
|
||||
const aTs: File = { path: "/a.ts", content: "const x = 0; const o = { x };" };
|
||||
const session = createSession(createServerHost([aTs]));
|
||||
openFilesForSession([aTs], session);
|
||||
|
||||
const response = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(aTs, "x"));
|
||||
assert.deepEqual<protocol.RenameResponseBody | undefined>(response, {
|
||||
info: {
|
||||
canRename: true,
|
||||
fileToRename: undefined,
|
||||
displayName: "x",
|
||||
fullDisplayName: "x",
|
||||
kind: ScriptElementKind.constElement,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
localizedErrorMessage: undefined,
|
||||
triggerSpan: protocolTextSpanFromSubstring(aTs.content, "x"),
|
||||
},
|
||||
locs: [
|
||||
{
|
||||
file: aTs.path,
|
||||
locs: [
|
||||
protocolRenameSpanFromSubstring(aTs.content, "x"),
|
||||
protocolRenameSpanFromSubstring(aTs.content, "x", { index: 1 }, { prefixText: "x: " }),
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -10070,13 +10110,13 @@ declare class TestLib {
|
||||
|
||||
const renameATs = (aTs: File): protocol.SpanGroup => ({
|
||||
file: aTs.path,
|
||||
locs: [protocolTextSpanFromSubstring(aTs.content, "fnA")],
|
||||
locs: [protocolRenameSpanFromSubstring(aTs.content, "fnA")],
|
||||
});
|
||||
const renameUserTs = (userTs: File): protocol.SpanGroup => ({
|
||||
file: userTs.path,
|
||||
locs: [
|
||||
protocolTextSpanFromSubstring(userTs.content, "fnA"),
|
||||
protocolTextSpanFromSubstring(userTs.content, "fnA", { index: 1 }),
|
||||
protocolRenameSpanFromSubstring(userTs.content, "fnA"),
|
||||
protocolRenameSpanFromSubstring(userTs.content, "fnA", { index: 1 }),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -10123,9 +10163,9 @@ declare class TestLib {
|
||||
const session = makeSampleProjects();
|
||||
const response = executeSessionRequest<protocol.RenameFullRequest, protocol.RenameFullResponse>(session, protocol.CommandTypes.RenameLocationsFull, protocolFileLocationFromSubstring(userTs, "fnA()"));
|
||||
assert.deepEqual<ReadonlyArray<RenameLocation>>(response, [
|
||||
documentSpanFromSubstring(userTs, "fnA"),
|
||||
documentSpanFromSubstring(userTs, "fnA", { index: 1 }),
|
||||
documentSpanFromSubstring(aTs, "fnA"),
|
||||
renameLocation(userTs, "fnA"),
|
||||
renameLocation(userTs, "fnA", { index: 1 }),
|
||||
renameLocation(aTs, "fnA"),
|
||||
]);
|
||||
verifyATsConfigOriginalProject(session);
|
||||
});
|
||||
@@ -10148,13 +10188,13 @@ declare class TestLib {
|
||||
{
|
||||
file: userTs.path,
|
||||
locs: [
|
||||
protocolTextSpanFromSubstring(userTs.content, "fnB"),
|
||||
protocolTextSpanFromSubstring(userTs.content, "fnB", { index: 1 }),
|
||||
protocolRenameSpanFromSubstring(userTs.content, "fnB"),
|
||||
protocolRenameSpanFromSubstring(userTs.content, "fnB", { index: 1 }),
|
||||
],
|
||||
},
|
||||
{
|
||||
file: bDts.path,
|
||||
locs: [protocolTextSpanFromSubstring(bDts.content, "fnB")],
|
||||
locs: [protocolRenameSpanFromSubstring(bDts.content, "fnB")],
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
+7
-1
@@ -4960,6 +4960,8 @@ declare namespace ts {
|
||||
originalFileName?: string;
|
||||
}
|
||||
interface RenameLocation extends DocumentSpan {
|
||||
readonly prefixText?: string;
|
||||
readonly suffixText?: string;
|
||||
}
|
||||
interface ReferenceEntry extends DocumentSpan {
|
||||
isWriteAccess: boolean;
|
||||
@@ -6475,7 +6477,11 @@ declare namespace ts.server.protocol {
|
||||
/** The file to which the spans apply */
|
||||
file: string;
|
||||
/** The text spans in this group */
|
||||
locs: TextSpan[];
|
||||
locs: RenameTextSpan[];
|
||||
}
|
||||
interface RenameTextSpan extends TextSpan {
|
||||
readonly prefixText?: string;
|
||||
readonly suffixText?: string;
|
||||
}
|
||||
interface RenameResponseBody {
|
||||
/**
|
||||
|
||||
@@ -4960,6 +4960,8 @@ declare namespace ts {
|
||||
originalFileName?: string;
|
||||
}
|
||||
interface RenameLocation extends DocumentSpan {
|
||||
readonly prefixText?: string;
|
||||
readonly suffixText?: string;
|
||||
}
|
||||
interface ReferenceEntry extends DocumentSpan {
|
||||
isWriteAccess: boolean;
|
||||
|
||||
@@ -10,4 +10,5 @@ verify.referenceGroups(r1, [
|
||||
{ definition: "function bar(): string", ranges: [r0] },
|
||||
{ definition: "var bar: () => string", ranges: [r1] },
|
||||
]);
|
||||
verify.rangesAreRenameLocations();
|
||||
verify.renameLocations(r0, [r0, { range: r1, suffixText: ": bar" }]);
|
||||
verify.renameLocations(r1, [{ range: r1, prefixText: "bar: " }])
|
||||
|
||||
@@ -9,14 +9,16 @@
|
||||
////[|x|]; [|y|];
|
||||
|
||||
const [x0, y0, x1, y1, x2, y2] = test.ranges();
|
||||
verify.referenceGroups(x0, [{ definition: "(property) Test.x: number", ranges: [x0, x1, x2] }]);
|
||||
verify.referenceGroups([x1, x2], [
|
||||
verify.referenceGroups(x0, [{ definition: "(property) Test.x: number", ranges: [x0, x1] }]);
|
||||
verify.referenceGroups(x1, [
|
||||
{ definition: "(property) Test.x: number", ranges: [x0] },
|
||||
{ definition: "const x: number", ranges: [x1, x2] },
|
||||
]);
|
||||
verify.referenceGroups(x2, [{ definition: "const x: number", ranges: [x1, x2] }]);
|
||||
|
||||
verify.referenceGroups(y0, [{ definition: "(property) Test.y: number", ranges: [y0, y1, y2] }]);
|
||||
verify.referenceGroups([y1, y2], [
|
||||
verify.referenceGroups(y0, [{ definition: "(property) Test.y: number", ranges: [y0, y1] }]);
|
||||
verify.referenceGroups(y1, [
|
||||
{ definition: "(property) Test.y: number", ranges: [y0] },
|
||||
{ definition: "const y: number", ranges: [y1, y2] },
|
||||
]);
|
||||
verify.referenceGroups(y2, [{ definition: "const y: number", ranges: [y1, y2] }]);
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
|
||||
const ranges = test.ranges();
|
||||
const [r0, r1, r2, r3] = ranges;
|
||||
verify.referenceGroups([r0, r1], [{ definition: "(property) I.property1: number", ranges }]);
|
||||
verify.referenceGroups([r2, r3], [
|
||||
verify.referenceGroups([r0, r1], [{ definition: "(property) I.property1: number", ranges: [r0, r1, r2] }]);
|
||||
verify.referenceGroups(r2, [
|
||||
{ definition: "(property) I.property1: number", ranges: [r0, r1] },
|
||||
{ definition: "var property1: number", ranges: [r2, r3] }
|
||||
{ definition: "var property1: number", ranges: [r2, r3] },
|
||||
]);
|
||||
|
||||
verify.referenceGroups(r3, [{ definition: "var property1: number", ranges: [r2, r3] }]);
|
||||
|
||||
@@ -317,7 +317,7 @@ declare namespace FourSlashInterface {
|
||||
}[]): void;
|
||||
renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string, fileToRename?: string, range?: Range): void;
|
||||
renameInfoFailed(message?: string): void;
|
||||
renameLocations(startRanges: ArrayOrSingle<Range>, options: Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges: Range[] }): void;
|
||||
renameLocations(startRanges: ArrayOrSingle<Range>, options: RenameLocationsOptions): void;
|
||||
|
||||
/** Verify the quick info available at the current marker. */
|
||||
quickInfoIs(expectedText: string, expectedDocumentation?: string): void;
|
||||
@@ -648,6 +648,13 @@ declare namespace FourSlashInterface {
|
||||
|
||||
type ArrayOrSingle<T> = T | ReadonlyArray<T>;
|
||||
type NewFileContent = string | { readonly [fileName: string]: string };
|
||||
|
||||
type RenameLocationsOptions = ReadonlyArray<RenameLocationOptions> | {
|
||||
readonly findInStrings?: boolean;
|
||||
readonly findInComments?: boolean;
|
||||
readonly ranges: ReadonlyArray<RenameLocationOptions>;
|
||||
}
|
||||
type RenameLocationOptions = Range | { readonly range: Range, readonly prefixText?: string, readonly suffixText?: string };
|
||||
}
|
||||
declare function verifyOperationIsCancelled(f: any): void;
|
||||
declare var test: FourSlashInterface.test_;
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
////const z = [|x|];
|
||||
|
||||
const [r0, r1, r2] = test.ranges();
|
||||
verify.referenceGroups([r0, r2], [
|
||||
{ definition: "const x: number", ranges: [r0, r2] },
|
||||
{ definition: "(property) x: number", ranges: [r1] },
|
||||
]);
|
||||
verify.referenceGroups(r1, [{ definition: "(property) x: number", ranges: [r0, r1, r2] }]);
|
||||
const local = { definition: "const x: number", ranges: [r0, r2] };
|
||||
const prop = { definition: "(property) x: number", ranges: [r1] };
|
||||
verify.referenceGroups(r0, [local, prop]);
|
||||
verify.referenceGroups(r1, [{ ...prop, ranges: [r0, r1] }]);
|
||||
verify.referenceGroups(r2, [local]);
|
||||
|
||||
@@ -6,13 +6,15 @@
|
||||
////}
|
||||
////var elems: I[];
|
||||
////
|
||||
////var p2: number, property1: number;
|
||||
////var p2: number, [|property1|]: number;
|
||||
////for ({ [|property1|] } = elems[0]; p2 < 100; p2++) {
|
||||
//// p2 = property1++;
|
||||
//// p2 = [|property1|]++;
|
||||
////}
|
||||
////for ({ [|property1|]: p2 } = elems[0]; p2 < 100; p2++) {
|
||||
////}
|
||||
|
||||
verify.noErrors();
|
||||
const ranges = test.ranges();
|
||||
const [r0, , r2] = ranges;
|
||||
verify.renameLocations([r0, r2], ranges);
|
||||
const [r0, r1, r2, r3, r4] = ranges;
|
||||
verify.renameLocations([r0, r4], [r0, { range: r2, suffixText: ": property1" }, r4]);
|
||||
verify.renameLocations([r1, r2, r3], [r1, { range: r2, prefixText: "property1: " }, r3]);
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////interface I {
|
||||
//// [|property1|]: number;
|
||||
//// property2: string;
|
||||
////}
|
||||
////var elems: I[];
|
||||
////
|
||||
////var p2: number, [|property1|]: number;
|
||||
////for ({ [|property1|] } = elems[0]; p2 < 100; p2++) {
|
||||
//// p2 = [|property1|]++;
|
||||
////}
|
||||
////for ({ [|property1|]: p2 } = elems[0]; p2 < 100; p2++) {
|
||||
////}
|
||||
|
||||
const ranges = test.ranges();
|
||||
const [r0, r1, r2, r3, r4] = ranges;
|
||||
verify.renameLocations([r0, r4], [r0, r2, r4]);
|
||||
verify.renameLocations([r1, r3], [r1, r2, r3]);
|
||||
verify.renameLocations(r2, ranges);
|
||||
@@ -6,13 +6,15 @@
|
||||
////}
|
||||
////var elems: I[];
|
||||
////
|
||||
////var property1: number, p2: number;
|
||||
////var [|property1|]: number, p2: number;
|
||||
////for ({ [|property1|] } of elems) {
|
||||
//// property1++;
|
||||
//// [|property1|]++;
|
||||
////}
|
||||
////for ({ [|property1|]: p2 } of elems) {
|
||||
////}
|
||||
|
||||
verify.noErrors();
|
||||
const ranges = test.ranges();
|
||||
const [r0, , r2] = ranges;
|
||||
verify.renameLocations([r0, r2], ranges);
|
||||
const [r0, r1, r2, r3, r4] = ranges;
|
||||
verify.renameLocations([r0, r4], [r0, { range: r2, suffixText: ": property1" }, r4]);
|
||||
verify.renameLocations([r1, r2, r3], [r1, { range: r2, prefixText: "property1: " }, r3]);
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////interface I {
|
||||
//// [|property1|]: number;
|
||||
//// property2: string;
|
||||
////}
|
||||
////var elems: I[];
|
||||
////
|
||||
////var [|property1|]: number, p2: number;
|
||||
////for ({ [|property1|] } of elems) {
|
||||
//// [|property1|]++;
|
||||
////}
|
||||
////for ({ [|property1|]: p2 } of elems) {
|
||||
////}
|
||||
|
||||
const ranges = test.ranges();
|
||||
const [r0, r1, r2, r3, r4] = ranges;
|
||||
verify.renameLocations([r0, r4], [r0, r2, r4]);
|
||||
verify.renameLocations([r1, r3], [r1, r2, r3]);
|
||||
verify.renameLocations(r2, ranges);
|
||||
@@ -4,10 +4,12 @@
|
||||
//// [|property1|]: number;
|
||||
//// property2: string;
|
||||
////}
|
||||
////var elems: I[], p1: number, property1: number;
|
||||
////var elems: I[], p1: number, [|property1|]: number;
|
||||
////[{ [|property1|]: p1 }] = elems;
|
||||
////[{ [|property1|] }] = elems;
|
||||
|
||||
const ranges = test.ranges();
|
||||
const [r0, r1] = ranges;
|
||||
verify.renameLocations([r0, r1], ranges);
|
||||
const [r0, r1, r2, r3] = ranges;
|
||||
verify.renameLocations([r0, r2], [r0, r2, { range: r3, suffixText: ": property1" }]);
|
||||
verify.renameLocations([r1, r3], [r1, { range: r3, prefixText: "property1: " }]);
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////interface I {
|
||||
//// [|property1|]: number;
|
||||
//// property2: string;
|
||||
////}
|
||||
////var elems: I[], p1: number, [|property1|]: number;
|
||||
////[{ [|property1|]: p1 }] = elems;
|
||||
////[{ [|property1|] }] = elems;
|
||||
|
||||
const ranges = test.ranges();
|
||||
const [r0, r1, r2, r3] = ranges;
|
||||
verify.renameLocations([r0, r2], [r0, r2, r3]);
|
||||
verify.renameLocations(r1, [r1, r3]);
|
||||
verify.renameLocations(r3, ranges);
|
||||
@@ -7,14 +7,16 @@
|
||||
//// secondary: string;
|
||||
//// };
|
||||
////}
|
||||
////let multiRobot: MultiRobot;
|
||||
////let multiRobot: MultiRobot, [|primary|]: string, secondary: string, primaryA: string, secondaryA: string, i: number;
|
||||
////for ({ skills: { [|primary|]: primaryA, secondary: secondaryA } } = multiRobot, i = 0; i < 1; i++) {
|
||||
//// console.log(primaryA);
|
||||
//// primaryA;
|
||||
////}
|
||||
////for ({ skills: { [|primary|], secondary } } = multiRobot, i = 0; i < 1; i++) {
|
||||
//// console.log(primary);
|
||||
//// [|primary|];
|
||||
////}
|
||||
|
||||
verify.noErrors();
|
||||
const ranges = test.ranges();
|
||||
const [r0, r1] = ranges;
|
||||
verify.renameLocations([r0, r1], ranges);
|
||||
const [r0, r1, r2, r3, r4] = ranges;
|
||||
verify.renameLocations([r0, r2], [r0, r2, { range: r3, suffixText: ": primary" }]);
|
||||
verify.renameLocations([r1, r3, r4], [r1, { range: r3, prefixText: "primary: " }, r4]);
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
//// secondary: string;
|
||||
//// };
|
||||
////}
|
||||
////let multiRobot: MultiRobot, [|primary|]: string;
|
||||
////let multiRobot: MultiRobot, [|primary|]: string, secondary: string, primaryA: string, secondaryA: string, i: number;
|
||||
////for ({ skills: { [|primary|]: primaryA, secondary: secondaryA } } = multiRobot, i = 0; i < 1; i++) {
|
||||
//// console.log(primaryA);
|
||||
//// primaryA;
|
||||
////}
|
||||
////for ({ skills: { [|primary|], secondary } } = multiRobot, i = 0; i < 1; i++) {
|
||||
//// console.log([|primary|]);
|
||||
//// [|primary|];
|
||||
////}
|
||||
|
||||
verify.noErrors();
|
||||
const ranges = test.ranges();
|
||||
const [r0, r1, r2, r3, r4] = ranges;
|
||||
verify.renameLocations([r0, r2], [r0, r2, r3]);
|
||||
verify.renameLocations([r1, r4], [r1, r3, r4]);
|
||||
verify.renameLocations(r3, ranges);
|
||||
verify.renameLocations([r0, r2], [r0, r2, { range: r3, suffixText: ": primary" }]);
|
||||
verify.renameLocations([r1, r3, r4], [r1, { range: r3, prefixText: "primary: " }, r4]);
|
||||
|
||||
@@ -8,13 +8,15 @@
|
||||
//// };
|
||||
////}
|
||||
////let multiRobots: MultiRobot[];
|
||||
////let [|primary|]: string, secondary: string, primaryA: string, secondaryA: string;
|
||||
////for ({ skills: { [|primary|]: primaryA, secondary: secondaryA } } of multiRobots) {
|
||||
//// console.log(primaryA);
|
||||
//// primaryA;
|
||||
////}
|
||||
////for ({ skills: { [|primary|], secondary } } of multiRobots) {
|
||||
//// console.log(primary);
|
||||
//// [|primary|];
|
||||
////}
|
||||
|
||||
const ranges = test.ranges();
|
||||
const [r0, r1] = ranges;
|
||||
verify.renameLocations([r0, r1], ranges);
|
||||
verify.noErrors();
|
||||
const [r0, r1, r2, r3, r4] = test.ranges();
|
||||
verify.renameLocations([r0, r2], [r0, r2, { range: r3, suffixText: ": primary" }]);
|
||||
verify.renameLocations([r1, r3, r4], [r1, { range: r3, prefixText: "primary: " }, r4])
|
||||
|
||||
@@ -17,6 +17,5 @@
|
||||
|
||||
const ranges = test.ranges();
|
||||
const [r0, r1, r2, r3, r4] = ranges;
|
||||
verify.renameLocations([r0, r2], [r0, r2, r3]);
|
||||
verify.renameLocations([r1, r4], [r1, r3, r4]);
|
||||
verify.renameLocations(r3, ranges);
|
||||
verify.renameLocations([r0, r2], [r0, r2, { range: r3, suffixText: ": primary" }]);
|
||||
verify.renameLocations([r1, r3, r4], [r1, { range: r3, prefixText: "primary: " }, r4]);
|
||||
|
||||
@@ -16,4 +16,7 @@
|
||||
//// }
|
||||
////}
|
||||
|
||||
verify.rangesAreRenameLocations();
|
||||
const [r0, r1, r2, r3, r4] = test.ranges();
|
||||
verify.renameLocations([r0, r2], [r0, { range: r1, suffixText: ": foo" }, r2, { range: r3, suffixText: ": foo" }]);
|
||||
verify.renameLocations(r1, [{ range: r1, prefixText: "foo: " }]);
|
||||
verify.renameLocations([r3, r4], [{ range: r3, prefixText: "foo: " }, r4]);
|
||||
|
||||
@@ -13,4 +13,6 @@
|
||||
//// [|property1|] = p2;
|
||||
////}
|
||||
|
||||
verify.rangesAreRenameLocations();
|
||||
const [r0, r1, r2, r3] = test.ranges();
|
||||
verify.renameLocations([r0, r1], [r0, r1, { range: r2, suffixText: ": property1" }]);
|
||||
verify.renameLocations([r2, r3], [{ range: r2, prefixText: "property1: " }, r3]);
|
||||
|
||||
@@ -12,4 +12,6 @@
|
||||
////for (let { [|property1|]: p2 } of elems) {
|
||||
////}
|
||||
|
||||
verify.rangesAreRenameLocations();
|
||||
const [r0, r1, r2, r3] = test.ranges();
|
||||
verify.renameLocations([r0, r3], [r0, { range: r1, suffixText: ": property1" }, r3]);
|
||||
verify.renameLocations([r1, r2], [{ range: r1, prefixText: "property1: " }, r2]);
|
||||
|
||||
@@ -4,4 +4,8 @@
|
||||
//// f({[|a|]});
|
||||
////}
|
||||
|
||||
verify.rangesAreRenameLocations();
|
||||
const [r0, r1, r2] = test.ranges();
|
||||
// renames the local
|
||||
verify.renameLocations([r0, r2], [{ range: r0, prefixText: "a: " }, { range: r2, prefixText: "a: " }]);
|
||||
// renames the property
|
||||
verify.renameLocations(r1, [{ range: r0, suffixText: ": a" }, r1, { range: r2, suffixText: ": a" }]);
|
||||
|
||||
@@ -15,4 +15,6 @@
|
||||
//// console.log([|primary|]);
|
||||
////}
|
||||
|
||||
verify.rangesAreRenameLocations();
|
||||
const [r0, r1, r2, r3] = test.ranges();
|
||||
verify.renameLocations([r0, r1], [r0, r1, { range: r2, suffixText: ": primary" }]);
|
||||
verify.renameLocations([r2, r3], [{ range: r2, prefixText: "primary: " }, r3]);
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
////import [|foo|] from 'bar';
|
||||
////const bar = { [|foo|] };
|
||||
|
||||
verify.rangesAreRenameLocations();
|
||||
const [r0, r1] = test.ranges();
|
||||
verify.renameLocations([r0, r1], [r0, { range: r1, prefixText: "foo: " }]);
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
////import * as [|foo|] from 'bar';
|
||||
////const bar = { [|foo|] };
|
||||
|
||||
verify.rangesAreRenameLocations();
|
||||
const [r0, r1] = test.ranges();
|
||||
verify.renameLocations([r0, r1], [r0, { range: r1, prefixText: "foo: " }]);
|
||||
|
||||
@@ -5,4 +5,6 @@
|
||||
////a = { [|e|] };
|
||||
////export { [|e|] };
|
||||
|
||||
verify.rangesAreRenameLocations();
|
||||
const [r0, r1, r2, r3] = test.ranges();
|
||||
//TODO: export should have prefix/suffix too
|
||||
verify.renameLocations([r0, r1, r2, r3], [r0, r1, { range: r2, prefixText: "e: " }, r3]);
|
||||
|
||||
@@ -6,4 +6,5 @@
|
||||
//// }
|
||||
//// }
|
||||
|
||||
verify.rangesAreRenameLocations();
|
||||
const [r0, r1] = test.ranges();
|
||||
verify.renameLocations([r0, r1], [{ range: r0, prefixText: "protectedParam: " }, r1]);
|
||||
|
||||
Reference in New Issue
Block a user