summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.pylintrc2
-rw-r--r--Makefile2
-rw-r--r--README.md8
-rw-r--r--doc/ranger.16
-rw-r--r--doc/rifle.14
-rwxr-xr-xexamples/rifle_sxiv.sh2
-rw-r--r--ranger/__init__.py2
-rw-r--r--ranger/api/commands.py12
-rwxr-xr-xranger/config/commands.py15
-rw-r--r--ranger/config/rc.conf4
-rw-r--r--ranger/config/rifle.conf1
-rw-r--r--ranger/container/directory.py1
-rw-r--r--ranger/container/file.py1
-rw-r--r--ranger/container/tags.py2
-rw-r--r--ranger/core/actions.py12
-rw-r--r--ranger/core/fm.py3
-rw-r--r--ranger/core/metadata.py39
-rw-r--r--ranger/core/tab.py2
-rwxr-xr-xranger/data/scope.sh20
-rw-r--r--ranger/ext/accumulator.py4
-rw-r--r--ranger/ext/img_display.py60
-rw-r--r--ranger/ext/next_available_filename.py1
-rwxr-xr-xranger/ext/rifle.py5
-rw-r--r--ranger/ext/signals.py6
-rw-r--r--ranger/ext/vcs/vcs.py7
-rw-r--r--ranger/ext/widestring.py4
-rw-r--r--ranger/gui/bar.py1
-rw-r--r--ranger/gui/ui.py3
-rw-r--r--ranger/gui/widgets/console.py20
-rw-r--r--ranger/gui/widgets/taskview.py2
30 files changed, 187 insertions, 64 deletions
diff --git a/.pylintrc b/.pylintrc
index d108c93f..75bb7baf 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -8,7 +8,7 @@ max-branches=16
 
 [FORMAT]
 max-line-length = 99
-disable=locally-disabled,locally-enabled,missing-docstring,duplicate-code,fixme,cyclic-import,redefined-variable-type
+disable=locally-disabled,locally-enabled,missing-docstring,duplicate-code,fixme,cyclic-import,redefined-variable-type,stop-iteration-return
 
 [TYPECHECK]
 ignored-classes=ranger.core.actions.Actions
diff --git a/Makefile b/Makefile
index a2fe08cc..99d3f53e 100644
--- a/Makefile
+++ b/Makefile
@@ -95,7 +95,7 @@ test_doctest:
 	done
 
 test_pytest:
-	echo "Running py.test tests..."
+	@echo "Running py.test tests..."
 	py.test tests
 
 test: test_pylint test_flake8 test_doctest test_pytest
