Start imports

This commit is contained in:
Andrew Branch 2019-04-11 11:38:05 -07:00
parent e62c2333eb
commit fd88e52252
No known key found for this signature in database
GPG key ID: 22CCA4B120C427D2
2 changed files with 92 additions and 23 deletions

View file

@ -112,6 +112,32 @@ namespace ts.server {
return edits.every(edit => textSpanEnd(edit.span) < pos);
}
function getGroupBounds<T>(array: ArrayLike<T>, index: number, predicate: (element: T) => boolean): [number, number] {
let first = index;
let last = index;
let i = index;
while (i > 0) {
const element = array[--i];
if (predicate(element)) {
first = i;
}
else {
break;
}
}
i = index;
while (i < array.length - 1) {
const element = array[++i];
if (predicate(element)) {
last = i;
}
else {
break;
}
}
return [first, last];
}
// CommandNames used to be exposed before TS 2.4 as a namespace
// In TS 2.4 we switched to an enum, keep this for backward compatibility
// The var assignment ensures that even though CommandTypes are a const enum
@ -2071,6 +2097,7 @@ namespace ts.server {
const { locations } = args;
const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args);
const isImport = or(isImportDeclaration, isImportEqualsDeclaration);
const sourceFile = languageService.getNonBoundSourceFile(file);
const scriptInfo = Debug.assertDefined(this.projectService.getScriptInfo(file));
const fullTextSpan = this.toLocationTextSpan(
@ -2092,37 +2119,41 @@ namespace ts.server {
};
// Skip top-level SyntaxList
let current: Node | undefined = sourceFile.getChildAt(0);
let isInTemplateSpan = false;
while (true) {
const children = current && current.getChildren(sourceFile);
if (!children || !children.length) break;
let parentNode = sourceFile.getChildAt(0);
outer: while (true) {
const children = parentNode.getChildren(sourceFile);
if (!children.length) break;
for (let i = 0; i < children.length; i++) {
const prevNode: Node | undefined = children[i - 1];
const node: Node = children[i];
const nextNode: Node | undefined = children[i + 1];
if (node.getStart(sourceFile) > pos) {
current = undefined;
break;
}
// Blocks are effectively redundant with SyntaxLists.
// TemplateSpans are an unintuitive grouping of two things which
// should be considered independently.
// Dive in without pushing a selection range.
const nodeIsTemplateSpan = isTemplateSpan(node);
const nodeIsTemplateSpanList = prevNode && isTemplateHead(prevNode);
if (isBlock(node) || nodeIsTemplateSpan || nodeIsTemplateSpanList) {
isInTemplateSpan = nodeIsTemplateSpan;
current = node;
break;
break outer;
}
if (positionBelongsToNode(node, pos, sourceFile)) {
// Blocks are effectively redundant with SyntaxLists.
// TemplateSpans, along with the SyntaxLists containing them,
// are a somewhat unintuitive grouping of things that should be
// considered independently. Dive in without pushing a selection range.
if (isBlock(node) || isTemplateSpan(node) || prevNode && isTemplateHead(prevNode)) {
parentNode = node;
break;
}
// Synthesize a stop for '${ ... }' since '${' and '}' actually belong to siblings.
if (isInTemplateSpan && nextNode && isTemplateMiddleOrTemplateTail(nextNode)) {
if (isTemplateSpan(parentNode) && nextNode && isTemplateMiddleOrTemplateTail(nextNode)) {
const start = node.getFullStart() - "${".length;
const end = nextNode.getStart() + "}".length;
pushSelectionRange(start, end, node.kind);
}
// Synthesize a stop for group of adjacent imports
else if (isImport(node)) {
const [firstImportIndex, lastImportIndex] = getGroupBounds(children, i, isImport);
pushSelectionRange(
children[firstImportIndex].getStart(),
children[lastImportIndex].getEnd());
}
// Blocks with braces should be selected from brace to brace, non-inclusive
const isBetweenBraces = isSyntaxList(node)
@ -2155,13 +2186,11 @@ namespace ts.server {
}
// String literals should have a stop both inside and outside their quotes.
if (isStringLiteral(node) || isTemplateLiteral(node)) {
else if (isStringLiteral(node) || isTemplateLiteral(node)) {
pushSelectionRange(start + 1, end - 1);
}
// If weve made it here, weve already used `isInTemplateSpan` as much as we need
isInTemplateSpan = false;
current = node;
parentNode = node;
break;
}
}

View file

@ -301,5 +301,45 @@ type X = {
end: { line: 3, offset: 5 } } } } } } }
]);
});
it.skip("works for ES2015 import lists", () => {
const getSelectionRange = setup("/file.ts", `
import { x as y, z } from './z';
import { b } from './';
console.log(1);`);
const locations = getSelectionRange([{ line: 2, offset: 10 }]);
assert.deepEqual(locations, [
{
textSpan: { // x
start: { line: 2, offset: 10 },
end: { line: 2, offset: 11 } },
parent: {
textSpan: { // x as y
start: { line: 2, offset: 10 },
end: { line: 2, offset: 16 } },
parent: {
textSpan: { // x as y, z
start: { line: 2, offset: 10 },
end: { line: 2, offset: 19 } },
parent: {
textSpan: { // { x as y, z }
start: { line: 2, offset: 8 },
end: { line: 2, offset: 21 } },
parent: {
textSpan: { // import { x as y, z } from './z';
start: { line: 2, offset: 1 },
end: { line: 2, offset: 33 } },
parent: {
textSpan: { // all imports
start: { line: 2, offset: 1 },
end: { line: 3, offset: 24 } },
parent: {
textSpan: { // SourceFile
start: { line: 1, offset: 1 },
end: { line: 5, offset: 16 } } } } } } } } }
]);
});
});
}