1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use std::rc::Rc;

use super::print_items::*;
use super::*;

pub fn indent_if_start_of_line(items: PrintItems) -> Condition {
  let rc_path = items.into_rc_path();
  if_true_or(
    "indentIfStartOfLine",
    condition_resolvers::is_start_of_line(),
    ir_helpers::with_indent(rc_path.into()),
    rc_path.into(),
  )
}

pub fn indent_if_start_of_line_or_start_of_line_indented(items: PrintItems) -> Condition {
  let rc_path = items.into_rc_path();
  conditions::if_true_or(
    "withIndentIfStartOfLineOrStartOfLineIndented",
    condition_resolvers::is_start_of_line_or_is_start_of_line_indented(),
    ir_helpers::with_indent(rc_path.into()),
    rc_path.into(),
  )
}

pub fn with_indent_if_start_of_line_indented(items: PrintItems) -> Condition {
  let rc_path = items.into_rc_path();
  if_true_or(
    "withIndentIfStartOfLineIndented",
    condition_resolvers::is_start_of_line_indented(),
    ir_helpers::with_indent(rc_path.into()),
    rc_path.into(),
  )
}

pub struct NewLineIfHangingSpaceOtherwiseOptions {
  pub start_lsil: LineStartIndentLevel,
  pub end_lsil: Option<LineStartIndentLevel>,
  pub space_char: Option<PrintItems>,
}

pub fn new_line_if_hanging_space_otherwise(opts: NewLineIfHangingSpaceOtherwiseOptions) -> Condition {
  let space_char = opts.space_char.unwrap_or_else(|| " ".into());
  let start_lsil = opts.start_lsil;
  let end_lsil = opts.end_lsil;

  if_true_or(
    "newLineIfHangingSpaceOtherwise",
    Rc::new(move |context| condition_helpers::is_hanging(context, start_lsil, end_lsil)),
    Signal::NewLine.into(),
    space_char,
  )
}

pub fn new_line_if_hanging(start_lsil: LineStartIndentLevel, end_lsil: Option<LineStartIndentLevel>) -> Condition {
  if_true(
    "newlineIfHanging",
    Rc::new(move |context| condition_helpers::is_hanging(context, start_lsil, end_lsil)),
    Signal::NewLine.into(),
  )
}

pub fn new_line_if_multiple_lines_space_or_new_line_otherwise(start_ln: LineNumber, end_ln: Option<LineNumber>) -> Condition {
  if_true_or(
    "newLineIfMultipleLinesSpaceOrNewLineOtherwise",
    Rc::new(move |context| {
      let start_ln = context.resolved_line_number(start_ln)?;
      let end_ln = {
        if let Some(end_ln) = end_ln {
          context.resolved_line_number(end_ln)?
        } else {
          context.writer_info.line_number
        }
      };

      Some(end_ln > start_ln)
    }),
    Signal::NewLine.into(),
    Signal::SpaceOrNewLine.into(),
  )
}

pub fn single_indent_if_start_of_line() -> Condition {
  if_true(
    "singleIndentIfStartOfLine",
    condition_resolvers::is_start_of_line(),
    Signal::SingleIndent.into(),
  )
}

/// Prints the provided items when the current relative column number is above
/// the specified width.
pub fn if_above_width(width: u8, items: PrintItems) -> Condition {
  if_above_width_or(width, items, PrintItems::new())
}

/// Prints the provided true_items when the current relative column number is above
/// the specified width or prints the false_items otherwise.
pub fn if_above_width_or(width: u8, true_items: PrintItems, false_items: PrintItems) -> Condition {
  Condition::new(
    "ifAboveWidth",
    ConditionProperties {
      condition: Rc::new(move |context| {
        let writer_info = &context.writer_info;
        let first_indent_col = writer_info.line_start_column_number() + (width as u32);
        Some(writer_info.column_number > first_indent_col)
      }),
      true_path: Some(true_items),
      false_path: if false_items.is_empty() { None } else { Some(false_items) },
    },
  )
}

pub fn if_true(name: &'static str, resolver: ConditionResolver, true_path: PrintItems) -> Condition {
  Condition::new(
    name,
    ConditionProperties {
      true_path: Some(true_path),
      false_path: None,
      condition: resolver,
    },
  )
}

pub fn if_true_or(name: &'static str, resolver: ConditionResolver, true_path: PrintItems, false_path: PrintItems) -> Condition {
  Condition::new(
    name,
    ConditionProperties {
      true_path: Some(true_path),
      false_path: Some(false_path),
      condition: resolver,
    },
  )
}

pub fn if_false(name: &'static str, resolver: ConditionResolver, false_path: PrintItems) -> Condition {
  Condition::new(
    name,
    ConditionProperties {
      true_path: None,
      false_path: Some(false_path),
      condition: resolver,
    },
  )
}