Bug 739192 - Source Editor should toggle comment on line or selection with one key binding; r=msucan
authorGirish Sharma <scrapmachines@gmail.com>
Mon, 09 Apr 2012 13:31:45 +0300
changeset 94544 cafe030aca8aa6d1a03fe9749644af10e5b7ddf8
parent 94543 7e22d83d5c6ce2124f2c8363144230c9a1f3d2c2
child 94545 c0946061c57f8b46123880df73afd7e82602f473
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmsucan
bugs739192
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 739192 - Source Editor should toggle comment on line or selection with one key binding; r=msucan
browser/devtools/sourceeditor/source-editor-orion.jsm
browser/devtools/sourceeditor/test/browser_bug725430_comment_uncomment.js
--- a/browser/devtools/sourceeditor/source-editor-orion.jsm
+++ b/browser/devtools/sourceeditor/source-editor-orion.jsm
@@ -148,26 +148,20 @@ const DEFAULT_KEYBINDINGS = [
   },
   {
     action: "Move Lines Down",
     code: Ci.nsIDOMKeyEvent.DOM_VK_DOWN,
     ctrl: Services.appinfo.OS == "Darwin",
     alt: true,
   },
   {
-    action: "Comment",
+    action: "Comment/Uncomment",
     code: Ci.nsIDOMKeyEvent.DOM_VK_SLASH,
     accel: true,
   },
-  {
-    action: "Uncomment",
-    code: Ci.nsIDOMKeyEvent.DOM_VK_SLASH,
-    accel: true,
-    shift: true,
-  },
 ];
 
 var EXPORTED_SYMBOLS = ["SourceEditor"];
 
 /**
  * The SourceEditor object constructor. The SourceEditor component allows you to
  * provide users with an editor tailored to the specific needs of editing source
  * code, aimed primarily at web developers.
@@ -398,18 +392,17 @@ SourceEditor.prototype = {
       "tab": [this._doTab, this],
       "Unindent Lines": [this._doUnindentLines, this],
       "enter": [this._doEnter, this],
       "Find...": [this.ui.find, this.ui],
       "Find Next Occurrence": [this.ui.findNext, this.ui],
       "Find Previous Occurrence": [this.ui.findPrevious, this.ui],
       "Goto Line...": [this.ui.gotoLine, this.ui],
       "Move Lines Down": [this._moveLines, this],
-      "Comment": [this._doComment, this],
-      "Uncomment": [this._doUncomment, this],
+      "Comment/Uncomment": [this._doCommentUncomment, this],
     };
 
     for (let name in actions) {
       let action = actions[name];
       this._view.setAction(name, action[0].bind(action[1]));
     }
 
     this._view.setAction("Move Lines Up", this._moveLines.bind(this, true));
@@ -1046,16 +1039,73 @@ SourceEditor.prototype = {
         break;
       default:
         return null;
     }
     return {line: line, blockStart: blockCommentStart, blockEnd: blockCommentEnd};
   },
 
   /**
+   * Decide whether to comment the selection/current line or to uncomment it.
+   *
+   * @private
+   */
+  _doCommentUncomment: function SE__doCommentUncomment()
+  {
+    if (this.readOnly) {
+      return false;
+    }
+
+    let commentObject = this._getCommentStrings();
+    if (!commentObject) {
+      return false;
+    }
+
+    let selection = this.getSelection();
+    let model = this._model;
+    let firstLine = model.getLineAtOffset(selection.start);
+    let lastLine = model.getLineAtOffset(selection.end);
+
+    // Checks for block comment.
+    let firstLineText = model.getLine(firstLine);
+    let lastLineText = model.getLine(lastLine);
+    let openIndex = firstLineText.indexOf(commentObject.blockStart);
+    let closeIndex = lastLineText.lastIndexOf(commentObject.blockEnd);
+    if (openIndex != -1 && closeIndex != -1 &&
+        (firstLine != lastLine ||
+        (closeIndex - openIndex) >= commentObject.blockStart.length)) {
+      return this._doUncomment();
+    }
+
+    if (!commentObject.line) {
+      return this._doComment();
+    }
+
+    // If the selection is not a block comment, check for the first and the last
+    // lines to be line commented.
+    let firstLastCommented = [firstLineText,
+                              lastLineText].every(function(aLineText) {
+      let openIndex = aLineText.indexOf(commentObject.line);
+      if (openIndex != -1) {
+        let textUntilComment = aLineText.slice(0, openIndex);
+        if (!textUntilComment || /^\s+$/.test(textUntilComment)) {
+          return true;
+        }
+      }
+      return false;
+    });
+    if (firstLastCommented) {
+      return this._doUncomment();
+    }
+
+    // If we reach here, then we have to comment the selection/line.
+    return this._doComment();
+  },
+
+  /**
    * Wrap the selected text in comments. If nothing is selected the current
    * caret line is commented out. Single line and block comments depend on the
    * current editor mode.
    *
    * @private
    */
   _doComment: function SE__doComment()
   {
@@ -1118,17 +1168,19 @@ SourceEditor.prototype = {
     let firstLine = this._model.getLineAtOffset(selection.start);
     let lastLine = this._model.getLineAtOffset(selection.end);
 
     // Uncomment a block of text.
     let firstLineText = this._model.getLine(firstLine);
     let lastLineText = this._model.getLine(lastLine);
     let openIndex = firstLineText.indexOf(commentObject.blockStart);
     let closeIndex = lastLineText.lastIndexOf(commentObject.blockEnd);
-    if (openIndex != -1 && closeIndex != -1) {
+    if (openIndex != -1 && closeIndex != -1 &&
+        (firstLine != lastLine ||
+        (closeIndex - openIndex) >= commentObject.blockStart.length)) {
       let firstLineStartOffset = this.getLineStart(firstLine);
       let lastLineStartOffset = this.getLineStart(lastLine);
       let openOffset = firstLineStartOffset + openIndex;
       let closeOffset = lastLineStartOffset + closeIndex;
 
       this.startCompoundChange();
       this.setText("", closeOffset, closeOffset + commentObject.blockEnd.length);
       this.setText("", openOffset, openOffset + commentObject.blockStart.length);
--- a/browser/devtools/sourceeditor/test/browser_bug725430_comment_uncomment.js
+++ b/browser/devtools/sourceeditor/test/browser_bug725430_comment_uncomment.js
@@ -43,108 +43,108 @@ function test() {
     editor.setCaretPosition(0);
     EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
     is(editor.getText(), "//" + text, "JS Single line Commenting Works");
     editor.undo();
     is(editor.getText(), text, "Undo Single Line Commenting action works");
     editor.redo();
     is(editor.getText(), "//" + text, "Redo works");
     editor.setCaretPosition(0);
-    EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin);
+    EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
     is(editor.getText(), text, "JS Single Line Uncommenting works");
 
     editor.setText(text);
 
     EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
     EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
     is(editor.getText(), "/*" + text + "*/", "JS Block Commenting works");
     editor.undo();
     is(editor.getText(), text, "Undo Block Commenting action works");
     editor.redo();
     is(editor.getText(), "/*" + text + "*/", "Redo works");
     EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
-    EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin);
+    EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
     is(editor.getText(), text, "JS Block Uncommenting works");
     editor.undo();
     is(editor.getText(), "/*" + text + "*/", "Undo Block Uncommenting works");
     editor.redo();
     is(editor.getText(), text, "Redo works");
 
-    let regText = "//firstline\n    //    secondline\nthird//line\nfourthline//";
-    let expText = "firstline\n        secondline\nthird//line\nfourthline//";
+    let regText = "//firstline\n    //    secondline\nthird//line\n//fourthline";
+    let expText = "firstline\n        secondline\nthird//line\nfourthline";
     editor.setText(regText);
     EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
-    EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin);
+    EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
     is(editor.getText(), expText, "JS Multiple Line Uncommenting works");
     editor.undo();
     is(editor.getText(), regText, "Undo Multiple Line Uncommenting works");
     editor.redo();
     is(editor.getText(), expText, "Redo works");
 
     editor.setMode(SourceEditor.MODES.CSS);
     editor.setText(text);
 
     expText = "/*firstline*/\nsecondline\nthirdline\nfourthline";
     editor.setCaretPosition(0);
     EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
     is(editor.getText(), expText, "CSS Commenting without selection works");
     editor.setCaretPosition(0);
-    EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin);
+    EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
     is(editor.getText(), text, "CSS Uncommenting without selection works");
 
     editor.setText(text);
 
     EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
     EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
     is(editor.getText(), "/*" + text + "*/", "CSS Multiple Line Commenting works");
     EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
-    EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin);
+    EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
     is(editor.getText(), text, "CSS Multiple Line Uncommenting works");
 
     editor.setMode(SourceEditor.MODES.HTML);
     editor.setText(text);
 
     expText = "<!--firstline-->\nsecondline\nthirdline\nfourthline";
     editor.setCaretPosition(0);
     EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
     is(editor.getText(), expText, "HTML Commenting without selection works");
     editor.setCaretPosition(0);
