summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml13
-rw-r--r--Makefile9
-rw-r--r--Pipfile15
-rw-r--r--Pipfile.lock175
-rw-r--r--README.md1
-rw-r--r--doc/colorschemes.md140
-rw-r--r--doc/colorschemes.txt92
-rw-r--r--doc/ranger.132
-rw-r--r--doc/ranger.pod26
-rw-r--r--doc/rifle.14
-rw-r--r--examples/bash_automatic_cd.sh4
-rw-r--r--ranger/config/rc.conf24
-rw-r--r--ranger/config/rifle.conf1
-rw-r--r--ranger/container/settings.py5
-rw-r--r--ranger/core/actions.py17
-rw-r--r--ranger/core/fm.py12
-rw-r--r--ranger/core/main.py2
-rw-r--r--ranger/core/runner.py2
-rw-r--r--ranger/ext/img_display.py279
-rwxr-xr-xranger/ext/rifle.py2
-rw-r--r--ranger/gui/widgets/browsercolumn.py24
-rw-r--r--ranger/gui/widgets/pager.py7
-rwxr-xr-xsetup.py2
23 files changed, 712 insertions, 176 deletions
diff --git a/.travis.yml b/.travis.yml
index f9e31256..0efd09fc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,13 +1,14 @@
-dist: 'trusty'
+dist: 'xenial'
 
 language: 'python'
 python:
-    - '2.7'
-    - '3.4'
-    - '3.5'
+  - '2.7'
+  - '3.4'
+  - '3.5'
 
 install:
-    - 'pip install pytest pylint flake8'
+  - 'pip install pipenv'
+  - 'pipenv update --dev'
 
 script:
-    - 'make test'
+  - 'make test'
diff --git a/Makefile b/Makefile
index 47e2fa01..6cd1ec8f 100644
--- a/Makefile
+++ b/Makefile
@@ -98,7 +98,11 @@ test_pytest:
 	@echo "Running py.test tests..."
 	py.test tests
 
-test: test_pylint test_flake8 test_doctest test_pytest
+test_other:
+	@echo "Checking completeness of man page..."
+	@tests/manpage_completion_test.py
+
+test: test_pylint test_flake8 test_doctest test_pytest test_other
 	@echo "Finished testing: All tests passed!"
 
 man:
@@ -122,4 +126,5 @@ todo:
 	@grep --color -Ion '\(TODO\|XXX\).*' -r ranger
 
 .PHONY: clean cleandoc compile default dist doc help install man manhtml \
