Bug 687160 - Source Editor should provide a line-based API; r=rcampbell
authorMihai Sucan <mihai.sucan@gmail.com>
Fri, 09 Dec 2011 19:10:15 +0200
changeset 82437 7cf966da2e9321b45a70a2da094cc2fa47b9a461
parent 82436 ff164dca09bcaf62edaf5c1b7eee290485a032f8
child 82438 5bd81a413dea2cf855d41a946bf5c7ca70f85f20
push id385
push usermihai.sucan@gmail.com
push dateTue, 13 Dec 2011 16:52:27 +0000
treeherderfx-team@5bd81a413dea [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrcampbell
bugs687160
milestone11.0a1
Bug 687160 - Source Editor should provide a line-based API; r=rcampbell
browser/devtools/sourceeditor/source-editor-orion.jsm
browser/devtools/sourceeditor/source-editor-textarea.jsm
browser/devtools/sourceeditor/test/Makefile.in
browser/devtools/sourceeditor/test/browser_bug687160_line_api.js
--- a/browser/devtools/sourceeditor/source-editor-orion.jsm
+++ b/browser/devtools/sourceeditor/source-editor-orion.jsm
@@ -670,16 +670,57 @@ SourceEditor.prototype = {
    *        The new caret offset you want to set.
    */
   setCaretOffset: function SE_setCaretOffset(aOffset)
   {
     this._view.setCaretOffset(aOffset, true);
   },
 
   /**
+   * Get the caret position.
+   *
+   * @return object
+   *         An object that holds two properties:
+   *         - line: the line number, counting from 0.
+   *         - col: the column number, counting from 0.
+   */
+  getCaretPosition: function SE_getCaretPosition()
+  {
+    let offset = this.getCaretOffset();
+    let line = this._model.getLineAtOffset(offset);
+    let lineStart = this._model.getLineStart(line);
+    let column = offset - lineStart;
+    return {line: line, col: column};
+  },
+
+  /**
+   * Set the caret position: line and column.
+   *
+   * @param number aLine
+   *        The new caret line location. Line numbers start from 0.
+   * @param number [aColumn=0]
+   *        Optional. The new caret column location. Columns start from 0.
+   */
+  setCaretPosition: function SE_setCaretPosition(aLine, aColumn)
+  {
+    this.setCaretOffset(this._model.getLineStart(aLine) + (aColumn || 0));
+  },
+
+  /**
+   * Get the line count.
+   *
+   * @return number
+   *         The number of lines in the document being edited.
+   */
+  getLineCount: function SE_getLineCount()
+  {
+    return this._model.getLineCount();
+  },
+
+  /**
    * Get the line delimiter used in the document being edited.
    *
    * @return string
    *         The line delimiter.
    */
   getLineDelimiter: function SE_getLineDelimiter()
   {
     return this._model.getLineDelimiter();
--- a/browser/devtools/sourceeditor/source-editor-textarea.jsm
+++ b/browser/devtools/sourceeditor/source-editor-textarea.jsm
@@ -590,16 +590,51 @@ SourceEditor.prototype = {
    *        The new caret offset you want to set.
    */
   setCaretOffset: function SE_setCaretOffset(aOffset)
   {
     this.setSelection(aOffset, aOffset);
   },
 
   /**
+   * Set the caret position: line and column.
+   *
+   * @param number aLine
+   *        The new caret line location. Line numbers start from 0.
+   * @param number [aColumn=0]
+   *        Optional. The new caret column location. Columns start from 0.
+   */
+  setCaretPosition: function SE_setCaretPosition(aLine, aColumn)
+  {
+    aColumn = aColumn || 0;
+
+    let text = this._textbox.value;
+    let i = 0, n = text.length, c0, c1;
+    let line = 0, col = 0;
+    while (i < n) {
+      c1 = text.charAt(i++);
+      if (line < aLine && (c1 == "\r" || (c0 != "\r" && c1 == "\n"))) {
+        // Count lines and reset the column only until we reach the desired line
+        // such that if the desired column is out of boundaries we will stop
+        // after the given number of characters from the line start.
+        line++;
+        col = 0;
+      } else {
+        col++;
+      }
+
+      if (line == aLine && col == aColumn) {
+        this.setCaretOffset(i);
+        return;
+      }
+      c0 = c1;
+    }
+  },
+
+  /**
    * Get the line delimiter used in the document being edited.
    *
    * @return string
    *         The line delimiter.
    */
   getLineDelimiter: function SE_getLineDelimiter()
   {
     return this._lineDelimiter;
--- a/browser/devtools/sourceeditor/test/Makefile.in
+++ b/browser/devtools/sourceeditor/test/Makefile.in
@@ -46,12 +46,13 @@ include $(topsrcdir)/config/rules.mk
 
 _BROWSER_TEST_FILES = \
 		browser_sourceeditor_initialization.js \
 		browser_bug684862_paste_html.js \
 		browser_bug687573_vscroll.js \
 		browser_bug687568_pagescroll.js \
 		browser_bug687580_drag_and_drop.js \
 		browser_bug695035_middle_click_paste.js \
+		browser_bug687160_line_api.js \
 		head.js \
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/sourceeditor/test/browser_bug687160_line_api.js
@@ -0,0 +1,79 @@
+/* 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";
+
+Cu.import("resource:///modules/source-editor.jsm");
+
+let testWin;
+let editor;
+
+function test()
+{
+  waitForExplicitFinish();
+
+  const windowUrl = "data:text/xml,<?xml version='1.0'?>" +
+    "<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'" +
+    " title='test for bug 660784' width='600' height='500'><hbox flex='1'/></window>";
+  const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
+
+  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, {}, editorLoaded);
+}
+
+function editorLoaded()
+{
+  let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT);
+
+  editor.focus();
+
+  editor.setText("line1\nline2\nline3");
+
+  if (component != "textarea") {
+    is(editor.getLineCount(), 3, "getLineCount() works");
+  }
+
+  editor.setCaretPosition(1);
+  is(editor.getCaretOffset(), 6, "setCaretPosition(line) works");
+
+  let pos;
+  if (component != "textarea") {
+    pos = editor.getCaretPosition();
+    ok(pos.line == 1 && pos.col == 0, "getCaretPosition() works");
+  }
+
+  editor.setCaretPosition(1, 3);
+  is(editor.getCaretOffset(), 9, "setCaretPosition(line, column) works");
+
+  if (component != "textarea") {
+    pos = editor.getCaretPosition();
+    ok(pos.line == 1 && pos.col == 3, "getCaretPosition() works");
+  }
+
+  editor.setCaretPosition(2);
+  is(editor.getCaretOffset(), 12, "setCaretLine() works, confirmed");
+
+  if (component != "textarea") {
+    pos = editor.getCaretPosition();
+    ok(pos.line == 2 && pos.col == 0, "setCaretPosition(line) works, again");
+  }
+
+  editor.destroy();
+
+  testWin.close();
+  testWin = editor = null;
+
+  waitForFocus(finish, window);
+}
+