diff -Nru ranger-1.5.2/CHANGELOG ranger-1.5.4/CHANGELOG --- ranger-1.5.2/CHANGELOG 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/CHANGELOG 2012-05-03 16:47:39.000000000 +0000 @@ -1,5 +1,32 @@ This log documents changes between stable versions. +2012-05-03: Version 1.5.4 +* Added exiftool to scope.sh by default +* Fixed a crash when entering a directory with a unicode name +* Speedup in ranger.ext.get_executables + +2012-03-05: Version 1.5.3 +* Added --selectfile option that selects a certain file on startup +* Added --list-tagged-files option +* Added --cmd option to run commands on startup +* Added --profile option for additional debug information on exit +* Added a visual mode (activate with "V", deactivate with Esc) +* Added a reversed visual mode (activate with "uV") +* Added $RANGER_LEVEL environment variable which ranger sets to "1" or higher + so programs can know that they were spawned from ranger +* Added run flag "r" for running with root privileges (needs sudo) +* Added run flag "t" for running in a new terminal (as specified in $TERMCMD) +* Added :relink command to change destinations of symlinks +* Added "dc" binding for getting the cumulative size of a directory +* Added "autoupdate_cumulative_size" option +* Added "pht" binding to Paste Hardlinked subTrees (like cp -l) +* Improved sorting speed of signals (noticable when caching many directories) +* Improved drawing speed +* Fixed unexpected behavior when displaying nonprintable characters +* Fixed :bulkrename to work with files starting with a minus sign +* Fixed RangerChooser example in man page +* Fixed crash when opening images with sxiv/feh by running "ranger " + 2011-10-23: Version 1.5.2 * Fixed graphical bug that appears in certian cases when drawing characters at the right edge. diff -Nru ranger-1.5.2/debian/changelog ranger-1.5.4/debian/changelog --- ranger-1.5.2/debian/changelog 2011-11-18 13:29:35.000000000 +0000 +++ ranger-1.5.4/debian/changelog 2012-05-19 13:00:25.000000000 +0000 @@ -1,3 +1,16 @@ +ranger (1.5.4-1) unstable; urgency=low + + * New upstream release. + * Remove Depends on 'less'. (Closes: #672144) + + -- Vern Sun Sat, 19 May 2012 20:56:15 +0800 + +ranger (1.5.3-1) unstable; urgency=low + + * New upstream release. + + -- Vern Sun Sat, 24 Mar 2012 10:14:32 +0800 + ranger (1.5.2-1) unstable; urgency=low * New upstream release. diff -Nru ranger-1.5.2/debian/control ranger-1.5.4/debian/control --- ranger-1.5.2/debian/control 2011-11-18 12:54:38.000000000 +0000 +++ ranger-1.5.4/debian/control 2012-05-19 12:56:10.000000000 +0000 @@ -4,13 +4,13 @@ Maintainer: Vern Sun Build-Depends: debhelper (>= 7.0.50), python (>= 2.6), ncurses-bin (>= 5.7+20100313-5) X-Python-Version: >= 2.6 -Standards-Version: 3.9.2 +Standards-Version: 3.9.3 Homepage: http://ranger.nongnu.org/ Package: ranger Architecture: all -Depends: ${misc:Depends}, ${python:Depends}, file, less -Suggests: atool, caca-utils, elinks-lite, highlight, poppler-utils +Depends: ${misc:Depends}, ${python:Depends}, file +Suggests: atool, caca-utils, elinks-lite, highlight, less, poppler-utils Provides: ${python:Provides} Description: File manager with an ncurses frontend written in Python Ranger is a free console file manager that gives you greater flexibility and a diff -Nru ranger-1.5.2/debian/copyright ranger-1.5.4/debian/copyright --- ranger-1.5.2/debian/copyright 2011-11-12 13:54:54.000000000 +0000 +++ ranger-1.5.4/debian/copyright 2012-05-19 15:23:56.000000000 +0000 @@ -6,7 +6,7 @@ Upstream Authors: Roman Zimbelmann David Barnett -Copyright (c) 2009-2011, Roman Zimbelmann +Copyright (c) 2009-2012, Roman Zimbelmann Copyright (c) 2010-2011, David Barnett License: diff -Nru ranger-1.5.2/doc/ranger.1 ranger-1.5.4/doc/ranger.1 --- ranger-1.5.2/doc/ranger.1 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/doc/ranger.1 2012-05-03 16:47:39.000000000 +0000 @@ -1,4 +1,4 @@ -.\" Automatically generated by Pod::Man 2.23 (Pod::Simple 3.14) +.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) .\" .\" Standard preamble: .\" ======================================================================== @@ -124,7 +124,7 @@ .\" ======================================================================== .\" .IX Title "RANGER 1" -.TH RANGER 1 "ranger-1.5.2" "10/24/2011" "ranger manual" +.TH RANGER 1 "ranger-1.5.4" "05/03/2012" "ranger manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -178,6 +178,9 @@ .IX Item "--choosedir=targetfile" Allows you to pick a directory with ranger. When you exit ranger, it will write the last visited directory into \fItargetfile\fR. +.IP "\fB\-\-selectfile\fR=\fItargetfile\fR" 14 +.IX Item "--selectfile=targetfile" +Open ranger with \fItargetfile\fR selected. .IP "\fB\-\-copy\-config\fR=\fIfile\fR" 14 .IX Item "--copy-config=file" Create copies of the default configuration files in your local configuration @@ -188,6 +191,10 @@ List common keys which are not bound to any action in the \*(L"browser\*(R" context. This list is not complete, you can bind any key that is supported by curses: use the key code returned by \f(CW\*(C`getch()\*(C'\fR. +.IP "\fB\-\-list\-tagged\-files\fR=\fItag\fR" 14 +.IX Item "--list-tagged-files=tag" +List all files which are tagged with the given tag. Note: Tags are single +characters. The default tag is \*(L"*\*(R" .IP "\fB\-\-fail\-unless\-cd\fR" 14 .IX Item "--fail-unless-cd" Return the exit code 1 if ranger is used to run a file instead of used for file @@ -201,6 +208,10 @@ When a filename is supplied, run it with the given \fIflags\fR to modify behavior. The execution of this file type is explicitly handled in the configuration. +.IP "\fB\-\-cmd\fR=\fIcommand\fR" 14 +.IX Item "--cmd=command" +Execute the command after the configuration has been read. Use this option +multiple times to run multiple commands. .IP "\fB\-\-version\fR" 14 .IX Item "--version" Print the version and exit. @@ -279,18 +290,23 @@ Flags give you a way to modify the behavior of the spawned process. They are used in the commands :open_with (key \*(L"r\*(R") and :shell (key \*(L"!\*(R"). .PP -.Vb 5 +.Vb 7 \& s Silent mode. Output will be discarded. \& d Detach the process. (Run in background) \& p Redirect output to the pager \& w Wait for an Enter\-press when the process is done \& c Run the current file only, instead of the selection +\& r Run application with root privilege (requires sudo) +\& t Run application in a new terminal window .Ve .PP By default, all the flags are off unless specified otherwise in the \fIapps.py\fR configuration file. You can specify as many flags as you want. An uppercase flag negates the effect: \*(L"ddcccDs\*(R" is equivalent to \*(L"cs\*(R". .PP +The \*(L"t\*(R" flag looks for the environment variable \s-1TERMCMD\s0, and uses it as the +terminal command, if it's not set it'll use xterm. +.PP Examples: \f(CW\*(C`:open_with p\*(C'\fR will pipe the output of that process into the pager. \f(CW\*(C`:shell \-w df\*(C'\fR will run \*(L"df\*(R" and wait for you to press Enter before switching back to ranger. @@ -733,6 +749,9 @@ .IX Item "rename newname" Rename the current file. If a file with that name already exists, the renaming will fail. Also try the key binding A for appending something to a file name. +.IP "relink \fInewpath\fR" 2 +.IX Item "relink newpath" +Change the link destination of the current symlink file to . First will load the original link. .IP "save_copy_buffer" 2 .IX Item "save_copy_buffer" Save the copy buffer from \fI~/.config/ranger/copy_buffer\fR. This can be used to @@ -853,6 +872,11 @@ .SH "ENVIRONMENT" .IX Header "ENVIRONMENT" These environment variables have an effect on ranger: +.IP "\s-1RANGER_LEVEL\s0" 8 +.IX Item "RANGER_LEVEL" +Ranger sets this environment variable to \*(L"1\*(R" or increments it if it already +exists. External programs can determine whether they were spawned from ranger +by checking for this variable. .IP "\s-1EDITOR\s0" 8 .IX Item "EDITOR" Defines the editor to be used for the \*(L"E\*(R" key. Defaults to the first installed @@ -861,6 +885,11 @@ .IX Item "SHELL" Defines the shell that ranger is going to use with the :shell command and the \*(L"S\*(R" key. Defaults to \*(L"bash\*(R". +.IP "\s-1TERMCMD\s0" 8 +.IX Item "TERMCMD" +Defines the terminal emulator command that ranger is going to use with the +:terminal command and the \*(L"t\*(R" run flag. Defaults to \*(L"x\-terminal-emulator\*(R" or +\&\*(L"xterm\*(R" .IP "\s-1XDG_CONFIG_HOME\s0" 8 .IX Item "XDG_CONFIG_HOME" Specifies the directory for configuration files. Defaults to \fI\f(CI$HOME\fI/.config\fR. @@ -875,6 +904,14 @@ docstrings. Using this will disable the key on commands. .SH "EXAMPLES" .IX Header "EXAMPLES" +.SS "\s-1BASH:\s0 Display that the shell spawned from ranger:" +.IX Subsection "BASH: Display that the shell spawned from ranger:" +By putting this in ~/.bashrc, \*(L"(in ranger) \*(R" will be displayed next to your +prompt to notify you that the shell spawned from ranger. +.PP +.Vb 1 +\& [ \-n "$RANGER_LEVEL" ] && PS1="$PS1"\*(Aq(in ranger) \*(Aq +.Ve .SS "\s-1VIM:\s0 File Chooser" .IX Subsection "VIM: File Chooser" This is a vim function which allows you to use ranger to select a file for @@ -882,7 +919,7 @@ .PP .Vb 9 \& fun! RangerChooser() -\& silent !ranger \-\-choosefile=/tmp/chosenfile \`[ \-z \*(Aq%\*(Aq ] && echo \-n . || dirname %\` +\& exec "silent !ranger \-\-choosefile=/tmp/chosenfile " . expand("%:p:h") \& if filereadable(\*(Aq/tmp/chosenfile\*(Aq) \& exec \*(Aqedit \*(Aq . system(\*(Aqcat /tmp/chosenfile\*(Aq) \& call system(\*(Aqrm /tmp/chosenfile\*(Aq) diff -Nru ranger-1.5.2/doc/ranger.pod ranger-1.5.4/doc/ranger.pod --- ranger-1.5.2/doc/ranger.pod 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/doc/ranger.pod 2012-05-03 16:47:39.000000000 +0000 @@ -67,6 +67,10 @@ Allows you to pick a directory with ranger. When you exit ranger, it will write the last visited directory into I. +=item B<--selectfile>=I + +Open ranger with I selected. + =item B<--copy-config>=I Create copies of the default configuration files in your local configuration @@ -79,6 +83,11 @@ This list is not complete, you can bind any key that is supported by curses: use the key code returned by C. +=item B<--list-tagged-files>=I + +List all files which are tagged with the given tag. Note: Tags are single +characters. The default tag is "*" + =item B<--fail-unless-cd> Return the exit code 1 if ranger is used to run a file instead of used for file @@ -95,6 +104,11 @@ behavior. The execution of this file type is explicitly handled in the configuration. +=item B<--cmd>=I + +Execute the command after the configuration has been read. Use this option +multiple times to run multiple commands. + =item B<--version> Print the version and exit. @@ -186,11 +200,16 @@ p Redirect output to the pager w Wait for an Enter-press when the process is done c Run the current file only, instead of the selection + r Run application with root privilege (requires sudo) + t Run application in a new terminal window By default, all the flags are off unless specified otherwise in the F configuration file. You can specify as many flags as you want. An uppercase flag negates the effect: "ddcccDs" is equivalent to "cs". +The "t" flag looks for the environment variable TERMCMD, and uses it as the +terminal command, if it's not set it'll use xterm. + Examples: C<:open_with p> will pipe the output of that process into the pager. C<:shell -w df> will run "df" and wait for you to press Enter before switching back to ranger. @@ -778,6 +797,10 @@ Rename the current file. If a file with that name already exists, the renaming will fail. Also try the key binding A for appending something to a file name. +=item relink I + +Change the link destination of the current symlink file to . First will load the original link. + =item save_copy_buffer Save the copy buffer from I<~/.config/ranger/copy_buffer>. This can be used to @@ -937,6 +960,12 @@ =over 8 +=item RANGER_LEVEL + +Ranger sets this environment variable to "1" or increments it if it already +exists. External programs can determine whether they were spawned from ranger +by checking for this variable. + =item EDITOR Defines the editor to be used for the "E" key. Defaults to the first installed @@ -947,6 +976,13 @@ Defines the shell that ranger is going to use with the :shell command and the "S" key. Defaults to "bash". +=item TERMCMD + +Defines the terminal emulator command that ranger is going to use with the +:terminal command and the "t" run flag. Defaults to "x-terminal-emulator" or +"xterm" + + =item XDG_CONFIG_HOME Specifies the directory for configuration files. Defaults to F<$HOME/.config>. @@ -968,13 +1004,20 @@ =head1 EXAMPLES +=head2 BASH: Display that the shell spawned from ranger: + +By putting this in ~/.bashrc, "(in ranger) " will be displayed next to your +prompt to notify you that the shell spawned from ranger. + + [ -n "$RANGER_LEVEL" ] && PS1="$PS1"'(in ranger) ' + =head2 VIM: File Chooser This is a vim function which allows you to use ranger to select a file for opening in your current vim session. fun! RangerChooser() - silent !ranger --choosefile=/tmp/chosenfile `[ -z '%' ] && echo -n . || dirname %` + exec "silent !ranger --choosefile=/tmp/chosenfile " . expand("%:p:h") if filereadable('/tmp/chosenfile') exec 'edit ' . system('cat /tmp/chosenfile') call system('rm /tmp/chosenfile') diff -Nru ranger-1.5.2/Makefile ranger-1.5.4/Makefile --- ranger-1.5.2/Makefile 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/Makefile 2012-05-03 16:47:39.000000000 +0000 @@ -36,6 +36,7 @@ @echo 'PYTHON = $(PYTHON)' @echo 'PYOPTIMIZE = $(PYOPTIMIZE)' @echo 'DOCDIR = $(DOCDIR)' + @echo 'DESTDIR = $(DESTDIR)' help: @echo 'make: Test and compile ranger.' @@ -90,4 +91,4 @@ todo: @grep --color -Ion '\(TODO\|XXX\).*' -r ranger -.PHONY: default options compile clean doc cleandoc snapshot install man todo +.PHONY: clean cleandoc compile default doc help install man manhtml options snapshot test todo diff -Nru ranger-1.5.2/ranger/api/apps.py ranger-1.5.4/ranger/api/apps.py --- ranger-1.5.2/ranger/api/apps.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/api/apps.py 2012-05-03 16:47:39.000000000 +0000 @@ -109,7 +109,7 @@ arguments = handler(context) # flatten if isinstance(arguments, str): - return (arguments, ) + return arguments if arguments is None: return None result = [] diff -Nru ranger-1.5.2/ranger/api/commands.py ranger-1.5.4/ranger/api/commands.py --- ranger-1.5.2/ranger/api/commands.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/api/commands.py 2012-05-03 16:47:39.000000000 +0000 @@ -13,6 +13,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +# TODO: Add an optional "!" to all commands and set a flag if it's there + import os import ranger import re @@ -264,9 +266,11 @@ if len(names) == 0: return - # one result. since it must be a directory, append a slash. + # one result. append a slash if it's a directory if len(names) == 1: - return self.start(1) + join(rel_dirname, names[0]) + '/' + path = join(rel_dirname, names[0]) + slash = '/' if os.path.isdir(path) else '' + return self.start(1) + path + slash # more than one result. append no slash, so the user can # manually type in the slash to advance into that directory diff -Nru ranger-1.5.2/ranger/container/settingobject.py ranger-1.5.4/ranger/container/settingobject.py --- ranger-1.5.2/ranger/container/settingobject.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/container/settingobject.py 2012-05-03 16:47:39.000000000 +0000 @@ -19,6 +19,7 @@ ALLOWED_SETTINGS = { 'autosave_bookmarks': bool, + 'autoupdate_cumulative_size': bool, 'collapse_preview': bool, 'colorscheme_overlay': (type(None), type(lambda:0)), 'colorscheme': str, @@ -31,6 +32,7 @@ 'draw_borders': bool, 'flushinput': bool, 'hidden_filter': lambda x: isinstance(x, str) or hasattr(x, 'match'), + 'init_function': (type(None), type(lambda:0)), 'load_default_rc': (bool, type(None)), 'max_console_history_size': (int, type(None)), 'max_history_size': (int, type(None)), diff -Nru ranger-1.5.2/ranger/container/tags.py ranger-1.5.4/ranger/container/tags.py --- ranger-1.5.2/ranger/container/tags.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/container/tags.py 2012-05-03 16:47:39.000000000 +0000 @@ -37,7 +37,7 @@ if 'tag' in others: tag = others['tag'] else: - tag = self.defautag + tag = self.default_tag self.sync() for item in items: self.tags[item] = tag diff -Nru ranger-1.5.2/ranger/core/actions.py ranger-1.5.4/ranger/core/actions.py --- ranger-1.5.2/ranger/core/actions.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/core/actions.py 2012-05-03 16:47:39.000000000 +0000 @@ -19,8 +19,8 @@ import shutil import string import tempfile -from os.path import join, isdir, realpath -from os import link, symlink, getcwd +from os.path import join, isdir, realpath, exists +from os import link, symlink, getcwd, listdir, stat from inspect import cleandoc import ranger @@ -28,6 +28,7 @@ from ranger.ext.relative_symlink import relative_symlink from ranger.ext.keybinding_parser import key_to_string, construct_keybinding from ranger.ext.shell_escape import shell_quote +from ranger.ext.next_available_filename import next_available_filename from ranger.core.shared import FileManagerAware, EnvironmentAware, \ SettingsAware from ranger.fsobject import File @@ -41,6 +42,11 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): search_method = 'ctime' + mode = 'normal' # either 'normal' or 'visual'. + _visual_reverse = False + _visual_start = None + _visual_start_pos = None + _previous_selection = None # -------------------------- # -- Basic Commands @@ -56,6 +62,32 @@ self.previews = {} self.env.garbage_collect(-1, self.tabs) self.enter_dir(old_path) + self.change_mode('normal') + + def change_mode(self, mode): + if mode == self.mode: + return + if mode == 'visual': + self._visual_start = self.env.cwd.pointed_obj + self._visual_start_pos = self.env.cwd.pointer + self._previous_selection = set(self.env.cwd.marked_items) + self.mark_files(val=not self._visual_reverse, movedown=False) + elif mode == 'normal': + if self.mode == 'visual': + self._visual_start = None + self._visual_start_pos = None + self._previous_selection = None + else: + return + self.mode = mode + self.ui.status.request_redraw() + + def toggle_visual_mode(self, reverse=False): + if self.mode == 'normal': + self._visual_reverse = reverse + self.change_mode('visual') + else: + self.change_mode('normal') def reload_cwd(self): try: @@ -87,12 +119,19 @@ self.notify("Aborting: " + item.get_description()) self.loader.remove(index=0) + def get_cumulative_size(self): + for f in self.env.get_selection() or (): + f.look_up_cumulative_size() + self.ui.status.request_redraw() + self.ui.redraw_main_column() + def redraw_window(self): """Redraw the window""" self.ui.redraw_window() def open_console(self, string='', prompt=None, position=None): - """Open the console if the current UI supports that""" + """Open the console""" + self.change_mode('normal') self.ui.open_console(string, prompt=prompt, position=position) def execute_console(self, string='', wildcards=[], quantifier=None): @@ -247,15 +286,16 @@ Both flags and mode specify how the program is run.""" # ranger can act as a file chooser when running with --choosefile=... - if ranger.arg.choosefile: - open(ranger.arg.choosefile, 'w').write(self.fm.env.cf.path) - - if ranger.arg.choosefiles: - open(ranger.arg.choosefiles, 'w').write("".join( - f.path + "\n" for f in self.fm.env.get_selection())) + if ('mode' not in kw or kw['mode'] == 0) and 'app' not in kw: + if ranger.arg.choosefile: + open(ranger.arg.choosefile, 'w').write(self.fm.env.cf.path) + + if ranger.arg.choosefiles: + open(ranger.arg.choosefiles, 'w').write("".join( + f.path + "\n" for f in self.fm.env.get_selection())) - if ranger.arg.choosefile or ranger.arg.choosefiles: - raise SystemExit() + if ranger.arg.choosefile or ranger.arg.choosefiles: + raise SystemExit() if isinstance(files, set): files = list(files) @@ -306,6 +346,7 @@ except: return self.env.enter_dir(directory) + self.change_mode('normal') if cwd and cwd.accessible and cwd.content_loaded: if 'right' in direction: mode = 0 @@ -324,8 +365,34 @@ current=cwd.pointer, pagesize=self.ui.browser.hei) cwd.move(to=newpos) + if self.mode == 'visual': + try: + startpos = cwd.index(self._visual_start) + except: + self._visual_start = None + startpos = min(self._visual_start_pos, len(cwd)) + # The files between here and _visual_start_pos + targets = set(cwd.files[min(startpos, newpos):\ + max(startpos, newpos) + 1]) + # The selection before activating visual mode + old = self._previous_selection + # The current selection + current = set(cwd.marked_items) + + # Set theory anyone? + if not self._visual_reverse: + for f in targets - current: + cwd.mark_item(f, True) + for f in current - old - targets: + cwd.mark_item(f, False) + else: + for f in targets & current: + cwd.mark_item(f, False) + for f in old - current - targets: + cwd.mark_item(f, True) def move_parent(self, n, narg=None): + self.change_mode('normal') if narg is not None: n *= narg parent = self.env.at_level(-1) @@ -354,18 +421,20 @@ def enter_dir(self, path, remember=False, history=True): """Enter the directory at the given path""" - if remember: - cwd = self.env.cwd - result = self.env.enter_dir(path, history=history) - self.bookmarks.remember(cwd) - return result - return self.env.enter_dir(path, history=history) + cwd = self.env.cwd + result = self.env.enter_dir(path, history=history) + if cwd != self.env.cwd: + if remember: + self.bookmarks.remember(cwd) + self.change_mode('normal') + return result def cd(self, path, remember=True): """enter the directory at the given path, remember=True""" self.enter_dir(path, remember=remember) def traverse(self): + self.change_mode('normal') cf = self.env.cf cwd = self.env.cwd if cf is not None and cf.is_directory: @@ -470,6 +539,8 @@ cwd.toggle_all_marks() else: cwd.mark_all(val) + if self.mode == 'visual': + self.change_mode('normal') else: for i in range(cwd.pointer, min(cwd.pointer + narg, len(cwd))): item = cwd.files[i] @@ -762,13 +833,14 @@ # directory paths only. def tab_open(self, name, path=None): - do_emit_signal = name != self.current_tab + tab_has_changed = name != self.current_tab self.current_tab = name if path or (name in self.tabs): self.enter_dir(path or self.tabs[name]) else: self._update_current_tab() - if do_emit_signal: + if tab_has_changed: + self.change_mode('normal') self.signal_emit('tab.change') def tab_close(self, name=None): @@ -913,21 +985,46 @@ def paste_symlink(self, relative=False): copied_files = self.env.copy for f in copied_files: + self.notify(next_available_filename(f.basename)) try: + new_name = next_available_filename(f.basename) if relative: - relative_symlink(f.path, join(getcwd(), f.basename)) + relative_symlink(f.path, join(getcwd(), new_name)) else: - symlink(f.path, join(getcwd(), f.basename)) + symlink(f.path, join(getcwd(), new_name)) except Exception as x: self.notify(x) def paste_hardlink(self): for f in self.env.copy: try: - link(f.path, join(getcwd(), f.basename)) + new_name = next_available_filename(f.basename) + link(f.path, join(getcwd(), new_name)) except Exception as x: self.notify(x) + def paste_hardlinked_subtree(self): + for f in self.env.copy: + try: + target_path = join(getcwd(), f.basename) + self._recurse_hardlinked_tree(f.path, target_path) + except Exception as x: + self.notify(x) + + def _recurse_hardlinked_tree(self, source_path, target_path): + if isdir(source_path): + if not exists(target_path): + os.mkdir(target_path, stat(source_path).st_mode) + for item in listdir(source_path): + self._recurse_hardlinked_tree( + join(source_path, item), + join(target_path, item)) + else: + if not exists(target_path) \ + or stat(source_path).st_ino != stat(target_path).st_ino: + link(source_path, + next_available_filename(target_path)) + def paste(self, overwrite=False): """Paste the selected items into the current directory""" copied_files = tuple(self.env.copy) diff -Nru ranger-1.5.2/ranger/core/fm.py ranger-1.5.4/ranger/core/fm.py --- ranger-1.5.2/ranger/core/fm.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/core/fm.py 2012-05-03 16:47:39.000000000 +0000 @@ -85,10 +85,13 @@ def mylogfunc(text): self.notify(text, bad=True) self.run = Runner(ui=self.ui, apps=self.apps, - logfunc=mylogfunc) + logfunc=mylogfunc, fm=self) self.env.signal_bind('cd', self._update_current_tab) + if self.settings.init_function: + self.settings.init_function(self) + def destroy(self): debug = ranger.arg.debug if self.ui: @@ -209,6 +212,8 @@ finally: if ranger.arg.choosedir and self.env.cwd and self.env.cwd.path: + # XXX: UnicodeEncodeError: 'utf-8' codec can't encode character + # '\udcf6' in position 42: surrogates not allowed open(ranger.arg.choosedir, 'w').write(self.env.cwd.path) self.bookmarks.remember(env.cwd) self.bookmarks.save() diff -Nru ranger-1.5.2/ranger/core/helper.py ranger-1.5.4/ranger/core/helper.py --- ranger-1.5.2/ranger/core/helper.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/core/helper.py 2012-05-03 16:47:39.000000000 +0000 @@ -66,6 +66,16 @@ ", it will write the name of the last visited directory to TARGET") parser.add_option('--list-unused-keys', action='store_true', help="List common keys which are not bound to any action.") + parser.add_option('--selectfile', type='string', metavar='filepath', + help="Open ranger with supplied file selected.") + parser.add_option('--list-tagged-files', type='string', default=None, + metavar='tag', + help="List all files which are tagged with the given tag, default: *") + parser.add_option('--profile', action='store_true', + help="Print statistics of CPU usage on exit.") + parser.add_option('--cmd', action='append', type='string', metavar='COMMAND', + help="Execute COMMAND after the configuration has been read. " + "Use this option multiple times to run multiple commands.") options, positional = parser.parse_args() arg = OpenStruct(options.__dict__, targets=positional) diff -Nru ranger-1.5.2/ranger/core/loader.py ranger-1.5.4/ranger/core/loader.py --- ranger-1.5.2/ranger/core/loader.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/core/loader.py 2012-05-03 16:47:39.000000000 +0000 @@ -146,7 +146,7 @@ return string.decode("utf-8") except (UnicodeDecodeError): if HAVE_CHARDET: - return string.decode(chardet.detect(str)["encoding"]) + return string.decode(chardet.detect(string)["encoding"]) else: return "" diff -Nru ranger-1.5.2/ranger/core/main.py ranger-1.5.4/ranger/core/main.py --- ranger-1.5.2/ranger/core/main.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/core/main.py 2012-05-03 16:47:39.000000000 +0000 @@ -38,6 +38,13 @@ except: print("Warning: Unable to set locale. Expect encoding problems.") + # so that programs can know that ranger spawned them: + level = 'RANGER_LEVEL' + if level in os.environ and os.environ[level].isdigit(): + os.environ[level] = str(int(os.environ[level]) + 1) + else: + os.environ[level] = '1' + if not 'SHELL' in os.environ: os.environ['SHELL'] = 'bash' @@ -46,9 +53,27 @@ fm = FM() fm.copy_config_files(arg.copy_config) return 1 if arg.fail_unless_cd else 0 + if arg.list_tagged_files: + fm = FM() + try: + f = open(fm.confpath('tagged'), 'r') + except: + pass + else: + for line in f.readlines(): + if len(line) > 2 and line[1] == ':': + if line[0] in arg.list_tagged_files: + sys.stdout.write(line[2:]) + elif len(line) > 0 and '*' in arg.list_tagged_files: + sys.stdout.write(line) + return 1 if arg.fail_unless_cd else 0 SettingsAware._setup(clean=arg.clean) + if arg.selectfile: + arg.selectfile = os.path.abspath(arg.selectfile) + arg.targets.insert(0, os.path.dirname(arg.selectfile)) + targets = arg.targets or ['.'] target = targets[0] if arg.targets: @@ -62,7 +87,8 @@ print(string) from ranger.core.runner import Runner from ranger.fsobject import File - runner = Runner(logfunc=print_function) + fm = FM() + runner = Runner(logfunc=print_function, fm=fm) load_apps(runner, arg.clean) runner(files=[File(target)], mode=arg.mode, flags=arg.flags) return 1 if arg.fail_unless_cd else 0 @@ -97,10 +123,26 @@ from ranger.ext import curses_interrupt_handler curses_interrupt_handler.install_interrupt_handler() + if arg.selectfile: + fm.select_file(arg.selectfile) + # Run the file manager fm.initialize() fm.ui.initialize() - fm.loop() + + if arg.cmd: + for command in arg.cmd: + fm.execute_console(command) + + if ranger.arg.profile: + import cProfile + import pstats + profile = None + ranger.__fm = fm + cProfile.run('ranger.__fm.loop()', '/tmp/ranger_profile') + profile = pstats.Stats('/tmp/ranger_profile', stream=sys.stderr) + else: + fm.loop() except Exception: import traceback crash_traceback = traceback.format_exc() @@ -116,11 +158,16 @@ fm.ui.destroy() except (AttributeError, NameError): pass + if ranger.arg.profile and profile: + profile.strip_dirs().sort_stats('cumulative').print_callees() if crash_traceback: print("ranger version: %s, executed with python %s" % (ranger.__version__, sys.version.split()[0])) print("Locale: %s" % '.'.join(str(s) for s in locale.getlocale())) - print("Current file: %s" % filepath) + try: + print("Current file: %s" % filepath) + except: + pass print(crash_traceback) print("ranger crashed. " \ "Please report this traceback at:") diff -Nru ranger-1.5.2/ranger/core/runner.py ranger-1.5.4/ranger/core/runner.py --- ranger-1.5.2/ranger/core/runner.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/core/runner.py 2012-05-03 16:47:39.000000000 +0000 @@ -30,15 +30,18 @@ p: redirect output to the pager c: run only the current file (not handled here) w: wait for enter-press afterwards +r: run application with root privilege (requires sudo) +t: run application in a new terminal window (An uppercase key negates the respective lower case flag) """ import os import sys from subprocess import Popen, PIPE +from ranger.ext.get_executables import get_executables -ALLOWED_FLAGS = 'sdpwcSDPWC' +ALLOWED_FLAGS = 'sdpwcrtSDPWCRT' def press_enter(): @@ -94,8 +97,9 @@ class Runner(object): - def __init__(self, ui=None, logfunc=None, apps=None): + def __init__(self, ui=None, logfunc=None, apps=None, fm=None): self.ui = ui + self.fm = fm self.logfunc = logfunc self.apps = apps self.zombies = set() @@ -132,7 +136,7 @@ # creating a Context object and passing it to # an Application object. - context = Context(app=app, files=files, mode=mode, + context = Context(app=app, files=files, mode=mode, fm=self.fm, flags=flags, wait=wait, popen_kws=popen_kws, file=files and files[0] or None) @@ -159,7 +163,6 @@ wait_for_enter = False devnull = None - popen_kws['args'] = action if 'shell' not in popen_kws: popen_kws['shell'] = isinstance(action, str) if 'stdout' not in popen_kws: @@ -188,16 +191,45 @@ if 'w' in context.flags: if not pipe_output and context.wait: # <-- sanity check wait_for_enter = True + if 'r' in context.flags: + if 'sudo' not in get_executables(): + return self._log("Can not run with 'r' flag, sudo is not installed!") + dflag = ('d' in context.flags) + if isinstance(action, str): + action = 'sudo ' + (dflag and '-b ' or '') + action + else: + action = ['sudo'] + (dflag and ['-b'] or []) + action + toggle_ui = True + context.wait = True + if 't' in context.flags: + if 'DISPLAY' not in os.environ: + return self._log("Can not run with 't' flag, no display found!") + term = os.environ.get('TERMCMD', os.environ.get('TERM')) + if term not in get_executables(): + term = 'x-terminal-emulator' + if term not in get_executables(): + term = 'xterm' + if isinstance(action, str): + action = term + ' -e ' + action + else: + action = [term, '-e'] + action + toggle_ui = False + context.wait = False + popen_kws['args'] = action # Finally, run it if toggle_ui: self._activate_ui(False) try: + error = None process = None + self.fm.signal_emit('runner.execute.before', + popen_kws=popen_kws, context=context) try: process = Popen(**popen_kws) except Exception as e: + error = e self._log("Failed to run: %s\n%s" % (str(action), str(e))) else: if context.wait: @@ -207,6 +239,8 @@ if wait_for_enter: press_enter() finally: + self.fm.signal_emit('runner.execute.after', + popen_kws=popen_kws, context=context, error=error) if devnull: devnull.close() if toggle_ui: diff -Nru ranger-1.5.2/ranger/core/shared.py ranger-1.5.4/ranger/core/shared.py --- ranger-1.5.2/ranger/core/shared.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/core/shared.py 2012-05-03 16:47:39.000000000 +0000 @@ -76,6 +76,8 @@ try: import options as my_options except ImportError: + # XXX: This mistakenly ignores ImportErrors inside options.py + # It should only ignore missing options.py instead. pass else: settings._setting_sources.append(my_options) diff -Nru ranger-1.5.2/ranger/data/mime.types ranger-1.5.4/ranger/data/mime.types --- ranger-1.5.2/ranger/data/mime.types 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/data/mime.types 2012-05-03 16:47:39.000000000 +0000 @@ -10,15 +10,15 @@ # ############################################################################### -audio/flac flac -audio/musepack mpc mpp mp+ -audio/ogg oga ogg spx -audio/wavpack wv wvc +audio/flac flac +audio/musepack mpc mpp mp+ +audio/ogg oga ogg spx +audio/wavpack wv wvc -video/mkv mkv -video/webm webm -video/flash flv -video/ogg ogv ogm -video/divx div divx +video/mkv mkv +video/webm webm +video/flash flv +video/ogg ogv ogm +video/divx div divx -text/x-ruby rb +text/x-ruby rb diff -Nru ranger-1.5.2/ranger/data/scope.sh ranger-1.5.4/ranger/data/scope.sh --- ranger-1.5.2/ranger/data/scope.sh 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/data/scope.sh 2012-05-03 16:47:39.000000000 +0000 @@ -1,7 +1,7 @@ #!/bin/bash # ranger supports enhanced previews. If the option "use_preview_script" -# is set to True (by default it's False), this script will be called -# and its output is displayed in ranger. ANSI color codes are supported. +# is set to True and this file exists, this script will be called and its +# output is displayed in ranger. ANSI color codes are supported. # NOTES: This script is considered a configuration file. If you upgrade # ranger, it will be left untouched. (You must update it yourself.) @@ -26,7 +26,7 @@ # Find out something about the file: mimetype=$(file --mime-type -Lb "$path") -extension=$(echo "$path" | grep '\.' | grep -o '[^.]\+$') +extension=${path##*.} # Functions: # "have $1" succeeds if $1 is an existing command/installed program @@ -67,9 +67,13 @@ img2txt --gamma=0.6 --width="$width" "$path" && exit 4 || exit 1;; # Display information about media files: video/* | audio/*) + have exiftool && exiftool "$path" && exit 5 # Use sed to remove spaces so the output fits into the narrow window - mediainfo "$path" | sed 's/ \+:/: /;' - success && exit 5 || exit 1;; + if have mediainfo; then + mediainfo "$path" | sed 's/ \+:/: /;' + success && exit 5 + fi + exit 1;; esac exit 1 diff -Nru ranger-1.5.2/ranger/defaults/apps.py ranger-1.5.4/ranger/defaults/apps.py --- ranger-1.5.2/ranger/defaults/apps.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/defaults/apps.py 2012-05-03 16:47:39.000000000 +0000 @@ -14,9 +14,17 @@ # in your ~/.config/ranger/apps.py, you should subclass the class defined # here like this: # -# from ranger.defaults.apps import CustomApplications as DefaultApps -# class CustomApplications(DeafultApps): -# +# from ranger.defaults.apps import CustomApplications as DefaultApps +# class CustomApplications(DeafultApps): +# +# +# To override app_defaults, you can write something like: +# +# def app_defaults(self, c): +# f = c.file +# if f.extension == 'lol': +# return "lolopener", c +# return DefaultApps.app_default(self, c) # # =================================================================== # This system is based on things called MODES and FLAGS. You can read @@ -27,6 +35,8 @@ # p Redirect output to the pager # w Wait for an Enter-press when the process is done # c Run the current file only, instead of the selection +# r Run application with root privilege +# t Run application in a new terminal window # # To implement flags in this file, you could do this: # context.flags += "d" @@ -97,23 +107,38 @@ if f.extension is not None: if f.extension in ('pdf', ): - return self.either(c, 'evince', 'zathura', 'apvlv') + return self.either(c, 'llpp', 'zathura', 'mupdf', 'apvlv', + 'evince', 'okular', 'epdfview') if f.extension == 'djvu': return self.either(c, 'evince') - if f.extension in ('xml', ): + if f.extension in ('xml', 'csv'): return self.either(c, 'editor') + if f.extension == 'mid': + return self.either(c, 'wildmidi') + if f.extension in ('html', 'htm', 'xhtml') or f.extension == 'swf': + c.flags += 'd' + handler = self.either(c, + 'luakit', 'uzbl', 'vimprobable', 'vimprobable2', 'jumanji', + 'firefox', 'seamonkey', 'iceweasel', 'opera', + 'surf', 'midori', 'epiphany', 'konqueror') + # Only return if some program was found: + if handler: + return handler if f.extension in ('html', 'htm', 'xhtml'): - return self.either(c, 'firefox', 'opera', 'jumanji', - 'luakit', 'elinks', 'lynx') - if f.extension == 'swf': - return self.either(c, 'firefox', 'opera', 'jumanji', 'luakit') + # These browsers can't handle flash, so they're not called above. + c.flags += 'D' + return self.either(c, 'elinks', 'links', 'links2', 'lynx', 'w3m') if f.extension == 'nes': return self.either(c, 'fceux') if f.extension in ('swc', 'smc', 'sfc'): return self.either(c, 'zsnes') - if f.extension in ('odt', 'ods', 'odp', 'odf', 'odg', - 'doc', 'xls'): - return self.either(c, 'libreoffice', 'soffice', 'ooffice') + if f.extension == 'doc': + return self.either(c, 'abiword', 'libreoffice', + 'soffice', 'ooffice') + if f.extension in ('odt', 'ods', 'odp', 'odf', 'odg', 'sxc', + 'stc', 'xls', 'xlsx', 'xlt', 'xlw', 'gnm', 'gnumeric'): + return self.either(c, 'gnumeric', 'kspread', + 'libreoffice', 'soffice', 'ooffice') if f.mimetype is not None: if INTERPRETED_LANGUAGES.match(f.mimetype): @@ -125,8 +150,8 @@ if f.video or f.audio: if f.video: c.flags += 'd' - return self.either(c, 'mplayer2', 'mplayer', 'smplayer', 'vlc', - 'totem') + return self.either(c, 'smplayer', 'gmplayer', 'mplayer2', + 'mplayer', 'vlc', 'totem') if f.image: if c.mode in (11, 12, 13, 14): @@ -201,7 +226,8 @@ @depends_on('feh', 'X') def app_feh(self, c): c.flags += 'd' - if c.mode is 0 and len(c.files) is 1: # view all files in the cwd + if c.mode is 0 and len(c.files) is 1 and self.fm.env.cwd: + # view all files in the cwd images = [f.basename for f in self.fm.env.cwd.files if f.image] return 'feh', '--start-at', c.file.basename, images return 'feh', c @@ -209,7 +235,7 @@ @depends_on('sxiv', 'X') def app_sxiv(self, c): c.flags = 'd' + c.flags - if len(c.files) is 1: + if len(c.files) is 1 and self.fm.env.cwd: images = [f.basename for f in self.fm.env.cwd.files if f.image] try: position = images.index(c.file.basename) + 1 @@ -281,8 +307,14 @@ CustomApplications.generic('fceux', 'wine', 'zsnes', deps=['X']) # Add those which should only run in X AND should be detached/forked here: -CustomApplications.generic('opera', 'firefox', 'apvlv', 'evince', - 'zathura', 'gimp', 'mirage', 'eog', 'jumanji', +CustomApplications.generic( + 'luakit', 'uzbl', 'vimprobable', 'vimprobable2', 'jumanji', + 'firefox', 'seamonkey', 'iceweasel', 'opera', + 'surf', 'midori', 'epiphany', 'konqueror', + 'evince', 'zathura', 'apvlv', 'okular', 'epdfview', 'mupdf', 'llpp', + 'eog', 'mirage', 'gimp', + 'libreoffice', 'soffice', 'ooffice', 'gnumeric', 'kspread', 'abiword', + 'gmplayer', 'smplayer', 'vlc', flags='d', deps=['X']) # What filetypes are recognized as scripts for interpreted languages? diff -Nru ranger-1.5.2/ranger/defaults/commands.py ranger-1.5.4/ranger/defaults/commands.py --- ranger-1.5.2/ranger/defaults/commands.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/defaults/commands.py 2012-05-03 16:47:39.000000000 +0000 @@ -218,7 +218,11 @@ return (start + program + ' ' for program \ in get_executables() if program.startswith(command)) if position_of_last_space == len(command) - 1: - return self.line + '%s ' + selection = self.fm.env.get_selection() + if len(selection) == 1: + return self.line + selection[0].shell_escaped_basename + ' ' + else: + return self.line + '%s ' else: before_word, start_of_word = self.line.rsplit(' ', 1) return (before_word + ' ' + file.shell_escaped_basename \ @@ -307,12 +311,12 @@ return app, flags, int(mode) - def _get_tab(self): + def tab(self): data = self.rest(1) if ' ' not in data: all_apps = self.fm.apps.all() if all_apps: - return (app for app in all_apps if app.startswith(data)) + return (self.firstpart + app for app in all_apps if app.startswith(data)) return None @@ -455,7 +459,12 @@ Spawns an "x-terminal-emulator" starting in the current directory. """ def execute(self): - self.fm.run('x-terminal-emulator', flags='d') + command = os.environ.get('TERMCMD', os.environ.get('TERM')) + if command not in get_executables(): + command = 'x-terminal-emulator' + if command not in get_executables(): + command = 'xterm' + self.fm.run(command, flags='d') class delete(Command): @@ -797,11 +806,11 @@ cmdfile.write(b"# This file will be executed when you close the editor.\n") cmdfile.write(b"# Please double-check everything, clear the file to abort.\n") if py3: - cmdfile.write("\n".join("mv -vi " + esc(old) + " " + esc(new) \ + cmdfile.write("\n".join("mv -vi -- " + esc(old) + " " + esc(new) \ for old, new in zip(filenames, new_filenames) \ if old != new).encode("utf-8")) else: - cmdfile.write("\n".join("mv -vi " + esc(old) + " " + esc(new) \ + cmdfile.write("\n".join("mv -vi -- " + esc(old) + " " + esc(new) \ for old, new in zip(filenames, new_filenames) if old != new)) cmdfile.flush() self.fm.execute_file([File(cmdfile.name)], app='editor') @@ -809,6 +818,45 @@ cmdfile.close() +class relink(Command): + """ + :relink + + Changes the linked path of the currently highlighted symlink to + """ + + def execute(self): + from ranger.fsobject import File + + new_path = self.rest(1) + cf = self.fm.env.cf + + if not new_path: + return self.fm.notify('Syntax: relink ', bad=True) + + if not cf.is_link: + return self.fm.notify('%s is not a symlink!' % cf.basename, bad=True) + + if new_path == os.readlink(cf.path): + return + + try: + os.remove(cf.path) + os.symlink(new_path, cf.path) + except OSError as err: + self.fm.notify(err) + + self.fm.reset() + self.fm.env.cwd.pointed_obj = cf + self.fm.env.cf = cf + + def tab(self): + if not self.rest(1): + return self.line+os.readlink(self.fm.env.cf.path) + else: + return self._tab_directory_content() + + class help_(Command): """ :help diff -Nru ranger-1.5.2/ranger/defaults/options.py ranger-1.5.4/ranger/defaults/options.py --- ranger-1.5.2/ranger/defaults/options.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/defaults/options.py 2012-05-03 16:47:39.000000000 +0000 @@ -26,7 +26,7 @@ # Which files should be hidden? Toggle this by typing `zh' or # changing the setting `show_hidden' hidden_filter = regexp( - r'^\.|\.(?:pyc|pyo|bak|swp)$|^lost\+found$|^__cache__$') + r'^\.|\.(?:pyc|pyo|bak|swp)$|^lost\+found$|^__(py)?cache__$') show_hidden = False # Which script is used to generate file previews? @@ -104,19 +104,35 @@ # When false, bookmarks are saved when ranger is exited. autosave_bookmarks = True +# You can display the "real" cumulative size of directories by using the +# command :get_cumulative_size or typing "dc". The size is expensive to +# calculate and will not be updated automatically. You can choose +# to update it automatically though by turning on this option: +autoupdate_cumulative_size = False + # Makes sense for screen readers: show_cursor = False # One of: size, basename, mtime, type sort = 'natural' sort_reverse = False -sort_case_insensitive = False +sort_case_insensitive = True sort_directories_first = True # Enable this if key combinations with the Alt Key don't work for you. # (Especially on xterm) xterm_alt_key = False +# A function that is called when the user interface is being set up. +init_function = None + +# You can use it to initialize some custom functionality or bind singals +#def init_function(fm): +# fm.notify("Hello :)") +# def on_tab_change(signal): +# signal.origin.notify("Changing tab! Yay!") +# fm.signal_bind("tab.change", on_tab_change) + # The color scheme overlay. Explained below. colorscheme_overlay = None diff -Nru ranger-1.5.2/ranger/defaults/rc.conf ranger-1.5.4/ranger/defaults/rc.conf --- ranger-1.5.2/ranger/defaults/rc.conf 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/defaults/rc.conf 2012-05-03 16:47:39.000000000 +0000 @@ -39,6 +39,7 @@ map reset map redraw_window map abort +map change_mode normal map i display_file map ? help @@ -62,8 +63,9 @@ map " tag_toggle tag=%any map mark_files toggle=True map v mark_files all=True toggle=True -map V mark_files all=True val=False map uv mark_files all=True val=False +map V toggle_visual_mode +map uV toggle_visual_mode reverse=True # For the nostalgics: Midnight Commander bindings map help @@ -145,6 +147,7 @@ map pl paste_symlink relative=False map pL paste_symlink relative=True map phl paste_hardlink +map pht paste_hardlinked_subtree map dd cut map ud uncut @@ -216,6 +219,8 @@ map oA chain set sort=atime; set sort_reverse=True map oT chain set sort="type"; set sort_reverse=True +map dc get_cumulative_size + # Settings map zc toggle_option collapse_preview map zd toggle_option sort_directories_first @@ -226,6 +231,7 @@ map zp toggle_option preview_files map zP toggle_option preview_directories map zs toggle_option sort_case_insensitive +map zu toggle_option autoupdate_cumulative_size map zv toggle_option use_preview_script map zf console filter diff -Nru ranger-1.5.2/ranger/ext/cached_function.py ranger-1.5.4/ranger/ext/cached_function.py --- ranger-1.5.2/ranger/ext/cached_function.py 1970-01-01 00:00:00.000000000 +0000 +++ ranger-1.5.4/ranger/ext/cached_function.py 2012-05-03 16:47:39.000000000 +0000 @@ -0,0 +1,27 @@ +# Copyright (C) 2012 Roman Zimbelmann +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +def cached_function(fnc): + cache = {} + def inner_cached_function(*args): + try: + return cache[args] + except: + value = fnc(*args) + cache[args] = value + return value + inner_cached_function._cache = cache + return inner_cached_function + diff -Nru ranger-1.5.2/ranger/ext/get_executables.py ranger-1.5.4/ranger/ext/get_executables.py --- ranger-1.5.2/ranger/ext/get_executables.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/ext/get_executables.py 2012-05-03 16:47:39.000000000 +0000 @@ -16,7 +16,6 @@ from stat import S_IXOTH, S_IFREG from ranger.ext.iter_tools import unique from os import listdir, environ, stat -from os.path import join _cached_executables = None @@ -54,7 +53,7 @@ except: continue for item in content: - abspath = join(path, item) + abspath = path + '/' + item try: filestat = stat(abspath) except: diff -Nru ranger-1.5.2/ranger/ext/human_readable.py ranger-1.5.4/ranger/ext/human_readable.py --- ranger-1.5.2/ranger/ext/human_readable.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/ext/human_readable.py 2012-05-03 16:47:39.000000000 +0000 @@ -13,7 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -def human_readable(byte, seperator=' '): +def human_readable(byte, separator=' '): """ Convert a large number of bytes to an easily readable format. @@ -24,30 +24,34 @@ >>> human_readable(2 ** 20 * 1023) '1023 M' """ + + # 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. if byte <= 0: return '0' if byte < 2**10: - return '%d%sB' % (byte, seperator) + return '%d%sB' % (byte, separator) if byte < 2**10 * 999: - return '%.3g%sK' % (byte / 2**10.0, seperator) + return '%.3g%sK' % (byte / 2**10.0, separator) if byte < 2**20: - return '%.4g%sK' % (byte / 2**10.0, seperator) + return '%.4g%sK' % (byte / 2**10.0, separator) if byte < 2**20 * 999: - return '%.3g%sM' % (byte / 2**20.0, seperator) + return '%.3g%sM' % (byte / 2**20.0, separator) if byte < 2**30: - return '%.4g%sM' % (byte / 2**20.0, seperator) + return '%.4g%sM' % (byte / 2**20.0, separator) if byte < 2**30 * 999: - return '%.3g%sG' % (byte / 2**30.0, seperator) + return '%.3g%sG' % (byte / 2**30.0, separator) if byte < 2**40: - return '%.4g%sG' % (byte / 2**30.0, seperator) + return '%.4g%sG' % (byte / 2**30.0, separator) if byte < 2**40 * 999: - return '%.3g%sT' % (byte / 2**40.0, seperator) + return '%.3g%sT' % (byte / 2**40.0, separator) if byte < 2**50: - return '%.4g%sT' % (byte / 2**40.0, seperator) + return '%.4g%sT' % (byte / 2**40.0, separator) if byte < 2**50 * 999: - return '%.3g%sP' % (byte / 2**50.0, seperator) + return '%.3g%sP' % (byte / 2**50.0, separator) if byte < 2**60: - return '%.4g%sP' % (byte / 2**50.0, seperator) + return '%.4g%sP' % (byte / 2**50.0, separator) return '>9000' if __name__ == '__main__': diff -Nru ranger-1.5.2/ranger/ext/next_available_filename.py ranger-1.5.4/ranger/ext/next_available_filename.py --- ranger-1.5.2/ranger/ext/next_available_filename.py 1970-01-01 00:00:00.000000000 +0000 +++ ranger-1.5.4/ranger/ext/next_available_filename.py 2012-05-03 16:47:39.000000000 +0000 @@ -0,0 +1,30 @@ +# Copyright (C) 2011 Roman Zimbelmann +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os.path + +def next_available_filename(fname, directory="."): + existing_files = os.listdir(directory) + + if fname not in existing_files: + return fname + if not fname.endswith("_"): + fname += "_" + if fname not in existing_files: + return fname + + for i in range(1, len(existing_files) + 1): + if fname + str(i) not in existing_files: + return fname + str(i) diff -Nru ranger-1.5.2/ranger/ext/shell_escape.py ranger-1.5.4/ranger/ext/shell_escape.py --- ranger-1.5.2/ranger/ext/shell_escape.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/ext/shell_escape.py 2012-05-03 16:47:39.000000000 +0000 @@ -18,17 +18,20 @@ """ META_CHARS = (' ', "'", '"', '`', '&', '|', ';', - '$', '!', '(', ')', '[', ']', '<', '>') + '$', '!', '(', ')', '[', ']', '<', '>', '\t') +UNESCAPABLE = set(map(chr, list(range(9)) + list(range(10, 32)) \ + + list(range(127, 256)))) META_DICT = dict([(mc, '\\' + mc) for mc in META_CHARS]) def shell_quote(string): """Escapes by quoting""" return "'" + str(string).replace("'", "'\\''") + "'" - def shell_escape(arg): """Escapes by adding backslashes""" arg = str(arg) + if UNESCAPABLE & set(arg): + return shell_quote(arg) arg = arg.replace('\\', '\\\\') # make sure this comes at the start for k, v in META_DICT.items(): arg = arg.replace(k, v) diff -Nru ranger-1.5.2/ranger/ext/signals.py ranger-1.5.4/ranger/ext/signals.py --- ranger-1.5.2/ranger/ext/signals.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/ext/signals.py 2012-05-03 16:47:39.000000000 +0000 @@ -126,7 +126,7 @@ handler._function = None self._signals = dict() - def signal_bind(self, signal_name, function, priority=0.5, weak=False): + def signal_bind(self, signal_name, function, priority=0.5, weak=False, autosort=True): """ Bind a function to the signal. @@ -162,9 +162,25 @@ handler = SignalHandler(signal_name, function, priority, nargs > 0) handlers.append(handler) - handlers.sort(key=lambda handler: -handler._priority) + if autosort: + handlers.sort(key=lambda handler: -handler._priority) return handler + def signal_force_sort(self, signal_name=None): + """ + Forces a sorting of signal handlers by priority. + + This is only necessary if you used signal_bind with autosort=False + after finishing to bind many signals at once. + """ + if signal_name is None: + for handlers in self._signals.values(): + handlers.sort(key=lambda handler: -handler._priority) + elif signal_name in self._signals: + self._signals[signal_name].sort(key=lambda handler: -handler._priority) + else: + return False + def signal_unbind(self, signal_handler): """ Removes a signal binding. diff -Nru ranger-1.5.2/ranger/fsobject/directory.py ranger-1.5.4/ranger/fsobject/directory.py --- ranger-1.5.2/ranger/fsobject/directory.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/fsobject/directory.py 2012-05-03 16:47:39.000000000 +0000 @@ -25,6 +25,7 @@ from ranger.core.shared import SettingsAware from ranger.ext.accumulator import Accumulator from ranger.ext.lazy_property import lazy_property +from ranger.ext.human_readable import human_readable def sort_by_basename(path): """returns path.basename (for sorting)""" @@ -79,6 +80,8 @@ content_outdated = False content_loaded = False + _cumulative_size_calculated = False + sort_dict = { 'basename': sort_by_basename, 'natural': sort_naturally, @@ -100,11 +103,11 @@ for opt in ('sort_directories_first', 'sort', 'sort_reverse', 'sort_case_insensitive'): self.settings.signal_bind('setopt.' + opt, - self.request_resort, weak=True) + self.request_resort, weak=True, autosort=False) for opt in ('hidden_filter', 'show_hidden'): self.settings.signal_bind('setopt.' + opt, - self.request_reload, weak=True) + self.request_reload, weak=True, autosort=False) self.use() def request_resort(self): @@ -185,8 +188,25 @@ hidden_filter = not self.settings.show_hidden \ and self.settings.hidden_filter filelist = os.listdir(mypath) - self.size = len(filelist) - self.infostring = ' %d' % self.size + + if self._cumulative_size_calculated: + # If self.content_loaded is true, this is not the first + # time loading. So I can't really be sure if the + # size has changed and I'll add a "?". + if self.content_loaded: + if self.fm.settings.autoupdate_cumulative_size: + self.look_up_cumulative_size() + else: + self.infostring = ' %s' % human_readable( + self.size, separator='? ') + else: + self.infostring = ' %s' % human_readable(self.size) + else: + self.size = len(filelist) + self.infostring = ' %d' % self.size + if self.is_link: + self.infostring = '->' + self.infostring + filenames = [mypath + (mypath == '/' and fname or '/' + fname)\ for fname in filelist if accept_file( fname, mypath, hidden_filter, self.filter)] @@ -327,6 +347,30 @@ else: self.correct_pointer() + def _get_cumulative_size(self): + if self.size == 0: + return 0 + cum = 0 + realpath = os.path.realpath + for dirpath, dirnames, filenames in os.walk(self.path, + onerror=lambda _: None): + for file in filenames: + try: + if dirpath == self.path: + stat = os_stat(realpath(dirpath + "/" + file)) + else: + stat = os_stat(dirpath + "/" + file) + cum += stat.st_size + except: + pass + return cum + + def look_up_cumulative_size(self): + self._cumulative_size_calculated = True + self.size = self._get_cumulative_size() + self.infostring = ('-> ' if self.is_link else ' ') + \ + human_readable(self.size) + @lazy_property def size(self): try: diff -Nru ranger-1.5.2/ranger/fsobject/fsobject.py ranger-1.5.4/ranger/fsobject/fsobject.py --- ranger-1.5.2/ranger/fsobject/fsobject.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/fsobject/fsobject.py 2012-05-03 16:47:39.000000000 +0000 @@ -26,8 +26,17 @@ from ranger.ext.lazy_property import lazy_property from ranger.ext.human_readable import human_readable +if hasattr(str, 'maketrans'): + maketrans = str.maketrans +else: + from string import maketrans +_unsafe_chars = '\n' + ''.join(map(chr, range(32))) + ''.join(map(chr, range(128, 256))) +_safe_string_table = maketrans(_unsafe_chars, '?' * len(_unsafe_chars)) _extract_number_re = re.compile(r'([^0-9]?)(\d*)') +def safe_path(path): + return path.translate(_safe_string_table) + class FileSystemObject(FileManagerAware): (basename, basename_lower, @@ -106,6 +115,11 @@ return [c if i % 3 == 1 else (int(c) if c else 0) for i, c in \ enumerate(_extract_number_re.split(self.basename_lower))] + @lazy_property + def safe_basename(self): + return self.basename.translate(_safe_string_table) + + for attr in ('video', 'audio', 'image', 'media', 'document', 'container'): exec("%s = lazy_property(" "lambda self: self.set_mimetype() or self.%s)" % (attr, attr)) @@ -117,6 +131,9 @@ def use(self): """Used in garbage-collecting. Override in Directory""" + def look_up_cumulative_size(self): + pass # normal files have no cumulative size + def set_mimetype(self): """assign attributes such as self.video according to the mimetype""" basename = self.basename diff -Nru ranger-1.5.2/ranger/gui/colorscheme.py ranger-1.5.4/ranger/gui/colorscheme.py --- ranger-1.5.2/ranger/gui/colorscheme.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/gui/colorscheme.py 2012-05-03 16:47:39.000000000 +0000 @@ -46,6 +46,8 @@ from ranger.gui.context import Context from ranger.core.helper import allow_access_to_confdir from ranger.core.shared import SettingsAware +from ranger.ext.cached_function import cached_function +from ranger.ext.iter_tools import flatten # ColorScheme is not SettingsAware but it will gain access # to the settings during the initialization. We can't import @@ -60,9 +62,6 @@ which fits to the given keys. """ - def __init__(self): - self.cache = {} - def get(self, *keys): """ Returns the (fg, bg, attr) for the given keys. @@ -70,33 +69,28 @@ Using this function rather than use() will cache all colors for faster access. """ - keys = frozenset(keys) - try: - return self.cache[keys] - - except KeyError: - context = Context(keys) + context = Context(keys) - # add custom error messages for broken colorschemes - color = self.use(context) - if self.settings.colorscheme_overlay: - result = self.settings.colorscheme_overlay(context, *color) - assert isinstance(result, (tuple, list)), \ - "Your colorscheme overlay doesn't return a tuple!" - assert all(isinstance(val, int) for val in result), \ - "Your colorscheme overlay doesn't return a tuple"\ - " containing 3 integers!" - color = result - self.cache[keys] = color - return color + # add custom error messages for broken colorschemes + color = self.use(context) + if self.settings.colorscheme_overlay: + result = self.settings.colorscheme_overlay(context, *color) + assert isinstance(result, (tuple, list)), \ + "Your colorscheme overlay doesn't return a tuple!" + assert all(isinstance(val, int) for val in result), \ + "Your colorscheme overlay doesn't return a tuple"\ + " containing 3 integers!" + color = result + return color + @cached_function def get_attr(self, *keys): """ Returns the curses attribute for the specified keys Ready to use for curses.setattr() """ - fg, bg, attr = self.get(*keys) + fg, bg, attr = self.get(*flatten(keys)) return attr | color_pair(get_color(fg, bg)) def use(self, context): diff -Nru ranger-1.5.2/ranger/gui/curses_shortcuts.py ranger-1.5.4/ranger/gui/curses_shortcuts.py --- ranger-1.5.2/ranger/gui/curses_shortcuts.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/gui/curses_shortcuts.py 2012-05-03 16:47:39.000000000 +0000 @@ -17,7 +17,6 @@ import curses import _curses -from ranger.ext.iter_tools import flatten from ranger.gui.color import get_color from ranger.core.shared import SettingsAware @@ -63,7 +62,6 @@ def color(self, *keys): """Change the colors from now on.""" - keys = flatten(keys) attr = self.settings.colorscheme.get_attr(*keys) try: self.win.attrset(attr) @@ -72,7 +70,6 @@ def color_at(self, y, x, wid, *keys): """Change the colors at the specified position""" - keys = flatten(keys) attr = self.settings.colorscheme.get_attr(*keys) try: self.win.chgat(y, x, wid, attr) diff -Nru ranger-1.5.2/ranger/gui/ui.py ranger-1.5.4/ranger/gui/ui.py --- ranger-1.5.2/ranger/gui/ui.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/gui/ui.py 2012-05-03 16:47:39.000000000 +0000 @@ -23,8 +23,8 @@ from ranger.ext.keybinding_parser import ALT_KEY TERMINALS_WITH_TITLE = ("xterm", "xterm-256color", "rxvt", - "rxvt-256color", "rxvt-unicode", "aterm", "Eterm", - "screen", "screen-256color") + "rxvt-256color", "rxvt-unicode", "rxvt-unicode-256color", + "aterm", "Eterm", "screen", "screen-256color") MOUSEMASK = curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION @@ -294,9 +294,12 @@ if os.sep in split[0]: cwd = os.sep.join(split[1:]) try: - sys.stdout.write("\033]2;ranger:" + cwd + "\007") - except UnicodeEncodeError: - sys.stdout.write("\033]2;ranger:" + ascii_only(cwd) + "\007") + fixed_cwd = cwd.encode('utf-8', 'surrogateescape'). \ + decode('utf-8', 'replace') + sys.stdout.write("\033]2;ranger:" + fixed_cwd + "\007") + sys.stdout.flush() + except: + pass self.win.refresh() def finalize(self): diff -Nru ranger-1.5.2/ranger/gui/widgets/browsercolumn.py ranger-1.5.4/ranger/gui/widgets/browsercolumn.py --- ranger-1.5.2/ranger/gui/widgets/browsercolumn.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/gui/widgets/browsercolumn.py 2012-05-03 16:47:39.000000000 +0000 @@ -178,7 +178,7 @@ self.win.move(0, 0) if not self.target.content_loaded: - self.color(base_color) + self.color(tuple(base_color)) self.addnstr("...", self.wid) self.color_reset() return @@ -187,13 +187,13 @@ base_color.append('main_column') if not self.target.accessible: - self.color(base_color, 'error') + self.color(tuple(base_color + ['error'])) self.addnstr("not accessible", self.wid) self.color_reset() return if self.target.empty(): - self.color(base_color, 'empty') + self.color(tuple(base_color + ['empty'])) self.addnstr("empty", self.wid) self.color_reset() return @@ -289,15 +289,15 @@ if x > 0: self.addstr(line, x, infostring) - self.color_at(line, 0, self.wid, this_color) + self.color_at(line, 0, self.wid, tuple(this_color)) if bad_info_color: start, wid = bad_info_color - self.color_at(line, start, wid, this_color, 'badinfo') + self.color_at(line, start, wid, tuple(this_color), 'badinfo') if (self.main_column or self.settings.display_tags_in_all_columns) \ and tagged and self.wid > 2: this_color.append('tag_marker') - self.color_at(line, 0, len(tagged_marker), this_color) + self.color_at(line, 0, len(tagged_marker), tuple(this_color)) self.color_reset() diff -Nru ranger-1.5.2/ranger/gui/widgets/browserview.py ranger-1.5.4/ranger/gui/widgets/browserview.py --- ranger-1.5.2/ranger/gui/widgets/browserview.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/gui/widgets/browserview.py 2012-05-03 16:47:39.000000000 +0000 @@ -215,7 +215,10 @@ ystart = self.hei - hei self.addnstr(ystart - 1, 0, "key command".ljust(self.wid), self.wid) - self.win.chgat(ystart - 1, 0, curses.A_UNDERLINE) + try: + self.win.chgat(ystart - 1, 0, curses.A_UNDERLINE) + except: + pass whitespace = " " * self.wid i = ystart for key, cmd in hints: diff -Nru ranger-1.5.2/ranger/gui/widgets/statusbar.py ranger-1.5.4/ranger/gui/widgets/statusbar.py --- ranger-1.5.2/ranger/gui/widgets/statusbar.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/gui/widgets/statusbar.py 2012-05-03 16:47:39.000000000 +0000 @@ -159,7 +159,10 @@ if stat is None: return - perms = target.get_permission_string() + if self.fm.mode != 'normal': + perms = '--%s--' % self.fm.mode.upper() + else: + perms = target.get_permission_string() how = getuid() == stat.st_uid and 'good' or 'bad' left.add(perms, 'permissions', how) left.add_space() @@ -231,17 +234,21 @@ if target.marked_items: if len(target.marked_items) == len(target.files): - right.add(human_readable(target.disk_usage, seperator='')) + right.add(human_readable(target.disk_usage, separator='')) else: - right.add(human_readable(sum(f.size \ - for f in target.marked_items \ - if f.is_file), seperator='')) + sumsize = sum(f.size for f in target.marked_items if not + f.is_directory or f._cumulative_size_calculated) + right.add(human_readable(sumsize, separator='')) right.add("/" + str(len(target.marked_items))) else: - right.add(human_readable(target.disk_usage, seperator='') + - " sum, ") - right.add(human_readable(self.env.get_free_space( \ - target.mount_path), seperator='') + " free") + right.add(human_readable(target.disk_usage, separator='') + " sum") + try: + free = self.env.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: @@ -251,7 +258,7 @@ elif len(target.files): right.add(str(target.pointer + 1) + '/' + str(len(target.files)) + ' ', base) - if max_pos == 0: + if max_pos <= 0: right.add('All', base, 'all') elif pos == 0: right.add('Top', base, 'top') diff -Nru ranger-1.5.2/ranger/gui/widgets/taskview.py ranger-1.5.4/ranger/gui/widgets/taskview.py --- ranger-1.5.2/ranger/gui/widgets/taskview.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/gui/widgets/taskview.py 2012-05-03 16:47:39.000000000 +0000 @@ -17,8 +17,6 @@ The TaskView allows you to modify what the loader is doing. """ -from collections import deque - from . import Widget from ranger.ext.accumulator import Accumulator @@ -31,7 +29,7 @@ self.scroll_begin = 0 def draw(self): - base_clr = deque() + base_clr = [] base_clr.append('in_taskview') lst = self.get_list() @@ -48,7 +46,7 @@ return self.addstr(0, 0, "Task View") - self.color_at(0, 0, self.wid, base_clr, 'title') + self.color_at(0, 0, self.wid, tuple(base_clr), 'title') if lst: for i in range(self.hei - 1): @@ -59,19 +57,19 @@ break y = i + 1 - clr = deque(base_clr) + clr = list(base_clr) if self.pointer == i: clr.append('selected') descr = obj.get_description() self.addstr(y, 0, descr, self.wid) - self.color_at(y, 0, self.wid, clr) + self.color_at(y, 0, self.wid, tuple(clr)) else: if self.hei > 1: self.addstr(1, 0, "No task in the queue.") - self.color_at(1, 0, self.wid, base_clr, 'error') + self.color_at(1, 0, self.wid, tuple(base_clr), 'error') self.color_reset() diff -Nru ranger-1.5.2/ranger/gui/widgets/titlebar.py ranger-1.5.4/ranger/gui/widgets/titlebar.py --- ranger-1.5.2/ranger/gui/widgets/titlebar.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/gui/widgets/titlebar.py 2012-05-03 16:47:39.000000000 +0000 @@ -102,6 +102,7 @@ self.result = bar.combine() def _get_left_part(self, bar): + # TODO: Properly escape non-printable chars without breaking unicode if self.env.username == 'root': clr = 'bad' else: @@ -160,5 +161,6 @@ self.win.move(0, 0) for part in result: self.color(*part.lst) - self.addstr(str(part)) + y, x = self.win.getyx() + self.addstr(y, x, str(part)) self.color_reset() diff -Nru ranger-1.5.2/ranger/__init__.py ranger-1.5.4/ranger/__init__.py --- ranger-1.5.2/ranger/__init__.py 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/ranger/__init__.py 2012-05-03 16:47:39.000000000 +0000 @@ -25,7 +25,7 @@ # Information __license__ = 'GPL3' -__version__ = '1.5.2' +__version__ = '1.5.4' __author__ = __maintainer__ = 'Roman Zimbelmann' __email__ = 'romanz@lavabit.com' diff -Nru ranger-1.5.2/README ranger-1.5.4/README --- ranger-1.5.2/README 2011-10-23 23:51:28.000000000 +0000 +++ ranger-1.5.4/README 2012-05-03 16:47:39.000000000 +0000 @@ -1,4 +1,4 @@ -ranger v.1.5.2 +ranger v.1.5.4 ============== ranger is a console file manager with VI key bindings. It provides a minimalistic and nice curses interface with a view on the directory hierarchy. @@ -56,6 +56,7 @@ Optional: * The "file" program for determining file types * The python module "chardet", in case of encoding detection problems +* "sudo" to use the "run as root"-feature Optional, for enhanced file previews (with "scope.sh"): * img2txt (from caca-utils) for previewing images @@ -89,7 +90,7 @@ current file. The second is the main column and the first shows the parent directory. -Ranger will automatically copy simple configuration files to ~/.config/ranger. -If you mess them up, just delete them and ranger will copy them again. Run -ranger with --dont-copy-config to disable this. Also check ranger/defaults/ -for the default configuration. +Ranger can automatically copy default configuration files to ~/.config/ranger +if you run it with the switch --copy-config. (see ranger --help for a +description of that switch.) Also check ranger/defaults/ for the default +configuration.