├── Default (OSX).sublime-keymap ├── Default (Linux).sublime-keymap ├── Default (Windows).sublime-keymap ├── push.bat ├── .github └── FUNDING.yml ├── readme.md ├── FindExcludePatterns.py └── FindResultsApplyChanges.py /Default (OSX).sublime-keymap: -------------------------------------------------------------------------------- 1 | [{ "keys": ["super+s"], "command": "save2" }] 2 | -------------------------------------------------------------------------------- /Default (Linux).sublime-keymap: -------------------------------------------------------------------------------- 1 | [{ "keys": ["ctrl+s"], "command": "save2" }] 2 | -------------------------------------------------------------------------------- /Default (Windows).sublime-keymap: -------------------------------------------------------------------------------- 1 | [{ "keys": ["ctrl+s"], "command": "save2" }] 2 | -------------------------------------------------------------------------------- /push.bat: -------------------------------------------------------------------------------- 1 | rem FF PTSD 2 | 3 | git add --all 4 | git commit -m "update" 5 | git push --prune 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [titoBouzout] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Find Results Apply Changes 2 | 3 | ## Description 4 | 5 | Applies any change you made to a "Find Results" buffer back to the files when 6 | you press `ctrl/cmd+s`. 7 | 8 | ## Source-code 9 | 10 | https://github.com/titoBouzout/FindResultsApplyChanges 11 | 12 | ## License 13 | 14 | "None are so hopelessly enslaved as those who falsely believe they are free." 15 | Johann Wolfgang von Goethe 16 | 17 | Copyeverything (C) 2014 Tito Bouzout 18 | 19 | This license apply to all the files inside this program unless noted different 20 | for some files or portions of code inside these files. 21 | 22 | This program is free software: you can redistribute it and/or modify it under 23 | the terms of the GNU General Public License as published by the Free Software 24 | Foundation. http://www.gnu.org/licenses/gpl.html 25 | 26 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 27 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 28 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public License along with 31 | this program. If not, see http://www.gnu.org/licenses/gpl.html 32 | -------------------------------------------------------------------------------- /FindExcludePatterns.py: -------------------------------------------------------------------------------- 1 | import sublime_plugin 2 | import sublime 3 | import re 4 | 5 | 6 | class FindExcludePatternsOMG(sublime_plugin.EventListener): 7 | def on_window_command(self, window, command_name, args): 8 | if ( 9 | command_name == "show_panel" 10 | and "panel" in args 11 | and args["panel"] == "find_in_files" 12 | and "FindExcludePatternsOMG" not in args 13 | ): 14 | s = sublime.load_settings("Preferences.sublime-settings") 15 | exclude = list( 16 | set( 17 | list( 18 | s.get("index_exclude_patterns", []) 19 | + s.get("binary_file_patterns", []) 20 | ) 21 | ) 22 | ) 23 | 24 | for k, v in enumerate(exclude): 25 | exclude[k] = exclude[k].replace("\\", "/") 26 | exclude = sorted(exclude) 27 | 28 | if "where" in args and args["where"]: 29 | where = args["where"] 30 | where = where.replace("\\", "/") 31 | where = where.replace("*", "") 32 | where = re.sub("([a-z])\:/", "", where, 0, re.I) 33 | where = re.sub("/$", "", where) 34 | new_exclude = [] 35 | for item in exclude: 36 | thingy = item 37 | thingy = thingy.replace("-*", "") 38 | thingy = thingy.replace("*", "") 39 | thingy = re.sub("([a-z])\:/", "", thingy, 0, re.I) 40 | thingy = re.sub("-/([a-z])\/", "", thingy, 0, re.I) 41 | thingy = re.sub("/$", "", thingy) 42 | if thingy not in where: 43 | new_exclude.append(item) 44 | args["where"] = args["where"] + "," + ("-" + (",-".join(new_exclude))) 45 | else: 46 | args["where"] = "-" + (",-".join(exclude)) 47 | 48 | args["where"] = args["where"].replace("\\", "/") 49 | args["FindExcludePatternsOMG"] = 1 50 | 51 | return (command_name, args) 52 | -------------------------------------------------------------------------------- /FindResultsApplyChanges.py: -------------------------------------------------------------------------------- 1 | # coding=utf8 2 | import sublime 3 | import sublime_plugin 4 | import re 5 | import os 6 | 7 | debug = False 8 | 9 | 10 | class Save2Command(sublime_plugin.WindowCommand): 11 | def run(self): 12 | window = sublime.active_window() 13 | view = window.active_view() 14 | if ( 15 | window 16 | and view 17 | and "Find Results.hidden-tmLanguage" in view.settings().get("syntax") 18 | ): 19 | view.run_command("find_results_apply_changes") 20 | else: 21 | view.run_command("save") 22 | 23 | 24 | class FindResultsApplyChangesEventListener(sublime_plugin.EventListener): 25 | def on_new(self, view): 26 | sublime.set_timeout(lambda: self.on_new_ugly(view), 0) 27 | 28 | def on_new_ugly(self, view): 29 | if view.name() == "Find Results": 30 | view.settings().set("result_file_regex", "") 31 | view.settings().set("result_line_regex", "") 32 | 33 | 34 | class FindResultsApplyChangesCommand(sublime_plugin.TextCommand): 35 | def run(self, edit): 36 | 37 | if sublime.active_window().active_view().name() == "Find Results": 38 | v = sublime.active_window().active_view() 39 | 40 | # avoid corruption 41 | 42 | if v.settings().get("FindResultsApplyChanges-possible-corruption", False): 43 | sublime.message_dialog( 44 | "Committing twice when new newlines has been inserted will corrupt the file" 45 | ) 46 | return 47 | 48 | # set 'Find results' regions 49 | 50 | if debug: 51 | draw = sublime.DRAW_OUTLINED 52 | else: 53 | draw = sublime.HIDDEN 54 | region_lines = v.find_all(r"^ +([0-9]+)(\: | )") 55 | v.erase_regions("FindResultsApplyChanges-lines") 56 | v.add_regions( 57 | "FindResultsApplyChanges-lines", 58 | region_lines, 59 | "entity.name.function", 60 | "", 61 | draw, 62 | ) 63 | 64 | region_files = v.find_all(r"^\n[^\n]+\:\n") 65 | v.erase_regions("FindResultsApplyChanges-files") 66 | v.add_regions( 67 | "FindResultsApplyChanges-files", 68 | region_files, 69 | "entity.class.name", 70 | "", 71 | draw, 72 | ) 73 | 74 | # get 'Find Results' regions 75 | 76 | region_files = v.get_regions("FindResultsApplyChanges-files") 77 | region_lines = v.get_regions("FindResultsApplyChanges-lines") 78 | 79 | changes = {} 80 | 81 | for file in range(len(region_files)): 82 | 83 | region_file = region_files[file] 84 | try: 85 | next_region_file = region_files[file + 1] 86 | except: 87 | next_region_file = sublime.Region(v.size(), v.size()) 88 | file_name = re.sub(r"\:$", "", v.substr(region_file).strip()) 89 | 90 | changes[file_name] = {} 91 | 92 | for line in range(len(region_lines)): 93 | 94 | region_line = region_lines[line] 95 | try: 96 | next_region_line = region_lines[line + 1] 97 | except: 98 | next_region_line = sublime.Region(v.size(), v.size()) 99 | 100 | if ( 101 | region_line.a > region_file.a 102 | and region_line.a < next_region_file.a 103 | ): 104 | line_number = ( 105 | int(re.sub(r"\:$", "", v.substr(region_line).strip())) - 1 106 | ) 107 | line_content = v.substr( 108 | sublime.Region( 109 | region_line.b, 110 | ( 111 | next_region_line.a 112 | if next_region_line.a < next_region_file.a 113 | else next_region_file.a 114 | ) 115 | - 1, 116 | ) 117 | ) 118 | line_content = re.sub( 119 | r"\n +\.+$", "", line_content 120 | ) # remove 'dots' Ellipsis 121 | changes[file_name][line_number] = line_content 122 | 123 | # remove footer 124 | if changes[file_name]: 125 | footer_line = max(changes[file_name].keys()) 126 | changes[file_name][footer_line] = re.sub( 127 | "\s+[0-9]+ matche?s? (across|in) [0-9]+ files?\s*$", 128 | "", 129 | changes[file_name][footer_line], 130 | ) 131 | 132 | # apply changes 133 | 134 | modified_files = 0 135 | for f in changes: 136 | f = f.strip() 137 | if f and changes[f] and os.path.exists(f): 138 | content = self.read(f).split("\n") 139 | modified = False 140 | for k in changes[f].keys(): 141 | k = int(k) 142 | if content[k] != changes[f][k]: 143 | content[k] = changes[f][k] 144 | if "\n" in changes[f][k]: 145 | v.settings().set( 146 | "FindResultsApplyChanges-possible-corruption", True 147 | ) 148 | modified = True 149 | if modified: 150 | self.write(f, "\n".join(content)) 151 | modified_files += 1 152 | if modified_files: 153 | sublime.status_message( 154 | "Written modifications to " + str(modified_files) + " file(s) " 155 | ) 156 | 157 | def is_enabled(self): 158 | return ( 159 | sublime.active_window().active_view() 160 | and sublime.active_window().active_view().name() == "Find Results" 161 | ) 162 | 163 | def read(self, filepath): 164 | with open(filepath, "r", newline=None, encoding="utf8") as f: 165 | return f.read() 166 | 167 | def write(self, filepath, c): 168 | with open(filepath, "w+", encoding="utf8", newline="") as f: 169 | f.write(str(c)) 170 | --------------------------------------------------------------------------------