diff --git a/Default (Linux).sublime-keymap b/Default (Linux).sublime-keymap index aaefb40..5f8d93b 100755 --- a/Default (Linux).sublime-keymap +++ b/Default (Linux).sublime-keymap @@ -10,7 +10,25 @@ {"key": "setting.is_widget", "operator": "equal", "operand": false} ] }, - { "keys": ["ctrl+alt+f"], "command": "multi_find_all" }, + + // 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+d"], "command": "selection_fields", "args": {"mode": "smart"} }, { "keys": ["escape"], "command": "selection_fields", "args": {"mode": "pop"}, diff --git a/Default (OSX).sublime-keymap b/Default (OSX).sublime-keymap index 00515da..83fefe2 100755 --- a/Default (OSX).sublime-keymap +++ b/Default (OSX).sublime-keymap @@ -10,7 +10,25 @@ {"key": "setting.is_widget", "operator": "equal", "operand": false} ] }, - { "keys": ["super+alt+j"], "command": "multi_find_all" }, + + // 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+d"], "command": "selection_fields", "args": {"mode": "smart"} }, { "keys": ["escape"], "command": "selection_fields", "args": {"mode": "pop"}, diff --git a/Default (Windows).sublime-keymap b/Default (Windows).sublime-keymap index aaefb40..5f8d93b 100755 --- a/Default (Windows).sublime-keymap +++ b/Default (Windows).sublime-keymap @@ -10,7 +10,25 @@ {"key": "setting.is_widget", "operator": "equal", "operand": false} ] }, - { "keys": ["ctrl+alt+f"], "command": "multi_find_all" }, + + // 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+d"], "command": "selection_fields", "args": {"mode": "smart"} }, { "keys": ["escape"], "command": "selection_fields", "args": {"mode": "pop"}, 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 dd04e86..149b050 100755 --- a/MultiEditUtils.py +++ b/MultiEditUtils.py @@ -4,19 +4,130 @@ class MultiFindAllCommand(sublime_plugin.TextCommand): - def run(self, edit): + 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(): substr = view.substr(region) - for region in view.find_all(substr, sublime.LITERAL): - newRegions.append(region) + + if case: + for region in view.find_all(substr, sublime.LITERAL): + newRegions.append(region) + else: + for region in view.find_all(substr, sublime.LITERAL | sublime.IGNORECASE): + newRegions.append(region) + + if word: + 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 = [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 26116fc..6f72144 100755 --- a/README.md +++ b/README.md @@ -67,7 +67,28 @@ 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 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 +``` + +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)