about summary refs log tree commit diff
path: root/ranger/.config/ranger/commands_full.py
diff options
context:
space:
mode:
authorRaphael McSinyx <vn.mcsinyx@gmail.com>2017-08-06 00:36:31 +0700
committerRaphael McSinyx <vn.mcsinyx@gmail.com>2017-08-06 00:36:31 +0700
commitd721c7a5f9fa2c26e97e616542b516daced4dc08 (patch)
tree9ff3fdaef426a079c82fe5e3a972c2514936d4e8 /ranger/.config/ranger/commands_full.py
parent04bb23545ae2d7029ecfc4401ea94d89970dd89c (diff)
downloaddotfiles-d721c7a5f9fa2c26e97e616542b516daced4dc08.tar.gz
Update 2017-08-06
Diffstat (limited to 'ranger/.config/ranger/commands_full.py')
-rw-r--r--ranger/.config/ranger/commands_full.py356
1 files changed, 169 insertions, 187 deletions
diff --git a/ranger/.config/ranger/commands_full.py b/ranger/.config/ranger/commands_full.py
index cb62a62..a384f42 100644
--- a/ranger/.config/ranger/commands_full.py
+++ b/ranger/.config/ranger/commands_full.py
@@ -21,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
@@ -83,6 +85,7 @@
 
 from ranger.api.commands import *
 
+
 class alias(Command):
     """:alias <newcommand> <oldcommand>
 
@@ -98,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>
 
@@ -125,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
 
@@ -133,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('~'):
@@ -153,7 +166,7 @@ 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
@@ -182,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)
 
 
@@ -197,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)
@@ -215,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()
@@ -225,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):
@@ -328,26 +338,32 @@ 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 (self.firstpart + colorscheme for 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):
@@ -357,16 +373,17 @@ class set_(Command):
                 return self.firstpart + 'False'
         # Tab complete colorscheme values if incomplete value is present
         if name == "colorscheme":
-            return (self.firstpart + colorscheme for 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))
@@ -432,7 +449,7 @@ class default_linemode(Command):
             for col in self.fm.ui.browser.columns:
                 col.need_redraw = True
 
-    def tab(self):
+    def tab(self, tabnum):
         mode = self.arg(1)
         return (self.arg(0) + " " + linemode
                 for linemode in self.fm.thisfile.linemode_dict.keys()
@@ -476,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,
@@ -500,41 +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
-            for f in self.fm.tags.tags:
-                if str(f).startswith(self.fm.thisfile.path):
-                    self.fm.tags.remove(f)
-            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':
-            for f in self.fm.tags.tags:
-                if str(f).startswith(self.fm.thisfile.path):
-                    self.fm.tags.remove(f)
-            self.fm.delete()
+            self.fm.delete(files)
 
 
 class mark_tag(Command):
@@ -547,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:
@@ -572,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)
 
@@ -583,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()
@@ -604,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()
@@ -641,7 +663,7 @@ class mkdir(Command):
         else:
             self.fm.notify("file/directory exists!", bad=True)
 
-    def tab(self):
+    def tab(self, tabnum):
         return self._tab_directory_content()
 
 
@@ -660,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()
 
 
@@ -676,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()
 
 
@@ -733,7 +755,7 @@ class rename(Command):
         new_name = self.rest(1)
 
         tagged = {}
-        old_name = self.fm.thisfile.basename
+        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]
@@ -742,7 +764,7 @@ class rename(Command):
         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):
@@ -750,15 +772,24 @@ class rename(Command):
 
         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.tags[t.replace(old_name, new_name)] = tagged[t]
                 self.fm.tags.dump()
 
-    def tab(self):
+    def tab(self, tabnum):
         return self._tab_directory_content()
 
+
 class rename_append(Command):
     """:rename_append
 
@@ -767,10 +798,12 @@ class rename_append(Command):
 
     def execute(self):
         cf = self.fm.thisfile
-        if cf.basename.find('.') != 0 and cf.basename.rfind('.') != -1 and not cf.is_directory:
-            self.fm.open_console('rename ' + cf.basename, position=(7 + cf.basename.rfind('.')))
+        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 ' + cf.basename)
+            self.fm.open_console('rename ' + path)
+
 
 class chmod(Command):
     """:chmod <octal number>
@@ -807,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
 
 
@@ -852,7 +885,7 @@ class bulkrename(Command):
         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)) \
+        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:
@@ -889,6 +922,7 @@ class bulkrename(Command):
         else:
             fm.notify("files have not been retagged")
 
+
 class relink(Command):
     """:relink <newpath>
 
