1 //: A debugging helper that lets you zoom in/out on a trace.
  2 //: Warning: this tool has zero automated tests.
  3 //:
  4 //: To try it out, first create an example trace:
  5 //:   mu --trace nqueens.mu
  6 //: Then to browse the trace, which was stored in a file called 'last_run':
  7 //:   mu browse-trace last_run
  8 //:
  9 //: You should now find yourself in a UI showing a subsequence of lines from
 10 //: the trace, each line starting with a numeric depth, and ending with a
 11 //: parenthetical count of trace lines hidden after it with greater depths.
 12 //:
 13 //: For example, this line:
 14 //:   2 app: line1 (30)
 15 //: indicates that it was logged with depth 2, and that 30 following lines
 16 //: have been hidden at a depth greater than 2.
 17 //:
 18 //: As an experiment, hidden counts of 1000 or more are in red to highlight
 19 //: where you might be particularly interested in expanding.
 20 //:
 21 //: The UI provides the following hotkeys:
 22 //:
 23 //:   `q` or `ctrl-c`: Quit.
 24 //:pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module ranger.ext.get_all_modules</title>
</head><body bgcolor="#f0f0f8">

<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="ranger.html"><font color="#ffffff">ranger</font></a>.<a href="ranger.ext.html"><font color="#ffffff">ext</font></a>.get_all_modules</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/hut/ranger/ranger/ext/get_all_modules.py">/home/hut/ranger/ranger/ext/get_all_modules.py</a></font></td></tr></table>
    <p><tt>#&nbsp;Copyright&nbsp;(C)&nbsp;2009,&nbsp;2010&nbsp;&nbsp;Roman&nbsp;Zimbelmann&nbsp;&lt;romanz@lavabit.com&gt;<br>
#<br>
#&nbsp;This&nbsp;program&nbsp;is&nbsp;free&nbsp;software:&nbsp;you&nbsp;can&nbsp;redistribute&nbsp;it&nbsp;and/or&nbsp;modify<br>
#&nbsp;it&nbsp;under&nbsp;the&nbsp;terms&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License&nbsp;as&nbsp;published&nbsp;by<br>
#&nbsp;the&nbsp;Free&nbsp;Software&nbsp;Foundation,&nbsp;either&nbsp;version&nbsp;3&nbsp;of&nbsp;the&nbsp;License,&nbsp;or<br>
#&nbsp;(at&nbsp;your&nbsp;option)&nbsp;any&nbsp;later&nbsp;version.<br>
#<br>
#&nbsp;This&nbsp;program&nbsp;is&nbsp;distributed&nbsp;in&nbsp;the&nbsp;hope&nbsp;that&nbsp;it&nbsp;will&nbsp;be&nbsp;useful,<br>
#&nbsp;but&nbsp;WITHOUT&nbsp;ANY&nbsp;WARRANTY;&nbsp;without&nbsp;even&nbsp;the&nbsp;implied&nbsp;warranty&nbsp;of<br>
#&nbsp;MERCHANTABILITY&nbsp;or&nbsp;FITNESS&nbsp;FOR&nbsp;A&nbsp;PARTICULAR&nbsp;PURPOSE.&nbsp;&nbsp;See&nbsp;the<br>
#&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License&nbsp;for&nbsp;more&nbsp;details.<br>
#<br>
#&nbsp;You&nbsp;should&nbsp;have&nbsp;received&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License<br>
#&nbsp;along&nbsp;with&nbsp;this&nbsp;program.&nbsp;&nbsp;If&nbsp;not,&nbsp;see&nbsp;&lt;<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>&gt;.</tt></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#eeaa77">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
    
