Python · 9193 bytes Raw Blame History
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()