about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-04-15 15:23:46 -0700
committerKartik K. Agaram <vc@akkartik.com>2015-04-15 15:23:46 -0700
commitd6852155e5308e2bb347402bf9e069c7153c2319 (patch)
tree3ef96705d73b339e0d934aa69cf8a6bd2cd0ed7f
parent8d09d030312098cb314619b0b5d0fc08e78e5562 (diff)
downloadmu-d6852155e5308e2bb347402bf9e069c7153c2319.tar.gz
1068 - better line numbers
Line numbers were broken after inserting some lines from elsewhere. Now
we add a #line directive not just before the lines but also after.
-rw-r--r--cpp/tangle/030tangle.cc61
-rw-r--r--cpp/tangle/030tangle.test.cc38
2 files changed, 90 insertions, 9 deletions
diff --git a/cpp/tangle/030tangle.cc b/cpp/tangle/030tangle.cc
index 59533ce8..adbf925b 100644
--- a/cpp/tangle/030tangle.cc
+++ b/cpp/tangle/030tangle.cc
@@ -1,3 +1,4 @@
+#include<assert.h>
 #include<sys/param.h>
 
 size_t Line_number = 0;
@@ -17,6 +18,7 @@ int tangle(int argc, const char* argv[]) {
 
 void tangle(istream& in, list<string>& out) {
   Line_number = 1;
+  out.push_back(line_directive(Line_number, Filename));
   string curr_line;
   while (!in.eof()) {
     getline(in, curr_line);
@@ -33,14 +35,7 @@ string Toplevel = "run";
 
 void process_next_hunk(istream& in, const string& directive, list<string>& out) {
   list<string> hunk;
-  {
-    ostringstream line_directive;
-    if (Filename.empty())
-      line_directive << "#line " << Line_number;
-    else
-      line_directive << "#line " << Line_number << " \"" << Filename << '"';
-    hunk.push_back(line_directive.str());
-  }
+  hunk.push_back(line_directive(Line_number, Filename));
   string curr_line;
   while (!in.eof()) {
     std::streampos old = in.tellg();
@@ -85,20 +80,29 @@ void process_next_hunk(istream& in, const string& directive, list<string>& out)
       return;
     }
 
+    size_t end_line_number = 0;
+    string end_filename;
+    scan_up_to_last_line_directive(target, out.begin(), end_line_number, end_filename);
+
     indent_all(hunk, target);
 
     if (cmd == "before") {
+      hunk.push_back(line_directive(end_line_number, end_filename));
       out.splice(target, hunk);
     }
     else if (cmd == "after") {
+      ++end_line_number;
+      hunk.push_back(line_directive(end_line_number, end_filename));
       ++target;
       out.splice(target, hunk);
     }
     else if (cmd == "replace" || cmd == "delete") {
+      hunk.push_back(line_directive(end_line_number, end_filename));
       out.splice(target, hunk);
       out.erase(target);
     }
     else if (cmd == "replace{}" || cmd == "delete{}") {
+      hunk.push_back(line_directive(end_line_number, end_filename));
       if (find_trim(hunk, ":OLD_CONTENTS") == hunk.end()) {
         out.splice(target, hunk);
         out.erase(target, balancing_curly(target));
@@ -369,3 +373,44 @@ string trim(const string& s) {
   ++last;
   return string(first, last);
 }
+
+string line_directive(size_t line_number, string filename) {
+  ostringstream result;
+  if (filename.empty())
+    result << "#line " << line_number;
+  else
+    result << "#line " << line_number << " \"" << filename << '"';
+  return result.str();
+}
+
+void scan_up_to_last_line_directive(list<string>::iterator p, list<string>::iterator begin, size_t& line_number, string& filename) {
+//?   cout << "scan: " << *p << " until " << *begin << '\n'; //? 1
+  int delta = 0;
+  if (p != begin) {
+    for (--p; p != begin; --p) {
+//?       cout << "  " << *p << ' ' << delta << '\n'; //? 1
+      if (starts_with(*p, "#line")) continue;
+//?       cout << "incrementing\n"; //? 1
+      ++delta;
+    }
+//?     cout << "delta: " << delta << '\n'; //? 1
+  }
+  if (p == begin) {
+    assert(starts_with(*p, "#line"));
+//?     cout << "hit begin\n";
+//?     line_number = delta;
+//?     return;
+  }
+  istringstream in(*p);
+  string directive_;
+  in >> directive_;
+  assert(directive_ == "#line");
+  in >> line_number;
+  line_number += delta;
+//?   cout << line_number << '\n'; //? 1
+  if (in.eof()) return;
+  in >> filename;
+  if (filename[0] == '"') filename.erase(0, 1);
+  if (filename[filename.size()-1] == '"') filename.erase(filename.size()-1);
+//?   cout << filename << '\n'; //? 1
+}
diff --git a/cpp/tangle/030tangle.test.cc b/cpp/tangle/030tangle.test.cc
index 4bc9fd6f..4aab6316 100644
--- a/cpp/tangle/030tangle.test.cc
+++ b/cpp/tangle/030tangle.test.cc
@@ -9,7 +9,7 @@ void test_tangle_with_linenumber() {
   istringstream in("a\nb\nc\n:(before b)\nd\n");
   list<string> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a#line 5dbc");
+  CHECK_TRACE_CONTENTS("tangle", "a#line 5d#line 2bc");
 }
 
 void test_tangle_with_filename() {
@@ -21,6 +21,33 @@ void test_tangle_with_filename() {
   CHECK_TRACE_CONTENTS("tangle", "a#line 5 \"foo\"dbc");
 }
 
+void test_tangle_with_multiple_filenames() {
+  istringstream in1("a\nb\nc");
+  list<string> dummy;
+  Filename = "foo";
+  tangle(in1, dummy);
+  CLEAR_TRACE;
+  Filename = "bar";
+  istringstream in2(":(before b)\nd\n");
+  tangle(in2, dummy);
+  Filename = "";
+  CHECK_TRACE_CONTENTS("tangle", "a#line 2 \"bar\"d#line 2 \"foo\"bc");
+}
+
+void test_tangle_with_multiple_filenames_after() {
+  istringstream in1("a\nb\nc");
+  list<string> dummy;
+  Filename = "foo";
+  tangle(in1, dummy);
+  CLEAR_TRACE;
+  Filename = "bar";
+  istringstream in2(":(after b)\nd\n");
+  tangle(in2, dummy);
+  Filename = "";
+  CHECK_TRACE_CONTENTS("tangle", "ab#line 2 \"bar\"d#line 3 \"foo\"c");
+//?   exit(0); //? 1
+}
+
 void test_tangle2() {
   istringstream in("a\nb\nc\n:(after b)\nd\n");
   list<string> dummy;
@@ -114,6 +141,7 @@ void test_tangle_supports_scenarios() {
   istringstream in(":(scenario does_bar)\nabc def\n+layer1: pqr\n+layer2: xyz");
   list<string> lines;
   tangle(in, lines);
+  CHECK_EQ(lines.front(), "#line 1");  lines.pop_front();
   CHECK_EQ(lines.front(), "#line 2");  lines.pop_front();
   CHECK_EQ(lines.front(), "TEST(does_bar)");  lines.pop_front();
   CHECK_EQ(lines.front(), "  run(\"abc def\\n\");");  lines.pop_front();
@@ -126,6 +154,7 @@ void test_tangle_supports_configurable_toplevel() {
   istringstream in(":(scenarios foo)\n:(scenario does_bar)\nabc def\n+layer1: pqr");
   list<string> lines;
   tangle(in, lines);
+  CHECK_EQ(lines.front(), "#line 1");  lines.pop_front();
   CHECK_EQ(lines.front(), "#line 3");  lines.pop_front();
   CHECK_EQ(lines.front(), "TEST(does_bar)");  lines.pop_front();
   CHECK_EQ(lines.front(), "  foo(\"abc def\\n\");");  lines.pop_front();
@@ -141,6 +170,7 @@ void test_tangle_can_hide_warnings_in_scenarios() {
   istringstream in(":(scenario does_bar)\nhide warnings\nabc def\n+layer1: pqr\n+layer2: xyz");
   list<string> lines;
   tangle(in, lines);
+  CHECK_EQ(lines.front(), "#line 1");  lines.pop_front();
   CHECK_EQ(lines.front(), "#line 2");  lines.pop_front();
   CHECK_EQ(lines.front(), "TEST(does_bar)");  lines.pop_front();
   CHECK_EQ(lines.front(), "  Hide_warnings = true;");  lines.pop_front();
@@ -154,6 +184,7 @@ void test_tangle_supports_strings_in_scenarios() {
   istringstream in(":(scenario does_bar)\nabc \"def\"\n+layer1: pqr\n+layer2: \"xyz\"");
   list<string> lines;
   tangle(in, lines);
+  CHECK_EQ(lines.front(), "#line 1");  lines.pop_front();
   CHECK_EQ(lines.front(), "#line 2");  lines.pop_front();
   CHECK_EQ(lines.front(), "TEST(does_bar)");  lines.pop_front();
   CHECK_EQ(lines.front(), "  run(\"abc \\\"def\\\"\\n\");");  lines.pop_front();
@@ -166,6 +197,7 @@ void test_tangle_supports_strings_in_scenarios2() {
   istringstream in(":(scenario does_bar)\nabc \"\"\n+layer1: pqr\n+layer2: \"\"");
   list<string> lines;
   tangle(in, lines);
+  CHECK_EQ(lines.front(), "#line 1");  lines.pop_front();
   CHECK_EQ(lines.front(), "#line 2");  lines.pop_front();
   CHECK_EQ(lines.front(), "TEST(does_bar)");  lines.pop_front();
   CHECK_EQ(lines.front(), "  run(\"abc \\\"\\\"\\n\");");  lines.pop_front();
@@ -178,6 +210,7 @@ void test_tangle_supports_multiline_input_in_scenarios() {
   istringstream in(":(scenario does_bar)\nabc def\n  efg\n+layer1: pqr\n+layer2: \"\"");
   list<string> lines;
   tangle(in, lines);
+  CHECK_EQ(lines.front(), "#line 1");  lines.pop_front();
   CHECK_EQ(lines.front(), "#line 2");  lines.pop_front();
   CHECK_EQ(lines.front(), "TEST(does_bar)");  lines.pop_front();
   CHECK_EQ(lines.front(), "  run(\"abc def\\n  efg\\n\");");  lines.pop_front();
@@ -190,6 +223,7 @@ void test_tangle_supports_reset_in_scenarios() {
   istringstream in(":(scenario does_bar)\nabc def\n===\nefg\n+layer1: pqr\n+layer2: \"\"");
   list<string> lines;
   tangle(in, lines);
+  CHECK_EQ(lines.front(), "#line 1");  lines.pop_front();
   CHECK_EQ(lines.front(), "#line 2");  lines.pop_front();
   CHECK_EQ(lines.front(), "TEST(does_bar)");  lines.pop_front();
   CHECK_EQ(lines.front(), "  run(\"abc def\\n\");");  lines.pop_front();
@@ -204,6 +238,7 @@ void test_tangle_can_check_for_absence_at_end_of_scenarios() {
   istringstream in(":(scenario does_bar)\nabc def\n  efg\n+layer1: pqr\n-layer1: xyz");
   list<string> lines;
   tangle(in, lines);
+  CHECK_EQ(lines.front(), "#line 1");  lines.pop_front();
   CHECK_EQ(lines.front(), "#line 2");  lines.pop_front();
   CHECK_EQ(lines.front(), "TEST(does_bar)");  lines.pop_front();
   CHECK_EQ(lines.front(), "  run(\"abc def\\n  efg\\n\");");  lines.pop_front();
@@ -217,6 +252,7 @@ void test_tangle_can_check_for_absence_at_end_of_scenarios2() {
   istringstream in(":(scenario does_bar)\nabc def\n  efg\n-layer1: pqr\n-layer1: xyz");
   list<string> lines;
   tangle(in, lines);
+  CHECK_EQ(lines.front(), "#line 1");  lines.pop_front();
   CHECK_EQ(lines.front(), "#line 2");  lines.pop_front();
   CHECK_EQ(lines.front(), "TEST(does_bar)");  lines.pop_front();
   CHECK_EQ(lines.front(), "  run(\"abc def\\n  efg\\n\");");  lines.pop_front();