File size: 7,448 Bytes
6abff73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
import { TSDocMessageId } from './TSDocMessageId';
// Internal parser state
var State;
(function (State) {
    // Initial state, looking for "/*"
    State[State["BeginComment1"] = 0] = "BeginComment1";
    // Looking for "*" or "* " after "/*"
    State[State["BeginComment2"] = 1] = "BeginComment2";
    // Like State.CollectingLine except immediately after the "/**"
    State[State["CollectingFirstLine"] = 2] = "CollectingFirstLine";
    // Collecting characters until we reach a newline
    State[State["CollectingLine"] = 3] = "CollectingLine";
    // After a newline, looking for the "*" that begins a new line, or the "*/" to end the comment
    State[State["AdvancingLine"] = 4] = "AdvancingLine";
    // Exiting the parser loop
    State[State["Done"] = 5] = "Done";
})(State || (State = {}));
/**
 * The main API for parsing TSDoc comments.
 */
export class LineExtractor {
    /**
     * This step parses an entire code comment from slash-star-star until star-slash,
     * and extracts the content lines.  The lines are stored in IDocCommentParameters.lines
     * and the overall text range is assigned to IDocCommentParameters.range.
     */
    static extract(parserContext) {
        const range = parserContext.sourceRange;
        const buffer = range.buffer;
        let commentRangeStart = 0;
        let commentRangeEnd = 0;
        // These must be set before entering CollectingFirstLine, CollectingLine, or AdvancingLine
        let collectingLineStart = 0;
        let collectingLineEnd = 0;
        let nextIndex = range.pos;
        let state = State.BeginComment1;
        const lines = [];
        while (state !== State.Done) {
            if (nextIndex >= range.end) {
                // reached the end of the input
                switch (state) {
                    case State.BeginComment1:
                    case State.BeginComment2:
                        parserContext.log.addMessageForTextRange(TSDocMessageId.CommentNotFound, 'Expecting a "/**" comment', range);
                        return false;
                    default:
                        parserContext.log.addMessageForTextRange(TSDocMessageId.CommentMissingClosingDelimiter, 'Unexpected end of input', range);
                        return false;
                }
            }
            const current = buffer[nextIndex];
            const currentIndex = nextIndex;
            ++nextIndex;
            const next = nextIndex < range.end ? buffer[nextIndex] : '';
            switch (state) {
                case State.BeginComment1:
                    if (current === '/' && next === '*') {
                        commentRangeStart = currentIndex;
                        ++nextIndex; // skip the star
                        state = State.BeginComment2;
                    }
                    else if (!LineExtractor._whitespaceCharacterRegExp.test(current)) {
                        parserContext.log.addMessageForTextRange(TSDocMessageId.CommentOpeningDelimiterSyntax, 'Expecting a leading "/**"', range.getNewRange(currentIndex, currentIndex + 1));
                        return false;
                    }
                    break;
                case State.BeginComment2:
                    if (current === '*') {
                        if (next === ' ') {
                            ++nextIndex; // Discard the space after the star
                        }
                        collectingLineStart = nextIndex;
                        collectingLineEnd = nextIndex;
                        state = State.CollectingFirstLine;
                    }
                    else {
                        parserContext.log.addMessageForTextRange(TSDocMessageId.CommentOpeningDelimiterSyntax, 'Expecting a leading "/**"', range.getNewRange(currentIndex, currentIndex + 1));
                        return false;
                    }
                    break;
                case State.CollectingFirstLine:
                case State.CollectingLine:
                    if (current === '\n') {
                        // Ignore an empty line if it is immediately after the "/**"
                        if (state !== State.CollectingFirstLine || collectingLineEnd > collectingLineStart) {
                            // Record the line that we collected
                            lines.push(range.getNewRange(collectingLineStart, collectingLineEnd));
                        }
                        collectingLineStart = nextIndex;
                        collectingLineEnd = nextIndex;
                        state = State.AdvancingLine;
                    }
                    else if (current === '*' && next === '/') {
                        if (collectingLineEnd > collectingLineStart) {
                            lines.push(range.getNewRange(collectingLineStart, collectingLineEnd));
                        }
                        collectingLineStart = 0;
                        collectingLineEnd = 0;
                        ++nextIndex; // skip the slash
                        commentRangeEnd = nextIndex;
                        state = State.Done;
                    }
                    else if (!LineExtractor._whitespaceCharacterRegExp.test(current)) {
                        collectingLineEnd = nextIndex;
                    }
                    break;
                case State.AdvancingLine:
                    if (current === '*') {
                        if (next === '/') {
                            collectingLineStart = 0;
                            collectingLineEnd = 0;
                            ++nextIndex; // skip the slash
                            commentRangeEnd = nextIndex;
                            state = State.Done;
                        }
                        else {
                            // Discard the "*" at the start of a line
                            if (next === ' ') {
                                ++nextIndex; // Discard the space after the star
                            }
                            collectingLineStart = nextIndex;
                            collectingLineEnd = nextIndex;
                            state = State.CollectingLine;
                        }
                    }
                    else if (current === '\n') {
                        // Blank line
                        lines.push(range.getNewRange(currentIndex, currentIndex));
                        collectingLineStart = nextIndex;
                    }
                    else if (!LineExtractor._whitespaceCharacterRegExp.test(current)) {
                        // If the star is missing, then start the line here
                        // Example: "/**\nL1*/"
                        // (collectingLineStart was the start of this line)
                        collectingLineEnd = nextIndex;
                        state = State.CollectingLine;
                    }
                    break;
            }
        }
        /**
         * Only fill in these if we successfully scanned a comment
         */
        parserContext.commentRange = range.getNewRange(commentRangeStart, commentRangeEnd);
        parserContext.lines = lines;
        return true;
    }
}
LineExtractor._whitespaceCharacterRegExp = /^\s$/;
//# sourceMappingURL=LineExtractor.js.map