forge_fmt/
inline_config.rs
1use 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.next().map(|(idx, _)| idx).unwrap_or_default();
103
104 let end_offset = loc.end();
105 let mut next_newline =
106 src[end_offset..].char_indices().skip_while(|(_, ch)| *ch != '\n');
107 let end =
108 end_offset + next_newline.next().map(|(idx, _)| idx).unwrap_or_default();
109
110 disabled_ranges.push(DisabledRange { start, end, loose: false });
111 }
112 InlineConfigItem::DisableNextLine => {
113 let offset = loc.end();
114 let mut char_indices =
115 src[offset..].char_indices().skip_while(|(_, ch)| *ch != '\n').skip(1);
116 if let Some((mut start, _)) = char_indices.next() {
117 start += offset;
118 let end = char_indices
119 .find(|(_, ch)| *ch == '\n')
120 .map(|(idx, _)| offset + idx + 1)
121 .unwrap_or(src.len());
122 disabled_ranges.push(DisabledRange { start, end, loose: false });
123 }
124 }
125 InlineConfigItem::DisableStart => {
126 if disabled_depth == 0 {
127 disabled_range_start = Some(loc.end());
128 }
129 disabled_depth += 1;
130 }
131 InlineConfigItem::DisableEnd => {
132 disabled_depth = disabled_depth.saturating_sub(1);
133 if disabled_depth == 0 {
134 if let Some(start) = disabled_range_start.take() {
135 disabled_ranges.push(DisabledRange {
136 start,
137 end: loc.start(),
138 loose: false,
139 })
140 }
141 }
142 }
143 }
144 }
145 if let Some(start) = disabled_range_start.take() {
146 disabled_ranges.push(DisabledRange { start, end: src.len(), loose: false })
147 }
148 Self { disabled_ranges }
149 }
150
151 pub fn is_disabled(&self, loc: Loc) -> bool {
153 self.disabled_ranges.iter().any(|range| range.includes(loc))
154 }
155}