diff --git a/src/server/session.ts b/src/server/session.ts index f64674b129a..ed9f4d8990a 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -2155,12 +2155,14 @@ namespace ts.server { children[lastImportIndex].getEnd()); } - // Blocks with braces should be selected from brace to brace, non-inclusive - const isBetweenBraces = isSyntaxList(node) + // Blocks with braces on separate lines should be selected from brace to brace, + // including whitespace but not including the braces themselves. + const isBetweenMultiLineBraces = isSyntaxList(node) && prevNode && prevNode.kind === SyntaxKind.OpenBraceToken - && nextNode && nextNode.kind === SyntaxKind.CloseBraceToken; - const start = isBetweenBraces ? prevNode.getEnd() : node.getStart(); - const end = isBetweenBraces ? nextNode.getStart() : node.getEnd(); + && nextNode && nextNode.kind === SyntaxKind.CloseBraceToken + && !positionsAreOnSameLine(prevNode.getStart(), nextNode.getStart(), sourceFile); + const start = isBetweenMultiLineBraces ? prevNode.getEnd() : node.getStart(); + const end = isBetweenMultiLineBraces ? nextNode.getStart() : node.getEnd(); pushSelectionRange(start, end, node.kind); // Mapped types _look_ like ObjectTypes with a single member, diff --git a/src/testRunner/unittests/tsserver/selectionRange.ts b/src/testRunner/unittests/tsserver/selectionRange.ts index ce88b4072cc..b4858407ee8 100644 --- a/src/testRunner/unittests/tsserver/selectionRange.ts +++ b/src/testRunner/unittests/tsserver/selectionRange.ts @@ -199,21 +199,13 @@ type X = IsExactlyAny

extends true ? T : ({ [K in keyof P]: IsExactlyAn const getSelectionRange = setup("/file.js", ` type X = { foo?: string; - readonly bar: number; + readonly bar: { x: number }; }`); const locations = getSelectionRange([ - { - line: 3, - offset: 5, - }, - { - line: 4, - offset: 5, - }, - { - line: 4, - offset: 14, - }, + { line: 3, offset: 5 }, + { line: 4, offset: 5 }, + { line: 4, offset: 14 }, + { line: 4, offset: 27 }, ]); const allMembersUp: protocol.SelectionRange = { @@ -238,9 +230,9 @@ type X = { start: { line: 4, offset: 5 }, end: { line: 4, offset: 17 } }, parent: { - textSpan: { // readonly bar: number; + textSpan: { // readonly bar: { x: number }; start: { line: 4, offset: 5 }, - end: { line: 4, offset: 26 } }, + end: { line: 4, offset: 33 } }, parent: allMembersUp } }; assert.deepEqual(locations, [ @@ -267,6 +259,19 @@ type X = { start: { line: 4, offset: 14 }, end: { line: 4, offset: 17 } }, parent: readonlyBarUp }, + { + 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: 19 }, + end: { line: 4, offset: 32 } }, + parent: readonlyBarUp } } }, ]); }); @@ -313,7 +318,7 @@ type X = { ]); }); - it.skip("works for ES2015 import lists", () => { + it("works for ES2015 import lists", () => { const getSelectionRange = setup("/file.ts", ` import { x as y, z } from './z'; import { b } from './';