mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-17 21:06:50 -05:00
Expand extractSymbol ranges (#42770)
* allow partial selections of node ranges * separate tests for better failure investigation * gate span expansion behind invoked command * add invoked test * comment wording * for test
This commit is contained in:
@@ -70,7 +70,7 @@ namespace ts.refactor.extractSymbol {
|
||||
let i = 0;
|
||||
for (const { functionExtraction, constantExtraction } of extractions) {
|
||||
const description = functionExtraction.description;
|
||||
if(refactorKindBeginsWith(extractFunctionAction.kind, requestedRefactor)){
|
||||
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
|
||||
@@ -94,8 +94,7 @@ namespace ts.refactor.extractSymbol {
|
||||
}
|
||||
}
|
||||
|
||||
// Skip these since we don't have a way to report errors yet
|
||||
if(refactorKindBeginsWith(extractConstantAction.kind, requestedRefactor)) {
|
||||
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
|
||||
@@ -265,24 +264,30 @@ namespace ts.refactor.extractSymbol {
|
||||
/**
|
||||
* getRangeToExtract takes a span inside a text file and returns either an expression or an array
|
||||
* of statements representing the minimum set of nodes needed to extract the entire span. This
|
||||
* process may fail, in which case a set of errors is returned instead (these are currently
|
||||
* not shown to the user, but can be used by us diagnostically)
|
||||
* process may fail, in which case a set of errors is returned instead. These errors are shown to
|
||||
* users if they have the provideRefactorNotApplicableReason option set.
|
||||
*/
|
||||
// exported only for tests
|
||||
export function getRangeToExtract(sourceFile: SourceFile, span: TextSpan, considerEmptySpans = true): RangeToExtract {
|
||||
export function getRangeToExtract(sourceFile: SourceFile, span: TextSpan, invoked = true): RangeToExtract {
|
||||
const { length } = span;
|
||||
if (length === 0 && !considerEmptySpans) {
|
||||
if (length === 0 && !invoked) {
|
||||
return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractEmpty)] };
|
||||
}
|
||||
const cursorRequest = length === 0 && considerEmptySpans;
|
||||
const cursorRequest = length === 0 && invoked;
|
||||
|
||||
const startToken = getTokenAtPosition(sourceFile, span.start);
|
||||
const endToken = findTokenOnLeftOfPosition(sourceFile, textSpanEnd(span));
|
||||
/* If the refactoring command is invoked through a keyboard action it's safe to assume that the user is actively looking for
|
||||
refactoring actions at the span location. As they may not know the exact range that will trigger a refactoring, we expand the
|
||||
searched span to cover a real node range making it more likely that something useful will show up. */
|
||||
const adjustedSpan = startToken && endToken && invoked ? getAdjustedSpanFromNodes(startToken, endToken, sourceFile) : span;
|
||||
|
||||
// Walk up starting from the the start position until we find a non-SourceFile node that subsumes the selected span.
|
||||
// This may fail (e.g. you select two statements in the root of a source file)
|
||||
const startToken = getTokenAtPosition(sourceFile, span.start);
|
||||
const start = cursorRequest ? getExtractableParent(startToken): getParentNodeInSpan(startToken, sourceFile, span);
|
||||
const start = cursorRequest ? getExtractableParent(startToken): getParentNodeInSpan(startToken, sourceFile, adjustedSpan);
|
||||
|
||||
// Do the same for the ending position
|
||||
const endToken = findTokenOnLeftOfPosition(sourceFile, textSpanEnd(span));
|
||||
const end = cursorRequest ? start : getParentNodeInSpan(endToken, sourceFile, span);
|
||||
const end = cursorRequest ? start : getParentNodeInSpan(endToken, sourceFile, adjustedSpan);
|
||||
|
||||
const declarations: Symbol[] = [];
|
||||
|
||||
@@ -295,6 +300,10 @@ namespace ts.refactor.extractSymbol {
|
||||
return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractRange)] };
|
||||
}
|
||||
|
||||
if (isJSDoc(start)) {
|
||||
return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractJSDoc)] };
|
||||
}
|
||||
|
||||
if (start.parent !== end.parent) {
|
||||
// start and end nodes belong to different subtrees
|
||||
return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractRange)] };
|
||||
@@ -332,10 +341,6 @@ namespace ts.refactor.extractSymbol {
|
||||
return { targetRange: { range: statements, facts: rangeFacts, declarations } };
|
||||
}
|
||||
|
||||
if (isJSDoc(start)) {
|
||||
return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractJSDoc)] };
|
||||
}
|
||||
|
||||
if (isReturnStatement(start) && !start.expression) {
|
||||
// Makes no sense to extract an expression-less return statement.
|
||||
return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractRange)] };
|
||||
@@ -605,6 +610,19 @@ namespace ts.refactor.extractSymbol {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes the final semicolon so that the span covers statements in cases where it would otherwise
|
||||
* only cover the declaration list.
|
||||
*/
|
||||
function getAdjustedSpanFromNodes(startNode: Node, endNode: Node, sourceFile: SourceFile): TextSpan {
|
||||
const start = startNode.getStart(sourceFile);
|
||||
let end = endNode.getEnd();
|
||||
if (sourceFile.text.charCodeAt(end) === CharacterCodes.semicolon) {
|
||||
end++;
|
||||
}
|
||||
return { start, length: end - start };
|
||||
}
|
||||
|
||||
function getStatementOrExpressionRange(node: Node): Statement[] | Expression | undefined {
|
||||
if (isStatement(node)) {
|
||||
return [node];
|
||||
|
||||
Reference in New Issue
Block a user