Skip to main content

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