Table of Contents
Open Table of Contents
Background
ranger is a terminal-based file manager that is written in Python. This is interesting for a couple of reasons. One is that it nicely integrates with the rest of the terminal. Another is that it can be extended fairly easily. It also supports vim-like key bindings. That last one may not seem like a merit but the interface ends up being really intuitive.
As part of my ongoing effort to be more organized I decided it’s about time I sort my wallpaper
collection. At first glance there’s a couple of ways to do this. But, by far the simplest is using
sub-directories for each distinct category. The problem is, even using ranger with its simple and
intuitive interface ends up still being a lot of busy work. We all hate busy work but as programmers
we can potentially do something about it (relevant xkcd).
Organizing Images Into Sub-directories
The best way I could think of to save myself some time is to somehow bind a key (like Space) to an arbitrary sequence of commands. Performing an action which in this case would be moving whichever file is selected to some sub-directory would just involve hitting a key. All that was left was actually implementing it.
Ranger is extended simply by adding a subclass of the Command class to a file called commands.py
in the ranger config directory. A sample commands.py can be obtained by running
ranger --copy-config=commands
The two methods of the Command class you should be concerned with are execute and tab. The
former should contain the code you want to run when your command is executed. The latter can be used
to provide completion options when a user hits the tab key. For our purposes implementing
tab-completion isn’t really necessary.
From what we’ve discussed so far, our new command needs to:
- Parse the sequence of commands entered by the user,
- Interpret special placeholders representing either the currently selected file or the current working directory,
- Map the Space key to the user specified command sequence,
- and, optionally restore the Space key to whatever it was. This isn’t necessary since the remapping
lasts only until
rangeris closed.
The Code
class bspace(Command):
""":bspace [command]
Temporarily bind a command to the space bar. {file} is
replaced with the currently selected item and {cwd} with
the current working directory. Run without arguments to
restore the original binding.
"""
def execute(self):
# default binding for <Space>
orig_binding = "mark_files toggle=True"
if len(self.args) == 1:
new_binding = orig_binding
else:
resolved_args = []
for arg in self.args[1:]:
# replace {file} with current file in command
if arg == '{file}':
resolved_args.append('%p')
# replace {cwd} with current working directory
elif arg == '{cwd}':
resolved_args.append('%d')
else:
resolved_args.append(arg)
new_binding = ' '.join(resolved_args)
self.fm.execute_console(f"map <Space> {new_binding}")
To use the command simply enter a command that you want to temporarily bind to the Space key after
:bspace in the ranger console. A couple of useful examples are:
:bspace delete {file}- deleting a file (please be careful).:bspace chain copy;cd ../paste;cd {cwd}- copy a file to the parent directory.:bspace shell cat {file} >> somefile.txt- appending to a particular file.:bspace chain cut;cd landscapes;paste;cd {cwd}- the command we need to organize images into sub-directories. Since ranger supports image previews, all we need to do is go through the directory and hit Space on every landscape image and it is moved to the relevant directory.landscapesin the command can be changed when we want to put an image in a different directory.
Here’s a gif of our command in action
Further Reading
- The ranger wiki has some great examples of custom commands.
- The Arch Wiki ahas a great page about ranger.
- The Official User Guide.
- The complete code can be found in a gist.
- I recently found TMSU which is a way to tag files and browse them through a virtual file-system (I haven’t had time to try it though so can’t say if it’s any good)