TypeScript/src/services/jsDoc.ts

474 lines
20 KiB
TypeScript
Raw Normal View History

/* @internal */
namespace ts.JsDoc {
const jsDocTagNames = [
2018-06-27 00:46:24 +02:00
"abstract",
"access",
"alias",
"argument",
"async",
"augments",
"author",
"borrows",
Add callback tag, with type parameters (#23947) * Add initial tests * Add types * Half of parsing (builds but does not pass tests) * Parsing done; types are uglier; doesn't crash but doesn't pass * Bind callback tag Builds but tests still don't pass * Only bind param tags inside callback tags * Fix binding switch to only handle param tags once * Checking is 1/3 done or so. Now I'm going to go rename some members to be more uniform. I hate unnnecessary conditionals. * Rename typeExpression to type (for some jsdoc) (maybe I'll rename more later) * Rename the rest of typeExpressions Turns out there is a constraint in services such that they all need to be named the same. * Few more checker changes * Revert "Rename the rest of typeExpressions" This reverts commit f41a96b24d44a6b696d39eee9e91ef7f606bea52. * Revert "Rename typeExpression to type (for some jsdoc)" This reverts commit 7d2233a00e5c6d794c1de32c03802e8ccce1914c. * Finish undoing typeExpression rename * Rename and improve getTypeParametersForAliasSymbol Plus some other small fixes * Core checking works, but is flabbergastingly messy I'm serious. * Callback return types work now * Fix crash in services * Make github diff smaller * Try to make github diff even smaller * Fix rename for callback tag * Fix nav bar for callback tag Also clean up some now-redundant code there to find the name of typedefs. * Handle ooorder callback tags Also get rid of redundant typedef name code *in the binder*. It's everywhere! * Add ooorder callback tag test * Parse comments for typedef/callback+display param comments * Always export callbacks This requires almost no new code since it is basically the same as typedefs * Update baselines * Fix support for nested namespaced callbacks And add test * Callbacks support type parameters 1. Haven't run it with all tests 2. Haven't tested typedef tags yet 3. Still allows shared symbols when on function or class declarations. * Template tags are now bound correctly * Test oorder template tags It works. * Parser cleanup * Cleanup types and utilities As much as possible, and not as much as I would like. * Handle callback more often in services * Cleanup of binder and checker * More checker cleanup * Remove TODOs and one more cleanup * Support parameter-less callback tags * Remove extra bind call on template type parameters * Bind template tag containers Doesn't quite work with typedefs, but that's because it's now stricter, without the typedef fixes. I'm going to merge with jsdoc/callback and see how it goes. * Fix fourslash failures * Stop pre-binding js type aliases Next up, stop pre-binding js type parameters * Further cleanup of delayed js type alias binding * Stop prebinding template tags too This gets rid of prebinding entirely * Remove TODO * Fix lint * Finish merge with use-jsdoc-aliases * Update callback tag baselines * Rename getTypeParametersForAliasSymbol The real fix is *probably* to rename Type.aliasTypeArguments to aliasTypeParameters, but I want to make sure and then put it in a separate PR.
2018-05-17 18:28:11 +02:00
"callback",
"class",
2018-07-14 01:02:20 +02:00
"classdesc",
"constant",
"constructor",
"constructs",
2018-06-27 00:46:24 +02:00
"copyright",
"default",
"deprecated",
"description",
2018-06-27 00:46:24 +02:00
"emits",
"enum",
"event",
"example",
2018-06-27 00:46:24 +02:00
"exports",
"extends",
2018-06-27 00:46:24 +02:00
"external",
"field",
2018-06-27 00:46:24 +02:00
"file",
2018-07-14 01:02:20 +02:00
"fileoverview",
2018-06-27 00:46:24 +02:00
"fires",
"function",
2018-06-27 00:46:24 +02:00
"generator",
"global",
2018-07-14 01:02:20 +02:00
"hideconstructor",
2018-06-27 00:46:24 +02:00
"host",
"ignore",
2018-06-27 00:46:24 +02:00
"implements",
2018-07-14 01:02:20 +02:00
"inheritdoc",
"inner",
2018-06-27 00:46:24 +02:00
"instance",
"interface",
"kind",
"lends",
2018-06-27 00:46:24 +02:00
"license",
"link",
2018-06-27 00:46:24 +02:00
"listens",
"member",
2018-07-14 01:02:20 +02:00
"memberof",
"method",
2018-06-27 00:46:24 +02:00
"mixes",
"module",
"name",
"namespace",
2018-06-27 00:46:24 +02:00
"override",
"package",
"param",
"private",
"property",
2018-06-27 00:46:24 +02:00
"protected",
"public",
2018-06-27 00:46:24 +02:00
"readonly",
"requires",
"returns",
"see",
"since",
"static",
2018-06-27 00:46:24 +02:00
"summary",
"template",
2018-06-27 00:46:24 +02:00
"this",
"throws",
2018-06-27 00:46:24 +02:00
"todo",
"tutorial",
"type",
"typedef",
2018-06-27 00:46:24 +02:00
"var",
"variation",
"version",
"virtual",
"yields"
];
let jsDocTagNameCompletionEntries: CompletionEntry[];
2017-03-02 02:46:35 +01:00
let jsDocTagCompletionEntries: CompletionEntry[];
export function getJsDocCommentsFromDeclarations(declarations: readonly Declaration[], checker?: TypeChecker): SymbolDisplayPart[] {
// Only collect doc comments from duplicate declarations once:
// In case of a union property there might be same declaration multiple times
// which only varies in type parameter
// Eg. const a: Array<string> | Array<number>; a.length
// The property length will have two declarations of property length coming
// from Array<T> - Array<string> and Array<number>
const parts: SymbolDisplayPart[][] = [];
forEachUnique(declarations, declaration => {
for (const jsdoc of getCommentHavingNodes(declaration)) {
// skip comments containing @typedefs since they're not associated with particular declarations
// Exceptions:
// - @typedefs are themselves declarations with associated comments
// - @param or @return indicate that the author thinks of it as a 'local' @typedef that's part of the function documentation
if (jsdoc.comment === undefined
|| isJSDoc(jsdoc)
&& declaration.kind !== SyntaxKind.JSDocTypedefTag && declaration.kind !== SyntaxKind.JSDocCallbackTag
&& jsdoc.tags
&& jsdoc.tags.some(t => t.kind === SyntaxKind.JSDocTypedefTag || t.kind === SyntaxKind.JSDocCallbackTag)
&& !jsdoc.tags.some(t => t.kind === SyntaxKind.JSDocParameterTag || t.kind === SyntaxKind.JSDocReturnTag)) {
continue;
}
const newparts = getDisplayPartsFromComment(jsdoc.comment, checker);
if (!contains(parts, newparts, isIdenticalListOfDisplayParts)) {
parts.push(newparts);
}
}
});
return flatten(intersperse(parts, [lineBreakPart()]));
}
function isIdenticalListOfDisplayParts(parts1: SymbolDisplayPart[], parts2: SymbolDisplayPart[]) {
return arraysEqual(parts1, parts2, (p1, p2) => p1.kind === p2.kind && p1.text === p2.text);
}
function getCommentHavingNodes(declaration: Declaration): readonly (JSDoc | JSDocTag)[] {
switch (declaration.kind) {
Add callback tag, with type parameters (#23947) * Add initial tests * Add types * Half of parsing (builds but does not pass tests) * Parsing done; types are uglier; doesn't crash but doesn't pass * Bind callback tag Builds but tests still don't pass * Only bind param tags inside callback tags * Fix binding switch to only handle param tags once * Checking is 1/3 done or so. Now I'm going to go rename some members to be more uniform. I hate unnnecessary conditionals. * Rename typeExpression to type (for some jsdoc) (maybe I'll rename more later) * Rename the rest of typeExpressions Turns out there is a constraint in services such that they all need to be named the same. * Few more checker changes * Revert "Rename the rest of typeExpressions" This reverts commit f41a96b24d44a6b696d39eee9e91ef7f606bea52. * Revert "Rename typeExpression to type (for some jsdoc)" This reverts commit 7d2233a00e5c6d794c1de32c03802e8ccce1914c. * Finish undoing typeExpression rename * Rename and improve getTypeParametersForAliasSymbol Plus some other small fixes * Core checking works, but is flabbergastingly messy I'm serious. * Callback return types work now * Fix crash in services * Make github diff smaller * Try to make github diff even smaller * Fix rename for callback tag * Fix nav bar for callback tag Also clean up some now-redundant code there to find the name of typedefs. * Handle ooorder callback tags Also get rid of redundant typedef name code *in the binder*. It's everywhere! * Add ooorder callback tag test * Parse comments for typedef/callback+display param comments * Always export callbacks This requires almost no new code since it is basically the same as typedefs * Update baselines * Fix support for nested namespaced callbacks And add test * Callbacks support type parameters 1. Haven't run it with all tests 2. Haven't tested typedef tags yet 3. Still allows shared symbols when on function or class declarations. * Template tags are now bound correctly * Test oorder template tags It works. * Parser cleanup * Cleanup types and utilities As much as possible, and not as much as I would like. * Handle callback more often in services * Cleanup of binder and checker * More checker cleanup * Remove TODOs and one more cleanup * Support parameter-less callback tags * Remove extra bind call on template type parameters * Bind template tag containers Doesn't quite work with typedefs, but that's because it's now stricter, without the typedef fixes. I'm going to merge with jsdoc/callback and see how it goes. * Fix fourslash failures * Stop pre-binding js type aliases Next up, stop pre-binding js type parameters * Further cleanup of delayed js type alias binding * Stop prebinding template tags too This gets rid of prebinding entirely * Remove TODO * Fix lint * Finish merge with use-jsdoc-aliases * Update callback tag baselines * Rename getTypeParametersForAliasSymbol The real fix is *probably* to rename Type.aliasTypeArguments to aliasTypeParameters, but I want to make sure and then put it in a separate PR.
2018-05-17 18:28:11 +02:00
case SyntaxKind.JSDocParameterTag:
case SyntaxKind.JSDocPropertyTag:
return [declaration as JSDocPropertyTag];
Add callback tag, with type parameters (#23947) * Add initial tests * Add types * Half of parsing (builds but does not pass tests) * Parsing done; types are uglier; doesn't crash but doesn't pass * Bind callback tag Builds but tests still don't pass * Only bind param tags inside callback tags * Fix binding switch to only handle param tags once * Checking is 1/3 done or so. Now I'm going to go rename some members to be more uniform. I hate unnnecessary conditionals. * Rename typeExpression to type (for some jsdoc) (maybe I'll rename more later) * Rename the rest of typeExpressions Turns out there is a constraint in services such that they all need to be named the same. * Few more checker changes * Revert "Rename the rest of typeExpressions" This reverts commit f41a96b24d44a6b696d39eee9e91ef7f606bea52. * Revert "Rename typeExpression to type (for some jsdoc)" This reverts commit 7d2233a00e5c6d794c1de32c03802e8ccce1914c. * Finish undoing typeExpression rename * Rename and improve getTypeParametersForAliasSymbol Plus some other small fixes * Core checking works, but is flabbergastingly messy I'm serious. * Callback return types work now * Fix crash in services * Make github diff smaller * Try to make github diff even smaller * Fix rename for callback tag * Fix nav bar for callback tag Also clean up some now-redundant code there to find the name of typedefs. * Handle ooorder callback tags Also get rid of redundant typedef name code *in the binder*. It's everywhere! * Add ooorder callback tag test * Parse comments for typedef/callback+display param comments * Always export callbacks This requires almost no new code since it is basically the same as typedefs * Update baselines * Fix support for nested namespaced callbacks And add test * Callbacks support type parameters 1. Haven't run it with all tests 2. Haven't tested typedef tags yet 3. Still allows shared symbols when on function or class declarations. * Template tags are now bound correctly * Test oorder template tags It works. * Parser cleanup * Cleanup types and utilities As much as possible, and not as much as I would like. * Handle callback more often in services * Cleanup of binder and checker * More checker cleanup * Remove TODOs and one more cleanup * Support parameter-less callback tags * Remove extra bind call on template type parameters * Bind template tag containers Doesn't quite work with typedefs, but that's because it's now stricter, without the typedef fixes. I'm going to merge with jsdoc/callback and see how it goes. * Fix fourslash failures * Stop pre-binding js type aliases Next up, stop pre-binding js type parameters * Further cleanup of delayed js type alias binding * Stop prebinding template tags too This gets rid of prebinding entirely * Remove TODO * Fix lint * Finish merge with use-jsdoc-aliases * Update callback tag baselines * Rename getTypeParametersForAliasSymbol The real fix is *probably* to rename Type.aliasTypeArguments to aliasTypeParameters, but I want to make sure and then put it in a separate PR.
2018-05-17 18:28:11 +02:00
case SyntaxKind.JSDocCallbackTag:
case SyntaxKind.JSDocTypedefTag:
Add callback tag, with type parameters (#23947) * Add initial tests * Add types * Half of parsing (builds but does not pass tests) * Parsing done; types are uglier; doesn't crash but doesn't pass * Bind callback tag Builds but tests still don't pass * Only bind param tags inside callback tags * Fix binding switch to only handle param tags once * Checking is 1/3 done or so. Now I'm going to go rename some members to be more uniform. I hate unnnecessary conditionals. * Rename typeExpression to type (for some jsdoc) (maybe I'll rename more later) * Rename the rest of typeExpressions Turns out there is a constraint in services such that they all need to be named the same. * Few more checker changes * Revert "Rename the rest of typeExpressions" This reverts commit f41a96b24d44a6b696d39eee9e91ef7f606bea52. * Revert "Rename typeExpression to type (for some jsdoc)" This reverts commit 7d2233a00e5c6d794c1de32c03802e8ccce1914c. * Finish undoing typeExpression rename * Rename and improve getTypeParametersForAliasSymbol Plus some other small fixes * Core checking works, but is flabbergastingly messy I'm serious. * Callback return types work now * Fix crash in services * Make github diff smaller * Try to make github diff even smaller * Fix rename for callback tag * Fix nav bar for callback tag Also clean up some now-redundant code there to find the name of typedefs. * Handle ooorder callback tags Also get rid of redundant typedef name code *in the binder*. It's everywhere! * Add ooorder callback tag test * Parse comments for typedef/callback+display param comments * Always export callbacks This requires almost no new code since it is basically the same as typedefs * Update baselines * Fix support for nested namespaced callbacks And add test * Callbacks support type parameters 1. Haven't run it with all tests 2. Haven't tested typedef tags yet 3. Still allows shared symbols when on function or class declarations. * Template tags are now bound correctly * Test oorder template tags It works. * Parser cleanup * Cleanup types and utilities As much as possible, and not as much as I would like. * Handle callback more often in services * Cleanup of binder and checker * More checker cleanup * Remove TODOs and one more cleanup * Support parameter-less callback tags * Remove extra bind call on template type parameters * Bind template tag containers Doesn't quite work with typedefs, but that's because it's now stricter, without the typedef fixes. I'm going to merge with jsdoc/callback and see how it goes. * Fix fourslash failures * Stop pre-binding js type aliases Next up, stop pre-binding js type parameters * Further cleanup of delayed js type alias binding * Stop prebinding template tags too This gets rid of prebinding entirely * Remove TODO * Fix lint * Finish merge with use-jsdoc-aliases * Update callback tag baselines * Rename getTypeParametersForAliasSymbol The real fix is *probably* to rename Type.aliasTypeArguments to aliasTypeParameters, but I want to make sure and then put it in a separate PR.
2018-05-17 18:28:11 +02:00
return [(declaration as JSDocTypedefTag), (declaration as JSDocTypedefTag).parent];
default:
return getJSDocCommentsAndTags(declaration);
}
}
export function getJsDocTagsFromDeclarations(declarations?: Declaration[], checker?: TypeChecker): JSDocTagInfo[] {
// Only collect doc comments from duplicate declarations once.
const infos: JSDocTagInfo[] = [];
forEachUnique(declarations, declaration => {
const tags = getJSDocTags(declaration);
// skip comments containing @typedefs since they're not associated with particular declarations
// Exceptions:
// - @param or @return indicate that the author thinks of it as a 'local' @typedef that's part of the function documentation
if (tags.some(t => t.kind === SyntaxKind.JSDocTypedefTag || t.kind === SyntaxKind.JSDocCallbackTag)
&& !tags.some(t => t.kind === SyntaxKind.JSDocParameterTag || t.kind === SyntaxKind.JSDocReturnTag)) {
return;
}
for (const tag of tags) {
infos.push({ name: tag.tagName.text, text: getCommentDisplayParts(tag, checker) });
}
});
return infos;
}
function getDisplayPartsFromComment(comment: string | readonly JSDocComment[], checker: TypeChecker | undefined): SymbolDisplayPart[] {
if (typeof comment === "string") {
return [textPart(comment)];
}
return flatMap(
comment,
node => node.kind === SyntaxKind.JSDocText ? [textPart(node.text)] : buildLinkParts(node, checker)
) as SymbolDisplayPart[];
}
function getCommentDisplayParts(tag: JSDocTag, checker?: TypeChecker): SymbolDisplayPart[] | undefined {
const { comment, kind } = tag;
const namePart = getTagNameDisplayPart(kind);
switch (kind) {
case SyntaxKind.JSDocImplementsTag:
return withNode((tag as JSDocImplementsTag).class);
case SyntaxKind.JSDocAugmentsTag:
return withNode((tag as JSDocAugmentsTag).class);
case SyntaxKind.JSDocTemplateTag:
return addComment((tag as JSDocTemplateTag).typeParameters.map(tp => tp.getText()).join(", "));
case SyntaxKind.JSDocTypeTag:
return withNode((tag as JSDocTypeTag).typeExpression);
case SyntaxKind.JSDocTypedefTag:
Add callback tag, with type parameters (#23947) * Add initial tests * Add types * Half of parsing (builds but does not pass tests) * Parsing done; types are uglier; doesn't crash but doesn't pass * Bind callback tag Builds but tests still don't pass * Only bind param tags inside callback tags * Fix binding switch to only handle param tags once * Checking is 1/3 done or so. Now I'm going to go rename some members to be more uniform. I hate unnnecessary conditionals. * Rename typeExpression to type (for some jsdoc) (maybe I'll rename more later) * Rename the rest of typeExpressions Turns out there is a constraint in services such that they all need to be named the same. * Few more checker changes * Revert "Rename the rest of typeExpressions" This reverts commit f41a96b24d44a6b696d39eee9e91ef7f606bea52. * Revert "Rename typeExpression to type (for some jsdoc)" This reverts commit 7d2233a00e5c6d794c1de32c03802e8ccce1914c. * Finish undoing typeExpression rename * Rename and improve getTypeParametersForAliasSymbol Plus some other small fixes * Core checking works, but is flabbergastingly messy I'm serious. * Callback return types work now * Fix crash in services * Make github diff smaller * Try to make github diff even smaller * Fix rename for callback tag * Fix nav bar for callback tag Also clean up some now-redundant code there to find the name of typedefs. * Handle ooorder callback tags Also get rid of redundant typedef name code *in the binder*. It's everywhere! * Add ooorder callback tag test * Parse comments for typedef/callback+display param comments * Always export callbacks This requires almost no new code since it is basically the same as typedefs * Update baselines * Fix support for nested namespaced callbacks And add test * Callbacks support type parameters 1. Haven't run it with all tests 2. Haven't tested typedef tags yet 3. Still allows shared symbols when on function or class declarations. * Template tags are now bound correctly * Test oorder template tags It works. * Parser cleanup * Cleanup types and utilities As much as possible, and not as much as I would like. * Handle callback more often in services * Cleanup of binder and checker * More checker cleanup * Remove TODOs and one more cleanup * Support parameter-less callback tags * Remove extra bind call on template type parameters * Bind template tag containers Doesn't quite work with typedefs, but that's because it's now stricter, without the typedef fixes. I'm going to merge with jsdoc/callback and see how it goes. * Fix fourslash failures * Stop pre-binding js type aliases Next up, stop pre-binding js type parameters * Further cleanup of delayed js type alias binding * Stop prebinding template tags too This gets rid of prebinding entirely * Remove TODO * Fix lint * Finish merge with use-jsdoc-aliases * Update callback tag baselines * Rename getTypeParametersForAliasSymbol The real fix is *probably* to rename Type.aliasTypeArguments to aliasTypeParameters, but I want to make sure and then put it in a separate PR.
2018-05-17 18:28:11 +02:00
case SyntaxKind.JSDocCallbackTag:
case SyntaxKind.JSDocPropertyTag:
case SyntaxKind.JSDocParameterTag:
case SyntaxKind.JSDocSeeTag:
const { name } = tag as JSDocTypedefTag | JSDocCallbackTag | JSDocPropertyTag | JSDocParameterTag | JSDocSeeTag;
return name ? withNode(name)
: comment === undefined ? undefined
: getDisplayPartsFromComment(comment, checker);
default:
return comment === undefined ? undefined : getDisplayPartsFromComment(comment, checker);
}
function withNode(node: Node) {
return addComment(node.getText());
}
function addComment(s: string) {
if (comment) {
if (s.match(/^https?$/)) {
return [textPart(s), ...getDisplayPartsFromComment(comment, checker)];
}
else {
return [namePart(s), spacePart(), ...getDisplayPartsFromComment(comment, checker)];
}
}
else {
return [textPart(s)];
}
}
}
function getTagNameDisplayPart(kind: SyntaxKind): (text: string) => SymbolDisplayPart {
switch (kind) {
case SyntaxKind.JSDocParameterTag:
return parameterNamePart;
case SyntaxKind.JSDocPropertyTag:
return propertyNamePart;
case SyntaxKind.JSDocTemplateTag:
return typeParameterNamePart;
case SyntaxKind.JSDocTypedefTag:
case SyntaxKind.JSDocCallbackTag:
return typeAliasNamePart;
default:
return textPart;
}
}
2017-03-02 02:46:35 +01:00
export function getJSDocTagNameCompletions(): CompletionEntry[] {
return jsDocTagNameCompletionEntries || (jsDocTagNameCompletionEntries = map(jsDocTagNames, tagName => {
return {
name: tagName,
kind: ScriptElementKind.keyword,
kindModifiers: "",
sortText: Completions.SortText.LocationPriority,
};
}));
}
export const getJSDocTagNameCompletionDetails = getJSDocTagCompletionDetails;
2017-03-02 02:46:35 +01:00
export function getJSDocTagCompletions(): CompletionEntry[] {
return jsDocTagCompletionEntries || (jsDocTagCompletionEntries = map(jsDocTagNames, tagName => {
2017-03-02 02:46:35 +01:00
return {
name: `@${tagName}`,
kind: ScriptElementKind.keyword,
kindModifiers: "",
sortText: Completions.SortText.LocationPriority
2017-03-03 16:00:52 +01:00
};
2017-03-02 02:46:35 +01:00
}));
}
export function getJSDocTagCompletionDetails(name: string): CompletionEntryDetails {
return {
name,
kind: ScriptElementKind.unknown, // TODO: should have its own kind?
kindModifiers: "",
displayParts: [textPart(name)],
documentation: emptyArray,
tags: undefined,
codeActions: undefined,
};
}
export function getJSDocParameterNameCompletions(tag: JSDocParameterTag): CompletionEntry[] {
2017-07-26 19:57:29 +02:00
if (!isIdentifier(tag.name)) {
return emptyArray;
}
const nameThusFar = tag.name.text;
const jsdoc = tag.parent;
const fn = jsdoc.parent;
if (!isFunctionLike(fn)) return [];
return mapDefined(fn.parameters, param => {
if (!isIdentifier(param.name)) return undefined;
const name = param.name.text;
Enable '--strictNullChecks' (#22088) * Enable '--strictNullChecks' * Fix API baselines * Make sys.getEnvironmentVariable non-nullable * make properties optional instead of using `| undefined` in thier type * reportDiagnostics should be required * Declare firstAccessor as non-nullable * Make `some` a type guard * Fix `getEnvironmentVariable` definition in tests * Pretend transformFlags are always defined * Fix one more use of sys.getEnvironmentVariable * `requiredResponse` accepts undefined, remove assertions * Mark optional properties as optional instead of using `| undefined` * Mark optional properties as optional instead of using ` | undefined` * Remove unnecessary null assertions * Put the bang on the declaration instead of every use * Make `createMapFromTemplate` require a parameter * Mark `EmitResult.emittedFiles` and `EmitResult.sourceMaps` as optional * Plumb through undefined in emitLsit and EmitExpressionList * `ElementAccessExpression.argumentExpression` can not be `undefined` * Add overloads for `writeTokenText` * Make `shouldWriteSeparatingLineTerminator` argument non-nullable * Make `synthesizedNodeStartsOnNewLine` argument required * `PropertyAssignment.initializer` cannot be undefined * Use one `!` at declaration site instead of on every use site * Capture host in a constant and avoid null assertions * Remove few more unused assertions * Update baselines * Use parameter defaults * Update baselines * Fix lint * Make Symbol#valueDeclaration and Symbol#declarations non-optional to reduce assertions * Make Node#symbol and Type#symbol non-optional to reduce assertions * Make `flags` non-nullable to reduce assertions * Convert some asserts to type guards * Make `isNonLocalAlias` a type guard * Add overload for `getSymbolOfNode` for `Declaration` * Some more `getSymbolOfNode` changes * Push undefined suppression into `typeToTypeNodeHelper` * `NodeBuilderContext.tracker` is never `undefined` * use `Debug.assertDefined` * Remove unnecessary tag * Mark `LiteralType.freshType` and `LiteralTupe.regularType` as required
2018-05-22 23:46:57 +02:00
if (jsdoc.tags!.some(t => t !== tag && isJSDocParameterTag(t) && isIdentifier(t.name) && t.name.escapedText === name) // TODO: GH#18217
|| nameThusFar !== undefined && !startsWith(name, nameThusFar)) {
return undefined;
}
return { name, kind: ScriptElementKind.parameterElement, kindModifiers: "", sortText: Completions.SortText.LocationPriority };
});
}
export function getJSDocParameterNameCompletionDetails(name: string): CompletionEntryDetails {
return {
name,
kind: ScriptElementKind.parameterElement,
kindModifiers: "",
displayParts: [textPart(name)],
documentation: emptyArray,
tags: undefined,
codeActions: undefined,
};
}
/**
* Checks if position points to a valid position to add JSDoc comments, and if so,
* returns the appropriate template. Otherwise returns an empty string.
* Valid positions are
* - outside of comments, statements, and expressions, and
* - preceding a:
* - function/constructor/method declaration
* - class declarations
* - variable statements
* - namespace declarations
* - interface declarations
* - method signatures
* - type alias declarations
*
* Hosts should ideally check that:
* - The line is all whitespace up to 'position' before performing the insertion.
* - If the keystroke sequence "/\*\*" induced the call, we also check that the next
* non-whitespace character is '*', which (approximately) indicates whether we added
* the second '*' to complete an existing (JSDoc) comment.
* @param fileName The file in which to perform the check.
* @param position The (character-indexed) position in the file where the check should
* be performed.
*/
export function getDocCommentTemplateAtPosition(newLine: string, sourceFile: SourceFile, position: number, options?: DocCommentTemplateOptions): TextInsertion | undefined {
const tokenAtPos = getTokenAtPosition(sourceFile, position);
const existingDocComment = findAncestor(tokenAtPos, isJSDoc);
if (existingDocComment && (existingDocComment.comment !== undefined || length(existingDocComment.tags))) {
// Non-empty comment already exists.
return undefined;
}
const tokenStart = tokenAtPos.getStart(sourceFile);
// Don't provide a doc comment template based on a *previous* node. (But an existing empty jsdoc comment will likely start before `position`.)
if (!existingDocComment && tokenStart < position) {
return undefined;
}
const commentOwnerInfo = getCommentOwnerInfo(tokenAtPos, options);
if (!commentOwnerInfo) {
return undefined;
}
const { commentOwner, parameters, hasReturn } = commentOwnerInfo;
if (commentOwner.getStart(sourceFile) < position) {
return undefined;
}
const indentationStr = getIndentationStringAtPosition(sourceFile, position);
const isJavaScriptFile = hasJSFileExtension(sourceFile.fileName);
const tags =
(parameters ? parameterDocComments(parameters || [], isJavaScriptFile, indentationStr, newLine) : "") +
(hasReturn ? returnsDocComment(indentationStr, newLine) : "");
// A doc comment consists of the following
// * The opening comment line
// * the first line (without a param) for the object's untagged info (this is also where the caret ends up)
// * the '@param'-tagged lines
// * the '@returns'-tag
// * TODO: other tags.
// * the closing comment line
// * if the caret was directly in front of the object, then we add an extra line and indentation.
const openComment = "/**";
const closeComment = " */";
if (tags) {
const preamble = openComment + newLine + indentationStr + " * ";
const endLine = tokenStart === position ? newLine + indentationStr : "";
const result = preamble + newLine + tags + indentationStr + closeComment + endLine;
return { newText: result, caretOffset: preamble.length };
}
return { newText: openComment + closeComment, caretOffset: 3 };
}
function getIndentationStringAtPosition(sourceFile: SourceFile, position: number): string {
const { text } = sourceFile;
const lineStart = getLineStartPositionForPosition(position, sourceFile);
let pos = lineStart;
for (; pos <= position && isWhiteSpaceSingleLine(text.charCodeAt(pos)); pos++);
return text.slice(lineStart, pos);
}
function parameterDocComments(parameters: readonly ParameterDeclaration[], isJavaScriptFile: boolean, indentationStr: string, newLine: string): string {
return parameters.map(({ name, dotDotDotToken }, i) => {
const paramName = name.kind === SyntaxKind.Identifier ? name.text : "param" + i;
const type = isJavaScriptFile ? (dotDotDotToken ? "{...any} " : "{any} ") : "";
return `${indentationStr} * @param ${type}${paramName}${newLine}`;
}).join("");
}
function returnsDocComment(indentationStr: string, newLine: string) {
return `${indentationStr} * @returns${newLine}`;
}
interface CommentOwnerInfo {
readonly commentOwner: Node;
readonly parameters?: readonly ParameterDeclaration[];
readonly hasReturn?: boolean;
}
function getCommentOwnerInfo(tokenAtPos: Node, options: DocCommentTemplateOptions | undefined): CommentOwnerInfo | undefined {
return forEachAncestor(tokenAtPos, n => getCommentOwnerInfoWorker(n, options));
}
function getCommentOwnerInfoWorker(commentOwner: Node, options: DocCommentTemplateOptions | undefined): CommentOwnerInfo | undefined | "quit" {
switch (commentOwner.kind) {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.Constructor:
case SyntaxKind.MethodSignature:
case SyntaxKind.ArrowFunction:
const host = commentOwner as ArrowFunction | FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature;
return { commentOwner, parameters: host.parameters, hasReturn: hasReturn(host, options) };
case SyntaxKind.PropertyAssignment:
return getCommentOwnerInfoWorker((commentOwner as PropertyAssignment).initializer, options);
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.PropertySignature:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.EnumMember:
case SyntaxKind.TypeAliasDeclaration:
return { commentOwner };
case SyntaxKind.VariableStatement: {
const varStatement = commentOwner as VariableStatement;
const varDeclarations = varStatement.declarationList.declarations;
const host = varDeclarations.length === 1 && varDeclarations[0].initializer
? getRightHandSideOfAssignment(varDeclarations[0].initializer)
: undefined;
return host
? { commentOwner, parameters: host.parameters, hasReturn: hasReturn(host, options) }
: { commentOwner };
}
case SyntaxKind.SourceFile:
return "quit";
case SyntaxKind.ModuleDeclaration:
// If in walking up the tree, we hit a a nested namespace declaration,
// then we must be somewhere within a dotted namespace name; however we don't
// want to give back a JSDoc template for the 'b' or 'c' in 'namespace a.b.c { }'.
return commentOwner.parent.kind === SyntaxKind.ModuleDeclaration ? undefined : { commentOwner };
case SyntaxKind.ExpressionStatement:
return getCommentOwnerInfoWorker((commentOwner as ExpressionStatement).expression, options);
case SyntaxKind.BinaryExpression: {
const be = commentOwner as BinaryExpression;
if (getAssignmentDeclarationKind(be) === AssignmentDeclarationKind.None) {
return "quit";
}
return isFunctionLike(be.right)
? { commentOwner, parameters: be.right.parameters, hasReturn: hasReturn(be.right, options) }
: { commentOwner };
}
case SyntaxKind.PropertyDeclaration:
const init = (commentOwner as PropertyDeclaration).initializer;
if (init && (isFunctionExpression(init) || isArrowFunction(init))) {
return { commentOwner, parameters: init.parameters, hasReturn: hasReturn(init, options) };
}
}
}
function hasReturn(node: Node, options: DocCommentTemplateOptions | undefined) {
return !!options?.generateReturnInDocTemplate &&
(isArrowFunction(node) && isExpression(node.body)
|| isFunctionLikeDeclaration(node) && node.body && isBlock(node.body) && !!forEachReturnStatement(node.body, n => n));
}
function getRightHandSideOfAssignment(rightHandSide: Expression): FunctionExpression | ArrowFunction | ConstructorDeclaration | undefined {
while (rightHandSide.kind === SyntaxKind.ParenthesizedExpression) {
rightHandSide = (rightHandSide as ParenthesizedExpression).expression;
}
switch (rightHandSide.kind) {
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return (rightHandSide as FunctionExpression);
case SyntaxKind.ClassExpression:
return find((rightHandSide as ClassExpression).members, isConstructorDeclaration);
}
}
}