1use std::{
12 fmt::{Display, Write},
13 marker::PhantomData,
14 path::Path,
15};
16
17use super::{Change, Tagger, Text};
18use crate::{
19 buffer::PathKind,
20 form::FormId,
21 text::{FormTag, Ghost, Spacer, SwapChar},
22};
23
24#[derive(Clone)]
60pub struct Builder {
61 text: Text,
62 last_form: Option<(usize, FormTag)>,
63 buffer: String,
64 last_was_empty: bool,
65 pub no_space_after_empty: bool,
67}
68
69impl Builder {
70 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn build(mut self) -> Text {
90 if let Some((b, id)) = self.last_form
91 && b < self.text.last_point().byte()
92 {
93 self.text.insert_tag(Tagger::basic(), b.., id);
94 }
95
96 self.text
97 }
98
99 pub fn build_no_double_nl(self) -> Text {
109 let mut text = self.build();
110 if let Some(last_last_byte) = text.len().checked_sub(2)
111 && let Some(strs) = text.get(last_last_byte..)
112 && strs == "\n\n"
113 {
114 text.replace_range(last_last_byte..last_last_byte + 1, "");
115 }
116
117 text
118 }
119
120 pub fn push<D: Display, _T>(&mut self, part: impl AsBuilderPart<D, _T>) {
129 self.push_builder_part(part.as_builder_part());
130 }
131
132 #[doc(hidden)]
133 pub fn push_builder_part<_T>(&mut self, part: BuilderPart<impl Display, _T>) {
134 fn push_simple(builder: &mut Builder, part: BuilderPart) {
135 use BuilderPart as BP;
136
137 let end = builder.text.last_point().byte();
138 let tagger = Tagger::basic();
139
140 match part {
141 BP::Text(text) => builder.push_text(text),
142 BP::Builder(other) => builder.push_builder(other),
143 BP::Path(path) => builder.push_str(path.to_string_lossy()),
144 BP::PathKind(text) => builder.push_text(&text),
145 BP::Form(tag) => {
146 let last_form = if tag == crate::form::DEFAULT_ID.to_tag(0) {
147 builder.last_form.take()
148 } else {
149 builder.last_form.replace((end, tag))
150 };
151
152 if let Some((b, tag)) = last_form
153 && b < end
154 {
155 builder.text.insert_tag(tagger, b..end, tag);
156 }
157 }
158 BP::Spacer(_) => builder.text.insert_tag(tagger, end, Spacer),
159 BP::Ghost(ghost) => builder.text.insert_tag(tagger, end, ghost.clone()),
160 BP::SwapChar(tag) => builder.text.insert_tag(tagger, end, tag),
161 BP::ToString(_) => unsafe { std::hint::unreachable_unchecked() },
162 }
163 }
164
165 match part.try_to_basic() {
166 Ok(part_ref) => push_simple(self, part_ref),
167 Err(BuilderPart::ToString(display)) => self.push_str(display),
168 Err(_) => unsafe { std::hint::unreachable_unchecked() },
169 }
170 }
171
172 pub fn last_was_empty(&self) -> bool {
177 self.last_was_empty
178 }
179
180 pub fn push_str<D: Display>(&mut self, d: D) {
192 self.buffer.clear();
193 write!(self.buffer, "{d}").unwrap();
194 if self.buffer.is_empty()
195 || (self.no_space_after_empty && self.buffer == " " && self.last_was_empty)
196 {
197 self.last_was_empty = true;
198 } else {
199 self.last_was_empty = false;
200 let end = self.text.last_point();
201 self.text
202 .apply_change(0, Change::str_insert(&self.buffer, end));
203 }
204 }
205
206 pub fn reset_form(&mut self) {
212 let end = self.text.last_point().byte();
213 if let Some((b, last_form)) = self.last_form.take() {
214 self.text.insert_tag(Tagger::basic(), b..end, last_form);
215 }
216 }
217
218 fn push_text(&mut self, text: &Text) {
220 self.last_was_empty = text.is_empty();
221 self.text.insert_text(self.text.last_point(), text);
222 }
223
224 fn push_builder(&mut self, other: &Builder) {
226 self.last_was_empty = other.text.is_empty();
227
228 let offset = self.text.last_point().byte();
229 self.text.insert_text(offset, &other.text);
230 let end = self.text.last_point().byte();
231
232 if let Some((b, id)) = other.last_form
233 && b < other.text.last_point().byte()
234 {
235 self.text.insert_tag(Tagger::basic(), offset + b..end, id);
236 }
237 }
238}
239
240impl Default for Builder {
241 fn default() -> Self {
242 Builder {
243 text: Text::new(),
244 last_form: None,
245 buffer: String::with_capacity(50),
246 last_was_empty: false,
247 no_space_after_empty: false,
248 }
249 }
250}
251
252impl std::fmt::Debug for Builder {
253 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
254 f.debug_struct("Builder")
255 .field("text", &self.text)
256 .finish_non_exhaustive()
257 }
258}
259
260impl From<Builder> for Text {
261 fn from(value: Builder) -> Self {
262 value.build()
263 }
264}
265
266#[derive(Clone)]
268pub enum BuilderPart<'a, D: Display = String, _T = ()> {
269 Text(&'a Text),
278 Builder(&'a Builder),
284 ToString(&'a D),
286 Path(&'a std::path::Path),
288 PathKind(Text),
290 Form(FormTag),
292 Spacer(PhantomData<_T>),
294 Ghost(&'a Ghost),
296 SwapChar(SwapChar),
298}
299
300impl<'a, D: Display, _T> BuilderPart<'a, D, _T> {
301 fn try_to_basic(self) -> Result<BuilderPart<'a>, Self> {
302 match self {
303 BuilderPart::Text(text) => Ok(BuilderPart::Text(text)),
304 BuilderPart::Builder(builder) => Ok(BuilderPart::Builder(builder)),
305 BuilderPart::ToString(_) => Err(self),
306 BuilderPart::Path(path) => Ok(BuilderPart::Path(path)),
307 BuilderPart::PathKind(text) => Ok(BuilderPart::PathKind(text)),
308 BuilderPart::Form(form_id) => Ok(BuilderPart::Form(form_id)),
309 BuilderPart::Spacer(_) => Ok(BuilderPart::Spacer(PhantomData)),
310 BuilderPart::Ghost(ghost) => Ok(BuilderPart::Ghost(ghost)),
311 BuilderPart::SwapChar(replacement) => Ok(BuilderPart::SwapChar(replacement)),
312 }
313 }
314}
315
316pub trait AsBuilderPart<D: Display = String, _T = ()> {
323 fn as_builder_part(&self) -> BuilderPart<'_, D, _T>;
325}
326
327macro_rules! implAsBuilderPart {
328 ($type:ident, $value:ident, $result:expr) => {
329 impl AsBuilderPart for $type {
330 fn as_builder_part(&self) -> BuilderPart<'_> {
331 let $value = self;
332 $result
333 }
334 }
335 };
336}
337
338implAsBuilderPart!(Builder, builder, BuilderPart::Builder(builder));
339implAsBuilderPart!(FormId, form_id, BuilderPart::Form(form_id.to_tag(0)));
340implAsBuilderPart!(FormTag, form_tag, BuilderPart::Form(*form_tag));
341implAsBuilderPart!(Spacer, _spacer, BuilderPart::Spacer(PhantomData));
342implAsBuilderPart!(Ghost, ghost, BuilderPart::Ghost(ghost));
343implAsBuilderPart!(SwapChar, replace, BuilderPart::SwapChar(*replace));
344implAsBuilderPart!(Text, text, BuilderPart::Text(text));
345implAsBuilderPart!(Path, path, BuilderPart::Path(path));
346implAsBuilderPart!(PathKind, path, BuilderPart::PathKind(path.name_txt()));
347
348impl<D: Display> AsBuilderPart<D, D> for D {
349 fn as_builder_part(&self) -> BuilderPart<'_, D, D> {
350 BuilderPart::ToString(self)
351 }
352}
353
354#[macro_export]
360macro_rules! txt {
361 ($($parts:tt)+) => {{
362 #[allow(unused_imports)]
363 use $crate::{
364 __parse_arg__, __parse_form__, __parse_str__, private_exports::format_like
365 };
366
367 let mut builder = $crate::text::Builder::new();
368 let _ = format_like!(
369 __parse_str__,
370 [('{', __parse_arg__, false), ('[', __parse_form__, true)],
371 &mut builder,
372 $($parts)*
373 );
374
375 builder.build()
376 }};
377}
378
379#[macro_export]
380#[doc(hidden)]
381macro_rules! __log__ {
382 ($lvl:expr, $location:expr, $($arg:tt)*) => {{
383 #[allow(unused_must_use)]
384 let text = $crate::text::txt!($($arg)*);
385
386 $crate::context::logs().push_record($crate::context::Record::new(
387 text,
388 $lvl,
389 $location
390 ));
391 }}
392}
393
394#[macro_export]
395#[doc(hidden)]
396macro_rules! __parse_str__ {
397 ($builder:expr, $str:literal) => {{
398 let builder = $builder;
399 builder.push_str($str);
400 builder
401 }};
402}
403
404#[macro_export]
405#[doc(hidden)]
406macro_rules! __parse_arg__ {
407 ($builder:expr,"", $arg:expr) => {{
408 use $crate::text::AsBuilderPart;
409 let builder = $builder;
410 builder.push_builder_part($arg.as_builder_part());
411 builder
412 }};
413 ($builder:expr, $modif:literal, $arg:expr) => {{
414 let builder = $builder;
415 builder.push_str(format!(concat!("{:", $modif, "}"), &$arg));
416 builder
417 }};
418}
419
420#[macro_export]
421#[doc(hidden)]
422macro_rules! __parse_form__ {
423 ($builder:expr, $priority:literal,) => {{
424 use $crate::text::AsBuilderPart;
425 const PRIORITY: u8 = $crate::priority($priority);
426 let builder = $builder;
427 let id = $crate::form::DEFAULT_ID;
428 builder.push_builder_part(id.to_tag(PRIORITY).as_builder_part());
429 builder
430 }};
431 ($builder:expr, $priority:literal, a) => {{
432 use $crate::text::AsBuilderPart;
433 const PRIORITY: u8 = $crate::priority($priority);
434 let builder = $builder;
435 let id = $crate::form::ACCENT_ID;
436 builder.push_builder_part(id.to_tag(PRIORITY).as_builder_part());
437 builder
438 }};
439 ($builder:expr, $priority:literal, $($form:tt)*) => {{
440 use $crate::text::AsBuilderPart;
441 const PRIORITY: u8 = $crate::priority($priority);
442 let builder = $builder;
443 let id = $crate::form::id_of!(concat!($(stringify!($form)),*));
444 builder.push_builder_part(id.to_tag(PRIORITY).as_builder_part());
445 builder
446 }};
447}