├── .common.py.swp
├── __init.py__
├── messages.json
├── Context.sublime-menu
├── messages
├── 1.0.2.md
├── 2.0.0.md
├── 1.0.3.md
└── install.txt
├── Default (Windows).sublime-keymap
├── .gitignore
├── Default (Linux).sublime-keymap
├── Default (OSX).sublime-keymap
├── bookmarkWatcher.py
├── Main.sublime-menu
├── Commands.sublime-commands
├── common.py
├── ui.py
├── visibilityHandler.py
├── bookmark.py
├── README.md
├── LICENSE
└── sublimebookmark.py
/.common.py.swp:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/__init.py__:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "install": "messages/install.txt"
3 | }
--------------------------------------------------------------------------------
/Context.sublime-menu:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "caption": "-"
4 | },
5 |
6 | {
7 | "caption": "Add bookmark",
8 | "command": "sublime_bookmark",
9 | "args" : { "type" : "add" }
10 | }
11 | ]
12 |
--------------------------------------------------------------------------------
/messages/1.0.2.md:
--------------------------------------------------------------------------------
1 | Sorry, this update breaks the save files, so all your bookmarks will be deleted :(
2 |
3 | I'll ensure that this *will not happen* in the future. Thanks for your cooperation,
4 | and sorry again!
5 |
6 | Yours humbly,
7 | ~bollu [https://github.com/bollu/]
--------------------------------------------------------------------------------
/Default (Windows).sublime-keymap:
--------------------------------------------------------------------------------
1 | [
2 | { "keys": ["ctrl+shift+f2"], "command": "sublime_bookmark", "args" : { "type" : "add" } },
3 | { "keys": ["f2"], "command": "sublime_bookmark", "args" : { "type" : "goto_next" } },
4 | { "keys": ["shift+f2"], "command": "sublime_bookmark", "args" : { "type" : "goto_prev" } },
5 | { "keys": ["ctrl+f2"], "command": "sublime_bookmark", "args" : { "type" : "toggle_line" } }
6 | ]
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.py[cod]
2 |
3 | # C extensions
4 | *.so
5 |
6 | # Packages
7 | *.egg
8 | *.egg-info
9 | dist
10 | build
11 | eggs
12 | parts
13 | bin
14 | var
15 | sdist
16 | develop-eggs
17 | .installed.cfg
18 | lib
19 | lib64
20 | __pycache__
21 |
22 | # Installer logs
23 | pip-log.txt
24 |
25 | # Unit test / coverage reports
26 | .coverage
27 | .tox
28 | nosetests.xml
29 |
30 | # Translations
31 | *.mo
32 |
33 | # Mr Developer
34 | .mr.developer.cfg
35 | .project
36 | .pydevproject
37 |
--------------------------------------------------------------------------------
/Default (Linux).sublime-keymap:
--------------------------------------------------------------------------------
1 | [
2 | { "keys": ["ctrl+shift+f2"], "command": "sublime_bookmark", "args" : { "type" : "add" } },
3 | { "keys": ["f2"], "command": "sublime_bookmark", "args" : { "type" : "goto_next" } },
4 | { "keys": ["shift+f2"], "command": "sublime_bookmark", "args" : { "type" : "goto_previous" } },
5 | { "keys": ["alt+f2"], "command": "sublime_bookmark", "args" : { "type" : "goto" } },
6 | { "keys": ["ctrl+f2"], "command": "sublime_bookmark", "args" : { "type" : "toggle_line" } }
7 | ]
--------------------------------------------------------------------------------
/Default (OSX).sublime-keymap:
--------------------------------------------------------------------------------
1 | [
2 | { "keys": ["super+shift+f2"], "command": "sublime_bookmark", "args" : { "type" : "add" } },
3 | { "keys": ["f2"], "command": "sublime_bookmark", "args" : { "type" : "goto_next" } },
4 | { "keys": ["shift+f2"], "command": "sublime_bookmark", "args" : { "type" : "goto_previous" } },
5 | { "keys": ["ctrl+f2"], "command": "sublime_bookmark", "args" : { "type" : "goto" } },
6 | { "keys": ["super+f2"], "command": "sublime_bookmark", "args" : { "type" : "toggle_line" } }
7 | ]
8 |
--------------------------------------------------------------------------------
/messages/2.0.0.md:
--------------------------------------------------------------------------------
1 | Sorry everybody, I've had a lot of college stuff to do and I haven't been able to work on
2 | this for a long time.
3 |
4 | However, I've taken the time to learn python and rewrite this from scratch. the codebase is sane, the features work, and most importantly - no more weirdness :)
5 |
6 | I'm sorry for all the trouble I've caused with the first two iterations - But I'm a newbie at python. This is not an excuse, I know, but is hopefully some form of apology.
7 |
8 | Please enjoy using the plugin, and as usual - contact me if you have any bugs / enhancements
9 | to [github](https://github.com/bollu/sublimeBookmark/issues?state=open).
10 |
11 | Thanks again!
12 | ~[bollu](https://github.com/bollu/)
--------------------------------------------------------------------------------
/messages/1.0.3.md:
--------------------------------------------------------------------------------
1 | Projects can now coexist peacefully with bookmarks :)
2 |
3 | Next time you use sublimeBookmarks, it will only show bookmarks belonging to the project
4 | you were working on.
5 |
6 | Also, the bookmarks will be *wiped one _last_ time*. I'm really sorry about this.
7 |
8 | If you had created bookmarks without a project, then these bookmarks will always be shown.
9 | These settings can be changed by going to _Preferences->Package Settings->sublimeBookmarks->Settings (Default)_,copy-pasting the settings to _Preferences->Package Settings->sublimeBookmarks->Settings (User)_ and then changing them.
10 |
11 | Have fun, and as usual, report all bugs to [github](https://github.com/bollu/sublimeBookmark/issues?state=open).
12 |
13 | Thanks for using the plugin :)
14 | ~bollu [https://github.com/bollu/]
--------------------------------------------------------------------------------
/messages/install.txt:
--------------------------------------------------------------------------------
1 | Thanks for installing *SublimeBookmarks*. I hope this plugin helps you :)
2 |
3 | To Use
4 | ======
5 |
6 | Visit the github page for a quick intro. [https://github.com/bollu/sublimeBookmark]
7 |
8 | Press Ctrl+Shift+P (or Cmd+Shift+P depending on your OS).
9 | Use _Add Bookmark_ to add a bookmark.
10 | Use _Goto Bookmark_ to goto a bookmark
11 | Use _Remove Bookmark_ to remove a bookmark
12 | Use _Remove All Bookmarks_ to clear the list of bookmarks
13 |
14 | There are three modes to the plugin:
15 | 1) All bookmarks will be shown all the time - bookmarks will not be filtered based on the current project. For this mode, use the __Show All Bookmarks__ option.
16 |
17 | 2) Only bookmarks of the current project will be shown - bookmarks belonging to other projects are *NOT* shown in this mode. Use the Use the __Show Bookmarks In Current Project__ for this mode.
18 |
19 | 3) Only bookmarks in the current file is shown - irrespective of project. Use the __Show Bookmarks In Current File__ option for this mode.
20 |
21 | I hope you enjoy using the plugin as much as I enjoyed writing it :)
22 |
23 | Thanks again!
24 | ~(bollu)[https://github.com/bollu/]
25 |
--------------------------------------------------------------------------------
/bookmarkWatcher.py:
--------------------------------------------------------------------------------
1 | import sublime
2 | import sublime_plugin
3 |
4 |
5 | class bookmarkWatcher(sublime_plugin.EventListener):
6 |
7 | def on_activated_async(self, view):
8 | sublime.active_window().run_command("sublime_bookmark",
9 | {"type": "mark_buffer"})
10 | sublime.active_window().run_command("sublime_bookmark",
11 | {"type": "move_bookmarks"})
12 |
13 | def on_modified_async(self, view):
14 | sublime.active_window().run_command("sublime_bookmark",
15 | {"type": "move_bookmarks"})
16 |
17 | def on_deactivated_async(self, view):
18 | sublime.active_window().run_command("sublime_bookmark",
19 | {"type": "mark_buffer"})
20 | sublime.active_window().run_command("sublime_bookmark",
21 | {"type": "move_bookmarks"})
22 |
23 | # Must be on close
24 | # not on pre close. on pre-close,the view still exists
25 | def on_close(self, view):
26 | sublime.active_window().run_command("sublime_bookmark",
27 | {"type": "update_temporary"})
28 |
29 | def on_pre_save_async(self, view):
30 | pass
31 | # sublime.run_command("sublime_bookmark", {"type": "save_data" } )
32 |
--------------------------------------------------------------------------------
/Main.sublime-menu:
--------------------------------------------------------------------------------
1 |
2 | [
3 | {
4 | "caption": "Edit",
5 | "mnemonic": "E",
6 | "id": "edit",
7 | "children":
8 | [
9 | {
10 | "caption": "Code Folding",
11 | "id": "fold",
12 | "children":
13 | [
14 | {"caption":"-"},
15 | { "caption": "Refold", "command": "buffer_scroll_re_fold" }
16 | ]
17 | }
18 | ]
19 | },
20 | {
21 | "caption": "Preferences",
22 | "mnemonic": "n",
23 | "id": "preferences",
24 | "children":
25 | [
26 | {"caption":"-"},
27 | {
28 | "caption": "Package Settings",
29 | "mnemonic": "P",
30 | "id": "package-settings",
31 | "children":
32 | [
33 | {
34 | "caption": "Sublime Bookmarks",
35 | "children":
36 | [
37 | { "caption": "-" },
38 | {
39 | "command": "open_file",
40 | "args": {
41 | "file": "${packages}/Sublime Bookmarks/Default (OSX).sublime-keymap",
42 | "platform": "OSX"
43 | },
44 | "caption": "Key Bindings – Default"
45 | },
46 | {
47 | "command": "open_file",
48 | "args": {
49 | "file": "${packages}/Sublime Bookmarks/Default (Linux).sublime-keymap",
50 | "platform": "Linux"
51 | },
52 | "caption": "Key Bindings – Default"
53 | },
54 | {
55 | "command": "open_file",
56 | "args": {
57 | "file": "${packages}/Sublime Bookmarks/Default (Windows).sublime-keymap",
58 | "platform": "Windows"
59 | },
60 | "caption": "Key Bindings – Default"
61 | },
62 | {
63 | "command": "open_file",
64 | "args": {
65 | "file": "${packages}/User/Default.sublime-keymap"
66 | },
67 | "caption": "Key Bindings – User"
68 | },
69 | { "caption": "-" }
70 | ]
71 | }
72 | ]
73 | }
74 | ]
75 | }
76 | ]
77 |
--------------------------------------------------------------------------------
/Commands.sublime-commands:
--------------------------------------------------------------------------------
1 | [
2 | //Add a bookmark
3 | {
4 | "caption": "SublimeBookmarks: Add Bookmark",
5 | "command": "sublime_bookmark",
6 | "args" : { "type" : "add" }
7 |
8 | },
9 |
10 | //Goto a specific bookmark
11 | {
12 | "caption": "SublimeBookmarks: Goto Bookmark",
13 | "command": "sublime_bookmark",
14 | "args" : { "type" : "goto" }
15 | },
16 | //Remove a single bookmark
17 | {
18 | "caption": "SublimeBookmarks: Remove A Bookmark",
19 | "command": "sublime_bookmark",
20 | "args" : { "type" : "remove" }
21 | },
22 |
23 | {
24 | "caption": "SublimeBookmarks: Goto Next Bookmark",
25 | "command": "sublime_bookmark",
26 | "args" : { "type" : "goto_next" }
27 | },
28 |
29 | {
30 | "caption": "SublimeBookmarks: Goto Previous Bookmark",
31 | "command": "sublime_bookmark",
32 | "args" : { "type" : "goto_previous" }
33 | },
34 |
35 | {
36 | "caption": "SublimeBookmarks: Remove All Bookmarks (Clear Bookmarks)",
37 | "command": "sublime_bookmark",
38 | "args" : { "type" : "remove_all" }
39 | },
40 | {
41 | "caption": "SublimeBookmarks: Toggle Current Line",
42 | "command": "sublime_bookmark",
43 | "args" : { "type" : "toggle_line" }
44 | },
45 | {
46 | "caption": "SublimeBookmarks: Show All Bookmarks ",
47 | "command": "sublime_bookmark",
48 | "args" : { "type" : "show_all_bookmarks" }
49 | },
50 |
51 | {
52 | "caption": "SublimeBookmarks: Show Bookmarks Only In Current Project ",
53 | "command": "sublime_bookmark",
54 | "args" : { "type" : "show_project_bookmarks" }
55 | },
56 |
57 | {
58 | "caption": "SublimeBookmarks: Show Bookmarks Only In Current File ",
59 | "command": "sublime_bookmark",
60 | "args" : { "type" : "show_file_bookmarks" }
61 | },
62 |
63 |
64 | ]
65 |
--------------------------------------------------------------------------------
/common.py:
--------------------------------------------------------------------------------
1 | import sublime
2 | import os.path
3 |
4 | # if someone names their project this, we're boned
5 | NO_PROJECT = "___NO_PROJECT_PRESENT____"
6 |
7 |
8 | REGION_BASE_TAG = int(11001001000011111101)
9 | SETTINGS_NAME = "SublimeBookmarks.sublime-settings"
10 |
11 | VERSION = "2.0.0"
12 |
13 |
14 | def Log(string):
15 | if True:
16 | print(string)
17 |
18 |
19 | def getViewByBufferID(window, bufferID):
20 | for view in window.views():
21 | if view.buffer_id() == int(bufferID):
22 | return view
23 | else:
24 | continue
25 |
26 | Log("NO VIEW. BUFFER ID: " + str(bufferID))
27 | return None
28 |
29 |
30 | def getCurrentLineRegion(view):
31 | assert (len(view.sel()) > 0)
32 | selectedRegion = view.sel()[0]
33 | region = view.line(selectedRegion)
34 |
35 | return region
36 |
37 |
38 | def getCurrentProjectPath(window):
39 | projectPath = window.project_file_name()
40 | if projectPath is None or projectPath is "":
41 | projectPath = NO_PROJECT
42 |
43 | return projectPath
44 |
45 |
46 | # [HACK] I think?
47 | def getCurrentActiveGroup(window):
48 | # (viewGroup, viewIndex) = window.get_view_index(view)
49 |
50 | return 0 # viewGroup
51 |
52 |
53 | def isLineEmpty(line):
54 | return len(line.strip()) == 0
55 |
56 |
57 | def isViewTemporary(view):
58 | return (view is None) or (view.file_name() is None)
59 |
60 |
61 | def getSavePath():
62 | # bookmark that represents the
63 | # file from which the panel was activated
64 | currentDir = os.path.dirname(sublime.packages_path())
65 | return currentDir + '/sublimeBookmarks.pickle'
66 |
67 |
68 | # MESSAGES
69 | def MESSAGE_NoBookmarkToGoto():
70 | sublime.status_message("SublimeBookmarks: NO ACCEPTABLE BOOKMARKS TO GOTO."
71 | "CHECK CURRENT MODE")
72 |
73 |
74 | def MESSAGE_BookmarkEmpty():
75 | sublime.status_message("SublimeBookmarks: BOOKMARK EMPTY."
76 | "NOT CREATING BOOKMARK")
77 |
--------------------------------------------------------------------------------
/ui.py:
--------------------------------------------------------------------------------
1 | from copy import deepcopy
2 |
3 |
4 | class OptionsSelector:
5 |
6 | def __init__(self, window, panelItems, onDone, onHighlight):
7 | self.window = window
8 | self.panelItems = deepcopy(panelItems)
9 | self.onDone = onDone
10 | self.onHighlight = onHighlight
11 |
12 | def start(self):
13 | startIndex = 0
14 | self.window.show_quick_panel(self.panelItems,
15 | self.onDone,
16 | 0,
17 | startIndex,
18 | self.onHighlight)
19 |
20 |
21 | class OptionsInput:
22 |
23 | def __init__(self, window, caption, initalText, onDone, onCancel):
24 | self.window = window
25 | self.caption = caption
26 | self.initalText = initalText
27 | self.onDone = onDone
28 | self.onCancel = onCancel
29 |
30 | def start(self):
31 | inputPanelView = self.window.show_input_panel(self.caption,
32 | self.initalText,
33 | self.onDone,
34 | None,
35 | self.onCancel)
36 |
37 | # select the text in the view so that
38 | # when the user types a new name, the old name is overwritten
39 | assert (len(inputPanelView.sel()) > 0)
40 | selectionRegion = inputPanelView.full_line(inputPanelView.sel()[0])
41 | inputPanelView.sel().add(selectionRegion)
42 |
43 |
44 | def createBookmarkPanelItems(window, visibleBookmarks):
45 | def ellipsisStringEnd(string, length):
46 | # I have NO idea why the hell this would happen. But it's happening.
47 | if string is None:
48 | return ""
49 | else:
50 | if len(string) <= length:
51 | return string
52 | else:
53 | return string[0:length - 3] + '.' * 3
54 |
55 | def ellipsisStringBegin(string, length):
56 | if string is None:
57 | return ""
58 | else:
59 | if len(string) <= length:
60 | return string
61 | else:
62 | return '.' * 3 + string[len(string)+3-(length):len(string)]
63 |
64 | bookmarkItems = []
65 |
66 | for bookmark in visibleBookmarks:
67 | bookmarkName = bookmark.getName()
68 |
69 | bookmarkLine = bookmark.getLineStr().lstrip()
70 | filepath = bookmark.getFilePath().lstrip()
71 | bookmarkFile = ellipsisStringBegin(filepath, 55)
72 |
73 | bookmarkItems.append([bookmarkName, bookmarkLine, bookmarkFile])
74 |
75 | return bookmarkItems
76 |
--------------------------------------------------------------------------------
/visibilityHandler.py:
--------------------------------------------------------------------------------
1 | from .common import *
2 |
3 |
4 | # whether all bookmarks (even unrelated) should be shown
5 | def SHOW_ALL_BOOKMARKS():
6 | return "Show All Bookmarks"
7 |
8 |
9 | def SHOW_ONLY_PROJECT_BOOKMARKS():
10 | return "Show Only Project Bookmarks"
11 |
12 |
13 | def SHOW_ONLY_FILE_BOOKMARKS():
14 | return "Show Only File Bookmarks"
15 |
16 |
17 | def shouldShowBookmark(window, activeView, bookmark, bookmarkMode):
18 | # 1. there is no current project now. Show all bookmarks
19 | # 2. current project matches bookmark path
20 | def isValidProject(currentProjectPath, bookmarkProjectPath):
21 | return currentProjectPath == NO_PROJECT or \
22 | currentProjectPath == bookmarkProjectPath
23 |
24 | currentFilePath = activeView.file_name()
25 | currentProjectPath = window.project_file_name()
26 |
27 | # free bookmarks can be shown. We don't need a criteria
28 | if bookmarkMode == SHOW_ALL_BOOKMARKS():
29 | return True
30 |
31 | elif bookmarkMode == SHOW_ONLY_PROJECT_BOOKMARKS() and \
32 | isValidProject(currentProjectPath, bookmark.getProjectPath()):
33 | return True
34 |
35 | elif bookmarkMode == SHOW_ONLY_FILE_BOOKMARKS() and \
36 | bookmark.getFilePath() == currentFilePath:
37 | return True
38 | else:
39 | # There are no bookmarks in the current file
40 | return False
41 |
42 | return False
43 |
44 |
45 | def ___sortBookmarks(visibleBookmarks, currentFile):
46 | from collections import defaultdict
47 |
48 | def lineSortFn(bookmark):
49 | return bookmark.getLineNumber()
50 |
51 | def sortByLineNumber(bookmarks):
52 | return sorted(bookmarks, key=lineSortFn)
53 |
54 | fileBookmarks = defaultdict(list)
55 | sortedBookmarks = []
56 |
57 | for bookmark in visibleBookmarks:
58 | filePath = bookmark.getFilePath()
59 | fileBookmarks[filePath].append(bookmark)
60 |
61 | # take all bookmarks in current file,
62 | # sort them,
63 | # remove the current file from the list of files
64 | currentFileBookmarks = fileBookmarks[currentFile]
65 | sortedBookmarks = sortedBookmarks + sortByLineNumber(currentFileBookmarks)
66 |
67 | del fileBookmarks[currentFile]
68 |
69 | # Iterate over all list of bookmarks in each file
70 | # sort them according to line number
71 | for bookmarkList in fileBookmarks.values():
72 | sortedBookmarkList = sortByLineNumber(bookmarkList)
73 | sortedBookmarks = sortedBookmarks + sortedBookmarkList
74 |
75 | return sortedBookmarks
76 |
77 |
78 | def getVisibleBookmarks(bookmarks, window, activeView, bookmarkMode):
79 | visibleBookmarks = []
80 | for bookmark in bookmarks:
81 | if shouldShowBookmark(window, activeView, bookmark, bookmarkMode):
82 | visibleBookmarks.append(bookmark)
83 |
84 | sortedBookmarks = ___sortBookmarks(visibleBookmarks,
85 | activeView.file_name())
86 | return sortedBookmarks
87 |
--------------------------------------------------------------------------------
/bookmark.py:
--------------------------------------------------------------------------------
1 | from .common import *
2 |
3 |
4 | class Bookmark:
5 |
6 | def __init__(self, uid, name, window, activeView):
7 | self.uid = int(uid)
8 | self.name = str(name)
9 |
10 | self.filePath = str(activeView.file_name())
11 | self.projectPath = getCurrentProjectPath(window)
12 |
13 | region = getCurrentLineRegion(activeView)
14 | self.regionA = int(region.a)
15 | self.regionB = int(region.b)
16 |
17 | self.group = getCurrentActiveGroup(window)
18 |
19 | self.lineStr = activeView.substr(region)
20 |
21 | cursorPos = activeView.sel()[0].begin()
22 | self.lineNumber = activeView.rowcol(cursorPos)[0]
23 |
24 | self.bufferID = activeView.buffer_id()
25 |
26 | def getBufferID(self):
27 | return self.bufferID
28 |
29 | def getName(self):
30 | return self.name
31 |
32 | def getUid(self):
33 | return self.uid
34 |
35 | def getRegion(self):
36 | return sublime.Region(self.regionA, self.regionB)
37 |
38 | def getFilePath(self):
39 | return self.filePath
40 |
41 | def getProjectPath(self):
42 | return self.projectPath
43 |
44 | def getLineNumber(self):
45 | return self.lineNumber
46 |
47 | def getLineStr(self):
48 | return self.lineStr
49 |
50 | def getGroup(self):
51 | return self.group
52 |
53 | def setLineStr(self, newLineStr):
54 | self.lineStr = str(newLineStr)
55 |
56 | def setRegion(self, region):
57 | self.regionA = region.a
58 | self.regionB = region.b
59 |
60 | def setGroup(self, group):
61 | self.group = int(group)
62 |
63 | def isTemporary(self):
64 | return self.filePath == "None"
65 |
66 | def goto(self, window):
67 | view = getBookmarkView(window, self)
68 | assert (view is not None)
69 |
70 | region = self.getRegion()
71 | view.show_at_center(region)
72 |
73 | # move cursor to the middle of the bookmark's region
74 | bookmarkRegionMid = 0.5 * (region.begin() + region.end())
75 | moveRegion = sublime.Region(bookmarkRegionMid, bookmarkRegionMid)
76 | view.sel().clear()
77 | view.sel().add(moveRegion)
78 |
79 | region = self.getRegion()
80 |
81 | line = view.substr(region)
82 | return isLineEmpty(line)
83 |
84 | # the bookmark is associated with the current view
85 | def isMyView(self, window, view):
86 | # I bloody hate python for this madness
87 | if view is None:
88 | return False
89 |
90 | bufferID = view.buffer_id()
91 | filePath = view.file_name()
92 |
93 | if self.isTemporary():
94 | return self.getBufferID() == bufferID
95 | else:
96 | return self.getFilePath() == filePath
97 |
98 | # updates the bookmark's data
99 | # 1) moved region to cover whole line
100 | # 2) updates the group info
101 | # 3) updates the current line string
102 | def updateData(self, window, myView):
103 | regions = myView.get_regions(str(self.uid))
104 |
105 | # the region is not loaded yet
106 | if len(regions) == 0:
107 | return
108 |
109 | lines = myView.split_by_newlines(regions[0])
110 |
111 | region = lines[0]
112 | self.regionA = region.a
113 | self.regionB = region.b
114 |
115 | self.lineStr = myView.substr(region)
116 | self.group = window.get_view_index(myView)[0]
117 |
118 |
119 | def getBookmarkView(window, bookmark):
120 | view = None
121 | if bookmark.isTemporary():
122 | # mimic behavior of open_file. so w have to focus the view too...
123 | view = getViewByBufferID(window, bookmark.getBufferID())
124 | window.focus_view(view)
125 | else:
126 | view = window.open_file(bookmark.getFilePath())
127 |
128 | assert view is not None
129 | return view
130 |
131 |
132 | def shouldRemoveTempBookmark(window, bookmark):
133 | assert(bookmark.isTemporary())
134 | return getViewByBufferID(window, bookmark.getBufferID()) is None
135 |
136 |
137 | # I hate this function.
138 | def moveBookmarkToGroup(window, bookmark, group):
139 | def moveViewToGroup(window, view, group):
140 | (viewGroup, viewIndex) = window.get_view_index(view)
141 |
142 | # the view is not in the required group so move it
143 | # we have to move the view to the other group and give it a new index
144 | if group != viewGroup or viewGroup == -1 or viewIndex == -1:
145 |
146 | # SUBLIME_BUG
147 | # if the group the view is currently in has only one element
148 | # - i.e this view, sublime text goes crazy.
149 | # It closes our options selector. So, we have to create
150 | # a new file in the old group and *only then* move the view.
151 | if len(window.views_in_group(viewGroup)) == 1:
152 | window.focus_group(viewGroup)
153 | window.new_file()
154 |
155 | # if there are 0 views, then the moved view will have index 0
156 | # if there are n views, the last view will have index (n-1), and
157 | # so the new view will have index n
158 | newIndex = len(window.views_in_group(group))
159 | # move the view to the highlighted group and assign a
160 | # correct index
161 | window.set_view_index(view, group, newIndex)
162 |
163 | # the view is in the right group, so chill
164 | else:
165 | pass
166 |
167 | view = None
168 |
169 | if bookmark.isTemporary():
170 | view = getViewByBufferID(window, bookmark.getBufferID())
171 | assert (view is not None)
172 | else:
173 | view = window.open_file(bookmark.getFilePath())
174 |
175 | assert (view is not None)
176 |
177 | # move the bookmark's view to the correct group
178 | moveViewToGroup(window, view, group)
179 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SublimeBookmark [UNMAINTAINED]
2 | ===============
3 |
4 | a better bookmark system for SublimeText
5 |
6 | 
7 |
8 |
9 | Unmaintained
10 | ============
11 | Unfortunately, being a college student doesn't leave me with time to fix the bugs that people seem to be having with
12 | this codebase. I do wish to rewrite this, since this was written when I was new to python. If **anyone** wishes to either
13 | 1. Take ownership of the package
14 | 2. Help me rewrite this
15 |
16 | Please do ping me, help is very appreciated `:)`
17 |
18 |
19 |
20 | Motivation
21 | ==========
22 |
23 | Let's face it: sublime text's bookmark system __sucks__.
24 | __\__
25 | It doesn't support named bookmarks. It doesn't save bookmark statuses, and it is just terrible to use overall.
26 | __\__.
27 |
28 | This is a replacement for sublime text's bookmark functionality. It's slicker and easier to use, and has way more features.
29 |
30 | **Note**: As of now, This is only for **Sublime Text 3**. False! It's been ported. Unfortunately, I havent' implement live preview and project based bookmark sorting. I'm not sure how to port these features over to Sublime Text 2. If someone knows, please do contact me!d
31 |
32 |
33 | Features
34 | ========
35 |
36 | * Named bookmarks
37 | * Bookmarks are saved across sessions
38 | * Goto any bookmark in the project
39 | * Add any number of bookmarks (not just 12).
40 | * Project based bookmarks (bookmarks are stored per-project and bookmarks can be navigated on a per-project basis) (Only for ST3)
41 |
42 | To Install
43 | ==========
44 |
45 |
46 |
47 | **With the Package Control plugin:** The easiest way to install SublimeBookmarks is through Package Control, which can be found at this site: http://wbond.net/sublime_packages/package_control
48 |
49 | Once you install Package Control, restart Sublime Text and bring up the Command Palette (``Command+Shift+P`` on OS X, ``Control+Shift+P`` on Linux/Windows). Select "Package Control: Install Package", wait while Package Control fetches the latest package list, then select SublimeBookmarks when the list appears. The advantage of using this method is that Package Control will automatically keep SublimeBookmarks up to date with the latest version.
50 |
51 |
52 |
53 | **Without Git:** Download the latest source from `GitHub ` and copy the whole directory into the Packages directory.
54 |
55 | **With Git:** Clone the repository in your Sublime Text Packages directory, located somewhere in user's "Home" directory::
56 |
57 | git clone -b st3 https://github.com/bollu/sublimeBookmark.git
58 |
59 | To Use
60 | ======
61 |
62 | ###Adding Bookmarks###
63 |
64 | Go to a line you wish to bookmark. Press ```ctrl + shift + P``` on Windows / Linux or ```cmd + shift + P``` on Mac and type ```SublimeBookmarks:Add Bookmark```. This opens up a panel where you can type the name of your bookmark. Once you're done naming your shiny new bookmark, hit ```Enter```. You should see a tiny yellow triangle next to your line. you're done!
65 |
66 |
67 | ###Accessing Bookmarks###
68 | Press ```ctrl + shift + P``` on Windows / Linux or ```cmd + shift + P``` on Mac and select ```SublimeBookmarks:Goto Bookmark```. This will bring up a list of all bookmarks. Type in the bookmark name you want to go to and press ```Enter``` to go to the bookmark
69 |
70 |
71 | ###Removing Bookmarks###
72 | Press ```ctrl + shift + P``` on Windows / Linux or ```cmd + shift + P``` on Mac and select ```Remove Bookmark```. Type the name of the bookmark you want to remove and press ```Enter```. This will remove the bookmark
73 |
74 | To remove _all_ bookmarks, select the option ```SublimeBookmarks:Remove All Bookmarks (Clear Bookmarks)```. This will clear _all bookmarks_. This _can not be undone_.
75 |
76 |
77 |
78 | ##Visibility Modes:##
79 |
80 | SublimeBookmarks has 3 visibility modes associated with it.
81 | The default mode is option 2.
82 |
83 | ###1) View all Bookmarks###
84 |
85 | This mode shows *all* bookmarks that have been created - irrespective of project or file information.
86 |
87 |
88 | To use this mode, Press ```ctrl + shift + P``` on Windows / Linux or ```cmd + shift + P``` on Mac and select ```SublimeBookmarks:Show All Bookmarks```
89 |
90 | This will show __all__ bookmarks created
91 |
92 |
93 | ###2) View only Project Bookmarks###
94 |
95 | This mode only shows bookmarksthat belong to the *current project* - it will not show other bookmarks *at all*
96 |
97 | Press ```ctrl + shift + P``` on Windows / Linux or ```cmd + shift + P``` on Mac and select ```SublimeBookmarks:Show Only Bookmarks In Current Project```
98 |
99 | This will only show bookmarks that belong to the current project.
100 |
101 | ###2) View only current file Bookmarks###
102 |
103 | This mode only shows bookmark that are present in the *current file*.
104 |
105 | Press ```ctrl + shift + P``` on Windows / Linux or ```cmd + shift + P``` on Mac and select ```SublimeBookmarks:Show Only Bookmarks In Current File```
106 |
107 | This will only show bookmarks that belong to the current file.
108 |
109 | Notes / Addendum
110 | ================
111 |
112 | ###Some TODO Stuff:###
113 |
114 | * Port to Sublime Text 2 (This is partially done. Unfortunately, I'm not able to implement live previews and project support. I don't really know how to port these to ST2. If someone does, please do contact me! )
115 | * Add an option to only show bookmarks belonging to current project (Nope, it now fully supports project-based bookmark management!)
116 |
117 |
118 | ###To Help###
119 |
120 | Just fork my repo and send a pull request. I'll gladly accept :)
121 |
122 | I've spent quite some time writing this and making it bug-free. It would really help me if you'd chip in a little something :) I'm a student, so a little goes a long way.
123 |
124 | [](https://www.gittip.com/bollu/)
125 |
126 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/sublimebookmark.py:
--------------------------------------------------------------------------------
1 | import sublime
2 | import sublime_plugin
3 | import os.path
4 | from pickle import dump, load, UnpicklingError, PicklingError
5 | from copy import deepcopy
6 |
7 |
8 | from .common import *
9 | from .bookmark import *
10 | from .visibilityHandler import *
11 | from .ui import *
12 |
13 |
14 | BOOKMARKS = []
15 | UID = None
16 |
17 |
18 | # list of bookmarks that have been deleted.
19 | # This is used to remove bookmarks' buffer highlights.
20 | # Without this, if a bookmark is removed,
21 | # when a file is revisited,
22 | # the buffer will still be marked. This will keep track of bookmarks
23 | # that have been removed.
24 | ERASED_BOOKMARKS = []
25 |
26 | # whether all bookmarks (even unrelated) should be shown
27 | BOOKMARKS_MODE = SHOW_ALL_BOOKMARKS()
28 |
29 |
30 | def removeBookmark(bookmark):
31 | global BOOKMARKS
32 | global ERASED_BOOKMARKS
33 | ERASED_BOOKMARKS.append(deepcopy(bookmark))
34 | BOOKMARKS.remove(bookmark)
35 |
36 |
37 | def addBookmark(bookmark):
38 | global BOOKMARKS
39 | BOOKMARKS.append(deepcopy(bookmark))
40 |
41 |
42 | class SublimeBookmarkCommand(sublime_plugin.WindowCommand):
43 |
44 | def __init__(self, window):
45 | self.window = window
46 | self.activeGroup = self.window.active_group()
47 | self.activeView = self.window.active_view()
48 |
49 | global BOOKMARKS
50 | global UID
51 |
52 | BOOKMARKS = []
53 | # Initialize to 0
54 | UID = 0
55 |
56 | # The bookmark to go to if the user cancels
57 | self.revertBookmark = None
58 |
59 | # Bookmarks that are being shown in the panel
60 | self.displayedBookmarks = None
61 |
62 | # The index used for goto next / goto previous
63 | # not a believer of dynamic typing
64 | self.global_bookmark_index = int(-1)
65 |
66 | currentDir = os.path.dirname(sublime.packages_path())
67 | self.SAVE_PATH = currentDir + '/sublimeBookmarks.pickle'
68 | Log(currentDir)
69 |
70 | self._Load()
71 |
72 | def run(self, type, name=None):
73 | global BOOKMARKS_MODE
74 |
75 | self.activeGroup = self.window.active_group()
76 | self.activeView = self.window.active_view()
77 |
78 | # update bookmark positions. We need to do it anyway...
79 | self._UpdateBookmarkPosition()
80 |
81 | # delete any temp bookmarks that have been since destroyed
82 | self._UpdateTemporaryBookmarks()
83 |
84 | if type == "add":
85 | self._addBookmark(name)
86 |
87 | elif type == "goto":
88 | # on highlighting, goto the current bookmark
89 | # on option select, just center the selected item
90 | self._createBookmarkPanel(self._HilightDoneCallback,
91 | self._AutoMoveToBookmarkCallback)
92 |
93 | elif type == "remove":
94 | # on highlighting, goto the current bookmark
95 | # on option select, remove the selected item
96 | self._createBookmarkPanel(self._RemoveDoneCallback,
97 | self._AutoMoveToBookmarkCallback)
98 |
99 | elif type == "remove_all":
100 | self._removeAllBookmarks()
101 |
102 | elif type == "toggle_line":
103 | self._toggleCurrentLine()
104 |
105 | elif type == "goto_next":
106 | self._quickGoto(True)
107 |
108 | elif type == "goto_previous":
109 | self._quickGoto(False)
110 |
111 | # BOOKMARK MODES
112 | elif type == "show_all_bookmarks":
113 | BOOKMARKS_MODE = SHOW_ALL_BOOKMARKS()
114 | self._Save()
115 | # update buffer to show all bookmarks
116 | self._updateBufferStatus()
117 |
118 | elif type == "show_project_bookmarks":
119 | BOOKMARKS_MODE = SHOW_ONLY_PROJECT_BOOKMARKS()
120 | self._Save()
121 | # update buffer to show only project bookmarks
122 | self._updateBufferStatus()
123 |
124 | elif type == "show_file_bookmarks":
125 | BOOKMARKS_MODE = SHOW_ONLY_FILE_BOOKMARKS()
126 | self._Save()
127 | # update buffer to show only project bookmarks
128 | self._updateBufferStatus()
129 |
130 | # ASYNC OPERATIONS
131 | elif type == "mark_buffer":
132 | self._updateBufferStatus()
133 |
134 | elif type == "move_bookmarks":
135 | self._UpdateBookmarkPosition()
136 |
137 | elif type == "update_temporary":
138 | self._UpdateTemporaryBookmarks()
139 |
140 | def _createBookmarkPanel(self, onHighlight, onDone):
141 |
142 | def moveBookmarksToActiveGroup(activeGroup):
143 | # move all open bookmark tabs to one group
144 | # so that group switching does not occur.
145 | for bookmark in BOOKMARKS:
146 | moveBookmarkToGroup(self.window, bookmark, self.activeGroup)
147 |
148 | def createPanel():
149 | bookmarkPanelItems = \
150 | createBookmarkPanelItems(self.window, self.displayedBookmarks)
151 |
152 | # create a selection panel and launch it
153 | selector = OptionsSelector(self.window,
154 | bookmarkPanelItems,
155 | onHighlight,
156 | onDone)
157 | selector.start()
158 |
159 | return True
160 |
161 | # load all visible bookmarks
162 | self.displayedBookmarks = getVisibleBookmarks(BOOKMARKS,
163 | self.window,
164 | self.activeView,
165 | BOOKMARKS_MODE)
166 |
167 | # if no bookmarks are acceptable, don't show bookmarks
168 | if len(self.displayedBookmarks) == 0:
169 | return False
170 |
171 | # create a revert bookmark to go back if the user cancels
172 | self._createRevertBookmark(self.activeView)
173 |
174 | # move all active bookmarks to the currently active group
175 | moveBookmarksToActiveGroup(self.activeGroup)
176 |
177 | # create the selection panel
178 | panelCreated = createPanel()
179 | if not panelCreated:
180 | MESSAGE_NoBookmarkToGoto()
181 | return False
182 |
183 | # Event handlers
184 | def _addBookmark(self, name):
185 | if name is None:
186 | window = self.window
187 | view = window.active_view()
188 | region = getCurrentLineRegion(view)
189 |
190 | # copy whatever is on the line for the bookmark name
191 | initialText = view.substr(region).strip()
192 |
193 | input = OptionsInput(self.window,
194 | "Add Bookmark",
195 | initialText,
196 | self._AddBookmarkCallback,
197 | None)
198 | input.start()
199 | else:
200 | self._AddBookmarkCallback(name)
201 |
202 | def _removeAllBookmarks(self):
203 | global ERASED_BOOKMARKS
204 | global BOOKMARKS
205 |
206 | for bookmark in BOOKMARKS: # noqa
207 | # store erased bookmarks for delayed removal
208 | ERASED_BOOKMARKS.append(deepcopy(bookmark))
209 |
210 | # yep. nuke em
211 | del BOOKMARKS
212 | BOOKMARKS = [] # noqa
213 |
214 | # update the buffer since we deleted a bookmark
215 | self._updateBufferStatus()
216 | # save to eternal storage
217 | self._Save()
218 |
219 | def _quickGoto(self, forward):
220 | # Gather appropriate bookmarks
221 | self.displayedBookmarks = getVisibleBookmarks(BOOKMARKS,
222 | self.window,
223 | self.activeView,
224 | BOOKMARKS_MODE)
225 |
226 | if 0 == len(self.displayedBookmarks):
227 | MESSAGE_NoBookmarkToGoto()
228 | return
229 |
230 | # increment or decrement
231 | if forward:
232 | self.global_bookmark_index = self.global_bookmark_index + 1
233 | else:
234 | self.global_bookmark_index = self.global_bookmark_index - 1
235 |
236 | # If we're pointing off the end, go to the first one instead
237 | if self.global_bookmark_index >= len(self.displayedBookmarks):
238 | self.global_bookmark_index = 0
239 | # If we're pointing off the beginning, go to the last one instead
240 | if self.global_bookmark_index < 0:
241 | self.global_bookmark_index = len(self.displayedBookmarks) - 1
242 |
243 | # Go there!
244 | bookmark = BOOKMARKS[self.global_bookmark_index]
245 | moveBookmarkToGroup(self.window, bookmark, self.activeGroup)
246 | self._AutoMoveToBookmarkCallback(self.global_bookmark_index)
247 |
248 | def _toggleCurrentLine(self):
249 | def getLineBookmark(window):
250 | currentFilePath = window.active_view().file_name()
251 | cursorRegion = getCurrentLineRegion(window.active_view())
252 |
253 | for bookmark in BOOKMARKS:
254 | if bookmark.getFilePath() == currentFilePath and \
255 | bookmark.getRegion().contains(cursorRegion):
256 | return bookmark
257 |
258 | # no bookmark
259 | return None
260 |
261 | bookmark = getLineBookmark(self.window)
262 |
263 | if bookmark is not None:
264 | global ERASED_BOOKMARKS
265 | global BOOKMARKS
266 |
267 | # add to list of erased bookmarks
268 | ERASED_BOOKMARKS.append(deepcopy(bookmark))
269 | BOOKMARKS.remove(bookmark)
270 |
271 | self._updateBufferStatus()
272 | # [File IO]
273 | self._Save()
274 | else:
275 | region = getCurrentLineRegion(self.activeView)
276 | # copy whatever is on the line for the bookmark name
277 | name = self.activeView.substr(region).strip()
278 |
279 | self._AddBookmarkCallback(name)
280 |
281 | def _updateBufferStatus(self):
282 | # marks the given bookmark on the buffer
283 | def markBuffer(view, bookmark):
284 | uid = bookmark.getUid()
285 | region = bookmark.getRegion()
286 | view.add_regions(str(uid), [region],
287 | "text.plain", "bookmark",
288 | sublime.DRAW_NO_FILL |
289 | sublime.DRAW_EMPTY_AS_OVERWRITE)
290 |
291 | # unmarks the given bookmark on the buffer
292 | def unmarkBuffer(view, bookmark):
293 | uid = bookmark.getUid()
294 | view.erase_regions(str(uid))
295 |
296 | if self.activeView is None:
297 | return
298 |
299 | # mark all bookmarks that are visible, and unmark invisible bookmarks
300 | for bookmark in BOOKMARKS:
301 | # if the bookmark should be shown
302 | # according to the current bookmark mode
303 | shouldShow = shouldShowBookmark(self.window,
304 | self.activeView,
305 | bookmark, BOOKMARKS_MODE)
306 |
307 | # only mark if we are in the right view.
308 | validContext = bookmark.isMyView(self.window, self.activeView)
309 |
310 | if validContext and shouldShow:
311 | markBuffer(self.activeView, bookmark)
312 | else:
313 | unmarkBuffer(self.activeView, bookmark)
314 |
315 | # unmark all erased bookmarks
316 | for bookmark in ERASED_BOOKMARKS:
317 | validContext = bookmark.isMyView(self.window, self.activeView)
318 |
319 | if validContext:
320 | unmarkBuffer(self.activeView, bookmark)
321 |
322 | # 1. move bookmarks
323 | # 2. update their regions when text is entered into the buffer
324 | def _UpdateBookmarkPosition(self):
325 | # this bookmark (might) have been changed
326 | # since it's in the current file
327 | # We're on a thread anyway so update it.
328 | for bookmark in BOOKMARKS:
329 |
330 | # if the activeView is the bookmark's view, update r
331 | # if bookmark.isMyView(self.window, self.activeView):
332 | if bookmark.isMyView(self.window, self.activeView) and not self.activeView.is_loading():
333 |
334 | bookmark.updateData(self.window, self.activeView)
335 |
336 | # the bookmark is empty - it has no data in it.
337 | if bookmark.isEmpty(self.activeView):
338 | Log("EMPTY BOOKMARK. NAME: " + bookmark.getName())
339 | removeBookmark(bookmark)
340 |
341 | # we've moved regions around so update the buffer
342 | self._updateBufferStatus()
343 | # we've moved bookmarks around and may also have deleted them. So, save
344 | self._Save()
345 |
346 | # check if the buffers associated
347 | # with temporary bookmark are still active or not, and remove
348 | # unnecessary bookmarks
349 | def _UpdateTemporaryBookmarks(self):
350 | for bookmark in BOOKMARKS:
351 | # if the bookmark is a temporary bookmark
352 | # and the bookmark has been deleted, remove the bookmark
353 | if bookmark.isTemporary() and \
354 | shouldRemoveTempBookmark(self.window, bookmark):
355 |
356 | Log("BOOKMARK IS TEMP AND BUFFER HAS BEEN REMOVED."
357 | "REMOVING. "
358 | "BUFFER: %s | NAME: %s" %
359 | (bookmark.getBufferID(), bookmark.getName()))
360 |
361 | removeBookmark(bookmark)
362 |
363 | # helpers
364 | # creates a bookmark that keeps track of where we were before opening
365 | # n options menu.
366 | def _createRevertBookmark(self, activeView):
367 | # there's no file open.
368 | # return None 'cause there's no place to return TO
369 | if isViewTemporary(activeView):
370 | self.revertBookmark = None
371 | return
372 |
373 | uid = -1
374 | name = ""
375 |
376 | self.revertBookmark = Bookmark(uid, name, self.window, activeView)
377 |
378 | # goes to the revert bookmark
379 | def _gotoRevertBookmark(self):
380 | if self.revertBookmark is None:
381 | return
382 | self.revertBookmark.goto(self.window)
383 | self.revertBookmark = None
384 |
385 | def _restoreFiles(self):
386 | for bookmark in BOOKMARKS:
387 | moveBookmarkToGroup(self.window, bookmark, bookmark.getGroup())
388 |
389 | # callbacks---------------------------------------------------
390 | def _AddBookmarkCallback(self, name):
391 |
392 | global UID
393 | assert UID is not None
394 |
395 | myUID = UID
396 | myUID = REGION_BASE_TAG + myUID
397 | UID = UID + 1
398 |
399 | # get region and line data
400 | region = getCurrentLineRegion(self.activeView)
401 | lineStr = self.activeView.substr(region)
402 |
403 | # there's no content
404 | if isLineEmpty(lineStr):
405 | MESSAGE_BookmarkEmpty()
406 | return
407 |
408 | bookmark = Bookmark(UID, name, self.window, self.activeView)
409 | addBookmark(bookmark)
410 |
411 | self._updateBufferStatus()
412 | self._Save()
413 |
414 | # display highlighted bookmark
415 | def _AutoMoveToBookmarkCallback(self, index):
416 | assert index < len(self.displayedBookmarks)
417 | bookmark = self.displayedBookmarks[index]
418 | assert bookmark is not None
419 |
420 | # goto highlighted bookmark
421 | bookmark.goto(self.window)
422 | self._updateBufferStatus()
423 |
424 | # if the user canceled, go back to the original file
425 | def _HilightDoneCallback(self, index):
426 |
427 | # restore all files back to their original places
428 | self._restoreFiles()
429 |
430 | # if the user canceled, then goto the revert bookmark
431 | if index == -1:
432 | self._gotoRevertBookmark()
433 |
434 | # otherwise, goto the selected bookmark
435 | else:
436 | # now open the selected bookmark and scroll to bookmark
437 | self._AutoMoveToBookmarkCallback(index)
438 |
439 | # move the correct bookmark back to the active group -
440 | # since all fails including the bookmark have been restored,
441 | # we have to move the bookmark back
442 | # ARRGH! this is __so__ hacky :(
443 | bookmark = self.displayedBookmarks[index]
444 | moveBookmarkToGroup(self.window, bookmark, self.activeGroup)
445 |
446 | # IMPORTANT - not doing this will cause bookmark to think it is
447 | # still in it's previous group.
448 | bookmark.updateData(self.window, self.activeView)
449 |
450 | self._updateBufferStatus()
451 |
452 | # remove the selected bookmark or go back if user cancelled
453 | def _RemoveDoneCallback(self, index):
454 | # restore all files back to their original places
455 | self._restoreFiles()
456 |
457 | # if the user canceled, then goto the revert bookmark
458 | if index == -1:
459 | self._gotoRevertBookmark()
460 | # otherwise, goto the selected bookmark
461 | else:
462 | assert index < len(self.displayedBookmarks)
463 | bookmark = self.displayedBookmarks[index]
464 | assert bookmark is not None
465 |
466 | # goto the removed bookmark
467 | bookmark.goto(self.window)
468 | removeBookmark(bookmark)
469 |
470 | # decrement global_bookmark_index so
471 | # goto_next will not skip anything
472 | if index <= self.global_bookmark_index:
473 | self.global_bookmark_index = self.global_bookmark_index - 1
474 |
475 | self._updateBufferStatus()
476 | self._Save()
477 |
478 | # Save Load
479 | def _Load(self):
480 | global BOOKMARKS
481 | global BOOKMARKS_MODE
482 | global UID
483 |
484 | Log("LOADING BOOKMARKS")
485 | try:
486 | savefile = open(self.SAVE_PATH, "rb")
487 |
488 | saveVersion = load(savefile)
489 |
490 | if saveVersion != VERSION:
491 | raise UnpicklingError("version difference in files")
492 |
493 | BOOKMARKS_MODE = load(savefile)
494 | UID = load(savefile)
495 | BOOKMARKS = load(savefile)
496 |
497 | except (OSError, IOError, UnpicklingError,
498 | EOFError, BaseException) as e:
499 | print("\nEXCEPTION:------- ")
500 | print(e)
501 | print("\nUNABLE TO LOAD BOOKMARKS. NUKING LOAD FILE")
502 | # clear the load file :]
503 | open(self.SAVE_PATH, "wb").close()
504 | # if you can't load, try and save a "default" state
505 | self._Save()
506 |
507 | def _Save(self):
508 | global BOOKMARKS
509 | global BOOKMARKS_MODE
510 | global UID
511 |
512 | try:
513 | savefile = open(self.SAVE_PATH, "wb")
514 |
515 | dump(VERSION, savefile)
516 | dump(BOOKMARKS_MODE, savefile)
517 | dump(UID, savefile)
518 | dump(BOOKMARKS, savefile)
519 |
520 | savefile.close()
521 | except (OSError, IOError, PicklingError) as e:
522 | print(e)
523 | print("\nUNABLE TO SAVE BOOKMARKS. PLEASE CONTACT DEV")
524 |
--------------------------------------------------------------------------------