<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl><dt><a name="-get_all_modules"><strong>get_all_modules</strong></a>(dirname)</dt><dd><tt>returns&nbsp;a&nbsp;list&nbsp;of&nbsp;strings&nbsp;containing&nbsp;the&nbsp;names&nbsp;of&nbsp;modules&nbsp;in&nbsp;a&nbsp;directory</tt></dd></dl>
</td></tr></table>
</body></html>
() - 5); 165 if (Left_of_screen < 0) Left_of_screen = 0; 166 } 167 else if (key == 'L') { 168 // pan screen one screen-width right 169 Left_of_screen += (tb_width() - 5); 170 } 171 else if (key == 'J' || key == TB_KEY_PGDN || key == TB_KEY_CTRL_F) { 172 // page-down 173 if (Trace_index.find(tb_height()-1) != Trace_index.end()) { 174 Top_of_screen = get(Trace_index, tb_height()-1) + 1; 175 refresh_screen_rows(); 176 } 177 } 178 else if (key == 'K' || key == TB_KEY_PGUP || key == TB_KEY_CTRL_B) { 179 // page-up is more convoluted 180 for (int screen_row = tb_height(); screen_row > 0 && Top_of_screen > 0; --screen_row) { 181 --Top_of_screen; 182 if (Top_of_screen <= 0) break; 183 while (Top_of_screen > 0 && !contains_key(Visible, Top_of_screen)) 184 --Top_of_screen; 185 } 186 if (Top_of_screen >= 0) 187 refresh_screen_rows(); 188 } 189 else if (key == 'g' || key == TB_KEY_HOME) { 190 Top_of_screen = 0; 191 Last_printed_row = 0; 192 Display_row = 0; 193 refresh_screen_rows(); 194 } 195 else if (key == 'G' || key == TB_KEY_END) { 196 // go to bottom of screen; largely like page-up, interestingly 197 Top_of_screen = SIZE(Trace_stream->past_lines)-1; 198 for (int screen_row = tb_height(); screen_row > 0 && Top_of_screen > 0; --screen_row) { 199 --Top_of_screen; 200 if (Top_of_screen <= 0) break; 201 while (Top_of_screen > 0 && !contains_key(Visible, Top_of_screen)) 202 --Top_of_screen; 203 } 204 refresh_screen_rows(); 205 // move cursor to bottom 206 Display_row = Last_printed_row; 207 refresh_screen_rows(); 208 } 209 else if (key == TB_KEY_CARRIAGE_RETURN) { 210 // expand lines under current by one level 211 assert(contains_key(Trace_index, Display_row)); 212 int start_index = get(Trace_index, Display_row); 213 int index = 0; 214 // simultaneously compute end_index and min_depth 215 int min_depth = 9999; 216 for (index = start_index+1; index < SIZE(Trace_stream->past_lines); ++index) { 217 if (contains_key(Visible, index)) break; 218 trace_line& curr_line = Trace_stream->past_lines.at(index); 219 assert(curr_line.depth > Trace_stream->past_lines.at(start_index).depth); 220 if (curr_line.depth < min_depth) min_depth = curr_line.depth; 221 } 222 int end_index = index; 223 // mark as visible all intervening indices at min_depth 224 for (index = start_index; index < end_index; ++index) { 225 trace_line& curr_line = Trace_stream->past_lines.at(index); 226 if (curr_line.depth == min_depth) { 227 Visible.insert(index); 228 } 229 } 230 refresh_screen_rows(); 231 } 232 else if (key == TB_KEY_BACKSPACE || key == TB_KEY_BACKSPACE2) { 233 // collapse all lines under current 234 assert(contains_key(Trace_index, Display_row)); 235 int start_index = get(Trace_index, Display_row); 236 int index = 0; 237 // end_index is the next line at a depth same as or lower than start_index 238 int initial_depth = Trace_stream->past_lines.at(start_index).depth; 239 for (index = start_index+1; index < SIZE(Trace_stream->past_lines); ++index) { 240 if (!contains_key(Visible, index)) continue; 241 trace_line& curr_line = Trace_stream->past_lines.at(index); 242 if (curr_line.depth <= initial_depth) break; 243 } 244 int end_index = index; 245 // mark as visible all intervening indices at min_depth 246 for (index = start_index+1; index < end_index; ++index) { 247 Visible.erase(index); 248 } 249 refresh_screen_rows(); 250 } 251 else if (key == '/') { 252 if (start_search_editor(FORWARD)) 253 search(Current_search_pattern, Current_search_direction); 254 } 255 else if (key == '?') { 256 if (start_search_editor(BACKWARD)) 257 search(Current_search_pattern, Current_search_direction); 258 } 259 else if (key == 'n') { 260 if (!Current_search_pattern.empty()) 261 search(Current_search_pattern, Current_search_direction); 262 } 263 else if (key == 'N') { 264 if (!Current_search_pattern.empty()) 265 search(Current_search_pattern, opposite(Current_search_direction)); 266 } 267 } 268 tb_shutdown(); 269 } 270 271 bool start_search_editor(search_direction dir) { 272 const int bottom_screen_line = tb_height()-1; 273 // run a little editor just in the last line of the screen 274 clear_line(bottom_screen_line); 275 int col = 0; // screen column of cursor on bottom line. also used to update pattern. 276 tb_set_cursor(col, bottom_screen_line); 277 tb_print('/', TB_WHITE, TB_BLACK); 278 ++col; 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 > /*slash*/1) { 294 --col; 295 tb_set_cursor(col, bottom_screen_line); 296 } 297 } 298 else if (key == TB_KEY_ARROW_RIGHT) { 299 if (col-/*slash*/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 = /*skip slash*/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)+/*skip slash*/1; 310 tb_set_cursor(col, bottom_screen_line); 311 } 312 else if (key == TB_KEY_BACKSPACE || key == TB_KEY_BACKSPACE2) { 313 if (col > /*slash*/1) { 314 assert(col <= SIZE(pattern)+1); 315 --col; 316 // update pattern 317 pattern.erase(col-/*slash*/1, /*len*/1); 318 // update screen 319 tb_set_cursor(col, bottom_screen_line); 320 for (int x = col; x < SIZE(pattern)+/*skip slash*/1; ++x) 321 tb_print(pattern.at(x-/*slash*/1), TB_WHITE, TB_BLACK); 322 tb_print(' ', TB_WHITE, TB_BLACK); 323 tb_set_cursor(col, bottom_screen_line); 324 } 325 } 326 else if (key == TB_KEY_CTRL_K) { 327 int old_pattern_size = SIZE(pattern); 328 pattern.erase(col-/*slash*/1, SIZE(pattern) - (col-/*slash*/1)); 329 tb_set_cursor(col, bottom_screen_line); 330 for (int x = col; x < old_pattern_size+/*slash*/1; ++x) 331 tb_print(' ', TB_WHITE, TB_BLACK); 332 tb_set_cursor(col, bottom_screen_line); 333 } 334 else if (key == TB_KEY_CTRL_U) { 335 int old_pattern_size = SIZE(pattern); 336 pattern.erase(0, col-/*slash*/1); 337 col = /*skip slash*/1; 338 tb_set_cursor(col, bottom_screen_line); 339 for (int x = /*slash*/1; x < SIZE(pattern)+/*skip slash*/1; ++x) 340 tb_print(pattern.at(x-/*slash*/1), TB_WHITE, TB_BLACK); 341 for (int x = SIZE(pattern)+/*slash*/1; x < old_pattern_size+/*skip slash*/1; ++x) 342 tb_print(' ', TB_WHITE, TB_BLACK); 343 tb_set_cursor(col, bottom_screen_line); 344 } 345 else if (key < 128) { // ascii only 346 // update pattern 347 char c = static_cast<char>(key); 348 assert(col-1 >= 0); 349 assert(col-1 <= SIZE(pattern)); 350 pattern.insert(col-/*slash*/1, /*num*/1, c); 351 // update screen 352 for (int x = col; x < SIZE(pattern)+/*skip slash*/1; ++x) 353 tb_print(pattern.at(x-/*slash*/1), TB_WHITE, TB_BLACK); 354 ++col; 355 tb_set_cursor(col, bottom_screen_line); 356 } 357 } 358 } 359 360 void search(const string& pat, search_direction dir) { 361 if (dir == FORWARD) search_next(pat); 362 else search_previous(pat); 363 } 364 365 search_direction opposite(search_direction dir) { 366 if (dir == FORWARD) return BACKWARD; 367 else return FORWARD; 368 } 369 370 void search_next(const string& pat) { 371 for (int trace_index = get(Trace_index, Display_row)+1; trace_index < SIZE(Trace_stream->past_lines); ++trace_index) { 372 if (!contains_key(Visible, trace_index)) continue; 373 const trace_line& line = Trace_stream->past_lines.at(trace_index); 374 if (line.label.find(pat) == string::npos && line.contents.find(pat) == string::npos) continue; 375 Top_of_screen = trace_index; 376 Display_row = 0; 377 refresh_screen_rows(); 378 return; 379 } 380 } 381 382 void search_previous(const string& pat) { 383 for (int trace_index = get(Trace_index, Display_row)-1; trace_index >= 0; --trace_index) { 384 if (!contains_key(Visible, trace_index)) continue; 385 const trace_line& line = Trace_stream->past_lines.at(trace_index); 386 if (line.label.find(pat) == string::npos && line.contents.find(pat) == string::npos) continue; 387 Top_of_screen = trace_index; 388 Display_row = 0; 389 refresh_screen_rows(); 390 return; 391 } 392 } 393 394 void clear_line(int screen_row) { 395 tb_set_cursor(0, screen_row); 396 for (int col = 0; col < tb_width(); ++col) 397 tb_print(' ', TB_WHITE, TB_BLACK); 398 tb_set_cursor(0, screen_row); 399 } 400 401 // update Trace_indices for each screen_row on the basis of Top_of_screen and Visible 402 void refresh_screen_rows() { 403 int screen_row = 0, index = 0; 404 Trace_index.clear(); 405 for (screen_row = 0, index = Top_of_screen; screen_row < tb_height() && index < SIZE(Trace_stream->past_lines); ++screen_row, ++index) { 406 // skip lines without depth for now 407 while (!contains_key(Visible, index)) { 408 ++index; 409 if (index >= SIZE(Trace_stream->past_lines)) goto done; 410 } 411 assert(index < SIZE(Trace_stream->past_lines)); 412 put(Trace_index, screen_row, index); 413 } 414 done:; 415 } 416 417 void render() { 418 int screen_row = 0; 419 for (screen_row = 0; screen_row < tb_height(); ++screen_row) { 420 if (!contains_key(Trace_index, screen_row)) break; 421 trace_line& curr_line = Trace_stream->past_lines.at(get(Trace_index, screen_row)); 422 ostringstream out; 423 out << std::setw(4) << curr_line.depth << ' ' << curr_line.label << ": " << curr_line.contents; 424 if (screen_row < tb_height()-1) { 425 int delta = lines_hidden(screen_row); 426 // home-brew escape sequence for red 427 if (delta > 1) { 428 if (delta > 999) out << static_cast<char>(1); 429 out << " (" << delta << ")"; 430 if (delta > 999) out << static_cast<char>(2); 431 } 432 } 433 render_line(screen_row, out.str(), screen_row == Display_row); 434 } 435 // clear rest of screen 436 Last_printed_row = screen_row-1; 437 for (; screen_row < tb_height(); ++screen_row) 438 render_line(screen_row, "~", /*cursor_line?*/false); 439 // move cursor back to display row at the end 440 tb_set_cursor(0, Display_row); 441 } 442 443 int lines_hidden(int screen_row) { 444 assert(contains_key(Trace_index, screen_row)); 445 if (!contains_key(Trace_index, screen_row+1)) 446 return SIZE(Trace_stream->past_lines) - get(Trace_index, screen_row); 447 else 448 return get(Trace_index, screen_row+1) - get(Trace_index, screen_row); 449 } 450 451 void render_line(int screen_row, const string& s, bool cursor_line) { 452 int col = 0; 453 int color = TB_WHITE; 454 int background_color = cursor_line ? /*subtle grey*/240 : TB_BLACK; 455 vector<pair<size_t, size_t> > highlight_ranges = find_all_occurrences(s, Current_search_pattern); 456 tb_set_cursor(0, screen_row); 457 for (col = 0; col < tb_width() && col+Left_of_screen < SIZE(s); ++col) { 458 char c = s.at(col+Left_of_screen); // todo: unicode 459 if (c == '\n') c = ';'; // replace newlines with semi-colons 460 // escapes. hack: can't start a line with them. 461 if (c == '\1') { color = /*red*/1; c = ' '; } 462 if (c == '\2') { color = TB_WHITE; c = ' '; } 463 if (in_range(highlight_ranges, col+Left_of_screen)) 464 tb_print(c, TB_BLACK, /*yellow*/11); 465 else 466 tb_print(c, color, background_color); 467 } 468 for (; col < tb_width(); ++col) 469 tb_print(' ', TB_WHITE, background_color); 470 } 471 472 vector<pair<size_t, size_t> > find_all_occurrences(const string& s, const string& pat) { 473 vector<pair<size_t, size_t> > result; 474 if (pat.empty()) return result; 475 size_t idx = 0; 476 while (true) { 477 size_t next_idx = s.find(pat, idx); 478 if (next_idx == string::npos) break; 479 result.push_back(pair<size_t, size_t>(next_idx, next_idx+SIZE(pat))); 480 idx = next_idx+SIZE(pat); 481 } 482 return result; 483 } 484 485 bool in_range(const vector<pair<size_t, size_t> >& highlight_ranges, size_t idx) { 486 for (int i = 0; i < SIZE(highlight_ranges); ++i) { 487 if (idx >= highlight_ranges.at(i).first && idx < highlight_ranges.at(i).second) 488 return true; 489 if (idx < highlight_ranges.at(i).second) break; 490 } 491 return false; 492 } 493 494 void load_trace(const char* filename) { 495 ifstream tin(filename); 496 if (!tin) { 497 cerr << "no such file: " << filename << '\n'; 498 exit(1); 499 } 500 Trace_stream = new trace_stream; 501 while (has_data(tin)) { 502 tin >> std::noskipws; 503 skip_whitespace_but_not_newline(tin); 504 if (!isdigit(tin.peek())) { 505 string dummy; 506 getline(tin, dummy); 507 continue; 508 } 509 tin >> std::skipws; 510 int depth; 511 tin >> depth; 512 string label; 513 tin >> label; 514 if (*--label.end() == ':') label.erase(--label.end()); 515 string line; 516 getline(tin, line); 517 Trace_stream->past_lines.push_back(trace_line(depth, label, line)); 518 } 519 cerr << "lines read: " << Trace_stream->past_lines.size() << '\n'; 520 } 521 522 int read_key() { 523 tb_event event; 524 do { 525 tb_poll_event(&event); 526 } while (event.type != TB_EVENT_KEY); 527 return event.key ? event.key : event.ch; 528 }