1use std::{
2 cell::{Ref, RefCell, RefMut},
3 rc::Rc,
4 sync::Arc,
5};
6
7use rustc_hash::FxHashMap;
8use swc_atoms::{atom, Atom};
9
10use crate::{
11 pos::Spanned,
12 syntax_pos::{BytePos, Span, DUMMY_SP},
13};
14
15pub trait Comments {
32 fn add_leading(&self, pos: BytePos, cmt: Comment);
33 fn add_leading_comments(&self, pos: BytePos, comments: Vec<Comment>);
34 fn has_leading(&self, pos: BytePos) -> bool;
35 fn move_leading(&self, from: BytePos, to: BytePos);
36 fn take_leading(&self, pos: BytePos) -> Option<Vec<Comment>>;
37 fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>>;
38
39 fn add_trailing(&self, pos: BytePos, cmt: Comment);
40 fn add_trailing_comments(&self, pos: BytePos, comments: Vec<Comment>);
41 fn has_trailing(&self, pos: BytePos) -> bool;
42 fn move_trailing(&self, from: BytePos, to: BytePos);
43 fn take_trailing(&self, pos: BytePos) -> Option<Vec<Comment>>;
44 fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>>;
45
46 fn add_pure_comment(&self, pos: BytePos);
47
48 fn with_leading<F, Ret>(&self, pos: BytePos, f: F) -> Ret
49 where
50 Self: Sized,
51 F: FnOnce(&[Comment]) -> Ret,
52 {
53 let cmts = self.take_leading(pos);
54
55 let ret = if let Some(cmts) = &cmts {
56 f(cmts)
57 } else {
58 f(&[])
59 };
60
61 if let Some(cmts) = cmts {
62 self.add_leading_comments(pos, cmts);
63 }
64
65 ret
66 }
67
68 fn with_trailing<F, Ret>(&self, pos: BytePos, f: F) -> Ret
69 where
70 Self: Sized,
71 F: FnOnce(&[Comment]) -> Ret,
72 {
73 let cmts = self.take_trailing(pos);
74
75 let ret = if let Some(cmts) = &cmts {
76 f(cmts)
77 } else {
78 f(&[])
79 };
80
81 if let Some(cmts) = cmts {
82 self.add_trailing_comments(pos, cmts);
83 }
84
85 ret
86 }
87
88 fn has_flag(&self, lo: BytePos, flag: &str) -> bool {
93 let cmts = self.take_leading(lo);
94
95 let ret = if let Some(comments) = &cmts {
96 (|| {
97 for c in comments {
98 if c.kind == CommentKind::Block {
99 for line in c.text.lines() {
100 let line = line.trim_start_matches(['*', ' ']);
102 let line = line.trim();
103
104 if line.len() == (flag.len() + 5)
106 && (line.starts_with("#__") || line.starts_with("@__"))
107 && line.ends_with("__")
108 && flag == &line[3..line.len() - 2]
109 {
110 return true;
111 }
112 }
113 }
114 }
115
116 false
117 })()
118 } else {
119 false
120 };
121
122 if let Some(cmts) = cmts {
123 self.add_trailing_comments(lo, cmts);
124 }
125
126 ret
127 }
128}
129
130macro_rules! delegate {
131 () => {
132 fn add_leading(&self, pos: BytePos, cmt: Comment) {
133 (**self).add_leading(pos, cmt)
134 }
135
136 fn add_leading_comments(&self, pos: BytePos, comments: Vec<Comment>) {
137 (**self).add_leading_comments(pos, comments)
138 }
139
140 fn has_leading(&self, pos: BytePos) -> bool {
141 (**self).has_leading(pos)
142 }
143
144 fn move_leading(&self, from: BytePos, to: BytePos) {
145 (**self).move_leading(from, to)
146 }
147
148 fn take_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
149 (**self).take_leading(pos)
150 }
151
152 fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
153 (**self).get_leading(pos)
154 }
155
156 fn add_trailing(&self, pos: BytePos, cmt: Comment) {
157 (**self).add_trailing(pos, cmt)
158 }
159
160 fn add_trailing_comments(&self, pos: BytePos, comments: Vec<Comment>) {
161 (**self).add_trailing_comments(pos, comments)
162 }
163
164 fn has_trailing(&self, pos: BytePos) -> bool {
165 (**self).has_trailing(pos)
166 }
167
168 fn move_trailing(&self, from: BytePos, to: BytePos) {
169 (**self).move_trailing(from, to)
170 }
171
172 fn take_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
173 (**self).take_trailing(pos)
174 }
175
176 fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
177 (**self).get_trailing(pos)
178 }
179
180 fn add_pure_comment(&self, pos: BytePos) {
181 (**self).add_pure_comment(pos)
182 }
183
184 fn has_flag(&self, lo: BytePos, flag: &str) -> bool {
185 (**self).has_flag(lo, flag)
186 }
187 };
188}
189
190impl<T> Comments for &'_ T
191where
192 T: ?Sized + Comments,
193{
194 delegate!();
195}
196
197impl<T> Comments for Arc<T>
198where
199 T: ?Sized + Comments,
200{
201 delegate!();
202}
203
204impl<T> Comments for Rc<T>
205where
206 T: ?Sized + Comments,
207{
208 delegate!();
209}
210
211impl<T> Comments for Box<T>
212where
213 T: ?Sized + Comments,
214{
215 delegate!();
216}
217
218#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
220pub struct NoopComments;
221
222impl Comments for NoopComments {
223 #[cfg_attr(not(debug_assertions), inline(always))]
224 fn add_leading(&self, _: BytePos, _: Comment) {}
225
226 #[cfg_attr(not(debug_assertions), inline(always))]
227 fn add_leading_comments(&self, _: BytePos, _: Vec<Comment>) {}
228
229 #[cfg_attr(not(debug_assertions), inline(always))]
230 fn has_leading(&self, _: BytePos) -> bool {
231 false
232 }
233
234 #[cfg_attr(not(debug_assertions), inline(always))]
235 fn move_leading(&self, _: BytePos, _: BytePos) {}
236
237 #[cfg_attr(not(debug_assertions), inline(always))]
238 fn take_leading(&self, _: BytePos) -> Option<Vec<Comment>> {
239 None
240 }
241
242 #[cfg_attr(not(debug_assertions), inline(always))]
243 fn get_leading(&self, _: BytePos) -> Option<Vec<Comment>> {
244 None
245 }
246
247 #[cfg_attr(not(debug_assertions), inline(always))]
248 fn add_trailing(&self, _: BytePos, _: Comment) {}
249
250 #[cfg_attr(not(debug_assertions), inline(always))]
251 fn add_trailing_comments(&self, _: BytePos, _: Vec<Comment>) {}
252
253 #[cfg_attr(not(debug_assertions), inline(always))]
254 fn has_trailing(&self, _: BytePos) -> bool {
255 false
256 }
257
258 #[cfg_attr(not(debug_assertions), inline(always))]
259 fn move_trailing(&self, _: BytePos, _: BytePos) {}
260
261 #[cfg_attr(not(debug_assertions), inline(always))]
262 fn take_trailing(&self, _: BytePos) -> Option<Vec<Comment>> {
263 None
264 }
265
266 #[cfg_attr(not(debug_assertions), inline(always))]
267 fn get_trailing(&self, _: BytePos) -> Option<Vec<Comment>> {
268 None
269 }
270
271 #[cfg_attr(not(debug_assertions), inline(always))]
272 fn add_pure_comment(&self, _: BytePos) {}
273
274 #[inline]
275 fn has_flag(&self, _: BytePos, _: &str) -> bool {
276 false
277 }
278}
279
280impl<C> Comments for Option<C>
282where
283 C: Comments,
284{
285 fn add_leading(&self, pos: BytePos, cmt: Comment) {
286 if let Some(c) = self {
287 c.add_leading(pos, cmt)
288 }
289 }
290
291 fn add_leading_comments(&self, pos: BytePos, comments: Vec<Comment>) {
292 if let Some(c) = self {
293 c.add_leading_comments(pos, comments)
294 }
295 }
296
297 fn has_leading(&self, pos: BytePos) -> bool {
298 if let Some(c) = self {
299 c.has_leading(pos)
300 } else {
301 false
302 }
303 }
304
305 fn move_leading(&self, from: BytePos, to: BytePos) {
306 if let Some(c) = self {
307 c.move_leading(from, to)
308 }
309 }
310
311 fn take_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
312 if let Some(c) = self {
313 c.take_leading(pos)
314 } else {
315 None
316 }
317 }
318
319 fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
320 if let Some(c) = self {
321 c.get_leading(pos)
322 } else {
323 None
324 }
325 }
326
327 fn add_trailing(&self, pos: BytePos, cmt: Comment) {
328 if let Some(c) = self {
329 c.add_trailing(pos, cmt)
330 }
331 }
332
333 fn add_trailing_comments(&self, pos: BytePos, comments: Vec<Comment>) {
334 if let Some(c) = self {
335 c.add_trailing_comments(pos, comments)
336 }
337 }
338
339 fn has_trailing(&self, pos: BytePos) -> bool {
340 if let Some(c) = self {
341 c.has_trailing(pos)
342 } else {
343 false
344 }
345 }
346
347 fn move_trailing(&self, from: BytePos, to: BytePos) {
348 if let Some(c) = self {
349 c.move_trailing(from, to)
350 }
351 }
352
353 fn take_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
354 if let Some(c) = self {
355 c.take_trailing(pos)
356 } else {
357 None
358 }
359 }
360
361 fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
362 if let Some(c) = self {
363 c.get_trailing(pos)
364 } else {
365 None
366 }
367 }
368
369 fn add_pure_comment(&self, pos: BytePos) {
370 assert_ne!(pos, BytePos(0), "cannot add pure comment to zero position");
371
372 if let Some(c) = self {
373 c.add_pure_comment(pos)
374 }
375 }
376
377 fn with_leading<F, Ret>(&self, pos: BytePos, f: F) -> Ret
378 where
379 Self: Sized,
380 F: FnOnce(&[Comment]) -> Ret,
381 {
382 if let Some(c) = self {
383 c.with_leading(pos, f)
384 } else {
385 f(&[])
386 }
387 }
388
389 fn with_trailing<F, Ret>(&self, pos: BytePos, f: F) -> Ret
390 where
391 Self: Sized,
392 F: FnOnce(&[Comment]) -> Ret,
393 {
394 if let Some(c) = self {
395 c.with_trailing(pos, f)
396 } else {
397 f(&[])
398 }
399 }
400
401 #[inline]
402 fn has_flag(&self, lo: BytePos, flag: &str) -> bool {
403 if let Some(c) = self {
404 c.has_flag(lo, flag)
405 } else {
406 false
407 }
408 }
409}
410
411pub type SingleThreadedCommentsMapInner = FxHashMap<BytePos, Vec<Comment>>;
412pub type SingleThreadedCommentsMap = Rc<RefCell<SingleThreadedCommentsMapInner>>;
413
414#[derive(Debug, Clone, Default)]
416pub struct SingleThreadedComments {
417 leading: SingleThreadedCommentsMap,
418 trailing: SingleThreadedCommentsMap,
419}
420
421impl Comments for SingleThreadedComments {
422 fn add_leading(&self, pos: BytePos, cmt: Comment) {
423 self.leading.borrow_mut().entry(pos).or_default().push(cmt);
424 }
425
426 fn add_leading_comments(&self, pos: BytePos, comments: Vec<Comment>) {
427 self.leading
428 .borrow_mut()
429 .entry(pos)
430 .or_default()
431 .extend(comments);
432 }
433
434 fn has_leading(&self, pos: BytePos) -> bool {
435 if let Some(v) = self.leading.borrow().get(&pos) {
436 !v.is_empty()
437 } else {
438 false
439 }
440 }
441
442 fn move_leading(&self, from: BytePos, to: BytePos) {
443 let cmt = self.take_leading(from);
444
445 if let Some(mut cmt) = cmt {
446 if from < to && self.has_leading(to) {
447 cmt.extend(self.take_leading(to).unwrap());
448 }
449
450 self.add_leading_comments(to, cmt);
451 }
452 }
453
454 fn take_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
455 self.leading.borrow_mut().remove(&pos)
456 }
457
458 fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
459 self.leading.borrow().get(&pos).map(|c| c.to_owned())
460 }
461
462 fn add_trailing(&self, pos: BytePos, cmt: Comment) {
463 self.trailing.borrow_mut().entry(pos).or_default().push(cmt);
464 }
465
466 fn add_trailing_comments(&self, pos: BytePos, comments: Vec<Comment>) {
467 self.trailing
468 .borrow_mut()
469 .entry(pos)
470 .or_default()
471 .extend(comments);
472 }
473
474 fn has_trailing(&self, pos: BytePos) -> bool {
475 if let Some(v) = self.trailing.borrow().get(&pos) {
476 !v.is_empty()
477 } else {
478 false
479 }
480 }
481
482 fn move_trailing(&self, from: BytePos, to: BytePos) {
483 let cmt = self.take_trailing(from);
484
485 if let Some(mut cmt) = cmt {
486 if from < to && self.has_trailing(to) {
487 cmt.extend(self.take_trailing(to).unwrap());
488 }
489
490 self.add_trailing_comments(to, cmt);
491 }
492 }
493
494 fn take_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
495 self.trailing.borrow_mut().remove(&pos)
496 }
497
498 fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
499 self.trailing.borrow().get(&pos).map(|c| c.to_owned())
500 }
501
502 fn add_pure_comment(&self, pos: BytePos) {
503 assert_ne!(pos, BytePos(0), "cannot add pure comment to zero position");
504
505 let mut leading_map = self.leading.borrow_mut();
506 let leading = leading_map.entry(pos).or_default();
507 let pure_comment = Comment {
508 kind: CommentKind::Block,
509 span: DUMMY_SP,
510 text: atom!("#__PURE__"),
511 };
512
513 if !leading.iter().any(|c| c.text == pure_comment.text) {
514 leading.push(pure_comment);
515 }
516 }
517
518 fn with_leading<F, Ret>(&self, pos: BytePos, f: F) -> Ret
519 where
520 Self: Sized,
521 F: FnOnce(&[Comment]) -> Ret,
522 {
523 let b = self.leading.borrow();
524 let cmts = b.get(&pos);
525
526 if let Some(cmts) = &cmts {
527 f(cmts)
528 } else {
529 f(&[])
530 }
531 }
532
533 fn with_trailing<F, Ret>(&self, pos: BytePos, f: F) -> Ret
534 where
535 Self: Sized,
536 F: FnOnce(&[Comment]) -> Ret,
537 {
538 let b = self.trailing.borrow();
539 let cmts = b.get(&pos);
540
541 if let Some(cmts) = &cmts {
542 f(cmts)
543 } else {
544 f(&[])
545 }
546 }
547
548 fn has_flag(&self, lo: BytePos, flag: &str) -> bool {
549 self.with_leading(lo, |comments| {
550 for c in comments {
551 if c.kind == CommentKind::Block {
552 for line in c.text.lines() {
553 let line = line.trim_start_matches(['*', ' ']);
555 let line = line.trim();
556
557 if line.len() == (flag.len() + 5)
559 && (line.starts_with("#__") || line.starts_with("@__"))
560 && line.ends_with("__")
561 && flag == &line[3..line.len() - 2]
562 {
563 return true;
564 }
565 }
566 }
567 }
568
569 false
570 })
571 }
572}
573
574impl SingleThreadedComments {
575 pub fn from_leading_and_trailing(
578 leading: SingleThreadedCommentsMap,
579 trailing: SingleThreadedCommentsMap,
580 ) -> Self {
581 SingleThreadedComments { leading, trailing }
582 }
583
584 pub fn take_all(self) -> (SingleThreadedCommentsMap, SingleThreadedCommentsMap) {
586 (self.leading, self.trailing)
587 }
588
589 pub fn borrow_all(
591 &self,
592 ) -> (
593 Ref<SingleThreadedCommentsMapInner>,
594 Ref<SingleThreadedCommentsMapInner>,
595 ) {
596 (self.leading.borrow(), self.trailing.borrow())
597 }
598
599 pub fn borrow_all_mut(
601 &self,
602 ) -> (
603 RefMut<SingleThreadedCommentsMapInner>,
604 RefMut<SingleThreadedCommentsMapInner>,
605 ) {
606 (self.leading.borrow_mut(), self.trailing.borrow_mut())
607 }
608
609 pub fn with_leading<F, Ret>(&self, pos: BytePos, op: F) -> Ret
610 where
611 F: FnOnce(&[Comment]) -> Ret,
612 {
613 if let Some(comments) = self.leading.borrow().get(&pos) {
614 op(comments)
615 } else {
616 op(&[])
617 }
618 }
619
620 pub fn with_trailing<F, Ret>(&self, pos: BytePos, op: F) -> Ret
621 where
622 F: FnOnce(&[Comment]) -> Ret,
623 {
624 if let Some(comments) = self.trailing.borrow().get(&pos) {
625 op(comments)
626 } else {
627 op(&[])
628 }
629 }
630}
631
632#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
633#[cfg_attr(
634 any(feature = "rkyv-impl"),
635 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
636)]
637#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
638#[cfg_attr(feature = "rkyv-impl", repr(C))]
639pub struct Comment {
640 pub kind: CommentKind,
641 pub span: Span,
642 pub text: Atom,
644}
645
646impl Spanned for Comment {
647 fn span(&self) -> Span {
648 self.span
649 }
650}
651
652#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
653#[cfg_attr(
654 any(feature = "rkyv-impl"),
655 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
656)]
657#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
658#[cfg_attr(feature = "rkyv-impl", repr(u32))]
659pub enum CommentKind {
660 Line = 0,
661 Block = 1,
662}
663
664#[deprecated(
665 since = "0.13.5",
666 note = "helper methods are merged into Comments itself"
667)]
668pub trait CommentsExt: Comments {
669 fn with_leading<F, Ret>(&self, pos: BytePos, op: F) -> Ret
670 where
671 F: FnOnce(&[Comment]) -> Ret,
672 {
673 if let Some(comments) = self.get_leading(pos) {
674 op(&comments)
675 } else {
676 op(&[])
677 }
678 }
679
680 fn with_trailing<F, Ret>(&self, pos: BytePos, op: F) -> Ret
681 where
682 F: FnOnce(&[Comment]) -> Ret,
683 {
684 if let Some(comments) = self.get_trailing(pos) {
685 op(&comments)
686 } else {
687 op(&[])
688 }
689 }
690}
691
692#[allow(deprecated)]
693impl<C> CommentsExt for C where C: Comments {}
694
695better_scoped_tls::scoped_tls!(
696 #[doc(hidden)]
698 pub static COMMENTS: Box<dyn Comments>
699);