Changeset 83725 in webkit
- Timestamp:
- Apr 13, 2011 6:39:00 AM (13 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r83724 r83725 1 2011-04-13 Andrey Adaikin <aandrey@google.com> 2 3 Reviewed by Pavel Feldman. 4 5 Web Inspector: Implement undo/redo in text editor 6 https://bugs.webkit.org/show_bug.cgi?id=58426 7 8 Native undo/redo does not work because we modify DOM structure (highlights, chunks and etc.) 9 Implement it via keyboard shortcuts for now (Cmd/Ctrl+Z and Cmd/Ctrl+Shift+Z). 10 FIXME: Do something with the popup's Undo and Redo menu options - they invoke native's undo/redo. 11 12 * inspector/front-end/SourceFrame.js: 13 (WebInspector.SourceFrame.prototype.endEditing): 14 * inspector/front-end/TextEditorModel.js: 15 (WebInspector.TextEditorModel.prototype.setText): 16 (WebInspector.TextEditorModel.prototype._innerSetText): 17 (WebInspector.TextEditorModel.prototype._pushUndoableCommand): 18 (WebInspector.TextEditorModel.prototype.undo): 19 (WebInspector.TextEditorModel.prototype.redo): 20 (WebInspector.TextEditorModel.prototype._doUndo): 21 * inspector/front-end/TextViewer.js: 22 (WebInspector.TextViewer.prototype._textChanged): 23 (WebInspector.TextViewer.prototype._enterInternalTextChangeMode): 24 (WebInspector.TextViewer.prototype._exitInternalTextChangeMode): 25 (WebInspector.TextViewer.prototype._registerShortcuts): 26 (WebInspector.TextViewer.prototype._cancelEditing): 27 (WebInspector.TextViewer.prototype._handleUndoRedo): 28 (WebInspector.TextEditorChunkedPanel.prototype.makeLineAChunk): 29 (WebInspector.TextEditorChunkedPanel.prototype._repaintAll): 30 (WebInspector.TextEditorGutterPanel.prototype.textChanged): 31 (WebInspector.TextEditorMainPanel.prototype.handleUndoRedo.callback): 32 (WebInspector.TextEditorMainPanel.prototype.handleUndoRedo): 33 (WebInspector.TextEditorMainPanel.prototype._restoreSelection): 34 (WebInspector.TextEditorMainPanel.prototype._applyDomUpdates): 35 (WebInspector.TextEditorMainPanel.prototype.textChanged): 36 (WebInspector.TextEditorMainPanel.prototype._updateChunksForRanges): 37 1 38 2011-04-13 Pavel Feldman <pfeldman@chromium.org> 2 39 -
trunk/Source/WebCore/inspector/front-end/SourceFrame.js
r83713 r83725 251 251 endEditing: function(oldRange, newRange) 252 252 { 253 if (!oldRange || !newRange) 254 return; 255 253 256 // Adjust execution line number. 254 257 if (typeof this._executionLineNumber === "number") { -
trunk/Source/WebCore/inspector/front-end/TextEditorModel.js
r80728 r83725 94 94 if (!range) 95 95 range = new WebInspector.TextRange(0, 0, this._lines.length - 1, this._lines[this._lines.length - 1].length); 96 var command = this._pushUndoableCommand(range , text);96 var command = this._pushUndoableCommand(range); 97 97 var newRange = this._innerSetText(range, text); 98 98 command.range = newRange.clone(); … … 118 118 119 119 var prefix = this._lines[range.startLine].substring(0, range.startColumn); 120 var prefixArguments = this._arguments121 120 var suffix = this._lines[range.startLine].substring(range.startColumn); 122 121 … … 244 243 }, 245 244 246 _pushUndoableCommand: function(range , text)245 _pushUndoableCommand: function(range) 247 246 { 248 247 var command = { … … 263 262 }, 264 263 265 undo: function( )264 undo: function(callback) 266 265 { 267 266 this._markRedoableState(); 268 267 269 268 this._inUndo = true; 270 var range = this._doUndo(this._undoStack );269 var range = this._doUndo(this._undoStack, callback); 271 270 delete this._inUndo; 272 271 … … 274 273 }, 275 274 276 redo: function( )275 redo: function(callback) 277 276 { 278 277 this.markUndoableState(); 279 278 280 279 this._inRedo = true; 281 var range = this._doUndo(this._redoStack );280 var range = this._doUndo(this._redoStack, callback); 282 281 delete this._inRedo; 283 282 … … 285 284 }, 286 285 287 _doUndo: function(stack )286 _doUndo: function(stack, callback) 288 287 { 289 288 var range = null; … … 293 292 294 293 range = this.setText(command.range, command.text); 294 if (callback) 295 callback(command.range, range); 295 296 if (i > 0 && stack[i - 1].explicit) 296 297 return range; -
trunk/Source/WebCore/inspector/front-end/TextViewer.js
r83585 r83725 36 36 this._textModel = textModel; 37 37 this._textModel.changeListener = this._textChanged.bind(this); 38 this._textModel.resetUndoStack(); 38 39 this._delegate = delegate; 39 40 … … 152 153 _textChanged: function(oldRange, newRange, oldText, newText) 153 154 { 154 if (!this._internalTextChangeMode) { 155 this._mainPanel.textChanged(oldRange, newRange); 156 this._gutterPanel.textChanged(oldRange, newRange); 157 this._updatePanelOffsets(); 158 } 159 }, 160 161 _enterInternalTextChangeMode: function() 162 { 163 this._internalTextChangeMode = true; 164 165 this._delegate.startEditing(); 166 }, 167 168 _exitInternalTextChangeMode: function(oldRange, newRange) 169 { 170 this._internalTextChangeMode = false; 171 172 // Update the gutter panel. 155 if (!this._internalTextChangeMode) 156 this._textModel.resetUndoStack(); 157 this._mainPanel.textChanged(oldRange, newRange); 173 158 this._gutterPanel.textChanged(oldRange, newRange); 174 159 this._updatePanelOffsets(); 175 160 }, 161 162 _enterInternalTextChangeMode: function() 163 { 164 this._internalTextChangeMode = true; 165 this._delegate.startEditing(); 166 }, 167 168 _exitInternalTextChangeMode: function(oldRange, newRange) 169 { 170 this._internalTextChangeMode = false; 176 171 this._delegate.endEditing(oldRange, newRange); 177 172 }, … … 236 231 _registerShortcuts: function() 237 232 { 233 var keys = WebInspector.KeyboardShortcut.Keys; 234 var modifiers = WebInspector.KeyboardShortcut.Modifiers; 235 238 236 this._shortcuts = {}; 239 237 var commitEditing = this._commitEditing.bind(this); 240 238 var cancelEditing = this._cancelEditing.bind(this); 241 this._shortcuts[WebInspector.KeyboardShortcut.makeKey("s", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta)] = commitEditing; 242 this._shortcuts[WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.Keys.Enter.code, WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta)] = commitEditing; 243 this._shortcuts[WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.Keys.Esc.code)] = cancelEditing; 239 this._shortcuts[WebInspector.KeyboardShortcut.makeKey("s", modifiers.CtrlOrMeta)] = commitEditing; 240 this._shortcuts[WebInspector.KeyboardShortcut.makeKey(keys.Enter.code, modifiers.CtrlOrMeta)] = commitEditing; 241 this._shortcuts[WebInspector.KeyboardShortcut.makeKey(keys.Esc.code)] = cancelEditing; 242 243 var handleUndo = this._handleUndoRedo.bind(this, 0); 244 var handleRedo = this._handleUndoRedo.bind(this, 1); 245 this._shortcuts[WebInspector.KeyboardShortcut.makeKey("z", modifiers.CtrlOrMeta)] = handleUndo; 246 this._shortcuts[WebInspector.KeyboardShortcut.makeKey("z", modifiers.Shift | modifiers.CtrlOrMeta)] = handleRedo; 244 247 }, 245 248 … … 279 282 this._delegate.cancelEditing(); 280 283 return true; 284 }, 285 286 _handleUndoRedo: function(redo) 287 { 288 return this._mainPanel.handleUndoRedo(redo); 281 289 } 282 290 } … … 357 365 var chunk = this.chunkForLine(lineNumber); 358 366 chunk.removeDecoration(decoration); 359 },360 361 textChanged: function(oldRange, newRange)362 {363 this._buildChunks();364 367 }, 365 368 … … 384 387 makeLineAChunk: function(lineNumber) 385 388 { 386 if (!this._textChunks)387 this._buildChunks();388 389 389 var chunkNumber = this._chunkNumberForLine(lineNumber); 390 390 var oldChunk = this._textChunks[chunkNumber]; … … 542 542 if (this._paintCoalescingLevel || this._dirtyLines) 543 543 return; 544 545 if (!this._textChunks)546 this._buildChunks();547 544 548 545 var visibleFrom = this.element.scrollTop; … … 622 619 textChanged: function(oldRange, newRange) 623 620 { 624 if (!this._textChunks) { 625 this._buildChunks(); 626 return; 627 } 621 this.beginDomUpdates(); 628 622 629 623 var linesDiff = newRange.linesCount - oldRange.linesCount; … … 661 655 } 662 656 } 657 658 this.endDomUpdates(); 663 659 }, 664 660 … … 904 900 this._cachedTextNodes = []; 905 901 this._cachedRows = []; 902 }, 903 904 handleUndoRedo: function(redo) 905 { 906 if (this._readOnly || this._dirtyLines) 907 return false; 908 909 this.beginUpdates(); 910 this._enterTextChangeMode(); 911 912 var callback = function(oldRange, newRange) { 913 this._exitTextChangeMode(oldRange, newRange); 914 this._enterTextChangeMode(); 915 }.bind(this); 916 917 var range = redo ? this._textModel.redo(callback) : this._textModel.undo(callback); 918 if (range) 919 this._restoreSelection(new WebInspector.TextRange(range.endLine, range.endColumn, range.endLine, range.endColumn), true); 920 921 this._exitTextChangeMode(null, null); 922 this.endUpdates(); 923 924 return true; 906 925 }, 907 926 … … 1170 1189 }, 1171 1190 1172 _restoreSelection: function(range )1191 _restoreSelection: function(range, scrollIntoView) 1173 1192 { 1174 1193 if (!range) … … 1177 1196 var end = range.isEmpty() ? start : this._positionToSelection(range.endLine, range.endColumn); 1178 1197 window.getSelection().setBaseAndExtent(start.container, start.offset, end.container, end.offset); 1198 1199 if (scrollIntoView) { 1200 for (var node = end.container; node; node = node.parentElement) { 1201 if (node.scrollIntoViewIfNeeded) { 1202 node.scrollIntoViewIfNeeded(); 1203 break; 1204 } 1205 } 1206 } 1179 1207 }, 1180 1208 … … 1406 1434 } 1407 1435 1408 // Try to decrease the range being replaced if possible.1436 // Try to decrease the range being replaced, if possible. 1409 1437 var startOffset = 0; 1410 1438 while (startLine < dirtyLines.start && startOffset < lines.length) { … … 1425 1453 lines = lines.slice(startOffset, endOffset); 1426 1454 1455 // Try to decrease the range being replaced by column offsets, if possible. 1456 var startColumn = 0; 1457 var endColumn = this._textModel.lineLength(endLine - 1); 1458 if (lines.length > 0) { 1459 var line1 = this._textModel.line(startLine); 1460 var line2 = lines[0]; 1461 while (line1[startColumn] && line1[startColumn] === line2[startColumn]) 1462 ++startColumn; 1463 lines[0] = line2.substring(startColumn); 1464 1465 var line1 = this._textModel.line(endLine - 1); 1466 var line2 = lines[lines.length - 1]; 1467 for (var i = 0; i < endColumn && i < line2.length; ++i) { 1468 if (startLine === endLine - 1 && endColumn - i <= startColumn) 1469 break; 1470 if (line1[endColumn - i - 1] !== line2[line2.length - i - 1]) 1471 break; 1472 } 1473 if (i) { 1474 endColumn -= i; 1475 lines[lines.length - 1] = line2.substring(0, line2.length - i); 1476 } 1477 } 1478 1427 1479 var selection = this._getSelection(); 1428 1480 1429 if (lines.length === 0 && endLine < this._textModel.linesCount) {1481 if (lines.length === 0 && endLine < this._textModel.linesCount) 1430 1482 var oldRange = new WebInspector.TextRange(startLine, 0, endLine, 0); 1431 var newRange = this._textModel.setText(oldRange, ""); 1432 } else if (lines.length === 0 && startLine > 0) { 1483 else if (lines.length === 0 && startLine > 0) 1433 1484 var oldRange = new WebInspector.TextRange(startLine - 1, this._textModel.lineLength(startLine - 1), endLine - 1, this._textModel.lineLength(endLine - 1)); 1434 var newRange = this._textModel.setText(oldRange, ""); 1435 } else { 1436 var oldRange = new WebInspector.TextRange(startLine, 0, endLine - 1, this._textModel.lineLength(endLine - 1)); 1437 var newRange = this._textModel.setText(oldRange, lines.join("\n")); 1438 } 1439 1485 else 1486 var oldRange = new WebInspector.TextRange(startLine, startColumn, endLine - 1, endColumn); 1487 1488 if (this._lastEditedRange && (lines.length !== 1 || this._lastEditedRange.endLine !== oldRange.startLine || this._lastEditedRange.endColumn !== oldRange.startColumn)) 1489 this._textModel.markUndoableState(); 1490 1491 var newRange = this._textModel.setText(oldRange, lines.join("\n")); 1492 this._lastEditedRange = newRange; 1493 1494 this._paintScheduledLines(true); 1495 this._restoreSelection(selection); 1496 1497 this._exitTextChangeMode(oldRange, newRange); 1498 }, 1499 1500 textChanged: function(oldRange, newRange) 1501 { 1440 1502 this.beginDomUpdates(); 1441 1503 this._removeDecorationsInRange(oldRange); 1442 1504 this._updateChunksForRanges(oldRange, newRange); 1443 1505 this._updateHighlightsForRange(newRange); 1444 this._paintScheduledLines(true);1445 1506 this.endDomUpdates(); 1446 1447 this._restoreSelection(selection);1448 1449 this._exitTextChangeMode(oldRange, newRange);1450 1507 }, 1451 1508 … … 1493 1550 for (var chunkNumber = firstChunkNumber; chunkNumber <= lastChunkNumber; ++chunkNumber) { 1494 1551 var chunk = this._textChunks[chunkNumber]; 1552 if (chunk.startLine + chunk.linesCount > this._textModel.linesCount) 1553 break; 1495 1554 var lineNumber = chunk.startLine; 1496 1555 for (var lineRow = firstLineRow; lineRow && lineNumber < chunk.startLine + chunk.linesCount; lineRow = lineRow.nextSibling) {
Note: See TracChangeset
for help on using the changeset viewer.