245
|
1 # -*- coding: utf-8 -*-
|
|
2 # Copyright (C) 2009-2013 Roman Zimbelmann <[email protected]>
|
|
3 # This configuration file is licensed under the same terms as ranger.
|
|
4 # ===================================================================
|
|
5 # This file contains ranger's commands.
|
|
6 # It's all in python; lines beginning with # are comments.
|
|
7 #
|
|
8 # Note that additional commands are automatically generated from the methods
|
|
9 # of the class ranger.core.actions.Actions.
|
|
10 #
|
|
11 # You can customize commands in the file ~/.config/ranger/commands.py.
|
|
12 # It has the same syntax as this file. In fact, you can just copy this
|
|
13 # file there with `ranger --copy-config=commands' and make your modifications.
|
|
14 # But make sure you update your configs when you update ranger.
|
|
15 #
|
|
16 # ===================================================================
|
|
17 # Every class defined here which is a subclass of `Command' will be used as a
|
|
18 # command in ranger. Several methods are defined to interface with ranger:
|
|
19 # execute(): called when the command is executed.
|
|
20 # cancel(): called when closing the console.
|
|
21 # tab(): called when <TAB> is pressed.
|
|
22 # quick(): called after each keypress.
|
|
23 #
|
|
24 # The return values for tab() can be either:
|
|
25 # None: There is no tab completion
|
|
26 # A string: Change the console to this string
|
|
27 # A list/tuple/generator: cycle through every item in it
|
|
28 #
|
|
29 # The return value for quick() can be:
|
|
30 # False: Nothing happens
|
|
31 # True: Execute the command afterwards
|
|
32 #
|
|
33 # The return value for execute() and cancel() doesn't matter.
|
|
34 #
|
|
35 # ===================================================================
|
|
36 # Commands have certain attributes and methods that facilitate parsing of
|
|
37 # the arguments:
|
|
38 #
|
|
39 # self.line: The whole line that was written in the console.
|
|
40 # self.args: A list of all (space-separated) arguments to the command.
|
|
41 # self.quantifier: If this command was mapped to the key "X" and
|
|
42 # the user pressed 6X, self.quantifier will be 6.
|
|
43 # self.arg(n): The n-th argument, or an empty string if it doesn't exist.
|
|
44 # self.rest(n): The n-th argument plus everything that followed. For example,
|
|
45 # If the command was "search foo bar a b c", rest(2) will be "bar a b c"
|
|
46 # self.start(n): The n-th argument and anything before it. For example,
|
|
47 # If the command was "search foo bar a b c", rest(2) will be "bar a b c"
|
|
48 #
|
|
49 # ===================================================================
|
|
50 # And this is a little reference for common ranger functions and objects:
|
|
51 #
|
|
52 # self.fm: A reference to the "fm" object which contains most information
|
|
53 # about ranger.
|
|
54 # self.fm.notify(string): Print the given string on the screen.
|
|
55 # self.fm.notify(string, bad=True): Print the given string in RED.
|
|
56 # self.fm.reload_cwd(): Reload the current working directory.
|
|
57 # self.fm.thisdir: The current working directory. (A File object.)
|
|
58 # self.fm.thisfile: The current file. (A File object too.)
|
|
59 # self.fm.thistab.get_selection(): A list of all selected files.
|
|
60 # self.fm.execute_console(string): Execute the string as a ranger command.
|
|
61 # self.fm.open_console(string): Open the console with the given string
|
|
62 # already typed in for you.
|
|
63 # self.fm.move(direction): Moves the cursor in the given direction, which
|
|
64 # can be something like down=3, up=5, right=1, left=1, to=6, ...
|
|
65 #
|
|
66 # File objects (for example self.fm.thisfile) have these useful attributes and
|
|
67 # methods:
|
|
68 #
|
|
69 # cf.path: The path to the file.
|
|
70 # cf.basename: The base name only.
|
|
71 # cf.load_content(): Force a loading of the directories content (which
|
|
72 # obviously works with directories only)
|
|
73 # cf.is_directory: True/False depending on whether it's a directory.
|
|
74 #
|
|
75 # For advanced commands it is unavoidable to dive a bit into the source code
|
|
76 # of ranger.
|
|
77 # ===================================================================
|
|
78
|
|
79 import os
|
|
80 from ranger.api.commands import *
|
|
81 from ranger.core.loader import CommandLoader
|
|
82
|
|
83 class alias(Command):
|
|
84 """:alias <newcommand> <oldcommand>
|
|
85
|
|
86 Copies the oldcommand as newcommand.
|
|
87 """
|
|
88
|
|
89 context = 'browser'
|
|
90 resolve_macros = False
|
|
91
|
|
92 def execute(self):
|
|
93 if not self.arg(1) or not self.arg(2):
|
|
94 self.fm.notify('Syntax: alias <newcommand> <oldcommand>', bad=True)
|
|
95 else:
|
|
96 self.fm.commands.alias(self.arg(1), self.rest(2))
|
|
97
|
|
98 class cd(Command):
|
|
99 """:cd [-r] <dirname>
|
|
100
|
|
101 The cd command changes the directory.
|
|
102 The command 'cd -' is equivalent to typing ``.
|
|
103 Using the option "-r" will get you to the real path.
|
|
104 """
|
|
105
|
|
106 def execute(self):
|
|
107 import os.path
|
|
108 if self.arg(1) == '-r':
|
|
109 self.shift()
|
|
110 destination = os.path.realpath(self.rest(1))
|
|
111 if os.path.isfile(destination):
|
|
112 destination = os.path.dirname(destination)
|
|
113 else:
|
|
114 destination = self.rest(1)
|
|
115
|
|
116 if not destination:
|
|
117 destination = '~'
|
|
118
|
|
119 if destination == '-':
|
|
120 self.fm.enter_bookmark('`')
|
|
121 else:
|
|
122 self.fm.cd(destination)
|
|
123
|
|
124 def tab(self):
|
|
125 import os
|
|
126 from os.path import dirname, basename, expanduser, join
|
|
127
|
|
128 cwd = self.fm.thisdir.path
|
|
129 rel_dest = self.rest(1)
|
|
130
|
|
131 bookmarks = [v.path for v in self.fm.bookmarks.dct.values()
|
|
132 if rel_dest in v.path ]
|
|
133
|
|
134 # expand the tilde into the user directory
|
|
135 if rel_dest.startswith('~'):
|
|
136 rel_dest = expanduser(rel_dest)
|
|
137
|
|
138 # define some shortcuts
|
|
139 abs_dest = join(cwd, rel_dest)
|
|
140 abs_dirname = dirname(abs_dest)
|
|
141 rel_basename = basename(rel_dest)
|
|
142 rel_dirname = dirname(rel_dest)
|
|
143
|
|
144 try:
|
|
145 # are we at the end of a directory?
|
|
146 if rel_dest.endswith('/') or rel_dest == '':
|
|
147 _, dirnames, _ = next(os.walk(abs_dest))
|
|
148
|
|
149 # are we in the middle of the filename?
|
|
150 else:
|
|
151 _, dirnames, _ = next(os.walk(abs_dirname))
|
|
152 dirnames = [dn for dn in dirnames \
|
|
153 if dn.startswith(rel_basename)]
|
|
154 except (OSError, StopIteration):
|
|
155 # os.walk found nothing
|
|
156 pass
|
|
157 else:
|
|
158 dirnames.sort()
|
|
159 dirnames = bookmarks + dirnames
|
|
160
|
|
161 # no results, return None
|
|
162 if len(dirnames) == 0:
|
|
163 return
|
|
164
|
|
165 # one result. since it must be a directory, append a slash.
|
|
166 if len(dirnames) == 1:
|
|
167 return self.start(1) + join(rel_dirname, dirnames[0]) + '/'
|
|
168
|
|
169 # more than one result. append no slash, so the user can
|
|
170 # manually type in the slash to advance into that directory
|
|
171 return (self.start(1) + join(rel_dirname, dirname) for dirname in dirnames)
|
|
172
|
|
173
|
|
174 class chain(Command):
|
|
175 """:chain <command1>; <command2>; ...
|
|
176
|
|
177 Calls multiple commands at once, separated by semicolons.
|
|
178 """
|
|
179 def execute(self):
|
|
180 for command in self.rest(1).split(";"):
|
|
181 self.fm.execute_console(command)
|
|
182
|
|
183
|
|
184 class shell(Command):
|
|
185 escape_macros_for_shell = True
|
|
186
|
|
187 def execute(self):
|
|
188 if self.arg(1) and self.arg(1)[0] == '-':
|
|
189 flags = self.arg(1)[1:]
|
|
190 command = self.rest(2)
|
|
191 else:
|
|
192 flags = ''
|
|
193 command = self.rest(1)
|
|
194
|
|
195 if not command and 'p' in flags:
|
|
196 command = 'cat %f'
|
|
197 if command:
|
|
198 if '%' in command:
|
|
199 command = self.fm.substitute_macros(command, escape=True)
|
|
200 self.fm.execute_command(command, flags=flags)
|
|
201
|
|
202 def tab(self):
|
|
203 from ranger.ext.get_executables import get_executables
|
|
204 if self.arg(1) and self.arg(1)[0] == '-':
|
|
205 command = self.rest(2)
|
|
206 else:
|
|
207 command = self.rest(1)
|
|
208 start = self.line[0:len(self.line) - len(command)]
|
|
209
|
|
210 try:
|
|
211 position_of_last_space = command.rindex(" ")
|
|
212 except ValueError:
|
|
213 return (start + program + ' ' for program \
|
|
214 in get_executables() if program.startswith(command))
|
|
215 if position_of_last_space == len(command) - 1:
|
|
216 selection = self.fm.thistab.get_selection()
|
|
217 if len(selection) == 1:
|
|
218 return self.line + selection[0].shell_escaped_basename + ' '
|
|
219 else:
|
|
220 return self.line + '%s '
|
|
221 else:
|
|
222 before_word, start_of_word = self.line.rsplit(' ', 1)
|
|
223 return (before_word + ' ' + file.shell_escaped_basename \
|
|
224 for file in self.fm.thisdir.files \
|
|
225 if file.shell_escaped_basename.startswith(start_of_word))
|
|
226
|
|
227 class open_with(Command):
|
|
228 def execute(self):
|
|
229 app, flags, mode = self._get_app_flags_mode(self.rest(1))
|
|
230 self.fm.execute_file(
|
|
231 files = [f for f in self.fm.thistab.get_selection()],
|
|
232 app = app,
|
|
233 flags = flags,
|
|
234 mode = mode)
|
|
235
|
|
236 def tab(self):
|
|
237 return self._tab_through_executables()
|
|
238
|
|
239 def _get_app_flags_mode(self, string):
|
|
240 """Extracts the application, flags and mode from a string.
|
|
241
|
|
242 examples:
|
|
243 "mplayer f 1" => ("mplayer", "f", 1)
|
|
244 "aunpack 4" => ("aunpack", "", 4)
|
|
245 "p" => ("", "p", 0)
|
|
246 "" => None
|
|
247 """
|
|
248
|
|
249 app = ''
|
|
250 flags = ''
|
|
251 mode = 0
|
|
252 split = string.split()
|
|
253
|
|
254 if len(split) == 0:
|
|
255 pass
|
|
256
|
|
257 elif len(split) == 1:
|
|
258 part = split[0]
|
|
259 if self._is_app(part):
|
|
260 app = part
|
|
261 elif self._is_flags(part):
|
|
262 flags = part
|
|
263 elif self._is_mode(part):
|
|
264 mode = part
|
|
265
|
|
266 elif len(split) == 2:
|
|
267 part0 = split[0]
|
|
268 part1 = split[1]
|
|
269
|
|
270 if self._is_app(part0):
|
|
271 app = part0
|
|
272 if self._is_flags(part1):
|
|
273 flags = part1
|
|
274 elif self._is_mode(part1):
|
|
275 mode = part1
|
|
276 elif self._is_flags(part0):
|
|
277 flags = part0
|
|
278 if self._is_mode(part1):
|
|
279 mode = part1
|
|
280 elif self._is_mode(part0):
|
|
281 mode = part0
|
|
282 if self._is_flags(part1):
|
|
283 flags = part1
|
|
284
|
|
285 elif len(split) >= 3:
|
|
286 part0 = split[0]
|
|
287 part1 = split[1]
|
|
288 part2 = split[2]
|
|
289
|
|
290 if self._is_app(part0):
|
|
291 app = part0
|
|
292 if self._is_flags(part1):
|
|
293 flags = part1
|
|
294 if self._is_mode(part2):
|
|
295 mode = part2
|
|
296 elif self._is_mode(part1):
|
|
297 mode = part1
|
|
298 if self._is_flags(part2):
|
|
299 flags = part2
|
|
300 elif self._is_flags(part0):
|
|
301 flags = part0
|
|
302 if self._is_mode(part1):
|
|
303 mode = part1
|
|
304 elif self._is_mode(part0):
|
|
305 mode = part0
|
|
306 if self._is_flags(part1):
|
|
307 flags = part1
|
|
308
|
|
309 return app, flags, int(mode)
|
|
310
|
|
311 def _is_app(self, arg):
|
|
312 return not self._is_flags(arg) and not arg.isdigit()
|
|
313
|
|
314 def _is_flags(self, arg):
|
|
315 from ranger.core.runner import ALLOWED_FLAGS
|
|
316 return all(x in ALLOWED_FLAGS for x in arg)
|
|
317
|
|
318 def _is_mode(self, arg):
|
|
319 return all(x in '0123456789' for x in arg)
|
|
320
|
|
321
|
|
322 class set_(Command):
|
|
323 """:set <option name>=<python expression>
|
|
324
|
|
325 Gives an option a new value.
|
|
326 """
|
|
327 name = 'set' # don't override the builtin set class
|
|
328 def execute(self):
|
|
329 name = self.arg(1)
|
|
330 name, value, _ = self.parse_setting_line()
|
|
331 self.fm.set_option_from_string(name, value)
|
|
332
|
|
333 def tab(self):
|
|
334 name, value, name_done = self.parse_setting_line()
|
|
335 settings = self.fm.settings
|
|
336 if not name:
|
|
337 return sorted(self.firstpart + setting for setting in settings)
|
|
338 if not value and not name_done:
|
|
339 return (self.firstpart + setting for setting in settings \
|
|
340 if setting.startswith(name))
|
|
341 if not value:
|
|
342 return self.firstpart + str(settings[name])
|
|
343 if bool in settings.types_of(name):
|
|
344 if 'true'.startswith(value.lower()):
|
|
345 return self.firstpart + 'True'
|
|
346 if 'false'.startswith(value.lower()):
|
|
347 return self.firstpart + 'False'
|
|
348
|
|
349
|
|
350 class setlocal(set_):
|
|
351 """:setlocal path=<python string> <option name>=<python expression>
|
|
352
|
|
353 Gives an option a new value.
|
|
354 """
|
|
355 PATH_RE = re.compile(r'^\s*path="?(.*?)"?\s*$')
|
|
356 def execute(self):
|
|
357 import os.path
|
|
358 match = self.PATH_RE.match(self.arg(1))
|
|
359 if match:
|
|
360 path = os.path.normpath(os.path.expanduser(match.group(1)))
|
|
361 self.shift()
|
|
362 elif self.fm.thisdir:
|
|
363 path = self.fm.thisdir.path
|
|
364 else:
|
|
365 path = None
|
|
366
|
|
367 if path:
|
|
368 name = self.arg(1)
|
|
369 name, value, _ = self.parse_setting_line()
|
|
370 self.fm.set_option_from_string(name, value, localpath=path)
|
|
371
|
|
372
|
|
373 class setintag(setlocal):
|
|
374 """:setintag <tag or tags> <option name>=<option value>
|
|
375
|
|
376 Sets an option for directories that are tagged with a specific tag.
|
|
377 """
|
|
378 def execute(self):
|
|
379 tags = self.arg(1)
|
|
380 self.shift()
|
|
381 name, value, _ = self.parse_setting_line()
|
|
382 self.fm.set_option_from_string(name, value, tags=tags)
|
|
383
|
|
384
|
|
385 class quit(Command):
|
|
386 """:quit
|
|
387
|
|
388 Closes the current tab. If there is only one tab, quit the program.
|
|
389 """
|
|
390
|
|
391 def execute(self):
|
|
392 if len(self.fm.tabs) <= 1:
|
|
393 self.fm.exit()
|
|
394 self.fm.tab_close()
|
|
395
|
|
396
|
|
397 class quitall(Command):
|
|
398 """:quitall
|
|
399
|
|
400 Quits the program immediately.
|
|
401 """
|
|
402
|
|
403 def execute(self):
|
|
404 self.fm.exit()
|
|
405
|
|
406
|
|
407 class quit_bang(quitall):
|
|
408 """:quit!
|
|
409
|
|
410 Quits the program immediately.
|
|
411 """
|
|
412 name = 'quit!'
|
|
413 allow_abbrev = False
|
|
414
|
|
415
|
|
416 class terminal(Command):
|
|
417 """:terminal
|
|
418
|
|
419 Spawns an "x-terminal-emulator" starting in the current directory.
|
|
420 """
|
|
421 def execute(self):
|
|
422 import os
|
|
423 from ranger.ext.get_executables import get_executables
|
|
424 command = os.environ.get('TERMCMD', os.environ.get('TERM'))
|
|
425 if command not in get_executables():
|
|
426 command = 'x-terminal-emulator'
|
|
427 if command not in get_executables():
|
|
428 command = 'xterm'
|
|
429 self.fm.run(command, flags='f')
|
|
430
|
|
431
|
|
432 class delete(Command):
|
|
433 """:delete
|
|
434
|
|
435 Tries to delete the selection.
|
|
436
|
|
437 "Selection" is defined as all the "marked files" (by default, you
|
|
438 can mark files with space or v). If there are no marked files,
|
|
439 use the "current file" (where the cursor is)
|
|
440
|
|
441 When attempting to delete non-empty directories or multiple
|
|
442 marked files, it will require a confirmation.
|
|
443 """
|
|
444
|
|
445 allow_abbrev = False
|
|
446
|
|
447 def execute(self):
|
|
448 import os
|
|
449 if self.rest(1):
|
|
450 self.fm.notify("Error: delete takes no arguments! It deletes "
|
|
451 "the selected file(s).", bad=True)
|
|
452 return
|
|
453
|
|
454 cwd = self.fm.thisdir
|
|
455 cf = self.fm.thisfile
|
|
456 if not cwd or not cf:
|
|
457 self.fm.notify("Error: no file selected for deletion!", bad=True)
|
|
458 return
|
|
459
|
|
460 confirm = self.fm.settings.confirm_on_delete
|
|
461 many_files = (cwd.marked_items or (cf.is_directory and not cf.is_link \
|
|
462 and len(os.listdir(cf.path)) > 0))
|
|
463
|
|
464 if confirm != 'never' and (confirm != 'multiple' or many_files):
|
|
465 self.fm.ui.console.ask("Confirm deletion of: %s (y/N)" %
|
|
466 ', '.join(f.basename for f in self.fm.thistab.get_selection()),
|
|
467 self._question_callback, ('n', 'N', 'y', 'Y'))
|
|
468 else:
|
|
469 # no need for a confirmation, just delete
|
|
470 self.fm.delete()
|
|
471
|
|
472 def _question_callback(self, answer):
|
|
473 if answer == 'y' or answer == 'Y':
|
|
474 self.fm.delete()
|
|
475
|
|
476
|
|
477 class mark_tag(Command):
|
|
478 """:mark_tag [<tags>]
|
|
479
|
|
480 Mark all tags that are tagged with either of the given tags.
|
|
481 When leaving out the tag argument, all tagged files are marked.
|
|
482 """
|
|
483 do_mark = True
|
|
484
|
|
485 def execute(self):
|
|
486 cwd = self.fm.thisdir
|
|
487 tags = self.rest(1).replace(" ","")
|
|
488 if not self.fm.tags:
|
|
489 return
|
|
490 for fileobj in cwd.files:
|
|
491 try:
|
|
492 tag = self.fm.tags.tags[fileobj.realpath]
|
|
493 except KeyError:
|
|
494 continue
|
|
495 if not tags or tag in tags:
|
|
496 cwd.mark_item(fileobj, val=self.do_mark)
|
|
497 self.fm.ui.status.need_redraw = True
|
|
498 self.fm.ui.need_redraw = True
|
|
499
|
|
500
|
|
501 class console(Command):
|
|
502 """:console <command>
|
|
503
|
|
504 Open the console with the given command.
|
|
505 """
|
|
506 def execute(self):
|
|
507 position = None
|
|
508 if self.arg(1)[0:2] == '-p':
|
|
509 try:
|
|
510 position = int(self.arg(1)[2:])
|
|
511 self.shift()
|
|
512 except:
|
|
513 pass
|
|
514 self.fm.open_console(self.rest(1), position=position)
|
|
515
|
|
516
|
|
517 class load_copy_buffer(Command):
|
|
518 """:load_copy_buffer
|
|
519
|
|
520 Load the copy buffer from confdir/copy_buffer
|
|
521 """
|
|
522 copy_buffer_filename = 'copy_buffer'
|
|
523 def execute(self):
|
|
524 from ranger.container.file import File
|
|
525 from os.path import exists
|
|
526 try:
|
|
527 fname = self.fm.confpath(self.copy_buffer_filename)
|
|
528 f = open(fname, 'r')
|
|
529 except:
|
|
530 return self.fm.notify("Cannot open %s" % \
|
|
531 (fname or self.copy_buffer_filename), bad=True)
|
|
532 self.fm.copy_buffer = set(File(g) \
|
|
533 for g in f.read().split("\n") if exists(g))
|
|
534 f.close()
|
|
535 self.fm.ui.redraw_main_column()
|
|
536
|
|
537
|
|
538 class save_copy_buffer(Command):
|
|
539 """:save_copy_buffer
|
|
540
|
|
541 Save the copy buffer to confdir/copy_buffer
|
|
542 """
|
|
543 copy_buffer_filename = 'copy_buffer'
|
|
544 def execute(self):
|
|
545 fname = None
|
|
546 try:
|
|
547 fname = self.fm.confpath(self.copy_buffer_filename)
|
|
548 f = open(fname, 'w')
|
|
549 except:
|
|
550 return self.fm.notify("Cannot open %s" % \
|
|
551 (fname or self.copy_buffer_filename), bad=True)
|
|
552 f.write("\n".join(f.path for f in self.fm.copy_buffer))
|
|
553 f.close()
|
|
554
|
|
555
|
|
556 class unmark_tag(mark_tag):
|
|
557 """:unmark_tag [<tags>]
|
|
558
|
|
559 Unmark all tags that are tagged with either of the given tags.
|
|
560 When leaving out the tag argument, all tagged files are unmarked.
|
|
561 """
|
|
562 do_mark = False
|
|
563
|
|
564
|
|
565 class mkdir(Command):
|
|
566 """:mkdir <dirname>
|
|
567
|
|
568 Creates a directory with the name <dirname>.
|
|
569 """
|
|
570
|
|
571 def execute(self):
|
|
572 from os.path import join, expanduser, lexists
|
|
573 from os import mkdir
|
|
574
|
|
575 dirname = join(self.fm.thisdir.path, expanduser(self.rest(1)))
|
|
576 if not lexists(dirname):
|
|
577 mkdir(dirname)
|
|
578 else:
|
|
579 self.fm.notify("file/directory exists!", bad=True)
|
|
580
|
|
581 def tab(self):
|
|
582 return self._tab_directory_content()
|
|
583
|
|
584
|
|
585 class touch(Command):
|
|
586 """:touch <fname>
|
|
587
|
|
588 Creates a file with the name <fname>.
|
|
589 """
|
|
590
|
|
591 def execute(self):
|
|
592 from os.path import join, expanduser, lexists
|
|
593
|
|
594 fname = join(self.fm.thisdir.path, expanduser(self.rest(1)))
|
|
595 if not lexists(fname):
|
|
596 open(fname, 'a').close()
|
|
597 else:
|
|
598 self.fm.notify("file/directory exists!", bad=True)
|
|
599
|
|
600 def tab(self):
|
|
601 return self._tab_directory_content()
|
|
602
|
|
603
|
|
604 class edit(Command):
|
|
605 """:edit <filename>
|
|
606
|
|
607 Opens the specified file in vim
|
|
608 """
|
|
609
|
|
610 def execute(self):
|
|
611 if not self.arg(1):
|
|
612 self.fm.edit_file(self.fm.thisfile.path)
|
|
613 else:
|
|
614 self.fm.edit_file(self.rest(1))
|
|
615
|
|
616 def tab(self):
|
|
617 return self._tab_directory_content()
|
|
618
|
|
619
|
|
620 class eval_(Command):
|
|
621 """:eval [-q] <python code>
|
|
622
|
|
623 Evaluates the python code.
|
|
624 `fm' is a reference to the FM instance.
|
|
625 To display text, use the function `p'.
|
|
626
|
|
627 Examples:
|
|
628 :eval fm
|
|
629 :eval len(fm.directories)
|
|
630 :eval p("Hello World!")
|
|
631 """
|
|
632 name = 'eval'
|
|
633 resolve_macros = False
|
|
634
|
|
635 def execute(self):
|
|
636 if self.arg(1) == '-q':
|
|
637 code = self.rest(2)
|
|
638 quiet = True
|
|
639 else:
|
|
640 code = self.rest(1)
|
|
641 quiet = False
|
|
642 import ranger
|
|
643 global cmd, fm, p, quantifier
|
|
644 fm = self.fm
|
|
645 cmd = self.fm.execute_console
|
|
646 p = fm.notify
|
|
647 quantifier = self.quantifier
|
|
648 try:
|
|
649 try:
|
|
650 result = eval(code)
|
|
651 except SyntaxError:
|
|
652 exec(code)
|
|
653 else:
|
|
654 if result and not quiet:
|
|
655 p(result)
|
|
656 except Exception as err:
|
|
657 p(err)
|
|
658
|
|
659
|
|
660 class rename(Command):
|
|
661 """:rename <newname>
|
|
662
|
|
663 Changes the name of the currently highlighted file to <newname>
|
|
664 """
|
|
665
|
|
666 def execute(self):
|
|
667 from ranger.container.file import File
|
|
668 from os import access
|
|
669
|
|
670 new_name = self.rest(1)
|
|
671
|
|
672 if not new_name:
|
|
673 return self.fm.notify('Syntax: rename <newname>', bad=True)
|
|
674
|
|
675 if new_name == self.fm.thisfile.basename:
|
|
676 return
|
|
677
|
|
678 if access(new_name, os.F_OK):
|
|
679 return self.fm.notify("Can't rename: file already exists!", bad=True)
|
|
680
|
|
681 self.fm.rename(self.fm.thisfile, new_name)
|
|
682 f = File(new_name)
|
|
683 self.fm.thisdir.pointed_obj = f
|
|
684 self.fm.thisfile = f
|
|
685
|
|
686 def tab(self):
|
|
687 return self._tab_directory_content()
|
|
688
|
|
689
|
|
690 class chmod(Command):
|
|
691 """:chmod <octal number>
|
|
692
|
|
693 Sets the permissions of the selection to the octal number.
|
|
694
|
|
695 The octal number is between 0 and 777. The digits specify the
|
|
696 permissions for the user, the group and others.
|
|
697
|
|
698 A 1 permits execution, a 2 permits writing, a 4 permits reading.
|
|
699 Add those numbers to combine them. So a 7 permits everything.
|
|
700 """
|
|
701
|
|
702 def execute(self):
|
|
703 mode = self.rest(1)
|
|
704 if not mode:
|
|
705 mode = str(self.quantifier)
|
|
706
|
|
707 try:
|
|
708 mode = int(mode, 8)
|
|
709 if mode < 0 or mode > 0o777:
|
|
710 raise ValueError
|
|
711 except ValueError:
|
|
712 self.fm.notify("Need an octal number between 0 and 777!", bad=True)
|
|
713 return
|
|
714
|
|
715 for file in self.fm.thistab.get_selection():
|
|
716 try:
|
|
717 os.chmod(file.path, mode)
|
|
718 except Exception as ex:
|
|
719 self.fm.notify(ex)
|
|
720
|
|
721 try:
|
|
722 # reloading directory. maybe its better to reload the selected
|
|
723 # files only.
|
|
724 self.fm.thisdir.load_content()
|
|
725 except:
|
|
726 pass
|
|
727
|
|
728
|
|
729 class bulkrename(Command):
|
|
730 """:bulkrename
|
|
731
|
|
732 This command opens a list of selected files in an external editor.
|
|
733 After you edit and save the file, it will generate a shell script
|
|
734 which does bulk renaming according to the changes you did in the file.
|
|
735
|
|
736 This shell script is opened in an editor for you to review.
|
|
737 After you close it, it will be executed.
|
|
738 """
|
|
739 def execute(self):
|
|
740 import sys
|
|
741 import tempfile
|
|
742 from ranger.container.file import File
|
|
743 from ranger.ext.shell_escape import shell_escape as esc
|
|
744 py3 = sys.version > "3"
|
|
745
|
|
746 # Create and edit the file list
|
|
747 filenames = [f.basename for f in self.fm.thistab.get_selection()]
|
|
748 listfile = tempfile.NamedTemporaryFile()
|
|
749
|
|
750 if py3:
|
|
751 listfile.write("\n".join(filenames).encode("utf-8"))
|
|
752 else:
|
|
753 listfile.write("\n".join(filenames))
|
|
754 listfile.flush()
|
|
755 self.fm.execute_file([File(listfile.name)], app='editor')
|
|
756 listfile.seek(0)
|
|
757 if py3:
|
|
758 new_filenames = listfile.read().decode("utf-8").split("\n")
|
|
759 else:
|
|
760 new_filenames = listfile.read().split("\n")
|
|
761 listfile.close()
|
|
762 if all(a == b for a, b in zip(filenames, new_filenames)):
|
|
763 self.fm.notify("No renaming to be done!")
|
|
764 return
|
|
765
|
|
766 # Generate and execute script
|
|
767 cmdfile = tempfile.NamedTemporaryFile()
|
|
768 cmdfile.write(b"# This file will be executed when you close the editor.\n")
|
|
769 cmdfile.write(b"# Please double-check everything, clear the file to abort.\n")
|
|
770 if py3:
|
|
771 cmdfile.write("\n".join("mv -vi -- " + esc(old) + " " + esc(new) \
|
|
772 for old, new in zip(filenames, new_filenames) \
|
|
773 if old != new).encode("utf-8"))
|
|
774 else:
|
|
775 cmdfile.write("\n".join("mv -vi -- " + esc(old) + " " + esc(new) \
|
|
776 for old, new in zip(filenames, new_filenames) if old != new))
|
|
777 cmdfile.flush()
|
|
778 self.fm.execute_file([File(cmdfile.name)], app='editor')
|
|
779 self.fm.run(['/bin/sh', cmdfile.name], flags='w')
|
|
780 cmdfile.close()
|
|
781
|
|
782
|
|
783 class relink(Command):
|
|
784 """:relink <newpath>
|
|
785
|
|
786 Changes the linked path of the currently highlighted symlink to <newpath>
|
|
787 """
|
|
788
|
|
789 def execute(self):
|
|
790 from ranger.container.file import File
|
|
791
|
|
792 new_path = self.rest(1)
|
|
793 cf = self.fm.thisfile
|
|
794
|
|
795 if not new_path:
|
|
796 return self.fm.notify('Syntax: relink <newpath>', bad=True)
|
|
797
|
|
798 if not cf.is_link:
|
|
799 return self.fm.notify('%s is not a symlink!' % cf.basename, bad=True)
|
|
800
|
|
801 if new_path == os.readlink(cf.path):
|
|
802 return
|
|
803
|
|
804 try:
|
|
805 os.remove(cf.path)
|
|
806 os.symlink(new_path, cf.path)
|
|
807 except OSError as err:
|
|
808 self.fm.notify(err)
|
|
809
|
|
810 self.fm.reset()
|
|
811 self.fm.thisdir.pointed_obj = cf
|
|
812 self.fm.thisfile = cf
|
|
813
|
|
814 def tab(self):
|
|
815 if not self.rest(1):
|
|
816 return self.line+os.readlink(self.fm.thisfile.path)
|
|
817 else:
|
|
818 return self._tab_directory_content()
|
|
819
|
|
820
|
|
821 class help_(Command):
|
|
822 """:help
|
|
823
|
|
824 Display ranger's manual page.
|
|
825 """
|
|
826 name = 'help'
|
|
827 def execute(self):
|
|
828 if self.quantifier == 1:
|
|
829 self.fm.dump_keybindings()
|
|
830 elif self.quantifier == 2:
|
|
831 self.fm.dump_commands()
|
|
832 elif self.quantifier == 3:
|
|
833 self.fm.dump_settings()
|
|
834 else:
|
|
835 self.fm.display_help()
|
|
836
|
|
837
|
|
838 class copymap(Command):
|
|
839 """:copymap <keys> <newkeys1> [<newkeys2>...]
|
|
840
|
|
841 Copies a "browser" keybinding from <keys> to <newkeys>
|
|
842 """
|
|
843 context = 'browser'
|
|
844
|
|
845 def execute(self):
|
|
846 if not self.arg(1) or not self.arg(2):
|
|
847 return self.fm.notify("Not enough arguments", bad=True)
|
|
848
|
|
849 for arg in self.args[2:]:
|
|
850 self.fm.ui.keymaps.copy(self.context, self.arg(1), arg)
|
|
851
|
|
852
|
|
853 class copypmap(copymap):
|
|
854 """:copypmap <keys> <newkeys1> [<newkeys2>...]
|
|
855
|
|
856 Copies a "pager" keybinding from <keys> to <newkeys>
|
|
857 """
|
|
858 context = 'pager'
|
|
859
|
|
860
|
|
861 class copycmap(copymap):
|
|
862 """:copycmap <keys> <newkeys1> [<newkeys2>...]
|
|
863
|
|
864 Copies a "console" keybinding from <keys> to <newkeys>
|
|
865 """
|
|
866 context = 'console'
|
|
867
|
|
868
|
|
869 class copytmap(copymap):
|
|
870 """:copycmap <keys> <newkeys1> [<newkeys2>...]
|
|
871
|
|
872 Copies a "taskview" keybinding from <keys> to <newkeys>
|
|
873 """
|
|
874 context = 'taskview'
|
|
875
|
|
876
|
|
877 class unmap(Command):
|
|
878 """:unmap <keys> [<keys2>, ...]
|
|
879
|
|
880 Remove the given "browser" mappings
|
|
881 """
|
|
882 context = 'browser'
|
|
883
|
|
884 def execute(self):
|
|
885 for arg in self.args[1:]:
|
|
886 self.fm.ui.keymaps.unbind(self.context, arg)
|
|
887
|
|
888
|
|
889 class cunmap(unmap):
|
|
890 """:cunmap <keys> [<keys2>, ...]
|
|
891
|
|
892 Remove the given "console" mappings
|
|
893 """
|
|
894 context = 'browser'
|
|
895
|
|
896
|
|
897 class punmap(unmap):
|
|
898 """:punmap <keys> [<keys2>, ...]
|
|
899
|
|
900 Remove the given "pager" mappings
|
|
901 """
|
|
902 context = 'pager'
|
|
903
|
|
904
|
|
905 class tunmap(unmap):
|
|
906 """:tunmap <keys> [<keys2>, ...]
|
|
907
|
|
908 Remove the given "taskview" mappings
|
|
909 """
|
|
910 context = 'taskview'
|
|
911
|
|
912
|
|
913 class map_(Command):
|
|
914 """:map <keysequence> <command>
|
|
915
|
|
916 Maps a command to a keysequence in the "browser" context.
|
|
917
|
|
918 Example:
|
|
919 map j move down
|
|
920 map J move down 10
|
|
921 """
|
|
922 name = 'map'
|
|
923 context = 'browser'
|
|
924 resolve_macros = False
|
|
925
|
|
926 def execute(self):
|
|
927 self.fm.ui.keymaps.bind(self.context, self.arg(1), self.rest(2))
|
|
928
|
|
929
|
|
930 class cmap(map_):
|
|
931 """:cmap <keysequence> <command>
|
|
932
|
|
933 Maps a command to a keysequence in the "console" context.
|
|
934
|
|
935 Example:
|
|
936 cmap <ESC> console_close
|
|
937 cmap <C-x> console_type test
|
|
938 """
|
|
939 context = 'console'
|
|
940
|
|
941
|
|
942 class tmap(map_):
|
|
943 """:tmap <keysequence> <command>
|
|
944
|
|
945 Maps a command to a keysequence in the "taskview" context.
|
|
946 """
|
|
947 context = 'taskview'
|
|
948
|
|
949
|
|
950 class pmap(map_):
|
|
951 """:pmap <keysequence> <command>
|
|
952
|
|
953 Maps a command to a keysequence in the "pager" context.
|
|
954 """
|
|
955 context = 'pager'
|
|
956
|
|
957
|
|
958 class scout(Command):
|
|
959 """:scout [-FLAGS] <pattern>
|
|
960
|
|
961 Swiss army knife command for searching, traveling and filtering files.
|
|
962 The command takes various flags as arguments which can be used to
|
|
963 influence its behaviour:
|
|
964
|
|
965 -a = automatically open a file on unambiguous match
|
|
966 -e = open the selected file when pressing enter
|
|
967 -f = filter files that match the current search pattern
|
|
968 -g = interpret pattern as a glob pattern
|
|
969 -i = ignore the letter case of the files
|
|
970 -k = keep the console open when changing a directory with the command
|
|
971 -l = letter skipping; e.g. allow "rdme" to match the file "readme"
|
|
972 -m = mark the matching files after pressing enter
|
|
973 -M = unmark the matching files after pressing enter
|
|
974 -p = permanent filter: hide non-matching files after pressing enter
|
|
975 -s = smart case; like -i unless pattern contains upper case letters
|
|
976 -t = apply filter and search pattern as you type
|
|
977 -v = inverts the match
|
|
978
|
|
979 Multiple flags can be combined. For example, ":scout -gpt" would create
|
|
980 a :filter-like command using globbing.
|
|
981 """
|
|
982 AUTO_OPEN = 'a'
|
|
983 OPEN_ON_ENTER = 'e'
|
|
984 FILTER = 'f'
|
|
985 SM_GLOB = 'g'
|
|
986 IGNORE_CASE = 'i'
|
|
987 KEEP_OPEN = 'k'
|
|
988 SM_LETTERSKIP = 'l'
|
|
989 MARK = 'm'
|
|
990 UNMARK = 'M'
|
|
991 PERM_FILTER = 'p'
|
|
992 SM_REGEX = 'r'
|
|
993 SMART_CASE = 's'
|
|
994 AS_YOU_TYPE = 't'
|
|
995 INVERT = 'v'
|
|
996
|
|
997 def __init__(self, *args, **kws):
|
|
998 Command.__init__(self, *args, **kws)
|
|
999 self._regex = None
|
|
1000 self.flags, self.pattern = self.parse_flags()
|
|
1001
|
|
1002 def execute(self):
|
|
1003 thisdir = self.fm.thisdir
|
|
1004 flags = self.flags
|
|
1005 pattern = self.pattern
|
|
1006 regex = self._build_regex()
|
|
1007 count = self._count(move=True)
|
|
1008
|
|
1009 self.fm.thistab.last_search = regex
|
|
1010 self.fm.set_search_method(order="search")
|
|
1011
|
|
1012 if self.MARK in flags or self.UNMARK in flags:
|
|
1013 value = flags.find(self.MARK) > flags.find(self.UNMARK)
|
|
1014 if self.FILTER in flags:
|
|
1015 for f in thisdir.files:
|
|
1016 thisdir.mark_item(f, value)
|
|
1017 else:
|
|
1018 for f in thisdir.files:
|
|
1019 if regex.search(f.basename):
|
|
1020 thisdir.mark_item(f, value)
|
|
1021
|
|
1022 if self.PERM_FILTER in flags:
|
|
1023 thisdir.filter = regex if pattern else None
|
|
1024
|
|
1025 # clean up:
|
|
1026 self.cancel()
|
|
1027
|
|
1028 if self.OPEN_ON_ENTER in flags or \
|
|
1029 self.AUTO_OPEN in flags and count == 1:
|
|
1030 if os.path.exists(pattern):
|
|
1031 self.fm.cd(pattern)
|
|
1032 else:
|
|
1033 self.fm.move(right=1)
|
|
1034
|
|
1035 if self.KEEP_OPEN in flags and thisdir != self.fm.thisdir:
|
|
1036 # reopen the console:
|
|
1037 self.fm.open_console(self.line[0:-len(pattern)])
|
|
1038
|
|
1039 if thisdir != self.fm.thisdir and pattern != "..":
|
|
1040 self.fm.block_input(0.5)
|
|
1041
|
|
1042 def cancel(self):
|
|
1043 self.fm.thisdir.temporary_filter = None
|
|
1044 self.fm.thisdir.refilter()
|
|
1045
|
|
1046 def quick(self):
|
|
1047 asyoutype = self.AS_YOU_TYPE in self.flags
|
|
1048 if self.FILTER in self.flags:
|
|
1049 self.fm.thisdir.temporary_filter = self._build_regex()
|
|
1050 if self.PERM_FILTER in self.flags and asyoutype:
|
|
1051 self.fm.thisdir.filter = self._build_regex()
|
|
1052 if self.FILTER in self.flags or self.PERM_FILTER in self.flags:
|
|
1053 self.fm.thisdir.refilter()
|
|
1054 if self._count(move=asyoutype) == 1 and self.AUTO_OPEN in self.flags:
|
|
1055 return True
|
|
1056 return False
|
|
1057
|
|
1058 def tab(self):
|
|
1059 self._count(move=True, offset=1)
|
|
1060
|
|
1061 def _build_regex(self):
|
|
1062 if self._regex is not None:
|
|
1063 return self._regex
|
|
1064
|
|
1065 frmat = "%s"
|
|
1066 flags = self.flags
|
|
1067 pattern = self.pattern
|
|
1068
|
|
1069 if pattern == ".":
|
|
1070 return re.compile("")
|
|
1071
|
|
1072 # Handle carets at start and dollar signs at end separately
|
|
1073 if pattern.startswith('^'):
|
|
1074 pattern = pattern[1:]
|
|
1075 frmat = "^" + frmat
|
|
1076 if pattern.endswith('$'):
|
|
1077 pattern = pattern[:-1]
|
|
1078 frmat += "$"
|
|
1079
|
|
1080 # Apply one of the search methods
|
|
1081 if self.SM_REGEX in flags:
|
|
1082 regex = pattern
|
|
1083 elif self.SM_GLOB in flags:
|
|
1084 regex = re.escape(pattern).replace("\\*", ".*").replace("\\?", ".")
|
|
1085 elif self.SM_LETTERSKIP in flags:
|
|
1086 regex = ".*".join(re.escape(c) for c in pattern)
|
|
1087 else:
|
|
1088 regex = re.escape(pattern)
|
|
1089
|
|
1090 regex = frmat % regex
|
|
1091
|
|
1092 # Invert regular expression if necessary
|
|
1093 if self.INVERT in flags:
|
|
1094 regex = "^(?:(?!%s).)*$" % regex
|
|
1095
|
|
1096 # Compile Regular Expression
|
|
1097 options = re.LOCALE | re.UNICODE
|
|
1098 if self.IGNORE_CASE in flags or self.SMART_CASE in flags and \
|
|
1099 pattern.islower():
|
|
1100 options |= re.IGNORECASE
|
|
1101 try:
|
|
1102 self._regex = re.compile(regex, options)
|
|
1103 except:
|
|
1104 self._regex = re.compile("")
|
|
1105 return self._regex
|
|
1106
|
|
1107 def _count(self, move=False, offset=0):
|
|
1108 count = 0
|
|
1109 cwd = self.fm.thisdir
|
|
1110 pattern = self.pattern
|
|
1111
|
|
1112 if not pattern:
|
|
1113 return 0
|
|
1114 if pattern == '.':
|
|
1115 return 0
|
|
1116 if pattern == '..':
|
|
1117 return 1
|
|
1118
|
|
1119 deq = deque(cwd.files)
|
|
1120 deq.rotate(-cwd.pointer - offset)
|
|
1121 i = offset
|
|
1122 regex = self._build_regex()
|
|
1123 for fsobj in deq:
|
|
1124 if regex.search(fsobj.basename):
|
|
1125 count += 1
|
|
1126 if move and count == 1:
|
|
1127 cwd.move(to=(cwd.pointer + i) % len(cwd.files))
|
|
1128 self.fm.thisfile = cwd.pointed_obj
|
|
1129 if count > 1:
|
|
1130 return count
|
|
1131 i += 1
|
|
1132
|
|
1133 return count == 1
|
|
1134
|
|
1135
|
|
1136 class grep(Command):
|
|
1137 """:grep <string>
|
|
1138
|
|
1139 Looks for a string in all marked files or directories
|
|
1140 """
|
|
1141
|
|
1142 def execute(self):
|
|
1143 if self.rest(1):
|
|
1144 action = ['grep', '--line-number']
|
|
1145 action.extend(['-e', self.rest(1), '-r'])
|
|
1146 action.extend(f.path for f in self.fm.thistab.get_selection())
|
|
1147 self.fm.execute_command(action, flags='p')
|
|
1148
|
|
1149
|
|
1150 # Version control commands
|
|
1151 # --------------------------------
|
|
1152 class stage(Command):
|
|
1153 """
|
|
1154 :stage
|
|
1155
|
|
1156 Stage selected files for the corresponding version control system
|
|
1157 """
|
|
1158 def execute(self):
|
|
1159 from ranger.ext.vcs import VcsError
|
|
1160
|
|
1161 filelist = [f.path for f in self.fm.thistab.get_selection()]
|
|
1162 self.fm.thisdir.vcs_outdated = True
|
|
1163 # for f in self.fm.thistab.get_selection():
|
|
1164 # f.vcs_outdated = True
|
|
1165
|
|
1166 try:
|
|
1167 self.fm.thisdir.vcs.add(filelist)
|
|
1168 except VcsError:
|
|
1169 self.fm.notify("Could not stage files.")
|
|
1170
|
|
1171 self.fm.reload_cwd()
|
|
1172
|
|
1173
|
|
1174 class unstage(Command):
|
|
1175 """
|
|
1176 :unstage
|
|
1177
|
|
1178 Unstage selected files for the corresponding version control system
|
|
1179 """
|
|
1180 def execute(self):
|
|
1181 from ranger.ext.vcs import VcsError
|
|
1182
|
|
1183 filelist = [f.path for f in self.fm.thistab.get_selection()]
|
|
1184 self.fm.thisdir.vcs_outdated = True
|
|
1185 # for f in self.fm.thistab.get_selection():
|
|
1186 # f.vcs_outdated = True
|
|
1187
|
|
1188 try:
|
|
1189 self.fm.thisdir.vcs.reset(filelist)
|
|
1190 except VcsError:
|
|
1191 self.fm.notify("Could not unstage files.")
|
|
1192
|
|
1193 self.fm.reload_cwd()
|
|
1194
|
|
1195
|
|
1196 class diff(Command):
|
|
1197 """
|
|
1198 :diff
|
|
1199
|
|
1200 Displays a diff of selected files against last last commited version
|
|
1201 """
|
|
1202 def execute(self):
|
|
1203 from ranger.ext.vcs import VcsError
|
|
1204 import tempfile
|
|
1205
|
|
1206 L = self.fm.thistab.get_selection()
|
|
1207 if len(L) == 0: return
|
|
1208
|
|
1209 filelist = [f.path for f in L]
|
|
1210 vcs = L[0].vcs
|
|
1211
|
|
1212 diff = vcs.get_raw_diff(filelist=filelist)
|
|
1213 if len(diff.strip()) > 0:
|
|
1214 tmp = tempfile.NamedTemporaryFile()
|
|
1215 tmp.write(diff.encode('utf-8'))
|
|
1216 tmp.flush()
|
|
1217
|
|
1218 pager = os.environ.get('PAGER', ranger.DEFAULT_PAGER)
|
|
1219 self.fm.run([pager, tmp.name])
|
|
1220 else:
|
|
1221 raise Exception("diff is empty")
|
|
1222
|
|
1223
|
|
1224 class log(Command):
|
|
1225 """
|
|
1226 :log
|
|
1227
|
|
1228 Displays the log of the current repo or files
|
|
1229 """
|
|
1230 def execute(self):
|
|
1231 from ranger.ext.vcs import VcsError
|
|
1232 import tempfile
|
|
1233
|
|
1234 L = self.fm.thistab.get_selection()
|
|
1235 if len(L) == 0: return
|
|
1236
|
|
1237 filelist = [f.path for f in L]
|
|
1238 vcs = L[0].vcs
|
|
1239
|
|
1240 log = vcs.get_raw_log(filelist=filelist)
|
|
1241 tmp = tempfile.NamedTemporaryFile()
|
|
1242 tmp.write(log.encode('utf-8'))
|
|
1243 tmp.flush()
|
|
1244
|
|
1245 pager = os.environ.get('PAGER', ranger.DEFAULT_PAGER)
|
|
1246 self.fm.run([pager, tmp.name])
|
|
1247
|
|
1248 class extracthere(Command):
|
|
1249 def execute(self):
|
|
1250 """ Extract copied files to current directory """
|
|
1251 copied_files = tuple(self.fm.env.copy)
|
|
1252
|
|
1253 if not copied_files:
|
|
1254 return
|
|
1255
|
|
1256 def refresh(_):
|
|
1257 cwd = self.fm.env.get_directory(original_path)
|
|
1258 cwd.load_content()
|
|
1259
|
|
1260 one_file = copied_files[0]
|
|
1261 cwd = self.fm.env.cwd
|
|
1262 original_path = cwd.path
|
|
1263 au_flags = ['-X', cwd.path]
|
|
1264 au_flags += self.line.split()[1:]
|
|
1265 au_flags += ['-e']
|
|
1266
|
|
1267 self.fm.env.copy.clear()
|
|
1268 self.fm.env.cut = False
|
|
1269 if len(copied_files) == 1:
|
|
1270 descr = "extracting: " + os.path.basename(one_file.path)
|
|
1271 else:
|
|
1272 descr = "extracting files from: " + os.path.basename(one_file.dirname)
|
|
1273 obj = CommandLoader(args=['aunpack'] + au_flags \
|
|
1274 + [f.path for f in copied_files], descr=descr)
|
|
1275
|
|
1276 obj.signal_bind('after', refresh)
|
|
1277 self.fm.loader.add(obj)
|
|
1278
|
|
1279 class compress(Command):
|
|
1280 def execute(self):
|
|
1281 """ Compress marked files to current directory """
|
|
1282 cwd = self.fm.env.cwd
|
|
1283 marked_files = cwd.get_selection()
|
|
1284
|
|
1285 if not marked_files:
|
|
1286 return
|
|
1287
|
|
1288 def refresh(_):
|
|
1289 cwd = self.fm.env.get_directory(original_path)
|
|
1290 cwd.load_content()
|
|
1291
|
|
1292 original_path = cwd.path
|
|
1293 parts = self.line.split()
|
|
1294 au_flags = parts[1:]
|
|
1295
|
|
1296 descr = "compressing files in: " + os.path.basename(parts[1])
|
|
1297 obj = CommandLoader(args=['apack'] + au_flags + \
|
|
1298 [os.path.relpath(f.path, cwd.path) for f in marked_files], descr=descr)
|
|
1299
|
|
1300 obj.signal_bind('after', refresh)
|
|
1301 self.fm.loader.add(obj)
|
|
1302
|
|
1303 def tab(self):
|
|
1304 """ Complete with current folder name """
|
|
1305
|
|
1306 extension = ['.zip', '.tar.gz', '.rar', '.7z']
|
|
1307 return ['compress ' + os.path.basename(self.fm.env.cwd.path) + ext for ext in extension]
|