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 :(before "End Primitive Recipe Declarations")
67 _BROWSE_TRACE,
68 :(before "End Primitive Recipe Numbers")
69 put(Recipe_ordinal, "$browse-trace", _BROWSE_TRACE);
70 :(before "End Primitive Recipe Checks")
71 case _BROWSE_TRACE: {
72 break;
73 }
74 :(before "End Primitive Recipe Implementations")
75 case _BROWSE_TRACE: {
76 start_trace_browser();
77 break;
78 }
79
80
81 :(after "Commandline Parsing")
82 if (argc == 3 && is_equal(argv[1], "browse-trace")) {
83 load_trace(argv[2]);
84 start_trace_browser();
85 return 0;
86 }
87
88 :(before "End Globals")
89 set<int> Visible;
90 int Top_of_screen = 0;
91 int Left_of_screen = 0;
92 int Last_printed_row = 0;
93 map<int, int> Trace_index;
94 string Current_search_pattern = "";
95 :(before "End Types")
96 enum search_direction { FORWARD, BACKWARD };
97 :(before "End Globals")
98 search_direction Current_search_direction = FORWARD;
99
100 :(code)
101 void start_trace_browser() {
102 if (!Trace_stream) return;
103 cerr << "computing min depth to display\n";
104 int min_depth = 9999;
105 for (int i = 0; i < SIZE(Trace_stream->past_lines); ++i) {
106 ¦ trace_line& curr_line = Trace_stream->past_lines.at(i);
107 ¦ if (curr_line.depth < min_depth) min_depth = curr_line.depth;
108 }
109 cerr << "min depth is " << min_depth << '\n';
110 cerr << "computing lines to display\n";
111 for (int i = 0; i < SIZE(Trace_stream->past_lines); ++i) {
112 ¦ if (Trace_stream->past_lines.at(i).depth == min_depth)
113 ¦ ¦ Visible.insert(i);
114 }
115 tb_init();
116 Display_row = Display_column = 0;
117 Top_of_screen = 0;
118 refresh_screen_rows();
119 while (true) {
120 ¦ render();
121 ¦ int key = read_key();
122 ¦ if (key == 'q' || key == 'Q' || key == TB_KEY_CTRL_C) break;
123 ¦ if (key == 'j' || key == TB_KEY_ARROW_DOWN) {
124 ¦ ¦
125 ¦ ¦ if (Display_row < Last_printed_row) ++Display_row;
126 ¦ }
127 ¦ else if (key == 'k' || key == TB_KEY_ARROW_UP) {
128 ¦ ¦
129 ¦ ¦ if (Display_row > 0) --Display_row;
130 ¦ }
131 ¦ else if (key == 't') {
132 ¦ ¦
133 ¦ ¦ Display_row = 0;
134 ¦ }
135 ¦ else if (key == 'c') {
136 ¦ ¦
137 ¦ ¦ Display_row = tb_height()/2;
138 ¦ ¦ while (!contains_key(Trace_index, Display_row))
139 ¦ ¦ ¦ --Display_row;
140 ¦ }
141 ¦ else if (key == 'b') {
142 ¦ ¦
143 ¦ ¦ Display_row = tb_height()-1;
144 ¦ ¦ while (!contains_key(Trace_index, Display_row))
145 ¦ ¦ ¦ --Display_row;
146 ¦ }
147 ¦ else if (key == 'T') {
148 ¦ ¦
149 ¦ ¦ Top_of_screen = get(Trace_index, Display_row);
150 ¦ ¦ Display_row = 0;
151 ¦ ¦ refresh_screen_rows();
152 ¦ }
153 ¦ else if (key == 'h' || key == TB_KEY_ARROW_LEFT) {
154 ¦ ¦
155 ¦ ¦ if (Left_of_screen > 0) --Left_of_screen;
156 ¦ }
157 ¦ else if (key == 'l' || key == TB_KEY_ARROW_RIGHT) {
158 ¦ ¦
159 ¦ ¦ ++Left_of_screen;
160 ¦ }
161 ¦ else if (key == 'H') {
162 ¦ ¦
163 ¦ ¦ Left_of_screen -= (tb_width() - 5);
164 ¦ ¦ if (Left_of_screen < 0) Left_of_screen = 0;
165 ¦ }
166 ¦ else if (key == 'L') {
167 ¦ ¦
168 ¦ ¦ Left_of_screen += (tb_width() - 5);
169 ¦ }
170 ¦ else if (key == 'J' || key == TB_KEY_PGDN || key == TB_KEY_CTRL_F) {
171 ¦ ¦
172 ¦ ¦ if (Trace_index.find(tb_height()-1) != Trace_index.end()) {
173 ¦ ¦ ¦ Top_of_screen = get(Trace_index, tb_height()-1) + 1;
174 ¦ ¦ ¦ refresh_screen_rows();
175 ¦ ¦ }
176 ¦ }
177 ¦ else if (key == 'K' || key == TB_KEY_PGUP || key == TB_KEY_CTRL_B) {
178 ¦ ¦
179 ¦ ¦ for (int screen_row = tb_height(); screen_row > 0 && Top_of_screen > 0; --screen_row) {
180 ¦ ¦ ¦ --Top_of_screen;
181 ¦ ¦ ¦ if (Top_of_screen <= 0) break;
182 ¦ ¦ ¦ while (Top_of_screen > 0 && !contains_key(Visible, Top_of_screen))
183 ¦ ¦ ¦ ¦ --Top_of_screen;
184 ¦ ¦ }
185 ¦ ¦ if (Top_of_screen >= 0)
186 ¦ ¦ ¦ refresh_screen_rows();
187 ¦ }
188 ¦ else if (key == 'g' || key == TB_KEY_HOME) {
189 ¦ ¦ ¦ Top_of_screen = 0;
190 ¦ ¦ ¦ Last_printed_row = 0;
191 ¦ ¦ ¦ Display_row = 0;
192 ¦ ¦ ¦ refresh_screen_rows();
193 ¦ }
194 ¦ else if (key == 'G' || key == TB_KEY_END) {
195 ¦ ¦
196 ¦ ¦ Top_of_screen = SIZE(Trace_stream->past_lines)-1;
197 ¦ ¦ for (int screen_row = tb_height(); screen_row > 0 && Top_of_screen > 0; --screen_row) {
198 ¦ ¦ ¦ --Top_of_screen;
199 ¦ ¦ ¦ if (Top_of_screen <= 0) break;
200 ¦ ¦ ¦ while (Top_of_screen > 0 && !contains_key(Visible, Top_of_screen))
201 ¦ ¦ ¦ ¦ --Top_of_screen;
202 ¦ ¦ }
203 ¦ ¦ refresh_screen_rows();
204 ¦ ¦
205 ¦ ¦ Display_row = Last_printed_row;
206 ¦ ¦ refresh_screen_rows();
207 ¦ }
208 ¦ else if (key == TB_KEY_CARRIAGE_RETURN) {
209 ¦ ¦
210 ¦ ¦ assert(contains_key(Trace_index, Display_row));
211 ¦ ¦ int start_index = get(Trace_index, Display_row);
212 ¦ ¦ int index = 0;
213 ¦ ¦
214 ¦ ¦ int min_depth = 9999;
215 ¦ ¦ for (index = start_index+1; index < SIZE(Trace_stream->past_lines); ++index) {
216 ¦ ¦ ¦ if (contains_key(Visible, index)) break;
217 ¦ ¦ ¦ trace_line& curr_line = Trace_stream->past_lines.at(index);
218 ¦ ¦ ¦ assert(curr_line.depth > Trace_stream->past_lines.at(start_index).depth);
219 ¦ ¦ ¦ if (curr_line.depth < min_depth) min_depth = curr_line.depth;
220 ¦ ¦ }
221 ¦ ¦ int end_index = index;
222 ¦ ¦
223 ¦ ¦ for (index = start_index; index < end_index; ++index) {
224 ¦ ¦ ¦ trace_line& curr_line = Trace_stream->past_lines.at(index);
225 ¦ ¦ ¦ if (curr_line.depth == min_depth) {
226 ¦ ¦ ¦ ¦ Visible.insert(index);
227 ¦ ¦ ¦ }
228 ¦ ¦ }
229 ¦ ¦ refresh_screen_rows();
230 ¦ }
231 ¦ else if (key == TB_KEY_BACKSPACE || key == TB_KEY_BACKSPACE2) {
232 ¦ ¦
233 ¦ ¦ assert(contains_key(Trace_index, Display_row));
234 ¦ ¦ int start_index = get(Trace_index, Display_row);
235 ¦ ¦ int index = 0;
236 ¦ ¦
237 ¦ ¦ int initial_depth = Trace_stream->past_lines.at(start_index).depth;
238 ¦ ¦ for (index = start_index+1; index < SIZE(Trace_stream->past_lines); ++index) {
239 ¦ ¦ ¦ if (!contains_key(Visible, index)) continue;
240 ¦ ¦ ¦ trace_line& curr_line = Trace_stream->past_lines.at(index);
241 ¦ ¦ ¦ if (curr_line.depth <= initial_depth) break;
242 ¦ ¦ }
243 ¦ ¦ int end_index = index;
244 ¦ ¦
245 ¦ ¦ for (index = start_index+1; index < end_index; ++index) {
246 ¦ ¦ ¦ Visible.erase(index);
247 ¦ ¦ }
248 ¦ ¦ refresh_screen_rows();
249 ¦ }
250 ¦ else if (key == '/') {
251 ¦ ¦ if (start_search_editor(FORWARD))
252 ¦ ¦ ¦ search(Current_search_pattern, Current_search_direction);
253 ¦ }
254 ¦ else if (key == '?') {
255 ¦ ¦ if (start_search_editor(BACKWARD))
256 ¦ ¦ ¦ search(Current_search_pattern, Current_search_direction);
257 ¦ }
258 ¦ else if (key == 'n') {
259 ¦ ¦ if (!Current_search_pattern.empty())
260 ¦ ¦ ¦ search(Current_search_pattern, Current_search_direction);
261 ¦ }
262 ¦ else if (key == 'N') {
263 ¦ ¦ if (!Current_search_pattern.empty())
264 ¦ ¦ ¦ search(Current_search_pattern, opposite(Current_search_direction));
265 ¦ }
266 }
267 tb_shutdown();
268 }
269
270 bool start_search_editor(search_direction dir) {
271 const int bottom_screen_line = tb_height()-1;
272
273 clear_line(bottom_screen_line);
274 tb_set_cursor(0, bottom_screen_line);
275 int col = 0;
276 tb_change_cell(col, bottom_screen_line, '/', TB_WHITE, TB_BLACK);
277 ++col;
278 tb_set_cursor(col, bottom_screen_line);
279 string pattern;
280 while (true) {
281 ¦ int key = read_key();
282 ¦ if (key == TB_KEY_ENTER) {
283 ¦ ¦ if (!pattern.empty()) {
284 ¦ ¦ ¦ Current_search_pattern = pattern;
285 ¦ ¦ ¦ Current_search_direction = dir;
286 ¦ ¦ }
287 ¦ ¦ return true;
288 ¦ }
289 ¦ else if (key == TB_KEY_ESC || key == TB_KEY_CTRL_C) {
290 ¦ ¦ return false;
291 ¦ }
292 ¦ else if (key == TB_KEY_ARROW_LEFT) {
293 ¦ ¦ if (col > 1) {
294 ¦ ¦ ¦ --col;
295 ¦ ¦ ¦ tb_set_cursor(col, bottom_screen_line);
296 ¦ ¦ }
297 ¦ }
298 ¦ else if (key == TB_KEY_ARROW_RIGHT) {
299 ¦ ¦ if (col-1 < SIZE(pattern)) {
300 ¦ ¦ ¦ ++col;
301 ¦ ¦ ¦ tb_set_cursor(col, bottom_screen_line);
302 ¦ ¦ }
303 ¦ }
304 ¦ else if (key == TB_KEY_HOME || key == TB_KEY_CTRL_A) {
305 ¦ ¦ col = 1;
306 ¦ ¦ tb_set_cursor(col, bottom_screen_line);
307 ¦ }
308 ¦ else if (key == TB_KEY_END || key == TB_KEY_CTRL_E) {
309 ¦ ¦ col = SIZE(pattern)+1;
310 ¦ ¦ tb_set_cursor(col, bottom_screen_line);
311 ¦ }
312 ¦ else if (key == TB_KEY_BACKSPACE || key == TB_KEY_BACKSPACE2) {
313 ¦ ¦ if (col > 1) {
314 ¦ ¦ ¦ --col;
315 ¦ ¦ ¦
316 ¦ ¦ ¦ pattern.erase(col-1, 1);
317 ¦ ¦ ¦
318 ¦ ¦ ¦ if (col > SIZE(pattern)) {
319 ¦ ¦ ¦ ¦ tb_change_cell(col, bottom_screen_line, ' ', TB_WHITE, TB_BLACK);
320 ¦ ¦ ¦ }
321 ¦ ¦ ¦ else {
322 ¦ ¦ ¦ ¦ assert(col <= SIZE(pattern));
323 ¦ ¦ ¦ ¦ for (int x = col; x < SIZE(pattern)+1; ++x)
324 ¦ ¦ ¦ ¦ ¦ tb_change_cell(x, bottom_screen_line, pattern.at(x-1), TB_WHITE, TB_BLACK);
325 ¦ ¦ ¦ ¦ tb_change_cell(SIZE(pattern)+1, bottom_screen_line, ' ', TB_WHITE, TB_BLACK);
326 ¦ ¦ ¦ }
327 ¦ ¦ ¦ tb_set_cursor(col, bottom_screen_line);
328 ¦ ¦ }
329 ¦ }
330 ¦ else if (key == TB_KEY_CTRL_K) {
331 ¦ ¦ int old_pattern_size = SIZE(pattern);
332 ¦ ¦ pattern.erase(col-1, SIZE(pattern) - (col-1));
333 ¦ ¦ for (int x = col; x < old_pattern_size+1; ++x)
334 ¦ ¦ ¦ tb_change_cell(x, bottom_screen_line, ' ', TB_WHITE, TB_BLACK);
335 ¦ ¦ tb_set_cursor(col, bottom_screen_line);
336 ¦ }
337 ¦ else if (key == TB_KEY_CTRL_U) {
338 ¦ ¦ int old_pattern_size = SIZE(pattern);
339 ¦ ¦ pattern.erase(0, col-1);
340 ¦ ¦ for (int x = 1; x < SIZE(pattern)+1; ++x)
341 ¦ ¦ ¦ tb_change_cell(x, bottom_screen_line, pattern.at(x-1), TB_WHITE, TB_BLACK);
342 ¦ ¦ for (int x = SIZE(pattern)+1; x < old_pattern_size+1; ++x)
343 ¦ ¦ ¦ tb_change_cell(x, bottom_screen_line, ' ', TB_WHITE, TB_BLACK);
344 ¦ ¦ tb_set_cursor(1, bottom_screen_line);
345 ¦ }
346 ¦ else if (key < 128) {
347 ¦ ¦
348 ¦ ¦ char c = static_cast<char>(key);
349 ¦ ¦ pattern.insert(col-1, 1, c);
350 ¦ ¦
351 ¦ ¦ for (int x = col; x < SIZE(pattern)+1; ++x)
352 ¦ ¦ ¦ tb_change_cell(x, bottom_screen_line, pattern.at(x-1), TB_WHITE, TB_BLACK);
353 ¦ ¦ ++col;
354 ¦ ¦ tb_set_cursor(col, bottom_screen_line);
355 ¦ }
356 }
357 }
358
359 void search(const string& pat, search_direction dir) {
360 if (dir == FORWARD) search_next(pat);
361 else search_previous(pat);
362 }
363
364 search_direction opposite(search_direction dir) {
365 if (dir == FORWARD) return BACKWARD;
366 else return FORWARD;
367 }
368
369 void search_next(const string& pat) {
370 for (int trace_index = get(Trace_index, Display_row)+1; trace_index < SIZE(Trace_stream->past_lines); ++trace_index) {
371 ¦ if (!contains_key(Visible, trace_index)) continue;
372 ¦ const trace_line& line = Trace_stream->past_lines.at(trace_index);
373 ¦ if (line.label.find(pat) == string::npos && line.contents.find(pat) == string::npos) continue;
374 ¦ Top_of_screen = trace_index;
375 ¦ Display_row = 0;
376 ¦ refresh_screen_rows();
377 ¦ return;
378 }
379 }
380
381 void search_previous(const string& pat) {
382 for (int trace_index = get(Trace_index, Display_row)-1; trace_index >= 0; --trace_index) {
383 ¦ if (!contains_key(Visible, trace_index)) continue;
384 ¦ const trace_line& line = Trace_stream->past_lines.at(trace_index);
385 ¦ if (line.label.find(pat) == string::npos && line.contents.find(pat) == string::npos) continue;
386 ¦ Top_of_screen = trace_index;
387 ¦ Display_row = 0;
388 ¦ refresh_screen_rows();
389 ¦ return;
390 }
391 }
392
393 void clear_line(int screen_row) {
394 for (int col = 0; col < tb_width(); ++col)
395 ¦ tb_change_cell(col, screen_row, ' ', TB_WHITE, TB_BLACK);
396 }
397
398
399 void refresh_screen_rows() {
400 int screen_row = 0, index = 0;
401 Trace_index.clear();
402 for (screen_row = 0, index = Top_of_screen; screen_row < tb_height() && index < SIZE(Trace_stream->past_lines); ++screen_row, ++index) {
403 ¦
404 ¦ while (!contains_key(Visible, index)) {
405 ¦ ¦ ++index;
406 ¦ ¦ if (index >= SIZE(Trace_stream->past_lines)) goto done;
407 ¦ }
408 ¦ assert(index < SIZE(Trace_stream->past_lines));
409 ¦ put(Trace_index, screen_row, index);
410 }
411 done:;
412 }
413
414 void render() {
415 int screen_row = 0;
416 for (screen_row = 0; screen_row < tb_height(); ++screen_row) {
417 ¦ if (!contains_key(Trace_index, screen_row)) break;
418 ¦ trace_line& curr_line = Trace_stream->past_lines.at(get(Trace_index, screen_row));
419 ¦ ostringstream out;
420 ¦ out << std::setw(4) << curr_line.depth << ' ' << curr_line.label << ": " << curr_line.contents;
421 ¦ if (screen_row < tb_height()-1) {
422 ¦ ¦ int delta = lines_hidden(screen_row);
423 ¦ ¦
424 ¦ ¦ if (delta > 1) {
425 ¦ ¦ ¦ if (delta > 999) out << static_cast<char>(1);
426 ¦ ¦ ¦ out << " (" << delta << ")";
427 ¦ ¦ ¦ if (delta > 999) out << static_cast<char>(2);
428 ¦ ¦ }
429 ¦ }
430 ¦ render_line(screen_row, out.str(), screen_row == Display_row);
431 }
432
433 Last_printed_row = screen_row-1;
434 for (; screen_row < tb_height(); ++screen_row)
435 ¦ render_line(screen_row, "~", false);
436
437 tb_set_cursor(0, Display_row);
438 }
439
440 int lines_hidden(int screen_row) {
441 assert(contains_key(Trace_index, screen_row));
442 if (!contains_key(Trace_index, screen_row+1))
443 ¦ return SIZE(Trace_stream->past_lines) - get(Trace_index, screen_row);
444 else
445 ¦ return get(Trace_index, screen_row+1) - get(Trace_index, screen_row);
446 }
447
448 void render_line(int screen_row, const string& s, bool cursor_line) {
449 int col = 0;
450 int color = TB_WHITE;
451 int background_color = cursor_line ? 240 : TB_BLACK;
452 vector<pair<size_t, size_t> > highlight_ranges = find_all_occurrences(s, Current_search_pattern);
453 for (col = 0; col < tb_width() && col+Left_of_screen < SIZE(s); ++col) {
454 ¦ char c = s.at(col+Left_of_screen);
455 ¦ if (c == '\n') c = ';';
456 ¦
457 ¦ if (c == '\1') { color = 1; c = ' '; }
458 ¦ if (c == '\2') { color = TB_WHITE; c = ' '; }
459 ¦ if (in_range(highlight_ranges, col+Left_of_screen))
460 ¦ ¦ tb_change_cell(col, screen_row, c, TB_BLACK, 11);
461 ¦ else
462 ¦ ¦ tb_change_cell(col, screen_row, c, color, background_color);
463 }
464 for (; col < tb_width(); ++col)
465 ¦ tb_change_cell(col, screen_row, ' ', TB_WHITE, background_color);
466 }
467
468 vector<pair<size_t, size_t> > find_all_occurrences(const string& s, const string& pat) {
469 vector<pair<size_t, size_t> > result;
470 if (pat.empty()) return result;
471 size_t idx = 0;
472 while (true) {
473 ¦ size_t next_idx = s.find(pat, idx);
474 ¦ if (next_idx == string::npos) break;
475 ¦ result.push_back(pair<size_t, size_t>(next_idx, next_idx+SIZE(pat)));
476 ¦ idx = next_idx+SIZE(pat);
477 }
478 return result;
479 }
480
481 bool in_range(const vector<pair<size_t, size_t> >& highlight_ranges, size_t idx) {
482 for (int i = 0; i < SIZE(highlight_ranges); ++i) {
483 ¦ if (idx >= highlight_ranges.at(i).first && idx < highlight_ranges.at(i).second)
484 ¦ ¦ return true;
485 ¦ if (idx < highlight_ranges.at(i).second) break;
486 }
487 return false;
488 }
489
490 void load_trace(const char* filename) {
491 ifstream tin(filename);
492 if (!tin) {
493 ¦ cerr << "no such file: " << filename << '\n';
494 ¦ exit(1);
495 }
496 Trace_stream = new trace_stream;
497 while (has_data(tin)) {
498 ¦ tin >> std::noskipws;
499 ¦ ¦ skip_whitespace_but_not_newline(tin);
500 ¦ ¦ if (!isdigit(tin.peek())) {
501 ¦ ¦ ¦ string dummy;
502 ¦ ¦ ¦ getline(tin, dummy);
503 ¦ ¦ ¦ continue;
504 ¦ ¦ }
505 ¦ tin >> std::skipws;
506 ¦ int depth;
507 ¦ tin >> depth;
508 ¦ string label;
509 ¦ tin >> label;
510 ¦ if (*--label.end() == ':') label.erase(--label.end());
511 ¦ string line;
512 ¦ getline(tin, line);
513 ¦ Trace_stream->past_lines.push_back(trace_line(depth, label, line));
514 }
515 cerr << "lines read: " << Trace_stream->past_lines.size() << '\n';
516 }
517
518 int read_key() {
519 tb_event event;
520 do {
521 ¦ tb_poll_event(&event);
522 } while (event.type != TB_EVENT_KEY);
523 return event.key ? event.key : event.ch;
524 }