diff options
-rw-r--r-- | doc/ranger.1 | 42 | ||||
-rw-r--r-- | doc/ranger.pod | 16 | ||||
-rw-r--r-- | examples/rc_emacs.conf | 8 | ||||
-rw-r--r-- | ranger/config/rc.conf | 8 | ||||
-rw-r--r-- | ranger/config/rifle.conf | 5 | ||||
-rw-r--r-- | ranger/container/settings.py | 2 | ||||
-rw-r--r-- | ranger/core/fm.py | 19 | ||||
-rw-r--r-- | ranger/core/linemode.py | 5 | ||||
-rw-r--r-- | ranger/core/main.py | 11 | ||||
-rwxr-xr-x | ranger/data/scope.sh | 6 | ||||
-rw-r--r-- | ranger/ext/img_display.py | 78 | ||||
-rw-r--r-- | ranger/ext/spawn.py | 49 | ||||
-rw-r--r-- | ranger/ext/vcs/vcs.py | 35 | ||||
-rw-r--r-- | ranger/gui/widgets/pager.py | 2 | ||||
-rw-r--r-- | ranger/gui/widgets/statusbar.py | 2 |
15 files changed, 240 insertions, 48 deletions
diff --git a/doc/ranger.1 b/doc/ranger.1 index 184fa7e5..26a78a49 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -1,4 +1,4 @@ -.\" Automatically generated by Pod::Man 4.07 (Pod::Simple 3.32) +.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== @@ -46,7 +46,7 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" -.\" If the F register is >0, we'll generate index entries on stderr for +.\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. @@ -54,16 +54,20 @@ .\" Avoid warning from groff about undefined register 'F'. .de IX .. -.if !\nF .nr F 0 -.if \nF>0 \{\ -. de IX -. tm Index:\\$1\t\\n%\t"\\$2" +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{ +. if \nF \{ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" .. -. if !\nF==2 \{\ -. nr % 0 -. nr F 2 +. if !\nF==2 \{ +. nr % 0 +. nr F 2 +. \} . \} .\} +.rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. @@ -129,7 +133,7 @@ .\" ======================================================================== .\" .IX Title "RANGER 1" -.TH RANGER 1 "ranger-1.7.2" "09/03/2016" "ranger manual" +.TH RANGER 1 "ranger-1.7.2" "10/08/2016" "ranger manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -283,6 +287,24 @@ This only works in iTerm2 compiled with image preview support, but works over ssh. .PP To enable this feature, set the option \f(CW\*(C`preview_images_method\*(C'\fR to iterm2. +.PP +\fIurxvt\fR +.IX Subsection "urxvt" +.PP +This only works in urxvt compiled with pixbuf support. Does not work over ssh. +.PP +Essentially this mode sets an image as a terminal background temporarily, so it +will break any previously set image background. +.PP +To enable this feature, set the option \f(CW\*(C`preview_images_method\*(C'\fR to urxvt. +.PP +\fIurxvt-full\fR +.IX Subsection "urxvt-full" +.PP +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. .SS "\s-1SELECTION\s0" .IX Subsection "SELECTION" The \fIselection\fR is defined as \*(L"All marked files \s-1IF THERE ARE ANY,\s0 otherwise diff --git a/doc/ranger.pod b/doc/ranger.pod index e8252dcb..fe2b45eb 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -186,6 +186,22 @@ ssh. To enable this feature, set the option C<preview_images_method> to iterm2. +=head3 urxvt + +This only works in urxvt compiled with pixbuf support. Does not work over ssh. + +Essentially this mode sets an image as a terminal background temporarily, so it +will break any previously set image background. + +To enable this feature, set the option C<preview_images_method> to urxvt. + +=head3 urxvt-full + +The same as urxvt but utilizing not only the preview pane but the whole terminal +window. + +To enable this feature, set the option C<preview_images_method> to urxvt-full. + =head2 SELECTION The I<selection> is defined as "All marked files IF THERE ARE ANY, otherwise diff --git a/examples/rc_emacs.conf b/examples/rc_emacs.conf index 39a6d654..694496c2 100644 --- a/examples/rc_emacs.conf +++ b/examples/rc_emacs.conf @@ -73,6 +73,14 @@ set preview_images false # Preview images in full color using iTerm2 image previews # (http://iterm2.com/images.html). This requires using iTerm2 compiled # with image preview support. +# +# * urxvt: +# Preview images in full color using urxvt image backgrounds. This +# requires using urxvt compiled with pixbuf support. +# +# * urxvt-full: +# The same as urxvt but utilizing not only the preview pane but the +# whole terminal window. set preview_images_method w3m # Use a unicode "..." character to mark cut-off filenames? diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf index cc7f5007..f25d3a0c 100644 --- a/ranger/config/rc.conf +++ b/ranger/config/rc.conf @@ -79,6 +79,14 @@ set preview_images false # Preview images in full color using iTerm2 image previews # (http://iterm2.com/images.html). This requires using iTerm2 compiled # with image preview support. +# +# * urxvt: +# Preview images in full color using urxvt image backgrounds. This +# requires using urxvt compiled with pixbuf support. +# +# * urxvt-full: +# The same as urxvt but utilizing not only the preview pane but the +# whole terminal window. set preview_images_method w3m # Use a unicode "..." character to mark cut-off filenames? diff --git a/ranger/config/rifle.conf b/ranger/config/rifle.conf index d6165f21..aadc2f2c 100644 --- a/ranger/config/rifle.conf +++ b/ranger/config/rifle.conf @@ -181,8 +181,11 @@ ext xcf, X, flag f = gimp -- "$@" #------------------------------------------- # Archives #------------------------------------------- + +# avoid password prompt by providing empty password +ext 7z, has 7z = 7z -p l "$@" | "$PAGER" # This requires atool -ext 7z|ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz, has als = als -- "$@" | "$PAGER" +ext ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz, has als = als -- "$@" | "$PAGER" ext iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has als = als -- "$@" | "$PAGER" ext 7z|ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz, has aunpack = aunpack -- "$@" ext iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has aunpack = aunpack -- "$@" diff --git a/ranger/container/settings.py b/ranger/container/settings.py index a5d71874..1faf5860 100644 --- a/ranger/container/settings.py +++ b/ranger/container/settings.py @@ -79,7 +79,7 @@ ALLOWED_SETTINGS = { ALLOWED_VALUES = { 'confirm_on_delete': ['always', 'multiple', 'never'], 'line_numbers': ['false', 'absolute', 'relative'], - 'preview_images_method': ['w3m', 'iterm2'], + 'preview_images_method': ['w3m', 'iterm2', 'urxvt', 'urxvt-full'], 'vcs_backend_bzr': ['enabled', 'local', 'disabled'], 'vcs_backend_git': ['enabled', 'local', 'disabled'], 'vcs_backend_hg': ['enabled', 'local', 'disabled'], diff --git a/ranger/core/fm.py b/ranger/core/fm.py index 523239c1..a08de2e1 100644 --- a/ranger/core/fm.py +++ b/ranger/core/fm.py @@ -15,6 +15,7 @@ import sys import ranger.api from ranger.core.actions import Actions from ranger.core.tab import Tab +from ranger.container import settings from ranger.container.tags import Tags, TagsDummy from ranger.gui.ui import UI from ranger.container.bookmarks import Bookmarks @@ -49,7 +50,7 @@ class FM(Actions, SignalDispatcher): self.ui = ui self.start_paths = paths self.directories = dict() - self.log = deque(maxlen=20) + self.log = deque(maxlen=1000) self.bookmarks = bookmarks self.current_tab = 1 self.tabs = {} @@ -70,9 +71,9 @@ class FM(Actions, SignalDispatcher): self.hostname = socket.gethostname() self.home_path = os.path.expanduser('~') - self.log.append('ranger {0} started! Process ID is {1}.' + self.log.appendleft('ranger {0} started! Process ID is {1}.' .format(__version__, os.getpid())) - self.log.append('Running on Python ' + sys.version.replace('\n', '')) + self.log.appendleft('Running on Python ' + sys.version.replace('\n', '')) mimetypes.knownfiles.append(os.path.expanduser('~/.mime.types')) mimetypes.knownfiles.append(self.relpath('data/mime.types')) @@ -97,7 +98,13 @@ class FM(Actions, SignalDispatcher): rifleconf = self.relpath('config/rifle.conf') self.rifle = Rifle(rifleconf) self.rifle.reload_config() - self.image_displayer = self._get_image_displayer() + + def set_image_displayer(): + self.image_displayer = self._get_image_displayer() + set_image_displayer() + self.settings.signal_bind('setopt.preview_images_method', + set_image_displayer, + priority=settings.SIGNAL_PRIORITY_AFTER_SYNC) if not ranger.arg.clean and self.tags is None: self.tags = Tags(self.confpath('tagged')) @@ -203,6 +210,10 @@ class FM(Actions, SignalDispatcher): return W3MImageDisplayer() elif self.settings.preview_images_method == "iterm2": return ITerm2ImageDisplayer() + elif self.settings.preview_images_method == "urxvt": + return URXVTImageDisplayer() + elif self.settings.preview_images_method == "urxvt-full": + return URXVTImageFSDisplayer() else: return ImageDisplayer() diff --git a/ranger/core/linemode.py b/ranger/core/linemode.py index 96557515..a8ce4e6d 100644 --- a/ranger/core/linemode.py +++ b/ranger/core/linemode.py @@ -7,6 +7,7 @@ import sys from abc import * from datetime import datetime from ranger.ext.human_readable import human_readable +from ranger.ext.spawn import spawn DEFAULT_LINEMODE = "filename" @@ -97,9 +98,9 @@ class FileInfoLinemode(LinemodeBase): def infostring(self, file, metadata): if not file.is_directory: - from subprocess import check_output, CalledProcessError + from subprocess import Popen, PIPE, CalledProcessError try: - fileinfo = check_output(["file", "-bL", file.path]).strip() + fileinfo = spawn(["file", "-bL", file.path]).strip() except CalledProcessError: return "unknown" if sys.version_info[0] >= 3: diff --git a/ranger/core/main.py b/ranger/core/main.py index 118a7480..93c71e81 100644 --- a/ranger/core/main.py +++ b/ranger/core/main.py @@ -112,7 +112,7 @@ def main(): if fm.username == 'root': fm.settings.preview_files = False fm.settings.use_preview_script = False - fm.log.append("Running as root, disabling the file previews.") + fm.log.appendleft("Running as root, disabling the file previews.") if not arg.debug: from ranger.ext import curses_interrupt_handler curses_interrupt_handler.install_interrupt_handler() @@ -313,12 +313,11 @@ def load_settings(fm, clean): else: module = importlib.import_module('plugins.' + plugin) fm.commands.load_commands_from_module(module) - fm.log.append("Loaded plugin '%s'." % plugin) - except Exception as e: - fm.log.append("Error in plugin '%s'" % plugin) + fm.log.appendleft("Loaded plugin '%s'." % plugin) + except Exception: import traceback - for line in traceback.format_exception_only(type(e), e): - fm.log.append(line) + fm.log.extendleft(reversed(traceback.format_exc().splitlines())) + fm.notify("Error in plugin '%s'" % plugin, bad=True) ranger.fm = None # COMPAT: Load the outdated options.py diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh index 44fcec2b..a0fb2ec0 100755 --- a/ranger/data/scope.sh +++ b/ranger/data/scope.sh @@ -65,14 +65,18 @@ fi case "$extension" in # Archive extensions: - 7z|a|ace|alz|arc|arj|bz|bz2|cab|cpio|deb|gz|jar|lha|lz|lzh|lzma|lzo|\ + a|ace|alz|arc|arj|bz|bz2|cab|cpio|deb|gz|jar|lha|lz|lzh|lzma|lzo|\ rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xpi|xz|Z|zip) try als "$path" && { dump | trim; exit 0; } try acat "$path" && { dump | trim; exit 3; } try bsdtar -lf "$path" && { dump | trim; exit 0; } exit 1;; rar) + # avoid password prompt by providing empty password try unrar -p- lt "$path" && { dump | trim; exit 0; } || exit 1;; + 7z) + # avoid password prompt by providing empty password + try 7z -p l "$path" && { dump | trim; exit 0; } || exit 1;; # PDF documents: pdf) try pdftotext -l 10 -nopgbrk -q "$path" - && \ diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py index ada7b31c..0e6b08fe 100644 --- a/ranger/ext/img_display.py +++ b/ranger/ext/img_display.py @@ -6,7 +6,7 @@ """Interface for drawing images into the console This module provides functions to draw images in the terminal using supported -implementations, which are currently w3m and iTerm2. +implementations, which are currently w3m, iTerm2 and urxvt. """ import base64 @@ -295,3 +295,79 @@ class ITerm2ImageDisplayer(ImageDisplayer, FileManagerAware): return 0, 0 file_handle.close() return width, height + + +class URXVTImageDisplayer(ImageDisplayer, FileManagerAware): + """Implementation of ImageDisplayer working by setting the urxvt + background image "under" the preview pane. + + Ranger must be running in urxvt for this to work. + + """ + + def _get_max_sizes(self): + """Use the whole terminal.""" + w = 100 + h = 100 + return w, h + + def _get_centered_offsets(self): + """Center the image.""" + x = 50 + y = 50 + return x, y + + def _get_sizes(self): + """Return the width and height of the preview pane in relation to the + whole terminal window. + + """ + if self.fm.ui.pager.visible: + return self._get_max_sizes() + + total_columns_ratio = sum(self.fm.settings.column_ratios) + preview_column_ratio = self.fm.settings.column_ratios[-1] + w = int((100 * preview_column_ratio) / total_columns_ratio) + h = 100 # As much as possible while preserving the aspect ratio. + return w, h + + def _get_offsets(self): + """Return the offsets of the image center.""" + if self.fm.ui.pager.visible: + return self._get_centered_offsets() + + x = 100 # Right-aligned. + y = 2 # TODO: Use the font size to calculate this offset. + return x, y + + def draw(self, path, start_x, start_y, width, height): + # The coordinates in the arguments are ignored as urxvt takes + # the coordinates in a non-standard way: the position of the + # image center as a percentage of the terminal size. As a + # result all values below are in percents. + + x, y = self._get_offsets() + w, h = self._get_sizes() + + sys.stdout.write("\033]20;{path};{w}x{h}+{x}+{y}:op=keep-aspect\a".format(**vars())) + sys.stdout.flush() + + def clear(self, start_x, start_y, width, height): + sys.stdout.write("\033]20;;100x100+1000+1000\a") + sys.stdout.flush() + + def quit(self): + sys.stdout.write("\033]20;;100x100+1000+1000\a") + sys.stdout.flush() + + +class URXVTImageFSDisplayer(URXVTImageDisplayer): + """URXVTImageDisplayer that utilizes the whole terminal.""" + + def _get_sizes(self): + """Use the whole terminal.""" + return self._get_max_sizes() + + def _get_offsets(self): + """Center the image.""" + return self._get_centered_offsets() diff --git a/ranger/ext/spawn.py b/ranger/ext/spawn.py index 7c5c921c..393d48d9 100644 --- a/ranger/ext/spawn.py +++ b/ranger/ext/spawn.py @@ -1,18 +1,57 @@ # This file is part of ranger, the console file manager. # License: GNU GPL version 3, see the file "AUTHORS" for details. -from subprocess import Popen, PIPE +from subprocess import Popen, PIPE, CalledProcessError ENCODING = 'utf-8' -def spawn(*args): - """Runs a program, waits for its termination and returns its stdout""" +def spawn(*args, **kwargs): + """Runs a program, waits for its termination and returns its stdout + + This function is similar to python 2.7's subprocess.check_output, + but is favored due to python 2.6 compatibility. + + The arguments may be: + + spawn(string) + spawn(command, arg1, arg2...) + spawn([command, arg1, arg2]) + + The string will be run through a shell, otherwise the command is executed + directly. + + The keyword argument "decode" determines if the output shall be decoded + with the encoding '%s'. + + Further keyword arguments are passed to Popen. + """ % (ENCODING, ) + if len(args) == 1: popen_arguments = args[0] shell = isinstance(popen_arguments, str) else: popen_arguments = args shell = False - process = Popen(popen_arguments, stdout=PIPE, shell=shell) + + if 'decode' in kwargs: + do_decode = kwargs['decode'] + del kwargs['decode'] + else: + do_decode = True + if 'stdout' not in kwargs: + kwargs['stdout'] = PIPE + if 'shell' not in kwargs: + kwargs['shell'] = shell + + process = Popen(popen_arguments, **kwargs) stdout, stderr = process.communicate() - return stdout.decode(ENCODING) + return_value = process.poll() + if return_value: + error = CalledProcessError(return_value, popen_arguments[0]) + error.output = stdout + raise error + + if do_decode: + return stdout.decode(ENCODING) + else: + return stdout diff --git a/ranger/ext/vcs/vcs.py b/ranger/ext/vcs/vcs.py index 9c7be653..cfdc1e3b 100644 --- a/ranger/ext/vcs/vcs.py +++ b/ranger/ext/vcs/vcs.py @@ -7,6 +7,7 @@ import os import subprocess import threading import time +from ranger.ext.spawn import spawn # Python2 compatibility try: @@ -119,14 +120,13 @@ class Vcs(object): # pylint: disable=too-many-instance-attributes with open(os.devnull, 'w') as devnull: try: if catchout: - output = subprocess.check_output(cmd, cwd=path, stderr=devnull) - if retbytes: - return output - else: - output = output.decode('UTF-8') + output = spawn(cmd, cwd=path, stderr=devnull, + decode=not retbytes) + if (not retbytes and rstrip_newline and + output.endswith('\n')): if rstrip_newline and output.endswith('\n'): return output[:-1] - return output + return output else: subprocess.check_call(cmd, cwd=path, stdout=devnull, stderr=devnull) except (subprocess.CalledProcessError, FileNotFoundError): @@ -460,15 +460,20 @@ class VcsThread(threading.Thread): # pylint: disable=too-many-instance-attribut self.paused.clear() self.awoken.clear() - self._queue_process() - - if self.redraw: - self.redraw = False - for column in self.ui.browser.columns: - if column.target and column.target.is_directory: - column.need_redraw = True - self.ui.status.need_redraw = True - self.ui.redraw() + try: + self._queue_process() + + if self.redraw: + self.redraw = False + for column in self.ui.browser.columns: + if column.target and column.target.is_directory: + column.need_redraw = True + self.ui.status.need_redraw = True + self.ui.redraw() + except Exception: # pylint: disable=broad-except + import traceback + self.ui.fm.log.extendleft(reversed(traceback.format_exc().splitlines())) + self.ui.fm.notify('VCS Exception', bad=True) def pause(self): """Pause thread""" diff --git a/ranger/gui/widgets/pager.py b/ranger/gui/widgets/pager.py index 58b791cd..0c81079b 100644 --- a/ranger/gui/widgets/pager.py +++ b/ranger/gui/widgets/pager.py @@ -231,7 +231,7 @@ class Pager(Widget): line = ansi.char_slice(line, startx, self.wid) + ansi.reset else: line = line[startx:self.wid + startx] - yield line.rstrip() + yield line.rstrip().replace('\r\n', '\n') except IndexError: raise StopIteration i += 1 diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py index 0828c372..0ac62d86 100644 --- a/ranger/gui/widgets/statusbar.py +++ b/ranger/gui/widgets/statusbar.py @@ -290,7 +290,7 @@ class StatusBar(Widget): elif pos >= max_pos: right.add('Bot', base, 'bot') else: - right.add('{:0.0%}'.format(float(pos) / max_pos), + right.add('{0:0.0%}'.format(float(pos) / max_pos), base, 'percentage') else: right.add('0/0 All', base, 'all') |