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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
//! Helpers to deal with several changes in events, batching them together.
//!
//! Preferably, changes should be kept to a minumum.
//! Sometimes, it’s needed to change the list of events, because parsing can be
//! messy, and it helps to expose a cleaner interface of events to the compiler
//! and other users.
//! It can also help to merge many adjacent similar events.
//! And, in other cases, it’s needed to parse subcontent: pass some events
//! through another tokenizer and inject the result.
/// To do: could we do without `HashMap`, so we don’t need `std`?
use std::collections::HashMap;
use crate::tokenizer::Event;
/// Shift `previous` and `next` links according to `jumps`.
///
/// This fixes links in case there are events removed or added between them.
fn shift_links(events: &mut [Event], jumps: &[(usize, isize)]) {
let map = |before| {
let mut jump_index = 0;
let mut jump = 0;
while jump_index < jumps.len() {
if jumps[jump_index].0 > before {
break;
}
jump = jumps[jump_index].1;
jump_index += 1;
}
#[allow(clippy::pedantic)]
let next_i = (before as isize) + jump;
assert!(next_i >= 0, "cannot shift before `0`");
#[allow(clippy::pedantic)]
let next = next_i as usize;
next
};
let mut index = 0;
while index < events.len() {
let event = &mut events[index];
event.previous = event.previous.map(map);
event.next = event.next.map(map);
index += 1;
}
}
/// Make it easy to insert and remove things while being performant and keeping
/// links in check.
pub struct EditMap {
/// Whether this map was consumed already.
consumed: bool,
/// Record of changes.
map: HashMap<usize, (usize, Vec<Event>)>,
}
impl EditMap {
/// Create a new edit map.
pub fn new() -> EditMap {
EditMap {
consumed: false,
map: HashMap::new(),
}
}
/// Create an edit: a remove and/or add at a certain place.
pub fn add(&mut self, index: usize, remove: usize, add: Vec<Event>) {
add_impl(self, index, remove, add, false);
}
pub fn add_before(&mut self, index: usize, remove: usize, add: Vec<Event>) {
add_impl(self, index, remove, add, true);
}
/// Done, change the events.
pub fn consume(&mut self, events: &mut [Event]) -> Vec<Event> {
let mut indices: Vec<&usize> = self.map.keys().collect();
let mut next_events: Vec<Event> = vec![];
let mut start = 0;
assert!(!self.consumed, "cannot consume after consuming");
self.consumed = true;
indices.sort_unstable();
let mut jumps: Vec<(usize, isize)> = vec![];
let mut index_into_indices = 0;
let mut shift = 0;
while index_into_indices < indices.len() {
let index = *indices[index_into_indices];
let edit = self.map.get(&index).unwrap();
#[allow(clippy::pedantic)]
let next = shift + (edit.1.len() as isize) - (edit.0 as isize);
shift = next;
jumps.push((index, shift));
index_into_indices += 1;
}
let mut index_into_indices = 0;
while index_into_indices < indices.len() {
let index = *indices[index_into_indices];
if start < index {
let append = &mut events[start..index].to_vec();
shift_links(append, &jumps);
next_events.append(append);
}
let (remove, add) = self.map.get(&index).unwrap();
if !add.is_empty() {
let append = &mut add.clone();
let mut index = 0;
while index < append.len() {
let event = &mut append[index];
assert!(event.previous.is_none(), "to do?");
assert!(event.next.is_none(), "to do?");
index += 1;
}
next_events.append(append);
}
start = index + remove;
index_into_indices += 1;
}
if start < events.len() {
next_events.append(&mut events[start..].to_vec());
}
next_events
}
}
fn add_impl(
edit_map: &mut EditMap,
index: usize,
mut remove: usize,
mut add: Vec<Event>,
before: bool,
) {
assert!(!edit_map.consumed, "cannot add after consuming");
if let Some((curr_remove, mut curr_add)) = edit_map.map.remove(&index) {
// To do: these might have to be split in several chunks instead
// of one, if links in `curr_add` are supported.
remove += curr_remove;
if before {
add.append(&mut curr_add);
} else {
curr_add.append(&mut add);
add = curr_add;
}
}
edit_map.map.insert(index, (remove, add));
}
|