diff options
author | Erez Shermer <erezshermer@gmail.com> | 2018-06-07 10:06:18 +0300 |
---|---|---|
committer | Erez Shermer <erezshermer@gmail.com> | 2018-06-07 10:06:18 +0300 |
commit | 83946054d1468e555ef6f283cc1d27baa8dfb24a (patch) | |
tree | dc5b0b95274566c9215312eb92376e9df223e7c2 /ranger | |
parent | b77e033746fe9120f2083bf59164b9ff4c553aa1 (diff) | |
parent | 81e1070f91ddc139f1a6abdfda962da3b1bb4c60 (diff) | |
download | ranger-83946054d1468e555ef6f283cc1d27baa8dfb24a.tar.gz |
Merge branch 'master' of https://github.com/esm7/ranger
Diffstat (limited to 'ranger')
-rw-r--r-- | ranger/colorschemes/default.py | 2 | ||||
-rw-r--r-- | ranger/config/__init__.py | 2 | ||||
-rwxr-xr-x | ranger/config/commands.py | 26 | ||||
-rw-r--r-- | ranger/config/rc.conf | 27 | ||||
-rw-r--r-- | ranger/config/rifle.conf | 3 | ||||
-rw-r--r-- | ranger/container/directory.py | 5 | ||||
-rw-r--r-- | ranger/container/fsobject.py | 6 | ||||
-rw-r--r-- | ranger/container/settings.py | 3 | ||||
-rw-r--r-- | ranger/core/actions.py | 98 | ||||
-rw-r--r-- | ranger/core/fm.py | 4 | ||||
-rw-r--r-- | ranger/core/main.py | 80 | ||||
-rwxr-xr-x | ranger/data/scope.sh | 3 | ||||
-rw-r--r-- | ranger/ext/direction.py | 4 | ||||
-rw-r--r-- | ranger/ext/human_readable.py | 4 | ||||
-rw-r--r-- | ranger/ext/img_display.py | 26 | ||||
-rwxr-xr-x | ranger/ext/rifle.py | 8 | ||||
-rw-r--r-- | ranger/gui/context.py | 2 | ||||
-rw-r--r-- | ranger/gui/ui.py | 6 | ||||
-rw-r--r-- | ranger/gui/widgets/__init__.py | 8 | ||||
-rw-r--r-- | ranger/gui/widgets/browsercolumn.py | 4 | ||||
-rw-r--r-- | ranger/gui/widgets/statusbar.py | 15 | ||||
-rw-r--r-- | ranger/gui/widgets/view_base.py | 4 |
22 files changed, 254 insertions, 86 deletions
diff --git a/ranger/colorschemes/default.py b/ranger/colorschemes/default.py index c88cdc7c..350c8359 100644 --- a/ranger/colorschemes/default.py +++ b/ranger/colorschemes/default.py @@ -138,6 +138,8 @@ class Default(ColorScheme): attr &= ~bold if context.vcsconflict: fg = magenta + elif context.vcsuntracked: + fg = cyan elif context.vcschanged: fg = red elif context.vcsunknown: diff --git a/ranger/config/__init__.py b/ranger/config/__init__.py index 71df3cb3..0facbdf8 100644 --- a/ranger/config/__init__.py +++ b/ranger/config/__init__.py @@ -1 +1 @@ -"""Default options and configration files""" +"""Default options and configuration files""" diff --git a/ranger/config/commands.py b/ranger/config/commands.py index a3837d8e..7f5fc764 100755 --- a/ranger/config/commands.py +++ b/ranger/config/commands.py @@ -3,8 +3,9 @@ # This configuration file is licensed under the same terms as ranger. # =================================================================== # -# NOTE: If you copied this file to ~/.config/ranger/commands_full.py, -# then it will NOT be loaded by ranger, and only serve as a reference. +# NOTE: If you copied this file to /etc/ranger/commands_full.py or +# ~/.config/ranger/commands_full.py, then it will NOT be loaded by ranger, +# and only serve as a reference. # # =================================================================== # This file contains ranger's commands. @@ -13,9 +14,14 @@ # Note that additional commands are automatically generated from the methods # of the class ranger.core.actions.Actions. # -# You can customize commands in the file ~/.config/ranger/commands.py. -# It has the same syntax as this file. In fact, you can just copy this -# file there with `ranger --copy-config=commands' and make your modifications. +# You can customize commands in the files /etc/ranger/commands.py (system-wide) +# and ~/.config/ranger/commands.py (per user). +# They have the same syntax as this file. In fact, you can just copy this +# file to ~/.config/ranger/commands_full.py with +# `ranger --copy-config=commands_full' and make your modifications, don't +# forget to rename it to commands.py. You can also use +# `ranger --copy-config=commands' to copy a short sample commands.py that +# has everything you need to get started. # But make sure you update your configs when you update ranger. # # =================================================================== @@ -120,9 +126,10 @@ class echo(Command): class cd(Command): - """:cd [-r] <dirname> + """:cd [-r] <path> The cd command changes the directory. + If the path is a file, selects that file. The command 'cd -' is equivalent to typing ``. Using the option "-r" will get you to the real path. """ @@ -1393,6 +1400,8 @@ class scout(Command): self.fm.cd(pattern) else: self.fm.move(right=1) + if self.quickly_executed: + self.fm.block_input(0.5) if self.KEEP_OPEN in flags and thisdir != self.fm.thisdir: # reopen the console: @@ -1718,6 +1727,7 @@ class yank(Command): modes = { '': 'basename', + 'name_without_extension': 'basename_without_extension', 'name': 'basename', 'dir': 'dirname', 'path': 'path', @@ -1750,7 +1760,9 @@ class yank(Command): clipboard_commands = clipboards() - selection = self.get_selection_attr(self.modes[self.arg(1)]) + mode = self.modes[self.arg(1)] + selection = self.get_selection_attr(mode) + new_clipboard_contents = "\n".join(selection) for command in clipboard_commands: process = subprocess.Popen(command, universal_newlines=True, diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf index 5a7a9a6d..1369bb27 100644 --- a/ranger/config/rc.conf +++ b/ranger/config/rc.conf @@ -1,7 +1,8 @@ # =================================================================== # This file contains the default startup commands for ranger. -# To change them, it is recommended to create the file -# ~/.config/ranger/rc.conf and add your custom commands there. +# To change them, it is recommended to create either /etc/ranger/rc.conf +# (system-wide) or ~/.config/ranger/rc.conf (per user) and add your custom +# commands there. # # If you copy this whole file there, you may want to set the environment # variable RANGER_LOAD_DEFAULT_RC to FALSE to avoid loading it twice. @@ -94,6 +95,10 @@ set preview_images false # whole terminal window. set preview_images_method w3m +# Delay in seconds before displaying an image with the w3m method. +# Increase it in case of experiencing display corruption. +set w3m_delay 0.02 + # Default iTerm2 font size (see: preview_images_method: iterm2) set iterm2_font_width 8 set iterm2_font_height 11 @@ -141,6 +146,9 @@ set mouse_enabled true set display_size_in_main_column true set display_size_in_status_bar true +# Display the free disk space in the status bar? +set display_free_space_in_status_bar true + # Display files tags in all columns or only in main column? set display_tags_in_all_columns true @@ -148,7 +156,7 @@ set display_tags_in_all_columns true set update_title false # Set the title to "ranger" in the tmux program? -set update_tmux_title false +set update_tmux_title true # Shorten the title if it gets long? The number defines how many # directories are displayed at once, 0 turns off this feature. @@ -257,6 +265,10 @@ set wrap_scroll false # directories, files and symlinks respectively. set global_inode_type_filter +# This setting allows to freeze the list of files to save I/O bandwidth. It +# should be 'false' during start-up, but you can toggle it by pressing F. +set freeze_files false + # =================================================================== # == Local Options # =================================================================== @@ -278,8 +290,8 @@ alias qall quitall alias qall! quitall! alias setl setlocal -alias filter scout -prt -alias find scout -aeit +alias filter scout -prts +alias find scout -aets alias mark scout -mr alias unmark scout -Mr alias search scout -rs @@ -382,6 +394,7 @@ map L history_go 1 map ] move_parent 1 map [ move_parent -1 map } traverse +map { traverse_backwards map ) jump_non map gh cd ~ @@ -393,6 +406,7 @@ map gL cd -r %f map go cd /opt map gv cd /var map gm cd /media +map gi eval fm.cd('/run/media/' + os.getenv('USER')) map gM cd /mnt map gs cd /srv map gp cd /tmp @@ -408,6 +422,7 @@ map dU shell -p du --max-depth=1 -h --apparent-size | sort -rh map yp yank path map yd yank dir map yn yank name +map y. yank name_without_extension # Filesystem Operations map = chmod @@ -511,6 +526,8 @@ map zc set collapse_preview! map zd set sort_directories_first! map zh set show_hidden! map <C-h> set show_hidden! +copymap <C-h> <backspace> +copymap <backspace> <backspace2> map zI set flushinput! map zi set preview_images! map zm set mouse_enabled! diff --git a/ranger/config/rifle.conf b/ranger/config/rifle.conf index e2653a76..b1a9bb71 100644 --- a/ranger/config/rifle.conf +++ b/ranger/config/rifle.conf @@ -151,7 +151,7 @@ ext pdf, has atril, X, flag f = atril -- "$@" ext pdf, has okular, X, flag f = okular -- "$@" ext pdf, has epdfview, X, flag f = epdfview -- "$@" ext pdf, has qpdfview, X, flag f = qpdfview "$@" -ext pdf, has open, X, flat f = open "$@" +ext pdf, has open, X, flag f = open "$@" ext docx?, has catdoc, terminal = catdoc -- "$@" | "$PAGER" @@ -183,6 +183,7 @@ mime ^image, has eog, X, flag f = eog -- "$@" mime ^image, has eom, X, flag f = eom -- "$@" mime ^image, has nomacs, X, flag f = nomacs -- "$@" mime ^image, has geeqie, X, flag f = geeqie -- "$@" +mime ^image, has gwenview, X, flag f = gwenview -- "$@" mime ^image, has gimp, X, flag f = gimp -- "$@" ext xcf, X, flag f = gimp -- "$@" diff --git a/ranger/container/directory.py b/ranger/container/directory.py index 18b1687c..e281e6c9 100644 --- a/ranger/container/directory.py +++ b/ranger/container/directory.py @@ -338,8 +338,9 @@ class Directory( # pylint: disable=too-many-instance-attributes,too-many-public dirlist = [ os.path.join("/", dirpath, d) for d in dirnames - if self.flat == -1 or - (dirpath.count(os.path.sep) - mypath.count(os.path.sep)) <= self.flat + if self.flat == -1 + or (dirpath.count(os.path.sep) + - mypath.count(os.path.sep)) <= self.flat ] filelist += dirlist filelist += [os.path.join("/", dirpath, f) for f in filenames] diff --git a/ranger/container/fsobject.py b/ranger/container/fsobject.py index 0c9f70f6..37151ecf 100644 --- a/ranger/container/fsobject.py +++ b/ranger/container/fsobject.py @@ -6,7 +6,7 @@ from __future__ import (absolute_import, division, print_function) import re from grp import getgrgid from os import lstat, stat -from os.path import abspath, basename, dirname, realpath, relpath +from os.path import abspath, basename, dirname, realpath, relpath, splitext from pwd import getpwuid from time import time @@ -171,6 +171,10 @@ class FileSystemObject( # pylint: disable=too-many-instance-attributes,too-many return basename_list @lazy_property + def basename_without_extension(self): + return splitext(self.basename)[0] + + @lazy_property def safe_basename(self): return self.basename.translate(_SAFE_STRING_TABLE) diff --git a/ranger/container/settings.py b/ranger/container/settings.py index 9603401f..d0ff2f5e 100644 --- a/ranger/container/settings.py +++ b/ranger/container/settings.py @@ -39,6 +39,7 @@ ALLOWED_SETTINGS = { 'dirname_in_tabs': bool, 'display_size_in_main_column': bool, 'display_size_in_status_bar': bool, + "display_free_space_in_status_bar": bool, 'display_tags_in_all_columns': bool, 'draw_borders': bool, 'draw_progress_bar_in_status_bar': bool, @@ -91,6 +92,7 @@ ALLOWED_SETTINGS = { 'vcs_backend_hg': str, 'vcs_backend_svn': str, 'viewmode': str, + 'w3m_delay': float, 'wrap_scroll': bool, 'xterm_alt_key': bool, } @@ -113,6 +115,7 @@ DEFAULT_VALUES = { type(None): None, str: "", int: 0, + float: 0.0, list: [], tuple: tuple([]), } diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 31fa9518..ae8e33d4 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -7,7 +7,7 @@ from __future__ import (absolute_import, division, print_function) import codecs import os -from os import link, symlink, getcwd, listdir, stat +from os import link, symlink, listdir, stat from os.path import join, isdir, realpath, exists import re import shlex @@ -110,7 +110,8 @@ class Actions( # pylint: disable=too-many-instance-attributes,too-many-public-m self.settings.set(option_name, self._parse_option_value(option_name, value), localpath, tags) - def _parse_option_value(self, name, value): + def _parse_option_value( # pylint: disable=too-many-return-statements + self, name, value): types = self.fm.settings.types_of(name) if bool in types: if value.lower() in ('false', 'off', '0'): @@ -124,6 +125,11 @@ class Actions( # pylint: disable=too-many-instance-attributes,too-many-public-m return int(value) except ValueError: pass + if float in types: + try: + return float(value) + except ValueError: + pass if str in types: return value if list in types: @@ -409,7 +415,8 @@ class Actions( # pylint: disable=too-many-instance-attributes,too-many-public-m # ranger can act as a file chooser when running with --choosefile=... if mode == 0 and 'label' not in kw: if ranger.args.choosefile: - open(ranger.args.choosefile, 'w').write(self.fm.thisfile.path) + with open(ranger.args.choosefile, 'w') as fobj: + fobj.write(self.fm.thisfile.path) if ranger.args.choosefiles: paths = [] @@ -605,6 +612,23 @@ class Actions( # pylint: disable=too-many-instance-attributes,too-many-public-m self.move(down=1) self.traverse() + def traverse_backwards(self): + self.change_mode('normal') + if self.thisdir.pointer == 0: + self.move(left=1) + if self.thisdir.pointer != 0: + self.traverse_backwards() + else: + self.move(up=1) + while True: + if self.thisfile is not None and self.thisfile.is_directory: + self.enter_dir(self.thisfile.path) + self.move(to=100, percentage=True) + elif self.thisdir.pointer == 0: + break + else: + self.move(up=1) + # -------------------------- # -- Shortcuts / Wrappers # -------------------------- @@ -978,6 +1002,7 @@ class Actions( # pylint: disable=too-many-instance-attributes,too-many-public-m if not self.settings.preview_script or not self.settings.use_preview_script: try: + # XXX: properly determine file's encoding return codecs.open(path, 'r', errors='ignore') # IOError for Python2, OSError for Python3 except (IOError, OSError): @@ -1063,14 +1088,7 @@ class Actions( # pylint: disable=too-many-instance-attributes,too-many-public-m data[(-1, -1)] = None data['foundpreview'] = False elif rcode == 2: - fobj = codecs.open(path, 'r', errors='ignore') - try: - data[(-1, -1)] = fobj.read(1024 * 32) - except UnicodeDecodeError: - fobj.close() - fobj = codecs.open(path, 'r', encoding='latin-1', errors='ignore') - data[(-1, -1)] = fobj.read(1024 * 32) - fobj.close() + data[(-1, -1)] = self.read_text_file(path, 1024 * 32) else: data[(-1, -1)] = None @@ -1111,6 +1129,35 @@ class Actions( # pylint: disable=too-many-instance-attributes,too-many-public-m return None + @staticmethod + def read_text_file(path, count=None): + """Encoding-aware reading of a text file.""" + try: + import chardet + except ImportError: + # Guess encoding ourselves. These should be the most frequently used ones. + encodings = ('utf-8', 'utf-16') + for encoding in encodings: + try: + with codecs.open(path, 'r', encoding=encoding) as fobj: + text = fobj.read(count) + except UnicodeDecodeError: + pass + else: + LOG.debug("guessed encoding of '%s' as %r", path, encoding) + return text + else: + with open(path, 'rb') as fobj: + data = fobj.read(count) + result = chardet.detect(data) + LOG.debug("chardet guess for '%s': %s", path, result) + guessed_encoding = result['encoding'] + return codecs.decode(data, guessed_encoding, 'replace') + + # latin-1 as the last resort + with codecs.open(path, 'r', encoding='latin-1', errors='replace') as fobj: + return fobj.read(count) + # -------------------------- # -- Tabs # -------------------------- @@ -1186,10 +1233,10 @@ class Actions( # pylint: disable=too-many-instance-attributes,too-many-public-m def tab_new(self, path=None, narg=None): if narg: return self.tab_open(narg, path) - for i in range(1, 10): - if i not in self.tabs: - return self.tab_open(i, path) - return None + i = 1 + while i in self.tabs: + i += 1 + return self.tab_open(i, path) def tab_switch(self, path, create_directory=False): """Switches to tab of given path, opening a new tab as necessary. @@ -1237,7 +1284,18 @@ class Actions( # pylint: disable=too-many-instance-attributes,too-many-public-m def get_tab_list(self): assert self.tabs, "There must be at least 1 tab at all times" - return sorted(self.tabs) + + class NaturalOrder(object): # pylint: disable=too-few-public-methods + def __init__(self, obj): + self.obj = obj + + def __lt__(self, other): + try: + return self.obj < other.obj + except TypeError: + return str(self.obj) < str(other.obj) + + return sorted(self.tabs, key=NaturalOrder) # -------------------------- # -- Overview of internals @@ -1373,9 +1431,9 @@ class Actions( # pylint: disable=too-many-instance-attributes,too-many-public-m self.notify(new_name) try: if relative: - relative_symlink(fobj.path, join(getcwd(), new_name)) + relative_symlink(fobj.path, join(self.fm.thisdir.path, new_name)) else: - symlink(fobj.path, join(getcwd(), new_name)) + symlink(fobj.path, join(self.fm.thisdir.path, new_name)) except OSError as ex: self.notify('Failed to paste symlink: View log for more info', bad=True, exception=ex) @@ -1384,7 +1442,7 @@ class Actions( # pylint: disable=too-many-instance-attributes,too-many-public-m for fobj in self.copy_buffer: new_name = next_available_filename(fobj.basename) try: - link(fobj.path, join(getcwd(), new_name)) + link(fobj.path, join(self.fm.thisdir.path, new_name)) except OSError as ex: self.notify('Failed to paste hardlink: View log for more info', bad=True, exception=ex) @@ -1392,7 +1450,7 @@ class Actions( # pylint: disable=too-many-instance-attributes,too-many-public-m def paste_hardlinked_subtree(self): for fobj in self.copy_buffer: try: - target_path = join(getcwd(), fobj.basename) + target_path = join(self.fm.thisdir.path, fobj.basename) self._recurse_hardlinked_tree(fobj.path, target_path) except OSError as ex: self.notify('Failed to paste hardlinked subtree: View log for more info', diff --git a/ranger/core/fm.py b/ranger/core/fm.py index c55a3922..d85dd48c 100644 --- a/ranger/core/fm.py +++ b/ranger/core/fm.py @@ -424,5 +424,5 @@ class FM(Actions, # pylint: disable=too-many-instance-attributes if not ranger.args.clean and self.settings.save_tabs_on_exit and len(self.tabs) > 1: with open(self.datapath('tabs'), 'a') as fobj: # Don't save active tab since launching ranger changes the active tab - fobj.write('\0'.join(v.path for t, v in self.tabs.items() - if t != self.current_tab) + '\0\0') + fobj.write('\0'.join(v.path for t, v in self.tabs.items()) + + '\0\0') diff --git a/ranger/core/main.py b/ranger/core/main.py index 4adea918..b5d0af77 100644 --- a/ranger/core/main.py +++ b/ranger/core/main.py @@ -89,14 +89,12 @@ def main( SettingsAware.settings_set(Settings()) + # TODO: deprecate --selectfile if args.selectfile: args.selectfile = os.path.abspath(args.selectfile) args.paths.insert(0, os.path.dirname(args.selectfile)) - if args.paths: - paths = [p[7:] if p.startswith('file:///') else p for p in args.paths] - else: - paths = [os.environ.get('PWD', os.getcwd())] + paths = get_paths(args) paths_inaccessible = [] for path in paths: try: @@ -182,6 +180,7 @@ def main( fm.select_file(args.selectfile) if args.cmd: + fm.enter_dir(fm.thistab.path) for command in args.cmd: fm.execute_console(command) @@ -235,6 +234,24 @@ https://github.com/ranger/ranger/issues return exit_code # pylint: disable=lost-exception +def get_paths(args): + if args.paths: + prefix = 'file:///' + prefix_length = len(prefix) + paths = [path[prefix_length:] if path.startswith(prefix) else path for path in args.paths] + else: + start_directory = os.environ.get('PWD') + is_valid_start_directory = start_directory and os.path.exists(start_directory) + if not is_valid_start_directory: + start_directory = __get_home_directory() + paths = [start_directory] + return paths + + +def __get_home_directory(): + return os.path.expanduser('~') + + def xdg_path(env_var): path = os.environ.get(env_var) if path and os.path.isabs(path): @@ -339,23 +356,50 @@ def load_settings( # pylint: disable=too-many-locals,too-many-branches,too-many fm.commands.load_commands_from_module(commands_default) if not clean: + system_confdir = os.path.join(os.sep, 'etc', 'ranger') + if os.path.exists(system_confdir): + sys.path.append(system_confdir) allow_access_to_confdir(ranger.args.confdir, True) # Load custom commands - custom_comm_path = fm.confpath('commands.py') - if os.path.exists(custom_comm_path): + def import_file(name, path): # From https://stackoverflow.com/a/67692 + # pragma pylint: disable=no-name-in-module,import-error,no-member + if sys.version_info >= (3, 5): + import importlib.util as util + spec = util.spec_from_file_location(name, path) + module = util.module_from_spec(spec) + spec.loader.exec_module(module) + elif (3, 3) <= sys.version_info < (3, 5): + from importlib.machinery import SourceFileLoader + module = SourceFileLoader(name, path).load_module() + else: + import imp + module = imp.load_source(name, path) + # pragma pylint: enable=no-name-in-module,import-error,no-member + return module + + def load_custom_commands(*paths): old_bytecode_setting = sys.dont_write_bytecode sys.dont_write_bytecode = True - try: - import commands as commands_custom - fm.commands.load_commands_from_module(commands_custom) - except ImportError as ex: - LOG.debug("Failed to import custom commands from '%s'", custom_comm_path) - LOG.exception(ex) - else: - LOG.debug("Loaded custom commands from '%s'", custom_comm_path) + for custom_comm_path in paths: + if os.path.exists(custom_comm_path): + try: + commands_custom = import_file('commands', + custom_comm_path) + fm.commands.load_commands_from_module(commands_custom) + except ImportError as ex: + LOG.debug("Failed to import custom commands from '%s'", + custom_comm_path) + LOG.exception(ex) + else: + LOG.debug("Loaded custom commands from '%s'", + custom_comm_path) sys.dont_write_bytecode = old_bytecode_setting + system_comm_path = os.path.join(system_confdir, 'commands.py') + custom_comm_path = fm.confpath('commands.py') + load_custom_commands(system_comm_path, custom_comm_path) + # XXX Load plugins (experimental) plugindir = fm.confpath('plugins') try: @@ -394,12 +438,16 @@ def load_settings( # pylint: disable=too-many-locals,too-many-branches,too-many allow_access_to_confdir(ranger.args.confdir, False) # Load rc.conf custom_conf = fm.confpath('rc.conf') + system_conf = os.path.join(system_confdir, 'rc.conf') default_conf = fm.relpath('config', 'rc.conf') custom_conf_is_readable = os.access(custom_conf, os.R_OK) - if (os.environ.get('RANGER_LOAD_DEFAULT_RC', 'TRUE').upper() != 'FALSE' or - not custom_conf_is_readable): + system_conf_is_readable = os.access(system_conf, os.R_OK) + if (os.environ.get('RANGER_LOAD_DEFAULT_RC', 'TRUE').upper() != 'FALSE' + or not (custom_conf_is_readable or system_conf_is_readable)): fm.source(default_conf) + if system_conf_is_readable: + fm.source(system_conf) if custom_conf_is_readable: fm.source(custom_conf) diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh index 540a910e..25251533 100755 --- a/ranger/data/scope.sh +++ b/ranger/data/scope.sh @@ -60,7 +60,8 @@ handle_extension() { # PDF pdf) # Preview as text conversion - pdftotext -l 10 -nopgbrk -q -- "${FILE_PATH}" - && exit 5 + pdftotext -l 10 -nopgbrk -q -- "${FILE_PATH}" - | fmt -w ${PV_WIDTH} && exit 5 + mutool draw -F txt -i -- "${FILE_PATH}" 1-10 | fmt -w ${PV_WIDTH} && exit 5 exiftool "${FILE_PATH}" && exit 5 exit 1;; diff --git a/ranger/ext/direction.py b/ranger/ext/direction.py index 7df45556..33ebb604 100644 --- a/ranger/ext/direction.py +++ b/ranger/ext/direction.py @@ -97,8 +97,8 @@ class Direction(dict): return self.get('cycle') in (True, 'true', 'on', 'yes') def one_indexed(self): - return ('one_indexed' in self and - self.get('one_indexed') in (True, 'true', 'on', 'yes')) + return ('one_indexed' in self + and self.get('one_indexed') in (True, 'true', 'on', 'yes')) def multiply(self, n): for key in ('up', 'right', 'down', 'left'): diff --git a/ranger/ext/human_readable.py b/ranger/ext/human_readable.py index df74eabf..f365e594 100644 --- a/ranger/ext/human_readable.py +++ b/ranger/ext/human_readable.py @@ -15,6 +15,10 @@ def human_readable(byte, separator=' '): # pylint: disable=too-many-return-stat '1023 M' """ + # handle automatically_count_files false + if byte is None: + return '' + # I know this can be written much shorter, but this long version # performs much better than what I had before. If you attempt to # shorten this code, take performance into consideration. diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py index 67941e27..4f447f39 100644 --- a/ranger/ext/img_display.py +++ b/ranger/ext/img_display.py @@ -60,7 +60,7 @@ class ImageDisplayer(object): pass -class W3MImageDisplayer(ImageDisplayer): +class W3MImageDisplayer(ImageDisplayer, FileManagerAware): """Implementation of ImageDisplayer using w3mimgdisplay, an utilitary program from w3m (a text-based web browser). w3mimgdisplay can display images either in virtual tty (using linux framebuffer) or in a Xorg session. @@ -119,6 +119,14 @@ class W3MImageDisplayer(ImageDisplayer): input_gen = self._generate_w3m_input(path, start_x, start_y, width, height) except ImageDisplayError: raise + + # Mitigate the issue with the horizontal black bars when + # selecting some images on some systems. 2 milliseconds seems + # enough. Adjust as necessary. + if self.fm.settings.w3m_delay > 0: + from time import sleep + sleep(self.fm.settings.w3m_delay) + self.process.stdin.write(input_gen) self.process.stdin.flush() self.process.stdout.readline() @@ -434,20 +442,20 @@ class URXVTImageDisplayer(ImageDisplayer, FileManagerAware): pct_width, pct_height = self._get_sizes() sys.stdout.write( - self.display_protocol + - path + - ";{pct_width}x{pct_height}+{pct_x}+{pct_y}:op=keep-aspect".format( + self.display_protocol + + path + + ";{pct_width}x{pct_height}+{pct_x}+{pct_y}:op=keep-aspect".format( pct_width=pct_width, pct_height=pct_height, pct_x=pct_x, pct_y=pct_y - ) + - self.close_protocol + ) + + self.close_protocol ) sys.stdout.flush() def clear(self, start_x, start_y, width, height): sys.stdout.write( - self.display_protocol + - ";100x100+1000+1000" + - self.close_protocol + self.display_protocol + + ";100x100+1000+1000" + + self.close_protocol ) sys.stdout.flush() diff --git a/ranger/ext/rifle.py b/ranger/ext/rifle.py index 70215039..672b0597 100755 --- a/ranger/ext/rifle.py +++ b/ranger/ext/rifle.py @@ -261,6 +261,14 @@ class Rifle(object): # pylint: disable=too-many-instance-attributes process = Popen(["file", "--mime-type", "-Lb", fname], stdout=PIPE, stderr=PIPE) mimetype, _ = process.communicate() self._mimetype = mimetype.decode(ENCODING).strip() + if self._mimetype == 'application/octet-stream': + try: + process = Popen(["mimetype", "--output-format", "%m", fname], + stdout=PIPE, stderr=PIPE) + mimetype, _ = process.communicate() + self._mimetype = mimetype.decode(ENCODING).strip() + except OSError: + pass return self._mimetype def _build_command(self, files, action, flags): diff --git a/ranger/gui/context.py b/ranger/gui/context.py index d8d1957c..96849686 100644 --- a/ranger/gui/context.py +++ b/ranger/gui/context.py @@ -23,7 +23,7 @@ CONTEXT_KEYS = [ 'keybuffer', 'infostring', 'vcsfile', 'vcsremote', 'vcsinfo', 'vcscommit', 'vcsdate', - 'vcsconflict', 'vcschanged', 'vcsunknown', 'vcsignored', + 'vcsconflict', 'vcschanged', 'vcsunknown', 'vcsignored', 'vcsuntracked', 'vcsstaged', 'vcssync', 'vcsnone', 'vcsbehind', 'vcsahead', 'vcsdiverged' ] diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index 990db0ad..4f76dfab 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -113,7 +113,7 @@ class UI( # pylint: disable=too-many-instance-attributes,too-many-public-method self._draw_title = curses.tigetflag('hs') # has_status_line # Save tmux setting `automatic-rename` - if self.settings.update_tmux_title: + if self.settings.update_tmux_title and 'TMUX' in os.environ: try: self._tmux_automatic_rename = check_output( ['tmux', 'show-window-options', '-v', 'automatic-rename']).strip() @@ -123,7 +123,7 @@ class UI( # pylint: disable=too-many-instance-attributes,too-many-public-method self.update_size() self.is_on = True - if self.settings.update_tmux_title: + if self.settings.update_tmux_title and 'TMUX' in os.environ: sys.stdout.write("\033kranger\033\\") sys.stdout.flush() @@ -172,7 +172,7 @@ class UI( # pylint: disable=too-many-instance-attributes,too-many-public-method DisplayableContainer.destroy(self) # Restore tmux setting `automatic-rename` - if self.settings.update_tmux_title: + if self.settings.update_tmux_title and 'TMUX' in os.environ: if self._tmux_automatic_rename: try: check_output(['tmux', 'set-window-option', diff --git a/ranger/gui/widgets/__init__.py b/ranger/gui/widgets/__init__.py index 36292103..c8f1262b 100644 --- a/ranger/gui/widgets/__init__.py +++ b/ranger/gui/widgets/__init__.py @@ -12,11 +12,11 @@ class Widget(Displayable): 'conflict': ( 'X', ['vcsconflict']), 'untracked': ( - '+', ['vcschanged']), + '?', ['vcsuntracked']), 'deleted': ( '-', ['vcschanged']), 'changed': ( - '*', ['vcschanged']), + '+', ['vcschanged']), 'staged': ( '*', ['vcsstaged']), 'ignored': ( @@ -26,7 +26,7 @@ class Widget(Displayable): 'none': ( ' ', []), 'unknown': ( - '?', ['vcsunknown']), + '!', ['vcsunknown']), } vcsremotestatus_symb = { @@ -41,7 +41,7 @@ class Widget(Displayable): 'none': ( '⌂', ['vcsnone']), 'unknown': ( - '?', ['vcsunknown']), + '!', ['vcsunknown']), } ellipsis = {False: '~', True: '…'} diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py index b38595c1..474fa7a6 100644 --- a/ranger/gui/widgets/browsercolumn.py +++ b/ranger/gui/widgets/browsercolumn.py @@ -324,8 +324,8 @@ class BrowserColumn(Pager): # pylint: disable=too-many-instance-attributes text = current_linemode.filetitle(drawn, metadata) - if drawn.marked and (self.main_column or - self.settings.display_tags_in_all_columns): + if drawn.marked and (self.main_column + or self.settings.display_tags_in_all_columns): text = " " + text # Computing predisplay data. predisplay contains a list of lists diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py index 266d48ca..3457955e 100644 --- a/ranger/gui/widgets/statusbar.py +++ b/ranger/gui/widgets/statusbar.py @@ -275,13 +275,14 @@ class StatusBar(Widget): # pylint: disable=too-many-instance-attributes right.add("/" + str(len(target.marked_items))) else: right.add(human_readable(target.disk_usage, separator='') + " sum") - try: - free = get_free_space(target.mount_path) - except OSError: - pass - else: - right.add(", ", "space") - right.add(human_readable(free, separator='') + " free") + if self.settings.display_free_space_in_status_bar: + try: + free = get_free_space(target.mount_path) + except OSError: + pass + else: + right.add(", ", "space") + right.add(human_readable(free, separator='') + " free") right.add(" ", "space") if target.marked_items: diff --git a/ranger/gui/widgets/view_base.py b/ranger/gui/widgets/view_base.py index 80061004..4493443e 100644 --- a/ranger/gui/widgets/view_base.py +++ b/ranger/gui/widgets/view_base.py @@ -72,8 +72,8 @@ class ViewBase(Widget, DisplayableContainer): # pylint: disable=too-many-instan sorted_bookmarks = sorted( ( item for item in self.fm.bookmarks - if self.fm.settings.show_hidden_bookmarks or - '/.' not in item[1].path + if self.fm.settings.show_hidden_bookmarks + or '/.' not in item[1].path ), key=lambda t: t[0].lower(), ) |