summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--AUTHORS2
-rw-r--r--CHANGELOG24
-rw-r--r--README.md12
-rw-r--r--doc/ranger.132
-rw-r--r--doc/ranger.pod30
-rw-r--r--doc/rifle.12
-rw-r--r--examples/README (renamed from doc/examples/README)0
-rw-r--r--examples/bash_automatic_cd.sh (renamed from doc/examples/bash_automatic_cd.sh)4
-rw-r--r--examples/bash_subshell_notice.sh (renamed from doc/examples/bash_subshell_notice.sh)2
-rw-r--r--examples/plugin_chmod_keybindings.py (renamed from doc/examples/plugin_chmod_keybindings.py)2
-rw-r--r--examples/plugin_file_filter.py (renamed from doc/examples/plugin_file_filter.py)2
-rw-r--r--examples/plugin_hello_world.py (renamed from doc/examples/plugin_hello_world.py)2
-rw-r--r--examples/plugin_new_macro.py (renamed from doc/examples/plugin_new_macro.py)2
-rw-r--r--examples/plugin_new_sorting_method.py (renamed from doc/examples/plugin_new_sorting_method.py)2
-rw-r--r--examples/rifle_different_file_opener.conf (renamed from doc/examples/rifle_different_file_opener.conf)2
-rwxr-xr-xexamples/rifle_sxiv.sh (renamed from doc/examples/rifle_sxiv.sh)2
-rw-r--r--examples/vim_file_chooser.vim (renamed from doc/examples/vim_file_chooser.vim)2
-rwxr-xr-xranger.py4
-rw-r--r--ranger/__init__.py4
-rw-r--r--ranger/api/commands.py3
-rw-r--r--ranger/config/commands.py66
-rw-r--r--ranger/config/rc.conf15
-rw-r--r--ranger/config/rifle.conf22
-rw-r--r--ranger/container/fsobject.py2
-rw-r--r--ranger/container/tags.py37
-rw-r--r--ranger/core/actions.py22
-rw-r--r--ranger/core/environment.py111
-rw-r--r--ranger/core/fm.py4
-rw-r--r--ranger/core/linemode.py18
-rw-r--r--ranger/core/loader.py7
-rw-r--r--ranger/core/shared.py7
-rwxr-xr-xranger/ext/rifle.py2
-rw-r--r--ranger/fsobject.py5
-rw-r--r--ranger/gui/context.py2
-rw-r--r--ranger/gui/displayable.py4
-rw-r--r--ranger/gui/widgets/statusbar.py9
-rwxr-xr-xsetup.py2
37 files changed, 270 insertions, 200 deletions
diff --git a/AUTHORS b/AUTHORS
index 1fda9164..62387efe 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -22,7 +22,7 @@ Copyright 2015  No Suck <admin@nosuck.org>
 Copyright 2015  Randy Nance <randynobx@gmail.com>
 Copyright 2015  Wojciech Siewierski <wojciech.siewierski@onet.pl>
 
-Ideally, all contributors of non-trivial code are named here to the extend that
+Ideally, all contributors of non-trivial code are named here to the extent that
 a name and e-mail address is available.  Please write a mail to hut@hut.pm if
 your name is missing, or in case of any other issues.
 
diff --git a/CHANGELOG b/CHANGELOG
index c5237279..041a7701 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,29 @@
 This log documents changes between stable versions.
 
+2015-04-13: version 1.7.0
+* The default editor is now "vim" instead of "nano"
+* Added automatic updates of tags when a file is renamed from within ranger
+* Added "preview_images_method" which can be set to "iterm2" to use native
+  iTerm2 image previews
+* Added ":rename_append" command to rename files without the file extension
+* Added ":linemode" command to change the way the files are displayed
+  Try this out by pressing M followed by one of the suggested keys.
+  New linemodes can be added with ranger.api.register_linemode().
+* Added ":filter_by_inode" command to only show directories, files or links
+* Added ":meta" command for managing custom file metadata
+* Added ":flat" command for displaying subdirectories
+* Added "solarized" colorscheme
+* Added generic ability to use scope.sh for image previews
+* Added video previews in scope.sh
+* Added option "sort_unicode" to sort according to unicode, not ASCII
+* ":mkdir" can now create multiple directory levels (like `mkdir -p`)
+* ":help" (key binding "?") is now interactive
+* ":find" (key binding "/") is now case insensitive by default
+* "ranger --copy-config=all" now copies a short sample commands.py rather than
+  the full one, so that you can update ranger without having broken commands.
+  The full commands.py is still copied to ~/.config/ranger/commands_full.py.
+* Fixed broken copying of symlinks
+
 2013-05-24: Version 1.6.1
 * Added support for version control systems, see:
   http://lists.nongnu.org/archive/html/ranger-users/2013-03/msg00007.html
diff --git a/README.md b/README.md
index 979aa8f9..c90dbbb8 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-ranger v.1.6.1
+ranger v.1.7.0
 ==============
 ranger is a console file manager with Emacs key bindings.  It provides a
 minimalistic and nice curses interface with a view on the directory hierarchy.
@@ -9,11 +9,11 @@ out which program to use for what file type.
 
 This file describes ranger and how to get it to run.  For instructions on the
 usage, please read the man page.  See HACKING.md for development specific
-information.  For configuration, check the files in ranger/config/.  They
-are usually installed to /usr/lib/python*/site-packages/ranger/config/
-and can be obtained with ranger's --copy-config option.  The doc/examples/
-directory contains several scripts and plugins that demonstrate how ranger can
-be extended or combined with other programs.
+information.  For configuration, check the files in ranger/config/ or copy the
+default config to ~/.config/ranger with ranger's --copy-config option.  The
+examples/ directory contains several scripts and plugins that demonstrate how
+ranger can be extended or combined with other programs.  These files can be
+found in the git repository or in /usr/share/doc/ranger.
 
 A note to packagers:  Versions meant for packaging are listed in the changelog
 on the website.
