@@ -112,32 +112,6 @@ namespace ts.server {
112112 return edits . every ( edit => textSpanEnd ( edit . span ) < pos ) ;
113113 }
114114
115- function getGroupBounds < T > ( array : ArrayLike < T > , index : number , predicate : ( element : T ) => boolean ) : [ number , number ] {
116- let first = index ;
117- let last = index ;
118- let i = index ;
119- while ( i > 0 ) {
120- const element = array [ -- i ] ;
121- if ( predicate ( element ) ) {
122- first = i ;
123- }
124- else {
125- break ;
126- }
127- }
128- i = index ;
129- while ( i < array . length - 1 ) {
130- const element = array [ ++ i ] ;
131- if ( predicate ( element ) ) {
132- last = i ;
133- }
134- else {
135- break ;
136- }
137- }
138- return [ first , last ] ;
139- }
140-
141115 // CommandNames used to be exposed before TS 2.4 as a namespace
142116 // In TS 2.4 we switched to an enum, keep this for backward compatibility
143117 // The var assignment ensures that even though CommandTypes are a const enum
@@ -2097,107 +2071,26 @@ namespace ts.server {
20972071 const { locations } = args ;
20982072 const { file, languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ;
20992073
2100- const isImport = or ( isImportDeclaration , isImportEqualsDeclaration ) ;
2074+
21012075 const sourceFile = languageService . getNonBoundSourceFile ( file ) ;
21022076 const scriptInfo = Debug . assertDefined ( this . projectService . getScriptInfo ( file ) ) ;
2103- const fullTextSpan = this . toLocationTextSpan (
2104- createTextSpanFromBounds ( sourceFile . getFullStart ( ) , sourceFile . getEnd ( ) ) ,
2105- scriptInfo ) ;
21062077
21072078 return map ( locations , location => {
21082079 const pos = this . getPosition ( location , scriptInfo ) ;
2109- let selectionRange : protocol . SelectionRange = { textSpan : fullTextSpan } ;
2080+ let selectionRange : protocol . SelectionRange | undefined ;
21102081 const pushSelectionRange = ( start : number , end : number , syntaxKind ?: SyntaxKind ) : void => {
21112082 // Skip ranges that are identical to the parent
21122083 const textSpan = this . toLocationTextSpan ( createTextSpanFromBounds ( start , end ) , scriptInfo ) ;
2113- if ( ! this . locationTextSpansAreEqual ( textSpan , selectionRange . textSpan ) ) {
2114- selectionRange = { textSpan, parent : selectionRange } ;
2084+ if ( ! selectionRange || ! this . locationTextSpansAreEqual ( textSpan , selectionRange . textSpan ) ) {
2085+ selectionRange = { textSpan, ... selectionRange && { parent : selectionRange } } ;
21152086 if ( syntaxKind ) {
21162087 Object . defineProperty ( selectionRange , "__debugKind" , { value : formatSyntaxKind ( syntaxKind ) } ) ;
21172088 }
21182089 }
21192090 } ;
21202091
2121- // Skip top-level SyntaxList
2122- let parentNode = sourceFile . getChildAt ( 0 ) ;
2123- outer: while ( true ) {
2124- const children = parentNode . getChildren ( sourceFile ) ;
2125- if ( ! children . length ) break ;
2126- for ( let i = 0 ; i < children . length ; i ++ ) {
2127- const prevNode : Node | undefined = children [ i - 1 ] ;
2128- const node : Node = children [ i ] ;
2129- const nextNode : Node | undefined = children [ i + 1 ] ;
2130- if ( node . getStart ( sourceFile ) > pos ) {
2131- break outer;
2132- }
2133-
2134- if ( positionBelongsToNode ( node , pos , sourceFile ) ) {
2135- // Blocks are effectively redundant with SyntaxLists.
2136- // TemplateSpans, along with the SyntaxLists containing them,
2137- // are a somewhat unintuitive grouping of things that should be
2138- // considered independently. Dive in without pushing a selection range.
2139- if ( isBlock ( node ) || isTemplateSpan ( node ) || isTemplateHead ( node ) || prevNode && isTemplateHead ( prevNode ) ) {
2140- parentNode = node ;
2141- break ;
2142- }
2143-
2144- // Synthesize a stop for '${ ... }' since '${' and '}' actually belong to siblings.
2145- if ( isTemplateSpan ( parentNode ) && nextNode && isTemplateMiddleOrTemplateTail ( nextNode ) ) {
2146- const start = node . getFullStart ( ) - "${" . length ;
2147- const end = nextNode . getStart ( ) + "}" . length ;
2148- pushSelectionRange ( start , end , node . kind ) ;
2149- }
2150- // Synthesize a stop for group of adjacent imports
2151- else if ( isImport ( node ) ) {
2152- const [ firstImportIndex , lastImportIndex ] = getGroupBounds ( children , i , isImport ) ;
2153- pushSelectionRange (
2154- children [ firstImportIndex ] . getStart ( ) ,
2155- children [ lastImportIndex ] . getEnd ( ) ) ;
2156- }
2157-
2158- // Blocks with braces on separate lines should be selected from brace to brace,
2159- // including whitespace but not including the braces themselves.
2160- const isBetweenMultiLineBraces = isSyntaxList ( node )
2161- && prevNode && prevNode . kind === SyntaxKind . OpenBraceToken
2162- && nextNode && nextNode . kind === SyntaxKind . CloseBraceToken
2163- && ! positionsAreOnSameLine ( prevNode . getStart ( ) , nextNode . getStart ( ) , sourceFile ) ;
2164- const start = isBetweenMultiLineBraces ? prevNode . getEnd ( ) : node . getStart ( ) ;
2165- const end = isBetweenMultiLineBraces ? nextNode . getStart ( ) : node . getEnd ( ) ;
2166- pushSelectionRange ( start , end , node . kind ) ;
2167-
2168- // Mapped types _look_ like ObjectTypes with a single member,
2169- // but in fact don’t contain a SyntaxList or a node containing
2170- // the “key/value” pair like ObjectTypes do, but it seems intuitive
2171- // that the selection would snap to those points. The philosophy
2172- // of choosing a selection range is not so much about what the
2173- // syntax currently _is_ as what the syntax might easily become
2174- // if the user is making a selection; e.g., we synthesize a selection
2175- // around the “key/value” pair not because there’s a node there, but
2176- // because it allows the mapped type to become an object type with a
2177- // few keystrokes.
2178- if ( isMappedTypeNode ( node ) ) {
2179- const openBraceToken = Debug . assertDefined ( node . getFirstToken ( ) ) ;
2180- const firstNonBraceToken = Debug . assertDefined ( node . getChildAt ( 1 ) ) ;
2181- const closeBraceToken = Debug . assertDefined ( node . getLastToken ( ) ) ;
2182- Debug . assertEqual ( openBraceToken . kind , SyntaxKind . OpenBraceToken ) ;
2183- Debug . assertEqual ( closeBraceToken . kind , SyntaxKind . CloseBraceToken ) ;
2184- const spanWithoutBraces = [ openBraceToken . getEnd ( ) , closeBraceToken . getStart ( ) ] as const ;
2185- const spanWithoutBracesOrTrivia = [ firstNonBraceToken . getStart ( ) , closeBraceToken . getFullStart ( ) ] as const ;
2186- pushSelectionRange ( ...spanWithoutBraces ) ;
2187- pushSelectionRange ( ...spanWithoutBracesOrTrivia ) ;
2188- }
2189-
2190- // String literals should have a stop both inside and outside their quotes.
2191- else if ( isStringLiteral ( node ) || isTemplateLiteral ( node ) ) {
2192- pushSelectionRange ( start + 1 , end - 1 ) ;
2193- }
2194-
2195- parentNode = node ;
2196- break ;
2197- }
2198- }
2199- }
2200- return selectionRange ;
2092+ getSelectionRange ( pos , sourceFile , pushSelectionRange ) ;
2093+ return selectionRange ! ;
22012094 } ) ;
22022095 }
22032096
0 commit comments