-	options snapshot test test_pylint test_flake8 test_doctest test_pytest todo pypi_sdist
+	options snapshot test test_pylint test_flake8 test_doctest test_pytest \
+	test_other todo pypi_sdist
diff --git a/Pipfile b/Pipfile
new file mode 100644
index 00000000..a927408c
--- /dev/null
+++ b/Pipfile
@@ -0,0 +1,15 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[requires]
+python_version = "3.5"
+
+[dev-packages]
+pytest = "*"
+"flake8" = "*"
+pylint = "<2.0.0"
+"enum34" = "*"
+
+[packages]
diff --git a/Pipfile.lock b/Pipfile.lock
new file mode 100644
index 00000000..be0d4a62
--- /dev/null
+++ b/Pipfile.lock
@@ -0,0 +1,175 @@
+{
+    "_meta": {
+        "hash": {
+            "sha256": "ccd51f0d238502cb3001a4b709d4455134eeaebb96800ebaad364567ba1ba784"
+        },
+        "pipfile-spec": 6,
+        "requires": {
+            "python_version": "3.5"
+        },
+        "sources": [
+            {
+                "name": "pypi",
+                "url": "https://pypi.org/simple",
+                "verify_ssl": true
+            }
+        ]
+    },
+    "default": {},
+    "develop": {
+        "astroid": {
+            "hashes": [
+                "sha256:0ef2bf9f07c3150929b25e8e61b5198c27b0dca195e156f0e4d5bdd89185ca1a",
+                "sha256:fc9b582dba0366e63540982c3944a9230cbc6f303641c51483fa547dcc22393a"
+            ],
+            "version": "==1.6.5"
+        },
+        "atomicwrites": {
+            "hashes": [
+                "sha256:240831ea22da9ab882b551b31d4225591e5e447a68c5e188db5b89ca1d487585",
+                "sha256:a24da68318b08ac9c9c45029f4a10371ab5b20e4226738e150e6e7c571630ae6"
+            ],
+            "version": "==1.1.5"
+        },
+        "attrs": {
+            "hashes": [
+                "sha256:4b90b09eeeb9b88c35bc642cbac057e45a5fd85367b985bd2809c62b7b939265",
+                "sha256:e0d0eb91441a3b53dab4d9b743eafc1ac44476296a2053b6ca3af0b139faf87b"
+            ],
+            "version": "==18.1.0"
+        },
+        "enum34": {
+            "hashes": [
+                "sha256:2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850",
+                "sha256:644837f692e5f550741432dd3f223bbb9852018674981b1664e5dc339387588a",
+                "sha256:6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79",
+                "sha256:8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1"
+            ],
+            "index": "pypi",
+            "version": "==1.1.6"
+        },
+        "flake8": {
+            "hashes": [
+                "sha256:7253265f7abd8b313e3892944044a365e3f4ac3fcdcfb4298f55ee9ddf188ba0",
+                "sha256:c7841163e2b576d435799169b78703ad6ac1bbb0f199994fc05f700b2a90ea37"
+            ],
+            "index": "pypi",
+            "version": "==3.5.0"
+        },
+        "isort": {
+            "hashes": [
+                "sha256:1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af",
+                "sha256:b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8",
+                "sha256:ec9ef8f4a9bc6f71eec99e1806bfa2de401650d996c59330782b89a5555c1497"
+            ],
+            "version": "==4.3.4"
+        },
+        "lazy-object-proxy": {
+            "hashes": [
+                "sha256:0ce34342b419bd8f018e6666bfef729aec3edf62345a53b537a4dcc115746a33",
+                "sha256:1b668120716eb7ee21d8a38815e5eb3bb8211117d9a90b0f8e21722c0758cc39",
+                "sha256:209615b0fe4624d79e50220ce3310ca1a9445fd8e6d3572a896e7f9146bbf019",
+                "sha256:27bf62cb2b1a2068d443ff7097ee33393f8483b570b475db8ebf7e1cba64f088",
+                "sha256:27ea6fd1c02dcc78172a82fc37fcc0992a94e4cecf53cb6d73f11749825bd98b",
+                "sha256:2c1b21b44ac9beb0fc848d3993924147ba45c4ebc24be19825e57aabbe74a99e",
+                "sha256:2df72ab12046a3496a92476020a1a0abf78b2a7db9ff4dc2036b8dd980203ae6",
+                "sha256:320ffd3de9699d3892048baee45ebfbbf9388a7d65d832d7e580243ade426d2b",
+                "sha256:50e3b9a464d5d08cc5227413db0d1c4707b6172e4d4d915c1c70e4de0bbff1f5",
+                "sha256:5276db7ff62bb7b52f77f1f51ed58850e315154249aceb42e7f4c611f0f847ff",
+                "sha256:61a6cf00dcb1a7f0c773ed4acc509cb636af2d6337a08f362413c76b2b47a8dd",
+                "sha256:6ae6c4cb59f199d8827c5a07546b2ab7e85d262acaccaacd49b62f53f7c456f7",
+                "sha256:7661d401d60d8bf15bb5da39e4dd72f5d764c5aff5a86ef52a042506e3e970ff",
+                "sha256:7bd527f36a605c914efca5d3d014170b2cb184723e423d26b1fb2fd9108e264d",
+                "sha256:7cb54db3535c8686ea12e9535eb087d32421184eacc6939ef15ef50f83a5e7e2",
+                "sha256:7f3a2d740291f7f2c111d86a1c4851b70fb000a6c8883a59660d95ad57b9df35",
+                "sha256:81304b7d8e9c824d058087dcb89144842c8e0dea6d281c031f59f0acf66963d4",
+                "sha256:933947e8b4fbe617a51528b09851685138b49d511af0b6c0da2539115d6d4514",
+                "sha256:94223d7f060301b3a8c09c9b3bc3294b56b2188e7d8179c762a1cda72c979252",
+                "sha256:ab3ca49afcb47058393b0122428358d2fbe0408cf99f1b58b295cfeb4ed39109",
+                "sha256:bd6292f565ca46dee4e737ebcc20742e3b5be2b01556dafe169f6c65d088875f",
+                "sha256:cb924aa3e4a3fb644d0c463cad5bc2572649a6a3f68a7f8e4fbe44aaa6d77e4c",
+                "sha256:d0fc7a286feac9077ec52a927fc9fe8fe2fabab95426722be4c953c9a8bede92",
+                "sha256:ddc34786490a6e4ec0a855d401034cbd1242ef186c20d79d2166d6a4bd449577",
+                "sha256:e34b155e36fa9da7e1b7c738ed7767fc9491a62ec6af70fe9da4a057759edc2d",
+                "sha256:e5b9e8f6bda48460b7b143c3821b21b452cb3a835e6bbd5dd33aa0c8d3f5137d",
+                "sha256:e81ebf6c5ee9684be8f2c87563880f93eedd56dd2b6146d8a725b50b7e5adb0f",
+                "sha256:eb91be369f945f10d3a49f5f9be8b3d0b93a4c2be8f8a5b83b0571b8123e0a7a",
+                "sha256:f460d1ceb0e4a5dcb2a652db0904224f367c9b3c1470d5a7683c0480e582468b"
+            ],
+            "version": "==1.3.1"
+        },
+        "mccabe": {
+            "hashes": [
+                "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
+                "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
+            ],
+            "version": "==0.6.1"
+        },
+        "more-itertools": {
+            "hashes": [
+                "sha256:2b6b9893337bfd9166bee6a62c2b0c9fe7735dcf85948b387ec8cba30e85d8e8",
+                "sha256:6703844a52d3588f951883005efcf555e49566a48afd4db4e965d69b883980d3",
+                "sha256:a18d870ef2ffca2b8463c0070ad17b5978056f403fb64e3f15fe62a52db21cc0"
+            ],
+            "version": "==4.2.0"
+        },
+        "pluggy": {
+            "hashes": [
+                "sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff",
+                "sha256:d345c8fe681115900d6da8d048ba67c25df42973bda370783cd58826442dcd7c",
+                "sha256:e160a7fcf25762bb60efc7e171d4497ff1d8d2d75a3d0df7a21b76821ecbf5c5"
+            ],
+            "version": "==0.6.0"
+        },
+        "py": {
+            "hashes": [
+                "sha256:3fd59af7435864e1a243790d322d763925431213b6b8529c6ca71081ace3bbf7",
+                "sha256:e31fb2767eb657cbde86c454f02e99cb846d3cd9d61b318525140214fdc0e98e"
+            ],
+            "version": "==1.5.4"
+        },
+        "pycodestyle": {
+            "hashes": [
+                "sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766",
+                "sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9"
+            ],
+            "version": "==2.3.1"
+        },
+        "pyflakes": {
+            "hashes": [
+                "sha256:08bd6a50edf8cffa9fa09a463063c425ecaaf10d1eb0335a7e8b1401aef89e6f",
+                "sha256:8d616a382f243dbf19b54743f280b80198be0bca3a5396f1d2e1fca6223e8805"
+            ],
+            "version": "==1.6.0"
+        },
+        "pylint": {
+            "hashes": [
+                "sha256:a48070545c12430cfc4e865bf62f5ad367784765681b3db442d8230f0960aa3c",
+                "sha256:fff220bcb996b4f7e2b0f6812fd81507b72ca4d8c4d05daf2655c333800cb9b3"
+            ],
+            "index": "pypi",
+            "version": "==1.9.2"
+        },
+        "pytest": {
+            "hashes": [
+                "sha256:0453c8676c2bee6feb0434748b068d5510273a916295fd61d306c4f22fbfd752",
+                "sha256:4b208614ae6d98195430ad6bde03641c78553acee7c83cec2e85d613c0cd383d"
+            ],
+            "index": "pypi",
+            "version": "==3.6.3"
+        },
+        "six": {
+            "hashes": [
+                "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
+                "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
+            ],
+            "version": "==1.11.0"
+        },
+        "wrapt": {
+            "hashes": [
+                "sha256:d4d560d479f2c21e1b5443bbd15fe7ec4b37fe7e53d335d3b9b0a7b1226fe3c6"
+            ],
+            "version": "==1.10.11"
+        }
+    }
+}
diff --git a/README.md b/README.md
index 8abb7265..e14bab46 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,7 @@ Optional:
 * The python module `chardet`, in case of encoding detection problems
 * `sudo` to use the "run as root"-feature
 * `w3m` for the `w3mimgdisplay` program to preview images
+* `python-bidi` for correct display of RTL file names (Hebrew, Arabic)
 
 Optional, for enhanced file previews (with `scope.sh`):
 
