1use std::fmt::{Display, Formatter};
8
9use crate::{
10 Group, Parse, ParseStream, Punct, Span, Spanned, TextPoint, TextRange, ToTokens,
11 TokenParseError, TokenStream, TokenTree,
12};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum DelimiterKind {
22 Empty,
23 Semicolon,
24 Parenthesis,
25 Bracket,
26 Brace,
27 AngleBar,
28}
29
30pub trait DelimiterToken: Parse + Spanned + ToTokens + Sized {
36 const KIND: DelimiterKind;
37
38 fn new(span: Span) -> Self;
39
40 fn surround(&self, output: &mut TokenStream, contents: TokenStream) {
41 Delimiter::new(Self::KIND, self.span()).surround(output, contents);
42 }
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Spanned)]
46pub struct Delimiter {
48 pub kind: DelimiterKind,
49 pub span: Span,
51}
52
53impl Delimiter {
54 pub fn new(kind: DelimiterKind, span: Span) -> Self {
55 Self { kind, span }
56 }
57
58 pub fn opening_span(self) -> Span {
60 boundary_span(self.span, Boundary::Opening, self.kind.opening().len())
61 }
62
63 pub fn closing_span(self) -> Span {
65 boundary_span(self.span, Boundary::Closing, self.kind.closing().len())
66 }
67
68 pub fn surround(&self, output: &mut TokenStream, contents: TokenStream) {
70 match self.kind {
71 DelimiterKind::Empty => output.extend([contents]),
72 DelimiterKind::Semicolon => {
73 output.extend([contents]);
74 output.push_punct(Punct::new(";", self.closing_span()));
75 }
76 DelimiterKind::Parenthesis
77 | DelimiterKind::Bracket
78 | DelimiterKind::Brace
79 | DelimiterKind::AngleBar => output.push_group(Group::new(*self, contents)),
80 }
81 }
82}
83
84#[doc(hidden)]
86pub fn parse_delimiter<D: DelimiterToken>(input: &mut ParseStream) -> Result<D, TokenParseError> {
87 match D::KIND {
88 DelimiterKind::Empty => {
89 let span = input
90 .peek()
91 .map(|token| boundary_before(token.span()))
92 .unwrap_or_else(|| input.eof_span());
93 Ok(D::new(span))
94 }
95 DelimiterKind::Semicolon => {
96 let semicolon = <Token![;]>::parse(input)?;
97 Ok(D::new(semicolon.span()))
98 }
99 kind => {
100 let Some(token) = input.next_token() else {
101 return Err(TokenParseError::UnexpectedEnd {
102 expected: kind.opening(),
103 span: input.eof_span(),
104 });
105 };
106 let span = token.span();
107 let TokenTree::Group(group) = token else {
108 return Err(TokenParseError::UnexpectedToken {
109 expected: kind.opening(),
110 found: token.spelling().unwrap_or("trivia").to_owned(),
111 span,
112 });
113 };
114 if group.delim_kind() != kind || !group.stream().is_empty() {
115 return Err(TokenParseError::UnexpectedToken {
116 expected: kind.opening(),
117 found: group.delim_kind().opening().to_owned(),
118 span,
119 });
120 }
121 Ok(D::new(group.span()))
122 }
123 }
124}
125
126impl Display for Delimiter {
127 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
128 formatter.write_str(self.kind.opening())
129 }
130}
131impl Display for DelimiterKind {
132 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
133 formatter.write_str(self.opening())
134 }
135}
136
137impl DelimiterKind {
138 pub fn opening(self) -> &'static str {
139 match self {
140 Self::Empty | Self::Semicolon => "",
141 Self::Parenthesis => "(",
142 Self::Bracket => "[",
143 Self::Brace => "{",
144 Self::AngleBar => "<|",
145 }
146 }
147
148 pub fn closing(self) -> &'static str {
149 match self {
150 Self::Empty => "",
151 Self::Semicolon => ";",
152 Self::Parenthesis => ")",
153 Self::Bracket => "]",
154 Self::Brace => "}",
155 Self::AngleBar => "|>",
156 }
157 }
158}
159
160#[derive(Clone, Copy)]
161enum Boundary {
162 Opening,
163 Closing,
164}
165
166fn boundary_span(span: Span, boundary: Boundary, width: usize) -> Span {
167 let Ok(range) = span.range() else {
168 return Span::detached();
169 };
170 let (Some(start), Some(end)) = (range.start(), range.end()) else {
171 return Span::detached();
172 };
173 if end.byte.saturating_sub(start.byte) < width {
174 return span;
175 }
176
177 let range = match boundary {
178 Boundary::Opening => TextRange::new(start, shifted(start, width)),
179 Boundary::Closing => TextRange::new(shifted_back(end, width), end),
180 };
181 match span {
182 Span::FileLocated { source, .. } => Span::new(source, range),
183 Span::LocalRefFrame { .. } => Span::in_tmp_file(range),
184 Span::Detached => Span::detached(),
185 }
186}
187
188fn shifted(mut point: TextPoint, width: usize) -> TextPoint {
189 point.byte = point.byte.saturating_add(width);
190 point.column = point.column.saturating_add(width as u32);
191 point
192}
193
194fn shifted_back(mut point: TextPoint, width: usize) -> TextPoint {
195 point.byte = point.byte.saturating_sub(width);
196 point.column = point.column.saturating_sub(width as u32);
197 point
198}
199
200fn boundary_before(span: Span) -> Span {
201 span.source()
202 .ok()
203 .zip(span.start_point().ok())
204 .map(|(source, point)| Span::new(source, TextRange::from_point(point)))
205 .unwrap_or_else(Span::detached)
206}