Bug 1005471 - Scratchpad "Jump to line" should preset input value based on current selection, handle LINE:COLUMN as well;r+=bgrins
authoranaran <adrian.aichner@gmail.com>
Mon, 02 Jun 2014 15:33:20 -0700
changeset 198726 d75f1adf40d42cfe00545aff3f8d9f60a67cc74f
parent 198725 a81285b5fabf53425ba074012ccbc2306e5310b1
child 198727 ab97b9288474d69d1d873b028f6877f21f9e5cbe
push id5990
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:40:24 +0000
treeherdermozilla-aurora@0796197efbc9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1005471, 100644
milestone32.0a1
Bug 1005471 - Scratchpad "Jump to line" should preset input value based on current selection, handle LINE:COLUMN as well;r+=bgrins --- browser/devtools/scratchpad/test/browser.ini | 2 + .../browser_scratchpad_pprint_error_goto_line.js | 66 ++++++++++ .../test/browser_scratchpad_run_error_goto_line.js | 49 ++++++++ browser/devtools/sourceeditor/editor.js | 29 ++++- browser/devtools/sourceeditor/test/browser.ini | 1 + .../sourceeditor/test/browser_editor_goto_line.js | 130 ++++++++++++++++++++ 6 files changed, 276 insertions(+), 1 deletions(-) create mode 100644 browser/devtools/scratchpad/test/browser_scratchpad_pprint_error_goto_line.js create mode 100644 browser/devtools/scratchpad/test/browser_scratchpad_run_error_goto_line.js create mode 100644 browser/devtools/sourceeditor/test/browser_editor_goto_line.js
browser/devtools/scratchpad/test/browser.ini
browser/devtools/scratchpad/test/browser_scratchpad_pprint_error_goto_line.js
browser/devtools/scratchpad/test/browser_scratchpad_run_error_goto_line.js
browser/devtools/sourceeditor/editor.js
browser/devtools/sourceeditor/test/browser.ini
browser/devtools/sourceeditor/test/browser_editor_goto_line.js
--- a/browser/devtools/scratchpad/test/browser.ini
+++ b/browser/devtools/scratchpad/test/browser.ini
@@ -18,22 +18,24 @@ support-files = head.js
 # [browser_scratchpad_confirm_close.js]
 # Disable test due to bug 807234 becoming basically permanent
 [browser_scratchpad_tab.js]
 [browser_scratchpad_wrong_window_focus.js]
 [browser_scratchpad_unsaved.js]
 [browser_scratchpad_falsy.js]
 [browser_scratchpad_edit_ui_updates.js]
 [browser_scratchpad_revert_to_saved.js]
+[browser_scratchpad_run_error_goto_line.js]
 [browser_scratchpad_contexts.js]
 [browser_scratchpad_execute_print.js]
 [browser_scratchpad_files.js]
 [browser_scratchpad_initialization.js]
 [browser_scratchpad_inspect.js]
 [browser_scratchpad_long_string.js]
 [browser_scratchpad_open.js]
 [browser_scratchpad_open_error_console.js]
 [browser_scratchpad_throw_output.js]
 [browser_scratchpad_pprint-02.js]
 [browser_scratchpad_pprint.js]
