(sourceFile, span, invoked)
| 160171 | */ |
| 160172 | // exported only for tests |
| 160173 | function getRangeToExtract(sourceFile, span, invoked) { |
| 160174 | if (invoked === void 0) { invoked = true; } |
| 160175 | var length = span.length; |
| 160176 | if (length === 0 && !invoked) { |
| 160177 | return { errors: [ts.createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractEmpty)] }; |
| 160178 | } |
| 160179 | var cursorRequest = length === 0 && invoked; |
| 160180 | var startToken = ts.findFirstNonJsxWhitespaceToken(sourceFile, span.start); |
| 160181 | var endToken = ts.findTokenOnLeftOfPosition(sourceFile, ts.textSpanEnd(span)); |
| 160182 | /* If the refactoring command is invoked through a keyboard action it's safe to assume that the user is actively looking for |
| 160183 | refactoring actions at the span location. As they may not know the exact range that will trigger a refactoring, we expand the |
| 160184 | searched span to cover a real node range making it more likely that something useful will show up. */ |
| 160185 | var adjustedSpan = startToken && endToken && invoked ? getAdjustedSpanFromNodes(startToken, endToken, sourceFile) : span; |
| 160186 | // Walk up starting from the the start position until we find a non-SourceFile node that subsumes the selected span. |
| 160187 | // This may fail (e.g. you select two statements in the root of a source file) |
| 160188 | var start = cursorRequest ? getExtractableParent(startToken) : ts.getParentNodeInSpan(startToken, sourceFile, adjustedSpan); |
| 160189 | // Do the same for the ending position |
| 160190 | var end = cursorRequest ? start : ts.getParentNodeInSpan(endToken, sourceFile, adjustedSpan); |
| 160191 | var declarations = []; |
| 160192 | // We'll modify these flags as we walk the tree to collect data |
| 160193 | // about what things need to be done as part of the extraction. |
| 160194 | var rangeFacts = RangeFacts.None; |
| 160195 | var thisNode; |
| 160196 | if (!start || !end) { |
| 160197 | // cannot find either start or end node |
| 160198 | return { errors: [ts.createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractRange)] }; |
| 160199 | } |
| 160200 | if (start.flags & 8388608 /* NodeFlags.JSDoc */) { |
| 160201 | return { errors: [ts.createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractJSDoc)] }; |
| 160202 | } |
| 160203 | if (start.parent !== end.parent) { |
| 160204 | // start and end nodes belong to different subtrees |
| 160205 | return { errors: [ts.createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractRange)] }; |
| 160206 | } |
| 160207 | if (start !== end) { |
| 160208 | // start and end should be statements and parent should be either block or a source file |
| 160209 | if (!isBlockLike(start.parent)) { |
| 160210 | return { errors: [ts.createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractRange)] }; |
| 160211 | } |
| 160212 | var statements = []; |
| 160213 | for (var _i = 0, _a = start.parent.statements; _i < _a.length; _i++) { |
| 160214 | var statement = _a[_i]; |
| 160215 | if (statement === start || statements.length) { |
| 160216 | var errors_1 = checkNode(statement); |
| 160217 | if (errors_1) { |
| 160218 | return { errors: errors_1 }; |
| 160219 | } |
| 160220 | statements.push(statement); |
| 160221 | } |
| 160222 | if (statement === end) { |
| 160223 | break; |
| 160224 | } |
| 160225 | } |
| 160226 | if (!statements.length) { |
| 160227 | // https://github.com/Microsoft/TypeScript/issues/20559 |
| 160228 | // Ranges like [|case 1: break;|] will fail to populate `statements` because |
| 160229 | // they will never find `start` in `start.parent.statements`. |
| 160230 | // Consider: We could support ranges like [|case 1:|] by refining them to just |
no test coverage detected