diff --git a/src/services/services.ts b/src/services/services.ts index ce9f6cdaf56..9a689a7ff33 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -352,7 +352,8 @@ module ts { function isName(pos: number, end: number, sourceFile: SourceFile, name: string) { return pos + name.length < end && sourceFile.text.substr(pos, name.length) === name && - isWhiteSpace(sourceFile.text.charCodeAt(pos + name.length)); + (isWhiteSpace(sourceFile.text.charCodeAt(pos + name.length)) || + isLineBreak(sourceFile.text.charCodeAt(pos + name.length))); } function isParamTag(pos: number, end: number, sourceFile: SourceFile) { @@ -360,9 +361,16 @@ module ts { return isName(pos, end, sourceFile, paramTag); } + function pushDocCommentLineText(docComments: SymbolDisplayPart[], text: string, blankLineCount: number) { + // Add the empty lines in between texts + while (blankLineCount--) docComments.push(textPart("")); + docComments.push(textPart(text)); + } + function getCleanedJsDocComment(pos: number, end: number, sourceFile: SourceFile) { var spacesToRemoveAfterAsterisk: number; var docComments: SymbolDisplayPart[] = []; + var blankLineCount = 0; var isInParamTag = false; while (pos < end) { @@ -411,7 +419,12 @@ module ts { // Continue with next line pos = consumeLineBreaks(pos, end, sourceFile); if (docCommentTextOfLine) { - docComments.push(textPart(docCommentTextOfLine)); + pushDocCommentLineText(docComments, docCommentTextOfLine, blankLineCount); + blankLineCount = 0; + } + else if (!isInParamTag && docComments.length) { + // This is blank line when there is text already parsed + blankLineCount++; } } @@ -423,6 +436,8 @@ module ts { var paramDocComments: SymbolDisplayPart[] = []; while (pos < end) { if (isParamTag(pos, end, sourceFile)) { + var blankLineCount = 0; + var recordedParamTag = false; // Consume leading spaces pos = consumeWhiteSpaces(pos + paramTag.length); if (pos >= end) { @@ -484,8 +499,13 @@ module ts { // at line break, set this comment line text and go to next line if (isLineBreak(ch)) { if (paramHelpString) { - paramDocComments.push(textPart(paramHelpString)); + pushDocCommentLineText(paramDocComments, paramHelpString, blankLineCount); paramHelpString = ""; + blankLineCount = 0; + recordedParamTag = true; + } + else if (recordedParamTag) { + blankLineCount++; } // Get the pos after cleaning start of the line @@ -506,7 +526,7 @@ module ts { // If there is param help text, add it top the doc comments if (paramHelpString) { - paramDocComments.push(textPart(paramHelpString)); + pushDocCommentLineText(paramDocComments, paramHelpString, blankLineCount); } paramHelpStringMargin = undefined; } @@ -2682,6 +2702,7 @@ module ts { case SyntaxKind.VarKeyword: case SyntaxKind.GetKeyword: case SyntaxKind.SetKeyword: + case SyntaxKind.ImportKeyword: return true; } @@ -2852,10 +2873,10 @@ module ts { if (flags & SymbolFlags.Property) { if (flags & SymbolFlags.UnionProperty) { - // If union property is result of union of non method (property/accessors), it is labeled as property + // If union property is result of union of non method (property/accessors/variables), it is labeled as property var unionPropertyKind = forEach(typeInfoResolver.getRootSymbols(symbol), rootSymbol => { var rootSymbolFlags = rootSymbol.getFlags(); - if (rootSymbolFlags & (SymbolFlags.Property | SymbolFlags.GetAccessor | SymbolFlags.SetAccessor)) { + if (rootSymbolFlags & (SymbolFlags.PropertyOrAccessor | SymbolFlags.Variable)) { return ScriptElementKind.memberVariableElement; } Debug.assert(!!(rootSymbolFlags & SymbolFlags.Method)); @@ -3129,13 +3150,13 @@ module ts { displayParts.push(keywordPart(SyntaxKind.ImportKeyword)); displayParts.push(spacePart()); addFullSymbolName(symbol); - displayParts.push(spacePart()); - displayParts.push(punctuationPart(SyntaxKind.EqualsToken)); - displayParts.push(spacePart()); ts.forEach(symbol.declarations, declaration => { if (declaration.kind === SyntaxKind.ImportDeclaration) { var importDeclaration = declaration; if (importDeclaration.externalModuleName) { + displayParts.push(spacePart()); + displayParts.push(punctuationPart(SyntaxKind.EqualsToken)); + displayParts.push(spacePart()); displayParts.push(keywordPart(SyntaxKind.RequireKeyword)); displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); displayParts.push(displayPart(getTextOfNode(importDeclaration.externalModuleName), SymbolDisplayPartKind.stringLiteral)); @@ -3143,7 +3164,12 @@ module ts { } else { var internalAliasSymbol = typeResolver.getSymbolInfo(importDeclaration.entityName); - addFullSymbolName(internalAliasSymbol, enclosingDeclaration); + if (internalAliasSymbol) { + displayParts.push(spacePart()); + displayParts.push(punctuationPart(SyntaxKind.EqualsToken)); + displayParts.push(spacePart()); + addFullSymbolName(internalAliasSymbol, enclosingDeclaration); + } } return true; } diff --git a/src/services/shims.ts b/src/services/shims.ts index 0de05edb78d..8c4da452b73 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -517,17 +517,6 @@ module ts { }; } - private realizeDiagnosticWithFileName(diagnostic: Diagnostic): { fileName: string; message: string; start: number; length: number; category: string; } { - return { - fileName: diagnostic.file.filename, - message: diagnostic.messageText, - start: diagnostic.start, - length: diagnostic.length, - /// TODO: no need for the tolowerCase call - category: DiagnosticCategory[diagnostic.category].toLowerCase() - }; - } - public getSyntacticClassifications(fileName: string, start: number, length: number): string { return this.forwardJSONCall( "getSyntacticClassifications('" + fileName + "', " + start + ", " + length + ")", @@ -569,7 +558,7 @@ module ts { "getCompilerOptionsDiagnostics()", () => { var errors = this.languageService.getCompilerOptionsDiagnostics(); - return errors.map(d => this.realizeDiagnosticWithFileName(d)) + return errors.map(LanguageServiceShimObject.realizeDiagnostic) }); } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index c1a7ab89789..67ed16e7c85 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -224,7 +224,10 @@ module ts { return nodeHasTokens((n).expression); } - if (n.kind === SyntaxKind.EndOfFileToken || n.kind === SyntaxKind.OmittedExpression || n.kind === SyntaxKind.Missing) { + if (n.kind === SyntaxKind.EndOfFileToken || + n.kind === SyntaxKind.OmittedExpression || + n.kind === SyntaxKind.Missing || + n.kind === SyntaxKind.Unknown) { return false; } diff --git a/tests/cases/fourslash/commentsLinePreservation.ts b/tests/cases/fourslash/commentsLinePreservation.ts new file mode 100644 index 00000000000..012272d7544 --- /dev/null +++ b/tests/cases/fourslash/commentsLinePreservation.ts @@ -0,0 +1,161 @@ +/// + +/////** This is firstLine +//// * This is second Line +//// * +//// * This is fourth Line +//// */ +////var /*a*/a: string; +/////** +//// * This is firstLine +//// * This is second Line +//// * +//// * This is fourth Line +//// */ +////var /*b*/b: string; +/////** +//// * This is firstLine +//// * This is second Line +//// * +//// * This is fourth Line +//// * +//// */ +////var /*c*/c: string; +/////** +//// * This is firstLine +//// * This is second Line +//// * @param param +//// * @random tag This should be third line +//// */ +////function /*d*/d(param: string) { /*1*/param = "hello"; } +/////** +//// * This is firstLine +//// * This is second Line +//// * @param param +//// */ +////function /*e*/e(param: string) { /*2*/param = "hello"; } +/////** +//// * This is firstLine +//// * This is second Line +//// * @param param1 first line of param +//// * +//// * param information third line +//// * @random tag This should be third line +//// */ +////function /*f*/f(param1: string) { /*3*/param1 = "hello"; } +/////** +//// * This is firstLine +//// * This is second Line +//// * @param param1 +//// * +//// * param information first line +//// * @random tag This should be third line +//// */ +////function /*g*/g(param1: string) { /*4*/param1 = "hello"; } +/////** +//// * This is firstLine +//// * This is second Line +//// * @param param1 +//// * +//// * param information first line +//// * +//// * param information third line +//// * @random tag This should be third line +//// */ +////function /*h*/h(param1: string) { /*5*/param1 = "hello"; } +/////** +//// * This is firstLine +//// * This is second Line +//// * @param param1 +//// * +//// * param information first line +//// * +//// * param information third line +//// * +//// */ +////function /*i*/i(param1: string) { /*6*/param1 = "hello"; } +/////** +//// * This is firstLine +//// * This is second Line +//// * @param param1 +//// * +//// * param information first line +//// * +//// * param information third line +//// */ +////function /*j*/j(param1: string) { /*7*/param1 = "hello"; } +/////** +//// * This is firstLine +//// * This is second Line +//// * @param param1 hello @randomtag +//// * +//// * random information first line +//// * +//// * random information third line +//// */ +////function /*k*/k(param1: string) { /*8*/param1 = "hello"; } +/////** +//// * This is firstLine +//// * This is second Line +//// * @param param1 first Line text +//// * +//// * @param param1 +//// * +//// * blank line that shouldnt be shown when starting this +//// * second time information about the param again +//// */ +////function /*l*/l(param1: string) { /*9*/param1 = "hello"; } + +goTo.marker('a'); +verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line\n\nThis is fourth Line"); + +goTo.marker('b'); +verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line\n\nThis is fourth Line"); + +goTo.marker('c'); +verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line\n\nThis is fourth Line"); + +goTo.marker('d'); +verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line\n@random tag This should be third line"); +goTo.marker('1'); +verify.quickInfoIs(undefined, ""); + +goTo.marker('e'); +verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line"); +goTo.marker('2'); +verify.quickInfoIs(undefined, ""); + +goTo.marker('f'); +verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line\n@random tag This should be third line"); +goTo.marker('3'); +verify.quickInfoIs(undefined, "first line of param\n\nparam information third line"); + +goTo.marker('g'); +verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line\n@random tag This should be third line"); +goTo.marker('4'); +verify.quickInfoIs(undefined, "param information first line"); + +goTo.marker('h'); +verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line\n@random tag This should be third line"); +goTo.marker('5'); +verify.quickInfoIs(undefined, "param information first line\n\nparam information third line"); + +goTo.marker('i'); +verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line"); +goTo.marker('6'); +verify.quickInfoIs(undefined, "param information first line\n\nparam information third line"); + +goTo.marker('j'); +verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line"); +goTo.marker('7'); +verify.quickInfoIs(undefined, "param information first line\n\nparam information third line"); + +goTo.marker('k'); +verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line\n@randomtag \n\n random information first line\n\n random information third line"); +goTo.marker('8'); +verify.quickInfoIs(undefined, "hello "); + +goTo.marker('l'); +verify.quickInfoIs(undefined, "This is firstLine\nThis is second Line"); +goTo.marker('9'); +verify.quickInfoIs(undefined, "first Line text\nblank line that shouldnt be shown when starting this \nsecond time information about the param again"); diff --git a/tests/cases/fourslash/completionAfterAtChar.ts b/tests/cases/fourslash/completionAfterAtChar.ts new file mode 100644 index 00000000000..4f3581cd483 --- /dev/null +++ b/tests/cases/fourslash/completionAfterAtChar.ts @@ -0,0 +1,6 @@ +/// + +////@a/**/ + +goTo.marker(); +verify.not.completionListIsEmpty(); \ No newline at end of file diff --git a/tests/cases/fourslash/completionEntryForImportName.ts b/tests/cases/fourslash/completionEntryForImportName.ts new file mode 100644 index 00000000000..5773eb5376e --- /dev/null +++ b/tests/cases/fourslash/completionEntryForImportName.ts @@ -0,0 +1,24 @@ +/// + +////import /*1*/ /*2*/ + +goTo.marker('1'); +verify.completionListIsEmpty(); +edit.insert('q'); +verify.completionListIsEmpty(); +verifyIncompleteImportName(); + +goTo.marker('2'); +edit.insert(" = "); +verifyIncompleteImportName(); + +goTo.marker("2"); +edit.moveRight(" = ".length); +edit.insert("a."); +verifyIncompleteImportName(); + +function verifyIncompleteImportName() { + goTo.marker('1'); + verify.completionListIsEmpty(); + verify.quickInfoIs("import q"); +} \ No newline at end of file diff --git a/tests/cases/fourslash/completionEntryForPropertyFromUnionOfModuleType.ts b/tests/cases/fourslash/completionEntryForPropertyFromUnionOfModuleType.ts new file mode 100644 index 00000000000..1e072acd1fa --- /dev/null +++ b/tests/cases/fourslash/completionEntryForPropertyFromUnionOfModuleType.ts @@ -0,0 +1,13 @@ +/// + +////module E { +//// export var n = 1; +////} +////module F { +//// export var n = 1; +////} +////var q: typeof E | typeof F; +////var j = q./*1*/ + +goTo.marker('1'); +verify.completionListContains('n', "(property) n: number"); \ No newline at end of file