+[browser_scratchpad_pprint_error_goto_line.js]
 [browser_scratchpad_restore.js]
 [browser_scratchpad_tab_switch.js]
 [browser_scratchpad_ui.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_pprint_error_goto_line.js
@@ -0,0 +1,66 @@
+/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+    openScratchpad(runTests);
+  }, true);
+
+  content.location = "data:text/html;charset=utf8,test Scratchpad pretty print error goto line.";
+}
+
+function testJumpToPrettyPrintError(sp, error, remark) {
+  info("will test jumpToLine after prettyPrint error" + remark);
+    // CodeMirror lines and columns are 0-based, Scratchpad UI and error
+    // stack are 1-based.
+    is(/Invalid regexp flag \(3:10\)/.test(error), true, "prettyPrint expects error in editor text:\n" + error);
+    const errorLine = 3, errorColumn = 10;
+    const editorDoc = sp.editor.container.contentDocument;
+    sp.editor.jumpToLine();
+    const lineInput = editorDoc.querySelector("input");
+    const errorLocation = lineInput.value;
+    const [ inputLine, inputColumn ] = errorLocation.split(":");
+    is(inputLine, errorLine, "jumpToLine input field is set from editor selection (line)");
+    is(inputColumn, errorColumn, "jumpToLine input field is set from editor selection (column)");
+    EventUtils.synthesizeKey("VK_RETURN", { }, editorDoc.defaultView);
+    // CodeMirror lines and columns are 0-based, Scratchpad UI and error
+    // stack are 1-based.
+    const cursor = sp.editor.getCursor();
+    is(inputLine, cursor.line + 1, "jumpToLine goto error location (line)");
+    is(inputColumn, cursor.ch + 1, "jumpToLine goto error location (column)");
+}
+
+function runTests(sw, sp)
+{
+  sp.setText([
+    "// line 1",
+    "// line 2",
+    "var re = /a bad /regexp/; // line 3 is an obvious syntax error!",
+    "// line 4",
+    "// line 5",
+    ""
+  ].join("\n"));
+  sp.prettyPrint().then(aFulfill => {
+    ok(false, "Expecting Invalid regexp flag (3:10)");
+    finish();
+  }, error => {
+    testJumpToPrettyPrintError(sp, error, " (Bug 1005471, first time)");
+  });
+  sp.prettyPrint().then(aFulfill => {
+    ok(false, "Expecting Invalid regexp flag (3:10)");
+    finish();
+  }, error => {
+    // Second time verifies bug in earlier implementation fixed.
+    testJumpToPrettyPrintError(sp, error, " (second time)");
+    finish();
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_run_error_goto_line.js
@@ -0,0 +1,49 @@
+/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+    openScratchpad(runTests);
+  }, true);
+
+  content.location = "data:text/html;charset=utf8,test Scratchpad pretty print.";
+}
+
+function runTests(sw)
+{
+  const sp = sw.Scratchpad;
+  sp.setText([
+    "// line 1",
+    "// line 2",
+    "var re = /a bad /regexp/; // line 3 is an obvious syntax error!",
+    "// line 4",
+    "// line 5",
+    ""
+  ].join("\n"));
+  sp.run().then(() => {
+    // CodeMirror lines and columns are 0-based, Scratchpad UI and error
+    // stack are 1-based.
+    let errorLine = 3;
+    let editorDoc = sp.editor.container.contentDocument;
+    sp.editor.jumpToLine();
+    let lineInput = editorDoc.querySelector("input");
+    let inputLine = lineInput.value;
+    is(inputLine, errorLine, "jumpToLine input field is set from editor selection");
+    EventUtils.synthesizeKey("VK_RETURN", { }, editorDoc.defaultView);
+    // CodeMirror lines and columns are 0-based, Scratchpad UI and error
+    // stack are 1-based.
+    let cursor = sp.editor.getCursor();
+    is(cursor.line + 1, inputLine, "jumpToLine goto error location (line)");
+    is(cursor.ch + 1, 1, "jumpToLine goto error location (column)");
+    finish();
+  });
+}
--- a/browser/devtools/sourceeditor/editor.js
+++ b/browser/devtools/sourceeditor/editor.js
@@ -1,8 +1,9 @@
+/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
 /* vim:set ts=2 sw=2 sts=2 et tw=80:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Cu, Cc, Ci, components } = require("chrome");
@@ -15,16 +16,21 @@ const DETECT_INDENT = "devtools.editor.d
 const DETECT_INDENT_MAX_LINES = 500;
 const L10N_BUNDLE = "chrome://browser/locale/devtools/sourceeditor.properties";
 const XUL_NS      = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 // Maximum allowed margin (in number of lines) from top or bottom of the editor
 // while shifting to a line which was initially out of view.
 const MAX_VERTICAL_OFFSET = 3;
 
+// Match @Scratchpad/N:LINE[:COLUMN] or (LINE[:COLUMN]) anywhere at an end of
+// line in text selection.
+const RE_SCRATCHPAD_ERROR = /(?:@Scratchpad\/\d+:|\()(\d+):?(\d+)?(?:\)|\n)/;
+const RE_JUMP_TO_LINE = /^(\d+):?(\d+)?/;
+
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const events  = require("devtools/toolkit/event-emitter");
 
 Cu.import("resource://gre/modules/Services.jsm");
 const L10N = Services.strings.createBundle(L10N_BUNDLE);
 
 // CM_STYLES, CM_SCRIPTS and CM_IFRAME represent the HTML,
 // JavaScript and CSS that is injected into an iframe in
@@ -747,17 +753,38 @@ Editor.prototype = {
 
     inp.type = "text";
     inp.style.width = "10em";
     inp.style.MozMarginStart = "1em";
 
     div.appendChild(txt);
     div.appendChild(inp);
 
-    this.openDialog(div, (line) => this.setCursor({ line: line - 1, ch: 0 }));
+    if (!this.hasMultipleSelections()) {
+      let cm = editors.get(this);
+      let sel = cm.getSelection();
+      // Scratchpad inserts and selects a comment after an error happens:
+      // "@Scratchpad/1:10:2". Parse this to get the line and column.
+      // In the string above this is line 10, column 2.
+      let match = sel.match(RE_SCRATCHPAD_ERROR);
+      if (match) {
+        let [ , line, column ] = match;
+        inp.value = column ? line + ":" + column : line;
+        inp.selectionStart = inp.selectionEnd = inp.value.length;
+      }
+    }
+
+    this.openDialog(div, (line) => {
+      // Handle LINE:COLUMN as well as LINE
+      let match = line.toString().match(RE_JUMP_TO_LINE);
+      if (match) {
+        let [ , line, column ] = match;
+        this.setCursor({line: line - 1, ch: column ? column - 1 : 0 });
+      }
+    });
   },
 
   /**
    * Moves the content of the current line or the lines selected up a line.
    */
   moveLineUp: function () {
     let cm = editors.get(this);
     let start = cm.getCursor("start");
--- a/browser/devtools/sourceeditor/test/browser.ini
+++ b/browser/devtools/sourceeditor/test/browser.ini
@@ -17,16 +17,17 @@ support-files =
   css_statemachine_testcases.css
   css_statemachine_tests.json
   css_autocompletion_tests.json
   vimemacs.html
   head.js
 
 [browser_editor_basic.js]
 [browser_editor_cursor.js]
+[browser_editor_goto_line.js]
 [browser_editor_history.js]
 [browser_editor_markers.js]
 [browser_editor_movelines.js]
 [browser_editor_addons.js]
 [browser_codemirror.js]
 [browser_css_autocompletion.js]
 [browser_css_getInfo.js]
 [browser_css_statemachine.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/sourceeditor/test/browser_editor_goto_line.js
@@ -0,0 +1,130 @@
+/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function testJumpToLine (ed, inputLine, expectCursor) {
+  ed.jumpToLine();
+  let editorDoc = ed.container.contentDocument;
+  let lineInput = editorDoc.querySelector("input");
+  lineInput.value = inputLine;
+  EventUtils.synthesizeKey("VK_RETURN", { }, editorDoc.defaultView);
+  // CodeMirror lines and columns are 0-based, Scratchpad UI is 1-based.
+  ch(ed.getCursor(), expectCursor, "jumpToLine " + inputLine + " expects cursor " + expectCursor.toSource());
+}
+
+function test() {
+  waitForExplicitFinish();
+  setup((ed, win) => {
+    let textLines = [
+      "// line 1",
+      "//  line 2",
+      "//   line 3",
+      "//    line 4",
+      "//     line 5",
+      ""];
+    ed.setText(textLines.join("\n"));
+    waitForFocus(function () {
+      let testVectors = [
+        // Various useless inputs go to line 0, column 0 or do nothing.
+        ["",
+         {line:0, ch:0}],
+        [":",
+         {line:0, ch:0}],
+        [" ",
+         {line:0, ch:0}],
+        [" : ",
+         {line:0, ch:0}],
+        ["a:b",
+         {line:0, ch:0}],
+        ["LINE:COLUMN",
+         {line:0, ch:0}],
+        ["-1",
+         {line:0, ch:0}],
+        [":-1",
+         {line:0, ch:0}],
+        ["-1:-1",
+         {line:0, ch:0}],
+        ["0",
+         {line:0, ch:0}],
+        [":0",
+         {line:0, ch:0}],
+        ["0:0",
+         {line:0, ch:0}],
+        // Starting here expect data needs to get updated for length changes to
+        // "textLines" above.
+        // Just jump to line
+        ["1",
+         {line:0, ch:0}],
+        // Jump to second character in line
+        ["1:2",
+         {line:0, ch:1}],
+        // Jump to last character on line
+        ["1:9",
+         {line:0, ch:8}],
+        // Jump just after last character on line (end of line)
+        ["1:10",
+         {line:0, ch:9}],
+        // Jump one character past end of line (gets clamped to end of line)
+        ["1:11",
+         {line:0, ch:9}],
+        ["2",
+         {line:1, ch:0}],
+        ["2:2",
+         {line:1, ch:1}],
+        ["2:10",
+         {line:1, ch:9}],
+        ["2:11",
+         {line:1, ch:10}],
+        ["2:12",
+         {line:1, ch:10}],
+        ["3",
+         {line:2, ch:0}],
+        ["3:2",
+         {line:2, ch:1}],
+        ["3:11",
+         {line:2, ch:10}],
+        ["3:12",
+         {line:2, ch:11}],
+        ["3:13",
+         {line:2, ch:11}],
+        ["4",
+         {line:3, ch:0}],
+        ["4:2",
+         {line:3, ch:1}],
+        ["4:12",
+         {line:3, ch:11}],
+        ["4:13",
+         {line:3, ch:12}],
+        ["4:14",
+         {line:3, ch:12}],
+        ["5",
+         {line:4, ch:0}],
+        ["5:2",
+         {line:4, ch:1}],
+        ["5:13",
+         {line:4, ch:12}],
+        ["5:14",
+         {line:4, ch:13}],
+        ["5:15",
+         {line:4, ch:13}],
+        // One line beyond last newline in editor text:
+        ["6",
+         {line:5, ch:0}],
+        ["6:2",
+         {line:5, ch:0}],
+        // Two line beyond last newline in editor text (gets clamped):
+        ["7",
+         {line:5, ch:0}],
+        ["7:2",
+         {line:5, ch:0}]
+      ];
+      testVectors.forEach(function (vector) {
+        testJumpToLine(ed, vector[0], vector[1]);
+      });
+      teardown(ed, win);
+    });
+  });
+}