hterm 1.43: Implement reverse wraparound.

* Implement reverse wraparound, add a test.

BUG=chromium:393737

Change-Id: I00841d6553408abbeae90d8d51fac9d6b1c7b003
Reviewed-on: https://chromium-review.googlesource.com/208551
Reviewed-by: Marvelous Marius <mschilder@chromium.org>
Tested-by: Robert Ginda <rginda@chromium.org>
diff --git a/hterm/doc/changelog.txt b/hterm/doc/changelog.txt
index a2d5487..ca86cc3 100644
--- a/hterm/doc/changelog.txt
+++ b/hterm/doc/changelog.txt
@@ -1,3 +1,7 @@
+1.43, 2014-07-15, Implement reverse wraparound.
+
+* Implement reverse wraparound, add a test.
+
 1.42, 2014-06-25, Stop requesting notification permission.
 
 * This code depended on a user action, but the preference observers aren't
diff --git a/hterm/js/hterm_terminal.js b/hterm/js/hterm_terminal.js
index 2849faf..35f96ea 100644
--- a/hterm/js/hterm_terminal.js
+++ b/hterm/js/hterm_terminal.js
@@ -2036,10 +2036,37 @@
 /**
  * Move the cursor left a specified number of columns.
  *
+ * If reverse wraparound mode is enabled and the previous row wrapped into
+ * the current row then we back up through the wraparound as well.
+ *
  * @param {integer} count The number of columns to move the cursor.
  */
 hterm.Terminal.prototype.cursorLeft = function(count) {
-  return this.cursorRight(-(count || 1));
+  count = count || 1;
+
+  if (count < 1)
+    return;
+
+  var currentColumn = this.screen_.cursorPosition.column;
+  var newColumn = currentColumn - count;
+  if (newColumn < 0) {
+    if (this.options_.reverseWraparound) {
+      var currentRow = this.screen_.cursorPosition.row;
+      if (currentRow > 0 &&
+          this.screen_.rowsArray[currentRow - 1].getAttribute(
+              'line-overflow')) {
+        this.setCursorPosition(currentRow - 1, this.screenSize.width - 1);
+        if (count - currentColumn - 1)
+          this.cursorLeft(count - currentColumn - 1);
+        return;
+      }
+    }
+
+    newColumn = 0;
+  }
+
+  this.setCursorColumn(newColumn);
+  return;
 };
 
 /**
@@ -2049,6 +2076,10 @@
  */
 hterm.Terminal.prototype.cursorRight = function(count) {
   count = count || 1;
+
+  if (count < 1)
+    return;
+
   var column = lib.f.clamp(this.screen_.cursorPosition.column + count,
                            0, this.screenSize.width - 1);
   this.setCursorColumn(column);
diff --git a/hterm/js/hterm_vt_tests.js b/hterm/js/hterm_vt_tests.js
index d0041dc..811c458 100644
--- a/hterm/js/hterm_vt_tests.js
+++ b/hterm/js/hterm_vt_tests.js
@@ -882,7 +882,7 @@
       result.assertEQ(row.childNodes[1].nodeName, 'SPAN', 'i: ' + i);
       result.assert(!!row.childNodes[1].style.color, 'i: ' + i);
       result.assert(!!row.childNodes[1].style.fontWeight == (i > 2), 'i: ' + i);
-      result.assert(
+      result.assertEQ(
           row.childNodes[1].style.fontStyle, (i == 1 ? 'italic' : ''),
           'i: ' + i);
     }
@@ -1250,6 +1250,49 @@
   });
 
 /**
+ * Test reverse wraparound.
+ */
+hterm.VT.Tests.addTest('reverse-wrap', function(result, cx) {
+    // A line ending with a hard CRLF.
+    var str = 'AAAA\r\n';
+
+    // Enough X's to wrap once and leave the cursor in the overflow state at
+    // the end of the third row.
+    for (var i = 0; i < this.visibleColumnCount * 2; i++)
+      str += 'X';
+
+    // CR to put us at col 0, backspace to put us at the last column of the
+    // previous row, if reverse wraparound is enabled.
+    str += '\r\bBB';
+
+    // Without reverse wraparound, we should get stuck at column 0 of the third
+    // row.
+    this.terminal.interpret(str);
+
+    result.assertEQ(this.terminal.getRowText(0), 'AAAA');
+    result.assertEQ(this.terminal.getRowText(1), 'XXXXXXXXXXXXXXX');
+    result.assertEQ(this.terminal.getRowText(2), 'BBXXXXXXXXXXXXX');
+
+    // With reverse wraparound, we'll back up to the previous row.
+    this.terminal.clearHome();
+    this.terminal.interpret('\x1b[?45h' + str);
+
+    result.assertEQ(this.terminal.getRowText(0), 'AAAA');
+    result.assertEQ(this.terminal.getRowText(1), 'XXXXXXXXXXXXXXB');
+    result.assertEQ(this.terminal.getRowText(2), 'BXXXXXXXXXXXXXX');
+
+    // But reverse wraparound shouldn't extend to the "AAAA" row, which ended
+    // with a \r\n rather than a soft column wrap.
+    this.terminal.interpret('\r\b\r\bCC');
+
+    result.assertEQ(this.terminal.getRowText(0), 'AAAA');
+    result.assertEQ(this.terminal.getRowText(1), 'CCXXXXXXXXXXXXB');
+    result.assertEQ(this.terminal.getRowText(2), 'BXXXXXXXXXXXXXX');
+
+    result.pass();
+  });
+
+/**
  * Test interactions between the cursor overflow bit and various
  * escape sequences.
  */