-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Sequential navigation in edit history #13418
Changes from 1 commit
43df078
21702ea
91690e7
88cac89
fbae4db
e60dc6a
05e8b99
97ee969
07541cf
3b3b544
b94e6bf
ca88b42
9ed941e
167e4dd
6616279
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
/* | ||
* Copyright (c) 2016 - present Adobe Systems Incorporated. All rights reserved. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a | ||
* copy of this software and associated documentation files (the "Software"), | ||
* to deal in the Software without restriction, including without limitation | ||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
* and/or sell copies of the Software, and to permit persons to whom the | ||
* Software is furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
* DEALINGS IN THE SOFTWARE. | ||
* | ||
*/ | ||
|
||
define(function (require, exports, module) { | ||
"use strict"; | ||
|
||
var Strings = brackets.getModule("strings"), | ||
MainViewManager = brackets.getModule("view/MainViewManager"), | ||
DocumentManager = brackets.getModule("document/DocumentManager"), | ||
DocumentCommandHandlers = brackets.getModule("document/DocumentCommandHandlers"), | ||
EditorManager = brackets.getModule("editor/EditorManager"), | ||
Editor = brackets.getModule("editor/Editor"), | ||
ProjectManager = brackets.getModule("project/ProjectManager"), | ||
CommandManager = brackets.getModule("command/CommandManager"), | ||
Commands = brackets.getModule("command/Commands"), | ||
Menus = brackets.getModule("command/Menus"), | ||
KeyBindingManager = brackets.getModule("command/KeyBindingManager"); | ||
|
||
var KeyboardPrefs = JSON.parse(require("text!keyboard.json")); | ||
|
||
// Command constants for navigation history | ||
var NAVIGATION_JUMP_BACK = "navigation.jump.back", | ||
NAVIGATION_JUMP_FWD = "navigation.jump.fwd"; | ||
|
||
var NAV_FRAME_CAPTURE_LATENCY = 3000; | ||
|
||
|
||
/* | ||
* Contains list of most recently opened files and their last known cursor position | ||
* @private | ||
* @type {Array.<Object>} | ||
*/ | ||
var jumpToPosStack = [], | ||
jumpedPosStack = [], | ||
captureTimer, | ||
activePosNotSynched = false, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: values with defaults first There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
jumpInProgress, | ||
command_JumpBack, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
command_JumpFwd, | ||
cmMarkers = {}; | ||
|
||
|
||
function NavigationFrame(editor, selectionObj) { | ||
this.cm = editor._codeMirror; | ||
this.file = editor.document.file._path; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might call this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. "filePath" would be more logical. |
||
this.paneId = editor._paneId; | ||
this.uId = (new Date()).getTime() + ""; | ||
this.selections = []; | ||
this.bookMarkIds = []; | ||
this._createMarkers(selectionObj.ranges); | ||
this._bindEditor(editor); | ||
} | ||
|
||
NavigationFrame.prototype._bindEditor = function (editor) { | ||
var self = this; | ||
editor.on("beforeDestroy", function () { | ||
self._backupSelectionRanges(); | ||
self.cm = null; | ||
self.bookMarkIds = null; | ||
}); | ||
} | ||
|
||
NavigationFrame.prototype._createMarkers = function (ranges) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. createMarkers and reinstateMarkers share a lot of code. Maybe we can consolidate them a little more? As I see it, this should work: NavigationFrame.prototype._reinstateMarkers = function (editor) {
this.cm = editor._codeMirror;
this.paneId = editor._paneId;
this._createMarkers(this.selections)
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem was with range start and end placeholder names but handled it now to make use of same function. |
||
var range, index, bookMark; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: new variables on new lines There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
this.bookMarkIds = []; | ||
for (index in ranges) { | ||
range = ranges[index]; | ||
if (range.anchor.line === range.head.line && range.anchor.ch === range.head.ch) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Q: can There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there is a cursor or selection, it can never be null and if there are no selections/cursor then the ranges would be an empty array. |
||
bookMark = this.cm.setBookmark(range.anchor, range.head); | ||
this.bookMarkIds.push(bookMark.id); | ||
} else { | ||
this.cm.markText(range.anchor, range.head, {className: (this.uId + "")}); | ||
} | ||
} | ||
}; | ||
|
||
NavigationFrame.prototype._backupSelectionRanges = function () { | ||
if (!this.cm) { | ||
return; | ||
} | ||
|
||
this.selections = []; | ||
var marker, selection, index; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: new variables on new lines There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
var self = this; | ||
var markers = this.cm.getAllMarks().filter(function (entry) { | ||
if (entry.className === self.uId || self.bookMarkIds.indexOf(entry.id) !== -1) { | ||
return entry; | ||
} | ||
}) | ||
for (index in markers) { | ||
marker = markers[index]; | ||
selection = marker.find(); | ||
if (marker.type === "bookmark") { | ||
this.selections.push({start: selection, end: selection}); | ||
} else { | ||
this.selections.push({start: selection.from, end: selection.to}); | ||
} | ||
} | ||
}; | ||
|
||
NavigationFrame.prototype.goTo = function () { | ||
var self = this; | ||
this._backupSelectionRanges(); | ||
jumpInProgress = true; | ||
CommandManager.execute(Commands.FILE_OPEN, {fullPath: this.file, paneId: this.paneId}).done(function () { | ||
EditorManager.getCurrentFullEditor().setSelections(self.selections, true); | ||
command_JumpFwd.setEnabled(true); | ||
}).always(function () { | ||
jumpInProgress = false; | ||
}); | ||
}; | ||
|
||
function _recordJumpDef(event, selectionObj) { | ||
if (jumpInProgress) { | ||
return; | ||
} | ||
jumpedPosStack = []; | ||
if (selectionObj.origin !== "+move" && (window.event && window.event.type !== "input")) { | ||
if (captureTimer) { | ||
window.clearTimeout(captureTimer); | ||
captureTimer = null; | ||
} | ||
captureTimer = window.setTimeout(function () { | ||
jumpToPosStack.push(new NavigationFrame(event.target, selectionObj)); | ||
command_JumpFwd.setEnabled(false); | ||
if (jumpToPosStack.length > 1) { | ||
command_JumpBack.setEnabled(true); | ||
} | ||
activePosNotSynched = false; | ||
}, NAV_FRAME_CAPTURE_LATENCY); | ||
} else { | ||
activePosNotSynched = true; | ||
} | ||
} | ||
|
||
function _jumpToPosBack() { | ||
if (!jumpedPosStack.length) { | ||
if (activePosNotSynched) { | ||
jumpToPosStack.push(new NavigationFrame(EditorManager.getCurrentFullEditor(), {ranges: EditorManager.getCurrentFullEditor()._codeMirror.listSelections()})); | ||
} else { | ||
jumpedPosStack.push(jumpToPosStack.pop()); | ||
} | ||
} | ||
var navFrame = jumpToPosStack.pop(); | ||
if (navFrame) { | ||
jumpedPosStack.push(navFrame); | ||
navFrame.goTo(); | ||
} | ||
} | ||
|
||
function _jumpToPosFwd() { | ||
var navFrame = jumpedPosStack.pop(); | ||
if (navFrame) { | ||
jumpToPosStack.push(navFrame); | ||
navFrame.goTo(); | ||
} | ||
} | ||
|
||
/** | ||
* Handle Active Editor change to update navigation information | ||
* @private | ||
*/ | ||
function _handleActiveEditorChange(event, current, previous) { | ||
if (current && current._paneId) { // Handle only full editors | ||
current.on("beforeSelectionChange", _recordJumpDef); | ||
} | ||
|
||
if (previous && previous._paneId) { | ||
previous.off("beforeSelectionChange", _recordJumpDef); | ||
} | ||
} | ||
|
||
function _handleProjectOpen() { | ||
jumpToPosStack = []; | ||
jumpedPosStack = []; | ||
} | ||
|
||
function init() { | ||
CommandManager.register(Strings.CMD_NAVIGATE_BACKWARD, NAVIGATION_JUMP_BACK, _jumpToPosBack); | ||
CommandManager.register(Strings.CMD_NAVIGATE_FORWARD, NAVIGATION_JUMP_FWD, _jumpToPosFwd); | ||
command_JumpBack = CommandManager.get(NAVIGATION_JUMP_BACK); | ||
command_JumpFwd = CommandManager.get(NAVIGATION_JUMP_FWD); | ||
command_JumpBack.setEnabled(false); | ||
command_JumpFwd.setEnabled(false); | ||
KeyBindingManager.addBinding(NAVIGATION_JUMP_BACK, KeyboardPrefs[NAVIGATION_JUMP_BACK]); | ||
KeyBindingManager.addBinding(NAVIGATION_JUMP_FWD, KeyboardPrefs[NAVIGATION_JUMP_FWD]); | ||
var menu = Menus.getMenu(Menus.AppMenuBar.NAVIGATE_MENU); | ||
menu.addMenuItem(NAVIGATION_JUMP_BACK, "", Menus.AFTER, Commands.NAVIGATE_PREV_DOC); | ||
menu.addMenuItem(NAVIGATION_JUMP_FWD, "", Menus.AFTER, NAVIGATION_JUMP_BACK); | ||
EditorManager.on("activeEditorChange", _handleActiveEditorChange); | ||
ProjectManager.on("projectOpen", _handleProjectOpen); | ||
} | ||
|
||
exports.init = init; | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: missing * (/**)