refactorConvertToGetAccessAndSetAccess: Don't trigger on leading trivia (#25054)

* refactorConvertToGetAccessAndSetAccess: Don't trigger on leading trivia

* Update API (#24966)
This commit is contained in:
Andy 2018-06-19 13:46:03 -07:00 committed by GitHub
parent db85f37669
commit 7f553f4f93
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 37 additions and 16 deletions

View file

@ -3045,6 +3045,10 @@ Actual: ${stringify(fullActual)}`);
}
}
public verifyRefactorsAvailable(names: ReadonlyArray<string>): void {
assert.deepEqual(unique(this.getApplicableRefactors(this.getSelection()), r => r.name), names);
}
public verifyRefactor({ name, actionName, refactors }: FourSlashInterface.VerifyRefactorOptions) {
const actualRefactors = this.getApplicableRefactors(this.getSelection()).filter(r => r.name === name && r.actions.some(a => a.name === actionName));
this.assertObjectsEqual(actualRefactors, refactors);
@ -3815,7 +3819,7 @@ ${code}
}
/** Collects an array of unique outputs. */
function unique<T>(inputs: T[], getOutput: (t: T) => string): string[] {
function unique<T>(inputs: ReadonlyArray<T>, getOutput: (t: T) => string): string[] {
const set = ts.createMap<true>();
for (const input of inputs) {
const out = getOutput(input);
@ -4106,6 +4110,10 @@ namespace FourSlashInterface {
this.state.verifyApplicableRefactorAvailableForRange(this.negative);
}
public refactorsAvailable(names: ReadonlyArray<string>): void {
this.state.verifyRefactorsAvailable(names);
}
public refactor(options: VerifyRefactorOptions) {
this.state.verifyRefactor(options);
}

View file

@ -9,20 +9,19 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
type ContainerDeclaration = ClassLikeDeclaration | ObjectLiteralExpression;
interface Info {
container: ContainerDeclaration;
isStatic: boolean;
isReadonly: boolean;
type: TypeNode | undefined;
declaration: AcceptedDeclaration;
fieldName: AcceptedNameType;
accessorName: AcceptedNameType;
originalName: AcceptedNameType;
renameAccessor: boolean;
readonly container: ContainerDeclaration;
readonly isStatic: boolean;
readonly isReadonly: boolean;
readonly type: TypeNode | undefined;
readonly declaration: AcceptedDeclaration;
readonly fieldName: AcceptedNameType;
readonly accessorName: AcceptedNameType;
readonly originalName: AcceptedNameType;
readonly renameAccessor: boolean;
}
function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined {
const { file } = context;
if (!getConvertibleFieldAtPosition(context, file)) return undefined;
if (!getConvertibleFieldAtPosition(context)) return undefined;
return [{
name: actionName,
@ -39,7 +38,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
function getEditsForAction(context: RefactorContext, _actionName: string): RefactorEditInfo | undefined {
const { file } = context;
const fieldInfo = getConvertibleFieldAtPosition(context, file);
const fieldInfo = getConvertibleFieldAtPosition(context);
if (!fieldInfo) return undefined;
const isJS = isSourceFileJavaScript(file);
@ -117,14 +116,14 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
return name.charCodeAt(0) === CharacterCodes._;
}
function getConvertibleFieldAtPosition(context: RefactorContext, file: SourceFile): Info | undefined {
const { startPosition, endPosition } = context;
function getConvertibleFieldAtPosition(context: RefactorContext): Info | undefined {
const { file, startPosition, endPosition } = context;
const node = getTokenAtPosition(file, startPosition, /*includeJsDocComment*/ false);
const declaration = findAncestor(node.parent, isAcceptedDeclaration);
// make sure declaration have AccessibilityModifier or Static Modifier or Readonly Modifier
const meaning = ModifierFlags.AccessibilityModifier | ModifierFlags.Static | ModifierFlags.Readonly;
if (!declaration || !rangeOverlapsWithStartEnd(declaration.name, startPosition, endPosition!) // TODO: GH#18217
if (!declaration || !nodeOverlapsWithStartEnd(declaration.name, file, startPosition, endPosition!) // TODO: GH#18217
|| !isConvertibleName(declaration.name) || (getModifierFlags(declaration) | meaning) !== meaning) return undefined;
const name = declaration.name.text;

View file

@ -439,6 +439,10 @@ namespace ts {
return startEndOverlapsWithStartEnd(r1.pos, r1.end, start, end);
}
export function nodeOverlapsWithStartEnd(node: Node, sourceFile: SourceFile, start: number, end: number) {
return startEndOverlapsWithStartEnd(node.getStart(sourceFile), node.end, start, end);
}
export function startEndOverlapsWithStartEnd(start1: number, end1: number, start2: number, end2: number) {
const start = Math.max(start1, start2);
const end = Math.min(end1, end2);

View file

@ -10670,6 +10670,7 @@ declare namespace ts {
function startEndContainsRange(start: number, end: number, range: TextRange): boolean;
function rangeContainsStartEnd(range: TextRange, start: number, end: number): boolean;
function rangeOverlapsWithStartEnd(r1: TextRange, start: number, end: number): boolean;
function nodeOverlapsWithStartEnd(node: Node, sourceFile: SourceFile, start: number, end: number): boolean;
function startEndOverlapsWithStartEnd(start1: number, end1: number, start2: number, end2: number): boolean;
/**
* Assumes `candidate.start <= position` holds.

View file

@ -190,6 +190,7 @@ declare namespace FourSlashInterface {
applicableRefactorAvailableForRange(): void;
refactorAvailable(name: string, actionName?: string): void;
refactorsAvailable(names: ReadonlyArray<string>): void;
refactor(options: {
name: string;
actionName: string;

View file

@ -0,0 +1,8 @@
/// <reference path='fourslash.ts' />
////class A {
/////*a*/ /*b*/p = 0;
////}
goTo.select("a", "b");
verify.refactorsAvailable([]);