about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-08-02 15:26:58 -0700
committerKartik K. Agaram <vc@akkartik.com>2015-08-02 16:18:16 -0700
commit7f402c85eb34a739055dc3e5bb4be337169ec68c (patch)
treecbb0365029213b87f8da70b00268bf8981b9a892
parentd082b17675f40037b0e6c26384d99362acd0749e (diff)
downloadmu-7f402c85eb34a739055dc3e5bb4be337169ec68c.tar.gz
1921 - show trace by clicking on code
Region to click on to edit is now reduced to just the menu bar for the
sandbox (excluding the 'x' for deleting the sandbox). The symmetry there
might be useful, but we'll see if the relative click area is
in line with how commonly the actions are performed.
-rw-r--r--003trace.cc12
-rw-r--r--020run.cc2
-rw-r--r--029tools.cc23
-rw-r--r--031address.cc4
-rw-r--r--042name.cc2
-rw-r--r--081run_interactive.cc27
-rwxr-xr-xbuild_and_test_until4
-rw-r--r--edit.mu153
8 files changed, 201 insertions, 26 deletions
diff --git a/003trace.cc b/003trace.cc
index 3695703a..d2afb21c 100644
--- a/003trace.cc
+++ b/003trace.cc
@@ -103,7 +103,7 @@ struct trace_stream {
   string curr_layer;
   int curr_depth;
   string dump_layer;
-  string collect_layer;  // if set, ignore all other layers
+  set<string> collect_layers;  // if not empty, ignore all absent layers
   ofstream null_stream;  // never opens a file, so writes silently fail
   trace_stream() :curr_stream(NULL), curr_depth(0) {}
   ~trace_stream() { if (curr_stream) delete curr_stream; }
@@ -113,13 +113,21 @@ struct trace_stream {
   }
 
   ostream& stream(int depth, string layer) {
-    if (!collect_layer.empty() && layer != collect_layer) return null_stream;
+    if (!is_collecting(layer)) return null_stream;
     curr_stream = new ostringstream;
     curr_layer = layer;
     curr_depth = depth;
     return *curr_stream;
   }
 
+  bool is_collecting(const string& layer) {
+    return collect_layers.empty() || collect_layers.find(layer) != collect_layers.end();
+  }
+
+  bool is_narrowly_collecting(const string& layer) {
+    return collect_layers.find(layer) != collect_layers.end();
+  }
+
   // be sure to call this before messing with curr_stream or curr_layer
   void newline() {
     if (!curr_stream) return;
diff --git a/020run.cc b/020run.cc
index a4b75db6..72fc2b7c 100644
--- a/020run.cc
+++ b/020run.cc
@@ -133,7 +133,7 @@ if (!Run_tests) {
   setup();
 //?   Trace_file = "interactive"; //? 2
 //?   START_TRACING_UNTIL_END_OF_SCOPE; //? 2
-//?   Trace_stream->collect_layer = "app"; //? 1
+//?   Trace_stream->collect_layer.insert("app"); //? 1
   transform_all();
   recipe_ordinal r = Recipe_ordinal[string("main")];
   if (r) run(r);
diff --git a/029tools.cc b/029tools.cc
index cd4123fa..587818ea 100644
--- a/029tools.cc
+++ b/029tools.cc
@@ -12,15 +12,22 @@ TRACE,
 Recipe_ordinal["trace"] = TRACE;
 :(before "End Primitive Recipe Implementations")
 case TRACE: {
-  if (SIZE(ingredients) != 2) {
-    raise << current_recipe_name() << ": 'trace' takes exactly two ingredients rather than '" << current_instruction().to_string() << "'\n" << end();
-    break;
+  if (SIZE(ingredients) == 2) {
+    assert(is_literal(current_instruction().ingredients.at(0)));
+    string label = current_instruction().ingredients.at(0).name;
+    assert(is_literal(current_instruction().ingredients.at(1)));
+    string message = current_instruction().ingredients.at(1).name;
+    trace(1, label) << message << end();
+  }
+  else if (SIZE(ingredients) == 1) {
+    assert(is_literal(current_instruction().ingredients.at(0)));
+    string message = current_instruction().ingredients.at(0).name;
+    cerr << "tracing " << message << '\n';
+    trace(1, "app") << message << end();
+  }
+  else {
+    raise << current_recipe_name() << ": 'trace' takes one or two ingredients rather than '" << current_instruction().to_string() << "'\n" << end();
   }
-  assert(is_literal(current_instruction().ingredients.at(0)));
-  string label = current_instruction().ingredients.at(0).name;
-  assert(is_literal(current_instruction().ingredients.at(1)));
-  string message = current_instruction().ingredients.at(1).name;
-  trace(1, label) << message << end();
   break;
 }
 
diff --git a/031address.cc b/031address.cc
index e79ff314..b7b395c6 100644
--- a/031address.cc
+++ b/031address.cc
@@ -25,7 +25,7 @@ recipe main [
 :(before "long long int base = x.value" following "void write_memory(reagent x, vector<double> data)")
 x = canonize(x);
 if (x.value == 0) {
-  raise << "can't write to location 0\n" << end();
+  raise << "can't write to location 0 in '" << current_instruction().to_string() << "'\n" << end();
   return;
 }
 
@@ -37,7 +37,7 @@ recipe main [
   1:address:number/lookup <- copy 34
 ]
 -mem: storing 34 in location 0
-+warn: can't write to location 0
++warn: can't write to location 0 in '1:address:number/lookup <- copy 34'
 
 :(code)
 reagent canonize(reagent x) {
diff --git a/042name.cc b/042name.cc
index 304d050d..45f5a3b2 100644
--- a/042name.cc
+++ b/042name.cc
@@ -148,7 +148,7 @@ recipe main [
   x:number/raw <- copy 0
 ]
 -name: assign x 1
-+warn: can't write to location 0
++warn: can't write to location 0 in 'x:number/raw <- copy 0'
 
 :(scenarios transform)
 :(scenario transform_names_warns_when_mixing_names_and_numeric_locations)
diff --git a/081run_interactive.cc b/081run_interactive.cc
index e8032100..59c79432 100644
--- a/081run_interactive.cc
+++ b/081run_interactive.cc
@@ -19,6 +19,7 @@ recipe main [
 //:   stringified output in case we want to print it to screen
 //:   any warnings encountered
 //:   simulated screen any prints went to
+//:   any 'app' layer traces generated
 :(before "End Primitive Recipe Declarations")
 RUN_INTERACTIVE,
 :(before "End Primitive Recipe Numbers")
@@ -26,7 +27,7 @@ Recipe_ordinal["run-interactive"] = RUN_INTERACTIVE;
 //? cerr << "run-interactive: " << RUN_INTERACTIVE << '\n'; //? 1
 :(before "End Primitive Recipe Implementations")
 case RUN_INTERACTIVE: {
-  products.resize(3);
+  products.resize(4);
   if (SIZE(ingredients) != 1) {
     raise << current_recipe_name() << ": 'run-interactive' requires exactly one ingredient, but got " << current_instruction().to_string() << '\n' << end();
     break;
@@ -40,6 +41,7 @@ case RUN_INTERACTIVE: {
     products.at(0).push_back(0);
     products.at(1).push_back(trace_contents("warn"));
     products.at(2).push_back(0);
+    products.at(3).push_back(trace_contents("app"));
     clean_up_interactive();
     break;  // done with this instruction
   }
@@ -81,7 +83,8 @@ bool run_interactive(long long int address) {
   if (!Trace_stream) {
     Trace_file = "";  // if there wasn't already a stream we don't want to save it
     Trace_stream = new trace_stream;
-    Trace_stream->collect_layer = "warn";
+    Trace_stream->collect_layers.insert("warn");
+    Trace_stream->collect_layers.insert("app");
   }
   // call run(string) but without the scheduling
   // we won't create a local scope so that we can get to the new screen after
@@ -164,7 +167,7 @@ void record_products(const instruction& instruction, const vector<vector<double>
 }
 :(before "Complete Call Fallthrough")
 if (current_instruction().operation == RUN_INTERACTIVE && !current_instruction().products.empty()) {
-  assert(SIZE(current_instruction().products) <= 3);
+  assert(SIZE(current_instruction().products) <= 4);
   // Send the results of the most recently executed instruction, regardless of
   // call depth, to be converted to string and potentially printed to string.
   vector<double> result;
@@ -180,6 +183,12 @@ if (current_instruction().operation == RUN_INTERACTIVE && !current_instruction()
     screen.push_back(Memory[SCREEN]);
     write_memory(current_instruction().products.at(2), screen);
   }
+  if (SIZE(current_instruction().products) >= 4) {
+//?     cerr << "emitting trace\n"; //? 1
+    vector<double> trace;
+    trace.push_back(trace_contents("app"));
+    write_memory(current_instruction().products.at(3), trace);
+  }
 }
 
 //: clean up reply after we've popped it off the call-stack
@@ -197,8 +206,8 @@ void clean_up_interactive() {
   Trace_stream->newline();  // flush trace
   Hide_warnings = false;
   Running_interactive = false;
-  // hack: assume collect_layer isn't set anywhere else
-  if (Trace_stream->collect_layer == "warn") {
+  // hack: assume collect_layers isn't set anywhere else
+  if (Trace_stream->is_narrowly_collecting("warn")) {
     delete Trace_stream;
     Trace_stream = NULL;
   }
@@ -250,7 +259,9 @@ bool is_mu_string(const reagent& x) {
 
 long long int trace_contents(const string& layer) {
   if (!Trace_stream) return 0;
+//?   cerr << "trace stream exists\n"; //? 1
   if (trace_count(layer) <= 0) return 0;
+//?   cerr << layer << " has something\n"; //? 1
   ostringstream out;
   for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
     if (p->label != layer) continue;
@@ -258,6 +269,7 @@ long long int trace_contents(const string& layer) {
     if (*--p->contents.end() != '\n') out << '\n';
   }
   assert(!out.str().empty());
+//?   cerr << layer << ":\n" << out.str() << "\n--\n"; //? 1
   return new_mu_string(out.str());
 }
 
@@ -281,7 +293,7 @@ case RELOAD: {
   if (!Trace_stream) {
     Trace_file = "";  // if there wasn't already a stream we don't want to save it
     Trace_stream = new trace_stream;
-    Trace_stream->collect_layer = "warn";
+    Trace_stream->collect_layers.insert("warn");
   }
   Hide_warnings = true;
   Disable_redefine_warnings = true;
@@ -291,7 +303,8 @@ case RELOAD: {
   Disable_redefine_warnings = false;
   Hide_warnings = false;
   products.at(0).push_back(trace_contents("warn"));
-  if (Trace_stream->collect_layer == "warn") {
+  // hack: assume collect_layers isn't set anywhere else
+  if (Trace_stream->is_narrowly_collecting("warn")) {
     delete Trace_stream;
     Trace_stream = NULL;
   }
diff --git a/build_and_test_until b/build_and_test_until
index b83db304..20845064 100755
--- a/build_and_test_until
+++ b/build_and_test_until
@@ -9,5 +9,5 @@ make enumerate/enumerate
 ./tangle/tangle $(./enumerate/enumerate --until $* |grep -v '.mu$') |grep -v "^\s*//:" > mu.cc
 cat /dev/null $(./enumerate/enumerate --until $* |grep '.mu$') > core.mu
 make autogenerated_lists
-make valgrind
-#? make test #? 2
+make valgrind #? 1
+#? make test #? 3
diff --git a/edit.mu b/edit.mu
index a19fd07d..82f81c5d 100644
--- a/edit.mu
+++ b/edit.mu
@@ -2673,10 +2673,12 @@ container sandbox-data [
   data:address:array:character
   response:address:array:character
   warnings:address:array:character
+  trace:address:array:character
   expected-response:address:array:character
   # coordinates to track clicks
   starting-row-on-screen:number
   response-starting-row-on-screen:number
+  display-trace?:boolean
   screen:address:screen  # prints in the sandbox go here
   next-sandbox:address:sandbox-data
 ]
@@ -2812,9 +2814,9 @@ recipe run-sandboxes [
     data <- get-address *curr, data:offset
     response:address:address:array:character <- get-address *curr, response:offset
     warnings:address:address:array:character <- get-address *curr, warnings:offset
+    trace:address:address:array:character <- get-address *curr, trace:offset
     fake-screen:address:address:screen <- get-address *curr, screen:offset
-    *response, *warnings, *fake-screen <- run-interactive *data
-#?     $print *warnings, [ ], **warnings, 10/newline
+    *response, *warnings, *fake-screen, *trace <- run-interactive *data
     curr <- get *curr, next-sandbox:offset
     loop
   }
@@ -2901,6 +2903,7 @@ recipe render-sandboxes [
   sandbox-response:address:array:character <- get *sandbox, response:offset
   sandbox-warnings:address:array:character <- get *sandbox, warnings:offset
   sandbox-screen:address <- get *sandbox, screen:offset
+  +render-sandbox-results
   {
     break-unless sandbox-warnings
     *response-starting-row <- copy 0  # no response
@@ -3237,7 +3240,7 @@ recipe foo [
   ]
   # click somewhere on the sandbox
   assume-console [
-    left-click 4, 30
+    left-click 3, 30
   ]
   run [
     event-loop screen:address, console:address, 3:address:programming-environment-data
@@ -3623,6 +3626,150 @@ after +render-sandbox-response [
   }
 ]
 
+## click on the code typed into a sandbox to toggle its trace
+
+scenario sandbox-click-on-code-toggles-app-trace [
+  $close-trace
+  assume-screen 40/width, 10/height
+  # basic recipe
+  1:address:array:character <- new [ 
+recipe foo [
+  trace [abc]
+]]
+  # run it
+  2:address:array:character <- new [foo]
+  assume-console [
+    press 65532  # F4
+  ]
+  3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
+  event-loop screen:address, console:address, 3:address:programming-environment-data
+  screen-should-contain [
+    .                     run (F4)           .
+    .                    ┊                   .
+    .recipe foo [        ┊━━━━━━━━━━━━━━━━━━━.
+    .  trace [abc]       ┊                  x.
+    .]                   ┊foo                .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━.
+    .                    ┊                   .
+  ]
+  # click on the 'foo' line in the sandbox
+  assume-console [
+    left-click 4, 21
+  ]
+  run [
+    event-loop screen:address, console:address, 3:address:programming-environment-data
+  ]
+  # trace now printed
+  screen-should-contain [
+    .                     run (F4)           .
+    .                    ┊                   .
+    .recipe foo [        ┊━━━━━━━━━━━━━━━━━━━.
+    .  trace [abc]       ┊                  x.
+    .]                   ┊foo                .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊abc                .
+    .                    ┊━━━━━━━━━━━━━━━━━━━.
+    .                    ┊                   .
+  ]
+  screen-should-contain-in-color 245/grey, [
+    .                                        .
+    .                    ┊                   .
+    .                    ┊━━━━━━━━━━━━━━━━━━━.
+    .                    ┊                  x.
+    .                    ┊                   .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊abc                .
+    .                    ┊━━━━━━━━━━━━━━━━━━━.
+    .                    ┊                   .
+  ]
+  # click again on the same region
+  assume-console [
+    left-click 4, 25
+  ]
+  run [
+    event-loop screen:address, console:address, 3:address:programming-environment-data
+  ]
+  # trace hidden again
+  screen-should-contain [
+    .                     run (F4)           .
+    .                    ┊                   .
+    .recipe foo [        ┊━━━━━━━━━━━━━━━━━━━.
+    .  trace [abc]       ┊                  x.
+    .]                   ┊foo                .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━.
+    .                    ┊                   .
+  ]
+]
+
+# clicks on sandbox code toggle its display-trace? flag
+after +global-touch [
+  # right side of screen? check if it's inside the code of any sandbox
+  {
+    sandbox-left-margin:number <- get *current-sandbox, left:offset
+    click-column:number <- get *t, column:offset
+    on-sandbox-side?:boolean <- greater-or-equal click-column, sandbox-left-margin
+    break-unless on-sandbox-side?
+    first-sandbox:address:sandbox-data <- get *env, sandbox:offset
+    break-unless first-sandbox
+    first-sandbox-begins:number <- get *first-sandbox, starting-row-on-screen:offset
+    click-row:number <- get *t, row:offset
+    below-sandbox-editor?:boolean <- greater-or-equal click-row, first-sandbox-begins
+    break-unless below-sandbox-editor?
+    # identify the sandbox whose code is being clicked on
+    sandbox:address:sandbox-data <- find-click-in-sandbox-code env, click-row
+    break-unless sandbox
+    # toggle its display-trace? property
+    x:address:boolean <- get-address *sandbox, display-trace?:offset
+    *x <- not *x
+    screen <- render-sandbox-side screen, env, 1/clear
+    # no change in cursor
+    show-screen screen
+    loop +next-event:label
+  }
+]
+
+recipe find-click-in-sandbox-code [
+  local-scope
+  env:address:programming-environment-data <- next-ingredient
+  click-row:number <- next-ingredient
+  # assert click-row >= sandbox.starting-row-on-screen
+  sandbox:address:sandbox-data <- get *env, sandbox:offset
+  start:number <- get *sandbox, starting-row-on-screen:offset
+  clicked-on-sandboxes?:boolean <- greater-or-equal click-row, start
+  assert clicked-on-sandboxes?, [extract-sandbox called on click to sandbox editor]
+  # while click-row < sandbox.next-sandbox.starting-row-on-screen
+  {
+    next-sandbox:address:sandbox-data <- get *sandbox, next-sandbox:offset
+    break-unless next-sandbox
+    next-start:number <- get *next-sandbox, starting-row-on-screen:offset
+    found?:boolean <- lesser-than click-row, next-start
+    break-if found?
+    sandbox <- copy next-sandbox
+    loop
+  }
+  # return sandbox if click is in its code region
+  response-starting-row:number <- get *sandbox, response-starting-row-on-screen:offset
+  click-above-response?:boolean <- lesser-than click-row, response-starting-row
+  start:number <- get *sandbox, starting-row-on-screen:offset
+  click-below-menu?:boolean <- greater-than click-row, start
+  click-on-sandbox-code?:boolean <- and click-above-response?, click-below-menu?
+  {
+    break-if click-on-sandbox-code?
+    reply 0/no-click-in-sandbox-output
+  }
+  reply sandbox
+]
+
+# when rendering a sandbox, dump its trace if display-trace? property is set
+after +render-sandbox-results [
+  {
+    display-trace?:boolean <- get *sandbox, display-trace?:offset
+    break-unless display-trace?
+    sandbox-trace:address:array:character <- get *sandbox, trace:offset
+    break-unless sandbox-trace  # nothing to print; move on
+    row, screen <- render-string, screen, sandbox-trace, left, right, 245/grey, row
+    jump +render-sandbox-end:label
+  }
+]
+
 ## handling malformed programs
 
 scenario run-shows-warnings-in-get [