herb 0.9.7-arm-linux-gnu → 0.10.0-arm-linux-gnu
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/ext/herb/extconf.rb +1 -0
- data/ext/herb/extension.c +108 -0
- data/herb.gemspec +1 -1
- data/lib/herb/3.2/herb.so +0 -0
- data/lib/herb/3.3/herb.so +0 -0
- data/lib/herb/3.4/herb.so +0 -0
- data/lib/herb/4.0/herb.so +0 -0
- data/lib/herb/action_view/render_analyzer.rb +1057 -0
- data/lib/herb/ast/erb_render_node.rb +155 -0
- data/lib/herb/bootstrap.rb +0 -1
- data/lib/herb/cli.rb +253 -19
- data/lib/herb/colors.rb +18 -0
- data/lib/herb/configuration.rb +49 -13
- data/lib/herb/defaults.yml +3 -0
- data/lib/herb/dev/runner.rb +445 -0
- data/lib/herb/dev/server.rb +207 -0
- data/lib/herb/dev/server_entry.rb +128 -0
- data/lib/herb/diff_operation.rb +34 -0
- data/lib/herb/diff_result.rb +59 -0
- data/lib/herb/engine/compiler.rb +56 -3
- data/lib/herb/engine/validators/render_validator.rb +92 -0
- data/lib/herb/engine.rb +58 -4
- data/lib/herb/html/util.rb +16 -0
- data/lib/herb/project.rb +1 -6
- data/lib/herb/version.rb +1 -1
- data/lib/herb.rb +41 -5
- data/sig/herb/action_view/render_analyzer.rbs +122 -0
- data/sig/herb/ast/erb_render_node.rbs +29 -0
- data/sig/herb/colors.rbs +12 -0
- data/sig/herb/configuration.rbs +20 -1
- data/sig/herb/dev/runner.rbs +59 -0
- data/sig/herb/dev/server.rbs +50 -0
- data/sig/herb/dev/server_entry.rbs +51 -0
- data/sig/herb/diff_operation.rbs +34 -0
- data/sig/herb/diff_result.rbs +34 -0
- data/sig/herb/engine/compiler.rbs +6 -0
- data/sig/herb/engine/validators/render_validator.rbs +21 -0
- data/sig/herb/engine.rbs +15 -0
- data/sig/herb/html/util.rbs +13 -0
- data/sig/herb.rbs +12 -2
- data/sig/herb_c_extension.rbs +1 -1
- data/sig/vendor/did_you_mean.rbs +6 -0
- data/sig/vendor/parallel.rbs +4 -0
- data/src/analyze/action_view/attribute_extraction_helpers.c +3 -2
- data/src/diff/herb_diff.c +137 -0
- data/src/diff/herb_diff_attributes.c +207 -0
- data/src/diff/herb_diff_children.c +518 -0
- data/src/diff/herb_diff_helpers.c +114 -0
- data/src/diff/herb_diff_nodes.c +707 -0
- data/src/diff/herb_hash.c +42 -0
- data/src/diff/herb_hash_index_map.c +47 -0
- data/src/diff/herb_hash_map.c +104 -0
- data/src/diff/herb_hash_tree.c +680 -0
- data/src/include/diff/herb_diff.h +118 -0
- data/src/include/diff/herb_hash.h +25 -0
- data/src/include/diff/herb_hash_index_map.h +32 -0
- data/src/include/diff/herb_hash_map.h +30 -0
- data/src/include/herb.h +1 -0
- data/src/include/version.h +1 -1
- data/templates/javascript/packages/core/src/config.ts.erb +43 -0
- data/templates/rust/src/ast/nodes.rs.erb +1 -1
- data/templates/rust/src/config.rs.erb +50 -0
- data/templates/src/diff/herb_diff_helpers.c.erb +38 -0
- data/templates/src/diff/herb_diff_nodes.c.erb +224 -0
- data/templates/src/diff/herb_hash_tree.c.erb +147 -0
- data/templates/template.rb +4 -4
- metadata +40 -4
- data/lib/herb/3.0/herb.so +0 -0
- data/lib/herb/3.1/herb.so +0 -0
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
#include "../include/diff/herb_diff.h"
|
|
2
|
+
#include "../include/diff/herb_hash_index_map.h"
|
|
3
|
+
#include "../include/macros.h"
|
|
4
|
+
|
|
5
|
+
#include <string.h>
|
|
6
|
+
|
|
7
|
+
// Longest Common Subsequence (LCS) algorithm using dynamic programming.
|
|
8
|
+
// Based on the approach described in:
|
|
9
|
+
// Hunt, J. W. and Szymanski, T. G. "A Fast Algorithm for Computing
|
|
10
|
+
// Longest Common Subsequences" (1977), Communications of the ACM, 20(5).
|
|
11
|
+
// https://en.wikipedia.org/wiki/Longest_common_subsequence
|
|
12
|
+
#define LCS_MAX_SIZE 256
|
|
13
|
+
|
|
14
|
+
typedef enum {
|
|
15
|
+
EDIT_KEEP,
|
|
16
|
+
EDIT_INSERT,
|
|
17
|
+
EDIT_DELETE,
|
|
18
|
+
EDIT_MOVE,
|
|
19
|
+
EDIT_CONSUMED,
|
|
20
|
+
EDIT_COALESCED_KEEP,
|
|
21
|
+
EDIT_WRAP,
|
|
22
|
+
EDIT_UNWRAP,
|
|
23
|
+
} edit_type_T;
|
|
24
|
+
|
|
25
|
+
typedef struct {
|
|
26
|
+
edit_type_T type;
|
|
27
|
+
size_t old_index;
|
|
28
|
+
size_t new_index;
|
|
29
|
+
} edit_entry_T;
|
|
30
|
+
|
|
31
|
+
static size_t element_attribute_count(const AST_NODE_T* node) {
|
|
32
|
+
if (node->type == AST_HTML_ELEMENT_NODE) {
|
|
33
|
+
const AST_HTML_ELEMENT_NODE_T* element = (const AST_HTML_ELEMENT_NODE_T*) node;
|
|
34
|
+
|
|
35
|
+
if (element->open_tag != NULL) {
|
|
36
|
+
const AST_HTML_OPEN_TAG_NODE_T* open_tag = (const AST_HTML_OPEN_TAG_NODE_T*) element->open_tag;
|
|
37
|
+
|
|
38
|
+
if (open_tag->children != NULL) { return hb_array_size(open_tag->children); }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (node->type == AST_HTML_CONDITIONAL_ELEMENT_NODE) {
|
|
43
|
+
const AST_HTML_CONDITIONAL_ELEMENT_NODE_T* element = (const AST_HTML_CONDITIONAL_ELEMENT_NODE_T*) node;
|
|
44
|
+
|
|
45
|
+
if (element->open_tag != NULL && element->open_tag->children != NULL) {
|
|
46
|
+
return hb_array_size(element->open_tag->children);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static bool nodes_match(
|
|
54
|
+
const AST_NODE_T* old_node,
|
|
55
|
+
const AST_NODE_T* new_node,
|
|
56
|
+
const herb_hash_map_T* old_hashes,
|
|
57
|
+
const herb_hash_map_T* new_hashes
|
|
58
|
+
) {
|
|
59
|
+
if (old_node == NULL || new_node == NULL) { return false; }
|
|
60
|
+
|
|
61
|
+
herb_hash_T old_hash = herb_hash_map_get(old_hashes, old_node);
|
|
62
|
+
herb_hash_T new_hash = herb_hash_map_get(new_hashes, new_node);
|
|
63
|
+
|
|
64
|
+
if (old_hash == new_hash) { return true; }
|
|
65
|
+
if (old_node->type != new_node->type) { return false; }
|
|
66
|
+
|
|
67
|
+
if (old_node->type == AST_HTML_ELEMENT_NODE || old_node->type == AST_HTML_CONDITIONAL_ELEMENT_NODE) {
|
|
68
|
+
herb_hash_T old_identity = herb_hash_node_identity(old_node, old_hashes);
|
|
69
|
+
herb_hash_T new_identity = herb_hash_node_identity(new_node, new_hashes);
|
|
70
|
+
|
|
71
|
+
if (old_identity != new_identity) { return false; }
|
|
72
|
+
|
|
73
|
+
if (element_attribute_count(old_node) == 0 || element_attribute_count(new_node) == 0) { return true; }
|
|
74
|
+
|
|
75
|
+
herb_hash_T old_move_identity = herb_hash_node_move_identity(old_node, old_hashes);
|
|
76
|
+
herb_hash_T new_move_identity = herb_hash_node_move_identity(new_node, new_hashes);
|
|
77
|
+
|
|
78
|
+
return old_move_identity == new_move_identity;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static void diff_children_linear(
|
|
85
|
+
const hb_array_T* old_children,
|
|
86
|
+
const hb_array_T* new_children,
|
|
87
|
+
const herb_diff_path_T parent_path,
|
|
88
|
+
const herb_hash_map_T* old_hashes,
|
|
89
|
+
const herb_hash_map_T* new_hashes,
|
|
90
|
+
herb_diff_result_T* result
|
|
91
|
+
) {
|
|
92
|
+
const size_t old_size = hb_array_size(old_children);
|
|
93
|
+
const size_t new_size = hb_array_size(new_children);
|
|
94
|
+
|
|
95
|
+
size_t common_prefix = 0;
|
|
96
|
+
|
|
97
|
+
while (common_prefix < old_size && common_prefix < new_size) {
|
|
98
|
+
const AST_NODE_T* old_child = (const AST_NODE_T*) hb_array_get(old_children, common_prefix);
|
|
99
|
+
const AST_NODE_T* new_child = (const AST_NODE_T*) hb_array_get(new_children, common_prefix);
|
|
100
|
+
herb_hash_T old_hash = herb_hash_map_get(old_hashes, old_child);
|
|
101
|
+
herb_hash_T new_hash = herb_hash_map_get(new_hashes, new_child);
|
|
102
|
+
|
|
103
|
+
if (old_hash != new_hash) { break; }
|
|
104
|
+
|
|
105
|
+
common_prefix++;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
size_t common_suffix = 0;
|
|
109
|
+
|
|
110
|
+
while (common_suffix < (old_size - common_prefix) && common_suffix < (new_size - common_prefix)) {
|
|
111
|
+
const AST_NODE_T* old_child = (const AST_NODE_T*) hb_array_get(old_children, old_size - 1 - common_suffix);
|
|
112
|
+
const AST_NODE_T* new_child = (const AST_NODE_T*) hb_array_get(new_children, new_size - 1 - common_suffix);
|
|
113
|
+
|
|
114
|
+
herb_hash_T old_hash = herb_hash_map_get(old_hashes, old_child);
|
|
115
|
+
herb_hash_T new_hash = herb_hash_map_get(new_hashes, new_child);
|
|
116
|
+
|
|
117
|
+
if (old_hash != new_hash) { break; }
|
|
118
|
+
|
|
119
|
+
common_suffix++;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const size_t old_middle_start = common_prefix;
|
|
123
|
+
const size_t old_middle_end = old_size - common_suffix;
|
|
124
|
+
const size_t new_middle_start = common_prefix;
|
|
125
|
+
const size_t new_middle_end = new_size - common_suffix;
|
|
126
|
+
|
|
127
|
+
for (size_t index = old_middle_start; index < old_middle_end; index++) {
|
|
128
|
+
const AST_NODE_T* old_child = (const AST_NODE_T*) hb_array_get(old_children, index);
|
|
129
|
+
|
|
130
|
+
herb_diff_emit_operation(
|
|
131
|
+
result,
|
|
132
|
+
HERB_DIFF_NODE_REMOVED,
|
|
133
|
+
herb_diff_path_append(parent_path, (uint32_t) index),
|
|
134
|
+
old_child,
|
|
135
|
+
NULL,
|
|
136
|
+
(uint32_t) index,
|
|
137
|
+
0
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
for (size_t index = new_middle_start; index < new_middle_end; index++) {
|
|
142
|
+
const AST_NODE_T* new_child = (const AST_NODE_T*) hb_array_get(new_children, index);
|
|
143
|
+
|
|
144
|
+
herb_diff_emit_operation(
|
|
145
|
+
result,
|
|
146
|
+
HERB_DIFF_NODE_INSERTED,
|
|
147
|
+
herb_diff_path_append(parent_path, (uint32_t) index),
|
|
148
|
+
NULL,
|
|
149
|
+
new_child,
|
|
150
|
+
0,
|
|
151
|
+
(uint32_t) index
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
void herb_diff_children(
|
|
157
|
+
const hb_array_T* old_children,
|
|
158
|
+
const hb_array_T* new_children,
|
|
159
|
+
const herb_diff_path_T parent_path,
|
|
160
|
+
const herb_hash_map_T* old_hashes,
|
|
161
|
+
const herb_hash_map_T* new_hashes,
|
|
162
|
+
herb_diff_result_T* result
|
|
163
|
+
) {
|
|
164
|
+
if (old_children == NULL && new_children == NULL) { return; }
|
|
165
|
+
|
|
166
|
+
if (old_children == NULL) {
|
|
167
|
+
for (size_t index = 0; index < hb_array_size(new_children); index++) {
|
|
168
|
+
const AST_NODE_T* new_child = (const AST_NODE_T*) hb_array_get(new_children, index);
|
|
169
|
+
|
|
170
|
+
herb_diff_emit_operation(
|
|
171
|
+
result,
|
|
172
|
+
HERB_DIFF_NODE_INSERTED,
|
|
173
|
+
herb_diff_path_append(parent_path, (uint32_t) index),
|
|
174
|
+
NULL,
|
|
175
|
+
new_child,
|
|
176
|
+
0,
|
|
177
|
+
(uint32_t) index
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (new_children == NULL) {
|
|
185
|
+
for (size_t index = 0; index < hb_array_size(old_children); index++) {
|
|
186
|
+
const AST_NODE_T* old_child = (const AST_NODE_T*) hb_array_get(old_children, index);
|
|
187
|
+
|
|
188
|
+
herb_diff_emit_operation(
|
|
189
|
+
result,
|
|
190
|
+
HERB_DIFF_NODE_REMOVED,
|
|
191
|
+
herb_diff_path_append(parent_path, (uint32_t) index),
|
|
192
|
+
old_child,
|
|
193
|
+
NULL,
|
|
194
|
+
(uint32_t) index,
|
|
195
|
+
0
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const size_t old_size = hb_array_size(old_children);
|
|
203
|
+
const size_t new_size = hb_array_size(new_children);
|
|
204
|
+
|
|
205
|
+
if (old_size == 0 && new_size == 0) { return; }
|
|
206
|
+
|
|
207
|
+
if (old_size > LCS_MAX_SIZE || new_size > LCS_MAX_SIZE) {
|
|
208
|
+
diff_children_linear(old_children, new_children, parent_path, old_hashes, new_hashes, result);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const size_t table_width = new_size + 1;
|
|
213
|
+
const size_t table_size = (old_size + 1) * table_width;
|
|
214
|
+
size_t* lcs_table = (size_t*) hb_allocator_alloc(result->allocator, table_size * sizeof(size_t));
|
|
215
|
+
memset(lcs_table, 0, table_size * sizeof(size_t));
|
|
216
|
+
|
|
217
|
+
for (size_t old_index = 1; old_index <= old_size; old_index++) {
|
|
218
|
+
for (size_t new_index = 1; new_index <= new_size; new_index++) {
|
|
219
|
+
const AST_NODE_T* old_child = (const AST_NODE_T*) hb_array_get(old_children, old_index - 1);
|
|
220
|
+
const AST_NODE_T* new_child = (const AST_NODE_T*) hb_array_get(new_children, new_index - 1);
|
|
221
|
+
|
|
222
|
+
if (nodes_match(old_child, new_child, old_hashes, new_hashes)) {
|
|
223
|
+
lcs_table[old_index * table_width + new_index] = lcs_table[(old_index - 1) * table_width + (new_index - 1)] + 1;
|
|
224
|
+
} else {
|
|
225
|
+
const size_t from_old = lcs_table[(old_index - 1) * table_width + new_index];
|
|
226
|
+
const size_t from_new = lcs_table[old_index * table_width + (new_index - 1)];
|
|
227
|
+
lcs_table[old_index * table_width + new_index] = MAX(from_old, from_new);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
size_t edit_capacity = old_size + new_size;
|
|
233
|
+
size_t edit_count = 0;
|
|
234
|
+
|
|
235
|
+
edit_entry_T* edit_script =
|
|
236
|
+
(edit_entry_T*) hb_allocator_alloc(result->allocator, edit_capacity * sizeof(edit_entry_T));
|
|
237
|
+
|
|
238
|
+
size_t old_index = old_size;
|
|
239
|
+
size_t new_index = new_size;
|
|
240
|
+
|
|
241
|
+
while (old_index > 0 || new_index > 0) {
|
|
242
|
+
if (old_index > 0 && new_index > 0) {
|
|
243
|
+
const AST_NODE_T* old_child = (const AST_NODE_T*) hb_array_get(old_children, old_index - 1);
|
|
244
|
+
const AST_NODE_T* new_child = (const AST_NODE_T*) hb_array_get(new_children, new_index - 1);
|
|
245
|
+
|
|
246
|
+
if (nodes_match(old_child, new_child, old_hashes, new_hashes)) {
|
|
247
|
+
edit_script[edit_count].type = EDIT_KEEP;
|
|
248
|
+
edit_script[edit_count].old_index = old_index - 1;
|
|
249
|
+
edit_script[edit_count].new_index = new_index - 1;
|
|
250
|
+
edit_count++;
|
|
251
|
+
|
|
252
|
+
old_index--;
|
|
253
|
+
new_index--;
|
|
254
|
+
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (new_index > 0
|
|
260
|
+
&& (old_index == 0 || lcs_table[old_index * table_width + (new_index - 1)] >= lcs_table[(old_index - 1) * table_width + new_index])) {
|
|
261
|
+
edit_script[edit_count].type = EDIT_INSERT;
|
|
262
|
+
edit_script[edit_count].old_index = 0;
|
|
263
|
+
edit_script[edit_count].new_index = new_index - 1;
|
|
264
|
+
edit_count++;
|
|
265
|
+
new_index--;
|
|
266
|
+
} else {
|
|
267
|
+
edit_script[edit_count].type = EDIT_DELETE;
|
|
268
|
+
edit_script[edit_count].old_index = old_index - 1;
|
|
269
|
+
edit_script[edit_count].new_index = 0;
|
|
270
|
+
edit_count++;
|
|
271
|
+
old_index--;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
size_t remove_count = 0;
|
|
276
|
+
size_t insert_count = 0;
|
|
277
|
+
|
|
278
|
+
for (size_t index = 0; index < edit_count; index++) {
|
|
279
|
+
if (edit_script[index].type == EDIT_DELETE) { remove_count++; }
|
|
280
|
+
if (edit_script[index].type == EDIT_INSERT) { insert_count++; }
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
bool* remove_matched = NULL;
|
|
284
|
+
bool* insert_matched = NULL;
|
|
285
|
+
|
|
286
|
+
if (remove_count > 0 && insert_count > 0) {
|
|
287
|
+
size_t remove_alloc = remove_count * sizeof(size_t);
|
|
288
|
+
size_t insert_alloc = insert_count * sizeof(size_t);
|
|
289
|
+
|
|
290
|
+
size_t* remove_indices = (size_t*) hb_allocator_alloc(result->allocator, remove_alloc);
|
|
291
|
+
size_t* insert_indices = (size_t*) hb_allocator_alloc(result->allocator, insert_alloc);
|
|
292
|
+
|
|
293
|
+
remove_matched = (bool*) hb_allocator_alloc(result->allocator, remove_count * sizeof(bool));
|
|
294
|
+
insert_matched = (bool*) hb_allocator_alloc(result->allocator, insert_count * sizeof(bool));
|
|
295
|
+
|
|
296
|
+
size_t remove_position = 0;
|
|
297
|
+
size_t insert_position = 0;
|
|
298
|
+
|
|
299
|
+
for (size_t index = 0; index < edit_count; index++) {
|
|
300
|
+
if (edit_script[index].type == EDIT_DELETE) {
|
|
301
|
+
remove_indices[remove_position] = index;
|
|
302
|
+
remove_matched[remove_position] = false;
|
|
303
|
+
remove_position++;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (edit_script[index].type == EDIT_INSERT) {
|
|
307
|
+
insert_indices[insert_position] = index;
|
|
308
|
+
insert_matched[insert_position] = false;
|
|
309
|
+
insert_position++;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
herb_hash_index_map_T move_identity_map;
|
|
314
|
+
herb_hash_index_map_init(&move_identity_map, insert_count, result->allocator);
|
|
315
|
+
|
|
316
|
+
for (size_t insert_index = 0; insert_index < insert_count; insert_index++) {
|
|
317
|
+
const edit_entry_T* insert_entry = &edit_script[insert_indices[insert_index]];
|
|
318
|
+
const AST_NODE_T* new_child = (const AST_NODE_T*) hb_array_get(new_children, insert_entry->new_index);
|
|
319
|
+
|
|
320
|
+
herb_hash_T new_identity = herb_hash_node_move_identity(new_child, new_hashes);
|
|
321
|
+
herb_hash_index_map_insert(&move_identity_map, new_identity, insert_index);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
for (size_t remove_index = 0; remove_index < remove_count; remove_index++) {
|
|
325
|
+
if (remove_matched[remove_index]) { continue; }
|
|
326
|
+
|
|
327
|
+
const edit_entry_T* remove_entry = &edit_script[remove_indices[remove_index]];
|
|
328
|
+
const AST_NODE_T* old_child = (const AST_NODE_T*) hb_array_get(old_children, remove_entry->old_index);
|
|
329
|
+
herb_hash_T old_identity = herb_hash_node_move_identity(old_child, old_hashes);
|
|
330
|
+
|
|
331
|
+
size_t matched_insert;
|
|
332
|
+
|
|
333
|
+
if (herb_hash_index_map_find_unmatched(&move_identity_map, old_identity, insert_matched, &matched_insert)) {
|
|
334
|
+
const edit_entry_T* insert_entry = &edit_script[insert_indices[matched_insert]];
|
|
335
|
+
|
|
336
|
+
remove_matched[remove_index] = true;
|
|
337
|
+
insert_matched[matched_insert] = true;
|
|
338
|
+
|
|
339
|
+
edit_script[remove_indices[remove_index]].type = EDIT_MOVE;
|
|
340
|
+
edit_script[remove_indices[remove_index]].new_index = insert_entry->new_index;
|
|
341
|
+
edit_script[insert_indices[matched_insert]].type = EDIT_CONSUMED;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
herb_hash_index_map_T tag_identity_map;
|
|
346
|
+
herb_hash_index_map_init(&tag_identity_map, insert_count, result->allocator);
|
|
347
|
+
|
|
348
|
+
for (size_t insert_index = 0; insert_index < insert_count; insert_index++) {
|
|
349
|
+
if (insert_matched[insert_index]) { continue; }
|
|
350
|
+
|
|
351
|
+
const edit_entry_T* insert_entry = &edit_script[insert_indices[insert_index]];
|
|
352
|
+
const AST_NODE_T* new_child = (const AST_NODE_T*) hb_array_get(new_children, insert_entry->new_index);
|
|
353
|
+
|
|
354
|
+
herb_hash_T new_tag_identity = herb_hash_node_identity(new_child, new_hashes);
|
|
355
|
+
herb_hash_index_map_insert(&tag_identity_map, new_tag_identity, insert_index);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
for (size_t remove_index = 0; remove_index < remove_count; remove_index++) {
|
|
359
|
+
if (remove_matched[remove_index]) { continue; }
|
|
360
|
+
|
|
361
|
+
const edit_entry_T* remove_entry = &edit_script[remove_indices[remove_index]];
|
|
362
|
+
const AST_NODE_T* old_child = (const AST_NODE_T*) hb_array_get(old_children, remove_entry->old_index);
|
|
363
|
+
|
|
364
|
+
herb_hash_T old_tag_identity = herb_hash_node_identity(old_child, old_hashes);
|
|
365
|
+
size_t matched_insert;
|
|
366
|
+
|
|
367
|
+
if (herb_hash_index_map_find_unmatched(&tag_identity_map, old_tag_identity, insert_matched, &matched_insert)) {
|
|
368
|
+
const edit_entry_T* insert_entry = &edit_script[insert_indices[matched_insert]];
|
|
369
|
+
|
|
370
|
+
remove_matched[remove_index] = true;
|
|
371
|
+
insert_matched[matched_insert] = true;
|
|
372
|
+
|
|
373
|
+
edit_script[remove_indices[remove_index]].type = EDIT_COALESCED_KEEP;
|
|
374
|
+
edit_script[remove_indices[remove_index]].new_index = insert_entry->new_index;
|
|
375
|
+
edit_script[insert_indices[matched_insert]].type = EDIT_CONSUMED;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
for (size_t remove_index = 0; remove_index < remove_count; remove_index++) {
|
|
380
|
+
if (remove_matched[remove_index]) { continue; }
|
|
381
|
+
|
|
382
|
+
const edit_entry_T* remove_entry = &edit_script[remove_indices[remove_index]];
|
|
383
|
+
const AST_NODE_T* old_child = (const AST_NODE_T*) hb_array_get(old_children, remove_entry->old_index);
|
|
384
|
+
|
|
385
|
+
herb_hash_T old_hash = herb_hash_map_get(old_hashes, old_child);
|
|
386
|
+
|
|
387
|
+
for (size_t insert_index = 0; insert_index < insert_count; insert_index++) {
|
|
388
|
+
if (insert_matched[insert_index]) { continue; }
|
|
389
|
+
|
|
390
|
+
const edit_entry_T* insert_entry = &edit_script[insert_indices[insert_index]];
|
|
391
|
+
const AST_NODE_T* new_child = (const AST_NODE_T*) hb_array_get(new_children, insert_entry->new_index);
|
|
392
|
+
herb_hash_T new_hash = herb_hash_map_get(new_hashes, new_child);
|
|
393
|
+
|
|
394
|
+
const AST_NODE_T* found_in_new = herb_diff_find_child_by_hash(new_child, old_hash, new_hashes);
|
|
395
|
+
|
|
396
|
+
if (found_in_new != NULL) {
|
|
397
|
+
remove_matched[remove_index] = true;
|
|
398
|
+
insert_matched[insert_index] = true;
|
|
399
|
+
|
|
400
|
+
edit_script[remove_indices[remove_index]].type = EDIT_WRAP;
|
|
401
|
+
edit_script[remove_indices[remove_index]].new_index = insert_entry->new_index;
|
|
402
|
+
edit_script[insert_indices[insert_index]].type = EDIT_CONSUMED;
|
|
403
|
+
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const AST_NODE_T* found_in_old = herb_diff_find_child_by_hash(old_child, new_hash, old_hashes);
|
|
408
|
+
|
|
409
|
+
if (found_in_old != NULL) {
|
|
410
|
+
remove_matched[remove_index] = true;
|
|
411
|
+
insert_matched[insert_index] = true;
|
|
412
|
+
|
|
413
|
+
edit_script[remove_indices[remove_index]].type = EDIT_UNWRAP;
|
|
414
|
+
edit_script[remove_indices[remove_index]].new_index = insert_entry->new_index;
|
|
415
|
+
edit_script[insert_indices[insert_index]].type = EDIT_CONSUMED;
|
|
416
|
+
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
for (size_t index = edit_count; index > 0; index--) {
|
|
424
|
+
const edit_entry_T* entry = &edit_script[index - 1];
|
|
425
|
+
|
|
426
|
+
if (entry->type == EDIT_KEEP) {
|
|
427
|
+
const AST_NODE_T* old_child = (const AST_NODE_T*) hb_array_get(old_children, entry->old_index);
|
|
428
|
+
const AST_NODE_T* new_child = (const AST_NODE_T*) hb_array_get(new_children, entry->new_index);
|
|
429
|
+
|
|
430
|
+
herb_diff_path_T child_path = herb_diff_path_append(parent_path, (uint32_t) entry->new_index);
|
|
431
|
+
herb_diff_node(old_child, new_child, child_path, old_hashes, new_hashes, result);
|
|
432
|
+
} else if (entry->type == EDIT_INSERT) {
|
|
433
|
+
const AST_NODE_T* new_child = (const AST_NODE_T*) hb_array_get(new_children, entry->new_index);
|
|
434
|
+
|
|
435
|
+
herb_diff_emit_operation(
|
|
436
|
+
result,
|
|
437
|
+
HERB_DIFF_NODE_INSERTED,
|
|
438
|
+
herb_diff_path_append(parent_path, (uint32_t) entry->new_index),
|
|
439
|
+
NULL,
|
|
440
|
+
new_child,
|
|
441
|
+
0,
|
|
442
|
+
(uint32_t) entry->new_index
|
|
443
|
+
);
|
|
444
|
+
} else if (entry->type == EDIT_DELETE) {
|
|
445
|
+
const AST_NODE_T* old_child = (const AST_NODE_T*) hb_array_get(old_children, entry->old_index);
|
|
446
|
+
|
|
447
|
+
herb_diff_emit_operation(
|
|
448
|
+
result,
|
|
449
|
+
HERB_DIFF_NODE_REMOVED,
|
|
450
|
+
herb_diff_path_append(parent_path, (uint32_t) entry->old_index),
|
|
451
|
+
old_child,
|
|
452
|
+
NULL,
|
|
453
|
+
(uint32_t) entry->old_index,
|
|
454
|
+
0
|
|
455
|
+
);
|
|
456
|
+
} else if (entry->type == EDIT_MOVE) {
|
|
457
|
+
const AST_NODE_T* old_child = (const AST_NODE_T*) hb_array_get(old_children, entry->old_index);
|
|
458
|
+
const AST_NODE_T* new_child = (const AST_NODE_T*) hb_array_get(new_children, entry->new_index);
|
|
459
|
+
|
|
460
|
+
herb_diff_emit_operation(
|
|
461
|
+
result,
|
|
462
|
+
HERB_DIFF_NODE_MOVED,
|
|
463
|
+
herb_diff_path_append(parent_path, (uint32_t) entry->new_index),
|
|
464
|
+
old_child,
|
|
465
|
+
new_child,
|
|
466
|
+
(uint32_t) entry->old_index,
|
|
467
|
+
(uint32_t) entry->new_index
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
herb_diff_path_T child_path = herb_diff_path_append(parent_path, (uint32_t) entry->new_index);
|
|
471
|
+
herb_diff_node(old_child, new_child, child_path, old_hashes, new_hashes, result);
|
|
472
|
+
} else if (entry->type == EDIT_COALESCED_KEEP) {
|
|
473
|
+
const AST_NODE_T* old_child = (const AST_NODE_T*) hb_array_get(old_children, entry->old_index);
|
|
474
|
+
const AST_NODE_T* new_child = (const AST_NODE_T*) hb_array_get(new_children, entry->new_index);
|
|
475
|
+
herb_diff_path_T child_path = herb_diff_path_append(parent_path, (uint32_t) entry->new_index);
|
|
476
|
+
|
|
477
|
+
if (entry->old_index != entry->new_index) {
|
|
478
|
+
herb_diff_emit_operation(
|
|
479
|
+
result,
|
|
480
|
+
HERB_DIFF_NODE_MOVED,
|
|
481
|
+
child_path,
|
|
482
|
+
old_child,
|
|
483
|
+
new_child,
|
|
484
|
+
(uint32_t) entry->old_index,
|
|
485
|
+
(uint32_t) entry->new_index
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
herb_diff_node(old_child, new_child, child_path, old_hashes, new_hashes, result);
|
|
490
|
+
} else if (entry->type == EDIT_WRAP) {
|
|
491
|
+
const AST_NODE_T* old_child = (const AST_NODE_T*) hb_array_get(old_children, entry->old_index);
|
|
492
|
+
const AST_NODE_T* new_child = (const AST_NODE_T*) hb_array_get(new_children, entry->new_index);
|
|
493
|
+
|
|
494
|
+
herb_diff_emit_operation(
|
|
495
|
+
result,
|
|
496
|
+
HERB_DIFF_NODE_WRAPPED,
|
|
497
|
+
herb_diff_path_append(parent_path, (uint32_t) entry->new_index),
|
|
498
|
+
old_child,
|
|
499
|
+
new_child,
|
|
500
|
+
(uint32_t) entry->old_index,
|
|
501
|
+
(uint32_t) entry->new_index
|
|
502
|
+
);
|
|
503
|
+
} else if (entry->type == EDIT_UNWRAP) {
|
|
504
|
+
const AST_NODE_T* old_child = (const AST_NODE_T*) hb_array_get(old_children, entry->old_index);
|
|
505
|
+
const AST_NODE_T* new_child = (const AST_NODE_T*) hb_array_get(new_children, entry->new_index);
|
|
506
|
+
|
|
507
|
+
herb_diff_emit_operation(
|
|
508
|
+
result,
|
|
509
|
+
HERB_DIFF_NODE_UNWRAPPED,
|
|
510
|
+
herb_diff_path_append(parent_path, (uint32_t) entry->new_index),
|
|
511
|
+
old_child,
|
|
512
|
+
new_child,
|
|
513
|
+
(uint32_t) entry->old_index,
|
|
514
|
+
(uint32_t) entry->new_index
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// NOTE: This file is generated by the templates/template.rb script and should not
|
|
2
|
+
// be modified manually. See /home/runner/work/herb/herb/templates/src/diff/herb_diff_helpers.c.erb
|
|
3
|
+
|
|
4
|
+
#include "../include/diff/herb_diff.h"
|
|
5
|
+
|
|
6
|
+
const hb_array_T* herb_diff_get_node_children(const AST_NODE_T* node) {
|
|
7
|
+
if (node == NULL) { return NULL; }
|
|
8
|
+
|
|
9
|
+
switch (node->type) {
|
|
10
|
+
case AST_DOCUMENT_NODE:
|
|
11
|
+
return ((const AST_DOCUMENT_NODE_T*) node)->children;
|
|
12
|
+
|
|
13
|
+
case AST_HTML_OPEN_TAG_NODE:
|
|
14
|
+
return ((const AST_HTML_OPEN_TAG_NODE_T*) node)->children;
|
|
15
|
+
|
|
16
|
+
case AST_HTML_CLOSE_TAG_NODE:
|
|
17
|
+
return ((const AST_HTML_CLOSE_TAG_NODE_T*) node)->children;
|
|
18
|
+
|
|
19
|
+
case AST_HTML_ELEMENT_NODE:
|
|
20
|
+
return ((const AST_HTML_ELEMENT_NODE_T*) node)->body;
|
|
21
|
+
|
|
22
|
+
case AST_HTML_CONDITIONAL_ELEMENT_NODE:
|
|
23
|
+
return ((const AST_HTML_CONDITIONAL_ELEMENT_NODE_T*) node)->body;
|
|
24
|
+
|
|
25
|
+
case AST_HTML_ATTRIBUTE_VALUE_NODE:
|
|
26
|
+
return ((const AST_HTML_ATTRIBUTE_VALUE_NODE_T*) node)->children;
|
|
27
|
+
|
|
28
|
+
case AST_HTML_ATTRIBUTE_NAME_NODE:
|
|
29
|
+
return ((const AST_HTML_ATTRIBUTE_NAME_NODE_T*) node)->children;
|
|
30
|
+
|
|
31
|
+
case AST_ERB_OPEN_TAG_NODE:
|
|
32
|
+
return ((const AST_ERB_OPEN_TAG_NODE_T*) node)->children;
|
|
33
|
+
|
|
34
|
+
case AST_HTML_COMMENT_NODE:
|
|
35
|
+
return ((const AST_HTML_COMMENT_NODE_T*) node)->children;
|
|
36
|
+
|
|
37
|
+
case AST_HTML_DOCTYPE_NODE:
|
|
38
|
+
return ((const AST_HTML_DOCTYPE_NODE_T*) node)->children;
|
|
39
|
+
|
|
40
|
+
case AST_XML_DECLARATION_NODE:
|
|
41
|
+
return ((const AST_XML_DECLARATION_NODE_T*) node)->children;
|
|
42
|
+
|
|
43
|
+
case AST_CDATA_NODE:
|
|
44
|
+
return ((const AST_CDATA_NODE_T*) node)->children;
|
|
45
|
+
|
|
46
|
+
case AST_ERB_ELSE_NODE:
|
|
47
|
+
return ((const AST_ERB_ELSE_NODE_T*) node)->statements;
|
|
48
|
+
|
|
49
|
+
case AST_ERB_IF_NODE:
|
|
50
|
+
return ((const AST_ERB_IF_NODE_T*) node)->statements;
|
|
51
|
+
|
|
52
|
+
case AST_ERB_BLOCK_NODE:
|
|
53
|
+
return ((const AST_ERB_BLOCK_NODE_T*) node)->body;
|
|
54
|
+
|
|
55
|
+
case AST_ERB_WHEN_NODE:
|
|
56
|
+
return ((const AST_ERB_WHEN_NODE_T*) node)->statements;
|
|
57
|
+
|
|
58
|
+
case AST_ERB_CASE_NODE:
|
|
59
|
+
return ((const AST_ERB_CASE_NODE_T*) node)->children;
|
|
60
|
+
|
|
61
|
+
case AST_ERB_CASE_MATCH_NODE:
|
|
62
|
+
return ((const AST_ERB_CASE_MATCH_NODE_T*) node)->children;
|
|
63
|
+
|
|
64
|
+
case AST_ERB_WHILE_NODE:
|
|
65
|
+
return ((const AST_ERB_WHILE_NODE_T*) node)->statements;
|
|
66
|
+
|
|
67
|
+
case AST_ERB_UNTIL_NODE:
|
|
68
|
+
return ((const AST_ERB_UNTIL_NODE_T*) node)->statements;
|
|
69
|
+
|
|
70
|
+
case AST_ERB_FOR_NODE:
|
|
71
|
+
return ((const AST_ERB_FOR_NODE_T*) node)->statements;
|
|
72
|
+
|
|
73
|
+
case AST_ERB_RESCUE_NODE:
|
|
74
|
+
return ((const AST_ERB_RESCUE_NODE_T*) node)->statements;
|
|
75
|
+
|
|
76
|
+
case AST_ERB_ENSURE_NODE:
|
|
77
|
+
return ((const AST_ERB_ENSURE_NODE_T*) node)->statements;
|
|
78
|
+
|
|
79
|
+
case AST_ERB_BEGIN_NODE:
|
|
80
|
+
return ((const AST_ERB_BEGIN_NODE_T*) node)->statements;
|
|
81
|
+
|
|
82
|
+
case AST_ERB_UNLESS_NODE:
|
|
83
|
+
return ((const AST_ERB_UNLESS_NODE_T*) node)->statements;
|
|
84
|
+
|
|
85
|
+
case AST_ERB_RENDER_NODE:
|
|
86
|
+
return ((const AST_ERB_RENDER_NODE_T*) node)->body;
|
|
87
|
+
|
|
88
|
+
case AST_ERB_IN_NODE:
|
|
89
|
+
return ((const AST_ERB_IN_NODE_T*) node)->statements;
|
|
90
|
+
|
|
91
|
+
default:
|
|
92
|
+
return NULL;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const AST_NODE_T* herb_diff_find_child_by_hash(
|
|
97
|
+
const AST_NODE_T* parent,
|
|
98
|
+
const herb_hash_T target_hash,
|
|
99
|
+
const herb_hash_map_T* hash_map
|
|
100
|
+
) {
|
|
101
|
+
const hb_array_T* children = herb_diff_get_node_children(parent);
|
|
102
|
+
if (children == NULL) { return NULL; }
|
|
103
|
+
|
|
104
|
+
const size_t count = hb_array_size(children);
|
|
105
|
+
|
|
106
|
+
for (size_t index = 0; index < count; index++) {
|
|
107
|
+
const AST_NODE_T* child = (const AST_NODE_T*) hb_array_get(children, index);
|
|
108
|
+
herb_hash_T child_hash = herb_hash_map_get(hash_map, child);
|
|
109
|
+
|
|
110
|
+
if (child_hash == target_hash) { return child; }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return NULL;
|
|
114
|
+
}
|