Bug 744021 - Source Editor jump to prev/next block should move to prev/next sibling block when caret has no parent block; r=msucan
authorGirish Sharma <scrapmachines@gmail.com>
Fri, 12 Oct 2012 22:27:18 +0530
changeset 111007 e87dd11b6c3e2fdc7b63c7296db4afe61f81fb07
parent 111006 51b26b095a27eaa681c70f091567485b482d5f31
child 111008 0161b4c400a18d8cbec2e8e45c3ebbcc9201638a
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersmsucan
bugs744021
milestone19.0a1
Bug 744021 - Source Editor jump to prev/next block should move to prev/next sibling block when caret has no parent block; r=msucan
browser/devtools/sourceeditor/source-editor-orion.jsm
browser/devtools/sourceeditor/test/Makefile.in
browser/devtools/sourceeditor/test/browser_bug729960_block_bracket_jump.js
browser/devtools/sourceeditor/test/browser_bug744021_next_prev_bracket_jump.js
--- a/browser/devtools/sourceeditor/source-editor-orion.jsm
+++ b/browser/devtools/sourceeditor/source-editor-orion.jsm
@@ -1226,38 +1226,55 @@ SourceEditor.prototype = {
     }
 
     let caretOffset = this.getCaretOffset() - 1;
     let matchingIndex = this._getMatchingBracketIndex(caretOffset);
 
     // If the caret is not at the closing bracket "}", find the index of the
     // opening bracket "{" for the current code block.
     if (matchingIndex == -1 || matchingIndex > caretOffset) {
+      matchingIndex = -1;
       let text = this.getText();
       let closingOffset = text.indexOf("}", caretOffset);
       while (closingOffset > -1) {
         let closingMatchingIndex = this._getMatchingBracketIndex(closingOffset);
         if (closingMatchingIndex < caretOffset && closingMatchingIndex != -1) {
           matchingIndex = closingMatchingIndex;
           break;
         }
         closingOffset = text.indexOf("}", closingOffset + 1);
       }
+      // Moving to the previous code block starting bracket if caret not inside
+      // any code block.
+      if (matchingIndex == -1) {
+        let lastClosingOffset = text.lastIndexOf("}", caretOffset);
+        while (lastClosingOffset > -1) {
+          let closingMatchingIndex =
+            this._getMatchingBracketIndex(lastClosingOffset);
+          if (closingMatchingIndex < caretOffset &&
+              closingMatchingIndex != -1) {
+            matchingIndex = closingMatchingIndex;
+            break;
+          }
+          lastClosingOffset = text.lastIndexOf("}", lastClosingOffset - 1);
+        }
+      }
     }
 
     if (matchingIndex > -1) {
-      this.setCaretOffset(matchingIndex);
+      this.setCaretOffset(matchingIndex + 1);
     }
 
     return true;
   },
 
   /**
-   * Moves the cursor to the matching closing bracket if at corresponding opening
-   * bracket, otherwise move to the closing bracket for the current block of code.
+   * Moves the cursor to the matching closing bracket if at corresponding
+   * opening bracket, otherwise move to the closing bracket for the current
+   * block of code.
    *
    * @private
    */
   _moveToBracketClosing: function SE__moveToBracketClosing()
   {
     let mode = this.getMode();
     // Returning early if not in JavaScipt or CSS mode.
     if (mode != SourceEditor.MODES.JAVASCRIPT &&
@@ -1266,26 +1283,41 @@ SourceEditor.prototype = {
     }
 
     let caretOffset = this.getCaretOffset();
     let matchingIndex = this._getMatchingBracketIndex(caretOffset - 1);
 
     // If the caret is not at the opening bracket "{", find the index of the
     // closing bracket "}" for the current code block.
     if (matchingIndex == -1 || matchingIndex < caretOffset) {
+      matchingIndex = -1;
       let text = this.getText();
       let openingOffset = text.lastIndexOf("{", caretOffset);
       while (openingOffset > -1) {
         let openingMatchingIndex = this._getMatchingBracketIndex(openingOffset);
         if (openingMatchingIndex > caretOffset) {
           matchingIndex = openingMatchingIndex;
           break;
         }
         openingOffset = text.lastIndexOf("{", openingOffset - 1);
       }
+      // Moving to the next code block ending bracket if caret not inside
+      // any code block.
+      if (matchingIndex == -1) {
+        let nextOpeningIndex = text.indexOf("{", caretOffset + 1);
+        while (nextOpeningIndex > -1) {
+          let openingMatchingIndex =
+            this._getMatchingBracketIndex(nextOpeningIndex);
+          if (openingMatchingIndex > caretOffset) {
+            matchingIndex = openingMatchingIndex;
+            break;
+          }
+          nextOpeningIndex = text.indexOf("{", nextOpeningIndex + 1);
+        }
+      }
     }
 
     if (matchingIndex > -1) {
       this.setCaretOffset(matchingIndex);
     }
 
     return true;
   },
--- a/browser/devtools/sourceeditor/test/Makefile.in
+++ b/browser/devtools/sourceeditor/test/Makefile.in
@@ -25,11 +25,12 @@ MOCHITEST_BROWSER_FILES = \
 		browser_bug707987_debugger_breakpoints.js \
 		browser_bug712982_line_ruler_click.js \
 		browser_bug725618_moveLines_shortcut.js \
 		browser_bug700893_dirty_state.js \
 		browser_bug729480_line_vertical_align.js \
 		browser_bug725430_comment_uncomment.js \
 		browser_bug731721_debugger_stepping.js \
 		browser_bug729960_block_bracket_jump.js \
+		browser_bug744021_next_prev_bracket_jump.js \
 		head.js \
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/sourceeditor/test/browser_bug729960_block_bracket_jump.js
+++ b/browser/devtools/sourceeditor/test/browser_bug729960_block_bracket_jump.js
@@ -53,39 +53,39 @@ function test() {
 
     // Setting caret at Line 1 bracket start.
     editor.setCaretOffset(19);
     EventUtils.synthesizeKey("]", {accelKey: true}, testWin);
     is(editor.getCaretOffset(), 220,
        "JS : Jump to closing bracket of the code block when caret at block start");
 
     EventUtils.synthesizeKey("[", {accelKey: true}, testWin);
-    is(editor.getCaretOffset(), 19,
+    is(editor.getCaretOffset(), 20,
        "JS : Jump to opening bracket of the code block when caret at block end");
 
     // Setting caret at Line 10 start.
     editor.setCaretOffset(161);
     EventUtils.synthesizeKey("[", {accelKey: true}, testWin);
-    is(editor.getCaretOffset(), 19,
+    is(editor.getCaretOffset(), 20,
        "JS : Jump to opening bracket of code block when inside the function");
 
     editor.setCaretOffset(161);
     EventUtils.synthesizeKey("]", {accelKey: true}, testWin);
     is(editor.getCaretOffset(), 220,
        "JS : Jump to closing bracket of code block when inside the function");
 
     // Setting caret at Line 6 start.
     editor.setCaretOffset(67);
     EventUtils.synthesizeKey("]", {accelKey: true}, testWin);
     is(editor.getCaretOffset(), 159,
        "JS : Jump to closing bracket in a nested function with caret inside");
 
     editor.setCaretOffset(67);
     EventUtils.synthesizeKey("[", {accelKey: true}, testWin);
-    is(editor.getCaretOffset(), 61,
+    is(editor.getCaretOffset(), 62,
        "JS : Jump to opening bracket in a nested function with caret inside");
 
     let CSSText = "#object {\n" +
                   "  property: value;\n" +
                   "  /* comment */\n" +
                   "}";
 
     editor.setMode(SourceEditor.MODES.CSS);
@@ -93,23 +93,23 @@ function test() {
 
     // Setting caret at Line 1 bracket start.
     editor.setCaretOffset(8);
     EventUtils.synthesizeKey("]", {accelKey: true}, testWin);
     is(editor.getCaretOffset(), 45,
        "CSS : Jump to closing bracket of the code block when caret at block start");
 
     EventUtils.synthesizeKey("[", {accelKey: true}, testWin);
-    is(editor.getCaretOffset(), 8,
+    is(editor.getCaretOffset(), 9,
        "CSS : Jump to opening bracket of the code block when caret at block end");
 
     // Setting caret at Line 3 start.
     editor.setCaretOffset(28);
     EventUtils.synthesizeKey("[", {accelKey: true}, testWin);
-    is(editor.getCaretOffset(), 8,
+    is(editor.getCaretOffset(), 9,
        "CSS : Jump to opening bracket of code block when inside the function");
 
     editor.setCaretOffset(28);
     EventUtils.synthesizeKey("]", {accelKey: true}, testWin);
     is(editor.getCaretOffset(), 45,
        "CSS : Jump to closing bracket of code block when inside the function");
 
     let HTMLText = "<html>\n" +
new file mode 100644
--- /dev/null
+++ b/browser/devtools/sourceeditor/test/browser_bug744021_next_prev_bracket_jump.js
@@ -0,0 +1,104 @@
+/* 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() {
+
+  let temp = {};
+  Cu.import("resource:///modules/source-editor.jsm", temp);
+  let SourceEditor = temp.SourceEditor;
+
+  waitForExplicitFinish();
+
+  let editor;
+
+  const windowUrl = "data:text/xml;charset=utf8,<?xml version='1.0'?><window " +
+    "xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul' " +
+    "title='test for bug 744021' width='600' height='500'><hbox flex='1'/>" +
+    "</window>";
+  const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable," +
+    "dialog=no";
+
+  let testWin = Services.ww.openWindow(null, windowUrl, "_blank",
+                                       windowFeatures, null);
+  testWin.addEventListener("load", function onWindowLoad() {
+    testWin.removeEventListener("load", onWindowLoad, false);
+    waitForFocus(initEditor, testWin);
+  }, false);
+
+  function initEditor()
+  {
+    let hbox = testWin.document.querySelector("hbox");
+    editor = new SourceEditor();
+    editor.init(hbox, {showLineNumbers: true}, editorLoaded);
+  }
+
+  function editorLoaded()
+  {
+    editor.focus();
+    let JSText = "function foo() {\n" +
+                 "  \n" +
+                 "  function level2() {\n" +
+                 "    \n" +
+                 "    function level3() {\n" +
+                 "      \n" +
+                 "    }\n" +
+                 "  }\n" +
+                 "  function bar() { /* Block Level 2 */ }\n" +
+                 "}\n" +
+                 "function baz() {\n" +
+                 "  \n" +
+                 "}";
+
+    editor.setMode(SourceEditor.MODES.JAVASCRIPT);
+    editor.setText(JSText);
+
+    // Setting caret at end of line 11 (function baz() {).
+    editor.setCaretOffset(147);
+    EventUtils.synthesizeKey("[", {accelKey: true}, testWin);
+    is(editor.getCaretOffset(), 16,
+       "JS : Jump to opening bracket of previous sibling block when no parent");
+
+    EventUtils.synthesizeKey("]", {accelKey: true}, testWin);
+    is(editor.getCaretOffset(), 129,
+       "JS : Jump to closing bracket of same code block");
+
+    EventUtils.synthesizeKey("]", {accelKey: true}, testWin);
+    is(editor.getCaretOffset(), 151,
+       "JS : Jump to closing bracket of next sibling code block");
+
+    let CSSText = "#object1 {\n" +
+                  "  property: value;\n" +
+                  "  /* comment */\n" +
+                  "}\n" +
+                  ".class1 {\n" +
+                  "  property: value;\n" +
+                  "}";
+
+    editor.setMode(SourceEditor.MODES.CSS);
+    editor.setText(CSSText);
+
+    // Setting caret at Line 5 end (.class1 {).
+    editor.setCaretOffset(57);
+    EventUtils.synthesizeKey("[", {accelKey: true}, testWin);
+    is(editor.getCaretOffset(), 10,
+       "CSS : Jump to opening bracket of previous sibling code block");
+
+    EventUtils.synthesizeKey("]", {accelKey: true}, testWin);
+    is(editor.getCaretOffset(), 46,
+       "CSS : Jump to closing bracket of same code block");
+
+    EventUtils.synthesizeKey("]", {accelKey: true}, testWin);
+    is(editor.getCaretOffset(), 77,
+       "CSS : Jump to closing bracket of next sibling code block");
+
+    editor.destroy();
+
+    testWin.close();
+    testWin = editor = null;
+
+    waitForFocus(finish, window);
+  }
+}