diff --git a/README.md b/README.md index 64b8e43..fbb9d26 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,13 @@ 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. - `{character count}`: select forward the given number of characters. - - `-[search term]`: select backwards up to and including the search term. - - `-/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). diff --git a/select-until.py b/select-until.py index 02d1f16..8bf7451 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,37 @@ 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] - regex = groups[3] - - 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())) + num = int(groups[0]) if groups[0] is not None else None + chars = groups[1] or groups[3] + regex = groups[2] + searchForward = SelectUntilCommand.searchForward + + 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 +74,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/. 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()