179 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			179 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # Forked from: https://github.com/optilude/SublimeTextMisc/blob/master/navigationHistory.py
 | |
| import sublime, sublime_plugin
 | |
| from collections import deque
 | |
| 
 | |
| MAX_SIZE = 64
 | |
| LINE_THRESHOLD = 2
 | |
| 
 | |
| class Location(object):
 | |
|     """A location in the history
 | |
|     """
 | |
| 
 | |
|     def __init__(self, path, line, col):
 | |
|         self.path = path
 | |
|         self.line = line
 | |
|         self.col = col
 | |
|     
 | |
|     def __eq__(self, other):
 | |
|         return self.path == other.path and self.line == other.line
 | |
|     
 | |
|     def __ne__(self, other):
 | |
|         return not self.__eq__(other)
 | |
|     
 | |
|     def __nonzero__(self):
 | |
|         return (self.path is not None and self.line is not None)
 | |
| 
 | |
|     def near(self, other):
 | |
|         return self.path == other.path and abs(self.line - other.line) <= LINE_THRESHOLD
 | |
| 
 | |
|     def copy(self):
 | |
|         return Location(self.path, self.line, self.col)
 | |
| 
 | |
| class History(object):
 | |
|     """Keep track of the history for a single window
 | |
|     """
 | |
| 
 | |
|     def __init__(self, max_size=MAX_SIZE):
 | |
|         self._current = None                # current location as far as the
 | |
|                                             # history is concerned
 | |
|         self._back = deque([], max_size)    # items before self._current
 | |
|         self._forward = deque([], max_size) # items after self._current
 | |
|         
 | |
|         self._last_movement = None          # last recorded movement
 | |
|     
 | |
|     def record_movement(self, location):
 | |
|         """Record movement to the given location, pushing history if
 | |
|         applicable
 | |
|         """
 | |
| 
 | |
|         if location:
 | |
|             if self.has_changed(location):
 | |
|                 #Push the current location if it hasn't been added already, so when going back after using goto it goes to the correct location
 | |
|                 if self._back and self._last_movement is not self._back[-1]:
 | |
|                     self.push(self._last_movement)                
 | |
|                 self.push(location)
 | |
|             self.mark_location(location)
 | |
| 
 | |
|     def mark_location(self, location):
 | |
|         """Remember the current location, for the purposes of being able
 | |
|         to do a has_changed() check.
 | |
|         """
 | |
|         self._last_movement = location.copy()
 | |
|     
 | |
|     def has_changed(self, location):
 | |
|         """Determine if the given location combination represents a
 | |
|         significant enough change to warrant pushing history.
 | |
|         """
 | |
| 
 | |
|         return self._last_movement is None or not self._last_movement.near(location)
 | |
|     
 | |
|     def push(self, location):
 | |
|         """Push the given location to the back history. Clear the forward
 | |
|         history.
 | |
|         """
 | |
| 
 | |
|         if self._current is not None:
 | |
|             self._back.append(self._current.copy())
 | |
|         self._current = location.copy()
 | |
|         self._forward.clear()
 | |
| 
 | |
|     def back(self):
 | |
|         """Move backward in history, returning the location to jump to.
 | |
|         Returns None if no history.
 | |
|         """
 | |
| 
 | |
|         if not self._back:
 | |
|             return None
 | |
|         
 | |
|         self._forward.appendleft(self._current)
 | |
|         self._current = self._back.pop()
 | |
|         self._last_movement = self._current # preempt, so we don't re-push
 | |
|         return self._current
 | |
| 
 | |
|     def forward(self):
 | |
|         """Move forward in history, returning the location to jump to.
 | |
|         Returns None if no history.
 | |
|         """
 | |
| 
 | |
|         if not self._forward:
 | |
|             return None
 | |
|         
 | |
|         self._back.append(self._current)
 | |
|         self._current = self._forward.popleft()
 | |
|         self._last_movement = self._current # preempt, so we don't re-push
 | |
|         return self._current
 | |
| 
 | |
| _histories = {} # window id -> History
 | |
| 
 | |
| def get_history():
 | |
|     """Get a History object for the current window,
 | |
|     creating a new one if required
 | |
|     """
 | |
| 
 | |
|     window = sublime.active_window()
 | |
|     if window is None:
 | |
|         return None
 | |
| 
 | |
|     window_id = window.id()
 | |
|     history = _histories.get(window_id, None)
 | |
|     if history is None:
 | |
|         _histories[window_id] = history = History()
 | |
|     return history
 | |
| 
 | |
| class NavigationHistoryRecorder(sublime_plugin.EventListener):
 | |
|     """Keep track of history
 | |
|     """
 | |
| 
 | |
|     def on_selection_modified(self, view):
 | |
|         """When the selection is changed, possibly record movement in the
 | |
|         history
 | |
|         """
 | |
|         history = get_history()
 | |
|         if history is None:
 | |
|             return
 | |
| 
 | |
|         path = view.file_name()
 | |
|         row, col = view.rowcol(view.sel()[0].a)
 | |
|         history.record_movement(Location(path, row + 1, col + 1))
 | |
|     
 | |
|     # def on_close(self, view):
 | |
|     #     """When a view is closed, check to see if the window was closed too
 | |
|     #     and clean up orphan histories
 | |
|     #     """
 | |
|     #
 | |
|     #     # XXX: This doesn't work - event runs before window is removed
 | |
|     #     # from sublime.windows()
 | |
|     #
 | |
|     #     windows_with_history = set(_histories.keys())
 | |
|     #     window_ids = set([w.id() for w in sublime.windows()])
 | |
|     #     closed_windows = windows_with_history.difference(window_ids)
 | |
|     #     for window_id in closed_windows:
 | |
|     #         del _histories[window_id]
 | |
| 
 | |
| class NavigationHistoryBack(sublime_plugin.TextCommand):
 | |
|     """Go back in history
 | |
|     """
 | |
| 
 | |
|     def run(self, edit):
 | |
|         history = get_history()
 | |
|         if history is None:
 | |
|             return
 | |
| 
 | |
|         location = history.back()
 | |
|         if location:
 | |
|             window = sublime.active_window()
 | |
|             window.open_file("{0}:{1}:{2}".format(location.path, location.line, location.col), sublime.ENCODED_POSITION)
 | |
| 
 | |
| class NavigationHistoryForward(sublime_plugin.TextCommand):
 | |
|     """Go forward in history
 | |
|     """
 | |
| 
 | |
|     def run(self, edit):
 | |
|         history = get_history()
 | |
|         if history is None:
 | |
|             return
 | |
| 
 | |
|         location = history.forward()
 | |
|         if location:
 | |
|             window = sublime.active_window()
 | |
|             window.open_file("{0}:{1}:{2}".format(location.path, location.line, location.col), sublime.ENCODED_POSITION) |