Fix "Extract to type alias" not available at end of span (#56467)

Signed-off-by: Babak K. Shandiz <babak.k.shandiz@gmail.com>
This commit is contained in:
Babak K. Shandiz
2024-01-05 17:52:26 +00:00
committed by GitHub
parent e441420483
commit b2c6a56e38
2 changed files with 82 additions and 7 deletions

View File

@@ -23,6 +23,7 @@ import {
getRefactorContextSpan,
getRenameLocation,
getTokenAtPosition,
getTouchingToken,
getUniqueName,
ignoreSourceNewlines,
isArray,
@@ -173,14 +174,9 @@ type ExtractInfo = TypeAliasInfo | InterfaceInfo;
function getRangeToExtract(context: RefactorContext, considerEmptySpans = true): ExtractInfo | RefactorErrorInfo | undefined {
const { file, startPosition } = context;
const isJS = isSourceFileJS(file);
const current = getTokenAtPosition(file, startPosition);
const range = createTextRangeFromSpan(getRefactorContextSpan(context));
const cursorRequest = range.pos === range.end && considerEmptySpans;
const overlappingRange = nodeOverlapsWithStartEnd(current, file, range.pos, range.end);
const firstType = findAncestor(current, node =>
node.parent && isTypeNode(node) && !rangeContainsSkipTrivia(range, node.parent, file) &&
(cursorRequest || overlappingRange));
const isCursorRequest = range.pos === range.end && considerEmptySpans;
const firstType = getFirstTypeAt(file, startPosition, range, isCursorRequest);
if (!firstType || !isTypeNode(firstType)) return { error: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_type_node) };
const checker = context.program.getTypeChecker();
@@ -209,6 +205,24 @@ function getRangeToExtract(context: RefactorContext, considerEmptySpans = true):
return { isJS, selection, enclosingNode, typeParameters, typeElements };
}
function getFirstTypeAt(file: SourceFile, startPosition: number, range: TextRange, isCursorRequest: boolean): Node | undefined {
const currentNodes = [
() => getTokenAtPosition(file, startPosition),
() => getTouchingToken(file, startPosition, () => true),
];
for (const f of currentNodes) {
const current = f();
const overlappingRange = nodeOverlapsWithStartEnd(current, file, range.pos, range.end);
const firstType = findAncestor(current, node =>
node.parent && isTypeNode(node) && !rangeContainsSkipTrivia(range, node.parent, file) &&
(isCursorRequest || overlappingRange));
if (firstType) {
return firstType;
}
}
return undefined;
}
function flattenTypeLiteralNodeReference(checker: TypeChecker, selection: TypeNode | TypeNode[] | undefined): readonly TypeElement[] | undefined {
if (!selection) return undefined;
if (isArray(selection)) {