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
use crate::{Segment, Token};
use std::cmp::min;
#[derive(Debug, PartialEq)]
pub enum Interpolatable {
String(String),
Interpolated(Vec<Token>),
}
impl From<Vec<Segment>> for Interpolatable {
fn from(item: Vec<Segment>) -> Self {
let mut tokens: Vec<Token> = vec![];
let mut string = String::new();
let mut interpolated = false;
for part in item {
match part {
Segment::Char(c) => string.push(c),
Segment::String(s) => string.push_str(&s),
Segment::Expr(t) => {
if !string.is_empty() {
tokens.push(Token::Segment(string.clone()));
string.clear();
}
tokens.push(t);
interpolated = true;
}
}
}
if interpolated {
if !string.is_empty() {
tokens.push(Token::Segment(string.clone()));
}
Self::Interpolated(tokens)
} else {
Self::String(string)
}
}
}
impl Interpolatable {
pub fn to_unindented(self) -> Self {
match self {
Self::Interpolated(tokens) => Self::Interpolated(Self::unindent_tokens(tokens)),
Self::String(string) => {
let mut tokens = Self::unindent_tokens(vec![Token::Segment(string)]);
if let Token::Segment(string) = tokens.remove(0) {
Self::String(string)
} else {
unreachable!()
}
}
}
}
fn unindent_tokens(mut tokens: Vec<Token>) -> Vec<Token> {
let mut after_newline = true;
let mut indentation = usize::MAX;
for t in &tokens {
if let Token::Segment(string) = t {
for line in string.lines() {
let mut whitespace = 0usize;
if after_newline {
for c in line.chars() {
match c {
' ' | '\t' => whitespace += 1,
_ => {
if whitespace == 0 {
return tokens;
}
indentation = min(indentation, whitespace);
break;
}
}
}
};
after_newline = true;
}
} else {
after_newline = false;
}
}
if indentation == usize::MAX {
return tokens;
}
after_newline = true;
let mut whitespace = indentation;
for t in &mut tokens {
if let Token::Segment(ref mut string) = t {
let mut new_string = String::new();
for c in string.chars() {
match c {
' ' | '\t' => {
if after_newline && whitespace > 0 {
whitespace -= 1;
continue;
}
new_string.push(c);
}
'\n' => {
after_newline = true;
whitespace = indentation;
new_string.push(c);
}
_ => {
after_newline = false;
new_string.push(c);
}
};
}
*string = new_string;
} else {
after_newline = false;
}
}
tokens
}
}