diff --git a/README.md b/README.md
index 64b8e43..37dcbb0 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ Fill a void in your Sublime Text multiple selection capabilities! This plugin al
## Usage
-- ctrl/alt + shift + s: pulls up an input field, where you can type:
+- ctrl/alt + shift + s once: pulls up an input field, where you can type:
- `search term` or `[search term]`: for each selection, select up to and including the first occurrence of the search term.
- `/regex search/`: select through the first occurrence of the regex.
@@ -13,6 +13,8 @@ Fill a void in your Sublime Text multiple selection capabilities! This plugin al
- `-/regex/`: backwards regex.
- `-{character count}`: select backwards a certain number of characters (`{-count}` works too).
+- ctrl/alt + shift + s a second time: reverse the search direction.
+
- ctrl/alt + shift + r: reverse all selections (so if the insertion point is at the end of the selection, it is moved to the beginning, and vice versa).
On Windows and Linux the default shortcuts use alt. On OSX ctrl is used.
diff --git a/select-until.py b/select-until.py
index 02d1f16..b4d98aa 100644
--- a/select-until.py
+++ b/select-until.py
@@ -10,13 +10,17 @@ def safe_end(region):
return -1
return region.end()
+def clean_up(view):
+ view.erase_regions("select-until-extended")
+ view.erase_regions("select-until")
+ view.erase_regions("select-until-originals")
+ SelectUntilCommand.running = False
+
def on_done(view, extend):
if extend:
newSels = view.get_regions("select-until-extended")
else:
newSels = view.get_regions("select-until")
- view.erase_regions("select-until-extended")
- view.erase_regions("select-until")
sels = view.sel()
sels.clear()
@@ -24,38 +28,38 @@ def on_done(view, extend):
sels.add(sel)
SelectUntilCommand.prevSelector = SelectUntilCommand.temp or SelectUntilCommand.prevSelector
+ clean_up(view)
-rSelector = re.compile("^(-?)(?:\{(-?\d+)\}|\[(.+)\]|/(.+)/)$")
+rSelector = re.compile("^(-?)(?:\{(-?\d+)\}|\[(.+)\]|/(.+)/|(.*))$")
def find_matching_point(view, sel, selector):
if selector == "": return -1
result = rSelector.search(selector)
- if result is None: return safe_end(view.find(selector, sel.end(), sublime.LITERAL))
-
groups = result.groups()
isReverse = (groups[0] == "-")
num = int(groups[1]) if groups[1] is not None else None
- chars = groups[2]
+ chars = groups[2] or groups[4]
regex = groups[3]
+ searchForward = isReverse ^ SelectUntilCommand.searchForward
- if num is not None:
- if isReverse: return sel.begin() - num
- else: return sel.end() + num
-
- if not isReverse:
- if regex is not None: return safe_end(view.find(regex, sel.end()))
+ if searchForward:
+ if num is not None: return sel.end() + num
+ elif regex is not None: return safe_end(view.find(regex, sel.end()))
else: return safe_end(view.find(chars, sel.end(), sublime.LITERAL))
- if regex is not None: regions = view.find_all(regex)
- else: regions = view.find_all(chars, sublime.LITERAL)
+ else:
+ if num is not None: return sel.begin() - num
+ elif regex is not None: regions = view.find_all(regex)
+ else: regions = view.find_all(chars, sublime.LITERAL)
+
+ for region in reversed(regions):
+ if region.end() <= sel.begin():
+ return region.begin()
- for region in reversed(regions):
- if region.end() <= sel.begin():
- return region.begin()
return -1
-def on_change(view, oriSels, selector):
+def on_change(view, oriSels, selector, extend):
SelectUntilCommand.temp = selector
extendedSels = []
newSels = []
@@ -71,31 +75,59 @@ def on_change(view, oriSels, selector):
newSels.append(region)
- view.add_regions("select-until-extended", extendedSels, "comment", "", sublime.DRAW_OUTLINED)
- view.add_regions("select-until", newSels, "comment", "", sublime.DRAW_OUTLINED)
+ view.add_regions("select-until-originals", oriSels, "comment", "", sublime.DRAW_EMPTY)
+ if extend:
+ view.erase_regions("select-until")
+ view.add_regions("select-until-extended", extendedSels, "entity", "", sublime.DRAW_OUTLINED)
+ else:
+ view.erase_regions("select-until-extended")
+ view.add_regions("select-until", newSels, "entity", "", sublime.DRAW_EMPTY)
def on_cancel(view, oriSels):
- view.erase_regions("select-until-extended")
- view.erase_regions("select-until")
-
sels = view.sel()
sels.clear()
for sel in oriSels:
sels.add(sel)
+ clean_up(view)
+
class SelectUntilCommand(sublime_plugin.TextCommand):
temp = ""
prevSelector = ""
+ #If we get called again while the quick panel's up, on_cancel gets called.
+ #There's no way in the API to distinguish this from the user pressing esc, so
+ #we have to do it ourselves.
+ running = False
+ searchForward = True
+
def run(self, edit, extend):
- view = self.view
+ #Make sure the view never refers to the quick panel - if we hit the shortcut
+ #while the panel is up, the wrong view is targetted.
+ view = self.view.window().active_view_in_group(self.view.window().active_group())
+
+ if SelectUntilCommand.running:
+ #Don't switch direction if the panel is open but unfocussed
+ if view != self.view and SelectUntilCommand.extend == extend:
+ SelectUntilCommand.searchForward = not SelectUntilCommand.searchForward
+ SelectUntilCommand.prevSelector = SelectUntilCommand.temp
+ else:
+ SelectUntilCommand.searchForward = True
+ SelectUntilCommand.running = True
+ SelectUntilCommand.extend = extend
+
+ #We have to use set_timeout here; otherwise the quick panel doesn't actually
+ #update correctly if we open it a second time. Seems to be a bug in Sublime.
+ sublime.set_timeout(lambda : self.show_panel(view, extend), 0)
+
+ def show_panel(self, view, extend):
oriSels = [ sel for sel in view.sel() ]
-
+ direction = "Next" if SelectUntilCommand.searchForward else "Previous"
v = view.window().show_input_panel(
- "Select Until Next -- chars or [chars] or {count} or /regex/. Use minus (-) to reverse search:",
+ "Select Until {} -- chars or [chars] or {{count}} or /regex/. Use minus (-) or press shortcut again to reverse search:".format(direction),
SelectUntilCommand.prevSelector,
lambda selector: on_done(view, extend),
- lambda selector: on_change(view, oriSels, selector),
+ lambda selector: on_change(view, oriSels, selector, extend),
lambda : on_cancel(view, oriSels)
)
v.sel().clear()