solang_forge_fmt/
inline_config.rs1use crate::comments::{CommentState, CommentStringExt};
4use itertools::Itertools;
5use solang_parser::pt::Loc;
6use std::{fmt, str::FromStr};
7
8#[allow(clippy::enum_variant_names)]
10#[derive(Clone, Copy, Debug)]
11pub enum InlineConfigItem {
12 DisableNextItem,
14 DisableLine,
16 DisableNextLine,
18 DisableStart,
20 DisableEnd,
22}
23
24impl FromStr for InlineConfigItem {
25 type Err = InvalidInlineConfigItem;
26 fn from_str(s: &str) -> Result<Self, Self::Err> {
27 Ok(match s {
28 "disable-next-item" => Self::DisableNextItem,
29 "disable-line" => Self::DisableLine,
30 "disable-next-line" => Self::DisableNextLine,
31 "disable-start" => Self::DisableStart,
32 "disable-end" => Self::DisableEnd,
33 s => return Err(InvalidInlineConfigItem(s.into())),
34 })
35 }
36}
37
38#[derive(Debug)]
39pub struct InvalidInlineConfigItem(String);
40
41impl fmt::Display for InvalidInlineConfigItem {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 f.write_fmt(format_args!("Invalid inline config item: {}", self.0))
44 }
45}
46
47#[derive(Debug)]
51struct DisabledRange {
52 start: usize,
53 end: usize,
54 loose: bool,
55}
56
57impl DisabledRange {
58 fn includes(&self, loc: Loc) -> bool {
59 loc.start() >= self.start && (if self.loose { loc.start() } else { loc.end() } <= self.end)
60 }
61}
62
63#[derive(Debug, Default)]
71pub struct InlineConfig {
72 disabled_ranges: Vec<DisabledRange>,
73}
74
75impl InlineConfig {
76 pub fn new(items: impl IntoIterator<Item = (Loc, InlineConfigItem)>, src: &str) -> Self {
79 let mut disabled_ranges = vec![];
80 let mut disabled_range_start = None;
81 let mut disabled_depth = 0usize;
82 for (loc, item) in items.into_iter().sorted_by_key(|(loc, _)| loc.start()) {
83 match item {
84 InlineConfigItem::DisableNextItem => {
85 let offset = loc.end();
86 let mut char_indices = src[offset..]
87 .comment_state_char_indices()
88 .filter_map(|(state, idx, ch)| match state {
89 CommentState::None => Some((idx, ch)),
90 _ => None,
91 })
92 .skip_while(|(_, ch)| ch.is_whitespace());
93 if let Some((mut start, _)) = char_indices.next() {
94 start += offset;
95 let end = char_indices
96 .find(|(_, ch)| !ch.is_whitespace())
97 .map(|(idx, _)| offset + idx)
98 .unwrap_or(src.len());
99 disabled_ranges.push(DisabledRange {
100 start,
101 end,
102 loose: true,
103 });
104 }
105 }
106 InlineConfigItem::DisableLine => {
107 let mut prev_newline = src[..loc.start()]
108 .char_indices()
109 .rev()
110 .skip_while(|(_, ch)| *ch != '\n');
111 let start = prev_newline.next().map(|(idx, _)| idx).unwrap_or_default();
112
113 let end_offset = loc.end();
114 let mut next_newline = src[end_offset..]
115 .char_indices()
116 .skip_while(|(_, ch)| *ch != '\n');
117 let end =
118 end_offset + next_newline.next().map(|(idx, _)| idx).unwrap_or_default();
119
120 disabled_ranges.push(DisabledRange {
121 start,
122 end,
123 loose: false,
124 });
125 }
126 InlineConfigItem::DisableNextLine => {
127 let offset = loc.end();
128 let mut char_indices = src[offset..]
129 .char_indices()
130 .skip_while(|(_, ch)| *ch != '\n')
131 .skip(1);
132 if let Some((mut start, _)) = char_indices.next() {
133 start += offset;
134 let end = char_indices
135 .find(|(_, ch)| *ch == '\n')
136 .map(|(idx, _)| offset + idx + 1)
137 .unwrap_or(src.len());
138 disabled_ranges.push(DisabledRange {
139 start,
140 end,
141 loose: false,
142 });
143 }
144 }
145 InlineConfigItem::DisableStart => {
146 if disabled_depth == 0 {
147 disabled_range_start = Some(loc.end());
148 }
149 disabled_depth += 1;
150 }
151 InlineConfigItem::DisableEnd => {
152 disabled_depth = disabled_depth.saturating_sub(1);
153 if disabled_depth == 0 {
154 if let Some(start) = disabled_range_start.take() {
155 disabled_ranges.push(DisabledRange {
156 start,
157 end: loc.start(),
158 loose: false,
159 })
160 }
161 }
162 }
163 }
164 }
165 if let Some(start) = disabled_range_start.take() {
166 disabled_ranges.push(DisabledRange {
167 start,
168 end: src.len(),
169 loose: false,
170 })
171 }
172 Self { disabled_ranges }
173 }
174
175 pub fn is_disabled(&self, loc: Loc) -> bool {
177 self.disabled_ranges.iter().any(|range| range.includes(loc))
178 }
179}