|
4 | 4 |
|
5 | 5 | import { createRequire } from "node:module"; |
6 | 6 | import { sourceText, initSourceText } from "./source_code.js"; |
7 | | -import { debugAssertIsNonNull } from "../utils/asserts.js"; |
| 7 | +import { debugAssert, debugAssertIsNonNull } from "../utils/asserts.js"; |
8 | 8 |
|
9 | 9 | import type { Comment, Node, NodeOrToken } from "./types.ts"; |
10 | 10 | import type { Span } from "./location.ts"; |
@@ -184,40 +184,90 @@ function initTokensWithComments() { |
184 | 184 | debugAssertIsNonNull(tokens); |
185 | 185 | debugAssertIsNonNull(comments); |
186 | 186 |
|
187 | | - // TODO: Replace `range[0]` with `start` once we have our own tokens which have `start` property. |
| 187 | + // TODO: Replace `range[0]` with `start` throughout this function |
| 188 | + // once we have our own tokens which have `start` property |
| 189 | + |
| 190 | + // Fast paths for file with no comments, or file which is only comments |
| 191 | + const commentsLength = comments.length; |
| 192 | + if (commentsLength === 0) { |
| 193 | + tokensWithComments = tokens; |
| 194 | + return; |
| 195 | + } |
| 196 | + |
| 197 | + const tokensLength = tokens.length; |
| 198 | + if (tokensLength === 0) { |
| 199 | + tokensWithComments = comments; |
| 200 | + return; |
| 201 | + } |
| 202 | + |
| 203 | + // File contains both tokens and comments. |
| 204 | + // Fill `tokensWithComments` with the 2 arrays interleaved in source order. |
188 | 205 | tokensWithComments = []; |
189 | 206 |
|
190 | | - const tokensLength = tokens.length, |
191 | | - commentsLength = comments.length; |
| 207 | + let tokenIndex = 0, |
| 208 | + commentIndex = 0, |
| 209 | + token = tokens[0], |
| 210 | + comment = comments[0], |
| 211 | + tokenStart = token.range[0], |
| 212 | + commentStart = comment.range[0]; |
| 213 | + |
| 214 | + // Push any leading comments |
| 215 | + while (commentStart < tokenStart) { |
| 216 | + // Push current comment |
| 217 | + tokensWithComments.push(comment); |
| 218 | + |
| 219 | + // If that was last comment, push all remaining tokens, and exit |
| 220 | + if (++commentIndex === commentsLength) { |
| 221 | + tokensWithComments.push(...tokens.slice(tokenIndex)); |
| 222 | + debugCheckTokensWithComments(); |
| 223 | + return; |
| 224 | + } |
192 | 225 |
|
193 | | - let tokensIndex = 0, |
194 | | - commentsIndex = 0; |
195 | | - while (tokensIndex < tokensLength && commentsIndex < commentsLength) { |
196 | | - let token = tokens[tokensIndex], |
197 | | - comment = comments[commentsIndex]; |
| 226 | + // Get next comment |
| 227 | + comment = comments[commentIndex]; |
| 228 | + commentStart = comment.range[0]; |
| 229 | + } |
198 | 230 |
|
199 | | - // TODO: Replace `range[0]` with `start` once we have our own tokens which have `start` property. |
200 | | - const nextTokenStart = token.range[0], |
201 | | - nextCommentStart = comment.range[0]; |
| 231 | + // Push a run of tokens, then a run of comments, and so on, until all tokens and comments are exhausted |
| 232 | + while (true) { |
| 233 | + // There's at least 1 token and 1 comment remaining, and token is first. |
| 234 | + // Push tokens until we reach the next comment or the end. |
| 235 | + do { |
| 236 | + // Push current token |
| 237 | + tokensWithComments.push(token); |
202 | 238 |
|
203 | | - if (nextTokenStart < nextCommentStart) { |
204 | | - while (tokensIndex < tokensLength && token.range[0] <= nextCommentStart) { |
205 | | - tokensWithComments.push(token); |
206 | | - token = tokens[++tokensIndex]; |
| 239 | + // If that was last token, push all remaining comments, and exit |
| 240 | + if (++tokenIndex === tokensLength) { |
| 241 | + tokensWithComments.push(...comments.slice(commentIndex)); |
| 242 | + debugCheckTokensWithComments(); |
| 243 | + return; |
207 | 244 | } |
208 | | - } else { |
209 | | - while (commentsIndex < commentsLength && comment.range[0] <= nextTokenStart) { |
210 | | - tokensWithComments.push(comment); |
211 | | - comment = comments[++commentsIndex]; |
| 245 | + |
| 246 | + // Get next token |
| 247 | + token = tokens[tokenIndex]; |
| 248 | + tokenStart = token.range[0]; |
| 249 | + } while (tokenStart < commentStart); |
| 250 | + |
| 251 | + // There's at least 1 token and 1 comment remaining, and comment is first. |
| 252 | + // Push comments until we reach the next token or the end. |
| 253 | + do { |
| 254 | + // Push current comment |
| 255 | + tokensWithComments.push(comment); |
| 256 | + |
| 257 | + // If that was last comment, push all remaining tokens, and exit |
| 258 | + if (++commentIndex === commentsLength) { |
| 259 | + tokensWithComments.push(...tokens.slice(tokenIndex)); |
| 260 | + debugCheckTokensWithComments(); |
| 261 | + return; |
212 | 262 | } |
213 | | - } |
214 | | - } |
215 | 263 |
|
216 | | - // After one of `tokens` or `comments` is exhausted, directly push the other's elements |
217 | | - while (commentsIndex < commentsLength) tokensWithComments.push(comments[commentsIndex++]); |
218 | | - while (tokensIndex < tokensLength) tokensWithComments.push(tokens[tokensIndex++]); |
| 264 | + // Get next comment |
| 265 | + comment = comments[commentIndex]; |
| 266 | + commentStart = comment.range[0]; |
| 267 | + } while (commentStart < tokenStart); |
| 268 | + } |
219 | 269 |
|
220 | | - debugCheckTokensWithComments(); |
| 270 | + debugAssert(false, "Unreachable"); |
221 | 271 | } |
222 | 272 |
|
223 | 273 | /** |
|
0 commit comments