microsoft / microsoft/TypeScript

Suggestion: Provide better APIs to simplify converting CommentRanges into SynthesizedComments

Offen
#17,615 2 Kommentare 0 Reaktionen 1 zugewiesene Person Auf GitHub ansehen

@rbuckton arbeitet bereits daran.

Seit 22.8.2017.

Domain: API In Discussion Suggestion
Vorherrschende Sprache
Go
Sterne
111k
Forks
14.3k
Ø Merge
1 T. 19 Std.
Gemergte PRs (30 T.)
117

Beschreibung

Scenario

Tsickle from the Angular project is a TypeScript transformer that makes the emitted JavaScript compatible with Google Closure Compiler.

For this, it converts types into comments so that Closure Compiler can understand the types. E.g.

/**
  * The test function
  */
function test(a: number): string { ...}

becomes

/**
  * The test function
  * @param {number} a
  * @return {string}
  */
function test(a: number): string { ...}

Additionally, it escapes existing @... tags in jsdoc tags that are unknown to Closure Compiler. E.g.

/**
 * For example,
 * @madeUpTag
 */
const c = 'c';

becomes

/**
 * For example,
 * \@madeUpTag
 */
const c = 'c';

Finally, it also generates new code that also contains comments. E.g. it always adds a file prelude and converts interfaces into functions with closure type annotations:

interface Point {
  x: number;
  y: number;
}

becomes

/**
 * @fileoverview Generated by tsickle
 */

function Point() { }
function Point_tsickle_Closure_declarations() {
    /** @type {number} */
    Point.prototype.x;
    /** @type {number} */
    Point.prototype.y;
}

It follows, that Tsickle needs to be able to potentially modify every comment in the emitted JavaScript, and be able to synthetically create any kind of comment as well. We recently switched to using transformers, and went with the following approach to modify comments: Suppress the original comment via ts.EmitFlags.NoComment, convert the ts.CommentRanges into ts.SynthesizedComments which can be modified.

Problems with the current API
We made this work, but we had to duplicate quite a but of logic from compiler/comments.ts:

  • for a parent and a child node that start / end at the same offset (e.g. VariableStatement and the first VariableDeclaration), ts.getLeadingCommentRanges / ts.getTrailingCommentRanges returns the same comments for both, as it is only based on the node position. With the current API, we need to keep track of which comments we already processed so that we don't get duplications in the output.
  • getting the leading inline comments before a node does not work when using ts.getLeadingCommentRanges as it skips the characters until the first newline. With the current API, we force the function to read all leading comments by using pos = 0 and just passing in the trivia of the node. This gives us all comments, but we also need to keep track of which comments were already processed so that we don't duplicate the trailing comments from the previous node.
  • there is no API do detect the different kind of comments (pinned, detached, tripple slash), and the API for emitting SynthesizedComments only has regular comments that are always emitted. I.e. using the current API, we need to duplicate a lot of the logic from TypeScript.
  • there is no API for detecting detached leading / trailing comments (e.g. for Blocks and SourceFile). With the current API, we need to duplicate the logic for detecting detached comments, including the counting of newlines between comments. There is also no direct API for emitting detached SynthesizedComments. So far we are using a ts.NonEmittedStatement with ts.EmitFlags.CustomPrologue (the EmitFlag is to prevent additional newlines before the comment).
  • synthetic comments already add the wrapping comment characters (/* / */ / // ) depending on the CommentKind, so when converting a CommentRange into a SynthesizedComment we need to strip them manually.

Proposal Attempt
Add the following APIs:

  • ts.getLeadingCommentRanges(node: ts.Node): ts.CommentRange[]
    • similar to ts.getLeadingCommentRanges(text: string, pos: number)
    • would solve the problems of inline leading comments
    • would only return a comment for either the parent or child when they start at the same position.
    • would never return leading detached comments
  • ts.getTrailingCommentRanges(node: ts.Node): ts.CommentRange[]:
    • similar to ts.getTrailingCommentRanges(text: string, pos: number)
    • would only return a comment for either the parent or child when they end at the same position.
    • would never return trailing detached comments
  • ts.getLeading/TrailingDetachedCommentRanges(node: ts.Node): ts.CommentRange[]:
    • returns the leading / trailing detached comments for a ts.Block and ts.SourceFile
  • ts.setSyntheticLeading/TrailingDetachedComments(node: ts.Node, comments: ts.SynthesizedComment[]):
    • would set leading / trailing detached synthesized comments for a ts.Block and ts.SourceFile

Additionally, change ts.SynthesizedComment / ts.CommentRange:

type CommentKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.TrippleSlashCommentTrivia;

interface ts.CommentRange {
  isPinned?: boolean;
}

interface ts.SynthesizedComment extends ts.CommentRange {
  ...
  containsTrivia?: boolean // whether the `text` already contains the trivia or not.
}

/cc @mprobst @evmar @alexeagle

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.