diff --git a/doc/ranger.1 b/doc/ranger.1
index 7c01dc26..426ec0cf 100644
--- a/doc/ranger.1
+++ b/doc/ranger.1
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "RANGER 1"
-.TH RANGER 1 "ranger-1.6.1" "03/31/2015" "ranger manual"
+.TH RANGER 1 "ranger-1.7.0" "04/13/2015" "ranger manual"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -162,8 +162,13 @@ The \fI\s-1README\s0\fR contains install instructions.
 The file \fI\s-1HACKING\s0.md\fR contains guidelines for code modification.
 .PP
 The directory \fIdoc/configs\fR contains configuration files.  They are usually
-installed to \fI/usr/lib/python*/site\-packages/ranger/config\fR and can be
-obtained with ranger's \-\-copy\-config option.
+installed to \fI/usr/share/doc/ranger/config\fR and can be obtained with ranger's
+\&\-\-copy\-config option.
+.PP
+The directory \fIexamples\fR contains reference implementations for ranger
+plugins, sample configuration files and some programs for integrating ranger
+with other software.  They are usually installed to
+\&\fI/usr/share/doc/ranger/examples\fR.
 .PP
 The man page of \fIrifle\fR\|(1) describes the functions of the file opener
 .PP
@@ -320,6 +325,14 @@ Macros for file paths are generally shell-escaped so they can be used in the
 Additionally, if you create a key binding that uses <any>, a special statement
 which accepts any key, then the macro \f(CW%any\fR (or \f(CW%any0\fR, \f(CW%any1\fR, \f(CW%any2\fR, ...) can be
 used in the command to get the key that was pressed.
+.PP
+The macro \f(CW%rangerdir\fR expands to the directory of ranger's python library, you
+can use it for something like this command:
+  alias show_commands shell less \f(CW%rangerdir\fR/config/commands.py
+.PP
+The macro \f(CW%space\fR expands to a space character. You can use it to add spaces to
+the end of a command when needed, while preventing editors to strip spaces off
+the end of the line automatically.
 .SS "\s-1BOOKMARKS\s0"
 .IX Subsection "BOOKMARKS"
 Type \fB<C\-x>rm<key>\fR to bookmark the current directory. You can
@@ -841,6 +854,7 @@ including their parameters, excluding descriptions:
 \& find pattern
 \& flat level
 \& grep pattern
+\& help
 \& linemode linemodename
 \& load_copy_buffer
 \& map key command
@@ -862,6 +876,7 @@ including their parameters, excluding descriptions:
 \& search pattern
 \& search_inc pattern
 \& set option value
+\& setintag tags option value
 \& setlocal [path=<path>] option value
 \& shell [\-FLAGS] command
 \& terminal
@@ -994,12 +1009,16 @@ values \-2 and less are invalid.
 .IP "grep \fIpattern\fR" 2
 .IX Item "grep pattern"
 Looks for a string in all marked files or directories.
+.IP "help" 2
+.IX Item "help"
+Provides a quick way to view ranger documentations.
 .IP "linemode \fIlinemodename\fR" 2
 .IX Item "linemode linemodename"
 Sets the linemode of all files in the current directory.  The linemode may be:
 .Sp
-.Vb 5
+.Vb 6
 \& "filename": display each line as "<basename>...<size>"
+\& "fileinfo": display each line as "<basename>...<file(1) output>"
 \& "permissions": display each line as "<permissions> <owner> <group> <basename>"
 \& "metatitle": display metadata from .metadata.json files if
 \&     available, fall back to the "filename" linemode if no
@@ -1140,6 +1159,11 @@ doesn't work for functions and regular expressions. Valid values are:
 \& list           | 1,2,3,4
 \& none           | none
 .Ve
+.IP "setintag \fItags\fR \fIoption\fR \fIvalue\fR" 2
+.IX Item "setintag tags option value"
+Assigns a new value to an option, but locally for the directories that are
+marked with \fItag\fR.  This means, that this option only takes effect when
+visiting that directory.
 .IP "setlocal [path=\fIpath\fR] \fIoption\fR \fIvalue\fR" 2
 .IX Item "setlocal [path=path] option value"
 Assigns a new value to an option, but locally for the directory given by
diff --git a/doc/ranger.pod b/doc/ranger.pod
index 5ebdbc98..eea82c3e 100644
--- a/doc/ranger.pod
+++ b/doc/ranger.pod
@@ -35,8 +35,13 @@ The F<README> contains install instructions.
 The file F<HACKING.md> contains guidelines for code modification.
 
 The directory F<doc/configs> contains configuration files.  They are usually
-installed to F</usr/lib/python*/site-packages/ranger/config> and can be
-obtained with ranger's --copy-config option.
+installed to F</usr/share/doc/ranger/config> and can be obtained with ranger's
+--copy-config option.
+
+The directory F<examples> contains reference implementations for ranger
+plugins, sample configuration files and some programs for integrating ranger
+with other software.  They are usually installed to
+F</usr/share/doc/ranger/examples>.
 
 The man page of rifle(1) describes the functions of the file opener
 
@@ -217,6 +222,14 @@ Additionally, if you create a key binding that uses <any>, a special statement
 which accepts any key, then the macro %any (or %any0, %any1, %any2, ...) can be
 used in the command to get the key that was pressed.
 
+The macro %rangerdir expands to the directory of ranger's python library, you
+can use it for something like this command:
+  alias show_commands shell less %rangerdir/config/commands.py
+
+The macro %space expands to a space character. You can use it to add spaces to
+the end of a command when needed, while preventing editors to strip spaces off
+the end of the line automatically.
+
 =head2 BOOKMARKS
 
 Type B<E<lt>C-xE<gt>rm<keyE<gt>> to bookmark the current directory. You can
@@ -848,6 +861,7 @@ including their parameters, excluding descriptions:
  find pattern
  flat level
  grep pattern
+ help
  linemode linemodename
  load_copy_buffer
  map key command
@@ -869,6 +883,7 @@ including their parameters, excluding descriptions:
  search pattern
  search_inc pattern
  set option value
+ setintag tags option value
  setlocal [path=<path>] option value
  shell [-FLAGS] command
  terminal
@@ -1024,11 +1039,16 @@ values -2 and less are invalid.
 
 Looks for a string in all marked files or directories.
 
+=item help
+
+Provides a quick way to view ranger documentations.
+
 =item linemode I<linemodename>
 
 Sets the linemode of all files in the current directory.  The linemode may be:
 
  "filename": display each line as "<basename>...<size>"
+ "fileinfo": display each line as "<basename>...<file(1) output>"
  "permissions": display each line as "<permissions> <owner> <group> <basename>"
  "metatitle": display metadata from .metadata.json files if
      available, fall back to the "filename" linemode if no
@@ -1183,6 +1203,12 @@ doesn't work for functions and regular expressions. Valid values are:
  list           | 1,2,3,4
  none           | none
 
+=item setintag I<tags> I<option> I<value>
+
+Assigns a new value to an option, but locally for the directories that are
+marked with I<tag>.  This means, that this option only takes effect when
+visiting that directory.
+
 =item setlocal [path=I<path>] I<option> I<value>
 
 Assigns a new value to an option, but locally for the directory given by
diff --git a/doc/rifle.1 b/doc/rifle.1
index f7e9df78..95010452 100644
--- a/doc/rifle.1
+++ b/doc/rifle.1
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "RIFLE 1"
-.TH RIFLE 1 "rifle-1.6.1" "03/31/2015" "rifle manual"
+.TH RIFLE 1 "rifle-1.7.0" "04/13/2015" "rifle manual"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff --git a/doc/examples/README b/examples/README
index ca514853..ca514853 100644
--- a/doc/examples/README
+++ b/examples/README
diff --git a/doc/examples/bash_automatic_cd.sh b/examples/bash_automatic_cd.sh
index 8d72c553..ac96ea12 100644
--- a/doc/examples/bash_automatic_cd.sh
+++ b/examples/bash_automatic_cd.sh
@@ -1,4 +1,4 @@
-# Compatible with ranger 1.4.2 through 1.6.*
+# Compatible with ranger 1.4.2 through 1.7.*
 #
 # Automatically change the directory in bash after closing ranger
 #
@@ -8,7 +8,7 @@
 # original directory.
 
 function ranger-cd {
-    tempfile='/tmp/chosendir'
+    tempfile="$(mktemp)"
     /usr/bin/ranger --choosedir="$tempfile" "${@:-$(pwd)}"
     test -f "$tempfile" &&
     if [ "$(cat -- "$tempfile")" != "$(echo -n `pwd`)" ]; then
diff --git a/doc/examples/bash_subshell_notice.sh b/examples/bash_subshell_notice.sh
index bc44d5a8..4c9269c4 100644
--- a/doc/examples/bash_subshell_notice.sh
+++ b/examples/bash_subshell_notice.sh
@@ -1,4 +1,4 @@
-# Compatible with ranger 1.5.3 through 1.6.*
+# Compatible with ranger 1.5.3 through 1.7.*
 #
 # Change the prompt when you open a shell from inside ranger
 #
diff --git a/doc/examples/plugin_chmod_keybindings.py b/examples/plugin_chmod_keybindings.py
index 0ab975ed..1c9558f7 100644
--- a/doc/examples/plugin_chmod_keybindings.py
+++ b/examples/plugin_chmod_keybindings.py
@@ -1,4 +1,4 @@
-# Compatible with ranger 1.6.*
+# Compatible with ranger 1.6.0 through ranger 1.7.*
 #
 # This plugin serves as an example for adding key bindings through a plugin.
 # It could replace the ten lines in the rc.conf that create the key bindings
diff --git a/doc/examples/plugin_file_filter.py b/examples/plugin_file_filter.py
index b9bea1f3..d5ea2d2d 100644
--- a/doc/examples/plugin_file_filter.py
+++ b/examples/plugin_file_filter.py
@@ -1,4 +1,4 @@
-# Compatible since ranger 1.6.1, git commit c82a8a76989c
+# Compatible since ranger 1.7.0 (git commit c82a8a76989c)
 #
 # This plugin hides the directories "/boot", "/sbin", "/proc" and "/sys" unless
 # the "show_hidden" option is activated.
diff --git a/doc/examples/plugin_hello_world.py b/examples/plugin_hello_world.py
index a803e21b..b64916d4 100644
--- a/doc/examples/plugin_hello_world.py
+++ b/examples/plugin_hello_world.py
@@ -1,4 +1,4 @@
-# Compatible with ranger 1.6.*
+# Compatible with ranger 1.6.0 through 1.7.*
 #
 # This is a sample plugin that displays "Hello World" in ranger's console after
 # it started.
diff --git a/doc/examples/plugin_new_macro.py b/examples/plugin_new_macro.py
index 159a92f2..6757e491 100644
--- a/doc/examples/plugin_new_macro.py
+++ b/examples/plugin_new_macro.py
@@ -1,4 +1,4 @@
-# Compatible with ranger 1.6.*
+# Compatible with ranger 1.6.0 through 1.7.*
 #
 # This plugin adds the new macro %date which is substituted with the current
 # date in commands that allow macros.  You can test it with the command
diff --git a/doc/examples/plugin_new_sorting_method.py b/examples/plugin_new_sorting_method.py
index 6b41b0e1..c6e35a68 100644
--- a/doc/examples/plugin_new_sorting_method.py
+++ b/examples/plugin_new_sorting_method.py
@@ -1,4 +1,4 @@
-# Compatible with ranger 1.6.*
+# Compatible with ranger 1.6.0 through 1.7.*
 #
 # This plugin adds the sorting algorithm called 'random'.  To enable it, type
 # ":set sort=random" or create a key binding with ":map oz set sort=random"
diff --git a/doc/examples/rifle_different_file_opener.conf b/examples/rifle_different_file_opener.conf
index 4a8250b8..695f27c6 100644
--- a/doc/examples/rifle_different_file_opener.conf
+++ b/examples/rifle_different_file_opener.conf
@@ -1,4 +1,4 @@
-# Compatible with ranger 1.6.*
+# Compatible with ranger 1.6.0 through 1.7.*
 #
 # Replace your rifle.conf with this file to use xdg-open as your file opener.
 # This is, of course, adaptable for use with any other file opener.
diff --git a/doc/examples/rifle_sxiv.sh b/examples/rifle_sxiv.sh
index 6307f1c2..8cb13907 100755
--- a/doc/examples/rifle_sxiv.sh
+++ b/examples/rifle_sxiv.sh
@@ -1,5 +1,5 @@
 #!/bin/sh
-# Compatible with ranger 1.6.*
+# Compatible with ranger 1.6.0 through 1.7.*
 #
 # This script searches image files in a directory, opens them all with sxiv and
 # sets the first argument to the first image displayed by sxiv.
diff --git a/doc/examples/vim_file_chooser.vim b/examples/vim_file_chooser.vim
index aa3af763..fb9b7e1b 100644
--- a/doc/examples/vim_file_chooser.vim
+++ b/examples/vim_file_chooser.vim
@@ -1,4 +1,4 @@
-" Compatible with ranger 1.4.2 through 1.6.*
+" Compatible with ranger 1.4.2 through 1.7.*
 "
 " Add ranger as a file chooser in vim
 "
diff --git a/ranger.py b/ranger.py
index 4b2e7daa..1d7e42e1 100755
--- a/ranger.py
+++ b/ranger.py
@@ -9,7 +9,7 @@
 # default is simply "ranger". (Not this file itself!)
 # The other arguments are passed to ranger.
 """":
-tempfile='/tmp/chosendir'
+tempfile="$(mktemp)"
 ranger="${1:-ranger}"
 test -z "$1" || shift
 "$ranger" --choosedir="$tempfile" "${@:-$(pwd)}"
@@ -17,8 +17,8 @@ returnvalue=$?
 test -f "$tempfile" &&
 if [ "$(cat -- "$tempfile")" != "$(echo -n `pwd`)" ]; then
     cd "$(cat "$tempfile")"
-    rm -f -- "$tempfile"
 fi
+rm -f -- "$tempfile"
 return $returnvalue
 """ and None
 
diff --git a/ranger/__init__.py b/ranger/__init__.py
index b850f455..fa9ba033 100644
--- a/ranger/__init__.py
+++ b/ranger/__init__.py
@@ -13,7 +13,7 @@ import os
 
 # Information
 __license__ = 'GPL3'
-__version__ = '1.6.1'
+__version__ = '1.7.0'
 __author__ = __maintainer__ = 'Roman Zimbelmann'
 __email__ = 'hut@hut.pm'
 
@@ -27,7 +27,7 @@ DEFAULT_PAGER = 'less'
 LOGFILE = '/tmp/ranger_errorlog'
 CACHEDIR = os.path.expanduser("~/.cache/ranger")
 USAGE = '%prog [options] [path]'
-VERSION = 'ranger-master %s\n\nPython %s' % (__version__, sys.version)
+VERSION = 'ranger-stable %s\n\nPython %s' % (__version__, sys.version)
 
 # If the environment variable XDG_CONFIG_HOME is non-empty, CONFDIR is ignored
 # and the configuration directory will be $XDG_CONFIG_HOME/ranger instead.
diff --git a/ranger/api/commands.py b/ranger/api/commands.py
index cf8d28a7..2cf96a9f 100644
--- a/ranger/api/commands.py
+++ b/ranger/api/commands.py
@@ -12,9 +12,6 @@ from ranger.core.shared import FileManagerAware
 from ranger.ext.lazy_property import lazy_property
 
 _SETTINGS_RE = re.compile(r'^\s*([^\s]+?)=(.*)$')
-DELETE_WARNING = 'delete seriously? '  # COMPAT
-
-def alias(*_): pass # COMPAT
 
 class CommandContainer(object):
     def __init__(self):
diff --git a/ranger/config/commands.py b/ranger/config/commands.py
index a1bc7a76..6cdd5cc2 100644
--- a/ranger/config/commands.py
+++ b/ranger/config/commands.py
@@ -435,7 +435,8 @@ class default_linemode(Command):
     def tab(self):
         mode = self.arg(1)
         return (self.arg(0) + " " + linemode
-                for linemode in self.fm.thisfile.linemode_dict.keys())
+                for linemode in self.fm.thisfile.linemode_dict.keys()
+                if linemode.startswith(self.arg(1)))
 
 
 class quit(Command):
@@ -523,10 +524,16 @@ class delete(Command):
                 self._question_callback, ('n', 'N', 'y', 'Y'))
         else:
             # no need for a confirmation, just delete
+            for f in self.fm.tags.tags:
+                if str(f).startswith(self.fm.thisfile.path):
+                    self.fm.tags.remove(f)
             self.fm.delete()
 
     def _question_callback(self, answer):
         if answer == 'y' or answer == 'Y':
+            for f in self.fm.tags.tags:
+                if str(f).startswith(self.fm.thisfile.path):
+                    self.fm.tags.remove(f)
             self.fm.delete()
 
 
@@ -725,6 +732,13 @@ class rename(Command):
 
         new_name = self.rest(1)
 
+        tagged = {}
+        old_name = self.fm.thisfile.basename
+        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)
 
@@ -738,6 +752,9 @@ class rename(Command):
             f = File(new_name)
             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):
         return self._tab_directory_content()
@@ -830,22 +847,47 @@ class bulkrename(Command):
             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(b"# This file will be executed when you close the editor.\n")
+        script_lines.append(b"# 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>
@@ -1429,7 +1471,11 @@ class meta(prompt_metadata):
         key = self.arg(1)
         metadata = self.fm.metadata.get_metadata(self.fm.thisfile.path)
         if key in metadata and metadata[key]:
-            return self.arg(0) + " " + metadata[key]
+            return [" ".join([self.arg(0), self.arg(1), metadata[key]])]
+        else:
+            return [self.arg(0) + " " + key for key in sorted(metadata)
+                    if key.startswith(self.arg(1))]
+
 
 class linemode(default_linemode):
     """
diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf
index 29ccdeb8..18dc8ed4 100644
--- a/ranger/config/rc.conf
+++ b/ranger/config/rc.conf
@@ -237,11 +237,12 @@ map <C-x>W display_log
 map <C-x>w taskview_open
 
 map <A-x>  console
-map <A-!>  console shell 
-map <A-f> chain draw_possible_programs; console open_with 
+map <A-!>  console shell%space
+map <A-f> chain draw_possible_programs; console open_with%space
 
 # Change the line mode
 map <C-x>mf linemode filename
+map <C-x>mi linemode fileinfo
 map <C-x>mp linemode permissions
 map <C-x>mt linemode metatitle
 
@@ -257,7 +258,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
 
@@ -272,7 +273,7 @@ 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 <INSERT>   console touch%space
 
 copymap <UP>       <C-p>
 copymap <DOWN>     <C-n>
@@ -315,7 +316,7 @@ map <C-x>wn shell -f echo -n %f    | xsel -i; xsel -o | xsel -i -b
 # Filesystem Operations
 map <C-x>= chmod
 
-map <A-d>  console rename 
+map <A-d>  console rename%space
 map <C-e>  eval fm.open_console('rename ' + fm.thisfile.basename)
 map <C-a>  eval fm.open_console('rename ' + fm.thisfile.basename, position=7)
 
@@ -336,7 +337,7 @@ map <C-x><A-w>a copy mode=add
 map <C-x><A-w>r copy mode=remove
 
 # Searching
-map <C-x>s console search_inc 
+map <C-x>s console search_inc%space
 map <C-s> search_next
 map <C-r> search_next forward=False
 
@@ -392,7 +393,7 @@ map <C-x>zP    toggle_option preview_directories
 map <C-x>zs    toggle_option sort_case_insensitive
 map <C-x>zu    toggle_option autoupdate_cumulative_size
 map <C-x>zv    toggle_option use_preview_script
-map <C-x>zf    console filter 
+map <C-x>zf    console filter%space
 
 # Bookmarks
 map <C-x>rb<any> enter_bookmark %any
diff --git a/ranger/config/rifle.conf b/ranger/config/rifle.conf
index 20727458..0dcc41b4 100644
--- a/ranger/config/rifle.conf
+++ b/ranger/config/rifle.conf
@@ -132,17 +132,6 @@ mime ^video, terminal, !X, has mplayer2  = mplayer2 -- "$@"
 mime ^video, terminal, !X, has mplayer   = mplayer -- "$@"
 
 #-------------------------------------------
-# Image Viewing:
-#-------------------------------------------
-mime ^image, has sxiv,   X, flag f = sxiv -- "$@"
-mime ^image, has feh,    X, flag f = feh -- "$@"
-mime ^image, has mirage, X, flag f = mirage -- "$@"
-mime ^image, has eog,    X, flag f = eog -- "$@"
-mime ^image, has eom,    X, flag f = eom -- "$@"
-mime ^image, has gimp,   X, flag f = gimp -- "$@"
-ext xcf,                 X, flag f = gimp -- "$@"
-
-#-------------------------------------------
 # Documents
 #-------------------------------------------
 ext pdf, has llpp,     X, flag f = llpp "$@"
@@ -168,6 +157,17 @@ ext djvu, has evince, X, flag f = evince -- "$@"
 ext djvu, has atril,  X, flag f = atril -- "$@"
 
 #-------------------------------------------
+# Image Viewing:
+#-------------------------------------------
+mime ^image, has sxiv,   X, flag f = sxiv -- "$@"
+mime ^image, has feh,    X, flag f = feh -- "$@"
+mime ^image, has mirage, X, flag f = mirage -- "$@"
+mime ^image, has eog,    X, flag f = eog -- "$@"
+mime ^image, has eom,    X, flag f = eom -- "$@"
+mime ^image, has gimp,   X, flag f = gimp -- "$@"
+ext xcf,                 X, flag f = gimp -- "$@"
+
+#-------------------------------------------
 # Archives
 #-------------------------------------------
 # This requires atool
diff --git a/ranger/container/fsobject.py b/ranger/container/fsobject.py
index 131e2304..1bf08e20 100644
--- a/ranger/container/fsobject.py
+++ b/ranger/container/fsobject.py
@@ -86,7 +86,7 @@ class FileSystemObject(FileManagerAware, SettingsAware):
     _linemode = DEFAULT_LINEMODE
     linemode_dict = dict(
         (linemode.name, linemode()) for linemode in
-        [DefaultLinemode, TitleLinemode, PermissionsLinemode]
+        [DefaultLinemode, TitleLinemode, PermissionsLinemode, FileInfoLinemode]
     )
 
     def __init__(self, path, preload=None, path_is_abs=False, basename_is_rel_to=None):
diff --git a/ranger/container/tags.py b/ranger/container/tags.py
index 7ecef603..098ae9e3 100644
--- a/ranger/container/tags.py
+++ b/ranger/container/tags.py
@@ -109,3 +109,40 @@ class Tags(object):
     def __nonzero__(self):
         return True
     __bool__ = __nonzero__
+
+
+class TagsDummy(Tags):
+    """A dummy Tags class for use with `ranger --clean`.
+
+    It acts like there are no tags and avoids writing any changes.
+    """
+
+    def __init__(self, filename):
+        self.tags = dict()
+
+    def __contains__(self, item):
+        return False
+
+    def add(self, *items, **others):
+        pass
+
+    def remove(self, *items, **others):
+        pass
+
+    def toggle(self, *items, **others):
+        pass
+
+    def marker(self, item):
+        return self.default_tag
+
+    def sync(self):
+        pass
+
+    def dump(self):
+        pass
+
+    def _compile(self, f):
+        pass
+
+    def _parse(self, f):
+        pass
diff --git a/ranger/core/actions.py b/ranger/core/actions.py
index c7bdcfec..58f7aa20 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -21,8 +21,7 @@ from ranger.ext.keybinding_parser import key_to_string, construct_keybinding
 from ranger.ext.shell_escape import shell_quote
 from ranger.ext.next_available_filename import next_available_filename
 from ranger.ext.rifle import squash_flags, ASK_COMMAND
-from ranger.core.shared import FileManagerAware, EnvironmentAware, \
-        SettingsAware
+from ranger.core.shared import FileManagerAware, SettingsAware
 from ranger.core.tab import Tab
 from ranger.container.file import File
 from ranger.core.loader import CommandLoader, CopyLoader
@@ -36,7 +35,7 @@ class _MacroTemplate(string.Template):
     delimiter = ranger.MACRO_DELIMITER
     idpattern = r"[_a-z0-9]*"
 
-class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
+class Actions(FileManagerAware, SettingsAware):
     # --------------------------
     # -- Basic Commands
     # --------------------------
@@ -210,6 +209,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
         macros = {}
 
         macros['rangerdir'] = ranger.RANGERDIR
+        macros['space'] = ' '
 
         if self.fm.thisfile:
             macros['f'] = self.fm.thisfile.relative_path
@@ -306,13 +306,17 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
                             (line, str(e)), bad=True)
 
     def execute_file(self, files, **kw):
-        """Execute a file.
+        """Uses the "rifle" module to open/execute a file
 
-        app is the name of a method in Applications, without the "app_"
-        flags is a string consisting of runner.ALLOWED_FLAGS
-        mode is a positive integer.
-        Both flags and mode specify how the program is run."""
-        # TODO: docstring out of date
+        Arguments are the same as for ranger.ext.rifle.Rifle.execute:
+
+        files: a list of file objects (not strings!)
+        number: a number to select which way to open the file, in case there
+            are multiple choices
+        label: a string to select an opening method by its label
+        flags: a string specifying additional options, see `man rifle`
+        mimetyle: pass the mimetype to rifle, overriding its own guess
+        """
 
         mode = kw['mode'] if 'mode' in kw else 0
 
diff --git a/ranger/core/environment.py b/ranger/core/environment.py
deleted file mode 100644
index fddb8f9f..00000000
--- a/ranger/core/environment.py
+++ /dev/null
@@ -1,111 +0,0 @@
-# This file is part of ranger, the console file manager.
-# License: GNU GPL version 3, see the file "AUTHORS" for details.
-
-# THIS WHOLE FILE IS OBSOLETE AND EXISTS FOR BACKWARDS COMPATIBILITIY
-
-import os
-from ranger.ext.signals import SignalDispatcher
-from ranger.core.shared import SettingsAware, FileManagerAware
-
-# COMPAT
-class Environment(SettingsAware, FileManagerAware, SignalDispatcher):
-    def __init__(self, path):
-        SignalDispatcher.__init__(self)
-
-    def _get_copy(self): return self.fm.copy_buffer
-    def _set_copy(self, obj): self.fm.copy_buffer = obj
-    copy = property(_get_copy, _set_copy)
-
-    def _get_cut(self): return self.fm.do_cut
-    def _set_cut(self, obj): self.fm.do_cut = obj
-    cut = property(_get_cut, _set_cut)
-
-    def _get_keymaps(self): return self.fm.ui.keymaps
-    def _set_keymaps(self, obj): self.fm.ui.keymaps = obj
-    keymaps = property(_get_keymaps, _set_keymaps)
-
-    def _get_keybuffer(self): return self.fm.ui.keybuffer
-    def _set_keybuffer(self, obj): self.fm.ui.keybuffer = obj
-    keybuffer = property(_get_keybuffer, _set_keybuffer)
-
-    def _get_username(self): return self.fm.username
-    def _set_username(self, obj): self.fm.username = obj
-    username = property(_get_username, _set_username)
-
-    def _get_hostname(self): return self.fm.hostname
-    def _set_hostname(self, obj): self.fm.hostname = obj
-    hostname = property(_get_hostname, _set_hostname)
-
-    def _get_home_path(self): return self.fm.home_path
-    def _set_home_path(self, obj): self.fm.home_path = obj
-    home_path = property(_get_home_path, _set_home_path)
-
-    def _get_get_directory(self): return self.fm.get_directory
-    def _set_get_directory(self, obj): self.fm.get_directory = obj
-    get_directory = property(_get_get_directory, _set_get_directory)
-
-    def _get_garbage_collect(self): return self.fm.garbage_collect
-    def _set_garbage_collect(self, obj): self.fm.garbage_collect = obj
-    garbage_collect = property(_get_garbage_collect, _set_garbage_collect)
-
-    def _get_cwd(self): return self.fm.thisdir
-    def _set_cwd(self, obj): self.fm.thisdir = obj
-    cwd = property(_get_cwd, _set_cwd)
-
-    def _get_cf(self): return self.fm.thisfile
-    def _set_cf(self, obj): self.fm.thisfile = obj
-    cf = property(_get_cf, _set_cf)
-
-    def _get_history(self): return self.fm.thistab.history
-    def _set_history(self, obj): self.fm.thistab.history = obj
-    history = property(_get_history, _set_history)
-
-    def _get_last_search(self): return self.fm.thistab.last_search
-    def _set_last_search(self, obj): self.fm.thistab.last_search = obj
-    last_search = property(_get_last_search, _set_last_search)
-
-    def _get_path(self): return self.fm.thistab.path
-    def _set_path(self, obj): self.fm.thistab.path = obj
-    path = property(_get_path, _set_path)
-
-    def _get_pathway(self): return self.fm.thistab.pathway
-    def _set_pathway(self, obj): self.fm.thistab.pathway = obj
-    pathway = property(_get_pathway, _set_pathway)
-
-    def _get_enter_dir(self): return self.fm.thistab.enter_dir
-    def _set_enter_dir(self, obj): self.fm.thistab.enter_dir = obj
-    enter_dir = property(_get_enter_dir, _set_enter_dir)
-
-    def _get_at_level(self): return self.fm.thistab.at_level
-    def _set_at_level(self, obj): self.fm.thistab.at_level = obj
-    at_level = property(_get_at_level, _set_at_level)
-
-    def _get_get_selection(self): return self.fm.thistab.get_selection
-    def _set_get_selection(self, obj): self.fm.thistab.get_selection = obj
-    get_selection = property(_get_get_selection, _set_get_selection)
-
-    def _get_assign_cursor_positions_for_subdirs(self):
-        return self.fm.thistab.assign_cursor_positions_for_subdirs
-    def _set_assign_cursor_positions_for_subdirs(self, obj):
-        self.fm.thistab.assign_cursor_positions_for_subdirs = obj
-    assign_cursor_positions_for_subdirs = property(
-            _get_assign_cursor_positions_for_subdirs,
-            _set_assign_cursor_positions_for_subdirs)
-
-    def _get_ensure_correct_pointer(self):
-        return self.fm.thistab.ensure_correct_pointer
-    def _set_ensure_correct_pointer(self, obj):
-        self.fm.thistab.ensure_correct_pointer = obj
-    ensure_correct_pointer = property(_get_ensure_correct_pointer,
-            _set_ensure_correct_pointer)
-
-    def _get_history_go(self): return self.fm.thistab.history_go
-    def _set_history_go(self, obj): self.fm.thistab.history_go = obj
-    history_go = property(_get_history_go, _set_history_go)
-
-    def _set_cf_from_signal(self, signal):
-        self.fm._cf = signal.new
-
-    def get_free_space(self, path):
-        stat = os.statvfs(path)
-        return stat.f_bavail * stat.f_frsize
diff --git a/ranger/core/fm.py b/ranger/core/fm.py
index c95ec905..0ba0f546 100644
--- a/ranger/core/fm.py
+++ b/ranger/core/fm.py
@@ -15,7 +15,7 @@ import sys
 import ranger.api
 from ranger.core.actions import Actions
 from ranger.core.tab import Tab
-from ranger.container.tags import Tags
+from ranger.container.tags import Tags, TagsDummy
 from ranger.gui.ui import UI
 from ranger.container.bookmarks import Bookmarks
 from ranger.core.runner import Runner
@@ -100,6 +100,8 @@ class FM(Actions, SignalDispatcher):
 
         if not ranger.arg.clean and self.tags is None:
             self.tags = Tags(self.confpath('tagged'))
+        elif ranger.arg.clean:
+            self.tags = TagsDummy("")
 
         if self.bookmarks is None:
             if ranger.arg.clean:
diff --git a/ranger/core/linemode.py b/ranger/core/linemode.py
index 7993af82..56fd3522 100644
--- a/ranger/core/linemode.py
+++ b/ranger/core/linemode.py
@@ -3,6 +3,7 @@
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
 # Author: Wojciech Siewierski <wojciech.siewierski@onet.pl>, 2015
 
+import sys
 from abc import *
 
 DEFAULT_LINEMODE = "filename"
@@ -84,3 +85,20 @@ class PermissionsLinemode(LinemodeBase):
 
     def infostring(self, file, metadata):
         return ""
+
+
+class FileInfoLinemode(LinemodeBase):
+    name = "fileinfo"
+
+    def filetitle(self, file, metadata):
+        return file.relative_path
+
+    def infostring(self, file, metadata):
+        if not file.is_directory:
+            from subprocess import check_output
+            fileinfo = check_output(["file", "-bL", file.path]).strip()
+            if sys.version_info[0] >= 3:
+                fileinfo = fileinfo.decode("utf-8")
+            return fileinfo
+        else:
+            raise NotImplementedError
diff --git a/ranger/core/loader.py b/ranger/core/loader.py
index 86a591f0..8eda544f 100644
--- a/ranger/core/loader.py
+++ b/ranger/core/loader.py
@@ -85,6 +85,13 @@ class CopyLoader(Loadable, FileManagerAware):
                 else:
                     self.description = "moving files from: " + self.one_file.dirname
                 for f in self.copy_buffer:
+                    for tf in self.fm.tags.tags:
+                        if tf == f.path or str(tf).startswith(f.path):
+                            tag = self.fm.tags.tags[tf]
+                            self.fm.tags.remove(tf)
+                            self.fm.tags.tags[tf.replace(f.path, self.original_path \
+                                    + '/' + f.basename)] = tag
+                            self.fm.tags.dump()
                     for _ in shutil_g.move(src=f.path,
                             dst=self.original_path,
                             overwrite=self.overwrite):
diff --git a/ranger/core/shared.py b/ranger/core/shared.py
index ab76b511..177ba15d 100644
--- a/ranger/core/shared.py
+++ b/ranger/core/shared.py
@@ -16,10 +16,3 @@ class SettingsAware(object):
     @staticmethod
     def _setup(settings):
         SettingsAware.settings = settings
-
-class EnvironmentAware(object):  # COMPAT
-    """DO NOT USE.  This is for backward compatibility only."""
-    @lazy_property
-    def env(self):
-        from ranger.core.environment import Environment
-        return Environment(".")
diff --git a/ranger/ext/rifle.py b/ranger/ext/rifle.py
index ddbfed07..b75b38b2 100755
--- a/ranger/ext/rifle.py
+++ b/ranger/ext/rifle.py
@@ -19,7 +19,7 @@ import re
 from subprocess import Popen, PIPE
 import sys
 
-__version__ = 'rifle 1.6.1'
+__version__ = 'rifle 1.7.0'
 
 # Options and constants that a user might want to change:
 DEFAULT_PAGER = 'less'
diff --git a/ranger/fsobject.py b/ranger/fsobject.py
deleted file mode 100644
index 2edaaf79..00000000
--- a/ranger/fsobject.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# THIS WHOLE FILE IS OBSOLETE AND EXISTS FOR BACKWARDS COMPATIBILITIY
-
-from ranger.container.fsobject import FileSystemObject, BAD_INFO
-from ranger.container.file import File
-from ranger.container.directory import Directory
diff --git a/ranger/gui/context.py b/ranger/gui/context.py
index 2ad27434..e5aef06c 100644
--- a/ranger/gui/context.py
+++ b/ranger/gui/context.py
@@ -11,7 +11,7 @@ CONTEXT_KEYS = ['reset', 'error', 'badinfo',
         'good', 'bad',
         'space', 'permissions', 'owner', 'group', 'mtime', 'nlink',
         'scroll', 'all', 'bot', 'top', 'percentage', 'filter',
-        'marked', 'tagged', 'tag_marker', 'cut', 'copied',
+        'flat', 'marked', 'tagged', 'tag_marker', 'cut', 'copied',
         'help_markup', # COMPAT
         'seperator', 'key', 'special', 'border', # COMPAT
         'title', 'text', 'highlight', 'bars', 'quotes', 'tab', 'loaded',
diff --git a/ranger/gui/displayable.py b/ranger/gui/displayable.py
index 7e4290ee..d3adfe50 100644
--- a/ranger/gui/displayable.py
+++ b/ranger/gui/displayable.py
@@ -1,10 +1,10 @@
 # This file is part of ranger, the console file manager.
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
 
-from ranger.core.shared import FileManagerAware, EnvironmentAware
+from ranger.core.shared import FileManagerAware
 from ranger.gui.curses_shortcuts import CursesShortcuts
 
-class Displayable(EnvironmentAware, FileManagerAware, CursesShortcuts):
+class Displayable(FileManagerAware, CursesShortcuts):
     """Displayables are objects which are displayed on the screen.
 
     This is just the abstract class, defining basic operations
diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py
index 9ff331a0..f5824d99 100644
--- a/ranger/gui/widgets/statusbar.py
+++ b/ranger/gui/widgets/statusbar.py
@@ -242,8 +242,15 @@ class StatusBar(Widget):
         max_pos = len(target) - self.column.hei
         base = 'scroll'
 
+        right.add(" ", "space")
+
+        if self.fm.thisdir.flat:
+            right.add("flat=", base, 'flat')
+            right.add(str(self.fm.thisdir.flat), base, 'flat')
+            right.add(", ", "space")
+
         if self.fm.thisdir.filter:
-            right.add(" f=`", base, 'filter')
+            right.add("f=`", base, 'filter')
             right.add(self.fm.thisdir.filter.pattern, base, 'filter')
             right.add("', ", "space")
 
diff --git a/setup.py b/setup.py
index c646f3fb..1e074f8d 100755
--- a/setup.py
+++ b/setup.py
@@ -34,7 +34,7 @@ if __name__ == '__main__':
                 _findall('doc/config/colorschemes')),
             ('share/doc/ranger/config', _findall('doc/config')),
             ('share/doc/ranger/tools', _findall('doc/tools')),
-            ('share/doc/ranger/examples', _findall('doc/examples')),
+            ('share/doc/ranger/examples', _findall('examples')),
         ],
         package_data={'ranger': ['data/*', 'config/rc.conf',
             'config/rifle.conf']},