diff options
-rw-r--r-- | Makefile | 18 | ||||
-rw-r--r-- | doc/ranger.1 | 17 | ||||
-rw-r--r-- | doc/ranger.pod | 17 | ||||
-rw-r--r-- | examples/rc_emacs.conf | 3 | ||||
-rwxr-xr-x | ranger/config/commands.py | 114 | ||||
-rw-r--r-- | ranger/config/rc.conf | 5 | ||||
-rw-r--r-- | ranger/config/rifle.conf | 20 | ||||
-rw-r--r-- | ranger/container/fsobject.py | 6 | ||||
-rw-r--r-- | ranger/container/settings.py | 1 | ||||
-rw-r--r-- | ranger/core/actions.py | 6 | ||||
-rw-r--r-- | ranger/core/fm.py | 25 | ||||
-rw-r--r-- | ranger/core/runner.py | 4 | ||||
-rw-r--r-- | ranger/ext/img_display.py | 35 | ||||
-rwxr-xr-x | ranger/ext/rifle.py | 4 | ||||
-rw-r--r-- | ranger/gui/widgets/statusbar.py | 6 |
15 files changed, 232 insertions, 49 deletions
diff --git a/Makefile b/Makefile index 7e2c62da..15dbb9db 100644 --- a/Makefile +++ b/Makefile @@ -125,11 +125,19 @@ test_other: test: test_py test_shellcheck @echo "Finished testing: All tests passed!" -man: - pod2man --stderr --center='ranger manual' --date='$(NAME)-$(VERSION)' \ - --release=$(shell date -u '+%Y-%m-%d') doc/ranger.pod doc/ranger.1 - pod2man --stderr --center='rifle manual' --date='$(NAME_RIFLE)-$(VERSION_RIFLE)' \ - --release=$(shell date -u '+%Y-%m-%d') doc/rifle.pod doc/rifle.1 +doc/ranger.1: doc/ranger.pod + pod2man --stderr --center='ranger manual' \ + --date='$(NAME)-$(VERSION)' \ + --release=$(shell date -u '+%Y-%m-%d') \ + doc/ranger.pod doc/ranger.1 + +doc/rifle.1: doc/rifle.pod + pod2man --stderr --center='rifle manual' \ + --date='$(NAME_RIFLE)-$(VERSION_RIFLE)' \ + --release=$(shell date -u '+%Y-%m-%d') \ + doc/rifle.pod doc/rifle.1 + +man: doc/ranger.1 doc/rifle.1 manhtml: pod2html doc/ranger.pod --outfile=doc/ranger.1.html diff --git a/doc/ranger.1 b/doc/ranger.1 index ee4c84e7..3521d762 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -133,7 +133,7 @@ .\" ======================================================================== .\" .IX Title "RANGER 1" -.TH RANGER 1 "ranger-1.9.2" "2019-09-24" "ranger manual" +.TH RANGER 1 "ranger-1.9.2" "2019-10-02" "ranger manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -1124,6 +1124,10 @@ Sets the state for the version control backend. The possible values are: \& local display only local state. \& enabled display both, local and remote state. May be slow for hg and bzr. .Ve +.IP "vcs_msg_length [int]" 4 +.IX Item "vcs_msg_length [int]" +Length to truncate first line of the commit messages to when shown in +the statusbar. Defaults to 50. .IP "viewmode [string]" 4 .IX Item "viewmode [string]" Sets the view mode, which can be \fBmiller\fR to display the files in the @@ -1208,6 +1212,7 @@ ranger. For your convenience, this is a list of the \*(L"public\*(R" commands i \& terminal \& tmap key command \& touch filename +\& trash \& travel pattern \& tunmap keys... \& unmap keys... @@ -1563,6 +1568,16 @@ Spawns the \fIx\-terminal-emulator\fR starting in the current directory. .IP "touch \fIfilename\fR" 2 .IX Item "touch filename" Creates an empty file with the name \fIfilename\fR, unless it already exists. +.IP "trash" 2 +.IX Item "trash" +Move all files in the selection to the trash using rifle. Rifle tries to use a +trash manager like \fItrash-cli\fR if available but will fall back to moving files +to either \fI\f(CI$XDG_DATA_HOME\fI/ranger\-trash\fR or \fI~/.ranger/ranger\-trash\fR. This is +a less permanent version of \fIdelete\fR, relying on the user to clear out the +trash whenever it's convenient. While having the possibility of restoring +trashed files until this happens. ranger will ask for a confirmation if you +attempt to trash multiple (marked) files or non-empty directories. This can be +changed by modifying the setting \*(L"confirm_on_delete\*(R". .IP "travel \fIpattern\fR" 2 .IX Item "travel pattern" Filters the current directory for files containing the letters in the diff --git a/doc/ranger.pod b/doc/ranger.pod index beb2fdb8..be964b37 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -1170,6 +1170,11 @@ Sets the state for the version control backend. The possible values are: local display only local state. enabled display both, local and remote state. May be slow for hg and bzr. +=item vcs_msg_length [int] + +Length to truncate first line of the commit messages to when shown in +the statusbar. Defaults to 50. + =item viewmode [string] Sets the view mode, which can be B<miller> to display the files in the @@ -1261,6 +1266,7 @@ ranger. For your convenience, this is a list of the "public" commands including terminal tmap key command touch filename + trash travel pattern tunmap keys... unmap keys... @@ -1651,6 +1657,17 @@ Spawns the I<x-terminal-emulator> starting in the current directory. Creates an empty file with the name I<filename>, unless it already exists. +=item trash + +Move all files in the selection to the trash using rifle. Rifle tries to use a +trash manager like I<trash-cli> if available but will fall back to moving files +to either F<$XDG_DATA_HOME/ranger-trash> or F<~/.ranger/ranger-trash>. This is +a less permanent version of I<delete>, relying on the user to clear out the +trash whenever it's convenient. While having the possibility of restoring +trashed files until this happens. ranger will ask for a confirmation if you +attempt to trash multiple (marked) files or non-empty directories. This can be +changed by modifying the setting "confirm_on_delete". + =item travel I<pattern> Filters the current directory for files containing the letters in the diff --git a/examples/rc_emacs.conf b/examples/rc_emacs.conf index e3596ba5..a69d68fe 100644 --- a/examples/rc_emacs.conf +++ b/examples/rc_emacs.conf @@ -59,6 +59,9 @@ set vcs_backend_git enabled set vcs_backend_hg disabled set vcs_backend_bzr 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 diff --git a/ranger/config/commands.py b/ranger/config/commands.py index 55dd9cd1..cb6fa132 100755 --- a/ranger/config/commands.py +++ b/ranger/config/commands.py @@ -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...] @@ -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 00a20def..e557d4de 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 @@ -401,6 +404,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 @@ -488,6 +492,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..8c48760e 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 "$@" @@ -264,5 +264,15 @@ label wallpaper, number 14, mime ^image, has feh, X = feh --bg-fill "$1" 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 82901ac0..6fc2da5e 100644 --- a/ranger/container/settings.py +++ b/ranger/container/settings.py @@ -94,6 +94,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, diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 25b01e2c..18302431 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -1597,13 +1597,15 @@ 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? 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/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/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 0a614344..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': diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py index 19113012..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'][:50], '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 |