CR feedback. Add more tests.

This commit is contained in:
zhengbli
2015-08-17 15:35:27 -07:00
parent 9dac516b26
commit 2a3867a742
3 changed files with 57 additions and 43 deletions

View File

@@ -132,7 +132,7 @@ namespace ts {
let emptyArray: any[] = [];
const JsDocTagNames: string[] = [
const jsDocTagNames: string[] = [
"augments",
"author",
"argument",
@@ -170,6 +170,7 @@ namespace ts {
"type",
"version"
];
let jsDocCompletionEntries: CompletionEntry[];
function createNode(kind: SyntaxKind, pos: number, end: number, flags: NodeFlags, parent?: Node): NodeObject {
let node = <NodeObject> new (getNodeConstructor(kind))();
@@ -2957,6 +2958,8 @@ namespace ts {
let sourceFile = getValidSourceFile(fileName);
let isJavaScriptFile = isJavaScript(fileName);
let isJsDocTagName = false;
let start = new Date().getTime();
let currentToken = getTokenAtPosition(sourceFile, position);
log("getCompletionData: Get current token: " + (new Date().getTime() - start));
@@ -2967,12 +2970,22 @@ namespace ts {
log("getCompletionData: Is inside comment: " + (new Date().getTime() - start));
if (insideComment) {
// The current position is next to the '@' sign, when no tag name being provided yet.
// Provide a full list of tag names
if (hasDocComment(sourceFile, position) && sourceFile.text.charCodeAt(position - 1) === CharacterCodes.at) {
isJsDocTagName = true;
}
// Completion should work inside certain JsDoc tags. For example:
// /** @type {number | string} */
// Completion should work in the brackets
let insideJsDocTagExpression = false;
let tag = getJsDocTagAtPosition(sourceFile, position);
if (tag) {
if (tag.tagName.pos <= position && position <= tag.tagName.end) {
isJsDocTagName = true;
}
switch (tag.kind) {
case SyntaxKind.JSDocTypeTag:
case SyntaxKind.JSDocParameterTag:
@@ -2983,7 +2996,14 @@ namespace ts {
break;
}
}
if (isJsDocTagName) {
return { symbols: undefined, isMemberCompletion: false, isNewIdentifierLocation: false, location: undefined, isRightOfDot: false, isJsDocTagName };
}
if (!insideJsDocTagExpression) {
// Proceed if the current position is in jsDoc tag expression; otherwise it is a normal
// comment or the plain text part of a jsDoc comment, so no completion should be available
log("Returning an empty list because completion was inside a regular comment or plain text part of a JsDoc comment.");
return undefined;
}
@@ -3072,7 +3092,7 @@ namespace ts {
log("getCompletionData: Semantic work: " + (new Date().getTime() - semanticStart));
return { symbols, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag) };
return { symbols, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), isJsDocTagName };
function getTypeScriptMemberSymbols(): void {
// Right of dot member completion list
@@ -3710,16 +3730,17 @@ namespace ts {
let completionData = getCompletionData(fileName, position);
if (!completionData) {
let entries = getJsDocCompletionEntries(fileName, position);
if (entries) {
return { isMemberCompletion: false, isNewIdentifierLocation: false, entries };
}
return undefined;
}
let { symbols, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot } = completionData;
let { symbols, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot, isJsDocTagName } = completionData;
let entries: CompletionEntry[];
if (isJsDocTagName) {
// If the current position is a jsDoc tag name, only tag names should be provided for completion
return { isMemberCompletion: false, isNewIdentifierLocation: false, entries: getAllJsDocCompletionEntries() };
}
if (isRightOfDot && isJavaScript(fileName)) {
entries = getCompletionEntriesFromSymbols(symbols);
addRange(entries, getJavaScriptCompletionEntries());
@@ -3733,7 +3754,7 @@ namespace ts {
}
// Add keywords if this is not a member completion list
if (!isMemberCompletion) {
if (!isMemberCompletion && !isJsDocTagName) {
addRange(entries, keywordCompletions);
}
@@ -3766,37 +3787,15 @@ namespace ts {
return entries;
}
function getJsDocCompletionEntries(fileName: string, position: number): CompletionEntry[] {
let sourceFile = getValidSourceFile(fileName);
let hasJsDocComment = ts.hasDocComment(sourceFile, position);
if (!hasJsDocComment) {
return undefined;
}
// Return a list of JsDoc tag names for completion if the current position is right after an '@' sign
if (sourceFile.text.charCodeAt(position - 1) === CharacterCodes.at) {
return getAllJsDocCompletionEntries();
}
// Also return tag names if the current position is inside a tag name, e.g. (at the location of '^')
// /** @par^ */
let tag = getJsDocTagAtPosition(sourceFile, position);
if (tag) {
if (tag.atToken.end <= position && position <= tag.tagName.end) {
return getAllJsDocCompletionEntries();
}
}
}
function getAllJsDocCompletionEntries(): CompletionEntry[] {
return ts.map(JsDocTagNames, tagName => {
return jsDocCompletionEntries || (jsDocCompletionEntries = ts.map(jsDocTagNames, tagName => {
return {
name: tagName,
kind: ScriptElementKind.keyword,
kindModifiers: "",
sortText: "0",
}
});
}));
}
function createCompletionEntry(symbol: Symbol, location: Node): CompletionEntry {

View File

@@ -475,18 +475,21 @@ namespace ts {
export function getJsDocTagAtPosition(sourceFile: SourceFile, position: number): JSDocTag {
let node = ts.getTokenAtPosition(sourceFile, position);
let jsDocComment: JSDocComment;
while (true) {
if (node === sourceFile ||
node.kind === SyntaxKind.VariableStatement ||
node.kind === SyntaxKind.FunctionDeclaration ||
node.kind === SyntaxKind.Parameter ||
node.jsDocComment) {
jsDocComment = node.jsDocComment;
break;
}
node = node.parent;
if (node.jsDocComment) {
jsDocComment = node.jsDocComment;
}
else {
while (node) {
if (node.kind === SyntaxKind.VariableStatement ||
node.kind === SyntaxKind.FunctionDeclaration ||
node.kind === SyntaxKind.Parameter) {
jsDocComment = node.jsDocComment;
break;
}
node = node.parent;
}
}
if (jsDocComment) {
for (let tag of jsDocComment.tags) {
if (tag.pos <= position && position <= tag.end) {
@@ -494,6 +497,7 @@ namespace ts {
}
}
}
return undefined;
}