1use std::{
12 fmt::{Display, Write},
13 marker::PhantomData,
14 path::Path,
15};
16
17use super::{Change, Ns, Text};
18use crate::{
19 buffer::PathKind,
20 form::FormId,
21 text::{FormTag, Inlay, Mask, Spacer},
22};
23
24#[derive(Clone)]
60pub struct Builder {
61 text: Text,
62 last_form: Option<(usize, FormTag)>,
63 last_mask: Option<(usize, Mask)>,
64 buffer: String,
65 last_was_empty: bool,
66 pub no_space_after_empty: bool,
68}
69
70impl Builder {
71 pub fn new() -> Self {
76 Self::default()
77 }
78
79 pub fn build(mut self) -> Text {
91 if let Some((b, id)) = self.last_form
92 && b < self.text.last_point().byte()
93 {
94 self.text.insert_tag(Ns::basic(), b.., id);
95 }
96
97 if let Some((b, id)) = self.last_mask
98 && b < self.text.last_point().byte()
99 {
100 self.text.insert_tag(Ns::basic(), b.., id);
101 }
102
103 self.text
104 }
105
106 pub fn build_no_double_nl(self) -> Text {
116 let mut text = self.build();
117 if let Some(last_last_byte) = text.len().checked_sub(2)
118 && let Some(strs) = text.get(last_last_byte..)
119 && strs == "\n\n"
120 {
121 text.replace_range(last_last_byte..last_last_byte + 1, "");
122 }
123
124 text
125 }
126
127 pub fn push<D: Display, _T>(&mut self, part: impl AsBuilderPart<D, _T>) {
136 self.push_builder_part(part.as_builder_part());
137 }
138
139 #[doc(hidden)]
140 pub fn push_builder_part<_T>(&mut self, part: BuilderPart<impl Display, _T>) {
141 fn push_simple(builder: &mut Builder, part: BuilderPart) {
142 use BuilderPart as BP;
143
144 let end = builder.text.last_point().byte();
145 let ns = Ns::basic();
146
147 match part {
148 BP::Text(text) => builder.push_text(text),
149 BP::Builder(other) => builder.push_builder(other),
150 BP::Path(path) => builder.push_str(path.to_string_lossy()),
151 BP::PathKind(text) => builder.push_text(&text),
152 BP::Form(tag) => {
153 let last_form = if tag == crate::form::DEFAULT_ID.to_tag(0) {
154 builder.last_form.take()
155 } else {
156 builder.last_form.replace((end, tag))
157 };
158
159 if let Some((b, tag)) = last_form
160 && b < end
161 {
162 builder.text.insert_tag(ns, b..end, tag);
163 }
164 }
165 BP::Spacer(_) => builder.text.insert_tag(ns, end, Spacer),
166 BP::Inlay(ghost) => builder.text.insert_tag(ns, end, ghost.clone()),
167 BP::ToString(_) => unsafe { std::hint::unreachable_unchecked() },
168 BP::Mask(mask) => {
169 let last_form = if mask == Mask::no_mask() {
170 builder.last_mask.take()
171 } else {
172 builder.last_mask.replace((end, mask))
173 };
174
175 if let Some((b, tag)) = last_form
176 && b < end
177 {
178 builder.text.insert_tag(ns, b..end, tag);
179 }
180 }
181 }
182 }
183
184 match part.try_to_basic() {
185 Ok(part_ref) => push_simple(self, part_ref),
186 Err(BuilderPart::ToString(display)) => self.push_str(display),
187 Err(_) => unsafe { std::hint::unreachable_unchecked() },
188 }
189 }
190
191 pub fn last_was_empty(&self) -> bool {
196 self.last_was_empty
197 }
198
199 pub fn push_str<D: Display>(&mut self, d: D) {
211 self.buffer.clear();
212 write!(self.buffer, "{d}").unwrap();
213 if self.buffer.is_empty()
214 || (self.no_space_after_empty && self.buffer == " " && self.last_was_empty)
215 {
216 self.last_was_empty = true;
217 } else {
218 self.last_was_empty = false;
219 let end = self.text.last_point();
220 self.text
221 .apply_change(0, Change::str_insert(&self.buffer, end));
222 }
223 }
224
225 pub fn reset_form(&mut self) {
231 let end = self.text.last_point().byte();
232 if let Some((b, last_form)) = self.last_form.take() {
233 self.text.insert_tag(Ns::basic(), b..end, last_form);
234 }
235 }
236
237 fn push_text(&mut self, text: &Text) {
239 self.last_was_empty = text.is_empty();
240 self.text.insert_text(self.text.last_point(), text);
241 }
242
243 fn push_builder(&mut self, other: &Builder) {
245 self.last_was_empty = other.text.is_empty();
246
247 let offset = self.text.last_point().byte();
248 self.text.insert_text(offset, &other.text);
249 let end = self.text.last_point().byte();
250
251 if let Some((b, id)) = other.last_form
252 && b < other.text.last_point().byte()
253 {
254 self.text.insert_tag(Ns::basic(), offset + b..end, id);
255 }
256 }
257}
258
259impl Default for Builder {
260 fn default() -> Self {
261 Builder {
262 text: Text::new(),
263 last_form: None,
264 last_mask: None,
265 buffer: String::with_capacity(50),
266 last_was_empty: false,
267 no_space_after_empty: false,
268 }
269 }
270}
271
272impl std::fmt::Debug for Builder {
273 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
274 f.debug_struct("Builder")
275 .field("text", &self.text)
276 .finish_non_exhaustive()
277 }
278}
279
280impl From<Builder> for Text {
281 fn from(value: Builder) -> Self {
282 value.build()
283 }
284}
285
286#[derive(Clone)]
288pub enum BuilderPart<'a, D: Display = String, _T = ()> {
289 Text(&'a Text),
298 Builder(&'a Builder),
304 ToString(&'a D),
306 Path(&'a std::path::Path),
308 PathKind(Text),
310 Form(FormTag),
312 Spacer(PhantomData<_T>),
314 Inlay(&'a Inlay),
316 Mask(Mask),
320}
321
322impl<'a, D: Display, _T> BuilderPart<'a, D, _T> {
323 fn try_to_basic(self) -> Result<BuilderPart<'a>, Self> {
324 match self {
325 Self::Text(text) => Ok(BuilderPart::Text(text)),
326 Self::Builder(builder) => Ok(BuilderPart::Builder(builder)),
327 Self::ToString(_) => Err(self),
328 Self::Path(path) => Ok(BuilderPart::Path(path)),
329 Self::PathKind(text) => Ok(BuilderPart::PathKind(text)),
330 Self::Form(form_id) => Ok(BuilderPart::Form(form_id)),
331 Self::Spacer(_) => Ok(BuilderPart::Spacer(PhantomData)),
332 Self::Inlay(ghost) => Ok(BuilderPart::Inlay(ghost)),
333 Self::Mask(mask) => Ok(BuilderPart::Mask(mask)),
334 }
335 }
336}
337
338pub trait AsBuilderPart<D: Display = String, _T = ()> {
345 fn as_builder_part(&self) -> BuilderPart<'_, D, _T>;
347}
348
349macro_rules! implAsBuilderPart {
350 ($type:ident, $value:ident, $result:expr) => {
351 impl AsBuilderPart for $type {
352 fn as_builder_part(&self) -> BuilderPart<'_> {
353 let $value = self;
354 $result
355 }
356 }
357 };
358}
359
360implAsBuilderPart!(Builder, builder, BuilderPart::Builder(builder));
361implAsBuilderPart!(FormId, form_id, BuilderPart::Form(form_id.to_tag(0)));
362implAsBuilderPart!(FormTag, form_tag, BuilderPart::Form(*form_tag));
363implAsBuilderPart!(Spacer, _spacer, BuilderPart::Spacer(PhantomData));
364implAsBuilderPart!(Inlay, ghost, BuilderPart::Inlay(ghost));
365implAsBuilderPart!(Text, text, BuilderPart::Text(text));
366implAsBuilderPart!(Path, path, BuilderPart::Path(path));
367implAsBuilderPart!(PathKind, path, BuilderPart::PathKind(path.name_txt()));
368implAsBuilderPart!(Mask, mask, BuilderPart::Mask(*mask));
369
370impl<D: Display> AsBuilderPart<D, D> for D {
371 fn as_builder_part(&self) -> BuilderPart<'_, D, D> {
372 BuilderPart::ToString(self)
373 }
374}
375
376#[macro_export]
382macro_rules! txt {
383 ($($parts:tt)+) => {{
384 #[allow(unused_imports)]
385 use $crate::{
386 __parse_arg__, __parse_form__, __parse_str__, private_exports::format_like
387 };
388
389 let mut builder = $crate::text::Builder::new();
390 let _ = format_like!(
391 __parse_str__,
392 [('{', __parse_arg__, false), ('[', __parse_form__, true)],
393 &mut builder,
394 $($parts)*
395 );
396
397 builder.build()
398 }};
399}
400
401#[macro_export]
402#[doc(hidden)]
403macro_rules! __log__ {
404 ($lvl:expr, $location:expr, $($arg:tt)*) => {{
405 #[allow(unused_must_use)]
406 let text = $crate::text::txt!($($arg)*);
407
408 $crate::context::logs().push_record($crate::context::Record::new(
409 text,
410 $lvl,
411 $location
412 ));
413 }}
414}
415
416#[macro_export]
417#[doc(hidden)]
418macro_rules! __parse_str__ {
419 ($builder:expr, $str:literal) => {{
420 let builder = $builder;
421 builder.push_str($str);
422 builder
423 }};
424}
425
426#[macro_export]
427#[doc(hidden)]
428macro_rules! __parse_arg__ {
429 ($builder:expr,"", $arg:expr) => {{
430 use $crate::text::AsBuilderPart;
431 let builder = $builder;
432 builder.push_builder_part($arg.as_builder_part());
433 builder
434 }};
435 ($builder:expr, $modif:literal, $arg:expr) => {{
436 let builder = $builder;
437 builder.push_str(format!(concat!("{:", $modif, "}"), &$arg));
438 builder
439 }};
440}
441
442#[macro_export]
443#[doc(hidden)]
444macro_rules! __parse_form__ {
445 ($builder:expr, $priority:literal,) => {{
446 use $crate::text::AsBuilderPart;
447 const PRIORITY: u8 = $crate::priority($priority);
448 let builder = $builder;
449 let id = $crate::form::DEFAULT_ID;
450 builder.push_builder_part(id.to_tag(PRIORITY).as_builder_part());
451 builder
452 }};
453 ($builder:expr, $priority:literal, a) => {{
454 use $crate::text::AsBuilderPart;
455 const PRIORITY: u8 = $crate::priority($priority);
456 let builder = $builder;
457 let id = $crate::form::ACCENT_ID;
458 builder.push_builder_part(id.to_tag(PRIORITY).as_builder_part());
459 builder
460 }};
461 ($builder:expr, $priority:literal, $($form:tt)*) => {{
462 use $crate::text::AsBuilderPart;
463 const PRIORITY: u8 = $crate::priority($priority);
464 let builder = $builder;
465 let id = $crate::form::id_of!(concat!($(stringify!($form)),*));
466 builder.push_builder_part(id.to_tag(PRIORITY).as_builder_part());
467 builder
468 }};
469}