summary refs log tree commit diff stats
path: root/ranger
diff options
context:
space:
mode:
authorhut <hut@hut.pm>2018-05-15 22:07:54 +0200
committerGitHub <noreply@github.com>2018-05-15 22:07:54 +0200
commit3f210e8eef8332d87e4060965a2790510546978f (patch)
treeba17af8e2a60e5db8e9b47884705aa30cacbac3f /ranger
parent703a9a20a640ee53080c9b3c405ae55f652b70e5 (diff)
parent77471381f2f9350c87ed989ded1428712002329a (diff)
downloadranger-3f210e8eef8332d87e4060965a2790510546978f.tar.gz
Merge branch 'master' into patch-1
Diffstat (limited to 'ranger')
-rw-r--r--ranger/config/__init__.py2
-rwxr-xr-xranger/config/commands.py19
-rw-r--r--ranger/config/rc.conf21
-rw-r--r--ranger/config/rifle.conf3
-rw-r--r--ranger/container/settings.py1
-rw-r--r--ranger/core/actions.py21
-rw-r--r--ranger/core/fm.py4
-rw-r--r--ranger/core/main.py81
-rw-r--r--ranger/core/tab.py8
-rwxr-xr-xranger/data/scope.sh3
-rw-r--r--ranger/ext/human_readable.py4
-rwxr-xr-xranger/ext/rifle.py8
-rw-r--r--ranger/gui/ui.py6
-rw-r--r--ranger/gui/widgets/statusbar.py15
14 files changed, 147 insertions, 49 deletions
diff --git a/ranger/config/__init__.py b/ranger/config/__init__.py
index 71df3cb3..0facbdf8 100644
--- a/ranger/config/__init__.py
+++ b/ranger/config/__init__.py
@@ -1 +1 @@
-"""Default options and configration files"""
+"""Default options and configuration files"""
diff --git a/ranger/config/commands.py b/ranger/config/commands.py
index a3837d8e..8d444dd6 100755
--- a/ranger/config/commands.py
+++ b/ranger/config/commands.py
@@ -3,8 +3,9 @@
 # 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.
+# NOTE: If you copied this file to /etc/ranger/commands_full.py or
+# ~/.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.
@@ -13,9 +14,14 @@
 # Note that additional commands are automatically generated from the methods
 # of the class ranger.core.actions.Actions.
 #
-# You can customize commands in the file ~/.config/ranger/commands.py.
-# It has the same syntax as this file.  In fact, you can just copy this
-# file there with `ranger --copy-config=commands' and make your modifications.
+# You can customize commands in the files /etc/ranger/commands.py (system-wide)
+# and ~/.config/ranger/commands.py (per user).
+# They have the same syntax as this file.  In fact, you can just copy this
+# file to ~/.config/ranger/commands_full.py with
+# `ranger --copy-config=commands_full' and make your modifications, don't
+# forget to rename it to commands.py.  You can also use
+# `ranger --copy-config=commands' to copy a short sample commands.py that
+# has everything you need to get started.
 # But make sure you update your configs when you update ranger.
 #
 # ===================================================================
@@ -120,9 +126,10 @@ class echo(Command):
 
 
 class cd(Command):
-    """:cd [-r] <dirname>
+    """:cd [-r] <path>
 
     The cd command changes the directory.
