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