diff --git a/Default (Linux).sublime-keymap b/Default (Linux).sublime-keymap index 8ac32c0..5f8d93b 100755 --- a/Default (Linux).sublime-keymap +++ b/Default (Linux).sublime-keymap @@ -11,15 +11,23 @@ ] }, - // multi find all - the first is the same behaviour as old [alt+f3] - { "keys": ["alt+f3", "alt+f3"], "command": "find_all_under"}, + // Multi Find All example keybindings, uncomment to activate + + // // main keybinding, set the search type you're most comfortable with, default is Case+Word + // { "keys": ["ctrl+alt+f", "ctrl+alt+f"], "command": "multi_find_all", "args": {"case": true, "word": true}}, + + // { "keys": ["ctrl+alt+f", "ctrl+c"], "command": "multi_find_all", "args": {"case": false}}, + // { "keys": ["ctrl+alt+f", "c"], "command": "multi_find_all", "args": {"case": true}}, + // { "keys": ["ctrl+alt+f", "ctrl+w"], "command": "multi_find_all", "args": {"case": false, "word": true}}, + // { "keys": ["ctrl+alt+f", "w"], "command": "multi_find_all", "args": {"case": true, "word": true}}, + // { "keys": ["ctrl+alt+f", "q"], "command": "multi_find_all", "args": {"case": true, "word": true, "ignore_comments": true}}, + + // // find all with regex search, additive(on top of current selection) or subtractive + // { "keys": ["ctrl+alt+f", "r"], "command": "multi_find_all_regex"}, + // { "keys": ["ctrl+alt+f", "ctrl+alt+r"], "command": "multi_find_all_regex", "args": {"subtract": true}}, + // { "keys": ["ctrl+alt+f", "ctrl+r"], "command": "multi_find_all_regex", "args": {"case": false}}, + // { "keys": ["ctrl+alt+f", "ctrl+alt+shift+r"], "command": "multi_find_all_regex", "args": {"subtract": true, "case": false}}, - { "keys": ["ctrl+alt+f"], "command": "multi_find_all" }, - { "keys": ["alt+f3", "c"], "command": "multi_find_all", "args": {"case": true}}, - { "keys": ["alt+f3", "ctrl+c"], "command": "multi_find_all", "args": {"case": false}}, - { "keys": ["alt+f3", "w"], "command": "multi_find_all", "args": {"case": true, "word": true}}, - { "keys": ["alt+f3", "ctrl+w"], "command": "multi_find_all", "args": {"case": false, "word": true}}, - { "keys": ["alt+f3", "q"], "command": "multi_find_all", "args": {"case": true, "word": true, "ignore_comments": true}}, { "keys": ["ctrl+alt+d"], "command": "selection_fields", "args": {"mode": "smart"} }, { "keys": ["escape"], "command": "selection_fields", diff --git a/Default (OSX).sublime-keymap b/Default (OSX).sublime-keymap index 9325397..83fefe2 100755 --- a/Default (OSX).sublime-keymap +++ b/Default (OSX).sublime-keymap @@ -11,15 +11,23 @@ ] }, - // multi find all - the first is the same behaviour as old [alt+f3] - { "keys": ["ctrl+super+g", "ctrl+super+g"], "command": "find_all_under"}, + // Multi Find All example keybindings, uncomment to activate + + // // main keybinding, set the search type you're most comfortable with, default is Case+Word + // { "keys": ["ctrl+super+g", "ctrl+super+g"], "command": "multi_find_all", "args": {"case": true, "word": true}}, + + // { "keys": ["ctrl+super+g", "ctrl+c"], "command": "multi_find_all", "args": {"case": false}}, + // { "keys": ["ctrl+super+g", "c"], "command": "multi_find_all", "args": {"case": true}}, + // { "keys": ["ctrl+super+g", "ctrl+w"], "command": "multi_find_all", "args": {"case": false, "word": true}}, + // { "keys": ["ctrl+super+g", "w"], "command": "multi_find_all", "args": {"case": true, "word": true}}, + // { "keys": ["ctrl+super+g", "q"], "command": "multi_find_all", "args": {"case": true, "word": true, "ignore_comments": true}}, + + // // find all with regex search, additive(on top of current selection) or subtractive + // { "keys": ["ctrl+super+g", "r"], "command": "multi_find_all_regex"}, + // { "keys": ["ctrl+super+g", "ctrl+alt+r"], "command": "multi_find_all_regex", "args": {"subtract": true}}, + // { "keys": ["ctrl+super+g", "ctrl+r"], "command": "multi_find_all_regex", "args": {"case": false}}, + // { "keys": ["ctrl+super+g", "ctrl+alt+shift+r"], "command": "multi_find_all_regex", "args": {"subtract": true, "case": false}}, - { "keys": ["super+alt+j"], "command": "multi_find_all" }, - { "keys": ["ctrl+super+g", "c"], "command": "multi_find_all", "args": {"case": true}}, - { "keys": ["ctrl+super+g", "ctrl+c"], "command": "multi_find_all", "args": {"case": false}}, - { "keys": ["ctrl+super+g", "w"], "command": "multi_find_all", "args": {"case": true, "word": true}}, - { "keys": ["ctrl+super+g", "ctrl+w"], "command": "multi_find_all", "args": {"case": false, "word": true}}, - { "keys": ["ctrl+super+g", "q"], "command": "multi_find_all", "args": {"case": true, "word": true, "ignore_comments": true}}, { "keys": ["super+alt+d"], "command": "selection_fields", "args": {"mode": "smart"} }, { "keys": ["escape"], "command": "selection_fields", diff --git a/Default (Windows).sublime-keymap b/Default (Windows).sublime-keymap index 8ac32c0..5f8d93b 100755 --- a/Default (Windows).sublime-keymap +++ b/Default (Windows).sublime-keymap @@ -11,15 +11,23 @@ ] }, - // multi find all - the first is the same behaviour as old [alt+f3] - { "keys": ["alt+f3", "alt+f3"], "command": "find_all_under"}, + // Multi Find All example keybindings, uncomment to activate + + // // main keybinding, set the search type you're most comfortable with, default is Case+Word + // { "keys": ["ctrl+alt+f", "ctrl+alt+f"], "command": "multi_find_all", "args": {"case": true, "word": true}}, + + // { "keys": ["ctrl+alt+f", "ctrl+c"], "command": "multi_find_all", "args": {"case": false}}, + // { "keys": ["ctrl+alt+f", "c"], "command": "multi_find_all", "args": {"case": true}}, + // { "keys": ["ctrl+alt+f", "ctrl+w"], "command": "multi_find_all", "args": {"case": false, "word": true}}, + // { "keys": ["ctrl+alt+f", "w"], "command": "multi_find_all", "args": {"case": true, "word": true}}, + // { "keys": ["ctrl+alt+f", "q"], "command": "multi_find_all", "args": {"case": true, "word": true, "ignore_comments": true}}, + + // // find all with regex search, additive(on top of current selection) or subtractive + // { "keys": ["ctrl+alt+f", "r"], "command": "multi_find_all_regex"}, + // { "keys": ["ctrl+alt+f", "ctrl+alt+r"], "command": "multi_find_all_regex", "args": {"subtract": true}}, + // { "keys": ["ctrl+alt+f", "ctrl+r"], "command": "multi_find_all_regex", "args": {"case": false}}, + // { "keys": ["ctrl+alt+f", "ctrl+alt+shift+r"], "command": "multi_find_all_regex", "args": {"subtract": true, "case": false}}, - { "keys": ["ctrl+alt+f"], "command": "multi_find_all" }, - { "keys": ["alt+f3", "c"], "command": "multi_find_all", "args": {"case": true}}, - { "keys": ["alt+f3", "ctrl+c"], "command": "multi_find_all", "args": {"case": false}}, - { "keys": ["alt+f3", "w"], "command": "multi_find_all", "args": {"case": true, "word": true}}, - { "keys": ["alt+f3", "ctrl+w"], "command": "multi_find_all", "args": {"case": false, "word": true}}, - { "keys": ["alt+f3", "q"], "command": "multi_find_all", "args": {"case": true, "word": true, "ignore_comments": true}}, { "keys": ["ctrl+alt+d"], "command": "selection_fields", "args": {"mode": "smart"} }, { "keys": ["escape"], "command": "selection_fields", diff --git a/Default.sublime-commands b/Default.sublime-commands index e39d1bd..c57624f 100755 --- a/Default.sublime-commands +++ b/Default.sublime-commands @@ -8,6 +8,6 @@ { "command": "split_selection", "caption" : "MultiEditUtils: Split selection" }, { "command": "strip_selection", "caption" : "MultiEditUtils: Strip Selection" }, { "command": "remove_empty_regions", "caption" : "MultiEditUtils: Remove Empty Regions" }, - { "command": "multi_find_all", "caption" : "MultiEditUtils: Multi FindAll" }, + { "command": "multi_find_menu", "caption" : "MultiEditUtils: Multi FindAll" }, { "command": "preserve_case", "caption" : "MultiEditUtils: Preserve Case" } ] diff --git a/MultiEditUtils.py b/MultiEditUtils.py index 247f13d..c6ee635 100755 --- a/MultiEditUtils.py +++ b/MultiEditUtils.py @@ -4,10 +4,37 @@ class MultiFindAllCommand(sublime_plugin.TextCommand): - def run(self, edit, case=True, word=False, ignore_comments=False): + def run(self, edit, case=True, word=False, ignore_comments=False, expand=True): view = self.view newRegions = [] + + # filter selections in order to exclude duplicates since it can hang + # Sublime if search is performed on dozens of selections, this doesn't + # happen with built-in command because it works on a single selection + initial = [sel for sel in view.sel()] + regions, substrings = [], [] + for region in view.sel(): + if expand and region.empty(): + # if expanding substring will be the word + region = view.word(region.a) + # add the region since nothing is selected yet + view.sel().add(region) + # filter by substring (word or not) + substr = view.substr(region) + if substr and substr not in substrings: + regions.append(region) + substrings.append(substr) + view.sel().clear() + if regions: + for region in regions: + view.sel().add(region) + else: + view.window().status_message("Multi Find All: nothing selected") + for sel in initial: + view.sel().add(sel) + return + selected_words = [view.substr(view.word(sel)).lower() for sel in view.sel()] for region in view.sel(): @@ -21,24 +48,86 @@ def run(self, edit, case=True, word=False, ignore_comments=False): newRegions.append(region) if word: - deleted = [] - for region in newRegions: - if view.substr(view.word(region)).lower() not in selected_words: - deleted.append(region) + deleted = [region for region in newRegions + if view.substr(view.word(region)).lower() not in selected_words] newRegions = [region for region in newRegions if region not in deleted] if ignore_comments: - deleted = [] - for region in newRegions: - scope = view.scope_name(region.begin()) - comment = re.search(r'\bcomment\b', scope) - if comment: - deleted.append(region) - newRegions = (region for region in newRegions if region not in deleted) + deleted = [region for region in newRegions + if re.search(r'\bcomment\b', view.scope_name(region.a))] + newRegions = [region for region in newRegions if region not in deleted] for region in newRegions: view.sel().add(region) +class MultiFindAllRegexCommand(sublime_plugin.TextCommand): + + def on_done(self, regex): + + case = sublime.IGNORECASE if not self.case else 0 + regions = self.view.find_all(regex, case) + + # we don't clear the selection so it's additive, it's nice to just add a + # regex search on top of a previous search + if not self.subtract: + for region in regions: + self.view.sel().add(region) + + # the resulting regions will be subtracted instead + else: + for region in regions: + self.view.sel().subtract(region) + + # remove empty selections in both cases, so there aren't loose cursors + regions = [r for r in self.view.sel() if not r.empty()] + self.view.sel().clear() + for region in regions: + self.view.sel().add(region) + for region in self.view.sel(): + print(region) + + def run(self, edit, case=True, subtract=False): + + self.edit = edit + self.case = case + self.subtract = subtract + c = "Additive regex search:" if not subtract else "Subtractive regex search:" + sublime.active_window().show_input_panel(c, "", self.on_done, None, None) + +class MultiFindMenuCommand(sublime_plugin.TextCommand): + + def run(self, edit): + + choice = [ + "Find All Case + Word +", + "Find All Case + Word -", + "Find All Case - Word +", + "Find All Case - Word -", + "Find All Case + Word + Ignore Comments)", + "Find Regex (Additive)", + "Find Regex (Subtractive)" + ] + + def on_done(index): + + if index == -1: + return + if index == 0: + self.view.run_command('multi_find_all', {"case": True, "word": True}) + elif index == 1: + self.view.run_command('multi_find_all', {"case": True}) + elif index == 2: + self.view.run_command('multi_find_all', {"case": False, "word": True}) + elif index == 3: + self.view.run_command('multi_find_all', {"case": False}) + elif index == 4: + self.view.run_command('multi_find_all', {"case": True, "word": True, "ignore_comments": True}) + elif index == 5: + self.view.run_command('multi_find_all_regex') + elif index == 6: + self.view.run_command('multi_find_all_regex', {"subtract": True}) + + self.view.window().show_quick_panel(choice, on_done, 1, 0, None) class JumpToLastRegionCommand(sublime_plugin.TextCommand): diff --git a/README.md b/README.md index 4c2fb18..6f72144 100755 --- a/README.md +++ b/README.md @@ -67,18 +67,29 @@ When splitting your selection or performing other actions on your selection, it ### Quick Find All for multiple selections -Similar to the built-in "Quick Find All" functionality, MultiEditUtils provides a functionality which selects all occurrences of all active selections. The default keybinding of the ```multi_find_all``` command is **ctrl+alt+f** (on Mac it's **cmd+alt+j**). +Similar to the built-in "Quick Find All" functionality, MultiEditUtils provides a functionality which selects all occurrences of all active selections. By default, it will select the word the cursor is on, if the selection is empty, just like `find_all_under` command. If you don't like this behaviour, add the argument `"expand": false` -These are additional keybindings for Windows/Linux (on Mac replace **alt+f3** with **ctrl+super+g**). +These are just suggested keybindings, but you'll have to activate them in your keymap file first. Here shown for Windows/Linux: + +``` +ctrl+alt+f, ctrl+alt+f case: true word: true + +ctrl+alt+f, c case: true +ctrl+alt+f, ctrl+c case: false +ctrl+alt+f, w case: true word: true +ctrl+alt+f, ctrl+w case: false word: true +ctrl+alt+f, q case: true word: true ignore_comments: true ``` -alt+f3, alt+f3 old 'find_all_under' command -alt+f3, c case: true -alt+f3, ctrl+c case: false -alt+f3, w case: true word: true -alt+f3, ctrl+w case: false word: true -alt+f3, q case: true word: true ignore_comments: true +Additionally, you can perform a regex search that finds all occurrences of the entered regex. It can be **additive** (applied on top of your current selection) or **subtractive** (removes the results of the search instead). Example keybindings: + +``` +ctrl+alt+f, r +ctrl+alt+f, ctrl+alt+r subtract: true +ctrl+alt+f, ctrl+r case : false +ctrl+alt+f, ctrl+alt+shift+r subtract: true case: false ``` + ![](http://philippotto.github.io/Sublime-MultiEditUtils/screens/08%20multi%20find%20all.gif)