| 1 | # This is a sample commands.py. You can add your own commands here. |
| 2 | # |
| 3 | # Please refer to commands_full.py for all the default commands and a complete |
| 4 | # documentation. Do NOT add them all here, or you may end up with defunct |
| 5 | # commands when upgrading ranger. |
| 6 | |
| 7 | # A simple command for demonstration purposes follows. |
| 8 | # ----------------------------------------------------------------------------- |
| 9 | |
| 10 | from __future__ import (absolute_import, division, print_function) |
| 11 | |
| 12 | # You can import any python module as needed. |
| 13 | import os |
| 14 | |
| 15 | # You always need to import ranger.api.commands here to get the Command class: |
| 16 | from ranger.api.commands import Command |
| 17 | |
| 18 | |
| 19 | # Any class that is a subclass of "Command" will be integrated into ranger as a |
| 20 | # command. Try typing ":my_edit<ENTER>" in ranger! |
| 21 | class my_edit(Command): |
| 22 | # The so-called doc-string of the class will be visible in the built-in |
| 23 | # help that is accessible by typing "?c" inside ranger. |
| 24 | """:my_edit <filename> |
| 25 | |
| 26 | A sample command for demonstration purposes that opens a file in an editor. |
| 27 | """ |
| 28 | |
| 29 | # The execute method is called when you run this command in ranger. |
| 30 | def execute(self): |
| 31 | # self.arg(1) is the first (space-separated) argument to the function. |
| 32 | # This way you can write ":my_edit somefilename<ENTER>". |
| 33 | if self.arg(1): |
| 34 | # self.rest(1) contains self.arg(1) and everything that follows |
| 35 | target_filename = self.rest(1) |
| 36 | else: |
| 37 | # self.fm is a ranger.core.filemanager.FileManager object and gives |
| 38 | # you access to internals of ranger. |
| 39 | # self.fm.thisfile is a ranger.container.file.File object and is a |
| 40 | # reference to the currently selected file. |
| 41 | target_filename = self.fm.thisfile.path |
| 42 | |
| 43 | # This is a generic function to print text in ranger. |
| 44 | self.fm.notify("Let's edit the file " + target_filename + "!") |
| 45 | |
| 46 | # Using bad=True in fm.notify allows you to print error messages: |
| 47 | if not os.path.exists(target_filename): |
| 48 | self.fm.notify("The given file does not exist!", bad=True) |
| 49 | return |
| 50 | |
| 51 | # This executes a function from ranger.core.acitons, a module with a |
| 52 | # variety of subroutines that can help you construct commands. |
| 53 | # Check out the source, or run "pydoc ranger.core.actions" for a list. |
| 54 | self.fm.edit_file(target_filename) |
| 55 | |
| 56 | # The tab method is called when you press tab, and should return a list of |
| 57 | # suggestions that the user will tab through. |
| 58 | # tabnum is 1 for <TAB> and -1 for <S-TAB> by default |
| 59 | def tab(self, tabnum): |
| 60 | # This is a generic tab-completion function that iterates through the |
| 61 | # content of the current directory. |
| 62 | return self._tab_directory_content() |
| 63 | |
| 64 | class fzf_select(Command): |
| 65 | """ |
| 66 | :fzf_select |
| 67 | Find a file or directory using fzf. |
| 68 | """ |
| 69 | def execute(self): |
| 70 | import subprocess |
| 71 | import os.path |
| 72 | from ranger.ext.get_executables import get_executables |
| 73 | |
| 74 | if 'fzf' not in get_executables(): |
| 75 | self.fm.notify('Could not find fzf in PATH', bad=True) |
| 76 | return |
| 77 | |
| 78 | env = os.environ.copy() |
| 79 | env['FZF_DEFAULT_OPTS'] = '--height=40% --layout=reverse --border' |
| 80 | |
| 81 | # Use fd if available, else find |
| 82 | if 'fd' in get_executables(): |
| 83 | fzf = self.fm.execute_command('fd . | fzf', env=env, |
| 84 | universal_newlines=True, |
| 85 | stdout=subprocess.PIPE) |
| 86 | else: |
| 87 | fzf = self.fm.execute_command('find . | fzf', env=env, |
| 88 | universal_newlines=True, |
| 89 | stdout=subprocess.PIPE) |
| 90 | |
| 91 | stdout, _ = fzf.communicate() |
| 92 | if fzf.returncode == 0: |
| 93 | selected = os.path.abspath(stdout.strip()) |
| 94 | if os.path.isdir(selected): |
| 95 | self.fm.cd(selected) |
| 96 | else: |
| 97 | self.fm.select_file(selected) |
| 98 | |
| 99 | class fzf_locate(Command): |
| 100 | """ |
| 101 | :fzf_locate |
| 102 | Find a file in the whole system using locate and fzf. |
| 103 | """ |
| 104 | def execute(self): |
| 105 | import subprocess |
| 106 | import os.path |
| 107 | from ranger.ext.get_executables import get_executables |
| 108 | |
| 109 | if 'fzf' not in get_executables(): |
| 110 | self.fm.notify('Could not find fzf in PATH', bad=True) |
| 111 | return |
| 112 | |
| 113 | if 'locate' not in get_executables(): |
| 114 | self.fm.notify('Could not find locate in PATH', bad=True) |
| 115 | return |
| 116 | |
| 117 | env = os.environ.copy() |
| 118 | env['FZF_DEFAULT_OPTS'] = '--height=40% --layout=reverse --border' |
| 119 | |
| 120 | fzf = self.fm.execute_command('locate / | fzf', env=env, |
| 121 | universal_newlines=True, |
| 122 | stdout=subprocess.PIPE) |
| 123 | |
| 124 | stdout, _ = fzf.communicate() |
| 125 | if fzf.returncode == 0: |
| 126 | selected = stdout.strip() |
| 127 | if os.path.exists(selected): |
| 128 | if os.path.isdir(selected): |
| 129 | self.fm.cd(selected) |
| 130 | else: |
| 131 | self.fm.select_file(selected) |
| 132 | |
| 133 | class fzf_grep(Command): |
| 134 | """ |
| 135 | :fzf_grep [<search_term>] |
| 136 | Search file contents using ripgrep and open with fzf. |
| 137 | """ |
| 138 | def execute(self): |
| 139 | import subprocess |
| 140 | import os |
| 141 | from ranger.ext.get_executables import get_executables |
| 142 | |
| 143 | if 'rg' not in get_executables(): |
| 144 | self.fm.notify('Could not find ripgrep in PATH', bad=True) |
| 145 | return |
| 146 | if 'fzf' not in get_executables(): |
| 147 | self.fm.notify('Could not find fzf in PATH', bad=True) |
| 148 | return |
| 149 | |
| 150 | search_term = self.rest(1) |
| 151 | if not search_term: |
| 152 | self.fm.notify('Usage: fzf_grep <search_term>', bad=True) |
| 153 | return |
| 154 | |
| 155 | env = os.environ.copy() |
| 156 | env['FZF_DEFAULT_OPTS'] = '--height=40% --layout=reverse --border' |
| 157 | |
| 158 | command = f"rg --files-with-matches --no-messages '{search_term}' | fzf" |
| 159 | fzf = self.fm.execute_command(command, env=env, |
| 160 | universal_newlines=True, |
| 161 | stdout=subprocess.PIPE) |
| 162 | |
| 163 | stdout, _ = fzf.communicate() |
| 164 | if fzf.returncode == 0: |
| 165 | selected = os.path.abspath(stdout.strip()) |
| 166 | self.fm.select_file(selected) |
| 167 | |
| 168 | class fzf_cd(Command): |
| 169 | """ |
| 170 | :fzf_cd |
| 171 | Change to a subdirectory using fzf. |
| 172 | """ |
| 173 | def execute(self): |
| 174 | import subprocess |
| 175 | import os.path |
| 176 | from ranger.ext.get_executables import get_executables |
| 177 | |
| 178 | if 'fzf' not in get_executables(): |
| 179 | self.fm.notify('Could not find fzf in PATH', bad=True) |
| 180 | return |
| 181 | |
| 182 | env = os.environ.copy() |
| 183 | env['FZF_DEFAULT_OPTS'] = '--height=40% --layout=reverse --border' |
| 184 | |
| 185 | # Find only directories |
| 186 | if 'fd' in get_executables(): |
| 187 | command = 'fd --type d . | fzf' |
| 188 | else: |
| 189 | command = 'find . -type d | fzf' |
| 190 | |
| 191 | fzf = self.fm.execute_command(command, env=env, |
| 192 | universal_newlines=True, |
| 193 | stdout=subprocess.PIPE) |
| 194 | |
| 195 | stdout, _ = fzf.communicate() |
| 196 | if fzf.returncode == 0: |
| 197 | selected = os.path.abspath(stdout.strip()) |
| 198 | self.fm.cd(selected) |
| 199 | |
| 200 | class mkcd(Command): |
| 201 | """ |
| 202 | :mkcd <dirname> |
| 203 | Create a directory and enter it. |
| 204 | """ |
| 205 | def execute(self): |
| 206 | from os.path import join, expanduser, lexists |
| 207 | from os import makedirs |
| 208 | |
| 209 | dirname = join(self.fm.thisdir.path, expanduser(self.rest(1))) |
| 210 | if not lexists(dirname): |
| 211 | makedirs(dirname) |
| 212 | self.fm.cd(dirname) |
| 213 | else: |
| 214 | self.fm.notify("Directory already exists!", bad=True) |
| 215 | |
| 216 | class compress(Command): |
| 217 | """ |
| 218 | :compress [<filename>] |
| 219 | Compress selected files to archive. |
| 220 | """ |
| 221 | def execute(self): |
| 222 | import os |
| 223 | cwd = self.fm.thisdir |
| 224 | marked_files = cwd.get_selection() |
| 225 | |
| 226 | if not marked_files: |
| 227 | return |
| 228 | |
| 229 | def refresh(_): |
| 230 | cwd = self.fm.get_directory(original_path) |
| 231 | cwd.load_content() |
| 232 | |
| 233 | original_path = cwd.path |
| 234 | parts = self.line.split() |
| 235 | if len(parts) > 1: |
| 236 | archive_name = parts[1] |
| 237 | else: |
| 238 | archive_name = os.path.basename(marked_files[0]) + '.tar.gz' |
| 239 | |
| 240 | # Create archive command |
| 241 | files_list = ' '.join([f.relative_path for f in marked_files]) |
| 242 | self.fm.execute_console(f'shell tar czf {archive_name} {files_list}') |
| 243 | self.fm.reload_cwd() |
| 244 | |
| 245 | class extract(Command): |
| 246 | """ |
| 247 | :extract |
| 248 | Extract selected archives. |
| 249 | """ |
| 250 | def execute(self): |
| 251 | cwd = self.fm.thisdir |
| 252 | marked_files = cwd.get_selection() |
| 253 | |
| 254 | if not marked_files: |
| 255 | return |
| 256 | |
| 257 | for f in marked_files: |
| 258 | if f.is_file: |
| 259 | self.fm.execute_console(f'shell atool -x {f.relative_path}') |
| 260 | |
| 261 | self.fm.reload_cwd() |