Modified: trunk/LayoutTests/inspector/editor/text-editor-undo-redo.html (143004 => 143005)
--- trunk/LayoutTests/inspector/editor/text-editor-undo-redo.html 2013-02-15 16:49:14 UTC (rev 143004)
+++ trunk/LayoutTests/inspector/editor/text-editor-undo-redo.html 2013-02-15 16:53:55 UTC (rev 143005)
@@ -19,6 +19,17 @@
return range;
}
+ function typeDelete(textModel, startRange, count)
+ {
+ var count = count || 1;
+ var range = startRange;
+ for (var i = 0; i < count; ++i) {
+ var deleteRange = range.isEmpty() ? textModel.growRangeRight(range) : range;
+ range = textModel.editRange(deleteRange, "", range).collapseToEnd();
+ }
+ return range;
+ }
+
function typeBackspace(textModel, startRange, count)
{
var count = count || 1;
@@ -88,6 +99,102 @@
next();
},
+ function testDelete(next)
+ {
+ var textModel = new WebInspector.TextEditorModel();
+ var functionText = " bar();\n baz();\n foo();";
+ textModel.setText("function foo()\n{\n\n}\n");
+ dumpTextModel("Text before edit:\n", textModel);
+ var range = typeText(textModel, new WebInspector.TextRange(2, 0, 2, 0), " bar();\n baz();\n foo();");
+ dumpTextModel("Text after edit:\n", textModel, range);
+ range = typeDelete(textModel, new WebInspector.TextRange(2, 0, 2, 0), functionText.length);
+ dumpTextModel("Text after deletes:\n", textModel, range);
+ range = textModel.undo();
+ dumpTextModel("Text after first undo:\n", textModel, range);
+ range = textModel.undo();
+ dumpTextModel("Text after second undo:\n", textModel, range);
+ range = textModel.redo();
+ dumpTextModel("Text after first redo:\n", textModel, range);
+ range = textModel.redo();
+ dumpTextModel("Text after second redo:\n", textModel, range);
+ next();
+ },
+
+ function testBackspace(next)
+ {
+ var textModel = new WebInspector.TextEditorModel();
+ var functionText = " bar();\n baz();\n foo();";
+ textModel.setText("function foo()\n{\n\n}\n");
+ dumpTextModel("Text before edit:\n", textModel);
+ var range = typeText(textModel, new WebInspector.TextRange(2, 0, 2, 0), " bar();\n baz();\n foo();");
+ dumpTextModel("Text after edit:\n", textModel, range);
+ range = typeBackspace(textModel, range, functionText.length);
+ dumpTextModel("Text after backspaces:\n", textModel, range);
+ range = textModel.undo();
+ dumpTextModel("Text after first undo:\n", textModel, range);
+ range = textModel.undo();
+ dumpTextModel("Text after second undo:\n", textModel, range);
+ range = textModel.redo();
+ dumpTextModel("Text after first redo:\n", textModel, range);
+ range = textModel.redo();
+ dumpTextModel("Text after second redo:\n", textModel, range);
+ next();
+ },
+
+ function testBackspaceAndDeleteInDifferentLines(next)
+ {
+ var textModel = new WebInspector.TextEditorModel();
+ var functionText = " bar();\n baz();\n foo();";
+ textModel.setText("function foo()\n{\n\n}\n");
+ dumpTextModel("Text before edit:\n", textModel);
+ var range = typeText(textModel, new WebInspector.TextRange(2, 0, 2, 0), " bar();\n baz();\n foo();");
+ dumpTextModel("Text after edit:\n", textModel, range);
+ range = typeBackspace(textModel, range);
+ dumpTextModel("Text after backspace:\n", textModel, range);
+ range = typeDelete(textModel, new WebInspector.TextRange(2, 0, 2, 0));
+ dumpTextModel("Text after delete:\n", textModel, range);
+ range = textModel.undo();
+ dumpTextModel("Text after first undo:\n", textModel, range);
+ range = textModel.undo();
+ dumpTextModel("Text after second undo:\n", textModel, range);
+ range = textModel.undo();
+ dumpTextModel("Text after third undo:\n", textModel, range);
+ range = textModel.redo();
+ dumpTextModel("Text after first redo:\n", textModel, range);
+ range = textModel.redo();
+ dumpTextModel("Text after second redo:\n", textModel, range);
+ range = textModel.redo();
+ dumpTextModel("Text after third redo:\n", textModel, range);
+ next();
+ },
+
+ function testPasteSeveralTimes(next)
+ {
+ var textModel = new WebInspector.TextEditorModel();
+ var functionText = " bar();\n baz();\n foo();";
+ textModel.setText("function foo()\n{\n\n}\n");
+ dumpTextModel("Text before edit:\n", textModel);
+ var range = typeText(textModel, new WebInspector.TextRange(2, 0, 2, 0), " bar();\n baz();\n foo();");
+ dumpTextModel("Text after edit:\n", textModel, range);
+ range = textModel.editRange(range, "42").collapseToEnd();
+ dumpTextModel("Text after first paste:\n", textModel, range);
+ range = textModel.editRange(range, "42").collapseToEnd();
+ dumpTextModel("Text after second paste:\n", textModel, range);
+ range = textModel.undo();
+ dumpTextModel("Text after first undo:\n", textModel, range);
+ range = textModel.undo();
+ dumpTextModel("Text after second undo:\n", textModel, range);
+ range = textModel.undo();
+ dumpTextModel("Text after third undo:\n", textModel, range);
+ range = textModel.redo();
+ dumpTextModel("Text after first redo:\n", textModel, range);
+ range = textModel.redo();
+ dumpTextModel("Text after second redo:\n", textModel, range);
+ range = textModel.redo();
+ dumpTextModel("Text after third redo:\n", textModel, range);
+ next();
+ },
+
function testSelectionAfterUndoRedo(next)
{
var textModel = new WebInspector.TextEditorModel();
Modified: trunk/Source/WebCore/inspector/front-end/TextEditorModel.js (143004 => 143005)
--- trunk/Source/WebCore/inspector/front-end/TextEditorModel.js 2013-02-15 16:49:14 UTC (rev 143004)
+++ trunk/Source/WebCore/inspector/front-end/TextEditorModel.js 2013-02-15 16:53:55 UTC (rev 143005)
@@ -67,6 +67,28 @@
},
/**
+ * @param {WebInspector.TextRange} range
+ * @return {boolean}
+ */
+ immediatelyPrecedes: function(range)
+ {
+ if (!range)
+ return false;
+ return this.endLine === range.startLine && this.endColumn === range.startColumn;
+ },
+
+ /**
+ * @param {WebInspector.TextRange} range
+ * @return {boolean}
+ */
+ immediatelyFollows: function(range)
+ {
+ if (!range)
+ return false;
+ return range.immediatelyPrecedes(this);
+ },
+
+ /**
* @return {number}
*/
get linesCount()
@@ -251,15 +273,63 @@
/**
* @param {WebInspector.TextRange} range
+ * @return {boolean}
+ */
+ _rangeHasOneCharacter: function(range)
+ {
+ if (range.startLine === range.endLine && range.endColumn - range.startColumn === 1)
+ return true;
+ if (range.endLine - range.startLine === 1 && range.endColumn === 0 && range.startColumn === this.lineLength(range.startLine))
+ return true;
+ return false;
+ },
+
+ /**
+ * @param {WebInspector.TextRange} range
* @param {string} text
* @param {WebInspector.TextRange=} originalSelection
+ * @return {boolean}
+ */
+ _isEditRangeUndoBoundary: function(range, text, originalSelection)
+ {
+ if (originalSelection && !originalSelection.isEmpty())
+ return true;
+ if (text)
+ return text.length > 1 || !range.isEmpty();
+ return !this._rangeHasOneCharacter(range);
+ },
+
+ /**
+ * @param {WebInspector.TextRange} range
+ * @param {string} text
+ * @return {boolean}
+ */
+ _isEditRangeAdjacentToLastCommand: function(range, text)
+ {
+ if (!this._lastCommand)
+ return true;
+ if (!text) {
+ // FIXME: Distinguish backspace and delete in lastCommand.
+ return this._lastCommand.newRange.immediatelyPrecedes(range) || this._lastCommand.newRange.immediatelyFollows(range);
+ }
+ return text.indexOf("\n") === -1 && this._lastCommand.newRange.immediatelyPrecedes(range);
+ },
+
+ /**
+ * @param {WebInspector.TextRange} range
+ * @param {string} text
+ * @param {WebInspector.TextRange=} originalSelection
* @return {WebInspector.TextRange}
*/
editRange: function(range, text, originalSelection)
- {
- if (this._lastEditedRange && (!text || text.indexOf("\n") !== -1 || this._lastEditedRange.endLine !== range.startLine || this._lastEditedRange.endColumn !== range.startColumn))
+ {
+ var undoBoundary = this._isEditRangeUndoBoundary(range, text, originalSelection);
+ if (undoBoundary || !this._isEditRangeAdjacentToLastCommand(range, text))
this._markUndoableState();
- return this._innerEditRange(range, text, originalSelection);
+ var newRange = this._innerEditRange(range, text, originalSelection);
+ if (undoBoundary)
+ this._markUndoableState();
+ return newRange;
},
/**
@@ -272,8 +342,7 @@
{
var originalText = this.copyRange(range);
var newRange = this._innerSetText(range, text);
- this._lastEditedRange = newRange;
- this._pushUndoableCommand(newRange, originalText, originalSelection || range);
+ this._lastCommand = this._pushUndoableCommand(newRange, originalText, originalSelection || range);
this.dispatchEventToListeners(WebInspector.TextEditorModel.Events.TextChanged, { oldRange: range, newRange: newRange, editRange: true });
return newRange;
},