-    EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin);
+    EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
     is(editor.getText(), text, "HTML Uncommenting without selection works");
 
     editor.setText(text);
 
     EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
     EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
     is(editor.getText(), "<!--" + text + "-->", "HTML Multiple Line Commenting works");
     EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
-    EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin);
+    EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
     is(editor.getText(), text, "HTML Multiple Line Uncommenting works");
 
     editor.setMode(SourceEditor.MODES.TEXT);
     editor.setText(text);
 
     editor.setCaretPosition(0);
     EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
     is(editor.getText(), text, "Commenting disabled in Text mode");
-    editor.setText("//" + text);
+    editor.setText(regText);
     EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
-    EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin);
-    is(editor.getText(), "//" + text, "Uncommenting disabled in Text mode");
+    EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
+    is(editor.getText(), regText, "Uncommenting disabled in Text mode");
 
     editor.setText(text);
     editor.readOnly = true;
 
     editor.setCaretPosition(0);
     EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
     is(editor.getText(), text, "Commenting disabled in ReadOnly mode");
-    editor.setText("//" + text);
+    editor.setText(regText);
     EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin);
-    EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin);
-    is(editor.getText(), "//" + text, "Uncommenting disabled in ReadOnly mode");
+    EventUtils.synthesizeKey("/", {accelKey: true}, testWin);
+    is(editor.getText(), regText, "Uncommenting disabled in ReadOnly mode");
 
     editor.destroy();
 
     testWin.close();
     testWin = editor = null;
 
     waitForFocus(finish, window);
   }