Add rules for expanding selection to sibling nodes

This commit is contained in:
Andrew Branch 2019-04-12 10:53:20 -07:00
parent 0f7bc02892
commit 70e2672ab3
No known key found for this signature in database
GPG key ID: 22CCA4B120C427D2
2 changed files with 74 additions and 44 deletions

View file

@ -13,9 +13,9 @@ namespace ts.SelectionRange {
const children = parentNode.getChildren(sourceFile);
if (!children.length) break;
for (let i = 0; i < children.length; i++) {
const prevNode: Node | undefined = children[i - 1];
let prevNode: Node | undefined = children[i - 1];
const node: Node = children[i];
const nextNode: Node | undefined = children[i + 1];
let nextNode: Node | undefined = children[i + 1];
if (node.getStart(sourceFile) > pos) {
break outer;
}
@ -30,6 +30,26 @@ namespace ts.SelectionRange {
break;
}
const siblingExpansionRule = getSiblingExpansionRule(parentNode);
let expansionCandidate: SyntaxKind | [SyntaxKind, SyntaxKind] | undefined;
while ((prevNode || nextNode) && (expansionCandidate = siblingExpansionRule.shift())) {
if (isArray(expansionCandidate)
&& prevNode && prevNode.kind === expansionCandidate[0]
&& nextNode && nextNode.kind === expansionCandidate[1]) {
pushSelectionRange(prevNode.getStart(), nextNode.getEnd());
prevNode = children[children.indexOf(prevNode) - 1];
nextNode = children[children.indexOf(nextNode) + 1];
}
else if (prevNode && prevNode.kind === expansionCandidate) {
pushSelectionRange(prevNode.getStart(), node.getEnd());
prevNode = children[children.indexOf(prevNode) - 1];
}
else if (nextNode && nextNode.kind === expansionCandidate) {
pushSelectionRange(node.getStart(), nextNode.getEnd());
nextNode = children[children.indexOf(nextNode) + 1];
}
}
// Synthesize a stop for '${ ... }' since '${' and '}' actually belong to siblings.
if (isTemplateSpan(parentNode) && nextNode && isTemplateMiddleOrTemplateTail(nextNode)) {
const start = node.getFullStart() - "${".length;
@ -103,15 +123,24 @@ namespace ts.SelectionRange {
}
}
// function getSiblingExpansionRule<T extends Node>(parentNode: T): (SyntaxKind | SyntaxKind[])[] | undefined {
// switch (parentNode.kind) {
// case SyntaxKind.BindingElement: return [SyntaxKind.Identifier, SyntaxKind.DotDotDotToken];
// case SyntaxKind.Parameter: return [SyntaxKind.Identifier, SyntaxKind.DotDotDotToken, SyntaxKind.QuestionToken];
// case SyntaxKind.PropertySignature: return [SyntaxKind.Identifier, SyntaxKind.QuestionToken, SyntaxKind.SyntaxList];
// case SyntaxKind.ElementAccessExpression: return [[SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken]];
// case SyntaxKind.IndexedAccessType: return [[SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken]];
// }
// }
function getSiblingExpansionRule<T extends Node>(parentNode: T): (SyntaxKind | [SyntaxKind, SyntaxKind])[] {
switch (parentNode.kind) {
case SyntaxKind.BindingElement: return [SyntaxKind.Identifier, SyntaxKind.DotDotDotToken];
case SyntaxKind.Parameter: return [SyntaxKind.Identifier, SyntaxKind.DotDotDotToken, SyntaxKind.QuestionToken];
case SyntaxKind.PropertySignature: return [SyntaxKind.Identifier, SyntaxKind.QuestionToken, SyntaxKind.SyntaxList];
case SyntaxKind.ElementAccessExpression: return [[SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken]];
case SyntaxKind.MappedType: return [
SyntaxKind.TypeParameter,
[SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken],
SyntaxKind.MinusToken,
SyntaxKind.PlusToken,
SyntaxKind.ReadonlyKeyword,
SyntaxKind.MinusToken,
SyntaxKind.PlusToken,
];
default: return [];
}
}
function getGroupBounds<T>(array: ArrayLike<T>, index: number, predicate: (element: T) => boolean): [number, number] {
let first = index;

View file

@ -231,44 +231,45 @@ type X = {
end: { line: 4, offset: 33 } },
parent: allMembersUp } };
assert.deepEqual(locations, [
{
textSpan: { // foo
assert.deepEqual(locations![0], {
textSpan: { // foo
start: { line: 3, offset: 5 },
end: { line: 3, offset: 8 } },
parent: {
textSpan: { // foo?
start: { line: 3, offset: 5 },
end: { line: 3, offset: 8 } },
end: { line: 3, offset: 9 } },
parent: {
textSpan: { // foo?
textSpan: { // foo?: string;
start: { line: 3, offset: 5 },
end: { line: 3, offset: 9 } },
parent: {
textSpan: { // foo?: string;
start: { line: 3, offset: 5 },
end: { line: 3, offset: 18 } },
parent: allMembersUp } } },
{
textSpan: { // readonly
start: { line: 4, offset: 5 },
end: { line: 4, offset: 13 } },
parent: readonlyBarUp },
{
textSpan: { // bar
start: { line: 4, offset: 14 },
end: { line: 4, offset: 17 } },
parent: readonlyBarUp },
{
textSpan: { // number
start: { line: 4, offset: 24 },
end: { line: 3, offset: 18 } },
parent: allMembersUp } } });
assert.deepEqual(locations![1], {
textSpan: { // readonly
start: { line: 4, offset: 5 },
end: { line: 4, offset: 13 } },
parent: readonlyBarUp });
assert.deepEqual(locations![2], {
textSpan: { // bar
start: { line: 4, offset: 14 },
end: { line: 4, offset: 17 } },
parent: readonlyBarUp });
assert.deepEqual(locations![3], {
textSpan: { // number
start: { line: 4, offset: 24 },
end: { line: 4, offset: 30 } },
parent: {
textSpan: { // x: number
start: { line: 4, offset: 21 },
end: { line: 4, offset: 30 } },
parent: {
textSpan: { // x: number
start: { line: 4, offset: 21 },
end: { line: 4, offset: 30 } },
parent: {
textSpan: { // { x: number }
start: { line: 4, offset: 19 },
end: { line: 4, offset: 32 } },
parent: readonlyBarUp } } },
]);
textSpan: { // { x: number }
start: { line: 4, offset: 19 },
end: { line: 4, offset: 32 } },
parent: readonlyBarUp.parent } } });
});
it("works for string literals and template strings", () => {