+    If the path is a file, selects that file.
     The command 'cd -' is equivalent to typing ``.
     Using the option "-r" will get you to the real path.
     """
diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf
index fe00c7c0..342c22d9 100644
--- a/ranger/config/rc.conf
+++ b/ranger/config/rc.conf
@@ -1,7 +1,8 @@
 # ===================================================================
 # 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.
+# To change them, it is recommended to create either /etc/ranger/rc.conf
+# (system-wide) or ~/.config/ranger/rc.conf (per user) and add your custom
+# commands there.
 #
 # If you copy this whole file there, you may want to set the environment
 # variable RANGER_LOAD_DEFAULT_RC to FALSE to avoid loading it twice.
@@ -137,6 +138,9 @@ set mouse_enabled true
 set display_size_in_main_column true
 set display_size_in_status_bar true
 
+# Display the free disk space in the status bar?
+set display_free_space_in_status_bar true
+
 # Display files tags in all columns or only in main column?
 set display_tags_in_all_columns true
 
@@ -144,7 +148,7 @@ set display_tags_in_all_columns true
 set update_title false
 
 # Set the title to "ranger" in the tmux program?
-set update_tmux_title false
+set update_tmux_title true
 
 # Shorten the title if it gets long?  The number defines how many
 # directories are displayed at once, 0 turns off this feature.
@@ -253,6 +257,10 @@ set wrap_scroll false
 # directories, files and symlinks respectively.
 set global_inode_type_filter
 
+# This setting allows to freeze the list of files to save I/O bandwidth.  It
+# should be 'false' during start-up, but you can toggle it by pressing F.
+set freeze_files false
+
 # ===================================================================
 # == Local Options
 # ===================================================================
@@ -274,8 +282,8 @@ alias qall  quitall
 alias qall! quitall!
 alias setl  setlocal
 
-alias filter     scout -prt
-alias find       scout -aeit
+alias filter     scout -prts
+alias find       scout -aets
 alias mark       scout -mr
 alias unmark     scout -Mr
 alias search     scout -rs
@@ -389,6 +397,7 @@ map gL cd -r %f
 map go cd /opt
 map gv cd /var
 map gm cd /media
+map gi eval fm.cd('/run/media/' + os.getenv('USER'))
 map gM cd /mnt
 map gs cd /srv
 map gp cd /tmp
@@ -507,6 +516,8 @@ map zc    set collapse_preview!
 map zd    set sort_directories_first!
 map zh    set show_hidden!
 map <C-h> set show_hidden!
+copymap <C-h> <backspace>
+copymap <backspace> <backspace2>
 map zI    set flushinput!
 map zi    set preview_images!
 map zm    set mouse_enabled!
diff --git a/ranger/config/rifle.conf b/ranger/config/rifle.conf
index e2653a76..b1a9bb71 100644
--- a/ranger/config/rifle.conf
+++ b/ranger/config/rifle.conf
@@ -151,7 +151,7 @@ ext pdf, has atril,    X, flag f = atril -- "$@"
 ext pdf, has okular,   X, flag f = okular -- "$@"
 ext pdf, has epdfview, X, flag f = epdfview -- "$@"
 ext pdf, has qpdfview, X, flag f = qpdfview "$@"
-ext pdf, has open,     X, flat f = open "$@"
+ext pdf, has open,     X, flag f = open "$@"
 
 ext docx?, has catdoc,       terminal = catdoc -- "$@" | "$PAGER"
 
@@ -183,6 +183,7 @@ mime ^image, has eog,       X, flag f = eog -- "$@"
 mime ^image, has eom,       X, flag f = eom -- "$@"
 mime ^image, has nomacs,    X, flag f = nomacs -- "$@"
 mime ^image, has geeqie,    X, flag f = geeqie -- "$@"
+mime ^image, has gwenview,  X, flag f = gwenview -- "$@"
 mime ^image, has gimp,      X, flag f = gimp -- "$@"
 ext xcf,                    X, flag f = gimp -- "$@"
 
diff --git a/ranger/container/settings.py b/ranger/container/settings.py
index 9f54f24d..170ace5a 100644
--- a/ranger/container/settings.py
+++ b/ranger/container/settings.py
@@ -38,6 +38,7 @@ ALLOWED_SETTINGS = {
     'dirname_in_tabs': bool,
     'display_size_in_main_column': bool,
     'display_size_in_status_bar': bool,
+    "display_free_space_in_status_bar": bool,
     'display_tags_in_all_columns': bool,
     'draw_borders': bool,
     'draw_progress_bar_in_status_bar': bool,
diff --git a/ranger/core/actions.py b/ranger/core/actions.py
index 6bbb35aa..3e488159 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -1186,10 +1186,10 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
     def tab_new(self, path=None, narg=None):
         if narg:
             return self.tab_open(narg, path)
-        for i in range(1, 10):
-            if i not in self.tabs:
-                return self.tab_open(i, path)
-        return None
+        i = 1
+        while i in self.tabs:
+            i += 1
+        return self.tab_open(i, path)
 
     def tab_switch(self, path, create_directory=False):
         """Switches to tab of given path, opening a new tab as necessary.
@@ -1237,7 +1237,18 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
 
     def get_tab_list(self):
         assert self.tabs, "There must be at least 1 tab at all times"