@@ -905,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
@@ -920,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()
 
@@ -933,6 +967,7 @@ class help_(Command):
     Display ranger's manual page.
     """
     name = 'help'
+
     def execute(self):
         def callback(answer):
             if answer == "q":
@@ -1090,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
@@ -1127,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:
@@ -1176,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:
@@ -1215,13 +1251,13 @@ class scout(Command):
             regex = "^(?:(?!%s).)*$" % regex
 
         # Compile Regular Expression
-        options = re.LOCALE | re.UNICODE
+        options = re.UNICODE
         if self.IGNORE_CASE in flags or self.SMART_CASE in flags and \
                 pattern.islower():
             options |= re.IGNORECASE
         try:
             self._regex = re.compile(regex, options)
-        except:
+        except Exception:
             self._regex = re.compile("")
         return self._regex
 
@@ -1230,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
@@ -1242,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))
@@ -1295,130 +1331,75 @@ class grep(Command):
             self.fm.execute_command(action, flags='p')
 
 
-# Version control commands
-# --------------------------------
-class stage(Command):
-    """
-    :stage
-
-    Stage selected files for the corresponding version control system
+class flat(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()
+    :flat <level>
 
+    Flattens the directory view up to the specified level.
 
-class unstage(Command):
+        -1 fully flattened
+         0 remove flattened view
     """
-    :unstage
 
-    Unstage selected files for the corresponding version control system
-    """
     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.")
+            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()
 
-        self.fm.reload_cwd()
+# Version control commands
+# --------------------------------
 
 
-class diff(Command):
+class stage(Command):
     """
-    :diff
+    :stage
 
-    Displays a diff of selected files against the last committed version
+    Stage selected files for the corresponding version control system
     """
     def execute(self):
         from ranger.ext.vcs import VcsError
-        import tempfile
-
-        L = self.fm.thistab.get_selection()
-        if len(L) == 0: return
-
-        filelist = [f.path for f in L]
-        vcs = L[0].vcs
-
-        diff = vcs.get_raw_diff(filelist=filelist)
-        if len(diff.strip()) > 0:
-            tmp = tempfile.NamedTemporaryFile()
-            tmp.write(diff.encode('utf-8'))
-            tmp.flush()
 
-            pager = os.environ.get('PAGER', ranger.DEFAULT_PAGER)
-            self.fm.run([pager, tmp.name])
+        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:
-            raise Exception("diff is empty")
+            self.fm.notify('Unable to stage files: Not in repository')
 
 
-class log(Command):
+class unstage(Command):
     """
-    :log
+    :unstage
 
-    Displays the log of the current repo or files
+    Unstage selected files for the corresponding version control system
     """
     def execute(self):
         from ranger.ext.vcs import VcsError
-        import tempfile
-
-        L = self.fm.thistab.get_selection()
-        if len(L) == 0: return
-
-        filelist = [f.path for f in L]
-        vcs = L[0].vcs
-
-        log = vcs.get_raw_log(filelist=filelist)
-        tmp = tempfile.NamedTemporaryFile()
-        tmp.write(log.encode('utf-8'))
-        tmp.flush()
-
-        pager = os.environ.get('PAGER', ranger.DEFAULT_PAGER)
-        self.fm.run([pager, tmp.name])
-
-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()
 
+        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')
 
 # Metadata commands
 # --------------------------------
 
+
 class prompt_metadata(Command):
     """
     :prompt_metadata <key1> [<key2> [<key3> ...]]
@@ -1428,6 +1409,7 @@ class prompt_metadata(Command):
 
     _command_name = "meta"
     _console_chain = None
+
     def execute(self):
         prompt_metadata._console_chain = self.args[1:]
         self._process_command_stack()
@@ -1467,7 +1449,7 @@ class meta(prompt_metadata):
             self.fm.metadata.set_metadata(f.path, update_dict)
         self._process_command_stack()
 
-    def tab(self):
+    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]: