diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | CHANGELOG | 275 | ||||
-rw-r--r-- | CHANGELOG.md | 14 | ||||
-rw-r--r-- | ranger/__init__.py | 2 | ||||
-rw-r--r-- | ranger/container/file.py | 2 | ||||
-rw-r--r-- | ranger/container/fsobject.py | 2 | ||||
-rw-r--r-- | ranger/container/settings.py | 1 | ||||
-rw-r--r-- | ranger/core/actions.py | 30 | ||||
-rw-r--r-- | ranger/core/loader.py | 20 | ||||
-rwxr-xr-x | ranger/data/scope.sh | 30 | ||||
-rw-r--r-- | ranger/ext/shutil_generatorized.py | 33 | ||||
-rw-r--r-- | ranger/ext/vcs/svn.py | 274 | ||||
-rw-r--r-- | ranger/ext/vcs/vcs.py | 3 | ||||
-rw-r--r-- | ranger/gui/widgets/browsercolumn.py | 18 | ||||
-rw-r--r-- | ranger/gui/widgets/browserview.py | 7 | ||||
-rwxr-xr-x | setup.py | 11 |
16 files changed, 386 insertions, 337 deletions
diff --git a/.gitignore b/.gitignore index 5357ead2..09f1a7e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *~ +*.egg-info *.pyc *.pyo stuff/* diff --git a/CHANGELOG b/CHANGELOG deleted file mode 100644 index 207e311f..00000000 --- a/CHANGELOG +++ /dev/null @@ -1,275 +0,0 @@ -This log documents changes between stable versions. - -2015-10-04: version 1.7.2 -* Fixed file name arguments passed to "sxiv" and "feh" when using ":flat" -* Fixed removal of empty directories when using ":rename" -* Fixed free disk space display on Mac OS X -* Fixed `examples/vim_file_chooser` to work with gvim too -* Fixed some other rare crashes and bugs -* Fixed downward mouse wheel scrolling -* Fixed warning about regex splits being drawin in title bar since python3.5 -* Really fixed "S" key binding not working when SHELL=fish -* Improved doc/cheatsheet.svg -* Added some entries to rifle.conf -* Added key bindings pO and pP which work like po and pp but queue the operation - in a first-in-first-out order. - -2015-05-04: version 1.7.1 -* Added doc/cheatsheet.svg -* Added examples/rc_emacs.conf, a config file adding emacs-like key bindings -* Added "env" keyword in rifle.conf -* Fixed ":bulkrename" command in python3 -* Fixed "S" key binding not working when SHELL=fish - -2015-04-13: version 1.7.0 -* The default editor is now "vim" instead of "nano" -* Added automatic updates of tags when a file is renamed from within ranger -* Added "preview_images_method" which can be set to "iterm2" to use native - iTerm2 image previews -* Added ":rename_append" command to rename files without the file extension -* Added ":linemode" command to change the way the files are displayed - Try this out by pressing M followed by one of the suggested keys. - New linemodes can be added with ranger.api.register_linemode(). -* Added ":filter_inode_type" command to only show directories, files or links -* Added ":meta" command for managing custom file metadata -* Added ":flat" command for displaying subdirectories -* Added "solarized" colorscheme -* Added generic ability to use scope.sh for image previews -* Added video previews in scope.sh -* Added option "sort_unicode" to sort according to unicode, not ASCII -* ":mkdir" can now create multiple directory levels (like `mkdir -p`) -* ":help" (key binding "?") is now interactive -* ":find" (key binding "/") is now case insensitive by default -* "ranger --copy-config=all" now copies a short sample commands.py rather than - the full one, so that you can update ranger without having broken commands. - The full commands.py is still copied to ~/.config/ranger/commands_full.py. -* Fixed broken copying of symlinks - -2013-05-24: Version 1.6.1 -* Added support for version control systems, see: - http://lists.nongnu.org/archive/html/ranger-users/2013-03/msg00007.html -* Added :scout command as a unified backend to :find, :search, etc -* Added "open_all_images" setting to remove the need for external scripts - to handle opening of all images in a directory at once. -* Now previewing with "i" uses the whole available width. - -2013-02-22: Version 1.6.0 -* Overhauled all config files. Please update them or use the --clean switch -* Added "examples/" directory to source code which contains sample programs or - plugins that can be used together with ranger -* Added progress bars to copying, moving and directory loading processes -* Added feature to draw images inside the console using w3mimgdisplay (you need - to add "set preview_images true" in rc.conf) -* Added a plugin system like in the program "anki", i.e. place any python file - into ~/.config/ranger/plugins/ and it will be imported by ranger -* Added a separate file launcher named "rifle" that is configured through - rifle.conf and is installed as a standalone program. - Using "ranger [filename]" from the shell for opening files is deprecated now, - please use "rifle [filename]" instead. -* Added "uq" keybinding to undo closed tabs -* Added :setlocal command to change settings for specific directories only -* Added :travel command to move more quickly to your destination -* Added 256 color support for scope.sh -* Added a real yes/no prompt for :delete command -* Added settings: confirm_on_delete, draw_progress_bar_in_status_bar, - preview_images, status_bar_on_top, update_tmux_title -* Added commands: :mark_tag, :unmark_tag -* Added BSD-friendly setsid implementation -* Added as-you-type filtering for :filter command -* Replaced "options.py" file by :set commands in rc.conf -* Replaced "apps.py" file with rifle.conf -* Improved "r" key to interface with rifle -* Rewritten "scope.sh" in POSIX shell -* Changed copying/moving code to work without GNU coreutils -* Changed key to untag files from "T" to "ut" -* Changed the flag "d" (for detached) to "f" (for fork) in program launcher -* Changed appearance of keybinding-hints and bookmarks -* Changed tabs with 4 spaces in the source code (see PEP 8) -* Removed ranger.core.environment class -* Removed settings: colorscheme_overlay, draw_bookmark_borders, init_function, - load_default_rc -* Fixed zombie process apocalypse -* Fixed draw_borders=true in combination with padding_right=false - -2012-08-10: Version 1.5.5 -* Ensure that detached programs continue to run when ranger is killed - -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 <image>" - -2011-10-23: Version 1.5.2 -* Fixed graphical bug that appears in certian cases when drawing - characters at the right edge. - -2011-10-23: Version 1.5.1 -* Added fm.select_file(path) -* Added --choosefiles option (like --choosefile, but chooses multiple files) -* Fixed --list-unused-keys -* Fixed Zombie processes -* Fixed handling of filenames with undecodable bytes (unicode surrogates) -* Fixed crashes due to incomplete loading of directories -* Fixed tab completion of the command "shell" -* Fixed "ot" and "oT" keys in rc.conf -* Fixed parsing of chained commands (like in the binding "om") - -2011-10-11: Version 1.5.0 -* Full python3.2 compatibility -* Added new configuration file "rc.conf" which contains a list - of commands that are executed on startup - mainly used for keybindings -* Added --list-unused-keys -* Added new program handlers to apps.py -* Added pop-up window for keychains and bookmarks -* Added load_default_rc option -* Fixed all known unicode issues -* Fixed crash when $TERM is unknown to the system -* Fixed scrolling in colored preview -* Changed the default column_ratios to 1/3/4 and sorting method to "natural" -* Changed :rename so it doesn't overwrite existing files -* Internal actions are now accessible as commands -* Replaced unittests by doctests -* Replaced integrated help with an extended man page and dynamic lists - of keybindings, commands and settings. -* Removed "keys.py" configuration file in favor of "rc.conf" -* Removed "texas" colorscheme -* apps.py: Now able to define programs that only run with Xorg -* commands.py: Using parse(self.line) to parse the line is unnecessary now. - parse(self.line).rest(n) is now written as self.rest(n). - However, parse(self.line).chunk(n) has been renamed to self.arg(n). -* commands.py: parse(self.line) + X is now self.firstpart + X -* commands.py: New special attribute "resolve_macros" which decides whether - strings like %f should be expanded to the name of the current file, etc. -* commands.py: New special attribute "escape_macros_for_shell" to toggle - whether or not macros should be escaped, so you can use them in other - commands than :shell, for example :edit %f -* Countless small fixes and improvements - -2011-10-02: Version 1.4.4 -* Added keys for chmod (like +ow for "chmod o+w", etc) -* Added "c" flag for running files -* Added various key bindings -* Added wavpack and webm types to mime.types -* Added option "display_tags_in_all_columns" -* Added command.cancel method which is called when pressing ESC in console -* Added sorting and cycling by ctime and atime -* Added custom tags (press "x) -* Added bittorrent preview -* Fixed blocking when using interactive scripts in scope.sh -* Fixed issues with ALT key -* Fixed pager crash when trying to read non-readable file -* Forbid piping things into ranger -* Improved hints - -2011-04-05: Version 1.4.3 -* Fixed mimetype checking when invoking ranger with a filename -* Fixed loss of bookmarks when disk is full -* Minor improvements - -2011-03-05: Version 1.4.2 -* Added --choosefile and --choosedir flag -* Added use of bookmarks in tab completion of the :cd command -* Fixed bug with detached programs and python 3.2 - -2011-01-04: Version 1.4.1 -* Fixed crash when preview failed under some circumstances -* Fixed graphical bug when pressing i - -2010-12-22: Version 1.4.0 -* Added option to use any external scripts for previews (see scope.sh) -* Added key: zv to toggle the use of the external script -* Added indicator for the used filter (type "zf") -* Added option padding_right to remove whitespace if theres no preview -* Added command :search_inc for incremental search -* Added commands :save_copy_buffer and :load_copy_buffer to share - the copied files between ranger instances -* Added mimeopen as a fallback if no useful application can be found -* Added natural sort, sorts 1foo before 10foo. (type "on") -* Added keys: yp, yd and yb to copy path, dirname or basename to seleciton -* Let open_with use the selection, not just one file -* Run files with right mouse click -* Implemented copying via coreutils rather than internal python code -* Improved handling of unicode -* Some restructuration of the source code - -2010-12-13: Version 1.2.3 -* Enable binding to alt-keys -* Fixed memory leak in garbage collecting of old, unused directory objects -* Fixed python3 incompatibilities -* Fixed problems with identifying changes of files -* Fixed lazy lookup of some FSObject attributes - -2010-10-10: Version 1.2.2 -* Prevent currently used directories from being garbage collected -* Disable mouse buttons when console is open -* Fixed :cd command: Without arguments, cd's into $HOME -* Fixed bug which prevented pydoc to work on some config files -* Fixed some bugs in "snow" and "jungle" colorschemes -* Several other clean-ups and fixes - -2010-09-16: Version 1.2.1 -* Fixed yy/pp bug when yanking multiple directories - -2010-09-13: Version 1.2.0 -* !!! Changed the default configuration directory to ~/.config/ranger !!! -* Removed "Console Modes", each old mode is now a simple command -* Disabled file previews by default if ranger is used by root -* Allow to jump to specific help sections by typing two numbers, e.g. 13? -* Added keys: da, dr, ya, yr for adding and removing files from copy buffer -* Added keys: gl and gL to resolve links, see 11? -* Added key: pL to create a relative symlink -* Added %<LETTER> and %<N><LETTER> macros for the console, see 33? -* Fixed ansi codes for colors in the pager -* Use the file ~/.mime.types for mime type detection -* Several clean-ups and fixes - -2010-07-17: Version 1.1.2 -* Fix crash when using scrollwheel to scroll down in some cases -* The command "ranger dir1 dir2 ..." opens multiple directories in tabs -* Removed pydoc html documentation by default, re-create it with "make doc" -* Minor fixes - -2010-06-18: Version 1.1.1 -* New install script, "setup.py" -* New flag for running programs: "w" (waits for enter press) -* Minor fixes - -2010-06-09: Version 1.1.0 -* Added a man page -* Tab support -* Improved directory loading performance -* Commands are definable in ~/.ranger/commands.py -* Case insensitive sorting (type zs) -* Better UTF support -* Possibility to turn off previews (zp and zP) -* Changing options with :set (e.g. :set column_ratios=1,2,3,4) -* Ask for confirmation when using :delete -* New invocation flag: --fail-unless-cd -* New hotkeys, commands, options. -* New syntax for ~/.ranger/keys.py -* Several user contributions -* And tons of general improvements - -NOTE: The syntax for configuration is still subject to change. diff --git a/CHANGELOG.md b/CHANGELOG.md index cd7bd32f..bff84808 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ This log documents changes between stable versions. +# 2015-10-04: version 1.7.2 +* Fixed file name arguments passed to `sxiv` and `feh` when using `:flat` +* Fixed removal of empty directories when using `:rename` +* Fixed free disk space display on Mac OS X +* Fixed `examples/vim_file_chooser` to work with gvim too +* Fixed some other rare crashes and bugs +* Fixed downward mouse wheel scrolling +* Fixed warning about regex splits being drawin in title bar since python3.5 +* Really fixed `S` key binding not working when SHELL=fish +* Improved `doc/cheatsheet.svg` +* Added some entries to rifle.conf +* Added key bindings `pO` and `pP` which work like `po` and `pp` but queue the + operation in a first-in-first-out order. + # 2015-05-04: version 1.7.1 * Added `doc/cheatsheet.svg` * Added `examples/rc_emacs.conf`, a config file adding emacs-like key bindings diff --git a/ranger/__init__.py b/ranger/__init__.py index 2d3f04d6..20c8ab35 100644 --- a/ranger/__init__.py +++ b/ranger/__init__.py @@ -28,7 +28,7 @@ DEFAULT_PAGER = 'less' LOGFILE = tempfile.gettempdir()+'/ranger_errorlog' CACHEDIR = os.path.expanduser("~/.cache/ranger") USAGE = '%prog [options] [path]' -VERSION = 'ranger-stable %s\n\nPython %s' % (__version__, sys.version) +VERSION = 'ranger-master %s\n\nPython %s' % (__version__, sys.version) # If the environment variable XDG_CONFIG_HOME is non-empty, CONFDIR is ignored # and the configuration directory will be $XDG_CONFIG_HOME/ranger instead. diff --git a/ranger/container/file.py b/ranger/container/file.py index 6be60f85..a3db63a5 100644 --- a/ranger/container/file.py +++ b/ranger/container/file.py @@ -76,8 +76,6 @@ class File(FileSystemObject): if self.fm.settings.preview_script and \ self.fm.settings.use_preview_script: return True - if self.image and self.fm.settings.preview_images: - return True if self.container: return False if PREVIEW_WHITELIST.search(self.basename): diff --git a/ranger/container/fsobject.py b/ranger/container/fsobject.py index b63781de..aa848b7a 100644 --- a/ranger/container/fsobject.py +++ b/ranger/container/fsobject.py @@ -281,6 +281,8 @@ class FileSystemObject(FileManagerAware, SettingsAware): backend_state = self.settings.vcs_backend_hg elif self.vcs.vcsname == 'bzr': backend_state = self.settings.vcs_backend_bzr + elif self.vcs.vcsname == 'svn': + backend_state = self.settings.vcs_backend_svn else: backend_state = 'disabled' diff --git a/ranger/container/settings.py b/ranger/container/settings.py index 1d91495f..db8ca45c 100644 --- a/ranger/container/settings.py +++ b/ranger/container/settings.py @@ -60,6 +60,7 @@ ALLOWED_SETTINGS = { 'vcs_backend_bzr': str, 'vcs_backend_git': str, 'vcs_backend_hg': str, + 'vcs_backend_svn': str, 'xterm_alt_key': bool, } diff --git a/ranger/core/actions.py b/ranger/core/actions.py index e3dba313..8167dbcf 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -799,14 +799,11 @@ class Actions(FileManagerAware, SettingsAware): return pager = self.ui.open_pager() - if self.settings.preview_images and self.thisfile.image: - pager.set_image(self.thisfile.realpath) + f = self.thisfile.get_preview_source(pager.wid, pager.hei) + if self.thisfile.is_image_preview(): + pager.set_image(f) else: - f = self.thisfile.get_preview_source(pager.wid, pager.hei) - if self.thisfile.is_image_preview(): - pager.set_image(f) - else: - pager.set_source(f) + pager.set_source(f) # -------------------------- # -- Previews @@ -835,10 +832,6 @@ class Actions(FileManagerAware, SettingsAware): if not path or not os.path.exists(path): return None - if self.settings.preview_images and file.image: - pager.set_image(path) - return None - if self.settings.preview_script and self.settings.use_preview_script: # self.previews is a 2 dimensional dict: # self.previews['/tmp/foo.jpg'][(80, 24)] = "the content..." @@ -874,6 +867,13 @@ class Actions(FileManagerAware, SettingsAware): data['loading'] = True + if 'directimagepreview' in data: + data['foundpreview'] = True + data['imagepreview'] = True + pager.set_image(path) + data['loading'] = False + return path + cacheimg = os.path.join(ranger.CACHEDIR, self.sha1_encode(path)) if (os.path.isfile(cacheimg) and os.path.getmtime(cacheimg) > os.path.getmtime(path)): data['foundpreview'] = True @@ -883,7 +883,8 @@ class Actions(FileManagerAware, SettingsAware): return cacheimg loadable = CommandLoader(args=[self.settings.preview_script, - path, str(width), str(height), cacheimg], read=True, + path, str(width), str(height), cacheimg, + str(self.settings.preview_images)], read=True, silent=True, descr="Getting preview of %s" % path) def on_after(signal): exit = signal.process.poll() @@ -899,6 +900,8 @@ class Actions(FileManagerAware, SettingsAware): data[(-1, -1)] = content elif exit == 6: data['imagepreview'] = True + elif exit == 7: + data['directimagepreview'] = True elif exit == 1: data[(-1, -1)] = None data['foundpreview'] = False @@ -922,6 +925,9 @@ class Actions(FileManagerAware, SettingsAware): if 'imagepreview' in data: pager.set_image(cacheimg) return cacheimg + elif 'directimagepreview' in data: + pager.set_image(path) + return path else: pager.set_source(self.thisfile.get_preview_source( pager.wid, pager.hei)) diff --git a/ranger/core/loader.py b/ranger/core/loader.py index 1f9ec9cf..411b16ec 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -77,7 +77,7 @@ class CopyLoader(Loadable, FileManagerAware): # TODO: Don't calculate size when renaming (needs detection) bytes_per_tick = shutil_g.BLOCK_SIZE size = max(1, self._calculate_size(bytes_per_tick)) - bar_tick = 100.0 / (float(size) / bytes_per_tick) + done = 0 if self.do_cut: self.original_copy_buffer.clear() if len(self.copy_buffer) == 1: @@ -92,11 +92,13 @@ class CopyLoader(Loadable, FileManagerAware): self.fm.tags.tags[tf.replace(f.path, self.original_path \ + '/' + f.basename)] = tag self.fm.tags.dump() - for _ in shutil_g.move(src=f.path, + d = 0 + for d in shutil_g.move(src=f.path, dst=self.original_path, overwrite=self.overwrite): - self.percent += bar_tick + self.percent = float(done + d) / size * 100. yield + done += d else: if len(self.copy_buffer) == 1: self.description = "copying: " + self.one_file.path @@ -104,18 +106,22 @@ class CopyLoader(Loadable, FileManagerAware): self.description = "copying files from: " + self.one_file.dirname for f in self.copy_buffer: if os.path.isdir(f.path) and not os.path.islink(f.path): - for _ in shutil_g.copytree(src=f.path, + d = 0 + for d in shutil_g.copytree(src=f.path, dst=os.path.join(self.original_path, f.basename), symlinks=True, overwrite=self.overwrite): - self.percent += bar_tick + self.percent = float(done + d) / size * 100. yield + done += d else: - for _ in shutil_g.copy2(f.path, self.original_path, + d = 0 + for d in shutil_g.copy2(f.path, self.original_path, symlinks=True, overwrite=self.overwrite): - self.percent += bar_tick + self.percent = float(done + d) / size * 100. yield + done += d cwd = self.fm.get_directory(self.original_path) cwd.load_content() diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh index 3611c961..19404019 100755 --- a/ranger/data/scope.sh +++ b/ranger/data/scope.sh @@ -17,12 +17,14 @@ # 4 | fix height | success. Don't reload when height changes # 5 | fix both | success. Don't ever reload # 6 | image | success. display the image $cached points to as an image preview +# 7 | image | success. display the file directly as an image # Meaningful aliases for arguments: -path="$1" # Full path of the selected file -width="$2" # Width of the preview pane (number of fitting characters) -height="$3" # Height of the preview pane (number of fitting characters) -cached="$4" # Path that should be used to cache image previews +path="$1" # Full path of the selected file +width="$2" # Width of the preview pane (number of fitting characters) +height="$3" # Height of the preview pane (number of fitting characters) +cached="$4" # Path that should be used to cache image previews +preview_images="$5" # "True" if image previews are enabled, "False" otherwise. maxln=200 # Stop after $maxln lines. Can be used like ls | head -n $maxln @@ -44,6 +46,23 @@ trim() { head -n "$maxln"; } # wraps highlight to treat exit code 141 (killed by SIGPIPE) as success highlight() { command highlight "$@"; test $? = 0 -o $? = 141; } +# Image previews, if enabled in ranger. +if [ "$preview_images" = "True" ]; then + case "$mimetype" in + # Image previews for SVG files, disabled by default. + ###image/svg+xml) + ### convert "$path" "$cached" && exit 6 || exit 1;; + # Image previews for image files. w3mimgdisplay will be called for all + # image files (unless overriden as above), but might fail for + # unsupported types. + image/*) + exit 7;; + # Image preview for video, disabled by default.: + ###video/*) + ### ffmpegthumbnailer -i "$path" -o "$cached" -s 0 && exit 6 || exit 1;; + esac +fi + case "$extension" in # Archive extensions: 7z|a|ace|alz|arc|arj|bz|bz2|cab|cpio|deb|gz|jar|lha|lz|lzh|lzma|lzo|\ @@ -76,9 +95,6 @@ case "$mimetype" in # Ascii-previews of images: image/*) img2txt --gamma=0.6 --width="$width" "$path" && exit 4 || exit 1;; - # Image preview for videos, disabled by default: - # video/*) - # ffmpegthumbnailer -i "$path" -o "$cached" -s 0 && exit 6 || exit 1;; # Display information about media files: video/* | audio/*) exiftool "$path" && exit 5 diff --git a/ranger/ext/shutil_generatorized.py b/ranger/ext/shutil_generatorized.py index b20d5cef..6b63f4c7 100644 --- a/ranger/ext/shutil_generatorized.py +++ b/ranger/ext/shutil_generatorized.py @@ -30,12 +30,14 @@ except NameError: def copyfileobj(fsrc, fdst, length=BLOCK_SIZE): """copy data from file-like object fsrc to file-like object fdst""" + done = 0 while 1: buf = fsrc.read(length) if not buf: break fdst.write(buf) - yield + done += len(buf) + yield done def _samefile(src, dst): # Macintosh, Unix. @@ -69,8 +71,8 @@ def copyfile(src, dst): try: fsrc = open(src, 'rb') fdst = open(dst, 'wb') - for _ in copyfileobj(fsrc, fdst): - yield + for done in copyfileobj(fsrc, fdst): + yield done finally: if fdst: fdst.close() @@ -107,8 +109,8 @@ def copy2(src, dst, overwrite=False, symlinks=False): os.unlink(dst) os.symlink(linkto, dst) else: - for _ in copyfile(src, dst): - yield + for done in copyfile(src, dst): + yield done copystat(src, dst) def get_safe_path(dst): @@ -165,6 +167,7 @@ def copytree(src, dst, symlinks=False, ignore=None, overwrite=False): if not overwrite: dst = get_safe_path(dst) os.makedirs(dst) + done = 0 for name in names: if name in ignored_names: continue @@ -178,14 +181,18 @@ def copytree(src, dst, symlinks=False, ignore=None, overwrite=False): os.symlink(linkto, dstname) copystat(srcname, dstname) elif os.path.isdir(srcname): - for _ in copytree(srcname, dstname, symlinks, + d = 0 + for d in copytree(srcname, dstname, symlinks, ignore, overwrite): - yield + yield done + d + done += d else: # Will raise a SpecialFileError for unsupported file types - for _ in copy2(srcname, dstname, + d = 0 + for d in copy2(srcname, dstname, overwrite=overwrite, symlinks=symlinks): - yield + yield done + d + done += d # catch the Error from the recursive copytree so that we can # continue with other files except Error as err: @@ -283,12 +290,12 @@ def move(src, dst, overwrite=False): if os.path.isdir(src): if _destinsrc(src, dst): raise Error("Cannot move a directory '%s' into itself '%s'." % (src, dst)) - for _ in copytree(src, real_dst, symlinks=True, overwrite=overwrite): - yield + for done in copytree(src, real_dst, symlinks=True, overwrite=overwrite): + yield done rmtree(src) else: - for _ in copy2(src, real_dst, symlinks=True, overwrite=overwrite): - yield + for done in copy2(src, real_dst, symlinks=True, overwrite=overwrite): + yield done os.unlink(src) def _destinsrc(src, dst): diff --git a/ranger/ext/vcs/svn.py b/ranger/ext/vcs/svn.py new file mode 100644 index 00000000..9bf8698c --- /dev/null +++ b/ranger/ext/vcs/svn.py @@ -0,0 +1,274 @@ +# -*- coding: utf-8 -*- +# This file is part of ranger, the console file manager. +# License: GNU GPL version 3, see the file "AUTHORS" for details. +# Author: Abdó Roig-Maranges <abdo.roig@gmail.com>, 2011-2012 +# Ryan Burns <rdburns@gmail.com>, 2015 +# +# R. Burns start with Abdó's Hg module and modified it for Subversion. +# +# vcs - a python module to handle various version control systems + +from __future__ import with_statement +import os +import re +import shutil +import logging +from datetime import datetime +from .vcs import Vcs, VcsError + +#logging.basicConfig(filename='rangersvn.log',level=logging.DEBUG, +# filemode='w') + +class SVN(Vcs): + vcsname = 'svn' + HEAD = 'HEAD' + # Auxiliar stuff + #--------------------------- + + def _svn(self, path, args, silent=True, catchout=False, bytes=False): + return self._vcs(path, 'svn', args, silent=silent, catchout=catchout, bytes=bytes) + + + def _has_head(self): + """Checks whether repo has head""" + return True + + + def _sanitize_rev(self, rev): + if rev == None: return None + rev = rev.strip() + if len(rev) == 0: return None + if rev[-1] == '+': rev = rev[:-1] + + try: + if int(rev) == 0: return None + except: + pass + + return rev + + + def _log(self, refspec=None, maxres=None, filelist=None): + """ Retrieves log message and parses it. + """ + args = ['log'] + + if refspec: args = args + ['--limit', '1', '-r', refspec] + elif maxres: args = args + ['--limit', str(maxres)] + + if filelist: args = args + filelist + logging.debug('Running svn log') + logging.debug(args) + + raw = self._svn(self.path, args, catchout=True) + logging.debug(raw) + L = re.findall(r"""^[-]*\s* # Dash line + r([0-9]*)\s\|\s # Revision [0] + (.*)\s\|\s # Author [1] + (.*?)\s # Date [2] + (.*?)\s # Time [3] + .*?\|\s*? # Dump rest of date string + ([0-9])+\sline(?:s)?\s* # Number of line(s) [4] + (.*)\s # Log Message [5] + [-]+\s* # Dash line + $""", raw, re.MULTILINE | re.VERBOSE) + + log = [] + for t in L: + logging.debug(t) + dt = {} + dt['short'] = t[0].strip() + dt['revid'] = t[0].strip() + dt['author'] = t[1].strip() + dt['date'] = datetime.strptime(t[2]+'T'+t[3], "%Y-%m-%dT%H:%M:%S") + dt['summary'] = t[5].strip() + log.append(dt) + logging.debug(log) + return log + + + def _svn_file_status(self, st): + if len(st) != 1: raise VcsError("Wrong hg file status string: %s" % st) + if st in "A": return 'staged' + elif st in "D": return 'deleted' + elif st in "I": return 'ignored' + elif st in "?": return 'untracked' + elif st in "C": return 'conflict' + else: return 'unknown' + + + + # Repo creation + #--------------------------- + + def init(self): + """Initializes a repo in current path""" + self._svn(self.path, ['init']) + self.update() + + + def clone(self, src): + """Checks out SVN repo""" + name = os.path.basename(self.path) + path = os.path.dirname(self.path) + try: + os.rmdir(self.path) + except OSError: + raise VcsError("Can't clone to %s. It is not an empty directory" % self.path) + + self._svn(path, ['co', src, name]) + self.update() + + + + # Action Interface + #--------------------------- + + def commit(self, message): + """Commits with a given message""" + self._svn(self.path, ['commit', '-m', message]) + + + def add(self, filelist=None): + """Adds files to the index, preparing for commit""" + if filelist != None: self._svn(self.path, ['add'] + filelist) + else: self._svn(self.path, ['add']) + + + def reset(self, filelist=None): + """Equivalent to svn revert""" + if filelist == None: filelist = self.get_status_allfiles().keys() + self._svn(self.path, ['revert'] + filelist) + + + def pull(self): + """Executes SVN Update""" + self._svn(self.path, ['update']) + + + def push(self): + """Push doesn't have an SVN analog.""" + raise NotImplementedError + + + def checkout(self, rev): + """Checks out a branch or revision""" + raise NotImplementedError + self._svn(self.path, ['update', rev]) + + + def extract_file(self, rev, name, dest): + """Extracts a file from a given revision and stores it in dest dir""" + if rev == self.INDEX: + shutil.copyfile(os.path.join(self.path, name), dest) + else: + file_contents = self._svn(self.path, ['cat', '-r', rev, name], catchout=True) + with open(dest, 'w') as f: + f.write(file_contents) + + + # Data Interface + #--------------------------- + + def get_status_allfiles(self): + """Returns a dict indexed by files not in sync their status as values. + Paths are given relative to the root. Strips trailing '/' from dirs.""" + raw = self._svn(self.path, ['status'], catchout=True, bytes=True) +# logging.debug(raw) + L = re.findall(r'^(.)\s*(.*?)\s*$', raw.decode('utf-8'), re.MULTILINE) + ret = {} + for st, p in L: + # Detect conflict by the existence of .orig files + if st == '?' and re.match(r'^.*\.orig\s*$', p): st = 'X' + sta = self._svn_file_status(st) + ret[os.path.normpath(p.strip())] = sta + return ret + + + def get_ignore_allfiles(self): + """Returns a set of all the ignored files in the repo""" + raw = self._svn(self.path, ['status'], catchout=True, bytes=True) +# logging.debug(raw) + L = re.findall(r'^I\s*(.*?)\s*$', raw.decode('utf-8'), re.MULTILINE) + return set(L) + + + def get_remote_status(self): + """Checks the status of the repo regarding sync state with remote branch. + + I'm not sure this make sense for SVN so we're just going to return 'sync'""" + return 'sync' + + def get_branch(self): + """Returns the current named branch, if this makes sense for the backend. None otherwise""" + return None + branch = self._svn(self.path, ['branch'], catchout=True) + return branch or None + + + def get_log(self, filelist=None, maxres=None): + """Get the entire log for the current HEAD""" + if not self._has_head(): return [] + return self._log(refspec=None, maxres=maxres, filelist=filelist) + + + def get_raw_log(self, filelist=None): + """Gets the raw log as a string""" + if not self._has_head(): return [] + args = ['log'] + if filelist: args = args + filelist + return self._svn(self.path, args, catchout=True) + + + def get_raw_diff(self, refspec=None, filelist=None): + """Gets the raw diff as a string""" + args = ['diff', '--git'] + if refspec: args = args + [refspec] + if filelist: args = args + filelist + return self._svn(self.path, args, catchout=True) + + + def get_remote(self, rev=None): + """Returns the url for the remote repo attached to head""" + raw = self.get_info(rev=rev) + L = re.findall('URL: (.*)\n', raw.decode('utf-8')) + + return L[0][0] + + + def get_revision_id(self, rev=None): + """Get a canonical key for the revision rev. + + This is just returning the rev for SVN""" + if rev == None: rev = self.HEAD + elif rev == self.INDEX: return None + rev = self._sanitize_rev(rev) + return rev + + + def get_info(self, rev=None): + """Gets info about the given revision rev""" + if rev == None: rev = self.HEAD + rev = self._sanitize_rev(rev) + if rev == self.HEAD and not self._has_head(): return None + logging.debug('refspec is ' + str(rev)) + L = self._log(refspec=rev) + logging.debug(len(L)) + if len(L) == 0: + raise VcsError("Revision %s does not exist" % rev) + elif len(L) > 1: + raise VcsError("More than one instance of revision %s ?!?" % rev) + else: + return L[0] + + + def get_files(self, rev=None): + """Gets a list of files in revision rev""" + if rev == None: rev = self.HEAD + rev = self._sanitize_rev(rev) + + raw = self._svn(self.path, ['ls', "-r", rev], catchout=True) + return raw.split('\n') + + +# vim: expandtab:shiftwidth=4:tabstop=4:softtabstop=4:textwidth=80 diff --git a/ranger/ext/vcs/vcs.py b/ranger/ext/vcs/vcs.py index 9bbb1779..3f7236f5 100644 --- a/ranger/ext/vcs/vcs.py +++ b/ranger/ext/vcs/vcs.py @@ -51,7 +51,8 @@ class Vcs(object): from .git import Git from .hg import Hg from .bzr import Bzr - self.repo_types = {'git': Git, 'hg': Hg, 'bzr': Bzr} + from .svn import SVN + self.repo_types = {'git': Git, 'hg': Hg, 'bzr': Bzr, 'svn': SVN} self.path = os.path.expanduser(path) self.status = {} diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py index 5d055a0b..b38d0b5b 100644 --- a/ranger/gui/widgets/browsercolumn.py +++ b/ranger/gui/widgets/browsercolumn.py @@ -180,19 +180,15 @@ class BrowserColumn(Pager): Pager.close(self) return - if self.fm.settings.preview_images and self.target.image: - self.set_image(self.target.realpath) - Pager.draw(self) + f = self.target.get_preview_source(self.wid, self.hei) + if f is None: + Pager.close(self) else: - f = self.target.get_preview_source(self.wid, self.hei) - if f is None: - Pager.close(self) + if self.target.is_image_preview(): + self.set_image(f) else: - if self.target.is_image_preview(): - self.set_image(f) - else: - self.set_source(f) - Pager.draw(self) + self.set_source(f) + Pager.draw(self) def _draw_directory(self): """Draw the contents of a directory""" diff --git a/ranger/gui/widgets/browserview.py b/ranger/gui/widgets/browserview.py index eb99369b..e9640208 100644 --- a/ranger/gui/widgets/browserview.py +++ b/ranger/gui/widgets/browserview.py @@ -258,9 +258,7 @@ class BrowserView(Widget, DisplayableContainer): result = not self.columns[-1].has_preview() target = self.columns[-1].target if not result and target and target.is_file: - if target.image and self.fm.settings.preview_images: - result = False # don't collapse when drawing images - elif self.fm.settings.preview_script and \ + if self.fm.settings.preview_script and \ self.fm.settings.use_preview_script: try: result = not self.fm.previews[target.realpath]['foundpreview'] @@ -355,8 +353,7 @@ class BrowserView(Widget, DisplayableContainer): self.columns[-1].visible = True if self.preview and self.is_collapsed != self._collapse(): - if (self.fm.settings.preview_images and - self.fm.settings.preview_files): + if self.fm.settings.preview_files: # force clearing the image when resizing preview column self.columns[-1].clear_image(force=True) self.resize(self.y, self.x, self.hei, self.wid) diff --git a/setup.py b/setup.py index 1e074f8d..4fbbccba 100755 --- a/setup.py +++ b/setup.py @@ -2,7 +2,12 @@ # This file is part of ranger, the console file manager. # License: GNU GPL version 3, see the file "AUTHORS" for details. -import distutils.core +# Setuptools is superior but not available in the stdlib +try: + from setuptools import setup +except ImportError: + from distutils.core import setup + import os.path import ranger @@ -11,7 +16,7 @@ def _findall(directory): if os.path.isfile(os.path.join(directory, f))] if __name__ == '__main__': - distutils.core.setup( + setup( name='ranger', description='Vim-like file manager', long_description=ranger.__doc__, @@ -27,7 +32,7 @@ if __name__ == '__main__': 'doc/rifle.1']), ('share/doc/ranger', ['README.md', - 'CHANGELOG', + 'CHANGELOG.md', 'HACKING.md', 'doc/colorschemes.txt']), ('share/doc/ranger/config/colorschemes', |