diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 3397cd242b1..9928be4529b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -621,7 +621,7 @@ namespace ts { return node && (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression); } - export function isFunctionLike(node: Node): boolean { + export function isFunctionLike(node: Node): node is FunctionLikeDeclaration { if (node) { switch (node.kind) { case SyntaxKind.Constructor: diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 1d333a29ccb..7faf4d1cc0b 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1908,7 +1908,7 @@ module FourSlash { } if (actual.newText !== expected.newText) { - this.raiseError(name + ' failed - expected insertion:\n' + expected.newText + '\nactual insertion:\n' + actual.newText); + this.raiseError(name + ' failed - expected insertion:\n' + this.clarifyNewlines(expected.newText) + '\nactual insertion:\n' + this.clarifyNewlines(actual.newText)); } if (actual.caretOffset !== expected.caretOffset) { @@ -1917,6 +1917,13 @@ module FourSlash { } } + private clarifyNewlines(str: string) { + return str.replace(/\r?\n/g, lineEnding => { + const representation = lineEnding === "\r\n" ? "CRLF" : "LF"; + return "# - " + representation + lineEnding; + }); + } + public verifyMatchingBracePosition(bracePosition: number, expectedMatchPosition: number) { this.taoInvalidReason = "verifyMatchingBracePosition NYI"; diff --git a/src/services/services.ts b/src/services/services.ts index 90885043720..3d4ecc4a14d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -7028,14 +7028,31 @@ namespace ts { // - methods // - constructors // - class decls - let containingFunction = getAncestor(tokenAtPos, SyntaxKind.FunctionDeclaration); + let commentOwner: Node; + findOwner: for (commentOwner = tokenAtPos; commentOwner; commentOwner = commentOwner.parent) { + switch (commentOwner.kind) { + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.Constructor: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.VariableStatement: + break findOwner; + case SyntaxKind.SourceFile: + return undefined; + case SyntaxKind.ModuleDeclaration: + // We don't want to give back a JSDoc for the 'b' in 'module a.b'. + if (false && commentOwner.parent.kind === SyntaxKind.ModuleDeclaration) { + return undefined; + } + break findOwner; + } + } - - if (!containingFunction || containingFunction.getStart() < position) { + if (!commentOwner || commentOwner.getStart() < position) { return undefined; } - let parameters = containingFunction.parameters; + let parameters = isFunctionLike(commentOwner) ? commentOwner.parameters : emptyArray; let posLineAndChar = sourceFile.getLineAndCharacterOfPosition(position); let lineStart = sourceFile.getLineStarts()[posLineAndChar.line];