diff --git a/doc/colorschemes.md b/doc/colorschemes.md
new file mode 100644
index 00000000..458cfc53
--- /dev/null
+++ b/doc/colorschemes.md
@@ -0,0 +1,140 @@
+Colorschemes
+============
+
+This text explains colorschemes and how they work.
+
+Context Tags
+------------
+
+Context tags provide information about the context and are Boolean values (`True`
+or `False`). For example, if the tag `in_titlebar` is set, you probably want to
+know about the color of a part of the titlebar now.
+
+The default context tags are specified in `/ranger/gui/context.py` in the
+constant `CONTEXT_KEYS`. Custom tags can be specified in a custom plugin file in
+`~/.config/ranger/plugins/`. The code to use follows.
+
+```python
+# Import the class
+import ranger.gui.context
+
+# Add your key names
+ranger.gui.context.CONTEXT_KEYS.append('my_key')
+
+# Set it to False (the default value)
+ranger.gui.context.Context.my_key = False
+
+# Or use an array for multiple names
+my_keys = ['key_one', 'key_two']
+ranger.gui.context.CONTEXT_KEYS.append(my_keys)
+
+# Set them to False
+for key in my_keys:
+    code = 'ranger.gui.context.Context.' + key + ' = False'
+    exec(code)
+```
+
+As you may or may not have guessed, this only tells ranger that they exist, not
+what they mean. To do this, you'll have to dig around in the source code. As an
+example, let's walk through adding a key that highlights `README.md` files
+differently. All the following code will be written in a standalone plugin file.
+
+First, from above, we'll add the key `readme` and set it to `False`.
+
+```python
+import ranger.gui.context
+
+ranger.gui.context.CONTEXT_KEYS.append('readme')
+ranger.gui.context.Context.readme = False
+```
+
+Then we'll use the hook `hook_before_drawing` to tell ranger that our key is
+talking about `README.md` files.
+
+```python
+import ranger.gui.widgets.browsercolumn
+
+OLD_HOOK_BEFORE_DRAWING = ranger.gui.widgets.browsercolumn.hook_before_drawing
+
+def new_hook_before_drawing(fsobject, color_list):
+    if fsobject.basename === 'README.md':
+        color_list.append('readme')
+
+    return OLD_HOOK_BEFORE_DRAWING(fsobject, color_list)
+
+ranger.gui.widgets.browsercolumn.hook_before_drawing = new_hook_before_drawing
+```
+
+Notice we call the old `hook_before_drawing`. This makes sure that we don't
+overwrite another plugin's code, we just append our own to it.
+
+To highlight it a different color, just [add it to your colorscheme][1]
+
+[1]:#adapt-a-colorscheme
+
+Implementation in the GUI Classes
+---------------------------------
+
+The class `CursesShortcuts` in the file `/ranger/gui/curses_shortcuts.py` defines
+the methods `color(*tags)`, `color_at(y, x, wid, *tags)` and `color_reset()`.
+This class is a superclass of `Displayable`, so these methods are available almost
+everywhere.
+
+Something like `color("in_titlebar", "directory")` will be called to get the
+color of directories in the titlebar. This creates a `ranger.gui.context.Context`
+object, sets its attributes `in_titlebar` and `directory` to True, leaves the
+others as `False`, and passes it to the colorscheme's `use(context)` method.
+
+The Color Scheme
+----------------
+
+A colorscheme should be a subclass of `ranger.gui.ColorScheme` and define the
+method `use(context)`. By looking at the context, this use-method has to
+determine a 3-tuple of integers: `(foreground, background, attribute)` and return
+it.
+
+`foreground` and `background` are integers representing colors, `attribute` is
+another integer with each bit representing one attribute. These integers are
+interpreted by the terminal emulator in use.
+
+Abbreviations for colors and attributes are defined in `ranger.gui.color`. Two
+attributes can be combined via bitwise OR: `bold | reverse`
+
+Once the color for a set of tags is determined, it will be cached by default. If
+you want more dynamic colorschemes (such as a different color for very large
+files), you will need to dig into the source code, perhaps add a custom tag and
+modify the draw-method of the widget to use that tag.
+
+Run `tc_colorscheme` to check if your colorschemes are valid.
+
+Specify a Colorscheme
+---------------------
+
+Colorschemes are searched for in these directories:
+
+- `~/.config/ranger/colorschemes/`
+- `/path/to/ranger/colorschemes/`
+
+To specify which colorscheme to use, change the option `colorscheme` in your
+rc.conf: `set colorscheme default`.
+
+This means, use the colorscheme contained in either
+`~/.config/ranger/colorschemes/default.py` or
+`/path/to/ranger/colorschemes/default.py`.
+
+Adapt a colorscheme
+-------------------
+
+You may want to adapt a colorscheme to your needs without having a complete copy
+of it, but rather the changes only. Say, you want the exact same colors as in
+the default colorscheme, but the directories to be green rather than blue,
+because you find the blue hard to read.
+
+This is done in the jungle colorscheme `ranger/colorschemes/jungle`, check it
+out for implementation details. In short, I made a subclass of the default
+scheme, set the initial colors to the result of the default `use()` method and
+modified the colors how I wanted.
+
+This has the obvious advantage that you need to write less, which results in
+less maintenance work and a greater chance that your colorscheme will work with
+future versions of ranger.
diff --git a/doc/colorschemes.txt b/doc/colorschemes.txt
deleted file mode 100644
index 145cc94e..00000000
--- a/doc/colorschemes.txt
+++ /dev/null
@@ -1,92 +0,0 @@
-Colorschemes
-============
-
-This text explains colorschemes and how they work.
-
-
-Context Tags
-------------
-
-Context Tags provide information about the context.  If the tag
-"in_titlebar" is set, you probably want to know about the color
-of a part of the titlebar now.
-
-There are a number of context tags, specified in /ranger/gui/context.py
-in the constant CONTEXT_KEYS.
-
-A Context object, defined in the same file, contains attributes with
-the names of all tags, whose values are either True or False.
-
-
-Implementation in the GUI Classes
----------------------------------
-
-The class CursesShortcuts in the file /ranger/gui/curses_shortcuts.py
-defines the methods color(*tags), color_at(y, x, wid, *tags) and
-color_reset().  This class is a superclass of Displayable, so these
-methods are available almost everywhere.
-
-Something like color("in_titlebar", "directory") will be called to
-get the color of directories in the titlebar.  This creates a
-ranger.gui.context.Context object, sets its attributes "in_titlebar" and
-"directory" to True, leaves the others as False, and passes it to the
-colorscheme's use(context) method.
-
-
-The Color Scheme
-----------------
-
-A colorscheme should be a subclass of ranger.gui.ColorScheme and
-define the method use(context).  By looking at the context, this use-method
-has to determine a 3-tuple of integers: (foreground, background, attribute)
-and return it.
-
-foreground and background are integers representing colors,
-attribute is another integer with each bit representing one attribute.
-These integers are interpreted by the used terminal emulator.
-
-Abbreviations for colors and attributes are defined in ranger.gui.color.
-Two attributes can be combined via bitwise OR: bold | reverse
-
-Once the color for a set of tags is determined, it will be cached by
-default.  If you want more dynamic colorschemes (such as a different
-color for very large files), you will need to dig into the source code,
-perhaps add an own tag and modify the draw-method of the widget to use
-that tag.
-
-Run tc_colorscheme to check if your colorschemes are valid.
-
-
-Specify a Colorscheme
----------------------
-
-Colorschemes are searched for in these directories:
-~/.config/ranger/colorschemes/
-/path/to/ranger/colorschemes/
-
-To specify which colorscheme to use, change the option "colorscheme"
-in your rc.conf:
-set colorscheme default
-
-This means, use the colorscheme contained in
-either ~/.config/ranger/colorschemes/default.py or
-/path/to/ranger/colorschemes/default.py.
-
-
-Adapt a colorscheme
--------------------
-
-You may want to adapt a colorscheme to your needs without having
-a complete copy of it, but rather the changes only.  Say, you
-want the exact same colors as in the default colorscheme, but
-the directories to be green rather than blue, because you find the
-blue hard to read.
-
-This is done in the jungle colorscheme ranger/colorschemes/jungle,
-check it out for implementation details.  In short, I made a subclass
-of the default scheme, set the initial colors to the result of the
-default use() method and modified the colors how I wanted.
-
-This has the obvious advantage that you need to write less, which
-results in less maintenance work and a greater chance that your colorscheme
-will work with future versions of ranger.
diff --git a/doc/ranger.1 b/doc/ranger.1
index 1290cb58..e141011f 100644
--- a/doc/ranger.1
+++ b/doc/ranger.1
@@ -129,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "RANGER 1"
-.TH RANGER 1 "ranger-1.9.1" "2018-05-14" "ranger manual"
+.TH RANGER 1 "ranger-1.9.1" "2018-07-15" "ranger manual"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -307,6 +307,13 @@ This feature relies on the dimensions of the terminal's font.  By default, a
 width of 8 and height of 11 are used.  To use other values, set the options
 \&\f(CW\*(C`iterm2_font_width\*(C'\fR and \f(CW\*(C`iterm2_font_height\*(C'\fR to the desired values.
 .PP
+\fIterminology\fR
+.IX Subsection "terminology"
+.PP
+This only works in terminology. It can render vector graphics, but works only locally.
+.PP
+To enable this feature, set the option \f(CW\*(C`preview_images_method\*(C'\fR to terminology.
+.PP
 \fIurxvt\fR
 .IX Subsection "urxvt"
 .PP
@@ -324,6 +331,14 @@ The same as urxvt but utilizing not only the preview pane but the whole terminal
 window.
 .PP
 To enable this feature, set the option \f(CW\*(C`preview_images_method\*(C'\fR to urxvt-full.
+.PP
+\fIkitty\fR
+.IX Subsection "kitty"
+.PP
+This only works on Kitty. It requires \s-1PIL\s0 (or pillow) to work.
+Allows remote image previews, for example in an ssh session.
+.PP
+To enable this feature, set the option \f(CW\*(C`preview_images_method\*(C'\fR to kitty.
 .SS "\s-1SELECTION\s0"
 .IX Subsection "SELECTION"
 The \fIselection\fR is defined as \*(L"All marked files \s-1IF THERE ARE ANY,\s0 otherwise
@@ -675,7 +690,7 @@ scrolling to switch directories.
 .SH "SETTINGS"
 .IX Header "SETTINGS"
 This section lists all built-in settings of ranger.  The valid types for the
-value are in [brackets].  The hotkey to toggle the setting is in <brokets>, if
+value are in [brackets].  The hotkey to toggle the setting is in <brakets>, if
 a hotkey exists.
 .PP
 Settings can be changed in the file \fI~/.config/ranger/rc.conf\fR or on the
@@ -893,6 +908,10 @@ to disable this feature.
 Which script should handle generating previews?  If the file doesn't exist, or
 use_preview_script is off, ranger will handle previews itself by just printing
 the content.
+.IP "relative_current_zero [bool]" 4
+.IX Item "relative_current_zero [bool]"
+When line_numbers is set to relative, show 0 on the current line if
+true or show the absolute number of the current line when false.
 .IP "save_backtick_bookmark [bool]" 4
 .IX Item "save_backtick_bookmark [bool]"
 Save the \f(CW\*(C`\`\*(C'\fR bookmark to disk.  This bookmark is used to switch to the last
@@ -955,6 +974,11 @@ Abbreviate \f(CW$HOME\fR with ~ in the titlebar (first line) of ranger?
 .IP "unicode_ellipsis [bool]" 4
 .IX Item "unicode_ellipsis [bool]"
 Use a unicode \*(L"...\*(R" character instead of \*(L"~\*(R" to mark cut-off filenames?
+.IP "bidi_support [bool]" 4
+.IX Item "bidi_support [bool]"
+Try to properly display file names in \s-1RTL\s0 languages (Hebrew, Arabic) by using
+a \s-1BIDI\s0 algorithm to reverse the relevant parts of the text.
+Requires the python-bidi pip package.
 .IP "update_title [bool]" 4
 .IX Item "update_title [bool]"
 Set a window title?
@@ -981,6 +1005,10 @@ Sets the state for the version control backend. The possible values are:
 Sets the view mode, which can be \fBmiller\fR to display the files in the
 traditional miller column view that shows multiple levels of the hierarchy, or
 \&\fBmultipane\fR to use multiple panes (one per tab) similar to midnight-commander.
+.IP "w3m_delay [float]" 4
+.IX Item "w3m_delay [float]"
+Delay in seconds before displaying an image with the w3m method.
+Increase it in case of experiencing display corruption.
 .IP "wrap_scroll [bool]" 4
 .IX Item "wrap_scroll [bool]"
 Enable scroll wrapping \- moving down while on the last item will wrap around to
diff --git a/doc/ranger.pod b/doc/ranger.pod
index 7da4478b..3062fef5 100644
--- a/doc/ranger.pod
+++ b/doc/ranger.pod
@@ -220,6 +220,12 @@ This feature relies on the dimensions of the terminal's font.  By default, a
 width of 8 and height of 11 are used.  To use other values, set the options
 C<iterm2_font_width> and C<iterm2_font_height> to the desired values.
 
+=head3 terminology
+
+This only works in terminology. It can render vector graphics, but works only locally.
+
+To enable this feature, set the option C<preview_images_method> to terminology.
+
 =head3 urxvt
 
 This only works in urxvt compiled with pixbuf support. Does not work over ssh.
@@ -236,6 +242,13 @@ window.
 
 To enable this feature, set the option C<preview_images_method> to urxvt-full.
 
+=head3 kitty
+
+This only works on Kitty. It requires PIL (or pillow) to work.
+Allows remote image previews, for example in an ssh session.
+
+To enable this feature, set the option C<preview_images_method> to kitty.
+
 =head2 SELECTION
 
 The I<selection> is defined as "All marked files IF THERE ARE ANY, otherwise
@@ -662,7 +675,7 @@ scrolling to switch directories.
 =head1 SETTINGS
 
 This section lists all built-in settings of ranger.  The valid types for the
-value are in [brackets].  The hotkey to toggle the setting is in <brokets>, if
+value are in [brackets].  The hotkey to toggle the setting is in <brakets>, if
 a hotkey exists.
 
 Settings can be changed in the file F<~/.config/ranger/rc.conf> or on the
@@ -908,6 +921,11 @@ Which script should handle generating previews?  If the file doesn't exist, or
 use_preview_script is off, ranger will handle previews itself by just printing
 the content.
 
+=item relative_current_zero [bool]
+
+When line_numbers is set to relative, show 0 on the current line if
+true or show the absolute number of the current line when false.
+
 =item save_backtick_bookmark [bool]
 
 Save the C<`> bookmark to disk.  This bookmark is used to switch to the last
@@ -987,6 +1005,12 @@ Abbreviate $HOME with ~ in the titlebar (first line) of ranger?
 
 Use a unicode "..." character instead of "~" to mark cut-off filenames?
 
+=item bidi_support [bool]
+
+Try to properly display file names in RTL languages (Hebrew, Arabic) by using
+a BIDI algorithm to reverse the relevant parts of the text.
+Requires the python-bidi pip package.
+
 =item update_title [bool]
 
 Set a window title?
diff --git a/doc/rifle.1 b/doc/rifle.1
index ad32e4f4..c8c679ec 100644
--- a/doc/rifle.1
+++ b/doc/rifle.1
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 4.07 (Pod::Simple 3.32)
+.\" Automatically generated by Pod::Man 4.09 (Pod::Simple 3.35)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -129,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "RIFLE 1"
-.TH RIFLE 1 "rifle-1.9.1" "05.03.2018" "rifle manual"
+.TH RIFLE 1 "rifle-1.9.1" "2018-06-07" "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/bash_automatic_cd.sh b/examples/bash_automatic_cd.sh
index bdac5757..04b58e24 100644
--- a/examples/bash_automatic_cd.sh
+++ b/examples/bash_automatic_cd.sh
@@ -6,12 +6,10 @@
 # the last visited one after ranger quits.
 # To undo the effect of this function, you can type "cd -" to return to the
 # original directory.
-# 
-# On OS X 10 or later, replace `usr/bin/ranger` with `/usr/local/bin/ranger`.
 
 function ranger-cd {
     tempfile="$(mktemp -t tmp.XXXXXX)"
-    /usr/bin/ranger --choosedir="$tempfile" "${@:-$(pwd)}"
+    ranger --choosedir="$tempfile" "${@:-$(pwd)}"
     test -f "$tempfile" &&
     if [ "$(cat -- "$tempfile")" != "$(echo -n `pwd`)" ]; then
         cd -- "$(cat "$tempfile")"
diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf
index 58d6d243..62331e22 100644
--- a/ranger/config/rc.conf
+++ b/ranger/config/rc.conf
@@ -86,6 +86,10 @@ set preview_images false
 #   width of 8 and height of 11 are used.  To use other values, set the options
 #   iterm2_font_width and iterm2_font_height to the desired values.
 #
+# * terminology:
+#   Previews images in full color in the terminology terminal emulator.
+#   Supports a wide variety of formats, even vector graphics like svg.
+#
 # * urxvt:
 #   Preview images in full color using urxvt image backgrounds. This
 #   requires using urxvt compiled with pixbuf support.
@@ -93,6 +97,15 @@ set preview_images false
 # * urxvt-full:
 #   The same as urxvt but utilizing not only the preview pane but the
 #   whole terminal window.
+#
+# * kitty:
+#   Preview images in full color using kitty image protocol.
+#   Requires python PIL or pillow library.
+#   If ranger does not share the local filesystem with kitty
+#   the transfer method is changed to encode the whole image;
+#   while slower, this allows remote previews,
+#   for example during an ssh session.
+#   Tmux is unsupported.
 set preview_images_method w3m
 
 # Delay in seconds before displaying an image with the w3m method.
@@ -106,6 +119,10 @@ set iterm2_font_height 11
 # Use a unicode "..." character to mark cut-off filenames?
 set unicode_ellipsis false
 
+# BIDI support - try to properly display file names in RTL languages (Hebrew, Arabic).
+# Requires the python-bidi pip package
+set bidi_support false
+
 # Show dotfiles in the bookmark preview box?
 set show_hidden_bookmarks true
 
@@ -244,9 +261,14 @@ set metadata_deep_search false
 # Clear all existing filters when leaving a directory
 set clear_filters_on_dir_change false
 
-# Disable displaying line numbers in main column
+# Disable displaying line numbers in main column.
+# Possible values: false, absolute, relative.
 set line_numbers false
 
+# When line_numbers=relative show the absolute line number in the
+# current line.
+set relative_current_zero false
+
 # Start line numbers from 1 instead of 0
 set one_indexed false
 
diff --git a/ranger/config/rifle.conf b/ranger/config/rifle.conf
index b1a9bb71..5a87c1a5 100644
--- a/ranger/config/rifle.conf
+++ b/ranger/config/rifle.conf
@@ -164,6 +164,7 @@ ext pptx?|od[dfgpst]|docx?|sxc|xlsx?|xlt|xlw|gnm|gnumeric, has ooffice,     X, f
 ext djvu, has zathura,X, flag f = zathura -- "$@"
 ext djvu, has evince, X, flag f = evince -- "$@"
 ext djvu, has atril,  X, flag f = atril -- "$@"
+ext djvu, has djview, X, flag f = djview -- "$@"
 
 ext epub, has ebook-viewer, X, flag f = ebook-viewer -- "$@"
 ext mobi, has ebook-viewer, X, flag f = ebook-viewer -- "$@"
diff --git a/ranger/container/settings.py b/ranger/container/settings.py
index 11097cec..94e455a5 100644
--- a/ranger/container/settings.py
+++ b/ranger/container/settings.py
@@ -27,6 +27,7 @@ ALLOWED_SETTINGS = {
     'automatically_count_files': bool,
     'autosave_bookmarks': bool,
     'autoupdate_cumulative_size': bool,
+    'bidi_support': bool,
     'cd_bookmarks': bool,
     'cd_tab_case': str,
     'cd_tab_fuzzy': bool,
@@ -65,6 +66,7 @@ ALLOWED_SETTINGS = {
     'preview_images_method': str,
     'preview_max_size': int,
     'preview_script': (str, type(None)),
+    'relative_current_zero': bool,
     'save_backtick_bookmark': bool,
     'save_console_history': bool,
     'save_tabs_on_exit': bool,
@@ -101,7 +103,8 @@ ALLOWED_VALUES = {
     'confirm_on_delete': ['multiple', 'always', 'never'],
     'line_numbers': ['false', 'absolute', 'relative'],
     'one_indexed': [False, True],
-    'preview_images_method': ['w3m', 'iterm2', 'urxvt', 'urxvt-full'],
+    'preview_images_method': ['w3m', 'iterm2', 'terminology',
+                              'urxvt', 'urxvt-full', 'kitty'],
     'vcs_backend_bzr': ['disabled', 'local', 'enabled'],
     'vcs_backend_git': ['enabled', 'disabled', 'local'],
     'vcs_backend_hg': ['disabled', 'local', 'enabled'],
diff --git a/ranger/core/actions.py b/ranger/core/actions.py
index ae8e33d4..86927a6e 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -490,10 +490,13 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
                 mode = narg
             tfile = self.thisfile
             selection = self.thistab.get_selection()
-            if not self.thistab.enter_dir(tfile) and selection:
-                result = self.execute_file(selection, mode=mode)
-                if result in (False, ASK_COMMAND):
-                    self.open_console('open_with ')
+            if selection:
+                if selection.is_directory:
+                    self.thistab.enter_dir(tfile)
+                else:
+                    result = self.execute_file(selection, mode=mode)
+                    if result in (False, ASK_COMMAND):
+                        self.open_console('open_with ')
         elif direction.vertical() and cwd.files:
             pos_new = direction.move(
                 direction=direction.down(),
@@ -1088,7 +1091,11 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
                 data[(-1, -1)] = None
                 data['foundpreview'] = False
             elif rcode == 2:
-                data[(-1, -1)] = self.read_text_file(path, 1024 * 32)
+                text = self.read_text_file(path, 1024 * 32)
+                if not isinstance(text, str):
+                    # Convert 'unicode' to 'str' in Python 2
+                    text = text.encode('utf-8')
+                data[(-1, -1)] = text
             else:
                 data[(-1, -1)] = None
 
diff --git a/ranger/core/fm.py b/ranger/core/fm.py
index d85dd48c..61b3cb11 100644
--- a/ranger/core/fm.py
+++ b/ranger/core/fm.py
@@ -24,7 +24,9 @@ from ranger.container.bookmarks import Bookmarks
 from ranger.core.runner import Runner
 from ranger.ext.img_display import (W3MImageDisplayer, ITerm2ImageDisplayer,
                                     TerminologyImageDisplayer,
-                                    URXVTImageDisplayer, URXVTImageFSDisplayer, ImageDisplayer)
+                                    URXVTImageDisplayer, URXVTImageFSDisplayer,
+                                    KittyImageDisplayer,
+                                    ImageDisplayer)
 from ranger.core.metadata import MetadataManager
 from ranger.ext.rifle import Rifle
 from ranger.container.directory import Directory
@@ -223,7 +225,7 @@ class FM(Actions,  # pylint: disable=too-many-instance-attributes
             for line in entry.splitlines():
                 yield line
 
-    def _get_image_displayer(self):
+    def _get_image_displayer(self):  # pylint: disable=too-many-return-statements
         if self.settings.preview_images_method == "w3m":
             return W3MImageDisplayer()
         elif self.settings.preview_images_method == "iterm2":
@@ -234,6 +236,8 @@ class FM(Actions,  # pylint: disable=too-many-instance-attributes
             return URXVTImageDisplayer()
         elif self.settings.preview_images_method == "urxvt-full":
             return URXVTImageFSDisplayer()
+        elif self.settings.preview_images_method == "kitty":
+            return KittyImageDisplayer()
         return ImageDisplayer()
 
     def _get_thisfile(self):
@@ -424,5 +428,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()) +
-                               '\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 b5d0af77..598ce243 100644
--- a/ranger/core/main.py
+++ b/ranger/core/main.py
@@ -363,7 +363,7 @@ def load_settings(  # pylint: disable=too-many-locals,too-many-branches,too-many
 
         # Load custom commands
         def import_file(name, path):  # From https://stackoverflow.com/a/67692
-            # pragma pylint: disable=no-name-in-module,import-error,no-member
+            # pragma pylint: disable=no-name-in-module,import-error,no-member, deprecated-method
             if sys.version_info >= (3, 5):
                 import importlib.util as util
                 spec = util.spec_from_file_location(name, path)
diff --git a/ranger/core/runner.py b/ranger/core/runner.py
index bb4e512a..f38b026a 100644
--- a/ranger/core/runner.py
+++ b/ranger/core/runner.py
@@ -235,7 +235,7 @@ class Runner(object):  # pylint: disable=too-few-public-methods
             self.fm.signal_emit('runner.execute.before',
                                 popen_kws=popen_kws, context=context)
             try:
-                if 'f' in context.flags:
+                if 'f' in context.flags and 'r' not in context.flags:
                     # This can fail and return False if os.fork() is not
                     # supported, but we assume it is, since curses is used.
                     Popen_forked(**popen_kws)
diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py
index 4f447f39..f78e170b 100644
--- a/ranger/ext/img_display.py
+++ b/ranger/ext/img_display.py
@@ -19,9 +19,13 @@ import imghdr
 import os
 import struct
 import sys
+import warnings
 from subprocess import Popen, PIPE
 
 import termios
+from contextlib import contextmanager
+import codecs
+from tempfile import NamedTemporaryFile
 
 from ranger.core.shared import FileManagerAware
 
@@ -35,6 +39,28 @@ W3MIMGDISPLAY_PATHS = [
     '/usr/local/libexec/w3m/w3mimgdisplay',
 ]
 
+# Helper functions shared between the previewers (make them static methods of the base class?)
+
+
+@contextmanager
+def temporarily_moved_cursor(to_y, to_x):
+    """Common boilerplate code to move the cursor to a drawing area. Use it as:
+        with temporarily_moved_cursor(dest_y, dest_x):
+            your_func_here()"""
+    curses.putp(curses.tigetstr("sc"))
+    move_cur(to_y, to_x)
+    yield
+    curses.putp(curses.tigetstr("rc"))
+    sys.stdout.flush()
+
+
+# this is excised since Terminology needs to move the cursor multiple times
+def move_cur(to_y, to_x):
+    tparm = curses.tparm(curses.tigetstr("cup"), to_y, to_x)
+    # on python2 stdout is already in binary mode, in python3 is accessed via buffer
+    bin_stdout = getattr(sys.stdout, 'buffer', sys.stdout)
+    bin_stdout.write(tparm)
+
 
 class ImageDisplayError(Exception):
     pass
@@ -218,15 +244,8 @@ class ITerm2ImageDisplayer(ImageDisplayer, FileManagerAware):
     """
 
     def draw(self, path, start_x, start_y, width, height):
-        curses.putp(curses.tigetstr("sc"))
-        tparm = curses.tparm(curses.tigetstr("cup"), start_y, start_x)
-        if sys.version_info[0] < 3:
-            sys.stdout.write(tparm)
-        else:
-            sys.stdout.buffer.write(tparm)  # pylint: disable=no-member
-        sys.stdout.write(self._generate_iterm2_input(path, width, height))
-        curses.putp(curses.tigetstr("rc"))
-        sys.stdout.flush()
+        with temporarily_moved_cursor(start_y, start_x):
+            sys.stdout.write(self._generate_iterm2_input(path, width, height))
 
     def clear(self, start_x, start_y, width, height):
         self.fm.ui.win.redrawwin()
@@ -335,44 +354,23 @@ class TerminologyImageDisplayer(ImageDisplayer, FileManagerAware):
         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,
+        with temporarily_moved_cursor(start_y, start_x):
+            # Write intent
+            sys.stdout.write("%s}ic#%d;%d;%s%s" % (
                 self.display_protocol,
+                width, height,
+                path,
                 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
+            # Write Replacement commands ('#')
+            for y in range(0, height):
+                move_cur(start_y + y, start_x)
+                sys.stdout.write("%s}ib%s%s%s}ie%s\n" % (  # needs a newline to work
+                    self.display_protocol,
+                    self.close_protocol,
+                    "#" * width,
+                    self.display_protocol,
+                    self.close_protocol))
 
     def clear(self, start_x, start_y, width, height):
         self.fm.ui.win.redrawwin()
@@ -473,3 +471,196 @@ class URXVTImageFSDisplayer(URXVTImageDisplayer):
     def _get_offsets(self):
         """Center the image."""
         return self._get_centered_offsets()
+
+
+class KittyImageDisplayer(ImageDisplayer):
+    """Implementation of ImageDisplayer for kitty (https://github.com/kovidgoyal/kitty/)
+    terminal. It uses the built APC to send commands and data to kitty,
+    which in turn renders the image. The APC takes the form
+    '\033_Gk=v,k=v...;bbbbbbbbbbbbbb\033\\'
+       |   ---------- --------------  |
+    escape code  |             |    escape code
+                 |  base64 encoded payload
+        key: value pairs as parameters
+    For more info please head over to :
+        https://github.com/kovidgoyal/kitty/blob/master/graphics-protocol.asciidoc"""
+    protocol_start = b'\x1b_G'
+    protocol_end = b'\x1b\\'
+    # we are going to use stdio in binary mode a lot, so due to py2 -> py3
+    # differnces is worth to do this:
+    stdbout = getattr(sys.stdout, 'buffer', sys.stdout)
+    stdbin = getattr(sys.stdin, 'buffer', sys.stdin)
+    # counter for image ids on kitty's end
+    image_id = 0
+    # we need to find out the encoding for a path string, ascii won't cut it
+    try:
+        fsenc = sys.getfilesystemencoding()  # returns None if standard utf-8 is used
+        # throws LookupError if can't find the codec, TypeError if fsenc is None
+        codecs.lookup(fsenc)
+    except (LookupError, TypeError):
+        fsenc = 'utf-8'
+
+    def __init__(self):
+        # the rest of the initializations that require reading stdio or raising exceptions
+        # are delayed to the first draw call, since curses
+        # and ranger exception handler are not online at __init__() time
+        self.needs_late_init = True
+        # to init in _late_init()
+        self.backend = None
+        self.stream = None
+        self.pix_row, self.pix_col = (0, 0)
+
+    def _late_init(self):
+        # tmux
+        if 'kitty' not in os.environ['TERM']:
+            # this doesn't seem to work, ranger freezes...
+            # commenting out the response check does nothing
+            # self.protocol_start = b'\033Ptmux;\033' + self.protocol_start
+            # self.protocol_end += b'\033\\'
+            raise ImgDisplayUnsupportedException(
+                'kitty previews only work in'
+                + ' kitty and outside tmux. '
+                + 'Make sure your TERM contains the string "kitty"')
+
+        # automatic check if we share the filesystem using a dummy file
+        with NamedTemporaryFile() as tmpf:
+            tmpf.write(bytearray([0xFF] * 3))
+            tmpf.flush()
+            for cmd in self._format_cmd_str(
+                    {'a': 'q', 'i': 1, 'f': 24, 't': 'f', 's': 1, 'v': 1, 'S': 3},
+                    payload=base64.standard_b64encode(tmpf.name.encode(self.fsenc))):
+                self.stdbout.write(cmd)
+            sys.stdout.flush()
+            resp = b''
+            while resp[-2:] != self.protocol_end:
+                resp += self.stdbin.read(1)
+        # set the transfer method based on the response
+        # if resp.find(b'OK') != -1:
+        if b'OK' in resp:
+            self.stream = False
+        elif b'EBADF' in resp:
+            self.stream = True
+        else:
+            raise ImgDisplayUnsupportedException(
+                'kitty replied an unexpected response: {}'.format(resp))
+
+        # get the image manipulation backend
+        try:
+            # pillow is the default since we are not going
+            # to spawn other processes, so it _should_ be faster
+            import PIL.Image
+            self.backend = PIL.Image
+        except ImportError:
+            raise ImageDisplayError("Image previews in kitty require PIL (pillow)")
+            # TODO: implement a wrapper class for Imagemagick process to
+            # replicate the functionality we use from im
+
+        # get dimensions of a cell in pixels
+        ret = fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ,
+                          struct.pack('HHHH', 0, 0, 0, 0))
+        n_cols, n_rows, x_px_tot, y_px_tot = struct.unpack('HHHH', ret)
+        self.pix_row, self.pix_col = x_px_tot // n_rows, y_px_tot // n_cols
+        self.needs_late_init = False
+
+    def draw(self, path, start_x, start_y, width, height):
+        self.image_id += 1
+        # dictionary to store the command arguments for kitty
+        # a is the display command, with T going for immediate output
+        # i is the id entifier for the image
+        cmds = {'a': 'T', 'i': self.image_id}
+        # sys.stderr.write('{}-{}@{}x{}\t'.format(start_x, start_y, width, height))
+
+        # finish initialization if it is the first call
+        if self.needs_late_init:
+            self._late_init()
+
+        with warnings.catch_warnings(record=True):  # as warn:
+            warnings.simplefilter('ignore', self.backend.DecompressionBombWarning)
+            image = self.backend.open(path)
+            # TODO: find a way to send a message to the user that
+            # doesn't stop the image from displaying
+            # if warn:
+            #     raise ImageDisplayError(str(warn[-1].message))
+        box = (width * self.pix_row, height * self.pix_col)
+
+        if image.width > box[0] or image.height > box[1]:
+            scale = min(box[0] / image.width, box[1] / image.height)
+            image = image.resize((int(scale * image.width), int(scale * image.height)),
+                                 self.backend.LANCZOS)
+
+        # start_x += ((box[0] - image.width) // 2) // self.pix_row
+        # start_y += ((box[1] - image.height) // 2) // self.pix_col
+        if self.stream:
+            # encode the whole image as base64
+            # TODO: implement z compression
+            # to possibly increase resolution in sent image
+            if image.mode != 'RGB' and image.mode != 'RGBA':
+                image = image.convert('RGB')
+            # t: transmissium medium, 'd' for embedded
+            # f: size of a pixel fragment (8bytes per color)
+            # s, v: size of the image to recompose the flattened data
+            # c, r: size in cells of the viewbox
+            cmds.update({'t': 'd', 'f': len(image.getbands()) * 8,
+                         's': image.width, 'v': image.height, })
+            payload = base64.standard_b64encode(
+                bytearray().join(map(bytes, image.getdata())))
+        else:
+            # put the image in a temporary png file
+            # t: transmissium medium, 't' for temporary file (kitty will delete it for us)
+            # f: size of a pixel fragment (100 just mean that the file is png encoded,
+            #       the only format except raw RGB(A) bitmap that kitty understand)
+            # c, r: size in cells of the viewbox
+            cmds.update({'t': 't', 'f': 100, })
+            with NamedTemporaryFile(prefix='ranger_thumb_', suffix='.png', delete=False) as tmpf:
+                image.save(tmpf, format='png', compress_level=0)
+                payload = base64.standard_b64encode(tmpf.name.encode(self.fsenc))
+
+        with temporarily_moved_cursor(int(start_y), int(start_x)):
+            for cmd_str in self._format_cmd_str(cmds, payload=payload):
+                self.stdbout.write(cmd_str)
+        # catch kitty answer before the escape codes corrupt the console
+        resp = b''
+        while resp[-2:] != self.protocol_end:
+            resp += self.stdbin.read(1)
+        if b'OK' in resp:
+            return
+        else:
+            raise ImageDisplayError('kitty replied "{}"'.format(resp))
+
+    def clear(self, start_x, start_y, width, height):
+        # let's assume that every time ranger call this
+        # it actually wants just to remove the previous image
+        # TODO: implement this using the actual x, y, since the protocol supports it
+        cmds = {'a': 'd', 'i': self.image_id}
+        for cmd_str in self._format_cmd_str(cmds):
+            self.stdbout.write(cmd_str)
+        self.stdbout.flush()
+        # kitty doesn't seem to reply on deletes, checking like we do in draw()
+        # will slows down scrolling with timeouts from select
+        self.image_id -= 1
+
+    def _format_cmd_str(self, cmd, payload=None, max_slice_len=2048):
+        central_blk = ','.join(["{}={}".format(k, v) for k, v in cmd.items()]).encode('ascii')
+        if payload is not None:
+            # we add the m key to signal a multiframe communication
+            # appending the end (m=0) key to a single message has no effect
+            while len(payload) > max_slice_len:
+                payload_blk, payload = payload[:max_slice_len], payload[max_slice_len:]
+                yield self.protocol_start + \
+                    central_blk + b',m=1;' + payload_blk + \
+                    self.protocol_end
+            yield self.protocol_start + \
+                central_blk + b',m=0;' + payload + \
+                self.protocol_end
+        else:
+            yield self.protocol_start + central_blk + b';' + self.protocol_end
+
+    def quit(self):
+        # clear all remaining images, then check if all files went through or are orphaned
+        while self.image_id >= 1:
+            self.clear(0, 0, 0, 0)
+        # for k in self.temp_paths:
+        #     try:
+        #         os.remove(self.temp_paths[k])
+        #     except (OSError, IOError):
+        #         continue
diff --git a/ranger/ext/rifle.py b/ranger/ext/rifle.py
index 672b0597..07a76488 100755
--- a/ranger/ext/rifle.py
+++ b/ranger/ext/rifle.py
@@ -357,7 +357,7 @@ class Rifle(object):  # pylint: disable=too-many-instance-attributes
             self.hook_before_executing(command, self._mimetype, self._app_flags)
             try:
                 if 'r' in flags:
-                    prefix = ['sudo', '-E', 'su', '-mc']
+                    prefix = ['sudo', '-E', 'su', 'root', '-mc']
                 else:
                     prefix = ['/bin/sh', '-c']
 
diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py
index cf322409..bc6f7b1b 100644
--- a/ranger/gui/widgets/browsercolumn.py
+++ b/ranger/gui/widgets/browsercolumn.py
@@ -10,6 +10,12 @@ import stat
 from time import time
 from os.path import splitext
 
+try:
+    from bidi.algorithm import get_display  # pylint: disable=import-error
+    HAVE_BIDI = True
+except ImportError:
+    HAVE_BIDI = False
+
 from ranger.ext.widestring import WideString
 from ranger.core import linemode
 
@@ -183,12 +189,12 @@ class BrowserColumn(Pager):  # pylint: disable=too-many-instance-attributes
     def _draw_file(self):
         """Draw a preview of the file, if the settings allow it"""
         self.win.move(0, 0)
-        if not self.target.accessible:
-            self.addnstr("not accessible", self.wid)
+        if self.target is None or not self.target.has_preview():
             Pager.close(self)
             return
 
-        if self.target is None or not self.target.has_preview():
+        if not self.target.accessible:
+            self.addnstr("not accessible", self.wid)
             Pager.close(self)
             return
 
@@ -206,7 +212,7 @@ class BrowserColumn(Pager):  # pylint: disable=too-many-instance-attributes
         line_number = i
         if self.settings.line_numbers == 'relative':
             line_number = abs(selected_i - i)
-            if line_number == 0:
+            if not self.settings.relative_current_zero and line_number == 0:
                 if self.settings.one_indexed:
                     line_number = selected_i + 1
                 else:
@@ -410,9 +416,15 @@ class BrowserColumn(Pager):  # pylint: disable=too-many-instance-attributes
     def _total_len(predisplay):
         return sum([len(WideString(s)) for s, _ in predisplay])
 
+    def _bidi_transpose(self, text):
+        if self.settings.bidi_support and HAVE_BIDI:
+            return get_display(text)
+        return text
+
     def _draw_text_display(self, text, space):
-        wtext = WideString(text)
-        wext = WideString(splitext(text)[1])
+        bidi_text = self._bidi_transpose(text)
+        wtext = WideString(bidi_text)
+        wext = WideString(splitext(bidi_text)[1])
         wellip = WideString(self.ellipsis[self.settings.unicode_ellipsis])
         if len(wtext) > space:
             wtext = wtext[:max(1, space - len(wext) - len(wellip))] + wellip + wext
diff --git a/ranger/gui/widgets/pager.py b/ranger/gui/widgets/pager.py
index 42adf1e9..d64d4ac1 100644
--- a/ranger/gui/widgets/pager.py
+++ b/ranger/gui/widgets/pager.py
@@ -109,8 +109,9 @@ class Pager(Widget):  # pylint: disable=too-many-instance-attributes
             try:
                 self.fm.image_displayer.draw(self.image, self.x, self.y,
                                              self.wid, self.hei)
-            except ImgDisplayUnsupportedException:
+            except ImgDisplayUnsupportedException as ex:
                 self.fm.settings.preview_images = False
+                self.fm.notify(ex, bad=True)
             except Exception as ex:  # pylint: disable=broad-except
                 self.fm.notify(ex, bad=True)
             else:
@@ -233,7 +234,7 @@ class Pager(Widget):  # pylint: disable=too-many-instance-attributes
     def _generate_lines(self, starty, startx):
         i = starty
         if not self.source:
-            raise StopIteration
+            return
         while True:
             try:
                 line = self._get_line(i).expandtabs(4)
@@ -243,5 +244,5 @@ class Pager(Widget):  # pylint: disable=too-many-instance-attributes
                     line = line[startx:self.wid + startx]
                 yield line.rstrip().replace('\r\n', '\n')
             except IndexError:
-                raise StopIteration
+                return
             i += 1
diff --git a/setup.py b/setup.py
index d7c54b00..edf48c4a 100755
--- a/setup.py
+++ b/setup.py
@@ -111,7 +111,7 @@ def main():
                 'doc/rifle.1',
             ]),
             ('share/doc/ranger', [
-                'doc/colorschemes.txt',
+                'doc/colorschemes.md',
                 'CHANGELOG.md',
                 'HACKING.md',
                 'README.md',