diff --git a/README.md b/README.md
index edbc2c4d..071b51d6 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-ranger 1.9.0b5
+ranger 1.9.0b6
 ==============
 
 [![Build Status](https://travis-ci.org/ranger/ranger.svg?branch=master)](https://travis-ci.org/ranger/ranger)
@@ -108,6 +108,6 @@ current file.  The second is the main column and the first shows the parent
 directory.
 
 Ranger can automatically copy default configuration files to `~/.config/ranger`
-if you run it with the switch `--copy-config`. See `ranger --help` for a
-description of that switch.  Also check `ranger/config/` for the default
-configuration.
+if you run it with the switch `--copy-config=( rc | scope | ... | all )`.
+See `ranger --help` for a description of that switch.  Also check
+`ranger/config/` for the default configuration.
diff --git a/doc/ranger.1 b/doc/ranger.1
index cd00cd95..bb7114a2 100644
--- a/doc/ranger.1
+++ b/doc/ranger.1
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 4.09 (Pod::Simple 3.35)
+.\" Automatically generated by Pod::Man 4.07 (Pod::Simple 3.32)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -129,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "RANGER 1"
-.TH RANGER 1 "ranger-1.9.0b5" "10/22/2017" "ranger manual"
+.TH RANGER 1 "ranger-1.9.0b6" "12/07/2017" "ranger manual"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -599,7 +599,7 @@ Toggle the mark-status of all files
 .IP "V" 14
 .IX Item "V"
 Starts the visual mode, which selects all files between the starting point and
-the cursor until you press \s-1ESC.\s0  To unselect files in the same way, use \*(L"uV\*(R".
+the cursor until you press \s-1ESC. \s0 To unselect files in the same way, use \*(L"uV\*(R".
 .IP "/" 14
 Search for files in the current directory.
 .IP ":" 14
diff --git a/doc/rifle.1 b/doc/rifle.1
index 9d9e08f9..8531d69c 100644
--- a/doc/rifle.1
+++ b/doc/rifle.1
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 4.09 (Pod::Simple 3.35)
+.\" Automatically generated by Pod::Man 4.07 (Pod::Simple 3.32)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -129,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "RIFLE 1"
-.TH RIFLE 1 "rifle-1.9.0b5" "10/22/2017" "rifle manual"
+.TH RIFLE 1 "rifle-1.9.0b6" "12/07/2017" "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/examples/rifle_sxiv.sh b/examples/rifle_sxiv.sh
index 8cb13907..0bdd892d 100755
--- a/examples/rifle_sxiv.sh
+++ b/examples/rifle_sxiv.sh
@@ -24,7 +24,7 @@ if [ $# -eq 0 ]; then
     exit
 fi
 
-[ "$1" == '--' ] && shift
+[ "$1" = '--' ] && shift
 
 abspath () {
     case "$1" in
diff --git a/ranger/__init__.py b/ranger/__init__.py
index d98a2bf5..f3495197 100644
--- a/ranger/__init__.py
+++ b/ranger/__init__.py
@@ -14,7 +14,7 @@ import os
 
 # Information
 __license__ = 'GPL3'
-__version__ = '1.9.0b5'
+__version__ = '1.9.0b6'
 __author__ = __maintainer__ = 'Roman Zimbelmann'
 __email__ = 'hut@hut.pm'
 
diff --git a/ranger/api/commands.py b/ranger/api/commands.py
index 24a82d0b..9c687927 100644
--- a/ranger/api/commands.py
+++ b/ranger/api/commands.py
@@ -54,8 +54,8 @@ class CommandContainer(FileManagerAware):
         cmd_cls = self.get_command(cmd_name)
         if cmd_cls is None:
             self.fm.notify('alias failed: No such command: {0}'.format(cmd_name), bad=True)
-            return None
-        self.commands[name] = _command_init(command_alias_factory(name, cmd_cls, full_command))
+        else:
+            self.commands[name] = _command_init(command_alias_factory(name, cmd_cls, full_command))
 
     def load_commands_from_module(self, module):
         for var in vars(module).values():
@@ -293,7 +293,7 @@ class Command(FileManagerAware):
 
             # no results, return None
             if not dirnames:
-                return
+                return None
 
             # one result. since it must be a directory, append a slash.
             if len(dirnames) == 1:
@@ -357,7 +357,7 @@ class Command(FileManagerAware):
         else:
             # no results, return None
             if not names:
-                return
+                return None
 
             # one result. append a slash if it's a directory
             if len(names) == 1:
@@ -374,7 +374,7 @@ class Command(FileManagerAware):
         programs = [program for program in get_executables() if
                     program.startswith(self.rest(1))]
         if not programs:
-            return
+            return None
         if len(programs) == 1:
             return self.start(1) + programs[0]
         programs.sort()
@@ -397,7 +397,7 @@ def command_function_factory(func):
 
         def execute(self):  # pylint: disable=too-many-branches
             if not func:
-                return
+                return None
             if len(self.args) == 1:
                 try:
                     return func(**{'narg': self.quantifier})
diff --git a/ranger/config/commands.py b/ranger/config/commands.py
index de3170bb..299f17c2 100755
--- a/ranger/config/commands.py
+++ b/ranger/config/commands.py
@@ -219,6 +219,8 @@ class cd(Command):
                 return matches
             paths = matches
 
+        return None
+
     def _tab_smart(self, dest, dest_abs):
         tokens = []
         basepath = dest_abs
@@ -457,6 +459,7 @@ class set_(Command):
         if name == "colorscheme":
             return sorted(self.firstpart + colorscheme for colorscheme
                           in get_all_colorschemes(self.fm) if colorscheme.startswith(value))
+        return None
 
 
 class setlocal(set_):
@@ -785,6 +788,7 @@ class load_copy_buffer(Command):
                                   for g in fobj.read().split("\n") if exists(g))
         fobj.close()
         self.fm.ui.redraw_main_column()
+        return None
 
 
 class save_copy_buffer(Command):
@@ -804,6 +808,7 @@ class save_copy_buffer(Command):
                                   (fname or self.copy_buffer_filename), bad=True)
         fobj.write("\n".join(fobj.path for fobj in self.fm.copy_buffer))
         fobj.close()
+        return None
 
 
 class unmark_tag(mark_tag):
@@ -925,7 +930,7 @@ class rename(Command):
             return self.fm.notify('Syntax: rename <newname>', bad=True)
 
         if new_name == self.fm.thisfile.relative_path:
-            return
+            return None
 
         if access(new_name, os.F_OK):
             return self.fm.notify("Can't rename: file already exists!", bad=True)
@@ -937,6 +942,8 @@ class rename(Command):
             self.fm.thisdir.pointed_obj = file_new
             self.fm.thisfile = file_new
 
+        return None
+
     def tab(self, tabnum):
         return self._tab_directory_content()
 
@@ -1118,7 +1125,7 @@ class relink(Command):
             return self.fm.notify('%s is not a symlink!' % tfile.relative_path, bad=True)
 
         if new_path == os.readlink(tfile.path):
-            return
+            return None
 
         try:
             os.remove(tfile.path)
@@ -1130,6 +1137,8 @@ class relink(Command):
         self.fm.thisdir.pointed_obj = tfile
         self.fm.thisfile = tfile
 
+        return None
+
     def tab(self, tabnum):
         if not self.rest(1):
             return self.line + os.readlink(self.fm.thisfile.path)
@@ -1177,6 +1186,8 @@ class copymap(Command):
         for arg in self.args[2:]:
             self.fm.ui.keymaps.copy(self.context, self.arg(1), arg)
 
+        return None
+
 
 class copypmap(copymap):
     """:copypmap <keys> <newkeys1> [<newkeys2>...]
diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf
index a306594a..6bb8f0d0 100644
--- a/ranger/config/rc.conf
+++ b/ranger/config/rc.conf
@@ -281,7 +281,7 @@ alias travel     scout -aefklst
 # ===================================================================
 
 # Basic
-map     Q quit!
+map     Q quitall
 map     q quit
 copymap q ZZ ZQ
 
@@ -329,7 +329,7 @@ map uV      toggle_visual_mode reverse=True
 
 # For the nostalgics: Midnight Commander bindings
 map <F1> help
-map <F2> console rename%space
+map <F2> rename_append
 map <F3> display_file
 map <F4> edit
 map <F5> copy
diff --git a/ranger/config/rifle.conf b/ranger/config/rifle.conf
index 1924bff3..8bd5f565 100644
--- a/ranger/config/rifle.conf
+++ b/ranger/config/rifle.conf
@@ -149,6 +149,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 docx?, has catdoc,       terminal = catdoc -- "$@" | "$PAGER"
 
diff --git a/ranger/container/directory.py b/ranger/container/directory.py
index 8ca533d7..18b1687c 100644
--- a/ranger/container/directory.py
+++ b/ranger/container/directory.py
@@ -179,6 +179,7 @@ class Directory(  # pylint: disable=too-many-instance-attributes,too-many-public
             self._vcs_signal_handler_installed = True
         if self.settings.vcs_aware:
             return Vcs(self)
+        return None
 
     def signal_function_factory(self, function):
         def signal_function():
diff --git a/ranger/container/file.py b/ranger/container/file.py
index 17ca577c..d2daa169 100644
--- a/ranger/container/file.py
+++ b/ranger/container/file.py
@@ -48,7 +48,6 @@ class File(FileSystemObject):
     preview_data = None
     preview_known = False
     preview_loading = False
-    linemode = "filename"
     _firstbytes = None
 
     @property
diff --git a/ranger/container/tags.py b/ranger/container/tags.py
index 7aed5131..dabaf6a8 100644
--- a/ranger/container/tags.py
+++ b/ranger/container/tags.py
@@ -102,7 +102,7 @@ class Tags(object):
     def _parse(self, fobj):
         result = dict()
         for line in fobj:
-            line = line.strip()
+            line = line.rstrip('\n')
             if len(line) > 2 and line[1] == ':':
                 tag, path = line[0], line[2:]
                 if tag in ALLOWED_KEYS:
diff --git a/ranger/core/actions.py b/ranger/core/actions.py
index 5f8f6c53..0298af19 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -64,7 +64,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
     def reset(self):
         """:reset
 
-        Reset the filemanager, clearing the directory buffer.
+        Reset the filemanager, clearing the directory buffer, reload rifle config
         """
         old_path = self.thisdir.path
         self.previews = {}
@@ -73,6 +73,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
         self.change_mode('normal')
         if self.metadata:
             self.metadata.reset()
+        self.rifle.reload_config()
 
     def change_mode(self, mode=None):
         """:change_mode <mode>
@@ -228,7 +229,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
         cmd_class = self.commands.get_command(command_name)
         if cmd_class is None:
             self.notify("Command not found: `%s'" % command_name, bad=True)
-            return
+            return None
         cmd = cmd_class(string, quantifier=quantifier)
 
         if cmd.resolve_macros and _MacroTemplate.delimiter in cmd.line:
@@ -251,6 +252,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
             if ranger.args.debug:
                 raise
             self.notify(ex)
+        return None
 
     def substitute_macros(self, string,  # pylint: disable=redefined-outer-name
                           additional=None, escape=False):
@@ -738,6 +740,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
     # -- Searching
     # --------------------------
 
+    # TODO: Do we still use this method? Should we remove it?
     def search_file(self, text, offset=1, regexp=True):
         if isinstance(text, str) and regexp:
             try:
@@ -746,6 +749,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
                 return False
         self.thistab.last_search = text
         self.search_next(order='search', offset=offset)
+        return None
 
     def search_next(self, order=None, offset=1, forward=True):
         original_order = order
@@ -796,6 +800,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
                 return cwd.cycle(forward=None)
 
             return cwd.cycle(forward=forward)
+        return None
 
     def set_search_method(self, order, forward=True):  # pylint: disable=unused-argument
         if order in ('search', 'tag', 'size', 'mimetype', 'ctime', 'mtime', 'atime'):
@@ -1083,6 +1088,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
                 else:
                     pager.set_source(self.thisfile.get_preview_source(
                         pager.wid, pager.hei))
+            return None
 
         def on_destroy(signal):  # pylint: disable=unused-argument
             try:
@@ -1173,6 +1179,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
         newtab = tablist[(current_index + offset) % len(tablist)]
         if newtab != self.current_tab:
             self.tab_open(newtab)
+        return None
 
     def tab_new(self, path=None, narg=None):
         if narg:
@@ -1180,6 +1187,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
         for i in range(1, 10):
             if i not in self.tabs:
                 return self.tab_open(i, path)
+        return None
 
     def tab_switch(self, path, create_directory=False):
         """Switches to tab of given path, opening a new tab as necessary.
diff --git a/ranger/core/fm.py b/ranger/core/fm.py
index 591edecc..c55a3922 100644
--- a/ranger/core/fm.py
+++ b/ranger/core/fm.py
@@ -23,6 +23,7 @@ from ranger.gui.ui import UI
 from ranger.container.bookmarks import Bookmarks
 from ranger.core.runner import Runner
 from ranger.ext.img_display import (W3MImageDisplayer, ITerm2ImageDisplayer,
+                                    TerminologyImageDisplayer,
                                     URXVTImageDisplayer, URXVTImageFSDisplayer, ImageDisplayer)
 from ranger.core.metadata import MetadataManager
 from ranger.ext.rifle import Rifle
@@ -227,6 +228,8 @@ class FM(Actions,  # pylint: disable=too-many-instance-attributes
             return W3MImageDisplayer()
         elif self.settings.preview_images_method == "iterm2":
             return ITerm2ImageDisplayer()
+        elif self.settings.preview_images_method == "terminology":
+            return TerminologyImageDisplayer()
         elif self.settings.preview_images_method == "urxvt":
             return URXVTImageDisplayer()
         elif self.settings.preview_images_method == "urxvt-full":
diff --git a/ranger/core/metadata.py b/ranger/core/metadata.py
index 28b1f5b7..75f7ba3c 100644
--- a/ranger/core/metadata.py
+++ b/ranger/core/metadata.py
@@ -90,26 +90,25 @@ class MetadataManager(object):
     def _get_entry(self, filename):
         if filename in self.metadata_cache:
             return self.metadata_cache[filename]
-        else:
-
-            # Try to find an entry for this file in any of
-            # the applicable .metadata.json files
-            for metafile in self._get_metafile_names(filename):
-                entries = self._get_metafile_content(metafile)
-                # Check for a direct match:
-                if filename in entries:
-                    entry = entries[filename]
-                # Check for a match of the base name:
-                elif basename(filename) in entries:
-                    entry = entries[basename(filename)]
-                else:
-                    # No match found, try another entry
-                    continue
-
-                self.metadata_cache[filename] = entry
-                return entry
-
-            raise KeyError
+
+        # Try to find an entry for this file in any of
+        # the applicable .metadata.json files
+        for metafile in self._get_metafile_names(filename):
+            entries = self._get_metafile_content(metafile)
+            # Check for a direct match:
+            if filename in entries:
+                entry = entries[filename]
+            # Check for a match of the base name:
+            elif basename(filename) in entries:
+                entry = entries[basename(filename)]
+            else:
+                # No match found, try another entry
+                continue
+
+            self.metadata_cache[filename] = entry
+            return entry
+
+        raise KeyError
 
     def _get_metafile_content(self, metafile):
         import json
diff --git a/ranger/core/tab.py b/ranger/core/tab.py
index 160f590f..1d5e69d4 100644
--- a/ranger/core/tab.py
+++ b/ranger/core/tab.py
@@ -111,7 +111,7 @@ class Tab(FileManagerAware, SettingsAware):  # pylint: disable=too-many-instance
         """Enter given path"""
         # TODO: Ensure that there is always a self.thisdir
         if path is None:
-            return
+            return None
         path = str(path)
 
         # clear filter in the folder we're leaving
diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh
index 8386eac2..560e27da 100755
--- a/ranger/data/scope.sh
+++ b/ranger/data/scope.sh
@@ -95,6 +95,15 @@ handle_image() {
 
         # Image
         image/*)
+            local orientation
+            orientation="$( identify -format '%[EXIF:Orientation]\n' -- "${FILE_PATH}" )"
+            # If orientation data is present and the image actually
+            # needs rotating ("1" means no rotation)...
+            if [[ -n "$orientation" && "$orientation" != 1 ]]; then
+                # ...auto-rotate the image according to the EXIF data.
+                convert -- "${FILE_PATH}" -auto-orient "${IMAGE_CACHE_PATH}" && exit 6
+            fi
+
             # `w3mimgdisplay` will be called for all images (unless overriden as above),
             # but might fail for unsupported types.
             exit 7;;
@@ -104,6 +113,15 @@ handle_image() {
         #     # Thumbnail
         #     ffmpegthumbnailer -i "${FILE_PATH}" -o "${IMAGE_CACHE_PATH}" -s 0 && exit 6
         #     exit 1;;
+        # PDF
+        # application/pdf)
+        #     pdftoppm -f 1 -l 1 \
+        #              -scale-to-x 1920 \
+        #              -scale-to-y -1 \
+        #              -singlefile \
+        #              -jpeg -tiffcompression jpeg \
+        #              -- "${FILE_PATH}" "${IMAGE_CACHE_PATH%.*}" \
+        #         && exit 6 || exit 1;;
     esac
 }
 
@@ -149,11 +167,11 @@ handle_fallback() {
 }
 
 
-handle_extension
 MIMETYPE="$( file --dereference --brief --mime-type -- "${FILE_PATH}" )"
 if [[ "${PV_IMAGE_ENABLED}" == 'True' ]]; then
     handle_image "${MIMETYPE}"
 fi
+handle_extension
 handle_mime "${MIMETYPE}"
 handle_fallback
 
diff --git a/ranger/ext/accumulator.py b/ranger/ext/accumulator.py
index b0e4a1c5..c2e33b59 100644
--- a/ranger/ext/accumulator.py
+++ b/ranger/ext/accumulator.py
@@ -29,12 +29,12 @@ class Accumulator(object):
 
     def move_to_obj(self, arg, attr=None):
         if not arg:
-            return
+            return None
 
         lst = self.get_list()
 
         if not lst:
-            return
+            return None
 
         do_get_attr = isinstance(attr, str)
 
diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py
index 5bc1fbe5..01c739d3 100644
--- a/ranger/ext/img_display.py
+++ b/ranger/ext/img_display.py
@@ -32,6 +32,7 @@ W3MIMGDISPLAY_PATHS = [
     '/usr/libexec/w3m/w3mimgdisplay',
     '/usr/lib64/w3m/w3mimgdisplay',
     '/usr/libexec64/w3m/w3mimgdisplay',
+    '/usr/local/libexec/w3m/w3mimgdisplay',
 ]
 
 
@@ -121,6 +122,8 @@ class W3MImageDisplayer(ImageDisplayer):
         self.process.stdin.write(input_gen)
         self.process.stdin.flush()
         self.process.stdout.readline()
+        self.quit()
+        self.is_initialized = False
 
     def clear(self, start_x, start_y, width, height):
         if not self.is_initialized or self.process.poll() is not None:
@@ -311,6 +314,63 @@ class ITerm2ImageDisplayer(ImageDisplayer, FileManagerAware):
         return width, height
 
 
+class TerminologyImageDisplayer(ImageDisplayer, FileManagerAware):
+    """Implementation of ImageDisplayer using terminology image display support
+    (https://github.com/billiob/terminology).
+
+    Ranger must be running in terminology for this to work.
+    Doesn't work with TMUX :/
+    """
+
+    def __init__(self):
+        self.display_protocol = "\033"
+        self.close_protocol = "\000"
+
+    def draw(self, path, start_x, start_y, width, height):
+        # Save cursor
+        curses.putp(curses.tigetstr("sc"))
+
+        y = start_y
+        # Move to drawing zone
+        self._move_to(start_x, y)
+
+        # Write intent
+        sys.stdout.write("%s}ic#%d;%d;%s%s" % (
+            self.display_protocol,
+            width, height,
+            path,
+            self.close_protocol))
+
+        # Write Replacement commands ('#')
+        for _ in range(0, height):
+            sys.stdout.write("%s}ib%s%s%s}ie%s" % (
+                self.display_protocol,
+                self.close_protocol,
+                "#" * width,
+                self.display_protocol,
+                self.close_protocol))
+            y = y + 1
+            self._move_to(start_x, y)
+
+        # Restore cursor
+        curses.putp(curses.tigetstr("rc"))
+
+        sys.stdout.flush()
+
+    @staticmethod
+    def _move_to(x, y):
+        # curses.move(y, x)
+        tparm = curses.tparm(curses.tigetstr("cup"), y, x)
+        if sys.version_info[0] < 3:
+            sys.stdout.write(tparm)
+        else:
+            sys.stdout.buffer.write(tparm)  # pylint: disable=no-member
+
+    def clear(self, start_x, start_y, width, height):
+        self.fm.ui.win.redrawwin()
+        self.fm.ui.win.refresh()
+
+
 class URXVTImageDisplayer(ImageDisplayer, FileManagerAware):
     """Implementation of ImageDisplayer working by setting the urxvt
     background image "under" the preview pane.
diff --git a/ranger/ext/next_available_filename.py b/ranger/ext/next_available_filename.py
index 91d48631..0a5e0856 100644
--- a/ranger/ext/next_available_filename.py
+++ b/ranger/ext/next_available_filename.py
@@ -19,3 +19,4 @@ def next_available_filename(fname, directory="."):
     for i in range(1, len(existing_files) + 1):
         if fname + str(i) not in existing_files:
             return fname + str(i)
+    return None
diff --git a/ranger/ext/rifle.py b/ranger/ext/rifle.py
index cfb07f5f..a6c0b9f0 100755
--- a/ranger/ext/rifle.py
+++ b/ranger/ext/rifle.py
@@ -244,6 +244,7 @@ class Rifle(object):  # pylint: disable=too-many-instance-attributes
             return bool(os.environ.get(argument))
         elif function == 'else':
             return True
+        return None
 
     def get_mimetype(self, fname):
         # Spawn "file" to determine the mime-type of the given file.
@@ -295,7 +296,7 @@ class Rifle(object):  # pylint: disable=too-many-instance-attributes
                     count = self._skip
                 yield (count, cmd, self._app_label, self._app_flags)
 
-    def execute(self, files,  # pylint: disable=too-many-branches,too-many-statements
+    def execute(self, files,  # noqa: E501 pylint: disable=too-many-branches,too-many-statements,too-many-locals
                 number=0, label=None, flags="", mimetype=None):
         """Executes the given list of files.
 
@@ -379,6 +380,8 @@ class Rifle(object):  # pylint: disable=too-many-instance-attributes
             finally:
                 self.hook_after_executing(command, self._mimetype, self._app_flags)
 
+        return None
+
 
 def find_conf_path():
     # Find configuration file path
diff --git a/ranger/ext/signals.py b/ranger/ext/signals.py
index cbd35643..67c8960d 100644
--- a/ranger/ext/signals.py
+++ b/ranger/ext/signals.py
@@ -155,6 +155,7 @@ class SignalDispatcher(object):
                 key=lambda handler: -handler.priority)
         return handler
 
+    # TODO: Do we still use this method? Should we remove it?
     def signal_force_sort(self, signal_name=None):
         """Forces a sorting of signal handlers by priority.
 
@@ -165,11 +166,12 @@ class SignalDispatcher(object):
             for handlers in self._signals.values():
                 handlers.sort(
                     key=lambda handler: -handler.priority)
+            return None
         elif signal_name in self._signals:
             self._signals[signal_name].sort(
                 key=lambda handler: -handler.priority)
-        else:
-            return False
+            return None
+        return False
 
     def signal_unbind(self, signal_handler):
         """Removes a signal binding.
diff --git a/ranger/ext/vcs/vcs.py b/ranger/ext/vcs/vcs.py
index ba28d141..e2838f8d 100644
--- a/ranger/ext/vcs/vcs.py
+++ b/ranger/ext/vcs/vcs.py
@@ -112,7 +112,11 @@ class Vcs(object):  # pylint: disable=too-many-instance-attributes
     def _run(self, args, path=None,  # pylint: disable=too-many-arguments
              catchout=True, retbytes=False, rstrip_newline=True):
         """Run a command"""
-        cmd = [self.repotype] + args
+        if self.repotype == 'hg':
+            # use "chg", a faster built-in client
+            cmd = ['chg'] + args
+        else:
+            cmd = [self.repotype] + args
         if path is None:
             path = self.path
 
@@ -125,6 +129,7 @@ class Vcs(object):  # pylint: disable=too-many-instance-attributes
             else:
                 with open(os.devnull, mode='w') as fd_devnull:
                     subprocess.check_call(cmd, cwd=path, stdout=fd_devnull, stderr=fd_devnull)
+                return None
         except (subprocess.CalledProcessError, OSError):
             raise VcsError('{0:s}: {1:s}'.format(str(cmd), path))
 
diff --git a/ranger/ext/widestring.py b/ranger/ext/widestring.py
index ed66f3c7..2721643c 100644
--- a/ranger/ext/widestring.py
+++ b/ranger/ext/widestring.py
@@ -1,4 +1,4 @@
-# -*- encoding: utf8 -*-
+# -*- encoding: utf-8 -*-
 # This file is part of ranger, the console file manager.
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
 
@@ -81,6 +81,7 @@ class WideString(object):  # pylint: disable=too-few-public-methods
             return WideString(self.string + string)
         elif isinstance(string, WideString):
             return WideString(self.string + string.string, self.chars + string.chars)
+        return None
 
     def __radd__(self, string):
         """
@@ -91,6 +92,7 @@ class WideString(object):  # pylint: disable=too-few-public-methods
             return WideString(string + self.string)
         elif isinstance(string, WideString):
             return WideString(string.string + self.string, string.chars + self.chars)
+        return None
 
     def __str__(self):
         return self.string
diff --git a/ranger/gui/bar.py b/ranger/gui/bar.py
index 64bd6ce1..3f4b891c 100644
--- a/ranger/gui/bar.py
+++ b/ranger/gui/bar.py
@@ -77,6 +77,7 @@ class Bar(object):
                 else:
                     item.cut_off(oversize)
                     break
+        return None
 
     def fill_gap(self, char, wid, gapwidth=False):
         del self.gap[:]
diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py
index 5877e484..990db0ad 100644
--- a/ranger/gui/ui.py
+++ b/ranger/gui/ui.py
@@ -509,6 +509,7 @@ class UI(  # pylint: disable=too-many-instance-attributes,too-many-public-method
         if viewmode == 'miller':
             from ranger.gui.widgets.view_miller import ViewMiller
             return ViewMiller
-        if viewmode == 'multipane':
+        elif viewmode == 'multipane':
             from ranger.gui.widgets.view_multipane import ViewMultipane
             return ViewMultipane
+        return None
diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py
index 6bcf20e9..13201e34 100644
--- a/ranger/gui/widgets/console.py
+++ b/ranger/gui/widgets/console.py
@@ -87,6 +87,15 @@ class Console(Widget):  # pylint: disable=too-many-instance-attributes,too-many-
                 fobj.close()
         Widget.destroy(self)
 
+    def _calculate_offset(self):
+        wid = self.wid - 2
+        whalf = wid // 2
+        if self.pos < whalf or len(self.line) < wid:
+            return 0
+        if self.pos > len(self.line) - (wid - whalf):
+            return len(self.line) - wid
+        return self.pos - whalf
+
     def draw(self):
         self.win.erase()
         if self.question_queue:
@@ -97,11 +106,9 @@ class Console(Widget):  # pylint: disable=too-many-instance-attributes,too-many-
 
         self.addstr(0, 0, self.prompt)
         line = WideString(self.line)
-        overflow = -self.wid + len(self.prompt) + len(line) + 1
-        if overflow > 0:
-            self.addstr(0, len(self.prompt), str(line[overflow:]))
-        else:
-            self.addstr(0, len(self.prompt), self.line)
+        if line:
+            x = self._calculate_offset()
+            self.addstr(0, len(self.prompt), str(line[x:]))
 
     def finalize(self):
         move = self.fm.ui.win.move
@@ -112,7 +119,8 @@ class Console(Widget):  # pylint: disable=too-many-instance-attributes,too-many-
                 pass
         else:
             try:
-                pos = uwid(self.line[0:self.pos]) + len(self.prompt)
+                x = self._calculate_offset()
+                pos = uwid(self.line[x:self.pos]) + len(self.prompt)
                 move(self.y, self.x + min(self.wid - 1, pos))
             except curses.error:
                 pass
diff --git a/ranger/gui/widgets/taskview.py b/ranger/gui/widgets/taskview.py
index 2c0cafb0..a4633e15 100644
--- a/ranger/gui/widgets/taskview.py
+++ b/ranger/gui/widgets/taskview.py
@@ -84,7 +84,7 @@ class TaskView(Widget, Accumulator):
         if i is None:
             i = self.pointer
 
-        self.fm.loader.move(_from=i, to=to)
+        self.fm.loader.move(pos_src=i, pos_dest=to)
 
     def press(self, key):
         self.fm.ui.keymaps.use_keymap('taskview')
36b0dadd88f82635f661f9d9df77604d'>76755b28 ^
d2bd40bf ^
76755b28 ^


d2bd40bf ^
76755b28 ^









90560d71 ^
672e3e50 ^
90560d71 ^


672e3e50 ^







90560d71 ^


9542bb11 ^



76755b28 ^
90560d71 ^

9542bb11 ^

76755b28 ^








9542bb11 ^

d2bd40bf ^
















90560d71 ^

76755b28 ^






























672e3e50 ^
76755b28 ^
672e3e50 ^
76755b28 ^




















































672e3e50 ^
c5ffb6e1 ^
90560d71 ^
76755b28 ^
672e3e50 ^



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634