diff options
author | siikamiika <siikamiika@users.noreply.github.com> | 2019-12-09 21:58:02 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-12-09 21:58:02 +0200 |
commit | a9a85c201eda486ff7d3be9b06fca705d4675612 (patch) | |
tree | 71d6a4e00c28a6e45738dff4d0a20c5b458825a3 /ranger | |
parent | 05e7e2c502a07d9b38ca0c8f055445c38b10d049 (diff) | |
parent | 4ff19f6c059fc0d477273304988dc6b7c0ee6320 (diff) | |
download | ranger-a9a85c201eda486ff7d3be9b06fca705d4675612.tar.gz |
Merge branch 'master' into feat-update-icon-title
Diffstat (limited to 'ranger')
-rwxr-xr-x | ranger/config/commands.py | 120 | ||||
-rw-r--r-- | ranger/config/rc.conf | 10 | ||||
-rw-r--r-- | ranger/config/rifle.conf | 26 | ||||
-rw-r--r-- | ranger/container/fsobject.py | 6 | ||||
-rw-r--r-- | ranger/container/settings.py | 5 | ||||
-rw-r--r-- | ranger/core/actions.py | 8 | ||||
-rw-r--r-- | ranger/core/fm.py | 25 | ||||
-rw-r--r-- | ranger/core/main.py | 10 | ||||
-rw-r--r-- | ranger/core/runner.py | 4 | ||||
-rw-r--r-- | ranger/data/mime.types | 4 | ||||
-rwxr-xr-x | ranger/data/scope.sh | 1 | ||||
-rw-r--r-- | ranger/ext/img_display.py | 35 | ||||
-rwxr-xr-x | ranger/ext/rifle.py | 7 | ||||
-rw-r--r-- | ranger/gui/widgets/statusbar.py | 6 |
14 files changed, 217 insertions, 50 deletions
diff --git a/ranger/config/commands.py b/ranger/config/commands.py index 2b1f4940..cb6fa132 100755 --- a/ranger/config/commands.py +++ b/ranger/config/commands.py @@ -572,7 +572,7 @@ class default_linemode(Command): class quit(Command): # pylint: disable=redefined-builtin """:quit - Closes the current tab, if there's only one tab. + Closes the current tab, if there's more than one tab. Otherwise quits if there are no tasks in progress. """ def _exit_no_work(self): @@ -591,7 +591,7 @@ class quit(Command): # pylint: disable=redefined-builtin class quit_bang(Command): """:quit! - Closes the current tab, if there's only one tab. + Closes the current tab, if there's more than one tab. Otherwise force quits immediately. """ name = 'quit!' @@ -699,6 +699,64 @@ class delete(Command): self.fm.delete(files) +class trash(Command): + """:trash + + Tries to move the selection or the files passed in arguments (if any) to + the trash, using rifle rules with label "trash". + The arguments use a shell-like escaping. + + "Selection" is defined as all the "marked files" (by default, you + can mark files with space or v). If there are no marked files, + use the "current file" (where the cursor is) + + When attempting to trash non-empty directories or multiple + marked files, it will require a confirmation. + """ + + allow_abbrev = False + escape_macros_for_shell = True + + def execute(self): + import shlex + from functools import partial + + def is_directory_with_files(path): + return os.path.isdir(path) and not os.path.islink(path) and len(os.listdir(path)) > 0 + + if self.rest(1): + files = shlex.split(self.rest(1)) + many_files = (len(files) > 1 or is_directory_with_files(files[0])) + else: + cwd = self.fm.thisdir + tfile = self.fm.thisfile + if not cwd or not tfile: + self.fm.notify("Error: no file selected for deletion!", bad=True) + return + + # relative_path used for a user-friendly output in the confirmation. + files = [f.relative_path for f in self.fm.thistab.get_selection()] + many_files = (cwd.marked_items or is_directory_with_files(tfile.path)) + + confirm = self.fm.settings.confirm_on_delete + if confirm != 'never' and (confirm != 'multiple' or many_files): + self.fm.ui.console.ask( + "Confirm deletion of: %s (y/N)" % ', '.join(files), + partial(self._question_callback, files), + ('n', 'N', 'y', 'Y'), + ) + else: + # no need for a confirmation, just delete + self.fm.execute_file(files, label='trash') + + def tab(self, tabnum): + return self._tab_directory_content() + + def _question_callback(self, files, answer): + if answer == 'y' or answer == 'Y': + self.fm.execute_file(files, label='trash') + + class jump_non(Command): """:jump_non [-FLAGS...] @@ -1244,7 +1302,7 @@ class copycmap(copymap): class copytmap(copymap): - """:copycmap <keys> <newkeys1> [<newkeys2>...] + """:copytmap <keys> <newkeys1> [<newkeys2>...] Copies a "taskview" keybinding from <keys> to <newkeys> """ @@ -1263,30 +1321,69 @@ class unmap(Command): self.fm.ui.keymaps.unbind(self.context, arg) -class cunmap(unmap): +class uncmap(unmap): + """:uncmap <keys> [<keys2>, ...] + + Remove the given "console" mappings + """ + context = 'console' + + +class cunmap(uncmap): """:cunmap <keys> [<keys2>, ...] Remove the given "console" mappings + + DEPRECATED in favor of uncmap. """ - context = 'browser' + def execute(self): + self.fm.notify("cunmap is deprecated in favor of uncmap!") + super(cunmap, self).execute() -class punmap(unmap): - """:punmap <keys> [<keys2>, ...] + +class unpmap(unmap): + """:unpmap <keys> [<keys2>, ...] Remove the given "pager" mappings """ context = 'pager' -class tunmap(unmap): - """:tunmap <keys> [<keys2>, ...] +class punmap(unpmap): + """:punmap <keys> [<keys2>, ...] + + Remove the given "pager" mappings + + DEPRECATED in favor of unpmap. + """ + + def execute(self): + self.fm.notify("punmap is deprecated in favor of unpmap!") + super(punmap, self).execute() + + +class untmap(unmap): + """:untmap <keys> [<keys2>, ...] Remove the given "taskview" mappings """ context = 'taskview' +class tunmap(untmap): + """:tunmap <keys> [<keys2>, ...] + + Remove the given "taskview" mappings + + DEPRECATED in favor of untmap. + """ + + def execute(self): + self.fm.notify("tunmap is deprecated in favor of untmap!") + super(tunmap, self).execute() + + class map_(Command): """:map <keysequence> <command> @@ -1827,11 +1924,14 @@ class yank(Command): ['xsel'], ['xsel', '-b'], ], + 'wl-copy': [ + ['wl-copy'], + ], 'pbcopy': [ ['pbcopy'], ], } - ordered_managers = ['pbcopy', 'xclip', 'xsel'] + ordered_managers = ['pbcopy', 'wl-copy', 'xclip', 'xsel'] executables = get_executables() for manager in ordered_managers: if manager in executables: diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf index b4bb4bfc..23601fb6 100644 --- a/ranger/config/rc.conf +++ b/ranger/config/rc.conf @@ -67,6 +67,9 @@ set vcs_backend_hg disabled set vcs_backend_bzr disabled set vcs_backend_svn disabled +# Truncate the long commit messages to this length when shown in the statusbar. +set vcs_msg_length 50 + # Use one of the supported image preview protocols set preview_images false @@ -304,6 +307,11 @@ set freeze_files false # Print file sizes in bytes instead of the default human-readable format. set size_in_bytes false +# Warn at startup if RANGER_LEVEL env var is greater than 0, in other words +# give a warning when you nest ranger in a subshell started by ranger. +# Special value "error" makes the warning more visible. +set nested_ranger_warning true + # =================================================================== # == Local Options # =================================================================== @@ -399,6 +407,7 @@ map <F5> copy map <F6> cut map <F7> console mkdir%space map <F8> console delete +#map <F8> console trash map <F10> exit # In case you work on a keyboard with dvorak layout @@ -486,6 +495,7 @@ map p`<any> paste dest=%any_path map p'<any> paste dest=%any_path map dD console delete +map dT console trash map dd cut map ud uncut diff --git a/ranger/config/rifle.conf b/ranger/config/rifle.conf index a90646e2..86f53fd1 100644 --- a/ranger/config/rifle.conf +++ b/ranger/config/rifle.conf @@ -26,7 +26,7 @@ # directory | $1 is a directory # number <n> | change the number of this command to n # terminal | stdin, stderr and stdout are connected to a terminal -# X | $DISPLAY is not empty (i.e. Xorg runs) +# X | A graphical environment is available (darwin, Xorg, or Wayland) # # There are also pseudo-conditions which have a "side effect": # flag <flags> | Change how the program is run. See below. @@ -66,13 +66,13 @@ ext x?html?, has uzbl-tabbed, X, flag f = uzbl-tabbed -- "$@" ext x?html?, has uzbl-browser, X, flag f = uzbl-browser -- "$@" ext x?html?, has uzbl-core, X, flag f = uzbl-core -- "$@" ext x?html?, has midori, X, flag f = midori -- "$@" -ext x?html?, has chromium-browser, X, flag f = chromium-browser -- "$@" -ext x?html?, has chromium, X, flag f = chromium -- "$@" -ext x?html?, has google-chrome, X, flag f = google-chrome -- "$@" ext x?html?, has opera, X, flag f = opera -- "$@" ext x?html?, has firefox, X, flag f = firefox -- "$@" ext x?html?, has seamonkey, X, flag f = seamonkey -- "$@" ext x?html?, has iceweasel, X, flag f = iceweasel -- "$@" +ext x?html?, has chromium-browser, X, flag f = chromium-browser -- "$@" +ext x?html?, has chromium, X, flag f = chromium -- "$@" +ext x?html?, has google-chrome, X, flag f = google-chrome -- "$@" ext x?html?, has epiphany, X, flag f = epiphany -- "$@" ext x?html?, has konqueror, X, flag f = konqueror -- "$@" ext x?html?, has elinks, terminal = elinks "$@" @@ -259,10 +259,26 @@ label wallpaper, number 12, mime ^image, has feh, X = feh --bg-tile "$1" label wallpaper, number 13, mime ^image, has feh, X = feh --bg-center "$1" label wallpaper, number 14, mime ^image, has feh, X = feh --bg-fill "$1" +#------------------------------------------- +# Generic file openers +#------------------------------------------- +label open, has xdg-open = xdg-open -- "$@" +label open, has open = open -- "$@" + # Define the editor for non-text files + pager as last action !mime ^text, !ext xml|json|csv|tex|py|pl|rb|js|sh|php = ask label editor, !mime ^text, !ext xml|json|csv|tex|py|pl|rb|js|sh|php = ${VISUAL:-$EDITOR} -- "$@" label pager, !mime ^text, !ext xml|json|csv|tex|py|pl|rb|js|sh|php = "$PAGER" -- "$@" -# The very last action, so that it's never triggered accidentally, is to execute a program: + +###################################################################### +# The actions below are left so low down in this file on purpose, so # +# they are never triggered accidentally. # +###################################################################### + +# Execute a file as program/script. mime application/x-executable = "$1" + +# Move the file to trash using trash-cli. +label trash, has trash-put = trash-put -- "$@" +label trash = mkdir -p -- ${XDG_DATA_DIR:-$HOME/.ranger}/ranger-trash; mv -- "$@" ${XDG_DATA_DIR:-$HOME/.ranger}/ranger-trash diff --git a/ranger/container/fsobject.py b/ranger/container/fsobject.py index 4fb47354..7de889bf 100644 --- a/ranger/container/fsobject.py +++ b/ranger/container/fsobject.py @@ -342,10 +342,10 @@ class FileSystemObject( # pylint: disable=too-many-instance-attributes,too-many if self.permissions is not None: return self.permissions - if self.is_directory: - perms = ['d'] - elif self.is_link: + if self.is_link: perms = ['l'] + elif self.is_directory: + perms = ['d'] else: perms = ['-'] diff --git a/ranger/container/settings.py b/ranger/container/settings.py index 3075aadf..5f0c528b 100644 --- a/ranger/container/settings.py +++ b/ranger/container/settings.py @@ -58,6 +58,7 @@ ALLOWED_SETTINGS = { 'max_history_size': (int, type(None)), 'metadata_deep_search': bool, 'mouse_enabled': bool, + 'nested_ranger_warning': str, 'one_indexed': bool, 'open_all_images': bool, 'padding_right': bool, @@ -94,6 +95,7 @@ ALLOWED_SETTINGS = { 'vcs_backend_git': str, 'vcs_backend_hg': str, 'vcs_backend_svn': str, + 'vcs_msg_length': int, 'viewmode': str, 'w3m_delay': float, 'w3m_offset': int, @@ -106,6 +108,7 @@ ALLOWED_VALUES = { 'confirm_on_delete': ['multiple', 'always', 'never'], 'draw_borders': ['none', 'both', 'outline', 'separators'], 'line_numbers': ['false', 'absolute', 'relative'], + 'nested_ranger_warning': ['true', 'false', 'error'], 'one_indexed': [False, True], 'preview_images_method': ['w3m', 'iterm2', 'terminology', 'urxvt', 'urxvt-full', 'kitty', @@ -277,7 +280,7 @@ class Settings(SignalDispatcher, FileManagerAware): if path: if path not in self._localsettings: try: - regex = re.compile(path) + regex = re.compile(re.escape(path)) except re.error: # Bad regular expression return self._localregexes[path] = regex diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 435fcf13..18302431 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -1597,20 +1597,22 @@ class Actions( # pylint: disable=too-many-instance-attributes,too-many-public-m Paste the selected items into the current directory or to dest if provided. """ - if dest is None or isdir(dest): + if dest is None: + dest = self.thistab.path + if isdir(dest): loadable = CopyLoader(self.copy_buffer, self.do_cut, overwrite, dest) self.loader.add(loadable, append=append) self.do_cut = False else: - self.notify('Failed to paste. The given path is invalid.', bad=True) + self.notify('Failed to paste. The destination is invalid.', bad=True) def delete(self, files=None): # XXX: warn when deleting mount points/unseen marked files? - self.notify("Deleting!") # COMPAT: old command.py use fm.delete() without arguments if files is None: files = (fobj.path for fobj in self.thistab.get_selection()) + self.notify("Deleting {}!".format(", ".join(files))) files = [os.path.abspath(path) for path in files] for path in files: # Untag the deleted files. diff --git a/ranger/core/fm.py b/ranger/core/fm.py index 43001e8b..7d23c9b6 100644 --- a/ranger/core/fm.py +++ b/ranger/core/fm.py @@ -22,11 +22,7 @@ from ranger.container.tags import Tags, TagsDummy from ranger.gui.ui import UI from ranger.container.bookmarks import Bookmarks from ranger.core.runner import Runner -from ranger.ext.img_display import (W3MImageDisplayer, ITerm2ImageDisplayer, - TerminologyImageDisplayer, - URXVTImageDisplayer, URXVTImageFSDisplayer, - KittyImageDisplayer, UeberzugImageDisplayer, - ImageDisplayer) +from ranger.ext.img_display import get_image_displayer from ranger.core.metadata import MetadataManager from ranger.ext.rifle import Rifle from ranger.container.directory import Directory @@ -104,7 +100,7 @@ class FM(Actions, # pylint: disable=too-many-instance-attributes def set_image_displayer(): if self.image_displayer: self.image_displayer.quit() - self.image_displayer = self._get_image_displayer() + self.image_displayer = get_image_displayer(self.settings.preview_images_method) set_image_displayer() self.settings.signal_bind('setopt.preview_images_method', set_image_displayer, priority=settings.SIGNAL_PRIORITY_AFTER_SYNC) @@ -227,23 +223,6 @@ class FM(Actions, # pylint: disable=too-many-instance-attributes for line in entry.splitlines(): yield line - 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": - return ITerm2ImageDisplayer() - elif self.settings.preview_images_method == "terminology": - return TerminologyImageDisplayer() - elif self.settings.preview_images_method == "urxvt": - return URXVTImageDisplayer() - elif self.settings.preview_images_method == "urxvt-full": - return URXVTImageFSDisplayer() - elif self.settings.preview_images_method == "kitty": - return KittyImageDisplayer() - elif self.settings.preview_images_method == "ueberzug": - return UeberzugImageDisplayer() - return ImageDisplayer() - def _get_thisfile(self): return self.thistab.thisfile diff --git a/ranger/core/main.py b/ranger/core/main.py index 23648677..aefaacbc 100644 --- a/ranger/core/main.py +++ b/ranger/core/main.py @@ -180,6 +180,16 @@ def main( for command in args.cmd: fm.execute_console(command) + if int(os.environ[level]) > 1: + warning = 'Warning:' + nested_warning = "You're in a nested ranger instance!" + warn_when_nested = fm.settings.nested_ranger_warning.lower() + if warn_when_nested == 'true': + fm.notify(' '.join((warning, nested_warning)), bad=False) + elif warn_when_nested == 'error': + fm.notify(' '.join((warning.upper(), nested_warning + '!!')), + bad=True) + if ranger.args.profile: import cProfile import pstats diff --git a/ranger/core/runner.py b/ranger/core/runner.py index f38b026a..d465f070 100644 --- a/ranger/core/runner.py +++ b/ranger/core/runner.py @@ -214,7 +214,9 @@ class Runner(object): # pylint: disable=too-few-public-methods toggle_ui = True context.wait = True if 't' in context.flags: - if 'DISPLAY' not in os.environ: + if not ('WAYLAND_DISPLAY' in os.environ + or sys.platform == 'darwin' + or 'DISPLAY' in os.environ): return self._log("Can not run with 't' flag, no display found!") term = get_term() if isinstance(action, str): diff --git a/ranger/data/mime.types b/ranger/data/mime.types index 900cb7d9..abee11f6 100644 --- a/ranger/data/mime.types +++ b/ranger/data/mime.types @@ -12,11 +12,13 @@ audio/flac flac audio/musepack mpc mpp mp+ -audio/ogg oga ogg spx +audio/ogg oga ogg spx opus audio/wavpack wv wvc audio/webm weba audio/x-ape ape +image/webp webp + video/mkv mkv video/webm webm video/flash flv diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh index 306eeed0..eb09b2bf 100755 --- a/ranger/data/scope.sh +++ b/ranger/data/scope.sh @@ -45,7 +45,6 @@ HIGHLIGHT_STYLE=${HIGHLIGHT_STYLE:-pablo} HIGHLIGHT_OPTIONS="--replace-tabs=${HIGHLIGHT_TABWIDTH} --style=${HIGHLIGHT_STYLE} ${HIGHLIGHT_OPTIONS:-}" PYGMENTIZE_STYLE=${PYGMENTIZE_STYLE:-autumn} - handle_extension() { case "${FILE_EXTENSION_LOWER}" in ## Archive diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py index 2cce5c7a..7e911848 100644 --- a/ranger/ext/img_display.py +++ b/ranger/ext/img_display.py @@ -23,6 +23,7 @@ import warnings import json import threading from subprocess import Popen, PIPE +from collections import defaultdict import termios from contextlib import contextmanager @@ -72,6 +73,33 @@ class ImgDisplayUnsupportedException(Exception): pass +def fallback_image_displayer(): + """Simply makes some noise when chosen. Temporary fallback behavior.""" + + raise ImgDisplayUnsupportedException + + +IMAGE_DISPLAYER_REGISTRY = defaultdict(fallback_image_displayer) + + +def register_image_displayer(nickname=None): + """Register an ImageDisplayer by nickname if available.""" + + def decorator(image_displayer_class): + if nickname: + registry_key = nickname + else: + registry_key = image_displayer_class.__name__ + IMAGE_DISPLAYER_REGISTRY[registry_key] = image_displayer_class + return image_displayer_class + return decorator + + +def get_image_displayer(registry_key): + image_displayer_class = IMAGE_DISPLAYER_REGISTRY[registry_key] + return image_displayer_class() + + class ImageDisplayer(object): """Image display provider functions for drawing images in the terminal""" @@ -90,6 +118,7 @@ class ImageDisplayer(object): pass +@register_image_displayer("w3m") class W3MImageDisplayer(ImageDisplayer, FileManagerAware): """Implementation of ImageDisplayer using w3mimgdisplay, an utilitary program from w3m (a text-based web browser). w3mimgdisplay can display @@ -243,6 +272,7 @@ class W3MImageDisplayer(ImageDisplayer, FileManagerAware): # ranger-independent libraries. +@register_image_displayer("iterm2") class ITerm2ImageDisplayer(ImageDisplayer, FileManagerAware): """Implementation of ImageDisplayer using iTerm2 image display support (http://iterm2.com/images.html). @@ -351,6 +381,7 @@ class ITerm2ImageDisplayer(ImageDisplayer, FileManagerAware): return width, height +@register_image_displayer("terminology") class TerminologyImageDisplayer(ImageDisplayer, FileManagerAware): """Implementation of ImageDisplayer using terminology image display support (https://github.com/billiob/terminology). @@ -390,6 +421,7 @@ class TerminologyImageDisplayer(ImageDisplayer, FileManagerAware): self.clear(0, 0, 0, 0) +@register_image_displayer("urxvt") class URXVTImageDisplayer(ImageDisplayer, FileManagerAware): """Implementation of ImageDisplayer working by setting the urxvt background image "under" the preview pane. @@ -474,6 +506,7 @@ class URXVTImageDisplayer(ImageDisplayer, FileManagerAware): self.clear(0, 0, 0, 0) # dummy assignments +@register_image_displayer("urxvt-full") class URXVTImageFSDisplayer(URXVTImageDisplayer): """URXVTImageDisplayer that utilizes the whole terminal.""" @@ -486,6 +519,7 @@ class URXVTImageFSDisplayer(URXVTImageDisplayer): return self._get_centered_offsets() +@register_image_displayer("kitty") class KittyImageDisplayer(ImageDisplayer, FileManagerAware): """Implementation of ImageDisplayer for kitty (https://github.com/kovidgoyal/kitty/) terminal. It uses the built APC to send commands and data to kitty, @@ -681,6 +715,7 @@ class KittyImageDisplayer(ImageDisplayer, FileManagerAware): # continue +@register_image_displayer("ueberzug") class UeberzugImageDisplayer(ImageDisplayer): """Implementation of ImageDisplayer using ueberzug. Ueberzug can display images in a Xorg session. diff --git a/ranger/ext/rifle.py b/ranger/ext/rifle.py index a55e14c7..a73a188b 100755 --- a/ranger/ext/rifle.py +++ b/ranger/ext/rifle.py @@ -237,7 +237,9 @@ class Rifle(object): # pylint: disable=too-many-instance-attributes self._app_flags = argument return True elif function == 'X': - return sys.platform == 'darwin' or 'DISPLAY' in os.environ + return ('WAYLAND_DISPLAY' in os.environ + or sys.platform == 'darwin' + or 'DISPLAY' in os.environ) elif function == 'env': return bool(os.environ.get(argument)) elif function == 'else': @@ -293,6 +295,9 @@ class Rifle(object): # pylint: disable=too-many-instance-attributes self._app_flags = '' self._app_label = None if skip_ask and cmd == ASK_COMMAND: + # TODO(vifon): Fix properly, see + # https://github.com/ranger/ranger/pull/1341#issuecomment-537264495 + count += 1 continue for test in tests: if not self._eval_condition(test, files, None): diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py index 71064ed4..fd44613e 100644 --- a/ranger/gui/widgets/statusbar.py +++ b/ranger/gui/widgets/statusbar.py @@ -212,7 +212,11 @@ class StatusBar(Widget): # pylint: disable=too-many-instance-attributes left.add_space() left.add(directory.vcs.rootvcs.head['date'].strftime(self.timeformat), 'vcsdate') left.add_space() - left.add(directory.vcs.rootvcs.head['summary'], 'vcscommit') + summary_length = self.settings.vcs_msg_length or 50 + left.add( + directory.vcs.rootvcs.head['summary'][:summary_length], + 'vcscommit' + ) def _get_owner(self, target): uid = target.stat.st_uid |