-        return sorted(self.tabs)
+
+        class NaturalOrder(object):  # pylint: disable=too-few-public-methods
+            def __init__(self, obj):
+                self.obj = obj
+
+            def __lt__(self, other):
+                try:
+                    return self.obj < other.obj
+                except TypeError:
+                    return str(self.obj) < str(other.obj)
+
+        return sorted(self.tabs, key=NaturalOrder)
 
     # --------------------------
     # -- Overview of internals
diff --git a/ranger/core/fm.py b/ranger/core/fm.py
index c55a3922..d85dd48c 100644
--- a/ranger/core/fm.py
+++ b/ranger/core/fm.py
@@ -424,5 +424,5 @@ class FM(Actions,  # pylint: disable=too-many-instance-attributes
             if not ranger.args.clean and self.settings.save_tabs_on_exit and len(self.tabs) > 1:
                 with open(self.datapath('tabs'), 'a') as fobj:
                     # Don't save active tab since launching ranger changes the active tab
-                    fobj.write('\0'.join(v.path for t, v in self.tabs.items()
-                                         if t != self.current_tab) + '\0\0')
+                    fobj.write('\0'.join(v.path for t, v in self.tabs.items()) +
+                               '\0\0')
diff --git a/ranger/core/main.py b/ranger/core/main.py
index 4adea918..ae7a691c 100644
--- a/ranger/core/main.py
+++ b/ranger/core/main.py
@@ -89,14 +89,12 @@ def main(
 
     SettingsAware.settings_set(Settings())
 
+    # TODO: deprecate --selectfile
     if args.selectfile:
         args.selectfile = os.path.abspath(args.selectfile)
         args.paths.insert(0, os.path.dirname(args.selectfile))
 
-    if args.paths:
-        paths = [p[7:] if p.startswith('file:///') else p for p in args.paths]
-    else:
-        paths = [os.environ.get('PWD', os.getcwd())]
+    paths = get_paths(args)
     paths_inaccessible = []
     for path in paths:
         try:
@@ -182,6 +180,7 @@ def main(
             fm.select_file(args.selectfile)
 
         if args.cmd:
+            fm.enter_dir(fm.thistab.path)
             for command in args.cmd:
                 fm.execute_console(command)
 
@@ -235,6 +234,24 @@ https://github.com/ranger/ranger/issues
         return exit_code  # pylint: disable=lost-exception
 
 
+def get_paths(args):
+    if args.paths:
+        prefix = 'file:///'
+        prefix_length = len(prefix)
+        paths = [path[prefix_length:] if path.startswith(prefix) else path for path in args.paths]
+    else:
+        start_directory = os.environ.get('PWD')
+        is_valid_start_directory = start_directory and os.path.exists(start_directory)
+        if not is_valid_start_directory:
+            start_directory = __get_home_directory()
+        paths = [start_directory]
+    return paths
+
+
+def __get_home_directory():
+    return os.path.expanduser('~')
+
+
 def xdg_path(env_var):
     path = os.environ.get(env_var)
     if path and os.path.isabs(path):
@@ -339,23 +356,50 @@ def load_settings(  # pylint: disable=too-many-locals,too-many-branches,too-many
     fm.commands.load_commands_from_module(commands_default)
 
     if not clean:
+        system_confdir = os.path.join(os.sep, 'etc', 'ranger')
+        if os.path.exists(system_confdir):
+            sys.path.append(system_confdir)
         allow_access_to_confdir(ranger.args.confdir, True)
 
         # Load custom commands
-        custom_comm_path = fm.confpath('commands.py')
-        if os.path.exists(custom_comm_path):
+        def import_file(name, path):  # From https://stackoverflow.com/a/67692
+            # pragma pylint: disable=no-name-in-module,import-error,no-member
+            if sys.version_info >= (3, 5):
+                import importlib.util as util
+                spec = util.spec_from_file_location(name, path)
+                module = util.module_from_spec(spec)
+                spec.loader.exec_module(module)
+            elif (3, 3) <= sys.version_info < (3, 5):
+                from importlib.machinery import SourceFileLoader
+                module = SourceFileLoader(name, path).load_module()
+            else:
+                import imp
+                module = imp.load_source(name, path)
+            # pragma pylint: enable=no-name-in-module,import-error,no-member
+            return module
+
+        def load_custom_commands(*paths):
             old_bytecode_setting = sys.dont_write_bytecode
             sys.dont_write_bytecode = True
-            try:
-                import commands as commands_custom
-                fm.commands.load_commands_from_module(commands_custom)
-            except ImportError as ex:
-                LOG.debug("Failed to import custom commands from '%s'", custom_comm_path)
-                LOG.exception(ex)
-            else:
-                LOG.debug("Loaded custom commands from '%s'", custom_comm_path)
+            for custom_comm_path in paths:
+                if os.path.exists(custom_comm_path):
+                    try:
+                        commands_custom = import_file('commands',
+                                                      custom_comm_path)
+                        fm.commands.load_commands_from_module(commands_custom)
+                    except ImportError as ex:
+                        LOG.debug("Failed to import custom commands from '%s'",
+                                  custom_comm_path)
+                        LOG.exception(ex)
+                    else:
+                        LOG.debug("Loaded custom commands from '%s'",
+                                  custom_comm_path)
             sys.dont_write_bytecode = old_bytecode_setting
 
+        system_comm_path = os.path.join(system_confdir, 'commands.py')
+        custom_comm_path = fm.confpath('commands.py')
+        load_custom_commands(system_comm_path, custom_comm_path)
+
         # XXX Load plugins (experimental)
         plugindir = fm.confpath('plugins')
         try:
@@ -394,12 +438,17 @@ def load_settings(  # pylint: disable=too-many-locals,too-many-branches,too-many
         allow_access_to_confdir(ranger.args.confdir, False)
         # Load rc.conf
         custom_conf = fm.confpath('rc.conf')
+        system_conf = os.path.join(system_confdir, 'rc.conf')
         default_conf = fm.relpath('config', 'rc.conf')
 
         custom_conf_is_readable = os.access(custom_conf, os.R_OK)
-        if (os.environ.get('RANGER_LOAD_DEFAULT_RC', 'TRUE').upper() != 'FALSE' or
-                not custom_conf_is_readable):
+        system_conf_is_readable = os.access(system_conf, os.R_OK)
+        if (os.environ.get('RANGER_LOAD_DEFAULT_RC', 'TRUE').upper() !=
+                'FALSE' or
+                not (custom_conf_is_readable or system_conf_is_readable)):
             fm.source(default_conf)
+        if system_conf_is_readable:
+            fm.source(system_conf)
         if custom_conf_is_readable:
             fm.source(custom_conf)
 
diff --git a/ranger/core/tab.py b/ranger/core/tab.py
index 1d5e69d4..7bb45d75 100644
--- a/ranger/core/tab.py
+++ b/ranger/core/tab.py
@@ -4,7 +4,7 @@
 from __future__ import (absolute_import, division, print_function)
 
 import os
-from os.path import abspath, normpath, join, expanduser, isdir
+from os.path import abspath, normpath, join, expanduser, isdir, dirname
 import sys
 
 from ranger.container import settings
@@ -123,9 +123,11 @@ class Tab(FileManagerAware, SettingsAware):  # pylint: disable=too-many-instance
 
         # get the absolute path
         path = normpath(join(self.path, expanduser(path)))
+        selectfile = None
 
         if not isdir(path):
-            return False
+            selectfile = path
+            path = dirname(path)
         new_thisdir = self.fm.get_directory(path)
 
         try:
@@ -155,6 +157,8 @@ class Tab(FileManagerAware, SettingsAware):  # pylint: disable=too-many-instance
         self.thisdir.sort_directories_first = self.fm.settings.sort_directories_first
         self.thisdir.sort_reverse = self.fm.settings.sort_reverse
         self.thisdir.sort_if_outdated()
+        if selectfile:
+            self.thisdir.move_to_obj(selectfile)
         if previous and previous.path != path:
             self.thisfile = self.thisdir.pointed_obj
         else:
diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh
index 540a910e..25251533 100755
--- a/ranger/data/scope.sh
+++ b/ranger/data/scope.sh
@@ -60,7 +60,8 @@ handle_extension() {
         # PDF
         pdf)
             # Preview as text conversion
-            pdftotext -l 10 -nopgbrk -q -- "${FILE_PATH}" - && exit 5
+            pdftotext -l 10 -nopgbrk -q -- "${FILE_PATH}" - | fmt -w ${PV_WIDTH} && exit 5
+            mutool draw -F txt -i -- "${FILE_PATH}" 1-10 | fmt -w ${PV_WIDTH} && exit 5
             exiftool "${FILE_PATH}" && exit 5
             exit 1;;
 
diff --git a/ranger/ext/human_readable.py b/ranger/ext/human_readable.py
index df74eabf..f365e594 100644
--- a/ranger/ext/human_readable.py
+++ b/ranger/ext/human_readable.py
@@ -15,6 +15,10 @@ def human_readable(byte, separator=' '):  # pylint: disable=too-many-return-stat
     '1023 M'
     """
 
+    # handle automatically_count_files false
+    if byte is None:
+        return ''
+
     # I know this can be written much shorter, but this long version
     # performs much better than what I had before.  If you attempt to
     # shorten this code, take performance into consideration.
diff --git a/ranger/ext/rifle.py b/ranger/ext/rifle.py
index 70215039..672b0597 100755
--- a/ranger/ext/rifle.py
+++ b/ranger/ext/rifle.py
@@ -261,6 +261,14 @@ class Rifle(object):  # pylint: disable=too-many-instance-attributes
             process = Popen(["file", "--mime-type", "-Lb", fname], stdout=PIPE, stderr=PIPE)
             mimetype, _ = process.communicate()
             self._mimetype = mimetype.decode(ENCODING).strip()
+            if self._mimetype == 'application/octet-stream':
+                try:
+                    process = Popen(["mimetype", "--output-format", "%m", fname],
+                                    stdout=PIPE, stderr=PIPE)
+                    mimetype, _ = process.communicate()
+                    self._mimetype = mimetype.decode(ENCODING).strip()
+                except OSError:
+                    pass
         return self._mimetype
 
     def _build_command(self, files, action, flags):
diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py
index 990db0ad..4f76dfab 100644
--- a/ranger/gui/ui.py
+++ b/ranger/gui/ui.py
@@ -113,7 +113,7 @@ class UI(  # pylint: disable=too-many-instance-attributes,too-many-public-method
             self._draw_title = curses.tigetflag('hs')  # has_status_line
 
             # Save tmux setting `automatic-rename`
-            if self.settings.update_tmux_title:
+            if self.settings.update_tmux_title and 'TMUX' in os.environ:
                 try:
                     self._tmux_automatic_rename = check_output(
                         ['tmux', 'show-window-options', '-v', 'automatic-rename']).strip()
@@ -123,7 +123,7 @@ class UI(  # pylint: disable=too-many-instance-attributes,too-many-public-method
         self.update_size()
         self.is_on = True
 
-        if self.settings.update_tmux_title:
+        if self.settings.update_tmux_title and 'TMUX' in os.environ:
             sys.stdout.write("\033kranger\033\\")
             sys.stdout.flush()
 
@@ -172,7 +172,7 @@ class UI(  # pylint: disable=too-many-instance-attributes,too-many-public-method
         DisplayableContainer.destroy(self)
 
         # Restore tmux setting `automatic-rename`
-        if self.settings.update_tmux_title:
+        if self.settings.update_tmux_title and 'TMUX' in os.environ:
             if self._tmux_automatic_rename:
                 try:
                     check_output(['tmux', 'set-window-option',
diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py
index 266d48ca..3457955e 100644
--- a/ranger/gui/widgets/statusbar.py
+++ b/ranger/gui/widgets/statusbar.py
@@ -275,13 +275,14 @@ class StatusBar(Widget):  # pylint: disable=too-many-instance-attributes
             right.add("/" + str(len(target.marked_items)))
         else:
             right.add(human_readable(target.disk_usage, separator='') + " sum")
-            try:
-                free = get_free_space(target.mount_path)
-            except OSError:
-                pass
-            else:
-                right.add(", ", "space")
-                right.add(human_readable(free, separator='') + " free")
+            if self.settings.display_free_space_in_status_bar:
+                try:
+                    free = get_free_space(target.mount_path)
+                except OSError:
+                    pass
+                else:
+                    right.add(", ", "space")
+                    right.add(human_readable(free, separator='') + " free")
         right.add("  ", "space")
 
         if target.marked_items: