1#![cfg_attr(test, deny(warnings))]
2
3use std::sync::Arc;
4
5use dashmap::DashMap;
6use rustc_hash::FxBuildHasher;
7use swc_atoms::atom;
8use swc_common::{
9 comments::{Comment, CommentKind, Comments},
10 BytePos, DUMMY_SP,
11};
12
13type CommentMap = Arc<DashMap<BytePos, Vec<Comment>, FxBuildHasher>>;
14
15#[derive(Clone, Default)]
17pub struct SwcComments {
18 pub leading: CommentMap,
19 pub trailing: CommentMap,
20}
21
22impl Comments for SwcComments {
23 fn add_leading(&self, pos: BytePos, cmt: Comment) {
24 self.leading.entry(pos).or_default().push(cmt);
25 }
26
27 fn add_leading_comments(&self, pos: BytePos, comments: Vec<Comment>) {
28 self.leading.entry(pos).or_default().extend(comments);
29 }
30
31 fn has_leading(&self, pos: BytePos) -> bool {
32 if let Some(v) = self.leading.get(&pos) {
33 !v.is_empty()
34 } else {
35 false
36 }
37 }
38
39 fn move_leading(&self, from: BytePos, to: BytePos) {
40 let cmt = self.take_leading(from);
41
42 if let Some(mut cmt) = cmt {
43 if from < to && self.has_leading(to) {
44 cmt.extend(self.take_leading(to).unwrap());
45 }
46
47 self.add_leading_comments(to, cmt);
48 }
49 }
50
51 fn take_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
52 self.leading.remove(&pos).map(|v| v.1)
53 }
54
55 fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
56 self.leading.get(&pos).map(|v| v.to_owned())
57 }
58
59 fn add_trailing(&self, pos: BytePos, cmt: Comment) {
60 self.trailing.entry(pos).or_default().push(cmt)
61 }
62
63 fn add_trailing_comments(&self, pos: BytePos, comments: Vec<Comment>) {
64 self.trailing.entry(pos).or_default().extend(comments)
65 }
66
67 fn has_trailing(&self, pos: BytePos) -> bool {
68 if let Some(v) = self.trailing.get(&pos) {
69 !v.is_empty()
70 } else {
71 false
72 }
73 }
74
75 fn move_trailing(&self, from: BytePos, to: BytePos) {
76 let cmt = self.take_trailing(from);
77
78 if let Some(mut cmt) = cmt {
79 if from < to && self.has_trailing(to) {
80 cmt.extend(self.take_trailing(to).unwrap());
81 }
82
83 self.add_trailing_comments(to, cmt);
84 }
85 }
86
87 fn take_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
88 self.trailing.remove(&pos).map(|v| v.1)
89 }
90
91 fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
92 self.trailing.get(&pos).map(|v| v.to_owned())
93 }
94
95 fn add_pure_comment(&self, pos: BytePos) {
96 let mut leading = self.leading.entry(pos).or_default();
97 let pure_comment = Comment {
98 kind: CommentKind::Block,
99 span: DUMMY_SP,
100 text: atom!("#__PURE__"),
101 };
102
103 if !leading.iter().any(|c| c.text == pure_comment.text) {
104 leading.push(pure_comment);
105 }
106 }
107
108 fn with_leading<F, Ret>(&self, pos: BytePos, f: F) -> Ret
109 where
110 Self: Sized,
111 F: FnOnce(&[Comment]) -> Ret,
112 {
113 let ret = if let Some(cmts) = self.leading.get(&pos) {
114 f(&cmts)
115 } else {
116 f(&[])
117 };
118
119 ret
120 }
121
122 fn with_trailing<F, Ret>(&self, pos: BytePos, f: F) -> Ret
123 where
124 Self: Sized,
125 F: FnOnce(&[Comment]) -> Ret,
126 {
127 let ret = if let Some(cmts) = &self.trailing.get(&pos) {
128 f(cmts)
129 } else {
130 f(&[])
131 };
132
133 ret
134 }
135
136 fn has_flag(&self, lo: BytePos, flag: &str) -> bool {
137 self.with_leading(lo, |comments| {
138 for c in comments {
139 if c.kind == CommentKind::Block {
140 for line in c.text.lines() {
141 let line = line.trim_start_matches(['*', ' ']);
143 let line = line.trim();
144
145 if line.len() == (flag.len() + 5)
147 && (line.starts_with("#__") || line.starts_with("@__"))
148 && line.ends_with("__")
149 && flag == &line[3..line.len() - 2]
150 {
151 return true;
152 }
153 }
154 }
155 }
156
157 false
158 })
159 }
160}