aboutsummaryrefslogtreecommitdiffstats
path: root/.config/ranger
diff options
context:
space:
mode:
Diffstat (limited to '.config/ranger')
-rwxr-xr-x[-rw-r--r--].config/ranger/commands.py580
-rw-r--r--.config/ranger/rc.conf209
-rwxr-xr-x.config/ranger/scope.sh52
3 files changed, 589 insertions, 252 deletions
diff --git a/.config/ranger/commands.py b/.config/ranger/commands.py
index 7ffd7f5..a384f42 100644..100755
--- a/.config/ranger/commands.py
+++ b/.config/ranger/commands.py
@@ -1,7 +1,12 @@
# -*- coding: utf-8 -*-
-# Copyright (C) 2009-2013 Roman Zimbelmann <hut@lavabit.com>
+# This file is part of ranger, the console file manager.
# This configuration file is licensed under the same terms as ranger.
# ===================================================================
+#
+# NOTE: If you copied this file to ~/.config/ranger/commands_full.py,
+# then it will NOT be loaded by ranger, and only serve as a reference.
+#
+# ===================================================================
# This file contains ranger's commands.
# It's all in python; lines beginning with # are comments.
#
@@ -16,10 +21,12 @@
# ===================================================================
# Every class defined here which is a subclass of `Command' will be used as a
# command in ranger. Several methods are defined to interface with ranger:
-# execute(): called when the command is executed.
-# cancel(): called when closing the console.
-# tab(): called when <TAB> is pressed.
-# quick(): called after each keypress.
+# execute(): called when the command is executed.
+# cancel(): called when closing the console.
+# tab(tabnum): called when <TAB> is pressed.
+# quick(): called after each keypress.
+#
+# tab() argument tabnum is 1 for <TAB> and -1 for <S-TAB> by default
#
# The return values for tab() can be either:
# None: There is no tab completion
@@ -42,9 +49,9 @@
# the user pressed 6X, self.quantifier will be 6.
# self.arg(n): The n-th argument, or an empty string if it doesn't exist.
# self.rest(n): The n-th argument plus everything that followed. For example,
-# If the command was "search foo bar a b c", rest(2) will be "bar a b c"
-# self.start(n): The n-th argument and anything before it. For example,
-# If the command was "search foo bar a b c", rest(2) will be "bar a b c"
+# if the command was "search foo bar a b c", rest(2) will be "bar a b c"
+# self.start(n): Anything before the n-th argument. For example, if the
+# command was "search foo bar a b c", start(2) will be "search foo"
#
# ===================================================================
# And this is a little reference for common ranger functions and objects:
@@ -78,6 +85,7 @@
from ranger.api.commands import *
+
class alias(Command):
""":alias <newcommand> <oldcommand>
@@ -93,6 +101,16 @@ class alias(Command):
else:
self.fm.commands.alias(self.arg(1), self.rest(2))
+
+class echo(Command):
+ """:echo <text>
+
+ Display the text in the statusbar.
+ """
+ def execute(self):
+ self.fm.notify(self.rest(1))
+
+
class cd(Command):
""":cd [-r] <dirname>
@@ -107,7 +125,8 @@ class cd(Command):
self.shift()
destination = os.path.realpath(self.rest(1))
if os.path.isfile(destination):
- destination = os.path.dirname(destination)
+ self.fm.select_file(destination)
+ return
else:
destination = self.rest(1)
@@ -119,7 +138,7 @@ class cd(Command):
else:
self.fm.cd(destination)
- def tab(self):
+ def tab(self, tabnum):
import os
from os.path import dirname, basename, expanduser, join
@@ -127,7 +146,7 @@ class cd(Command):
rel_dest = self.rest(1)
bookmarks = [v.path for v in self.fm.bookmarks.dct.values()
- if rel_dest in v.path ]
+ if rel_dest in v.path]
# expand the tilde into the user directory
if rel_dest.startswith('~'):
@@ -147,14 +166,15 @@ class cd(Command):
# are we in the middle of the filename?
else:
_, dirnames, _ = next(os.walk(abs_dirname))
- dirnames = [dn for dn in dirnames \
+ dirnames = [dn for dn in dirnames
if dn.startswith(rel_basename)]
except (OSError, StopIteration):
# os.walk found nothing
pass
else:
dirnames.sort()
- dirnames = bookmarks + dirnames
+ if self.fm.settings.cd_bookmarks:
+ dirnames = bookmarks + dirnames
# no results, return None
if len(dirnames) == 0:
@@ -175,7 +195,7 @@ class chain(Command):
Calls multiple commands at once, separated by semicolons.
"""
def execute(self):
- for command in self.rest(1).split(";"):
+ for command in [s.strip() for s in self.rest(1).split(";")]:
self.fm.execute_console(command)
@@ -190,14 +210,10 @@ class shell(Command):
flags = ''
command = self.rest(1)
- if not command and 'p' in flags:
- command = 'cat %f'
if command:
- if '%' in command:
- command = self.fm.substitute_macros(command, escape=True)
self.fm.execute_command(command, flags=flags)
- def tab(self):
+ def tab(self, tabnum):
from ranger.ext.get_executables import get_executables
if self.arg(1) and self.arg(1)[0] == '-':
command = self.rest(2)
@@ -208,7 +224,7 @@ class shell(Command):
try:
position_of_last_space = command.rindex(" ")
except ValueError:
- return (start + program + ' ' for program \
+ return (start + program + ' ' for program
in get_executables() if program.startswith(command))
if position_of_last_space == len(command) - 1:
selection = self.fm.thistab.get_selection()
@@ -218,20 +234,21 @@ class shell(Command):
return self.line + '%s '
else:
before_word, start_of_word = self.line.rsplit(' ', 1)
- return (before_word + ' ' + file.shell_escaped_basename \
- for file in self.fm.thisdir.files \
+ return (before_word + ' ' + file.shell_escaped_basename
+ for file in self.fm.thisdir.files or []
if file.shell_escaped_basename.startswith(start_of_word))
+
class open_with(Command):
def execute(self):
app, flags, mode = self._get_app_flags_mode(self.rest(1))
self.fm.execute_file(
- files = [f for f in self.fm.thistab.get_selection()],
- app = app,
- flags = flags,
- mode = mode)
+ files=[f for f in self.fm.thistab.get_selection()],
+ app=app,
+ flags=flags,
+ mode=mode)
- def tab(self):
+ def tab(self, tabnum):
return self._tab_through_executables()
def _get_app_flags_mode(self, string):
@@ -321,36 +338,52 @@ class set_(Command):
""":set <option name>=<python expression>
Gives an option a new value.
+
+ Use `:set <option>!` to toggle or cycle it, e.g. `:set flush_input!`
"""
name = 'set' # don't override the builtin set class
+
def execute(self):
name = self.arg(1)
- name, value, _ = self.parse_setting_line()
- self.fm.set_option_from_string(name, value)
+ name, value, _, toggle = self.parse_setting_line_v2()
+ if toggle:
+ self.fm.toggle_option(name)
+ else:
+ self.fm.set_option_from_string(name, value)
- def tab(self):
+ def tab(self, tabnum):
+ from ranger.gui.colorscheme import get_all_colorschemes
name, value, name_done = self.parse_setting_line()
settings = self.fm.settings
if not name:
return sorted(self.firstpart + setting for setting in settings)
if not value and not name_done:
- return (self.firstpart + setting for setting in settings \
+ return sorted(self.firstpart + setting for setting in settings
if setting.startswith(name))
if not value:
+ # Cycle through colorschemes when name, but no value is specified
+ if name == "colorscheme":
+ return sorted(self.firstpart + colorscheme for colorscheme
+ in get_all_colorschemes())
return self.firstpart + str(settings[name])
if bool in settings.types_of(name):
if 'true'.startswith(value.lower()):
return self.firstpart + 'True'
if 'false'.startswith(value.lower()):
return self.firstpart + 'False'
+ # Tab complete colorscheme values if incomplete value is present
+ if name == "colorscheme":
+ return sorted(self.firstpart + colorscheme for colorscheme
+ in get_all_colorschemes() if colorscheme.startswith(value))
class setlocal(set_):
- """:setlocal path=<python string> <option name>=<python expression>
+ """:setlocal path=<regular expression> <option name>=<python expression>
Gives an option a new value.
"""
PATH_RE = re.compile(r'^\s*path="?(.*?)"?\s*$')
+
def execute(self):
import os.path
match = self.PATH_RE.match(self.arg(1))
@@ -380,6 +413,49 @@ class setintag(setlocal):
self.fm.set_option_from_string(name, value, tags=tags)
+class default_linemode(Command):
+ def execute(self):
+ import re
+ from ranger.container.fsobject import FileSystemObject
+
+ if len(self.args) < 2:
+ self.fm.notify("Usage: default_linemode [path=<regexp> | tag=<tag(s)>] <linemode>", bad=True)
+
+ # Extract options like "path=..." or "tag=..." from the command line
+ arg1 = self.arg(1)
+ method = "always"
+ argument = None
+ if arg1.startswith("path="):
+ method = "path"
+ argument = re.compile(arg1[5:])
+ self.shift()
+ elif arg1.startswith("tag="):
+ method = "tag"
+ argument = arg1[4:]
+ self.shift()
+
+ # Extract and validate the line mode from the command line
+ linemode = self.rest(1)
+ if linemode not in FileSystemObject.linemode_dict:
+ self.fm.notify("Invalid linemode: %s; should be %s" %
+ (linemode, "/".join(FileSystemObject.linemode_dict)), bad=True)
+
+ # Add the prepared entry to the fm.default_linemodes
+ entry = [method, argument, linemode]
+ self.fm.default_linemodes.appendleft(entry)
+
+ # Redraw the columns
+ if hasattr(self.fm.ui, "browser"):
+ for col in self.fm.ui.browser.columns:
+ col.need_redraw = True
+
+ def tab(self, tabnum):
+ mode = self.arg(1)
+ return (self.arg(0) + " " + linemode
+ for linemode in self.fm.thisfile.linemode_dict.keys()
+ if linemode.startswith(self.arg(1)))
+
+
class quit(Command):
""":quit
@@ -417,20 +493,15 @@ class terminal(Command):
Spawns an "x-terminal-emulator" starting in the current directory.
"""
def execute(self):
- import os
- from ranger.ext.get_executables import get_executables
- command = os.environ.get('TERMCMD', os.environ.get('TERM'))
- if command not in get_executables():
- command = 'x-terminal-emulator'
- if command not in get_executables():
- command = 'xterm'
- self.fm.run(command, flags='f')
+ from ranger.ext.get_executables import get_term
+ self.fm.run(get_term(), flags='f')
class delete(Command):
""":delete
- Tries to delete the selection.
+ Tries to delete the selection or the files passed in arguments (if any).
+ The arguments use a shell-like escaping.
"Selection" is defined as all the "marked files" (by default, you
can mark files with space or v). If there are no marked files,
@@ -441,35 +512,49 @@ class delete(Command):
"""
allow_abbrev = False
+ escape_macros_for_shell = True
def execute(self):
import os
+ import shlex
+ from functools import partial
+ from ranger.container.file import File
+
+ def is_directory_with_files(f):
+ import os.path
+ return (os.path.isdir(f) and not os.path.islink(f)
+ and len(os.listdir(f)) > 0)
+
if self.rest(1):
- self.fm.notify("Error: delete takes no arguments! It deletes "
- "the selected file(s).", bad=True)
- return
+ files = shlex.split(self.rest(1))
+ many_files = (len(files) > 1 or is_directory_with_files(files[0]))
+ else:
+ cwd = self.fm.thisdir
+ cf = self.fm.thisfile
+ if not cwd or not cf:
+ self.fm.notify("Error: no file selected for deletion!", bad=True)
+ return
- cwd = self.fm.thisdir
- cf = self.fm.thisfile
- if not cwd or not cf:
- self.fm.notify("Error: no file selected for deletion!", bad=True)
- return
+ # relative_path used for a user-friendly output in the confirmation.
+ files = [f.relative_path for f in self.fm.thistab.get_selection()]
+ many_files = (cwd.marked_items or is_directory_with_files(cf.path))
confirm = self.fm.settings.confirm_on_delete
- many_files = (cwd.marked_items or (cf.is_directory and not cf.is_link \
- and len(os.listdir(cf.path)) > 0))
-
if confirm != 'never' and (confirm != 'multiple' or many_files):
+ filename_list = files
self.fm.ui.console.ask("Confirm deletion of: %s (y/N)" %
- ', '.join(f.basename for f in self.fm.thistab.get_selection()),
- self._question_callback, ('n', 'N', 'y', 'Y'))
+ ', '.join(files),
+ partial(self._question_callback, files), ('n', 'N', 'y', 'Y'))
else:
# no need for a confirmation, just delete
- self.fm.delete()
+ self.fm.delete(files)
+
+ def tab(self, tabnum):
+ return self._tab_directory_content()
- def _question_callback(self, answer):
+ def _question_callback(self, files, answer):
if answer == 'y' or answer == 'Y':
- self.fm.delete()
+ self.fm.delete(files)
class mark_tag(Command):
@@ -482,8 +567,8 @@ class mark_tag(Command):
def execute(self):
cwd = self.fm.thisdir
- tags = self.rest(1).replace(" ","")
- if not self.fm.tags:
+ tags = self.rest(1).replace(" ", "")
+ if not self.fm.tags or not cwd.files:
return
for fileobj in cwd.files:
try:
@@ -507,7 +592,7 @@ class console(Command):
try:
position = int(self.arg(1)[2:])
self.shift()
- except:
+ except Exception:
pass
self.fm.open_console(self.rest(1), position=position)
@@ -518,16 +603,17 @@ class load_copy_buffer(Command):
Load the copy buffer from confdir/copy_buffer
"""
copy_buffer_filename = 'copy_buffer'
+
def execute(self):
from ranger.container.file import File
from os.path import exists
try:
fname = self.fm.confpath(self.copy_buffer_filename)
f = open(fname, 'r')
- except:
- return self.fm.notify("Cannot open %s" % \
+ except Exception:
+ return self.fm.notify("Cannot open %s" %
(fname or self.copy_buffer_filename), bad=True)
- self.fm.copy_buffer = set(File(g) \
+ self.fm.copy_buffer = set(File(g)
for g in f.read().split("\n") if exists(g))
f.close()
self.fm.ui.redraw_main_column()
@@ -539,13 +625,14 @@ class save_copy_buffer(Command):
Save the copy buffer to confdir/copy_buffer
"""
copy_buffer_filename = 'copy_buffer'
+
def execute(self):
fname = None
try:
fname = self.fm.confpath(self.copy_buffer_filename)
f = open(fname, 'w')
- except:
- return self.fm.notify("Cannot open %s" % \
+ except Exception:
+ return self.fm.notify("Cannot open %s" %
(fname or self.copy_buffer_filename), bad=True)
f.write("\n".join(f.path for f in self.fm.copy_buffer))
f.close()
@@ -568,15 +655,15 @@ class mkdir(Command):
def execute(self):
from os.path import join, expanduser, lexists
- from os import mkdir
+ from os import makedirs
dirname = join(self.fm.thisdir.path, expanduser(self.rest(1)))
if not lexists(dirname):
- mkdir(dirname)
+ makedirs(dirname)
else:
self.fm.notify("file/directory exists!", bad=True)
- def tab(self):
+ def tab(self, tabnum):
return self._tab_directory_content()
@@ -595,7 +682,7 @@ class touch(Command):
else:
self.fm.notify("file/directory exists!", bad=True)
- def tab(self):
+ def tab(self, tabnum):
return self._tab_directory_content()
@@ -611,7 +698,7 @@ class edit(Command):
else:
self.fm.edit_file(self.rest(1))
- def tab(self):
+ def tab(self, tabnum):
return self._tab_directory_content()
@@ -667,24 +754,57 @@ class rename(Command):
new_name = self.rest(1)
+ tagged = {}
+ old_name = self.fm.thisfile.relative_path
+ for f in self.fm.tags.tags:
+ if str(f).startswith(self.fm.thisfile.path):
+ tagged[f] = self.fm.tags.tags[f]
+ self.fm.tags.remove(f)
+
if not new_name:
return self.fm.notify('Syntax: rename <newname>', bad=True)
- if new_name == self.fm.thisfile.basename:
+ if new_name == old_name:
return
if access(new_name, os.F_OK):
return self.fm.notify("Can't rename: file already exists!", bad=True)
- self.fm.rename(self.fm.thisfile, new_name)
- f = File(new_name)
- self.fm.thisdir.pointed_obj = f
- self.fm.thisfile = f
-
- def tab(self):
+ if self.fm.rename(self.fm.thisfile, new_name):
+ f = File(new_name)
+ # Update bookmarks that were pointing on the previous name
+ obsoletebookmarks = [b for b in self.fm.bookmarks
+ if b[1].path == self.fm.thisfile]
+ if obsoletebookmarks:
+ for key, _ in obsoletebookmarks:
+ self.fm.bookmarks[key] = f
+ self.fm.bookmarks.update_if_outdated()
+
+ self.fm.thisdir.pointed_obj = f
+ self.fm.thisfile = f
+ for t in tagged:
+ self.fm.tags.tags[t.replace(old_name, new_name)] = tagged[t]
+ self.fm.tags.dump()
+
+ def tab(self, tabnum):
return self._tab_directory_content()
+class rename_append(Command):
+ """:rename_append
+
+ Creates an open_console for the rename command, automatically placing the cursor before the file extension.
+ """
+
+ def execute(self):
+ cf = self.fm.thisfile
+ path = cf.relative_path.replace("%", "%%")
+ if path.find('.') != 0 and path.rfind('.') != -1 and not cf.is_directory:
+ self.fm.open_console('rename ' + path, position=(7 + path.rfind('.')))
+ else:
+ self.fm.open_console('rename ' + path)
+
+
class chmod(Command):
""":chmod <octal number>
@@ -720,7 +840,7 @@ class chmod(Command):
# reloading directory. maybe its better to reload the selected
# files only.
self.fm.thisdir.load_content()
- except:
+ except Exception:
pass
@@ -739,44 +859,69 @@ class bulkrename(Command):
import tempfile
from ranger.container.file import File
from ranger.ext.shell_escape import shell_escape as esc
- py3 = sys.version > "3"
+ py3 = sys.version_info[0] >= 3
# Create and edit the file list
- filenames = [f.basename for f in self.fm.thistab.get_selection()]
- listfile = tempfile.NamedTemporaryFile()
+ filenames = [f.relative_path for f in self.fm.thistab.get_selection()]
+ listfile = tempfile.NamedTemporaryFile(delete=False)
+ listpath = listfile.name
if py3:
listfile.write("\n".join(filenames).encode("utf-8"))
else:
listfile.write("\n".join(filenames))
- listfile.flush()
- self.fm.execute_file([File(listfile.name)], app='editor')
- listfile.seek(0)
- if py3:
- new_filenames = listfile.read().decode("utf-8").split("\n")
- else:
- new_filenames = listfile.read().split("\n")
listfile.close()
+ self.fm.execute_file([File(listpath)], app='editor')
+ listfile = open(listpath, 'r')
+ new_filenames = listfile.read().split("\n")
+ listfile.close()
+ os.unlink(listpath)
if all(a == b for a, b in zip(filenames, new_filenames)):
self.fm.notify("No renaming to be done!")
return
- # Generate and execute script
+ # Generate script
cmdfile = tempfile.NamedTemporaryFile()
- cmdfile.write(b"# This file will be executed when you close the editor.\n")
- cmdfile.write(b"# Please double-check everything, clear the file to abort.\n")
+ script_lines = []
+ script_lines.append("# This file will be executed when you close the editor.\n")
+ script_lines.append("# Please double-check everything, clear the file to abort.\n")
+ script_lines.extend("mv -vi -- %s %s\n" % (esc(old), esc(new))
+ for old, new in zip(filenames, new_filenames) if old != new)
+ script_content = "".join(script_lines)
if py3:
- cmdfile.write("\n".join("mv -vi -- " + esc(old) + " " + esc(new) \
- for old, new in zip(filenames, new_filenames) \
- if old != new).encode("utf-8"))
+ cmdfile.write(script_content.encode("utf-8"))
else:
- cmdfile.write("\n".join("mv -vi -- " + esc(old) + " " + esc(new) \
- for old, new in zip(filenames, new_filenames) if old != new))
+ cmdfile.write(script_content)
cmdfile.flush()
+
+ # Open the script and let the user review it, then check if the script
+ # was modified by the user
self.fm.execute_file([File(cmdfile.name)], app='editor')
+ cmdfile.seek(0)
+ script_was_edited = (script_content != cmdfile.read())
+
+ # Do the renaming
self.fm.run(['/bin/sh', cmdfile.name], flags='w')
cmdfile.close()
+ # Retag the files, but only if the script wasn't changed during review,
+ # because only then we know which are the source and destination files.
+ if not script_was_edited:
+ tags_changed = False
+ for old, new in zip(filenames, new_filenames):
+ if old != new:
+ oldpath = self.fm.thisdir.path + '/' + old
+ newpath = self.fm.thisdir.path + '/' + new
+ if oldpath in self.fm.tags:
+ old_tag = self.fm.tags.tags[oldpath]
+ self.fm.tags.remove(oldpath)
+ self.fm.tags.tags[newpath] = old_tag
+ tags_changed = True
+ if tags_changed:
+ self.fm.tags.dump()
+ else:
+ fm.notify("files have not been retagged")
+
class relink(Command):
""":relink <newpath>
@@ -794,7 +939,7 @@ class relink(Command):
return self.fm.notify('Syntax: relink <newpath>', bad=True)
if not cf.is_link:
- return self.fm.notify('%s is not a symlink!' % cf.basename, bad=True)
+ return self.fm.notify('%s is not a symlink!' % cf.relative_path, bad=True)
if new_path == os.readlink(cf.path):
return
@@ -809,9 +954,9 @@ class relink(Command):
self.fm.thisdir.pointed_obj = cf
self.fm.thisfile = cf
- def tab(self):
+ def tab(self, tabnum):
if not self.rest(1):
- return self.line+os.readlink(self.fm.thisfile.path)
+ return self.line + os.readlink(self.fm.thisfile.path)
else:
return self._tab_directory_content()
@@ -822,15 +967,22 @@ class help_(Command):
Display ranger's manual page.
"""
name = 'help'
+
def execute(self):
- if self.quantifier == 1:
- self.fm.dump_keybindings()
- elif self.quantifier == 2:
- self.fm.dump_commands()
- elif self.quantifier == 3:
- self.fm.dump_settings()
- else:
- self.fm.display_help()
+ def callback(answer):
+ if answer == "q":
+ return
+ elif answer == "m":
+ self.fm.display_help()
+ elif answer == "c":
+ self.fm.dump_commands()
+ elif answer == "k":
+ self.fm.dump_keybindings()
+ elif answer == "s":
+ self.fm.dump_settings()
+
+ c = self.fm.ui.console.ask("View [m]an page, [k]ey bindings,"
+ " [c]ommands or [s]ettings? (press q to abort)", callback, list("mkcsq") + [chr(27)])
class copymap(Command):
@@ -922,6 +1074,9 @@ class map_(Command):
resolve_macros = False
def execute(self):
+ if not self.arg(1) or not self.arg(2):
+ return self.fm.notify("Not enough arguments", bad=True)
+
self.fm.ui.keymaps.bind(self.context, self.arg(1), self.rest(2))
@@ -970,6 +1125,7 @@ class scout(Command):
-m = mark the matching files after pressing enter
-M = unmark the matching files after pressing enter
-p = permanent filter: hide non-matching files after pressing enter
+ -r = interpret pattern as a regular expression pattern
-s = smart case; like -i unless pattern contains upper case letters
-t = apply filter and search pattern as you type
-v = inverts the match
@@ -1007,14 +1163,14 @@ class scout(Command):
self.fm.thistab.last_search = regex
self.fm.set_search_method(order="search")
- if self.MARK in flags or self.UNMARK in flags:
+ if (self.MARK in flags or self.UNMARK in flags) and thisdir.files:
value = flags.find(self.MARK) > flags.find(self.UNMARK)
if self.FILTER in flags:
for f in thisdir.files:
thisdir.mark_item(f, value)
else:
for f in thisdir.files:
- if regex.search(f.basename):
+ if regex.search(f.relative_path):
thisdir.mark_item(f, value)
if self.PERM_FILTER in flags:
@@ -1032,9 +1188,12 @@ class scout(Command):
if self.KEEP_OPEN in flags and thisdir != self.fm.thisdir:
# reopen the console:
- self.fm.open_console(self.line[0:-len(pattern)])
+ if not pattern:
+ self.fm.open_console(self.line)
+ else:
+ self.fm.open_console(self.line[0:-len(pattern)])
- if thisdir != self.fm.thisdir and pattern != "..":
+ if self.quickly_executed and thisdir != self.fm.thisdir and pattern != "..":
self.fm.block_input(0.5)
def cancel(self):
@@ -1053,8 +1212,8 @@ class scout(Command):
return True
return False
- def tab(self):
- self._count(move=True, offset=1)
+ def tab(self, tabnum):
+ self._count(move=True, offset=tabnum)
def _build_regex(self):
if self._regex is not None:
@@ -1098,7 +1257,7 @@ class scout(Command):
options |= re.IGNORECASE
try:
self._regex = re.compile(regex, options)
- except:
+ except Exception:
self._regex = re.compile("")
return self._regex
@@ -1107,7 +1266,7 @@ class scout(Command):
cwd = self.fm.thisdir
pattern = self.pattern
- if not pattern:
+ if not pattern or not cwd.files:
return 0
if pattern == '.':
return 0
@@ -1119,7 +1278,7 @@ class scout(Command):
i = offset
regex = self._build_regex()
for fsobj in deq:
- if regex.search(fsobj.basename):
+ if regex.search(fsobj.relative_path):
count += 1
if move and count == 1:
cwd.move(to=(cwd.pointer + i) % len(cwd.files))
@@ -1131,6 +1290,33 @@ class scout(Command):
return count == 1
+class filter_inode_type(Command):
+ """
+ :filter_inode_type [dfl]
+
+ Displays only the files of specified inode type. Parameters
+ can be combined.
+
+ d display directories
+ f display files
+ l display links
+ """
+
+ FILTER_DIRS = 'd'
+ FILTER_FILES = 'f'
+ FILTER_LINKS = 'l'
+
+ def execute(self):
+ if not self.arg(1):
+ self.fm.thisdir.inode_type_filter = None
+ else:
+ self.fm.thisdir.inode_type_filter = lambda file: (
+ True if ((self.FILTER_DIRS in self.arg(1) and file.is_directory) or
+ (self.FILTER_FILES in self.arg(1) and file.is_file and not file.is_link) or
+ (self.FILTER_LINKS in self.arg(1) and file.is_link)) else False)
+ self.fm.thisdir.refilter()
+
+
class grep(Command):
""":grep <string>
@@ -1145,8 +1331,32 @@ class grep(Command):
self.fm.execute_command(action, flags='p')
+class flat(Command):
+ """
+ :flat <level>
+
+ Flattens the directory view up to the specified level.
+
+ -1 fully flattened
+ 0 remove flattened view
+ """
+
+ def execute(self):
+ try:
+ level = self.rest(1)
+ level = int(level)
+ except ValueError:
+ level = self.quantifier
+ if level < -1:
+ self.fm.notify("Need an integer number (-1, 0, 1, ...)", bad=True)
+ self.fm.thisdir.unload()
+ self.fm.thisdir.flat = level
+ self.fm.thisdir.load_content()
+
# Version control commands
# --------------------------------
+
+
class stage(Command):
"""
:stage
@@ -1156,17 +1366,15 @@ class stage(Command):
def execute(self):
from ranger.ext.vcs import VcsError
- filelist = [f.path for f in self.fm.thistab.get_selection()]
- self.fm.thisdir.vcs_outdated = True
-# for f in self.fm.thistab.get_selection():
-# f.vcs_outdated = True
-
- try:
- self.fm.thisdir.vcs.add(filelist)
- except VcsError:
- self.fm.notify("Could not stage files.")
-
- self.fm.reload_cwd()
+ if self.fm.thisdir.vcs and self.fm.thisdir.vcs.track:
+ filelist = [f.path for f in self.fm.thistab.get_selection()]
+ try:
+ self.fm.thisdir.vcs.action_add(filelist)
+ except VcsError as error:
+ self.fm.notify('Unable to stage files: {0:s}'.format(str(error)))
+ self.fm.ui.vcsthread.process(self.fm.thisdir)
+ else:
+ self.fm.notify('Unable to stage files: Not in repository')
class unstage(Command):
@@ -1178,67 +1386,101 @@ class unstage(Command):
def execute(self):
from ranger.ext.vcs import VcsError
- filelist = [f.path for f in self.fm.thistab.get_selection()]
- self.fm.thisdir.vcs_outdated = True
-# for f in self.fm.thistab.get_selection():
-# f.vcs_outdated = True
-
- try:
- self.fm.thisdir.vcs.reset(filelist)
- except VcsError:
- self.fm.notify("Could not unstage files.")
+ if self.fm.thisdir.vcs and self.fm.thisdir.vcs.track:
+ filelist = [f.path for f in self.fm.thistab.get_selection()]
+ try:
+ self.fm.thisdir.vcs.action_reset(filelist)
+ except VcsError as error:
+ self.fm.notify('Unable to unstage files: {0:s}'.format(str(error)))
+ self.fm.ui.vcsthread.process(self.fm.thisdir)
+ else:
+ self.fm.notify('Unable to unstage files: Not in repository')
- self.fm.reload_cwd()
+# Metadata commands
+# --------------------------------
-class diff(Command):
+class prompt_metadata(Command):
"""
- :diff
+ :prompt_metadata <key1> [<key2> [<key3> ...]]
- Displays a diff of selected files against last last commited version
+ Prompt the user to input metadata for multiple keys in a row.
"""
+
+ _command_name = "meta"
+ _console_chain = None
+
def execute(self):
- from ranger.ext.vcs import VcsError
- import tempfile
+ prompt_metadata._console_chain = self.args[1:]
+ self._process_command_stack()
- L = self.fm.thistab.get_selection()
- if len(L) == 0: return
+ def _process_command_stack(self):
+ if prompt_metadata._console_chain:
+ key = prompt_metadata._console_chain.pop()
+ self._fill_console(key)
+ else:
+ for col in self.fm.ui.browser.columns:
+ col.need_redraw = True
- filelist = [f.path for f in L]
- vcs = L[0].vcs
+ def _fill_console(self, key):
+ metadata = self.fm.metadata.get_metadata(self.fm.thisfile.path)
+ if key in metadata and metadata[key]:
+ existing_value = metadata[key]
+ else:
+ existing_value = ""
+ text = "%s %s %s" % (self._command_name, key, existing_value)
+ self.fm.open_console(text, position=len(text))
+
+
+class meta(prompt_metadata):
+ """
+ :meta <key> [<value>]
- diff = vcs.get_raw_diff(filelist=filelist)
- if len(diff.strip()) > 0:
- tmp = tempfile.NamedTemporaryFile()
- tmp.write(diff.encode('utf-8'))
- tmp.flush()
+ Change metadata of a file. Deletes the key if value is empty.
+ """
- pager = os.environ.get('PAGER', ranger.DEFAULT_PAGER)
- self.fm.run([pager, tmp.name])
+ def execute(self):
+ key = self.arg(1)
+ value = self.rest(1)
+ update_dict = dict()
+ update_dict[key] = self.rest(2)
+ selection = self.fm.thistab.get_selection()
+ for f in selection:
+ self.fm.metadata.set_metadata(f.path, update_dict)
+ self._process_command_stack()
+
+ def tab(self, tabnum):
+ key = self.arg(1)
+ metadata = self.fm.metadata.get_metadata(self.fm.thisfile.path)
+ if key in metadata and metadata[key]:
+ return [" ".join([self.arg(0), self.arg(1), metadata[key]])]
else:
- raise Exception("diff is empty")
+ return [self.arg(0) + " " + key for key in sorted(metadata)
+ if key.startswith(self.arg(1))]
-class log(Command):
+class linemode(default_linemode):
"""
- :log
+ :linemode <mode>
- Displays the log of the current repo or files
+ Change what is displayed as a filename.
+
+ - "mode" may be any of the defined linemodes (see: ranger.core.linemode).
+ "normal" is mapped to "filename".
"""
+
def execute(self):
- from ranger.ext.vcs import VcsError
- import tempfile
+ mode = self.arg(1)
- L = self.fm.thistab.get_selection()
- if len(L) == 0: return
+ if mode == "normal":
+ mode = DEFAULT_LINEMODE
- filelist = [f.path for f in L]
- vcs = L[0].vcs
+ if mode not in self.fm.thisfile.linemode_dict:
+ self.fm.notify("Unhandled linemode: `%s'" % mode, bad=True)
+ return
- log = vcs.get_raw_log(filelist=filelist)
- tmp = tempfile.NamedTemporaryFile()
- tmp.write(log.encode('utf-8'))
- tmp.flush()
+ self.fm.thisdir._set_linemode_of_children(mode)
- pager = os.environ.get('PAGER', ranger.DEFAULT_PAGER)
- self.fm.run([pager, tmp.name])
+ # Ask the browsercolumns to redraw
+ for col in self.fm.ui.browser.columns:
+ col.need_redraw = True
diff --git a/.config/ranger/rc.conf b/.config/ranger/rc.conf
index ab29508..15eca50 100644
--- a/.config/ranger/rc.conf
+++ b/.config/ranger/rc.conf
@@ -1,4 +1,4 @@
-# ==================================================================
+# ===================================================================
# This file contains the default startup commands for ranger.
# To change them, it is recommended to create the file
# ~/.config/ranger/rc.conf and add your custom commands there.
@@ -19,6 +19,13 @@
# == Options
# ===================================================================
+# Which viewmode should be used? Possible values are:
+# miller: Use miller columns which show multiple levels of the hierarchy
+# multipane: Midnight-commander like multipane view showing all tabs next
+# to each other
+set viewmode miller
+#set viewmode multipane
+
# How many columns are there, and what are their relative widths?
set column_ratios 1,3,4
@@ -29,18 +36,21 @@ set hidden_filter ^\.|\.(?:pyc|pyo|bak|swp)$|^lost\+found$|^__(py)?cache__$
set show_hidden false
# Ask for a confirmation when running the "delete" command?
-# Valid values are "always" (default), "never", "multiple"
+# Valid values are "always", "never", "multiple" (default)
# With "multiple", ranger will ask only if you delete multiple files at once.
set confirm_on_delete always
# Which script is used to generate file previews?
# ranger ships with scope.sh, a script that calls external programs (see
-# README for dependencies) to preview images, archives, etc.
+# README.md for dependencies) to preview images, archives, etc.
set preview_script ~/.config/ranger/scope.sh
-# Use the external preview script or display simple plain text previews?
+# Use the external preview script or display simple plain text or image previews?
set use_preview_script true
+# Automatically count files in the directory, even before entering them?
+set automatically_count_files true
+
# Open all images in this directory when running certain image viewers
# like feh or sxiv? You can still open selected files by marking them.
set open_all_images true
@@ -55,11 +65,30 @@ set vcs_backend_git enabled
set vcs_backend_hg disabled
set vcs_backend_bzr disabled
-# Preview images in full color with the external command "w3mimgpreview"?
-# This requires the console web browser "w3m" and a supported terminal.
-# It has been successfully tested with "xterm" and "urxvt" without tmux.
+# Use one of the supported image preview protocols
set preview_images false
+# Set the preview image method. Supported methods:
+#
+# * w3m (default):
+# Preview images in full color with the external command "w3mimgpreview"?
+# This requires the console web browser "w3m" and a supported terminal.
+# It has been successfully tested with "xterm" and "urxvt" without tmux.
+#
+# * iterm2:
+# Preview images in full color using iTerm2 image previews
+# (http://iterm2.com/images.html). This requires using iTerm2 compiled
+# with image preview support.
+#
+# * urxvt:
+# Preview images in full color using urxvt image backgrounds. This
+# requires using urxvt compiled with pixbuf support.
+#
+# * urxvt-full:
+# The same as urxvt but utilizing not only the preview pane but the
+# whole terminal window.
+set preview_images_method urxvt
+
# Use a unicode "..." character to mark cut-off filenames?
set unicode_ellipsis false
@@ -67,17 +96,17 @@ set unicode_ellipsis false
set show_hidden_bookmarks true
# Which colorscheme to use? These colorschemes are available by default:
-# default, jungle, snow
+# default, jungle, snow, solarized
set colorscheme default
# Preview files on the rightmost column?
# And collapse (shrink) the last column if there is nothing to preview?
set preview_files true
set preview_directories true
-set collapse_preview false
+set collapse_preview true
# Save the console history on exit?
-set save_console_history true
+set save_console_history false
# Draw the status bar on top of the browser window (default: bottom)
set status_bar_on_top false
@@ -122,7 +151,7 @@ set max_console_history_size 50
# Try to keep so much space between the top/bottom border when scrolling:
set scroll_offset 8
-# Flush the input after each key hit? (Noticable when ranger lags)
+# Flush the input after each key hit? (Noticeable when ranger lags)
set flushinput true
# Padding on the right when there's no preview?
@@ -144,18 +173,45 @@ set autoupdate_cumulative_size false
# Turning this on makes sense for screen readers:
set show_cursor false
-# One of: size, basename, mtime, type
+# One of: size, natural, basename, atime, ctime, mtime, type, random
set sort natural
# Additional sorting options
set sort_reverse false
set sort_case_insensitive true
set sort_directories_first true
+set sort_unicode true
# Enable this if key combinations with the Alt Key don't work for you.
# (Especially on xterm)
set xterm_alt_key false
+# Whether to include bookmarks in cd command
+set cd_bookmarks false
+
+# Avoid previewing files larger than this size, in bytes. Use a value of 0 to
+# disable this feature.
+set preview_max_size 0
+
+# Add the highlighted file to the path in the titlebar
+set show_selection_in_titlebar true
+
+# The delay that ranger idly waits for user input, in milliseconds, with a
+# resolution of 100ms. Lower delay reduces lag between directory updates but
+# increases CPU load.
+set idle_delay 2000
+
+# When the metadata manager module looks for metadata, should it only look for
+# a ".metadata.json" file in the current directory, or do a deep search and
+# check all directories above the current one as well?
+set metadata_deep_search false
+
+# Clear all existing filters when leaving a directory
+set clear_filters_on_dir_change false
+
+# Disable displaying line numbers in main column
+set line_numbers false
+
# ===================================================================
# == Local Options
# ===================================================================
@@ -171,11 +227,12 @@ set xterm_alt_key false
alias e edit
alias q quit
alias q! quitall
+alias qa quitall
alias qall quitall
alias setl setlocal
alias filter scout -prt
-alias find scout -aet
+alias find scout -aeit
alias mark scout -mr
alias unmark scout -Mr
alias search scout -rs
@@ -196,6 +253,7 @@ map <C-r> reset
map <C-l> redraw_window
map <C-c> abort
map <esc> change_mode normal
+map ~ set viewmode!
map i display_file
map ? help
@@ -203,18 +261,23 @@ map W display_log
map w taskview_open
map S shell $SHELL
-map de delete
-
map : console
map ; console
-map ! console shell
+map ! console shell%space
map @ console -p6 shell %%s
-map # console shell -p
-map l console shell -p
-map s console shell
-map r chain draw_possible_programs; console open_with
-map f console find
-map cd console cd
+map # console shell -p%space
+map s console shell%space
+map r chain draw_possible_programs; console open_with%%space
+map f console find%space
+map cd console cd%space
+
+# Change the line mode
+map Mf linemode filename
+map Mi linemode fileinfo
+map Mm linemode mtime
+map Mp linemode permissions
+map Ms linemode sizemtime
+map Mt linemode metatitle
# Tagging / Marking
map t tag_toggle
@@ -232,7 +295,7 @@ map <F3> display_file
map <F4> edit
map <F5> copy
map <F6> cut
-map <F7> console mkdir
+map <F7> console mkdir%space
map <F8> console delete
map <F10> exit
@@ -246,8 +309,8 @@ map <END> move to=-1
map <PAGEDOWN> move down=1 pages=True
map <PAGEUP> move up=1 pages=True
map <CR> move right=1
-map <DELETE> console delete
-map <INSERT> console touch
+#map <DELETE> console delete
+map <INSERT> console touch%space
# VIM-like
copymap <UP> k
@@ -271,53 +334,44 @@ map ] move_parent 1
map [ move_parent -1
map } traverse
-map gh cd ~
-map ge cd /etc
-map gu cd /usr
-map gd cd /dev
-map gl cd -r .
-map gL cd -r %f
-map go cd /opt
-map gv cd /var
-map gm cd /media
-map gM cd /mnt
-map gs cd /srv
-map gr cd /
-map gR eval fm.cd(ranger.RANGERDIR)
-map g/ cd /
-map g? cd /usr/share/doc/ranger
-
# External Programs
map E edit
map du shell -p du --max-depth=1 -h --apparent-size
map dU shell -p du --max-depth=1 -h --apparent-size | sort -rh
-map yp shell -d echo -n %d/%f | xsel -i
-map yd shell -d echo -n %d | xsel -i
-map yn shell -d echo -n %f | xsel -i
+map yp shell -f echo -n %d/%f | xsel -i; xsel -o | xsel -i -b
+map yd shell -f echo -n %d | xsel -i; xsel -o | xsel -i -b
+map yn shell -f echo -n %f | xsel -i; xsel -o | xsel -i -b
# Filesystem Operations
map = chmod
-map cw console rename
-map A eval fm.open_console('rename ' + fm.thisfile.basename)
-map I eval fm.open_console('rename ' + fm.thisfile.basename, position=7)
+map cw console rename%space
+map a rename_append
+map A eval fm.open_console('rename ' + fm.thisfile.relative_path.replace("%", "%%"))
+map I eval fm.open_console('rename ' + fm.thisfile.relative_path.replace("%", "%%"), position=7)
map pp paste
map po paste overwrite=True
+map pP paste append=True
+map pO paste overwrite=True append=True
map pl paste_symlink relative=False
map pL paste_symlink relative=True
map phl paste_hardlink
map pht paste_hardlinked_subtree
+map dD console delete
+
map dd cut
map ud uncut
map da cut mode=add
map dr cut mode=remove
+map dt cut mode=toggle
map yy copy
map uy uncut
map ya copy mode=add
map yr copy mode=remove
+map yt copy mode=toggle
# Temporary workarounds
map dgg eval fm.cut(dirarg=dict(to=0), narg=quantifier)
@@ -330,7 +384,7 @@ map yj eval fm.copy(dirarg=dict(down=1), narg=quantifier)
map yk eval fm.copy(dirarg=dict(up=1), narg=quantifier)
# Searching
-map / console search
+map / console search%space
map n search_next
map N search_next forward=False
map ct search_next order=tag
@@ -342,7 +396,7 @@ map ca search_next order=atime
# Tabs
map <C-t> tab_new ~
-map <C-n> tab_move 1
+map <C-t> tab_move 1
map <C-p> tab_move -1
map <C-w> tab_close
map <TAB> tab_move 1
@@ -365,7 +419,8 @@ map <a-8> tab_open 8
map <a-9> tab_open 9
# Sorting
-map or toggle_option sort_reverse
+map or set sort_reverse!
+map oz set sort=random
map os chain set sort=size; set sort_reverse=False
map ob chain set sort=basename; set sort_reverse=False
map on chain set sort=natural; set sort_reverse=False
@@ -373,6 +428,7 @@ map om chain set sort=mtime; set sort_reverse=False
map oc chain set sort=ctime; set sort_reverse=False
map oa chain set sort=atime; set sort_reverse=False
map ot chain set sort=type; set sort_reverse=False
+map oe chain set sort=extension; set sort_reverse=False
map oS chain set sort=size; set sort_reverse=True
map oB chain set sort=basename; set sort_reverse=True
@@ -381,22 +437,24 @@ map oM chain set sort=mtime; set sort_reverse=True
map oC chain set sort=ctime; set sort_reverse=True
map oA chain set sort=atime; set sort_reverse=True
map oT chain set sort=type; set sort_reverse=True
+map oE chain set sort=extension; set sort_reverse=True
map dc get_cumulative_size
# Settings
-map zc toggle_option collapse_preview
-map zd toggle_option sort_directories_first
-map zh toggle_option show_hidden
-map <C-h> toggle_option show_hidden
-map zi toggle_option flushinput
-map zm toggle_option mouse_enabled
-map zp toggle_option preview_files
-map zP toggle_option preview_directories
-map zs toggle_option sort_case_insensitive
-map zu toggle_option autoupdate_cumulative_size
-map zv toggle_option use_preview_script
-map zf console filter
+map zc set collapse_preview!
+map zd set sort_directories_first!
+map zh set show_hidden!
+map <C-h> set show_hidden!
+map zI set flushinput!
+map zi set preview_images!
+map zm set mouse_enabled!
+map zp set preview_files!
+map zP set preview_directories!
+map zs set sort_case_insensitive!
+map zu set autoupdate_cumulative_size!
+map zv set use_preview_script!
+map zf console filter%space
# Bookmarks
map `<any> enter_bookmark %any
@@ -408,17 +466,17 @@ map m<bg> draw_bookmarks
copymap m<bg> um<bg> `<bg> '<bg>
# Generate all the chmod bindings with some python help:
-eval for arg in "rwxXst": cmd("map +u{0} shell -d chmod u+{0} %s".format(arg))
-eval for arg in "rwxXst": cmd("map +g{0} shell -d chmod g+{0} %s".format(arg))
-eval for arg in "rwxXst": cmd("map +o{0} shell -d chmod o+{0} %s".format(arg))
-eval for arg in "rwxXst": cmd("map +a{0} shell -d chmod a+{0} %s".format(arg))
-eval for arg in "rwxXst": cmd("map +{0} shell -d chmod u+{0} %s".format(arg))
-
-eval for arg in "rwxXst": cmd("map -u{0} shell -d chmod u-{0} %s".format(arg))
-eval for arg in "rwxXst": cmd("map -g{0} shell -d chmod g-{0} %s".format(arg))
-eval for arg in "rwxXst": cmd("map -o{0} shell -d chmod o-{0} %s".format(arg))
-eval for arg in "rwxXst": cmd("map -a{0} shell -d chmod a-{0} %s".format(arg))
-eval for arg in "rwxXst": cmd("map -{0} shell -d chmod u-{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map +u{0} shell -f chmod u+{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map +g{0} shell -f chmod g+{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map +o{0} shell -f chmod o+{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map +a{0} shell -f chmod a+{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map +{0} shell -f chmod u+{0} %s".format(arg))
+
+eval for arg in "rwxXst": cmd("map -u{0} shell -f chmod u-{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map -g{0} shell -f chmod g-{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map -o{0} shell -f chmod o-{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map -a{0} shell -f chmod a-{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map -{0} shell -f chmod u-{0} %s".format(arg))
# ===================================================================
# == Define keys for the console
@@ -442,11 +500,14 @@ cmap <left> eval fm.ui.console.move(left=1)
cmap <right> eval fm.ui.console.move(right=1)
cmap <home> eval fm.ui.console.move(right=0, absolute=True)
cmap <end> eval fm.ui.console.move(right=-1, absolute=True)
+cmap <a-left> eval fm.ui.console.move_word(left=1)
+cmap <a-right> eval fm.ui.console.move_word(right=1)
# Line Editing
cmap <backspace> eval fm.ui.console.delete(-1)
cmap <delete> eval fm.ui.console.delete(0)
cmap <C-w> eval fm.ui.console.delete_word()
+cmap <A-d> eval fm.ui.console.delete_word(backward=False)
cmap <C-k> eval fm.ui.console.delete_rest(1)
cmap <C-u> eval fm.ui.console.delete_rest(-1)
cmap <C-y> eval fm.ui.console.paste()
@@ -496,6 +557,7 @@ copypmap <PAGEDOWN> n f <C-F> <Space>
copypmap <PAGEUP> p b <C-B>
# Basic
+pmap <C-l> redraw_window
pmap <ESC> pager_close
copypmap <ESC> q Q i <F3>
pmap E edit_file
@@ -531,5 +593,6 @@ tmap <pageup> eval -q fm.ui.taskview.task_move(0)
tmap <delete> eval -q fm.ui.taskview.task_remove()
# Basic
+tmap <C-l> redraw_window
tmap <ESC> taskview_close
copytmap <ESC> q Q w <C-c>
diff --git a/.config/ranger/scope.sh b/.config/ranger/scope.sh
index 4786154..3d1f725 100755
--- a/.config/ranger/scope.sh
+++ b/.config/ranger/scope.sh
@@ -16,42 +16,67 @@
# 3 | fix width | success. Don't reload when width changes
# 4 | fix height | success. Don't reload when height changes
# 5 | fix both | success. Don't ever reload
+# 6 | image | success. display the image $cached points to as an image preview
+# 7 | image | success. display the file directly as an image
# Meaningful aliases for arguments:
-path="$1" # Full path of the selected file
-width="$2" # Width of the preview pane (number of fitting characters)
-height="$3" # Height of the preview pane (number of fitting characters)
+path="$1" # Full path of the selected file
+width="$2" # Width of the preview pane (number of fitting characters)
+height="$3" # Height of the preview pane (number of fitting characters)
+cached="$4" # Path that should be used to cache image previews
+preview_images="$5" # "True" if image previews are enabled, "False" otherwise.
maxln=200 # Stop after $maxln lines. Can be used like ls | head -n $maxln
# Find out something about the file:
mimetype=$(file --mime-type -Lb "$path")
-extension=${path##*.}
+extension=$(/bin/echo "${path##*.}" | awk '{print tolower($0)}')
# Functions:
# runs a command and saves its output into $output. Useful if you need
# the return value AND want to use the output in a pipe
try() { output=$(eval '"$@"'); }
-# writes the output of the previouosly used "try" command
-dump() { echo "$output"; }
+# writes the output of the previously used "try" command
+dump() { /bin/echo "$output"; }
# a common post-processing function used after most commands
trim() { head -n "$maxln"; }
# wraps highlight to treat exit code 141 (killed by SIGPIPE) as success
-highlight() { command highlight "$@"; test $? = 0 -o $? = 141; }
+safepipe() { "$@"; test $? = 0 -o $? = 141; }
+
+# Image previews, if enabled in ranger.
+if [ "$preview_images" = "True" ]; then
+ case "$mimetype" in
+ # Image previews for SVG files, disabled by default.
+ ###image/svg+xml)
+ ### convert "$path" "$cached" && exit 6 || exit 1;;
+ # Image previews for image files. w3mimgdisplay will be called for all
+ # image files (unless overriden as above), but might fail for
+ # unsupported types.
+ image/*)
+ exit 7;;
+ # Image preview for video, disabled by default.:
+ ###video/*)
+ ### ffmpegthumbnailer -i "$path" -o "$cached" -s 0 && exit 6 || exit 1;;
+ esac
+fi
case "$extension" in
# Archive extensions:
- 7z|a|ace|alz|arc|arj|bz|bz2|cab|cpio|deb|gz|jar|lha|lz|lzh|lzma|lzo|\
+ a|ace|alz|arc|arj|bz|bz2|cab|cpio|deb|gz|jar|lha|lz|lzh|lzma|lzo|\
rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xpi|xz|Z|zip)
try als "$path" && { dump | trim; exit 0; }
try acat "$path" && { dump | trim; exit 3; }
try bsdtar -lf "$path" && { dump | trim; exit 0; }
exit 1;;
rar)
+ # avoid password prompt by providing empty password
try unrar -p- lt "$path" && { dump | trim; exit 0; } || exit 1;;
+ 7z)
+ # avoid password prompt by providing empty password
+ try 7z -p l "$path" && { dump | trim; exit 0; } || exit 1;;
# PDF documents:
pdf)
try pdftotext -l 10 -nopgbrk -q "$path" - && \
@@ -59,6 +84,9 @@ case "$extension" in
# BitTorrent Files
torrent)
try transmission-show "$path" && { dump | trim; exit 5; } || exit 1;;
+ # ODT Files
+ odt|ods|odp|sxw)
+ try odt2txt "$path" && { dump | trim; exit 5; } || exit 1;;
# HTML Pages:
htm|html|xhtml)
try w3m -dump "$path" && { dump | trim | fmt -s -w $width; exit 4; }
@@ -70,8 +98,12 @@ esac
case "$mimetype" in
# Syntax highlight for text files:
text/* | */xml)
- try highlight --out-format=ansi "$path" && { dump | trim; exit 5; } || exit 2;;
- ## Ascii-previews of images:
+ pygmentize_format=terminal
+ highlight_format=ansi
+ try safepipe highlight --out-format=${highlight_format} "$path" && { dump | trim; exit 5; }
+ try safepipe pygmentize -f ${pygmentize_format} "$path" && { dump | trim; exit 5; }
+ exit 2;;
+ # Ascii-previews of images:
#image/*)
# img2txt --gamma=0.6 --width="$width" "$path" && exit 4 || exit 1;;
# Display information about media files: