From 1d2d6cf5544ce260203d6ad2c52e7b41b9471e42 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Mon, 6 Mar 2017 09:06:58 -0800 Subject: 3757 - more permissive trace browser UI --- 100trace_browser.cc | 38 +++-- html/100trace_browser.cc.html | 356 +++++++++++++++++++++--------------------- 2 files changed, 203 insertions(+), 191 deletions(-) diff --git a/100trace_browser.cc b/100trace_browser.cc index c783db37..3e472fc3 100644 --- a/100trace_browser.cc +++ b/100trace_browser.cc @@ -19,25 +19,25 @@ //: //: The UI provides the following hotkeys: //: -//: 'q': Quit. +//: `q` or `ctrl-c`: Quit. //: -//: 'Enter': 'Zoom into' this line. Expand some or all of the hidden lines +//: `Enter`: 'Zoom into' this line. Expand some or all of the hidden lines //: at the next higher level, updating parenthetical counts of hidden lines. //: -//: 'Backspace': 'Zoom out' on a line after zooming in, collapsing expanded +//: `Backspace`: 'Zoom out' on a line after zooming in, collapsing expanded //: lines below by some series of commands. //: -//: 'j': Move cursor down one line. -//: 'k': Move cursor up one line. -//: 'J': Move cursor down one page. -//: 'K': Move cursor up one page. -//: (arrow and pgUp/pgDn keys should also work) +//: `j` or `down-arrow`: Move cursor down one line. +//: `k` or `up-arrow`: Move cursor up one line. +//: `J` or `ctrl-f` or `page-down`: Move cursor down one page. +//: `K` or `ctrl-b` or `page-up`: Move cursor up one page. //: -//: 'G': Move cursor to end of trace. +//: `g` or `home`: Move cursor to start of trace. +//: `G` or `end`: Move cursor to end of trace. //: -//: 'H': Move cursor to top line on screen. -//: 'M': Move cursor to center line on screen. -//: 'L': Move cursor to bottom line on screen. +//: `H`: Move cursor to top line on screen. +//: `M`: Move cursor to center line on screen. +//: `L`: Move cursor to bottom line on screen. //: browse the trace we just created :(before "End Primitive Recipe Declarations") @@ -94,7 +94,7 @@ void start_trace_browser() { tb_poll_event(&event); } while (event.type != TB_EVENT_KEY); int key = event.key ? event.key : event.ch; - if (key == 'q' || key == 'Q') break; + if (key == 'q' || key == 'Q' || key == TB_KEY_CTRL_C) break; if (key == 'j' || key == TB_KEY_ARROW_DOWN) { // move cursor one line down if (Display_row < Last_printed_row) ++Display_row; @@ -115,14 +115,14 @@ void start_trace_browser() { // move cursor to bottom of screen Display_row = tb_height()-1; } - if (key == 'J' || key == TB_KEY_PGDN) { + if (key == 'J' || key == TB_KEY_PGDN || key == TB_KEY_CTRL_F) { // page-down if (Trace_index.find(tb_height()-1) != Trace_index.end()) { Top_of_screen = get(Trace_index, tb_height()-1) + 1; refresh_screen_rows(); } } - if (key == 'K' || key == TB_KEY_PGUP) { + if (key == 'K' || key == TB_KEY_PGUP || key == TB_KEY_CTRL_B) { // page-up is more convoluted for (int screen_row = tb_height(); screen_row > 0 && Top_of_screen > 0; --screen_row) { --Top_of_screen; @@ -133,7 +133,13 @@ void start_trace_browser() { if (Top_of_screen >= 0) refresh_screen_rows(); } - if (key == 'G') { + if (key == 'g' || key == TB_KEY_HOME) { + Top_of_screen = 0; + Last_printed_row = 0; + Display_row = 0; + refresh_screen_rows(); + } + if (key == 'G' || key == TB_KEY_END) { // go to bottom of screen; largely like page-up, interestingly Top_of_screen = SIZE(Trace_stream->past_lines)-1; for (int screen_row = tb_height(); screen_row > 0 && Top_of_screen > 0; --screen_row) { diff --git a/html/100trace_browser.cc.html b/html/100trace_browser.cc.html index 0361eeff..0da9a5bd 100644 --- a/html/100trace_browser.cc.html +++ b/html/100trace_browser.cc.html @@ -77,25 +77,25 @@ if ('onhashchange' in window) { 19 //: 20 //: The UI provides the following hotkeys: 21 //: - 22 //: 'q': Quit. + 22 //: `q` or `ctrl-c`: Quit. 23 //: - 24 //: 'Enter': 'Zoom into' this line. Expand some or all of the hidden lines + 24 //: `Enter`: 'Zoom into' this line. Expand some or all of the hidden lines 25 //: at the next higher level, updating parenthetical counts of hidden lines. - 26 //: - 27 //: 'Backspace': 'Zoom out' on a line after zooming in, collapsing expanded + 26 //: + 27 //: `Backspace`: 'Zoom out' on a line after zooming in, collapsing expanded 28 //: lines below by some series of <Enter> commands. 29 //: - 30 //: 'j': Move cursor down one line. - 31 //: 'k': Move cursor up one line. - 32 //: 'J': Move cursor down one page. - 33 //: 'K': Move cursor up one page. - 34 //: (arrow and pgUp/pgDn keys should also work) - 35 //: - 36 //: 'G': Move cursor to end of trace. + 30 //: `j` or `down-arrow`: Move cursor down one line. + 31 //: `k` or `up-arrow`: Move cursor up one line. + 32 //: `J` or `ctrl-f` or `page-down`: Move cursor down one page. + 33 //: `K` or `ctrl-b` or `page-up`: Move cursor up one page. + 34 //: + 35 //: `g` or `home`: Move cursor to start of trace. + 36 //: `G` or `end`: Move cursor to end of trace. 37 //: - 38 //: 'H': Move cursor to top line on screen. - 39 //: 'M': Move cursor to center line on screen. - 40 //: 'L': Move cursor to bottom line on screen. + 38 //: `H`: Move cursor to top line on screen. + 39 //: `M`: Move cursor to center line on screen. + 40 //: `L`: Move cursor to bottom line on screen. 41 42 //: browse the trace we just created 43 :(before "End Primitive Recipe Declarations") @@ -115,7 +115,7 @@ if ('onhashchange' in window) { 57 //: browse a trace loaded from a file 58 :(after "Commandline Parsing") 59 if (argc == 3 && is_equal(argv[1], "browse-trace")) { - 60 load_trace(argv[2]); + 60 load_trace(argv[2]); 61 start_trace_browser(); 62 return 0; 63 } @@ -145,14 +145,14 @@ if ('onhashchange' in window) { 87 Display_row = Display_column = 0; 88 tb_event event; 89 Top_of_screen = 0; - 90 refresh_screen_rows(); + 90 refresh_screen_rows(); 91 while (true) { - 92 render(); + 92 render(); 93 do { 94 tb_poll_event(&event); 95 } while (event.type != TB_EVENT_KEY); 96 int key = event.key ? event.key : event.ch; - 97 if (key == 'q' || key == 'Q') break; + 97 if (key == 'q' || key == 'Q' || key == TB_KEY_CTRL_C) break; 98 if (key == 'j' || key == TB_KEY_ARROW_DOWN) { 99 // move cursor one line down 100 if (Display_row < Last_printed_row) ++Display_row; @@ -173,14 +173,14 @@ if ('onhashchange' in window) { 115 // move cursor to bottom of screen 116 Display_row = tb_height()-1; 117 } -118 if (key == 'J' || key == TB_KEY_PGDN) { +118 if (key == 'J' || key == TB_KEY_PGDN || key == TB_KEY_CTRL_F) { 119 // page-down 120 if (Trace_index.find(tb_height()-1) != Trace_index.end()) { 121 Top_of_screen = get(Trace_index, tb_height()-1) + 1; -122 refresh_screen_rows(); +122 refresh_screen_rows(); 123 } 124 } -125 if (key == 'K' || key == TB_KEY_PGUP) { +125 if (key == 'K' || key == TB_KEY_PGUP || key == TB_KEY_CTRL_B) { 126 // page-up is more convoluted 127 for (int screen_row = tb_height(); screen_row > 0 && Top_of_screen > 0; --screen_row) { 128 --Top_of_screen; @@ -189,161 +189,167 @@ if ('onhashchange' in window) { 131 --Top_of_screen; 132 } 133 if (Top_of_screen >= 0) -134 refresh_screen_rows(); +134 refresh_screen_rows(); 135 } -136 if (key == 'G') { -137 // go to bottom of screen; largely like page-up, interestingly -138 Top_of_screen = SIZE(Trace_stream->past_lines)-1; -139 for (int screen_row = tb_height(); screen_row > 0 && Top_of_screen > 0; --screen_row) { -140 --Top_of_screen; -141 if (Top_of_screen <= 0) break; -142 while (Top_of_screen > 0 && !contains_key(Visible, Top_of_screen)) -143 --Top_of_screen; -144 } -145 refresh_screen_rows(); -146 // move cursor to bottom -147 Display_row = Last_printed_row; -148 refresh_screen_rows(); -149 } -150 if (key == TB_KEY_CARRIAGE_RETURN) { -151 // expand lines under current by one level -152 assert(contains_key(Trace_index, Display_row)); -153 int start_index = get(Trace_index, Display_row); -154 int index = 0; -155 // simultaneously compute end_index and min_depth -156 int min_depth = 9999; -157 for (index = start_index+1; index < SIZE(Trace_stream->past_lines); ++index) { -158 if (contains_key(Visible, index)) break; -159 trace_line& curr_line = Trace_stream->past_lines.at(index); -160 assert(curr_line.depth > Trace_stream->past_lines.at(start_index).depth); -161 if (curr_line.depth < min_depth) min_depth = curr_line.depth; -162 } -163 int end_index = index; -164 // mark as visible all intervening indices at min_depth -165 for (index = start_index; index < end_index; ++index) { -166 trace_line& curr_line = Trace_stream->past_lines.at(index); -167 if (curr_line.depth == min_depth) { -168 Visible.insert(index); -169 } -170 } -171 refresh_screen_rows(); -172 } -173 if (key == TB_KEY_BACKSPACE || key == TB_KEY_BACKSPACE2) { -174 // collapse all lines under current -175 assert(contains_key(Trace_index, Display_row)); -176 int start_index = get(Trace_index, Display_row); -177 int index = 0; -178 // end_index is the next line at a depth same as or lower than start_index -179 int initial_depth = Trace_stream->past_lines.at(start_index).depth; -180 for (index = start_index+1; index < SIZE(Trace_stream->past_lines); ++index) { -181 if (!contains_key(Visible, index)) continue; -182 trace_line& curr_line = Trace_stream->past_lines.at(index); -183 if (curr_line.depth <= initial_depth) break; -184 } -185 int end_index = index; -186 // mark as visible all intervening indices at min_depth -187 for (index = start_index+1; index < end_index; ++index) { -188 Visible.erase(index); -189 } -190 refresh_screen_rows(); -191 } -192 } -193 tb_shutdown(); -194 } -195 -196 // update Trace_indices for each screen_row on the basis of Top_of_screen and Visible -197 void refresh_screen_rows() { -198 int screen_row = 0, index = 0; -199 Trace_index.clear(); -200 for (screen_row = 0, index = Top_of_screen; screen_row < tb_height() && index < SIZE(Trace_stream->past_lines); ++screen_row, ++index) { -201 // skip lines without depth for now -202 while (!contains_key(Visible, index)) { -203 ++index; -204 if (index >= SIZE(Trace_stream->past_lines)) goto done; -205 } -206 assert(index < SIZE(Trace_stream->past_lines)); -207 put(Trace_index, screen_row, index); -208 } -209 done:; -210 } -211 -212 void render() { -213 int screen_row = 0; -214 for (screen_row = 0; screen_row < tb_height(); ++screen_row) { -215 if (!contains_key(Trace_index, screen_row)) break; -216 trace_line& curr_line = Trace_stream->past_lines.at(get(Trace_index, screen_row)); -217 ostringstream out; -218 out << std::setw(4) << curr_line.depth << ' ' << curr_line.label << ": " << curr_line.contents; -219 if (screen_row < tb_height()-1) { -220 int delta = lines_hidden(screen_row); -221 // home-brew escape sequence for red -222 if (delta > 999) out << static_cast<char>(1); -223 out << " (" << delta << ")"; -224 if (delta > 999) out << static_cast<char>(2); -225 } -226 render_line(screen_row, out.str()); -227 } -228 // clear rest of screen -229 Last_printed_row = screen_row-1; -230 for (; screen_row < tb_height(); ++screen_row) { -231 render_line(screen_row, "~"); -232 } -233 // move cursor back to display row at the end -234 tb_set_cursor(0, Display_row); -235 tb_present(); -236 } -237 -238 int lines_hidden(int screen_row) { -239 assert(contains_key(Trace_index, screen_row)); -240 if (!contains_key(Trace_index, screen_row+1)) -241 return SIZE(Trace_stream->past_lines) - get(Trace_index, screen_row); -242 else -243 return get(Trace_index, screen_row+1) - get(Trace_index, screen_row); -244 } -245 -246 void render_line(int screen_row, const string& s) { -247 int col = 0; -248 int color = TB_WHITE; -249 for (col = 0; col < tb_width() && col < SIZE(s); ++col) { -250 char c = s.at(col); // todo: unicode -251 if (c == '\n') c = ';'; // replace newlines with semi-colons -252 // escapes. hack: can't start a line with them. -253 if (c == '\1') { color = /*red*/1; c = ' '; } -254 if (c == '\2') { color = TB_WHITE; c = ' '; } -255 tb_change_cell(col, screen_row, c, color, TB_BLACK); -256 } -257 for (; col < tb_width(); ++col) { -258 tb_change_cell(col, screen_row, ' ', TB_WHITE, TB_BLACK); -259 } -260 } -261 -262 void load_trace(const char* filename) { -263 ifstream tin(filename); -264 if (!tin) { -265 cerr << "no such file: " << filename << '\n'; -266 exit(1); -267 } -268 Trace_stream = new trace_stream; -269 while (has_data(tin)) { -270 tin >> std::noskipws; -271 skip_whitespace_but_not_newline(tin); -272 if (!isdigit(tin.peek())) { -273 string dummy; -274 getline(tin, dummy); -275 continue; -276 } -277 tin >> std::skipws; -278 int depth; -279 tin >> depth; -280 string label; -281 tin >> label; -282 if (*--label.end() == ':') label.erase(--label.end()); -283 string line; -284 getline(tin, line); -285 Trace_stream->past_lines.push_back(trace_line(depth, label, line)); -286 } -287 cerr << "lines read: " << Trace_stream->past_lines.size() << '\n'; -288 } +136 if (key == 'g' || key == TB_KEY_HOME) { +137 Top_of_screen = 0; +138 Last_printed_row = 0; +139 Display_row = 0; +140 refresh_screen_rows(); +141 } +142 if (key == 'G' || key == TB_KEY_END) { +143 // go to bottom of screen; largely like page-up, interestingly +144 Top_of_screen = SIZE(Trace_stream->past_lines)-1; +145 for (int screen_row = tb_height(); screen_row > 0 && Top_of_screen > 0; --screen_row) { +146 --Top_of_screen; +147 if (Top_of_screen <= 0) break; +148 while (Top_of_screen > 0 && !contains_key(Visible, Top_of_screen)) +149 --Top_of_screen; +150 } +151 refresh_screen_rows(); +152 // move cursor to bottom +153 Display_row = Last_printed_row; +154 refresh_screen_rows(); +155 } +156 if (key == TB_KEY_CARRIAGE_RETURN) { +157 // expand lines under current by one level +158 assert(contains_key(Trace_index, Display_row)); +159 int start_index = get(Trace_index, Display_row); +160 int index = 0; +161 // simultaneously compute end_index and min_depth +162 int min_depth = 9999; +163 for (index = start_index+1; index < SIZE(Trace_stream->past_lines); ++index) { +164 if (contains_key(Visible, index)) break; +165 trace_line& curr_line = Trace_stream->past_lines.at(index); +166 assert(curr_line.depth > Trace_stream->past_lines.at(start_index).depth); +167 if (curr_line.depth < min_depth) min_depth = curr_line.depth; +168 } +169 int end_index = index; +170 // mark as visible all intervening indices at min_depth +171 for (index = start_index; index < end_index; ++index) { +172 trace_line& curr_line = Trace_stream->past_lines.at(index); +173 if (curr_line.depth == min_depth) { +174 Visible.insert(index); +175 } +176 } +177 refresh_screen_rows(); +178 } +179 if (key == TB_KEY_BACKSPACE || key == TB_KEY_BACKSPACE2) { +180 // collapse all lines under current +181 assert(contains_key(Trace_index, Display_row)); +182 int start_index = get(Trace_index, Display_row); +183 int index = 0; +184 // end_index is the next line at a depth same as or lower than start_index +185 int initial_depth = Trace_stream->past_lines.at(start_index).depth; +186 for (index = start_index+1; index < SIZE(Trace_stream->past_lines); ++index) { +187 if (!contains_key(Visible, index)) continue; +188 trace_line& curr_line = Trace_stream->past_lines.at(index); +189 if (curr_line.depth <= initial_depth) break; +190 } +191 int end_index = index; +192 // mark as visible all intervening indices at min_depth +193 for (index = start_index+1; index < end_index; ++index) { +194 Visible.erase(index); +195 } +196 refresh_screen_rows(); +197 } +198 } +199 tb_shutdown(); +200 } +201 +202 // update Trace_indices for each screen_row on the basis of Top_of_screen and Visible +203 void refresh_screen_rows() { +204 int screen_row = 0, index = 0; +205 Trace_index.clear(); +206 for (screen_row = 0, index = Top_of_screen; screen_row < tb_height() && index < SIZE(Trace_stream->past_lines); ++screen_row, ++index) { +207 // skip lines without depth for now +208 while (!contains_key(Visible, index)) { +209 ++index; +210 if (index >= SIZE(Trace_stream->past_lines)) goto done; +211 } +212 assert(index < SIZE(Trace_stream->past_lines)); +213 put(Trace_index, screen_row, index); +214 } +215 done:; +216 } +217 +218 void render() { +219 int screen_row = 0; +220 for (screen_row = 0; screen_row < tb_height(); ++screen_row) { +221 if (!contains_key(Trace_index, screen_row)) break; +222 trace_line& curr_line = Trace_stream->past_lines.at(get(Trace_index, screen_row)); +223 ostringstream out; +224 out << std::setw(4) << curr_line.depth << ' ' << curr_line.label << ": " << curr_line.contents; +225 if (screen_row < tb_height()-1) { +226 int delta = lines_hidden(screen_row); +227 // home-brew escape sequence for red +228 if (delta > 999) out << static_cast<char>(1); +229 out << " (" << delta << ")"; +230 if (delta > 999) out << static_cast<char>(2); +231 } +232 render_line(screen_row, out.str()); +233 } +234 // clear rest of screen +235 Last_printed_row = screen_row-1; +236 for (; screen_row < tb_height(); ++screen_row) { +237 render_line(screen_row, "~"); +238 } +239 // move cursor back to display row at the end +240 tb_set_cursor(0, Display_row); +241 tb_present(); +242 } +243 +244 int lines_hidden(int screen_row) { +245 assert(contains_key(Trace_index, screen_row)); +246 if (!contains_key(Trace_index, screen_row+1)) +247 return SIZE(Trace_stream->past_lines) - get(Trace_index, screen_row); +248 else +249 return get(Trace_index, screen_row+1) - get(Trace_index, screen_row); +250 } +251 +252 void render_line(int screen_row, const string& s) { +253 int col = 0; +254 int color = TB_WHITE; +255 for (col = 0; col < tb_width() && col < SIZE(s); ++col) { +256 char c = s.at(col); // todo: unicode +257 if (c == '\n') c = ';'; // replace newlines with semi-colons +258 // escapes. hack: can't start a line with them. +259 if (c == '\1') { color = /*red*/1; c = ' '; } +260 if (c == '\2') { color = TB_WHITE; c = ' '; } +261 tb_change_cell(col, screen_row, c, color, TB_BLACK); +262 } +263 for (; col < tb_width(); ++col) { +264 tb_change_cell(col, screen_row, ' ', TB_WHITE, TB_BLACK); +265 } +266 } +267 +268 void load_trace(const char* filename) { +269 ifstream tin(filename); +270 if (!tin) { +271 cerr << "no such file: " << filename << '\n'; +272 exit(1); +273 } +274 Trace_stream = new trace_stream; +275 while (has_data(tin)) { +276 tin >> std::noskipws; +277 skip_whitespace_but_not_newline(tin); +278 if (!isdigit(tin.peek())) { +279 string dummy; +280 getline(tin, dummy); +281 continue; +282 } +283 tin >> std::skipws; +284 int depth; +285 tin >> depth; +286 string label; +287 tin >> label; +288 if (*--label.end() == ':') label.erase(--label.end()); +289 string line; +290 getline(tin, line); +291 Trace_stream->past_lines.push_back(trace_line(depth, label, line)); +292 } +293 cerr << "lines read: " << Trace_stream->past_lines.size() << '\n'; +294 } -- cgit 1.4.1-2-gfad0