dprint_core/formatting/
print_items.rs

1use std::borrow::Cow;
2use std::cell::UnsafeCell;
3use std::mem;
4use std::rc::Rc;
5
6use super::condition_resolvers;
7use super::printer::Printer;
8use super::thread_state;
9
10#[derive(Default)]
11pub struct PrintItems {
12  pub(super) first_node: Option<PrintItemPath>,
13  last_node: Option<PrintItemPath>,
14}
15
16impl PrintItems {
17  pub fn new() -> Self {
18    Self {
19      first_node: None,
20      last_node: None,
21    }
22  }
23
24  pub fn into_rc_path(self) -> Option<PrintItemPath> {
25    self.first_node
26  }
27
28  pub fn push_item(&mut self, item: PrintItem) {
29    self.push_item_internal(item);
30  }
31
32  #[inline]
33  fn push_item_internal(&mut self, item: PrintItem) {
34    let node = thread_state::with_bump_allocator(|bump| bump.alloc_print_node_cell(PrintNodeCell::new(item)));
35    if let Some(first_node) = &self.first_node {
36      let new_last_node = node.get_last_next().unwrap_or(node);
37      self.last_node.as_ref().unwrap_or(first_node).set_next(Some(node));
38      self.last_node = Some(new_last_node);
39    } else {
40      self.last_node = node.get_last_next();
41      self.first_node = Some(node);
42    }
43  }
44}
45
46impl PrintItems {
47  pub fn extend(&mut self, items: PrintItems) {
48    if let Some(first_node) = items.first_node {
49      if let Some(current_first_node) = &self.first_node {
50        self.last_node.as_ref().unwrap_or(current_first_node).set_next(Some(first_node));
51
52        if items.last_node.is_some() {
53          self.last_node = items.last_node;
54        } else if items.first_node.is_some() {
55          self.last_node = items.first_node;
56        }
57      } else {
58        self.first_node = items.first_node;
59        self.last_node = items.last_node;
60      }
61    }
62  }
63
64  /// Pushes a string that will have its width computed at runtime.
65  ///
66  /// Instead of using this, prefer to use `push_sc` where possible
67  /// with the `sc!` macro from the dprint-core-macros crate.
68  pub fn push_str_runtime_width_computed(&mut self, item: &'static str) {
69    self.push_cow_string(Cow::Borrowed(item))
70  }
71
72  pub fn push_force_current_line_indentation(&mut self) {
73    const STR_EMPTY: StringContainer = StringContainer { text: "", char_count: 0 };
74    self.push_item_internal(PrintItem::String(&STR_EMPTY))
75  }
76
77  pub fn push_space(&mut self) {
78    const STR_SPACE: StringContainer = StringContainer { text: " ", char_count: 1 };
79    self.push_item_internal(PrintItem::String(&STR_SPACE))
80  }
81
82  /// Pushes a string that's had its width computed at compile time.
83  pub fn push_sc(&mut self, item: &'static StringContainer) {
84    self.push_item_internal(PrintItem::String(item))
85  }
86
87  pub fn push_string(&mut self, item: String) {
88    self.push_cow_string(Cow::Owned(item))
89  }
90
91  fn push_cow_string(&mut self, item: Cow<'static, str>) {
92    let string_container = thread_state::with_bump_allocator(|bump| bump.alloc_string(item));
93    self.push_item_internal(PrintItem::String(string_container));
94  }
95
96  pub fn push_condition(&mut self, condition: Condition) {
97    let condition = thread_state::with_bump_allocator(|bump| bump.alloc_condition(condition));
98    self.push_item_internal(PrintItem::Condition(condition));
99  }
100
101  pub fn push_info(&mut self, info: impl Into<Info>) {
102    self.push_item_internal(PrintItem::Info(info.into()));
103  }
104
105  pub fn push_line_and_column(&mut self, line_and_col: LineAndColumn) {
106    self.push_info(line_and_col.line);
107    self.push_info(line_and_col.column);
108  }
109
110  pub fn push_anchor(&mut self, anchor: impl Into<Anchor>) {
111    self.push_item_internal(PrintItem::Anchor(anchor.into()));
112  }
113
114  pub fn push_reevaluation(&mut self, condition_reevaluation: ConditionReevaluation) {
115    self.push_item_internal(PrintItem::ConditionReevaluation(condition_reevaluation));
116  }
117
118  pub fn push_signal(&mut self, signal: Signal) {
119    self.push_item_internal(PrintItem::Signal(signal));
120  }
121
122  pub fn push_path(&mut self, path: PrintItemPath) {
123    self.push_item_internal(PrintItem::RcPath(path))
124  }
125
126  pub fn push_optional_path(&mut self, path: Option<PrintItemPath>) {
127    if let Some(path) = path {
128      self.push_path(path);
129    }
130  }
131
132  pub fn is_empty(&self) -> bool {
133    self.first_node.is_none()
134  }
135
136  // todo: clean this up
137  #[cfg(debug_assertions)]
138  pub fn get_as_text(&self) -> String {
139    return if let Some(first_node) = &self.first_node {
140      get_items_as_text(first_node, String::from(""))
141    } else {
142      String::new()
143    };
144
145    fn get_items_as_text(items: PrintItemPath, indent_text: String) -> String {
146      let mut text = String::new();
147      for item in PrintItemsIterator::new(items) {
148        match item {
149          PrintItem::Signal(signal) => text.push_str(&get_line(format!("Signal::{:?}", signal), &indent_text)),
150          PrintItem::Condition(condition) => {
151            text.push_str(&get_line(format!("Condition: {}", condition.name), &indent_text));
152            if let Some(true_path) = &condition.true_path {
153              text.push_str(&get_line(String::from("  true:"), &indent_text));
154              text.push_str(&get_items_as_text(true_path, format!("{}    ", &indent_text)));
155            }
156            if let Some(false_path) = &condition.false_path {
157              text.push_str(&get_line(String::from("  false:"), &indent_text));
158              text.push_str(&get_items_as_text(false_path, format!("{}    ", &indent_text)));
159            }
160          }
161          PrintItem::String(str_text) => text.push_str(&get_line(format!("`{}`", str_text.text), &indent_text)),
162          PrintItem::RcPath(path) => text.push_str(&get_items_as_text(path, indent_text.clone())),
163          PrintItem::Anchor(Anchor::LineNumber(line_number_anchor)) => {
164            text.push_str(&get_line(format!("Line number anchor: {}", line_number_anchor.name()), &indent_text))
165          }
166          PrintItem::Info(info) => {
167            let (desc, name) = match info {
168              Info::LineNumber(info) => ("Line number", info.name()),
169              Info::ColumnNumber(info) => ("Column number", info.name()),
170              Info::IsStartOfLine(info) => ("Is start of line", info.name()),
171              Info::IndentLevel(info) => ("Indent level", info.name()),
172              Info::LineStartColumnNumber(info) => ("Line start column number", info.name()),
173              Info::LineStartIndentLevel(info) => ("Line start indent level", info.name()),
174            };
175            text.push_str(&get_line(format!("{}: {}", desc, name), &indent_text))
176          }
177          PrintItem::ConditionReevaluation(reevaluation) => text.push_str(&get_line(format!("Condition reevaluation: {}", reevaluation.name()), &indent_text)),
178        }
179      }
180
181      return text;
182
183      fn get_line(text: String, indent_text: &str) -> String {
184        format!("{}{}\n", indent_text, text)
185      }
186    }
187  }
188
189  pub fn iter(&self) -> PrintItemsIterator {
190    PrintItemsIterator { node: self.first_node }
191  }
192}
193
194pub struct PrintItemsIterator {
195  node: Option<PrintItemPath>,
196}
197
198impl PrintItemsIterator {
199  pub fn new(path: PrintItemPath) -> Self {
200    Self { node: Some(path) }
201  }
202}
203
204impl Iterator for PrintItemsIterator {
205  type Item = PrintItem;
206
207  fn next(&mut self) -> Option<PrintItem> {
208    let node = self.node.take();
209
210    match node {
211      Some(node) => {
212        self.node = node.get_next();
213        Some(node.get_item())
214      }
215      None => None,
216    }
217  }
218}
219
220impl From<&'static str> for PrintItems {
221  fn from(value: &'static str) -> Self {
222    let mut items = PrintItems::new();
223    items.push_str_runtime_width_computed(value);
224    items
225  }
226}
227
228impl From<String> for PrintItems {
229  fn from(value: String) -> Self {
230    let mut items = PrintItems::new();
231    items.push_string(value);
232    items
233  }
234}
235
236impl From<Condition> for PrintItems {
237  fn from(value: Condition) -> Self {
238    let mut items = PrintItems::new();
239    items.push_condition(value);
240    items
241  }
242}
243
244impl From<Signal> for PrintItems {
245  fn from(value: Signal) -> Self {
246    let mut items = PrintItems::new();
247    items.push_signal(value);
248    items
249  }
250}
251
252impl From<PrintItemPath> for PrintItems {
253  fn from(value: PrintItemPath) -> Self {
254    let mut items = PrintItems::new();
255    items.push_path(value);
256    items
257  }
258}
259
260impl<T> From<Option<T>> for PrintItems
261where
262  PrintItems: From<T>,
263{
264  fn from(value: Option<T>) -> Self {
265    value.map(PrintItems::from).unwrap_or_default()
266  }
267}
268
269/** Tracing */
270
271#[cfg(feature = "tracing")]
272#[derive(serde::Serialize)]
273#[serde(rename_all = "camelCase")]
274pub struct Trace {
275  /// The relative time of the trace from the start of printing in nanoseconds.
276  pub nanos: u128,
277  pub print_node_id: u32,
278  #[serde(skip_serializing_if = "Option::is_none")]
279  pub writer_node_id: Option<u32>,
280}
281
282#[cfg(feature = "tracing")]
283#[derive(serde::Serialize)]
284#[serde(rename_all = "camelCase")]
285pub struct TraceWriterNode {
286  pub writer_node_id: u32,
287  #[serde(skip_serializing_if = "Option::is_none")]
288  pub previous_node_id: Option<u32>,
289  pub text: String,
290}
291
292#[cfg(feature = "tracing")]
293#[derive(serde::Serialize)]
294#[serde(rename_all = "camelCase")]
295pub struct TracePrintNode {
296  pub print_node_id: u32,
297  #[serde(skip_serializing_if = "Option::is_none")]
298  pub next_print_node_id: Option<u32>,
299  pub print_item: TracePrintItem,
300}
301
302#[cfg(feature = "tracing")]
303#[derive(serde::Serialize)]
304#[serde(tag = "kind", content = "content", rename_all = "camelCase")]
305pub enum TracePrintItem {
306  String(String),
307  Condition(TraceCondition),
308  Info(TraceInfo),
309  Signal(Signal),
310  /// Identifier to the print node.
311  RcPath(u32),
312  Anchor(TraceLineNumberAnchor),
313  ConditionReevaluation(TraceConditionReevaluation),
314}
315
316#[cfg(feature = "tracing")]
317#[derive(serde::Serialize)]
318#[serde(tag = "kind", content = "content", rename_all = "camelCase")]
319pub enum TraceInfo {
320  LineNumber(TraceInfoInner),
321  ColumnNumber(TraceInfoInner),
322  IsStartOfLine(TraceInfoInner),
323  IndentLevel(TraceInfoInner),
324  LineStartColumnNumber(TraceInfoInner),
325  LineStartIndentLevel(TraceInfoInner),
326}
327
328#[cfg(feature = "tracing")]
329#[derive(serde::Serialize)]
330#[serde(rename_all = "camelCase")]
331pub struct TraceInfoInner {
332  pub info_id: u32,
333  pub name: String,
334}
335
336#[cfg(feature = "tracing")]
337impl TraceInfoInner {
338  pub fn new(info_id: u32, name: &str) -> Self {
339    Self {
340      info_id,
341      name: name.to_string(),
342    }
343  }
344}
345
346#[cfg(feature = "tracing")]
347#[derive(serde::Serialize)]
348#[serde(rename_all = "camelCase")]
349pub struct TraceLineNumberAnchor {
350  pub anchor_id: u32,
351  pub name: String,
352}
353
354#[cfg(feature = "tracing")]
355#[derive(serde::Serialize)]
356#[serde(rename_all = "camelCase")]
357pub struct TraceConditionReevaluation {
358  pub condition_id: u32,
359  pub name: String,
360}
361
362#[cfg(feature = "tracing")]
363#[derive(serde::Serialize)]
364#[serde(rename_all = "camelCase")]
365pub struct TraceCondition {
366  pub condition_id: u32,
367  pub name: String,
368  pub is_stored: bool,
369  pub store_save_point: bool,
370  #[serde(skip_serializing_if = "Option::is_none")]
371  /// Identifier to the true path print node.
372  pub true_path: Option<u32>,
373  #[serde(skip_serializing_if = "Option::is_none")]
374  /// Identifier to the false path print node.
375  pub false_path: Option<u32>,
376}
377
378pub struct PrintNode {
379  pub(super) next: Option<PrintItemPath>,
380  pub(super) item: PrintItem,
381  #[cfg(feature = "tracing")]
382  pub print_node_id: u32,
383}
384
385impl PrintNode {
386  fn new(item: PrintItem) -> PrintNode {
387    PrintNode {
388      item,
389      next: None,
390      #[cfg(feature = "tracing")]
391      print_node_id: thread_state::next_print_node_id(),
392    }
393  }
394
395  fn set_next(&mut self, new_next: Option<PrintItemPath>) {
396    let past_next = mem::replace(&mut self.next, new_next);
397
398    if let Some(past_next) = past_next {
399      if let Some(new_next) = new_next {
400        new_next.get_last_next().unwrap_or(new_next).set_next(Some(past_next));
401      }
402    }
403  }
404}
405
406/// A fast implementation of RefCell<PrintNode> that avoids runtime checks on borrows.
407pub struct PrintNodeCell {
408  value: UnsafeCell<PrintNode>,
409}
410
411impl PrintNodeCell {
412  pub(super) fn new(item: PrintItem) -> PrintNodeCell {
413    PrintNodeCell {
414      value: UnsafeCell::new(PrintNode::new(item)),
415    }
416  }
417
418  #[inline]
419  pub(super) fn get_item(&self) -> PrintItem {
420    unsafe { (*self.value.get()).item.clone() }
421  }
422
423  #[inline]
424  pub(super) fn get_next(&self) -> Option<PrintItemPath> {
425    unsafe { (*self.value.get()).next }
426  }
427
428  #[inline]
429  pub(super) fn set_next(&self, new_next: Option<PrintItemPath>) {
430    unsafe {
431      (*self.value.get()).set_next(new_next);
432    }
433  }
434
435  #[inline]
436  pub(super) fn get_last_next(&self) -> Option<PrintItemPath> {
437    let mut current = self.get_next();
438    loop {
439      if let Some(last) = &current {
440        if let Some(next) = last.get_next() {
441          current.replace(next);
442          continue;
443        }
444      }
445      break;
446    }
447
448    current
449  }
450
451  #[cfg(feature = "tracing")]
452  pub(super) fn get_node_id(&self) -> u32 {
453    unsafe { (*self.get_node()).print_node_id }
454  }
455
456  /// Gets the node unsafely. Be careful when using this and ensure no mutation is
457  /// happening during the borrow.
458  #[inline]
459  pub(super) unsafe fn get_node(&self) -> *mut PrintNode {
460    self.value.get()
461  }
462
463  #[inline]
464  pub fn take_next(self) -> Option<PrintItemPath> {
465    self.value.into_inner().next.take()
466  }
467}
468
469pub type PrintItemPath = UnsafePrintLifetime<PrintNodeCell>;
470
471/// This lifetime value is a lie that is not represented or enforced by the compiler.
472/// What actually happens is the reference will remain active until the print
473/// items are printed. At that point, it's unsafe to use them anymore.
474///
475/// To get around this unsafeness, the API would have to be sacrificed by passing
476/// around an object that wraps an arena. Perhaps that will be the way going forward
477/// in the future, but for now this was an easy way to get the big performance
478/// boost from an arena without changing the API much.
479pub(super) type UnsafePrintLifetime<T> = &'static T;
480
481/* Print item and kinds */
482
483/// The different items the printer could encounter.
484#[derive(Clone)]
485pub enum PrintItem {
486  String(UnsafePrintLifetime<StringContainer>),
487  Condition(UnsafePrintLifetime<Condition>),
488  Signal(Signal),
489  RcPath(PrintItemPath),
490  Anchor(Anchor),
491  Info(Info),
492  ConditionReevaluation(ConditionReevaluation),
493}
494
495#[derive(Clone, PartialEq, Eq, Copy, Debug, serde::Serialize)]
496pub enum Signal {
497  /// Signal that a new line should occur based on the printer settings.
498  NewLine,
499  /// Signal that a tab should occur based on the printer settings.
500  Tab,
501  /// Signal that the current location could be a newline when
502  /// exceeding the line width.
503  PossibleNewLine,
504  /// Signal that the current location should be a space, but
505  /// could be a newline if exceeding the line width.
506  SpaceOrNewLine,
507  /// Expect the next character to be a newline. If it's not, force a newline.
508  ExpectNewLine,
509  /// Queue a start indent to be set after the next written item.
510  QueueStartIndent,
511  /// Signal the start of a section that should be indented.
512  StartIndent,
513  /// Signal the end of a section that should be indented.
514  FinishIndent,
515  /// Signal the start of a group of print items that have a lower precedence
516  /// for being broken up with a newline for exceeding the line width.
517  StartNewLineGroup,
518  /// Signal the end of a newline group.
519  FinishNewLineGroup,
520  /// Signal that a single indent should occur based on the printer settings.
521  SingleIndent,
522  /// Signal to the printer that it should stop using indentation.
523  StartIgnoringIndent,
524  /// Signal to the printer that it should start using indentation again.
525  FinishIgnoringIndent,
526  /// Signal to the printer that it shouldn't print any new lines.
527  StartForceNoNewLines,
528  /// Signal to the printer that it should finish not printing any new lines.
529  FinishForceNoNewLines,
530  /// Signal that a space should occur if not trailing.
531  SpaceIfNotTrailing,
532}
533
534#[derive(Clone)]
535pub enum Anchor {
536  LineNumber(LineNumberAnchor),
537}
538
539impl From<LineNumberAnchor> for Anchor {
540  fn from(anchor: LineNumberAnchor) -> Self {
541    Anchor::LineNumber(anchor)
542  }
543}
544
545/// Handles updating the position of a future resolved line
546/// number if the anchor changes.
547#[derive(Clone)]
548pub struct LineNumberAnchor {
549  id: u32,
550  line_number: LineNumber,
551}
552
553impl LineNumberAnchor {
554  pub fn new(line_number: LineNumber) -> Self {
555    Self {
556      id: thread_state::next_line_number_anchor_id(),
557      line_number,
558    }
559  }
560
561  #[inline]
562  pub fn unique_id(&self) -> u32 {
563    self.id
564  }
565
566  #[inline]
567  pub fn line_number_id(&self) -> u32 {
568    self.line_number.id
569  }
570
571  #[inline]
572  pub fn name(&self) -> &'static str {
573    self.line_number.name()
574  }
575}
576
577#[derive(Clone, PartialEq, Eq, Copy, Debug)]
578pub enum Info {
579  LineNumber(LineNumber),
580  ColumnNumber(ColumnNumber),
581  IsStartOfLine(IsStartOfLine),
582  IndentLevel(IndentLevel),
583  LineStartColumnNumber(LineStartColumnNumber),
584  LineStartIndentLevel(LineStartIndentLevel),
585}
586
587impl From<LineNumber> for Info {
588  fn from(info: LineNumber) -> Self {
589    Info::LineNumber(info)
590  }
591}
592
593impl From<ColumnNumber> for Info {
594  fn from(info: ColumnNumber) -> Self {
595    Info::ColumnNumber(info)
596  }
597}
598
599impl From<IsStartOfLine> for Info {
600  fn from(info: IsStartOfLine) -> Self {
601    Info::IsStartOfLine(info)
602  }
603}
604
605impl From<IndentLevel> for Info {
606  fn from(info: IndentLevel) -> Self {
607    Info::IndentLevel(info)
608  }
609}
610
611impl From<LineStartColumnNumber> for Info {
612  fn from(info: LineStartColumnNumber) -> Self {
613    Info::LineStartColumnNumber(info)
614  }
615}
616
617impl From<LineStartIndentLevel> for Info {
618  fn from(info: LineStartIndentLevel) -> Self {
619    Info::LineStartIndentLevel(info)
620  }
621}
622
623/// Helper IR that holds line and column number IR.
624#[derive(Clone, PartialEq, Eq, Copy, Debug)]
625pub struct LineAndColumn {
626  pub line: LineNumber,
627  pub column: ColumnNumber,
628}
629
630impl LineAndColumn {
631  pub fn new(name: &'static str) -> Self {
632    Self {
633      line: LineNumber::new(name),
634      column: ColumnNumber::new(name),
635    }
636  }
637}
638
639#[derive(Clone, PartialEq, Eq, Copy, Debug)]
640pub struct LineNumber {
641  id: u32,
642  /// Name for debugging purposes.
643  #[cfg(debug_assertions)]
644  name: &'static str,
645}
646
647impl LineNumber {
648  pub fn new(_name: &'static str) -> Self {
649    Self {
650      id: thread_state::next_line_number_id(),
651      #[cfg(debug_assertions)]
652      name: _name,
653    }
654  }
655
656  #[inline]
657  pub fn unique_id(&self) -> u32 {
658    self.id
659  }
660
661  #[inline]
662  pub fn name(&self) -> &'static str {
663    #[cfg(debug_assertions)]
664    return self.name;
665    #[cfg(not(debug_assertions))]
666    return "line_number";
667  }
668}
669
670#[derive(Clone, PartialEq, Eq, Copy, Debug)]
671pub struct ColumnNumber {
672  id: u32,
673  /// Name for debugging purposes.
674  #[cfg(debug_assertions)]
675  name: &'static str,
676}
677
678impl ColumnNumber {
679  pub fn new(_name: &'static str) -> Self {
680    Self {
681      id: thread_state::next_column_number_id(),
682      #[cfg(debug_assertions)]
683      name: _name,
684    }
685  }
686
687  #[inline]
688  pub fn unique_id(&self) -> u32 {
689    self.id
690  }
691
692  #[inline]
693  pub fn name(&self) -> &'static str {
694    #[cfg(debug_assertions)]
695    return self.name;
696    #[cfg(not(debug_assertions))]
697    return "column_number";
698  }
699}
700
701#[derive(Clone, PartialEq, Eq, Copy, Debug)]
702pub struct IsStartOfLine {
703  id: u32,
704  /// Name for debugging purposes.
705  #[cfg(debug_assertions)]
706  name: &'static str,
707}
708
709impl IsStartOfLine {
710  pub fn new(_name: &'static str) -> Self {
711    Self {
712      id: thread_state::next_is_start_of_line_id(),
713      #[cfg(debug_assertions)]
714      name: _name,
715    }
716  }
717
718  #[inline]
719  pub fn unique_id(&self) -> u32 {
720    self.id
721  }
722
723  #[inline]
724  pub fn name(&self) -> &'static str {
725    #[cfg(debug_assertions)]
726    return self.name;
727    #[cfg(not(debug_assertions))]
728    return "is_start_of_line";
729  }
730}
731
732#[derive(Clone, PartialEq, Eq, Copy, Debug)]
733pub struct LineStartColumnNumber {
734  id: u32,
735  /// Name for debugging purposes.
736  #[cfg(debug_assertions)]
737  name: &'static str,
738}
739
740impl LineStartColumnNumber {
741  pub fn new(_name: &'static str) -> Self {
742    Self {
743      id: thread_state::next_line_start_column_number_id(),
744      #[cfg(debug_assertions)]
745      name: _name,
746    }
747  }
748
749  #[inline]
750  pub fn unique_id(&self) -> u32 {
751    self.id
752  }
753
754  #[inline]
755  pub fn name(&self) -> &'static str {
756    #[cfg(debug_assertions)]
757    return self.name;
758    #[cfg(not(debug_assertions))]
759    return "line_start_column_number";
760  }
761}
762
763#[derive(Clone, PartialEq, Eq, Copy, Debug)]
764pub struct IndentLevel {
765  id: u32,
766  /// Name for debugging purposes.
767  #[cfg(debug_assertions)]
768  name: &'static str,
769}
770
771impl IndentLevel {
772  pub fn new(_name: &'static str) -> Self {
773    Self {
774      id: thread_state::next_indent_level_id(),
775      #[cfg(debug_assertions)]
776      name: _name,
777    }
778  }
779
780  #[inline]
781  pub fn unique_id(&self) -> u32 {
782    self.id
783  }
784
785  #[inline]
786  pub fn name(&self) -> &'static str {
787    #[cfg(debug_assertions)]
788    return self.name;
789    #[cfg(not(debug_assertions))]
790    return "indent_level";
791  }
792}
793
794#[derive(Clone, PartialEq, Eq, Copy, Debug)]
795pub struct LineStartIndentLevel {
796  id: u32,
797  /// Name for debugging purposes.
798  #[cfg(debug_assertions)]
799  name: &'static str,
800}
801
802impl LineStartIndentLevel {
803  pub fn new(_name: &'static str) -> Self {
804    Self {
805      id: thread_state::next_line_start_indent_level_id(),
806      #[cfg(debug_assertions)]
807      name: _name,
808    }
809  }
810
811  #[inline]
812  pub fn unique_id(&self) -> u32 {
813    self.id
814  }
815
816  #[inline]
817  pub fn name(&self) -> &'static str {
818    #[cfg(debug_assertions)]
819    return self.name;
820    #[cfg(not(debug_assertions))]
821    return "line_start_indent_level";
822  }
823}
824
825/// Used to re-evaluate a condition.
826#[derive(Clone, Copy, PartialEq, Eq, Debug)]
827pub struct ConditionReevaluation {
828  pub(crate) condition_reevaluation_id: u32,
829  pub(crate) condition_id: u32,
830  /// Name for debugging purposes.
831  #[cfg(debug_assertions)]
832  name: &'static str,
833}
834
835impl ConditionReevaluation {
836  pub(crate) fn new(_name: &'static str, condition_id: u32) -> Self {
837    ConditionReevaluation {
838      condition_reevaluation_id: thread_state::next_condition_reevaluation_id(),
839      condition_id,
840      #[cfg(debug_assertions)]
841      name: _name,
842    }
843  }
844
845  pub fn name(&self) -> &'static str {
846    #[cfg(debug_assertions)]
847    return self.name;
848    #[cfg(not(debug_assertions))]
849    return "condition_reevaluation";
850  }
851}
852
853/// Conditionally print items based on a condition.
854///
855/// These conditions are extremely flexible and can even be resolved based on
856/// information found later on in the file.
857#[derive(Clone)]
858pub struct Condition {
859  /// Unique identifier.
860  id: u32,
861  /// Name for debugging purposes.
862  #[cfg(debug_assertions)]
863  name: &'static str,
864  /// If a reference has been created for the condition via `create_reference()`. If so, the printer
865  /// will store the condition and it will be retrievable via a condition resolver.
866  pub(super) is_stored: bool,
867  pub(super) store_save_point: bool,
868  /// The condition to resolve.
869  pub(super) condition: ConditionResolver,
870  /// The items to print when the condition is true.
871  pub(super) true_path: Option<PrintItemPath>,
872  /// The items to print when the condition is false or undefined (not yet resolved).
873  pub(super) false_path: Option<PrintItemPath>,
874}
875
876impl Condition {
877  pub fn new(name: &'static str, properties: ConditionProperties) -> Self {
878    Self::new_internal(name, properties)
879  }
880
881  pub fn new_true() -> Self {
882    Self::new_internal(
883      "trueCondition",
884      ConditionProperties {
885        condition: condition_resolvers::true_resolver(),
886        true_path: None,
887        false_path: None,
888      },
889    )
890  }
891
892  pub fn new_false() -> Self {
893    Self::new_internal(
894      "falseCondition",
895      ConditionProperties {
896        condition: condition_resolvers::false_resolver(),
897        true_path: None,
898        false_path: None,
899      },
900    )
901  }
902
903  fn new_internal(_name: &'static str, properties: ConditionProperties) -> Self {
904    Self {
905      id: thread_state::next_condition_id(),
906      is_stored: false,
907      store_save_point: false,
908      #[cfg(debug_assertions)]
909      name: _name,
910      condition: properties.condition,
911      true_path: properties.true_path.and_then(|x| x.first_node),
912      false_path: properties.false_path.and_then(|x| x.first_node),
913    }
914  }
915
916  #[inline]
917  pub fn unique_id(&self) -> u32 {
918    self.id
919  }
920
921  #[inline]
922  pub fn name(&self) -> &'static str {
923    #[cfg(debug_assertions)]
924    return self.name;
925    #[cfg(not(debug_assertions))]
926    return "condition";
927  }
928
929  #[inline]
930  pub fn true_path(&self) -> &Option<PrintItemPath> {
931    &self.true_path
932  }
933
934  #[inline]
935  pub fn false_path(&self) -> &Option<PrintItemPath> {
936    &self.false_path
937  }
938
939  #[inline]
940  pub(super) fn resolve(&self, context: &mut ConditionResolverContext) -> Option<bool> {
941    (self.condition)(context)
942  }
943
944  pub fn create_reference(&mut self) -> ConditionReference {
945    self.is_stored = true;
946    ConditionReference::new(self.name(), self.id)
947  }
948
949  pub fn create_reevaluation(&mut self) -> ConditionReevaluation {
950    self.store_save_point = true;
951    self.is_stored = true;
952    ConditionReevaluation::new(self.name(), self.id)
953  }
954}
955
956#[derive(Clone, PartialEq, Eq, Copy, Debug)]
957pub struct ConditionReference {
958  #[cfg(debug_assertions)]
959  pub(super) name: &'static str,
960  pub(super) id: u32,
961}
962
963impl ConditionReference {
964  pub(super) fn new(_name: &'static str, id: u32) -> ConditionReference {
965    ConditionReference {
966      #[cfg(debug_assertions)]
967      name: _name,
968      id,
969    }
970  }
971
972  #[inline]
973  pub(super) fn name(&self) -> &'static str {
974    #[cfg(debug_assertions)]
975    return self.name;
976    #[cfg(not(debug_assertions))]
977    return "conditionRef";
978  }
979
980  /// Creates a condition resolver that checks the value of the condition this references.
981  pub fn create_resolver(&self) -> ConditionResolver {
982    let captured_self = *self;
983    Rc::new(move |condition_context: &mut ConditionResolverContext| condition_context.resolved_condition(&captured_self))
984  }
985}
986
987/// Properties for the condition.
988pub struct ConditionProperties {
989  /// The condition to resolve.
990  pub condition: ConditionResolver,
991  /// The items to print when the condition is true.
992  pub true_path: Option<PrintItems>,
993  /// The items to print when the condition is false or undefined (not yet resolved).
994  pub false_path: Option<PrintItems>,
995}
996
997/// Function used to resolve a condition.
998pub type ConditionResolver = Rc<dyn Fn(&mut ConditionResolverContext) -> Option<bool>>;
999
1000/// Context used when resolving a condition.
1001pub struct ConditionResolverContext<'a, 'b> {
1002  printer: &'a mut Printer<'b>,
1003  /// Gets the writer info at the condition's location.
1004  pub writer_info: WriterInfo,
1005}
1006
1007impl<'a, 'b> ConditionResolverContext<'a, 'b> {
1008  pub(super) fn new(printer: &'a mut Printer<'b>, writer_info: WriterInfo) -> Self {
1009    ConditionResolverContext { printer, writer_info }
1010  }
1011
1012  /// Gets if a condition was true, false, or returns None when not yet resolved.
1013  /// A condition reference can be retrieved by calling the `create_reference()` on a condition.
1014  pub fn resolved_condition(&mut self, condition_reference: &ConditionReference) -> Option<bool> {
1015    self.printer.resolved_condition(condition_reference)
1016  }
1017
1018  /// Gets a resolved line and column.
1019  pub fn resolved_line_and_column(&mut self, line_and_column: LineAndColumn) -> Option<(u32, u32)> {
1020    let line = self.printer.resolved_line_number(line_and_column.line)?;
1021    let column = self.printer.resolved_column_number(line_and_column.column)?;
1022    Some((line, column))
1023  }
1024
1025  /// Gets the line number or returns None when not yet resolved.
1026  pub fn resolved_line_number(&mut self, line_number: LineNumber) -> Option<u32> {
1027    self.printer.resolved_line_number(line_number)
1028  }
1029
1030  /// Gets the column number or returns None when not yet resolved.
1031  pub fn resolved_column_number(&mut self, column_number: ColumnNumber) -> Option<u32> {
1032    self.printer.resolved_column_number(column_number)
1033  }
1034
1035  /// Gets if the info is at the start of the line or returns None when not yet resolved.
1036  pub fn resolved_is_start_of_line(&mut self, is_start_of_line: IsStartOfLine) -> Option<bool> {
1037    self.printer.resolved_is_start_of_line(is_start_of_line)
1038  }
1039
1040  /// Gets if the indent level at this info or returns None when not yet resolved.
1041  pub fn resolved_indent_level(&mut self, indent_level: IndentLevel) -> Option<u8> {
1042    self.printer.resolved_indent_level(indent_level)
1043  }
1044
1045  /// Gets the column number at the start of the line this info appears or returns None when not yet resolved.
1046  pub fn resolved_line_start_column_number(&mut self, line_start_column_number: LineStartColumnNumber) -> Option<u32> {
1047    self.printer.resolved_line_start_column_number(line_start_column_number)
1048  }
1049
1050  /// Gets the indent level at the start of the line this info appears or returns None when not yet resolved.
1051  pub fn resolved_line_start_indent_level(&mut self, line_start_indent_level: LineStartIndentLevel) -> Option<u8> {
1052    self.printer.resolved_line_start_indent_level(line_start_indent_level)
1053  }
1054
1055  /// Clears the line and column from being stored.
1056  pub fn clear_line_and_column(&mut self, lc: LineAndColumn) {
1057    self.clear_info(lc.line);
1058    self.clear_info(lc.column);
1059  }
1060
1061  /// Clears the info from being stored.
1062  pub fn clear_info(&mut self, info: impl Into<Info>) {
1063    self.printer.clear_info(info.into())
1064  }
1065
1066  /// Gets if the printer is currently forcing no newlines.
1067  pub fn is_forcing_no_newlines(&self) -> bool {
1068    self.printer.is_forcing_no_newlines()
1069  }
1070}
1071
1072/// A container that holds the string's value and character count.
1073#[derive(Clone)]
1074pub struct StringContainer {
1075  /// The string value.
1076  pub text: UnsafePrintLifetime<str>,
1077  /// The cached character count.
1078  /// It is much faster to cache this than to recompute it all the time.
1079  pub(super) char_count: u32,
1080}
1081
1082impl StringContainer {
1083  /// Creates a new string container.
1084  pub fn new(text: UnsafePrintLifetime<str>) -> Self {
1085    let char_count = unicode_width::UnicodeWidthStr::width(text) as u32;
1086    Self { text, char_count }
1087  }
1088
1089  /// This is used by the sc! proc macro and should not be used otherwise
1090  /// because the character count is pre-computed.
1091  pub const fn proc_macro_new_with_char_count(text: UnsafePrintLifetime<str>, char_count: u32) -> Self {
1092    Self { text, char_count }
1093  }
1094}
1095
1096/// Information about a certain location being printed.
1097#[derive(Clone, Debug)]
1098pub struct WriterInfo {
1099  pub line_number: u32,
1100  pub column_number: u32,
1101  pub indent_level: u8,
1102  pub line_start_indent_level: u8,
1103  pub indent_width: u8,
1104  pub expect_newline_next: bool,
1105}
1106
1107impl WriterInfo {
1108  /// Gets if the current column number equals the line start column number
1109  /// or if a newline is expected next.
1110  pub fn is_start_of_line(&self) -> bool {
1111    self.expect_newline_next || self.is_column_number_at_line_start()
1112  }
1113
1114  /// Gets if the start of the line is indented.
1115  pub fn is_start_of_line_indented(&self) -> bool {
1116    self.line_start_indent_level > self.indent_level
1117  }
1118
1119  /// Gets if the current column number is at the line start column number.
1120  pub fn is_column_number_at_line_start(&self) -> bool {
1121    self.column_number == self.line_start_column_number()
1122  }
1123
1124  pub fn line_start_column_number(&self) -> u32 {
1125    (self.line_start_indent_level as u32) * (self.indent_width as u32)
1126  }
1127
1128  /// Gets the line and column number.
1129  pub fn line_and_column(&self) -> (u32, u32) {
1130    (self.line_number, self.column_number)
1131  }
1132}