From 42cda6f82a8019cf477527ff4b44b22948ae65cf Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 20 Apr 2010 10:51:07 +0200 Subject: core.actions: use `cp` for copying --- ranger/core/actions.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 00e4782b..ef18a553 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -16,9 +16,11 @@ import os import re import shutil +import time from os.path import join, isdir from os import symlink, getcwd from inspect import cleandoc +from subprocess import Popen, PIPE import ranger from ranger.ext.direction import Direction @@ -591,18 +593,13 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): else: descr = "copying files from: " + one_file.dirname def generate(): - for f in self.env.copy: - if isdir(f.path): - for _ in shutil_g.copytree(src=f.path, - dst=join(self.env.cwd.path, f.basename), - symlinks=True, - overwrite=overwrite): - yield - else: - for _ in shutil_g.copy2(f.path, original_path, - symlinks=True, - overwrite=overwrite): - yield + process = Popen(['cp', '--backup=existing', '--archive', + '-t', self.env.cwd.path] + \ + [f.path for f in self.env.copy], + stdout=PIPE, stderr=PIPE) + while process.poll() is None: + yield + time.sleep(0.05) cwd = self.env.get_directory(original_path) cwd.load_content() -- cgit 1.4.1-2-gfad0 From b1ed3da5ebd065e2077370bf7fc24112906943e3 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 20 Apr 2010 12:58:31 +0200 Subject: core.actions.paste(): use select for parallel listening for stderr --- ranger/core/actions.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index ef18a553..fa77a497 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -15,6 +15,7 @@ import os import re +import select import shutil import time from os.path import join, isdir @@ -593,13 +594,17 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): else: descr = "copying files from: " + one_file.dirname def generate(): + null = open(os.devnull, 'w') process = Popen(['cp', '--backup=existing', '--archive', '-t', self.env.cwd.path] + \ [f.path for f in self.env.copy], - stdout=PIPE, stderr=PIPE) + stdout=null, stderr=PIPE) while process.poll() is None: + rd, _, __ = select.select( + [process.stderr], [], [], 0.05) + if rd: + self.notify(process.stderr.readline(), bad=True) yield - time.sleep(0.05) cwd = self.env.get_directory(original_path) cwd.load_content() -- cgit 1.4.1-2-gfad0 From 456e2e5353de6e6defb8379b6d105eef5b5f65cc Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 20 Apr 2010 14:25:12 +0200 Subject: core.actions.paste(): use `mv`, delete shutil_generatorized --- ranger/core/actions.py | 16 +- ranger/ext/shutil_generatorized.py | 302 ------------------------------------- 2 files changed, 11 insertions(+), 307 deletions(-) delete mode 100644 ranger/ext/shutil_generatorized.py diff --git a/ranger/core/actions.py b/ranger/core/actions.py index fa77a497..47b15fa6 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -581,11 +581,17 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): else: descr = "moving files from: " + one_file.dirname def generate(): - for f in copied_files: - for _ in shutil_g.move(src=f.path, - dst=original_path, - overwrite=overwrite): - yield + null = open(os.devnull, 'w') + process = Popen(['mv', '--backup=existing', + '-t', self.env.cwd.path] + \ + [f.path for f in copied_files], + stdout=null, stderr=PIPE) + while process.poll() is None: + rd, _, __ = select.select( + [process.stderr], [], [], 0.05) + if rd: + self.notify(process.stderr.readline(), bad=True) + yield cwd = self.env.get_directory(original_path) cwd.load_content() else: diff --git a/ranger/ext/shutil_generatorized.py b/ranger/ext/shutil_generatorized.py deleted file mode 100644 index a02ccfe4..00000000 --- a/ranger/ext/shutil_generatorized.py +++ /dev/null @@ -1,302 +0,0 @@ -# This file was taken from the python standard library and has been -# slightly modified to do a "yield" after every 16KB of copying -"""Utility functions for copying files and directory trees. - -XXX The functions here don't copy the resource fork or other metadata on Mac. - -""" - -import os -import sys -import stat -from os.path import abspath - -__all__ = ["copyfileobj","copyfile","copystat","copy2", - "copytree","move","rmtree","Error", "SpecialFileError"] - -APPENDIX = '_' - -class Error(EnvironmentError): - pass - -class SpecialFileError(EnvironmentError): - """Raised when trying to do a kind of operation (e.g. copying) which is - not supported on a special file (e.g. a named pipe)""" - -try: - WindowsError -except NameError: - WindowsError = None - -def copyfileobj(fsrc, fdst, length=16*1024): - """copy data from file-like object fsrc to file-like object fdst""" - while 1: - buf = fsrc.read(length) - if not buf: - break - fdst.write(buf) - yield - -def _samefile(src, dst): - # Macintosh, Unix. - if hasattr(os.path,'samefile'): - try: - return os.path.samefile(src, dst) - except OSError: - return False - - # All other platforms: check for same pathname. - return (os.path.normcase(abspath(src)) == - os.path.normcase(abspath(dst))) - -def copyfile(src, dst): - """Copy data from src to dst""" - if _samefile(src, dst): - raise Error("`%s` and `%s` are the same file" % (src, dst)) - - fsrc = None - fdst = None - for fn in [src, dst]: - try: - st = os.stat(fn) - except OSError: - # File most likely does not exist - pass - else: - # XXX What about other special files? (sockets, devices...) - if stat.S_ISFIFO(st.st_mode): - raise SpecialFileError("`%s` is a named pipe" % fn) - try: - fsrc = open(src, 'rb') - fdst = open(dst, 'wb') - for _ in copyfileobj(fsrc, fdst): - yield - finally: - if fdst: - fdst.close() - if fsrc: - fsrc.close() - -def copystat(src, dst): - """Copy all stat info (mode bits, atime, mtime, flags) from src to dst""" - st = os.stat(src) - mode = stat.S_IMODE(st.st_mode) - if hasattr(os, 'utime'): - try: os.utime(dst, (st.st_atime, st.st_mtime)) - except: pass - if hasattr(os, 'chmod'): - try: os.chmod(dst, mode) - except: pass - if hasattr(os, 'chflags') and hasattr(st, 'st_flags'): - try: os.chflags(dst, st.st_flags) - except: pass - -def copy2(src, dst, overwrite=False, symlinks=False): - """Copy data and all stat info ("cp -p src dst"). - - The destination may be a directory. - - """ - if os.path.isdir(dst): - dst = os.path.join(dst, os.path.basename(src)) - if not overwrite: - dst = get_safe_path(dst) - if symlinks and os.path.islink(src): - linkto = os.readlink(src) - os.symlink(linkto, dst) - else: - for _ in copyfile(src, dst): - yield - copystat(src, dst) - -def get_safe_path(dst): - if not os.path.exists(dst): - return dst - if not dst.endswith(APPENDIX): - dst += APPENDIX - if not os.path.exists(dst): - return dst - n = 0 - test_dst = dst + str(n) - while os.path.exists(test_dst): - n += 1 - test_dst = dst + str(n) - - return test_dst - -def copytree(src, dst, symlinks=False, ignore=None, overwrite=False): - """Recursively copy a directory tree using copy2(). - - The destination directory must not already exist. - If exception(s) occur, an Error is raised with a list of reasons. - - If the optional symlinks flag is true, symbolic links in the - source tree result in symbolic links in the destination tree; if - it is false, the contents of the files pointed to by symbolic - links are copied. - - The optional ignore argument is a callable. If given, it - is called with the `src` parameter, which is the directory - being visited by copytree(), and `names` which is the list of - `src` contents, as returned by os.listdir(): - - callable(src, names) -> ignored_names - - Since copytree() is called recursively, the callable will be - called once for each directory that is copied. It returns a - list of names relative to the `src` directory that should - not be copied. - - XXX Consider this example code rather than the ultimate tool. - - """ - names = os.listdir(src) - if ignore is not None: - ignored_names = ignore(src, names) - else: - ignored_names = set() - - errors = [] - try: - os.makedirs(dst) - except Exception as err: - if not overwrite: - dst = get_safe_path(dst) - os.makedirs(dst) - for name in names: - if name in ignored_names: - continue - srcname = os.path.join(src, name) - dstname = os.path.join(dst, name) - try: - if symlinks and os.path.islink(srcname): - linkto = os.readlink(srcname) - if os.path.lexists(dstname): - if not os.path.islink(dstname) \ - or os.readlink(dstname) != linkto: - os.unlink(dstname) - os.symlink(linkto, dstname) - elif os.path.isdir(srcname): - for _ in copytree(srcname, dstname, symlinks, - ignore, overwrite): - yield - else: - # Will raise a SpecialFileError for unsupported file types - for _ in copy2(srcname, dstname, - overwrite=overwrite, symlinks=symlinks): - yield - # catch the Error from the recursive copytree so that we can - # continue with other files - except Error as err: - errors.extend(err.args[0]) - except EnvironmentError as why: - errors.append((srcname, dstname, str(why))) - try: - copystat(src, dst) - except OSError as why: - if WindowsError is not None and isinstance(why, WindowsError): - # Copying file access times may fail on Windows - pass - else: - errors.extend((src, dst, str(why))) - if errors: - raise Error(errors) - -def rmtree(path, ignore_errors=False, onerror=None): - """Recursively delete a directory tree. - - If ignore_errors is set, errors are ignored; otherwise, if onerror - is set, it is called to handle the error with arguments (func, - path, exc_info) where func is os.listdir, os.remove, or os.rmdir; - path is the argument to that function that caused it to fail; and - exc_info is a tuple returned by sys.exc_info(). If ignore_errors - is false and onerror is None, an exception is raised. - - """ - if ignore_errors: - def onerror(*args): - pass - elif onerror is None: - def onerror(*args): - raise - try: - if os.path.islink(path): - # symlinks to directories are forbidden, see bug #1669 - raise OSError("Cannot call rmtree on a symbolic link") - except OSError: - onerror(os.path.islink, path, sys.exc_info()) - # can't continue even if onerror hook returns - return - names = [] - try: - names = os.listdir(path) - except os.error as err: - onerror(os.listdir, path, sys.exc_info()) - for name in names: - fullname = os.path.join(path, name) - try: - mode = os.lstat(fullname).st_mode - except os.error: - mode = 0 - if stat.S_ISDIR(mode): - rmtree(fullname, ignore_errors, onerror) - else: - try: - os.remove(fullname) - except os.error as err: - onerror(os.remove, fullname, sys.exc_info()) - try: - os.rmdir(path) - except os.error: - onerror(os.rmdir, path, sys.exc_info()) - - -def _basename(path): - # A basename() variant which first strips the trailing slash, if present. - # Thus we always get the last component of the path, even for directories. - return os.path.basename(path.rstrip(os.path.sep)) - -def move(src, dst, overwrite=False): - """Recursively move a file or directory to another location. This is - similar to the Unix "mv" command. - - If the destination is a directory or a symlink to a directory, the source - is moved inside the directory. The destination path must not already - exist. - - If the destination already exists but is not a directory, it may be - overwritten depending on os.rename() semantics. - - If the destination is on our current filesystem, then rename() is used. - Otherwise, src is copied to the destination and then removed. - A lot more could be done here... A look at a mv.c shows a lot of - the issues this implementation glosses over. - - """ - real_dst = dst - if not overwrite: - real_dst = get_safe_path(os.path.join(dst, _basename(src))) - try: - os.rename(src, real_dst) - except OSError: - 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 - rmtree(src) - else: - for _ in copy2(src, real_dst, symlinks=True, overwrite=overwrite): - yield - os.unlink(src) - -def _destinsrc(src, dst): - src = abspath(src) - dst = abspath(dst) - if not src.endswith(os.path.sep): - src += os.path.sep - if not dst.endswith(os.path.sep): - dst += os.path.sep - return dst.startswith(src) - -# vi: expandtab sts=4 ts=4 sw=4 -- cgit 1.4.1-2-gfad0 From 996d96d15f58fc796b60bd4be701973f757ca164 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 20 Apr 2010 15:45:10 +0200 Subject: core.loader: added CommandLoader --- ranger/core/actions.py | 44 +++++++++++++------------------------------- ranger/core/loader.py | 21 +++++++++++++++++++++ 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 47b15fa6..083f0c80 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -30,7 +30,7 @@ from ranger.shared import FileManagerAware, EnvironmentAware, SettingsAware from ranger.gui.widgets import console_mode as cmode from ranger.fsobject import File from ranger.ext import shutil_generatorized as shutil_g -from ranger.core.loader import LoadableObject +from ranger.core.loader import CommandLoader class Actions(FileManagerAware, EnvironmentAware, SettingsAware): search_method = 'ctime' @@ -567,6 +567,10 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): if not copied_files: return + def refresh(_): + cwd = self.env.get_directory(original_path) + cwd.load_content() + original_path = self.env.cwd.path try: one_file = copied_files[0] @@ -580,41 +584,19 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): descr = "moving: " + one_file.path else: descr = "moving files from: " + one_file.dirname - def generate(): - null = open(os.devnull, 'w') - process = Popen(['mv', '--backup=existing', - '-t', self.env.cwd.path] + \ - [f.path for f in copied_files], - stdout=null, stderr=PIPE) - while process.poll() is None: - rd, _, __ = select.select( - [process.stderr], [], [], 0.05) - if rd: - self.notify(process.stderr.readline(), bad=True) - yield - cwd = self.env.get_directory(original_path) - cwd.load_content() + obj = CommandLoader(args=['mv', '--backup=existing', + '-t', self.env.cwd.path] + [f.path for f in copied_files], + descr=descr, end_hook=refresh) else: if len(copied_files) == 1: descr = "copying: " + one_file.path else: descr = "copying files from: " + one_file.dirname - def generate(): - null = open(os.devnull, 'w') - process = Popen(['cp', '--backup=existing', '--archive', - '-t', self.env.cwd.path] + \ - [f.path for f in self.env.copy], - stdout=null, stderr=PIPE) - while process.poll() is None: - rd, _, __ = select.select( - [process.stderr], [], [], 0.05) - if rd: - self.notify(process.stderr.readline(), bad=True) - yield - cwd = self.env.get_directory(original_path) - cwd.load_content() - - self.loader.add(LoadableObject(generate(), descr)) + obj = CommandLoader(args=['cp', '--backup=existing', '--archive', + '-t', self.env.cwd.path] + [f.path for f in self.env.copy], + descr=descr, end_hook=refresh) + + self.loader.add(obj) def delete(self): self.notify("Deleting!", duration=1) diff --git a/ranger/core/loader.py b/ranger/core/loader.py index 4f4424e4..e4bc2d95 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -35,6 +35,27 @@ class LoadableObject(object): return self.description +class CommandLoader(LoadableObject): + def __init__(self, args, descr, begin_hook=None, end_hook=None): + self.description = descr + self.args = args + self.begin_hook = begin_hook + self.end_hook = end_hook + + def load_generator(self): + process = Popen(self.args, stdout=open(os.devnull, 'w'), stderr=PIPE) + if self.begin_hook: + self.begin_hook(process) + while process.poll() is None: + rd, _, __ = select.select( + [process.stderr], [], [], 0.05) + if rd: + self.notify(process.stderr.readline(), bad=True) + yield + if self.end_hook(process): + self.end_hook(process) + + class Loader(FileManagerAware): seconds_of_work_time = 0.03 -- cgit 1.4.1-2-gfad0 From af2c87a88b5fb34cd5d5a385bc79e71643f582e1 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 20 Apr 2010 16:26:41 +0200 Subject: core.loader: fixed --- ranger/core/actions.py | 4 ---- ranger/core/loader.py | 13 +++++++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 083f0c80..408bbf3a 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -15,13 +15,10 @@ import os import re -import select import shutil -import time from os.path import join, isdir from os import symlink, getcwd from inspect import cleandoc -from subprocess import Popen, PIPE import ranger from ranger.ext.direction import Direction @@ -29,7 +26,6 @@ from ranger import fsobject from ranger.shared import FileManagerAware, EnvironmentAware, SettingsAware from ranger.gui.widgets import console_mode as cmode from ranger.fsobject import File -from ranger.ext import shutil_generatorized as shutil_g from ranger.core.loader import CommandLoader class Actions(FileManagerAware, EnvironmentAware, SettingsAware): diff --git a/ranger/core/loader.py b/ranger/core/loader.py index e4bc2d95..24dac464 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -14,9 +14,13 @@ # along with this program. If not, see . from collections import deque -from time import time -from ranger.shared import FileManagerAware +from time import time, sleep +from subprocess import Popen, PIPE import math +import os +import select + +from ranger.shared import FileManagerAware def status_generator(): """Generate a rotating line which can be used as a throbber""" @@ -37,12 +41,12 @@ class LoadableObject(object): class CommandLoader(LoadableObject): def __init__(self, args, descr, begin_hook=None, end_hook=None): - self.description = descr + LoadableObject.__init__(self, self.generate(), descr) self.args = args self.begin_hook = begin_hook self.end_hook = end_hook - def load_generator(self): + def generate(self): process = Popen(self.args, stdout=open(os.devnull, 'w'), stderr=PIPE) if self.begin_hook: self.begin_hook(process) @@ -51,6 +55,7 @@ class CommandLoader(LoadableObject): [process.stderr], [], [], 0.05) if rd: self.notify(process.stderr.readline(), bad=True) + sleep(0.02) yield if self.end_hook(process): self.end_hook(process) -- cgit 1.4.1-2-gfad0 From 42ae95008fcba918380a1016420dd24d888cb9df Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 27 Apr 2010 20:37:11 +0200 Subject: messing around --- ranger/__main__.py | 12 ++++-------- ranger/core/fm.py | 15 +++++++++++++++ ranger/core/loader.py | 23 ++++++++++++++++++++++- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/ranger/__main__.py b/ranger/__main__.py index 8bb0bfa1..d25065a1 100644 --- a/ranger/__main__.py +++ b/ranger/__main__.py @@ -187,10 +187,10 @@ def main(): else: path = '.' + # Initialize objects + EnvironmentAware._assign(Environment(path)) + fm = FM() try: - # Initialize objects - EnvironmentAware._assign(Environment(path)) - fm = FM() load_settings(fm, ranger.arg.clean) FileManagerAware._assign(fm) fm.ui = UI() @@ -200,11 +200,7 @@ def main(): fm.ui.initialize() fm.loop() finally: - # Finish, clean up - try: - fm.ui.destroy() - except (AttributeError, NameError): - pass + fm.destroy() if __name__ == '__main__': diff --git a/ranger/core/fm.py b/ranger/core/fm.py index 0702e472..7a55afa7 100644 --- a/ranger/core/fm.py +++ b/ranger/core/fm.py @@ -94,6 +94,21 @@ class FM(Actions, SignalDispatcher): self.env.signal_bind('cd', self._update_current_tab) + def destroy(self): + debug = ranger.arg.debug + if self.ui: + try: + self.ui.destroy() + except: + if debug: + raise + if self.loader: + try: + self.loader.destroy() + except: + if debug: + raise + def block_input(self, sec=0): self.input_blocked = sec != 0 self.input_blocked_until = time() + sec diff --git a/ranger/core/loader.py b/ranger/core/loader.py index 24dac464..53d9dc6e 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -30,14 +30,25 @@ def status_generator(): yield '\\' yield '|' + class LoadableObject(object): def __init__(self, gen, descr): self.load_generator = gen self.description = descr + self.paused = False def get_description(self): return self.description + def pause(self): + self.paused = True + + def unpause(self): + self.paused = False + + def destroy(self): + pass + class CommandLoader(LoadableObject): def __init__(self, args, descr, begin_hook=None, end_hook=None): @@ -47,7 +58,9 @@ class CommandLoader(LoadableObject): self.end_hook = end_hook def generate(self): - process = Popen(self.args, stdout=open(os.devnull, 'w'), stderr=PIPE) + self.process = process = Popen(self.args, + stdout=open(os.devnull, 'w'), + stderr=PIPE) if self.begin_hook: self.begin_hook(process) while process.poll() is None: @@ -60,6 +73,10 @@ class CommandLoader(LoadableObject): if self.end_hook(process): self.end_hook(process) + def destroy(self): + if self.process: + self.process.kill() + class Loader(FileManagerAware): seconds_of_work_time = 0.03 @@ -151,3 +168,7 @@ class Loader(FileManagerAware): def has_work(self): """Is there anything to load?""" return bool(self.queue) + + def destroy(self): + while self.queue: + self.queue.pop().destroy() -- cgit 1.4.1-2-gfad0 From 792d6b23711c428a4a2b98537f44e2fbaed97fdd Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 28 Apr 2010 15:53:53 +0200 Subject: core.loader: bugfix --- ranger/core/loader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/core/loader.py b/ranger/core/loader.py index 53d9dc6e..56395ae8 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -70,7 +70,7 @@ class CommandLoader(LoadableObject): self.notify(process.stderr.readline(), bad=True) sleep(0.02) yield - if self.end_hook(process): + if self.end_hook: self.end_hook(process) def destroy(self): -- cgit 1.4.1-2-gfad0 From 35b72f662d50dab160dbb32705f32d34762765a1 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 15 May 2010 20:22:32 +0200 Subject: core.loader: finished CommandLoader --- ranger/core/loader.py | 34 ++++++++++++++++++++++++++++------ ranger/fsobject/directory.py | 3 ++- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/ranger/core/loader.py b/ranger/core/loader.py index 56395ae8..ad6864e2 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -31,11 +31,11 @@ def status_generator(): yield '|' -class LoadableObject(object): +class Loadable(object): + paused = False def __init__(self, gen, descr): self.load_generator = gen self.description = descr - self.paused = False def get_description(self): return self.description @@ -44,15 +44,19 @@ class LoadableObject(object): self.paused = True def unpause(self): - self.paused = False + try: + del self.paused + except: + pass def destroy(self): pass -class CommandLoader(LoadableObject): +class CommandLoader(Loadable, FileManagerAware): + finished = False def __init__(self, args, descr, begin_hook=None, end_hook=None): - LoadableObject.__init__(self, self.generate(), descr) + Loadable.__init__(self, self.generate(), descr) self.args = args self.begin_hook = begin_hook self.end_hook = end_hook @@ -67,12 +71,24 @@ class CommandLoader(LoadableObject): rd, _, __ = select.select( [process.stderr], [], [], 0.05) if rd: - self.notify(process.stderr.readline(), bad=True) + error = process.stderr.readline().decode('utf-8') + self.fm.notify(error, bad=True) sleep(0.02) yield + self.finished = True if self.end_hook: self.end_hook(process) + def pause(self): + if not self.finished and not self.paused: + self.process.send_signal(20) + Loadable.pause(self) + + def unpause(self): + if not self.finished and self.paused: + self.process.send_signal(18) + Loadable.unpause(self) + def destroy(self): if self.process: self.process.kill() @@ -113,6 +129,8 @@ class Loader(FileManagerAware): if to == 0: self.queue.appendleft(item) + if _from != 0: + self.queue[1].pause() elif to == -1: self.queue.append(item) else: @@ -132,6 +150,7 @@ class Loader(FileManagerAware): item = self.queue[index] if hasattr(item, 'unload'): item.unload() + item.destroy() del self.queue[index] def work(self): @@ -152,7 +171,10 @@ class Loader(FileManagerAware): self.rotate() if item != self.old_item: + if self.old_item: + self.old_item.pause() self.old_item = item + item.unpause() end_time = time() + self.seconds_of_work_time diff --git a/ranger/fsobject/directory.py b/ranger/fsobject/directory.py index 930ecb70..bab650c6 100644 --- a/ranger/fsobject/directory.py +++ b/ranger/fsobject/directory.py @@ -20,6 +20,7 @@ from os.path import join, isdir, basename from collections import deque from time import time +from ranger.core.loader import Loadable from ranger.ext.mount_path import mount_path from ranger.fsobject import BAD_INFO, File, FileSystemObject from ranger.shared import SettingsAware @@ -50,7 +51,7 @@ def accept_file(fname, hidden_filter, name_filter): return False return True -class Directory(FileSystemObject, Accumulator, SettingsAware): +class Directory(FileSystemObject, Accumulator, Loadable, SettingsAware): is_directory = True enterable = False load_generator = None -- cgit 1.4.1-2-gfad0 From 572e74f28c63a2dce6268e0be3ec3df10ac58312 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 15 May 2010 23:37:16 +0200 Subject: core.actions: improved paste() --- ranger/core/actions.py | 11 +++++------ ranger/core/loader.py | 7 +++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index c61ef338..16978591 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -569,10 +569,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): cwd.load_content() original_path = self.env.cwd.path - try: - one_file = copied_files[0] - except: - one_file = None + one_file = copied_files[0] if self.env.cut: self.env.copy.clear() @@ -582,7 +579,8 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): else: descr = "moving files from: " + one_file.dirname obj = CommandLoader(args=['mv', '--backup=existing', - '-t', self.env.cwd.path] + [f.path for f in copied_files], + '--suffix=_', '-ft', self.env.cwd.path] + \ + [f.path for f in copied_files], descr=descr, end_hook=refresh) else: if len(copied_files) == 1: @@ -590,7 +588,8 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): else: descr = "copying files from: " + one_file.dirname obj = CommandLoader(args=['cp', '--backup=existing', '--archive', - '-t', self.env.cwd.path] + [f.path for f in self.env.copy], + '--suffix=_', '-frt', self.env.cwd.path] + \ + [f.path for f in self.env.copy], descr=descr, end_hook=refresh) self.loader.add(obj) diff --git a/ranger/core/loader.py b/ranger/core/loader.py index ad6864e2..e5a136d0 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -54,6 +54,13 @@ class Loadable(object): class CommandLoader(Loadable, FileManagerAware): + """ + Run an external command with the loader. + + Output from stderr will be reported. Ensure that the process doesn't + ever ask for input, otherwise the loader will be blocked until this + object is removed from the queue (type ^C in ranger) + """ finished = False def __init__(self, args, descr, begin_hook=None, end_hook=None): Loadable.__init__(self, self.generate(), descr) -- cgit 1.4.1-2-gfad0 From 37a60686b340f030a2fc37e7ac9d19a701de9e6b Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 16 May 2010 00:54:22 +0200 Subject: core.loader: catch "select.error"s --- ranger/core/loader.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ranger/core/loader.py b/ranger/core/loader.py index e5a136d0..e33c2e9e 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -75,11 +75,14 @@ class CommandLoader(Loadable, FileManagerAware): if self.begin_hook: self.begin_hook(process) while process.poll() is None: - rd, _, __ = select.select( - [process.stderr], [], [], 0.05) - if rd: - error = process.stderr.readline().decode('utf-8') - self.fm.notify(error, bad=True) + try: + rd, _, __ = select.select( + [process.stderr], [], [], 0.05) + if rd: + error = process.stderr.readline().decode('utf-8') + self.fm.notify(error, bad=True) + except select.error: + pass sleep(0.02) yield self.finished = True -- cgit 1.4.1-2-gfad0 From 7a0f5d2f25a807e80a691c1346043a993d9d2214 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 3 Jun 2010 21:15:18 +0200 Subject: added gui/ansi.py from David Barnetts branch --- ranger/gui/ansi.py | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/tc_ansi.py | 45 +++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 ranger/gui/ansi.py create mode 100644 test/tc_ansi.py diff --git a/ranger/gui/ansi.py b/ranger/gui/ansi.py new file mode 100644 index 00000000..7d5f2623 --- /dev/null +++ b/ranger/gui/ansi.py @@ -0,0 +1,66 @@ +# Copyright (C) 2010 David Barnett +# +# 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 re + +ansi_re = re.compile('(\x1b' + r'\[\d+(?:;\d+)*?m)') + +def split_ansi_from_text(ansi_text): + return ansi_re.split(ansi_text) + +def text_with_fg_bg(ansi_text): + for chunk in split_ansi_from_text(ansi_text): + if chunk.startswith('\x1b'): + attr_text = re.match('\x1b' + r'\[(.*?)m', chunk).group(1) + fg, bg = -1, -1 + for attr in attr_text.split(';'): + m = re.match('^3(\d)$', attr) + if m: + fg = int(m.group(1)) + m = re.match('^4(\d)$', attr) + if m: + bg = int(m.group(1)) + yield (fg, bg) + else: + yield chunk + +def char_len(ansi_text): + return len(ansi_re.sub('', ansi_text)) + +def char_slice(ansi_text, start, end): + slice_chunks = [] + # skip to start + last_color = None + skip_len_left = start + len_left = end - start + for chunk in split_ansi_from_text(ansi_text): + m = ansi_re.match(chunk) + if m: + last_color = chunk + else: + if skip_len_left > len(chunk): + skip_len_left -= len(chunk) + else: # finished skipping to start + if skip_len_left > 0: + chunk = chunk[skip_len_left:] + chunk_left = chunk[:len_left] + if len(chunk_left): + if last_color is not None: + slice_chunks.append(last_color) + slice_chunks.append(chunk_left) + len_left -= len(chunk_left) + if len_left == 0: + break + return ''.join(slice_chunks) diff --git a/test/tc_ansi.py b/test/tc_ansi.py new file mode 100644 index 00000000..5fc4e53b --- /dev/null +++ b/test/tc_ansi.py @@ -0,0 +1,45 @@ +# Copyright (C) 2010 David Barnett +# +# 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 . + +if __name__ == '__main__': from __init__ import init; init() + +import unittest +from ranger.gui import ansi + +class TestDisplayable(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + def test_char_len(self): + ansi_string = "X" + self.assertEqual(ansi.char_len(ansi_string), 1) + + def test_char_len2(self): + ansi_string = "XY" + self.assertEqual(ansi.char_len(ansi_string), 2) + + def test_char_len3(self): + ansi_string = "XY" + self.assertEqual(ansi.char_len(ansi_string), 2) + + def test_char_slice(self): + ansi_string = "XY" + self.assertEqual(ansi.char_slice(ansi_string, 0, 1), "X") + +if __name__ == '__main__': + unittest.main() -- cgit 1.4.1-2-gfad0 From cfa5ab76e62d73af76f131cb82938faa069e332e Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 3 Jun 2010 22:06:42 +0200 Subject: gui.ansi: some modifications/completions --- ranger/gui/ansi.py | 47 ++++++++++++++++++++++++++++++++++------------- test/tc_ansi.py | 9 ++------- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/ranger/gui/ansi.py b/ranger/gui/ansi.py index 7d5f2623..fe0753a1 100644 --- a/ranger/gui/ansi.py +++ b/ranger/gui/ansi.py @@ -1,4 +1,5 @@ # Copyright (C) 2010 David Barnett +# Copyright (C) 2010 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 @@ -13,26 +14,45 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from ranger.gui import color import re -ansi_re = re.compile('(\x1b' + r'\[\d+(?:;\d+)*?m)') +ansi_re = re.compile('(\033' + r'\[\d+(?:;\d+)*?[a-zA-Z])') def split_ansi_from_text(ansi_text): return ansi_re.split(ansi_text) def text_with_fg_bg(ansi_text): for chunk in split_ansi_from_text(ansi_text): - if chunk.startswith('\x1b'): - attr_text = re.match('\x1b' + r'\[(.*?)m', chunk).group(1) - fg, bg = -1, -1 - for attr in attr_text.split(';'): - m = re.match('^3(\d)$', attr) - if m: - fg = int(m.group(1)) - m = re.match('^4(\d)$', attr) - if m: - bg = int(m.group(1)) - yield (fg, bg) + if chunk[0] == '\033': + if chunk[-1] != 'm': + continue + match = re.match(r'^.\[(.*).$', chunk) + attr_args = match.group(1) + fg, bg, attr = -1, -1, 0 + + # Convert arguments to attributes/colors + for arg in attr_args.split(';'): + n = int(arg) + if n == 0: + fg, bg, attr = 0, 0, 0 + elif n == 1: + attr |= color.bold + elif n == 4: + attr |= color.underline + elif n == 7: + attr |= color.reverse + elif n == 8: + attr |= color.invisible + elif n >= 30 and n <= 37: + fg = n - 30 + elif n == 39: + fg = -1 + elif n >= 40 and n <= 47: + bg = n - 40 + elif n == 49: + bg = -1 + yield (fg, bg, attr) else: yield chunk @@ -48,7 +68,8 @@ def char_slice(ansi_text, start, end): for chunk in split_ansi_from_text(ansi_text): m = ansi_re.match(chunk) if m: - last_color = chunk + if chunk[-1] == 'm': + last_color = chunk else: if skip_len_left > len(chunk): skip_len_left -= len(chunk) diff --git a/test/tc_ansi.py b/test/tc_ansi.py index 5fc4e53b..0a6ad8b1 100644 --- a/test/tc_ansi.py +++ b/test/tc_ansi.py @@ -19,12 +19,6 @@ import unittest from ranger.gui import ansi class TestDisplayable(unittest.TestCase): - def setUp(self): - pass - - def tearDown(self): - pass - def test_char_len(self): ansi_string = "X" self.assertEqual(ansi.char_len(ansi_string), 1) @@ -39,7 +33,8 @@ class TestDisplayable(unittest.TestCase): def test_char_slice(self): ansi_string = "XY" - self.assertEqual(ansi.char_slice(ansi_string, 0, 1), "X") + expected = "X" + self.assertEqual(ansi.char_slice(ansi_string, 0, 1), expected) if __name__ == '__main__': unittest.main() -- cgit 1.4.1-2-gfad0 From 43e0f44a4788f3251d5f2ad7c1bfeff014353aba Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 3 Jun 2010 23:01:49 +0200 Subject: added more code from David Barnett, previews work --- ranger/core/actions.py | 9 ++------- ranger/ext/preview.sh | 21 +++++++++++++++++++++ ranger/fsobject/file.py | 21 +++++++++++++++------ ranger/gui/ansi.py | 17 ++++++++++++----- ranger/gui/curses_shortcuts.py | 9 +++++++++ ranger/gui/widgets/pager.py | 20 +++++++++++++++++--- 6 files changed, 76 insertions(+), 21 deletions(-) create mode 100755 ranger/ext/preview.sh diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 14f862c7..2db749cd 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -468,13 +468,8 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): if not hasattr(self.ui, 'open_embedded_pager'): return - try: - f = open(self.env.cf.path, 'r') - except: - pass - else: - pager = self.ui.open_embedded_pager() - pager.set_source(f) + pager = self.ui.open_embedded_pager() + pager.set_source(self.env.cf.get_preview_source()) # -------------------------- # -- Tabs diff --git a/ranger/ext/preview.sh b/ranger/ext/preview.sh new file mode 100755 index 00000000..80186e4d --- /dev/null +++ b/ranger/ext/preview.sh @@ -0,0 +1,21 @@ +#!/bin/bash +mimetype=$(file --mime-type -Lb "$1") +basetype=$(echo "$mimetype" | grep -o '^[^/]\+') +extension=$(echo "$1" | grep '\.' | grep -o '[^.]\+$') + +case "$basetype" in + text) + highlight --ansi "$1" || cat "$1" + exit 0;; + image) + img2txt "$1" || exit 1 + exit 0;; +esac + +case "$extension" in + zip|gz) + atool -l "$1" + exit 0;; +esac + +exit 1 diff --git a/ranger/fsobject/file.py b/ranger/fsobject/file.py index 2619fa35..25902f57 100644 --- a/ranger/fsobject/file.py +++ b/ranger/fsobject/file.py @@ -16,6 +16,9 @@ import re import zipfile from ranger.fsobject import FileSystemObject +from subprocess import Popen, PIPE +from ranger.core.runner import devnull +from ranger import relpath N_FIRST_BYTES = 20 control_characters = set(chr(n) for n in @@ -28,8 +31,8 @@ PREVIEW_BLACKLIST = re.compile(r""" # one character extensions: [oa] # media formats: - | avi | [mj]pe?g | mp\d | og[gmv] | wm[av] | mkv | flv - | png | bmp | vob | wav | mpc | flac | divx? | xcf | pdf + | avi | mpe?g | mp\d | og[gmv] | wm[av] | mkv | flv + | vob | wav | mpc | flac | divx? | xcf | pdf # binary files: | torrent | class | so | img | py[co] | dmg # containers: @@ -78,15 +81,21 @@ class File(FileSystemObject): return False if not self.accessible or self.is_fifo or self.is_device: return False + if self.image or self.container: + return True if PREVIEW_WHITELIST.search(self.basename): return True if PREVIEW_BLACKLIST.search(self.basename): return False - if self.extension not in ('zip',) and self.is_binary(): + if self.is_binary(): return False return True def get_preview_source(self): - if self.extension == 'zip': - return '\n'.join(zipfile.ZipFile(self.path).namelist()) - return open(self.path, 'r') + try: + p = Popen([relpath('ext/preview.sh'), self.path], + stdout=PIPE, stderr=devnull) + if not p.poll(): + return p.stdout + except: + return open(self.path, 'r') diff --git a/ranger/gui/ansi.py b/ranger/gui/ansi.py index fe0753a1..a9b37665 100644 --- a/ranger/gui/ansi.py +++ b/ranger/gui/ansi.py @@ -17,14 +17,15 @@ from ranger.gui import color import re -ansi_re = re.compile('(\033' + r'\[\d+(?:;\d+)*?[a-zA-Z])') +ansi_re = re.compile('(\033' + r'\[\d*(?:;\d+)*?[a-zA-Z])') +reset = '\033[0m' def split_ansi_from_text(ansi_text): return ansi_re.split(ansi_text) -def text_with_fg_bg(ansi_text): +def text_with_fg_bg_attr(ansi_text): for chunk in split_ansi_from_text(ansi_text): - if chunk[0] == '\033': + if chunk and chunk[0] == '\033': if chunk[-1] != 'm': continue match = re.match(r'^.\[(.*).$', chunk) @@ -33,9 +34,15 @@ def text_with_fg_bg(ansi_text): # Convert arguments to attributes/colors for arg in attr_args.split(';'): - n = int(arg) + try: + n = int(arg) + except: + if arg == '': + n = 0 + else: + continue if n == 0: - fg, bg, attr = 0, 0, 0 + fg, bg, attr = -1, -1, 0 elif n == 1: attr |= color.bold elif n == 4: diff --git a/ranger/gui/curses_shortcuts.py b/ranger/gui/curses_shortcuts.py index 3df45700..42f9dada 100644 --- a/ranger/gui/curses_shortcuts.py +++ b/ranger/gui/curses_shortcuts.py @@ -1,4 +1,5 @@ # Copyright (C) 2009, 2010 Roman Zimbelmann +# Copyright (C) 2010 David Barnett # # 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 @@ -13,9 +14,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import curses import _curses from ranger.ext.iter_tools import flatten +from ranger.gui.color import get_color from ranger.shared import SettingsAware def ascii_only(string): @@ -95,6 +98,12 @@ class CursesShortcuts(SettingsAware): except _curses.error: pass + def set_fg_bg_attr(self, fg, bg, attr): + try: + self.win.attrset(curses.color_pair(get_color(fg, bg)) | attr) + except _curses.error: + pass + def color_reset(self): """Change the colors to the default colors""" CursesShortcuts.color(self, 'reset') diff --git a/ranger/gui/widgets/pager.py b/ranger/gui/widgets/pager.py index c0bc98b4..58fcdfd1 100644 --- a/ranger/gui/widgets/pager.py +++ b/ranger/gui/widgets/pager.py @@ -1,4 +1,5 @@ # Copyright (C) 2009, 2010 Roman Zimbelmann +# Copyright (C) 2010 David Barnett # # 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 @@ -18,6 +19,7 @@ The pager displays text and allows you to scroll inside it. """ import re from . import Widget +from ranger.gui import ansi from ranger.ext.direction import Direction from ranger.container.keymap import CommandArgs @@ -106,6 +108,13 @@ class Pager(Widget): if TITLE_REGEXP.match(line): self.color_at(i, 0, -1, 'title', *baseclr) + elif self.markup == 'ansi': + self.win.move(i, 0) + for chunk in ansi.text_with_fg_bg_attr(line): + if isinstance(chunk, tuple): + self.set_fg_bg_attr(*chunk) + else: + self.addstr(chunk) def move(self, narg=None, **kw): direction = Direction(kw) @@ -158,12 +167,13 @@ class Pager(Widget): if isinstance(source, str): self.source_is_stream = False - self.lines = source.split('\n') + self.lines = source.splitlines() elif hasattr(source, '__getitem__'): self.source_is_stream = False self.lines = source elif hasattr(source, 'readline'): self.source_is_stream = True + self.markup = 'ansi' self.lines = [] else: self.source = None @@ -206,8 +216,12 @@ class Pager(Widget): while True: try: line = self._get_line(i).expandtabs(4) - line = line[startx:self.wid + startx].rstrip() - yield line + if self.markup is 'ansi': + line = ansi.char_slice(line, startx, self.wid + startx) \ + + ansi.reset + else: + line = line[startx:self.wid + startx] + yield line.rstrip() except IndexError: raise StopIteration i += 1 -- cgit 1.4.1-2-gfad0 From dd7f4e5c27fc04cb2abb93e659c3ec1c4411a662 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 4 Jun 2010 16:52:30 +0200 Subject: added preview_script option --- ranger/api/options.py | 1 + ranger/defaults/options.py | 3 +++ ranger/fsobject/file.py | 15 +++++++++------ ranger/shared/settings.py | 1 + 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/ranger/api/options.py b/ranger/api/options.py index ee947b39..aee6150b 100644 --- a/ranger/api/options.py +++ b/ranger/api/options.py @@ -17,3 +17,4 @@ import re from re import compile as regexp from ranger.api import * from ranger.gui import color +from ranger import relpath, relpath_conf diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index 23752d97..feb02a58 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -39,6 +39,9 @@ hidden_filter = regexp( r'^\.|\.(?:pyc|pyo|bak|swp)$|~$|lost\+found') show_hidden = False +# Which script is used to generate file previews? +preview_script = relpath('ext/preview.sh') # relative to rangers path + # Show dotfiles in the bookmark preview box? show_hidden_bookmarks = True diff --git a/ranger/fsobject/file.py b/ranger/fsobject/file.py index 25902f57..cd5b37ff 100644 --- a/ranger/fsobject/file.py +++ b/ranger/fsobject/file.py @@ -92,10 +92,13 @@ class File(FileSystemObject): return True def get_preview_source(self): - try: - p = Popen([relpath('ext/preview.sh'), self.path], - stdout=PIPE, stderr=devnull) - if not p.poll(): + if self.fm.settings.preview_script: + try: + p = Popen([self.fm.settings.preview_script, self.path], + stdout=PIPE, stderr=devnull) + if p.poll(): # nonzero exit code + return None return p.stdout - except: - return open(self.path, 'r') + except: + pass + return open(self.path, 'r') diff --git a/ranger/shared/settings.py b/ranger/shared/settings.py index f5d8614f..24aea39c 100644 --- a/ranger/shared/settings.py +++ b/ranger/shared/settings.py @@ -41,6 +41,7 @@ ALLOWED_SETTINGS = { 'tilde_in_titlebar': bool, 'max_history_size': (int, type(None)), 'max_console_history_size': (int, type(None)), + 'preview_script': (str, type(None)), 'scroll_offset': int, 'preview_files': bool, 'preview_directories': bool, -- cgit 1.4.1-2-gfad0 From 1d895690f81f2cb3a308416c29b280e91e681e36 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 8 Jun 2010 00:40:43 +0200 Subject: preview: renamed script to "scripts/scope.sh" --- ranger/defaults/options.py | 2 +- ranger/ext/preview.sh | 21 --------------------- ranger/scripts/scope.sh | 22 ++++++++++++++++++++++ 3 files changed, 23 insertions(+), 22 deletions(-) delete mode 100755 ranger/ext/preview.sh create mode 100644 ranger/scripts/scope.sh diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index feb02a58..4ff774c6 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -40,7 +40,7 @@ hidden_filter = regexp( show_hidden = False # Which script is used to generate file previews? -preview_script = relpath('ext/preview.sh') # relative to rangers path +preview_script = relpath('scripts/scope.sh') # relative to rangers path # Show dotfiles in the bookmark preview box? show_hidden_bookmarks = True diff --git a/ranger/ext/preview.sh b/ranger/ext/preview.sh deleted file mode 100755 index 80186e4d..00000000 --- a/ranger/ext/preview.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -mimetype=$(file --mime-type -Lb "$1") -basetype=$(echo "$mimetype" | grep -o '^[^/]\+') -extension=$(echo "$1" | grep '\.' | grep -o '[^.]\+$') - -case "$basetype" in - text) - highlight --ansi "$1" || cat "$1" - exit 0;; - image) - img2txt "$1" || exit 1 - exit 0;; -esac - -case "$extension" in - zip|gz) - atool -l "$1" - exit 0;; -esac - -exit 1 diff --git a/ranger/scripts/scope.sh b/ranger/scripts/scope.sh new file mode 100644 index 00000000..998476dc --- /dev/null +++ b/ranger/scripts/scope.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# This script is responsible to generate the previews for ranger. +mimetype=$(file --mime-type -Lb "$1") +basetype=$(echo "$mimetype" | grep -o '^[^/]\+') +extension=$(echo "$1" | grep '\.' | grep -o '[^.]\+$') + +case "$basetype" in + text) + highlight --ansi "$1" || cat "$1" + exit 0;; + image) + img2txt "$1" || exit 1 + exit 0;; +esac + +case "$extension" in + zip|gz) + atool -l "$1" + exit 0;; +esac + +exit 1 -- cgit 1.4.1-2-gfad0 From 9a41e0fc3695c2c7f9b07a93c48f72ee9838c698 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 12 Jun 2010 02:33:24 +0200 Subject: scripts/scope: make it executable --- ranger/scripts/scope.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 ranger/scripts/scope.sh diff --git a/ranger/scripts/scope.sh b/ranger/scripts/scope.sh old mode 100644 new mode 100755 -- cgit 1.4.1-2-gfad0 From 6613bf4181f3d6d4b612e98e865192b73ebbe90c Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 15 Jun 2010 11:32:02 +0200 Subject: widgets.pager: more efficient width calculation --- ranger/gui/widgets/pager.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ranger/gui/widgets/pager.py b/ranger/gui/widgets/pager.py index 58fcdfd1..5e761a0f 100644 --- a/ranger/gui/widgets/pager.py +++ b/ranger/gui/widgets/pager.py @@ -35,6 +35,7 @@ class Pager(Widget): old_source = None old_scroll_begin = 0 old_startx = 0 + max_width = None def __init__(self, win, embedded=False): Widget.__init__(self, win) self.embedded = embedded @@ -122,7 +123,7 @@ class Pager(Widget): self.startx = direction.move( direction=direction.right(), override=narg, - maximum=self._get_max_width(), + maximum=self.max_width, current=self.startx, pagesize=self.wid, offset=-self.wid + 1) @@ -201,6 +202,8 @@ class Pager(Widget): if attempt_to_read and self.source_is_stream: try: for l in self.source: + if len(l) > self.max_width: + self.max_width = len(l) self.lines.append(l) if len(self.lines) > n: break @@ -225,6 +228,3 @@ class Pager(Widget): except IndexError: raise StopIteration i += 1 - - def _get_max_width(self): - return max(len(line) for line in self.lines) -- cgit 1.4.1-2-gfad0 From d5ba00761ed55e9f5c18b71c7eae546eb4056844 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 22 Jun 2010 00:55:38 +0200 Subject: move scripts/scope.sh to data/scope.sh --- ranger/data/scope.sh | 22 ++++++++++++++++++++++ ranger/defaults/options.py | 2 +- ranger/scripts/scope.sh | 22 ---------------------- 3 files changed, 23 insertions(+), 23 deletions(-) create mode 100755 ranger/data/scope.sh delete mode 100755 ranger/scripts/scope.sh diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh new file mode 100755 index 00000000..998476dc --- /dev/null +++ b/ranger/data/scope.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# This script is responsible to generate the previews for ranger. +mimetype=$(file --mime-type -Lb "$1") +basetype=$(echo "$mimetype" | grep -o '^[^/]\+') +extension=$(echo "$1" | grep '\.' | grep -o '[^.]\+$') + +case "$basetype" in + text) + highlight --ansi "$1" || cat "$1" + exit 0;; + image) + img2txt "$1" || exit 1 + exit 0;; +esac + +case "$extension" in + zip|gz) + atool -l "$1" + exit 0;; +esac + +exit 1 diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index 4ff774c6..6e840e33 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -40,7 +40,7 @@ hidden_filter = regexp( show_hidden = False # Which script is used to generate file previews? -preview_script = relpath('scripts/scope.sh') # relative to rangers path +preview_script = relpath('data/scope.sh') # relative to rangers path # Show dotfiles in the bookmark preview box? show_hidden_bookmarks = True diff --git a/ranger/scripts/scope.sh b/ranger/scripts/scope.sh deleted file mode 100755 index 998476dc..00000000 --- a/ranger/scripts/scope.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -# This script is responsible to generate the previews for ranger. -mimetype=$(file --mime-type -Lb "$1") -basetype=$(echo "$mimetype" | grep -o '^[^/]\+') -extension=$(echo "$1" | grep '\.' | grep -o '[^.]\+$') - -case "$basetype" in - text) - highlight --ansi "$1" || cat "$1" - exit 0;; - image) - img2txt "$1" || exit 1 - exit 0;; -esac - -case "$extension" in - zip|gz) - atool -l "$1" - exit 0;; -esac - -exit 1 -- cgit 1.4.1-2-gfad0 From 48a13dcdb31313d74e71028a32e19041f3a7f76c Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 22 Jun 2010 01:42:53 +0200 Subject: main: copy scope.sh to ~/.ranger on first start --- ranger/__main__.py | 12 +++++++++++- ranger/defaults/options.py | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/ranger/__main__.py b/ranger/__main__.py index ac6b2362..048e00b9 100644 --- a/ranger/__main__.py +++ b/ranger/__main__.py @@ -22,7 +22,7 @@ # convenient exception handling in ranger.py (ImportError) import locale -import os +import os.path import sys def parse_arguments(): @@ -60,6 +60,13 @@ def parse_arguments(): return arg +def copy_config_files(): + import shutil + from ranger import relpath, relpath_conf + if not os.path.exists(relpath_conf('scope.sh')): + shutil.copy(relpath('data', 'scope.sh'), relpath_conf('scope.sh')) + + def allow_access_to_confdir(confdir, allow): if allow: try: @@ -200,6 +207,9 @@ def main(): else: path = '.' + if not ranger.arg.clean: + copy_config_files() + crash_traceback = None try: # Initialize objects diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index 6e840e33..63b1e1a7 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -40,7 +40,7 @@ hidden_filter = regexp( show_hidden = False # Which script is used to generate file previews? -preview_script = relpath('data/scope.sh') # relative to rangers path +preview_script = relpath_conf('scope.sh') # relative to config directory # Show dotfiles in the bookmark preview box? show_hidden_bookmarks = True -- cgit 1.4.1-2-gfad0 From 356726c41d5f7a95fdf60ed867f05d2d10bff70a Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 22 Jun 2010 01:44:48 +0200 Subject: data/scope.sh: improved, commented --- ranger/data/scope.sh | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh index 998476dc..ad14f5e5 100755 --- a/ranger/data/scope.sh +++ b/ranger/data/scope.sh @@ -1,21 +1,30 @@ #!/bin/bash -# This script is responsible to generate the previews for ranger. + +# This script is called whenever you preview a file. +# Its output is used as the preview. ANSI color codes are supported. + +# Meaning of exit codes: +# code | meaning | action of ranger +# -----+------------+------------------------------------------- +# 0 | success | display stdout as preview +# 1 | no preview | display no preview at all + + mimetype=$(file --mime-type -Lb "$1") -basetype=$(echo "$mimetype" | grep -o '^[^/]\+') extension=$(echo "$1" | grep '\.' | grep -o '[^.]\+$') -case "$basetype" in - text) - highlight --ansi "$1" || cat "$1" +case "$mimetype" in + text/*) + highlight --ansi "$1" || cat "$1" || exit 1 exit 0;; - image) + image/*) img2txt "$1" || exit 1 exit 0;; esac case "$extension" in zip|gz) - atool -l "$1" + atool -l "$1" || exit 1 exit 0;; esac -- cgit 1.4.1-2-gfad0 From 3531f1e49d890a75fec3ca6a2ddc773832b1b6a5 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 22 Jun 2010 02:16:17 +0200 Subject: ranger.__init__: added relpath_script --- ranger/__init__.py | 12 ++++++++++++ ranger/api/options.py | 2 +- ranger/defaults/options.py | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ranger/__init__.py b/ranger/__init__.py index 4fcc2ecf..54baa3c0 100644 --- a/ranger/__init__.py +++ b/ranger/__init__.py @@ -61,6 +61,18 @@ def relpath_conf(*paths): else: return os.path.join(arg.confdir, *paths) +def relpath_script(*paths): + """ + Returns the path relative to where scripts are stored. + + It's relpath('data', *paths) with the --clean flag and + relpath_conf(*paths) without --clean. + """ + if arg.clean: + return relpath('data', *paths) + else: + return relpath_conf(*paths) + def relpath(*paths): """returns the path relative to rangers library directory""" return os.path.join(RANGERDIR, *paths) diff --git a/ranger/api/options.py b/ranger/api/options.py index aee6150b..93da4df1 100644 --- a/ranger/api/options.py +++ b/ranger/api/options.py @@ -17,4 +17,4 @@ import re from re import compile as regexp from ranger.api import * from ranger.gui import color -from ranger import relpath, relpath_conf +from ranger import relpath, relpath_conf, relpath_script diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index 63b1e1a7..67009426 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -40,7 +40,7 @@ hidden_filter = regexp( show_hidden = False # Which script is used to generate file previews? -preview_script = relpath_conf('scope.sh') # relative to config directory +preview_script = relpath_script('scope.sh') # relative to config directory # Show dotfiles in the bookmark preview box? show_hidden_bookmarks = True -- cgit 1.4.1-2-gfad0 From 500cf2593322008c05afeed9117dcc5d35b6fa9b Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 22 Jun 2010 10:59:44 +0200 Subject: README: added dependencies for scope.sh --- README | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README b/README index 562dfbdf..7a293b97 100644 --- a/README +++ b/README @@ -50,6 +50,11 @@ Optional: * The "file" program * A pager ("less" by default) +For scope.sh: (enhanced file previews) +* img2txt (from caca-utils) for previewing images +* highlight for syntax highlighting of code +* atool for previews of archives + Getting Started --------------- -- cgit 1.4.1-2-gfad0 From 2e386864912dd028774ffcd997f5ea686e955d0b Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 22 Jun 2010 11:01:54 +0200 Subject: defaults.options: deactivate scope.sh by default --- ranger/defaults/options.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index 67009426..8f8ee830 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -40,7 +40,13 @@ hidden_filter = regexp( show_hidden = False # Which script is used to generate file previews? -preview_script = relpath_script('scope.sh') # relative to config directory +preview_script = None + +# Ranger ships with scope.sh, a script that calls external programs (see +# README for dependencies) to preview images, archives, etc. +# As of now, it's disabled by default because of poor performance. +# Uncomment this line to enable it: +#preview_script = relpath_script('scope.sh') # Show dotfiles in the bookmark preview box? show_hidden_bookmarks = True -- cgit 1.4.1-2-gfad0 From bf14a7e72e7c03673949388365744f26e227ec83 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 22 Jun 2010 11:27:21 +0200 Subject: data/scope.sh: added lynx --- README | 1 + ranger/data/scope.sh | 22 +++++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/README b/README index 7a293b97..d92543f7 100644 --- a/README +++ b/README @@ -54,6 +54,7 @@ For scope.sh: (enhanced file previews) * img2txt (from caca-utils) for previewing images * highlight for syntax highlighting of code * atool for previews of archives +* lynx or elinks for previews of html pages Getting Started diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh index ad14f5e5..ef5f8ff4 100755 --- a/ranger/data/scope.sh +++ b/ranger/data/scope.sh @@ -13,19 +13,27 @@ mimetype=$(file --mime-type -Lb "$1") extension=$(echo "$1" | grep '\.' | grep -o '[^.]\+$') +case "$extension" in + # Archive extensions: + tar|gz|tgz|bz|tbz|bz2|tbz2|Z|tZ|lzo|tzo|lz|tlz|xz|txz|7z|t7z|\ + zip|jar|war|rar|lha|lzh|alz|ace|a|arj|arc|rpm|cab|lzma|rz|cpio) + atool -l "$1" || exit 1 + exit 0;; + # HTML Pages: + htm|html|xhtml) + lynx -dump "$1" || elinks -dump "$1" || exit 1 + exit 0;; +esac + case "$mimetype" in - text/*) + # Syntax highlight for text files: + text/* | */xml) highlight --ansi "$1" || cat "$1" || exit 1 exit 0;; + # Ascii-previews of images: image/*) img2txt "$1" || exit 1 exit 0;; esac -case "$extension" in - zip|gz) - atool -l "$1" || exit 1 - exit 0;; -esac - exit 1 -- cgit 1.4.1-2-gfad0 From dc7ee19fc15eee33c948d0ccd6c06df9682c0bf3 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 22 Jun 2010 14:25:03 +0200 Subject: fsobject.file: dynamic width of caca previews, cleanups --- ranger/core/actions.py | 2 +- ranger/data/scope.sh | 16 +++++++++++++--- ranger/fsobject/file.py | 8 +++----- ranger/gui/widgets/browsercolumn.py | 2 +- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 743d82c5..44061aca 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -471,7 +471,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): return pager = self.ui.open_embedded_pager() - pager.set_source(self.env.cf.get_preview_source()) + pager.set_source(self.env.cf.get_preview_source(pager)) # -------------------------- # -- Tabs diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh index ef5f8ff4..80fd2ea7 100755 --- a/ranger/data/scope.sh +++ b/ranger/data/scope.sh @@ -3,12 +3,22 @@ # This script is called whenever you preview a file. # Its output is used as the preview. ANSI color codes are supported. -# Meaning of exit codes: +# NOTE: This is considered to be a configuration file. If you upgrade +# ranger, it will be left untouched. (You must update it yourself) + +# Meanings of arguments: +# name | meaning +# -----+-------------------------------------------------------- +# $1 | Full filename of the selected file +# $2 | Width of the preview pane (number of fitting characters) +# $3 | Height of the preview pane (number of fitting characters) + +# Meanings of exit codes: # code | meaning | action of ranger # -----+------------+------------------------------------------- # 0 | success | display stdout as preview # 1 | no preview | display no preview at all - +# 2 | plain text | display the plain content of the file mimetype=$(file --mime-type -Lb "$1") extension=$(echo "$1" | grep '\.' | grep -o '[^.]\+$') @@ -32,7 +42,7 @@ case "$mimetype" in exit 0;; # Ascii-previews of images: image/*) - img2txt "$1" || exit 1 + img2txt -W "$2" "$1" || exit 1 exit 0;; esac diff --git a/ranger/fsobject/file.py b/ranger/fsobject/file.py index 216b4754..4acad100 100644 --- a/ranger/fsobject/file.py +++ b/ranger/fsobject/file.py @@ -14,7 +14,6 @@ # along with this program. If not, see . import re -import zipfile from ranger.fsobject import FileSystemObject from subprocess import Popen, PIPE from ranger.core.runner import devnull @@ -35,8 +34,6 @@ PREVIEW_BLACKLIST = re.compile(r""" | vob | wav | mpc | flac | divx? | xcf | pdf # binary files: | torrent | class | so | img | py[co] | dmg - # containers: - | iso | rar | 7z | tar | gz | bz2 | tgz ) # ignore filetype-independent suffixes: (\.part|\.bak|~)? @@ -93,10 +90,11 @@ class File(FileSystemObject): return False return True - def get_preview_source(self): + def get_preview_source(self, widget): if self.fm.settings.preview_script: try: - p = Popen([self.fm.settings.preview_script, self.path], + p = Popen([self.fm.settings.preview_script, self.path, + str(widget.wid), str(widget.hei)], stdout=PIPE, stderr=devnull) if p.poll(): # nonzero exit code return None diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py index d617e64e..5585bccc 100644 --- a/ranger/gui/widgets/browsercolumn.py +++ b/ranger/gui/widgets/browsercolumn.py @@ -155,7 +155,7 @@ class BrowserColumn(Pager): return try: - f = self.target.get_preview_source() + f = self.target.get_preview_source(self) except: Pager.close(self) else: -- cgit 1.4.1-2-gfad0 From 2a8ba97dc9f883dc61a3ab083747c20c0385eb88 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 21 Sep 2010 23:30:14 +0200 Subject: foo --- ranger/__init__.py | 52 ++++++++++++++++++++++++++------------------- ranger/defaults/commands.py | 2 -- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/ranger/__init__.py b/ranger/__init__.py index 1f8cc324..87376500 100644 --- a/ranger/__init__.py +++ b/ranger/__init__.py @@ -15,55 +15,63 @@ """Ranger - file browser for the unix terminal""" -import os -import sys +from os import path, environ from ranger.ext.openstruct import OpenStruct +from sys import argv +# Information __license__ = 'GPL3' __version__ = '1.3.0' -__credits__ = 'Roman Zimbelmann' -__author__ = 'Roman Zimbelmann' -__maintainer__ = 'Roman Zimbelmann' +__author__ = __maintainer__ = 'Roman Zimbelmann' __email__ = 'romanz@lavabit.com' -__copyright__ = """ -Copyright (C) 2009, 2010 Roman Zimbelmann -""" - +# Constants USAGE = '%prog [options] [path/filename]' -if 'XDG_CONFIG_HOME' in os.environ and os.environ['XDG_CONFIG_HOME']: - DEFAULT_CONFDIR = os.environ['XDG_CONFIG_HOME'] + '/ranger' +RANGERDIR = path.dirname(__file__) +LOGFILE = '/tmp/errorlog' +if 'XDG_CONFIG_HOME' in environ and environ['XDG_CONFIG_HOME']: + DEFAULT_CONFDIR = environ['XDG_CONFIG_HOME'] + '/ranger' else: DEFAULT_CONFDIR = '~/.config/ranger' -RANGERDIR = os.path.dirname(__file__) -LOGFILE = '/tmp/errorlog' -arg = OpenStruct( - debug=False, clean=False, confdir=DEFAULT_CONFDIR, +DEBUG = ('-d' in argv or '--debug' in argv) and ('--' not in argv or + (('-d' in argv and argv.index('-d') < argv.index('--')) or + ('--debug' in argv and argv.index('--debug') < argv.index('--')))) + +# Get some valid arguments before actually parsing them in main() +arg = OpenStruct(debug=DEBUG, clean=False, confdir=DEFAULT_CONFDIR, mode=0, flags='', targets=[]) -#for python3-only versions, this could be replaced with: -#def log(*objects, start='ranger:', sep=' ', end='\n'): -# print(start, *objects, end=end, sep=sep, file=open(LOGFILE, 'a')) +# Debugging features. These will be activated when run with --debug. +# Example usage in the code: +# import ranger; ranger.log("hello world") def log(*objects, **keywords): """ Writes objects to a logfile (for the purpose of debugging only.) Has the same arguments as print() in python3. """ - if LOGFILE is None or arg.clean: - return + if not DEBUG: return start = 'start' in keywords and keywords['start'] or 'ranger:' sep = 'sep' in keywords and keywords['sep'] or ' ' _file = 'file' in keywords and keywords['file'] or open(LOGFILE, 'a') end = 'end' in keywords and keywords['end'] or '\n' _file.write(sep.join(map(str, (start, ) + objects)) + end) +def log_traceback(): + if not DEBUG: return + import traceback + traceback.print_stack(file=open(LOGFILE, 'a')) + +# Handy functions def relpath_conf(*paths): """returns the path relative to rangers configuration directory""" if arg.clean: assert 0, "Should not access relpath_conf in clean mode!" else: - return os.path.join(arg.confdir, *paths) + return path.join(arg.confdir, *paths) def relpath(*paths): """returns the path relative to rangers library directory""" - return os.path.join(RANGERDIR, *paths) + return path.join(RANGERDIR, *paths) + +# Clean up +del path, environ, OpenStruct, argv diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index d3c05023..b61d5a5a 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -308,8 +308,6 @@ class set(Command): def tab(self): line = parse(self.line) - from ranger import log - log(line.parse_setting_line()) name, value, name_done = line.parse_setting_line() settings = self.fm.settings if not name: -- cgit 1.4.1-2-gfad0 From d56375f95f91b9670a2353ee8d8883eca5c35f1e Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 7 Oct 2010 17:17:44 +0200 Subject: enable shope.sh by default --- ranger/defaults/options.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index 39046443..5ce617bc 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -40,13 +40,11 @@ hidden_filter = regexp( show_hidden = False # Which script is used to generate file previews? -preview_script = None +#preview_script = None # Ranger ships with scope.sh, a script that calls external programs (see # README for dependencies) to preview images, archives, etc. -# As of now, it's disabled by default because of poor performance. -# Uncomment this line to enable it: -#preview_script = relpath_script('scope.sh') +preview_script = relpath_script('scope.sh') # Show dotfiles in the bookmark preview box? show_hidden_bookmarks = True -- cgit 1.4.1-2-gfad0 From 1fb50effe36b4d051ecf2aa7891bd609d942f898 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 8 Oct 2010 19:48:58 +0200 Subject: core.loader: fix bugs after merge --- ranger/core/loader.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ranger/core/loader.py b/ranger/core/loader.py index 8272d0e0..42136011 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -17,7 +17,7 @@ from collections import deque from time import time, sleep from subprocess import Popen, PIPE from time import time -from ranger.shared import FileManagerAware +from ranger.core.shared import FileManagerAware import math import os import select @@ -105,7 +105,6 @@ class Loader(FileManagerAware): self.item = None self.load_generator = None self.throbber_status = 0 - self.status_generator = status_generator() self.rotate() self.old_item = None -- cgit 1.4.1-2-gfad0 From 085934209cdfe195b7621a6e722c5688da3d7c1c Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 8 Oct 2010 20:28:56 +0200 Subject: core.loader: use signals with CommandLoader --- ranger/core/actions.py | 7 +++---- ranger/core/loader.py | 17 ++++++++--------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 1dc51502..e290db58 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -695,8 +695,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): descr = "moving files from: " + one_file.dirname obj = CommandLoader(args=['mv', '--backup=existing', '--suffix=_', '-ft', self.env.cwd.path] + \ - [f.path for f in copied_files], - descr=descr, end_hook=refresh) + [f.path for f in copied_files], descr=descr) else: if len(copied_files) == 1: descr = "copying: " + one_file.path @@ -704,9 +703,9 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): descr = "copying files from: " + one_file.dirname obj = CommandLoader(args=['cp', '--backup=existing', '--archive', '--suffix=_', '-frt', self.env.cwd.path] + \ - [f.path for f in self.env.copy], - descr=descr, end_hook=refresh) + [f.path for f in self.env.copy], descr=descr) + obj.signal_bind('after', refresh) self.loader.add(obj) def delete(self): diff --git a/ranger/core/loader.py b/ranger/core/loader.py index 42136011..54d5a279 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -18,6 +18,7 @@ from time import time, sleep from subprocess import Popen, PIPE from time import time from ranger.core.shared import FileManagerAware +from ranger.ext.signal_dispatcher import SignalDispatcher import math import os import select @@ -45,7 +46,7 @@ class Loadable(object): pass -class CommandLoader(Loadable, FileManagerAware): +class CommandLoader(Loadable, SignalDispatcher, FileManagerAware): """ Run an external command with the loader. @@ -54,32 +55,30 @@ class CommandLoader(Loadable, FileManagerAware): object is removed from the queue (type ^C in ranger) """ finished = False - def __init__(self, args, descr, begin_hook=None, end_hook=None): + def __init__(self, args, descr): + SignalDispatcher.__init__(self) Loadable.__init__(self, self.generate(), descr) self.args = args - self.begin_hook = begin_hook - self.end_hook = end_hook def generate(self): self.process = process = Popen(self.args, stdout=open(os.devnull, 'w'), stderr=PIPE) - if self.begin_hook: - self.begin_hook(process) + self.signal_emit('before', process=process) while process.poll() is None: try: rd, _, __ = select.select( [process.stderr], [], [], 0.05) if rd: error = process.stderr.readline().decode('utf-8') - self.fm.notify(error, bad=True) + if error: + self.fm.notify(error, bad=True) except select.error: pass sleep(0.02) yield self.finished = True - if self.end_hook: - self.end_hook(process) + self.signal_emit('after', process=process) def pause(self): if not self.finished and not self.paused: -- cgit 1.4.1-2-gfad0 From d538bd40182a00d4b6e6327d091a9e0943ee2976 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 8 Oct 2010 21:14:42 +0200 Subject: fsobject.file: load previews in fm.loader --- ranger/core/loader.py | 3 +-- ranger/fsobject/file.py | 28 +++++++++++++++++++--------- ranger/gui/widgets/pager.py | 1 + 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/ranger/core/loader.py b/ranger/core/loader.py index 54d5a279..66f7017c 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -62,8 +62,7 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware): def generate(self): self.process = process = Popen(self.args, - stdout=open(os.devnull, 'w'), - stderr=PIPE) + stdout=PIPE, stderr=PIPE) self.signal_emit('before', process=process) while process.poll() is None: try: diff --git a/ranger/fsobject/file.py b/ranger/fsobject/file.py index 57d82b31..5458b8b1 100644 --- a/ranger/fsobject/file.py +++ b/ranger/fsobject/file.py @@ -17,6 +17,7 @@ import re from ranger.fsobject import FileSystemObject from subprocess import Popen, PIPE from ranger.core.runner import devnull +from ranger.core.loader import CommandLoader N_FIRST_BYTES = 20 control_characters = set(chr(n) for n in @@ -53,6 +54,8 @@ PREVIEW_WHITELIST = re.compile(r""" class File(FileSystemObject): is_file = True + preview_data = None + preview_known = False @property def firstbytes(self): @@ -91,15 +94,22 @@ class File(FileSystemObject): return False return True + def update_preview(self, signal): + self.preview_known = True + self.preview_data = None + if not signal.process.poll(): + self.preview_data = signal.process.stdout.read() + self.fm.ui.pager.need_redraw = True + self.fm.ui.redraw() + def get_preview_source(self, widget): if self.fm.settings.preview_script: - try: - p = Popen([self.fm.settings.preview_script, self.path, - str(widget.wid), str(widget.hei)], - stdout=PIPE, stderr=devnull) - if p.poll(): # nonzero exit code - return None - return p.stdout - except: - pass + if self.preview_known: + return self.preview_data + loadable = CommandLoader(args=[self.fm.settings.preview_script, + self.path, str(widget.wid), str(widget.hei)], + descr="Getting preview of %s" % self.path) + loadable.signal_bind('after', self.update_preview, weak=True) + self.fm.loader.add(loadable) + return "loading..." return open(self.path, 'r') diff --git a/ranger/gui/widgets/pager.py b/ranger/gui/widgets/pager.py index 5e761a0f..798c4697 100644 --- a/ranger/gui/widgets/pager.py +++ b/ranger/gui/widgets/pager.py @@ -168,6 +168,7 @@ class Pager(Widget): if isinstance(source, str): self.source_is_stream = False + self.markup = 'ansi' self.lines = source.splitlines() elif hasattr(source, '__getitem__'): self.source_is_stream = False -- cgit 1.4.1-2-gfad0 From c1f3125c53152b4eaa554a5e4d3a2ee2dbd6dfd8 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 8 Oct 2010 21:59:43 +0200 Subject: fsobject.file: reload pager when preview finished loading --- ranger/core/actions.py | 2 +- ranger/core/loader.py | 2 +- ranger/fsobject/file.py | 19 +++++++++++-------- ranger/gui/widgets/browsercolumn.py | 3 ++- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index e290db58..59eef2be 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -569,7 +569,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): return pager = self.ui.open_embedded_pager() - pager.set_source(self.env.cf.get_preview_source(pager)) + pager.set_source(self.env.cf.get_preview_source(pager.wid, pager.hei)) # -------------------------- # -- Tabs diff --git a/ranger/core/loader.py b/ranger/core/loader.py index 66f7017c..c03ecc29 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -74,7 +74,7 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware): self.fm.notify(error, bad=True) except select.error: pass - sleep(0.02) +# sleep(0.02) yield self.finished = True self.signal_emit('after', process=process) diff --git a/ranger/fsobject/file.py b/ranger/fsobject/file.py index 5458b8b1..2701537c 100644 --- a/ranger/fsobject/file.py +++ b/ranger/fsobject/file.py @@ -56,6 +56,7 @@ class File(FileSystemObject): is_file = True preview_data = None preview_known = False + preview_loading = False @property def firstbytes(self): @@ -94,22 +95,24 @@ class File(FileSystemObject): return False return True - def update_preview(self, signal): + def _update_preview(self, signal): self.preview_known = True self.preview_data = None if not signal.process.poll(): self.preview_data = signal.process.stdout.read() - self.fm.ui.pager.need_redraw = True - self.fm.ui.redraw() + if self.fm.env.cf.path == self.path: + self.fm.ui.browser.pager.need_redraw = True + self.fm.ui.browser.need_redraw = True - def get_preview_source(self, widget): + def get_preview_source(self, width, height): if self.fm.settings.preview_script: - if self.preview_known: + if self.preview_known or self.preview_loading: return self.preview_data + self.preview_loading = True loadable = CommandLoader(args=[self.fm.settings.preview_script, - self.path, str(widget.wid), str(widget.hei)], + self.path, str(width), str(height)], descr="Getting preview of %s" % self.path) - loadable.signal_bind('after', self.update_preview, weak=True) + loadable.signal_bind('after', self._update_preview, weak=True) self.fm.loader.add(loadable) - return "loading..." + return None return open(self.path, 'r') diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py index 63323f65..eb898e2a 100644 --- a/ranger/gui/widgets/browsercolumn.py +++ b/ranger/gui/widgets/browsercolumn.py @@ -156,8 +156,9 @@ class BrowserColumn(Pager): return try: - f = self.target.get_preview_source(self) + f = self.target.get_preview_source(self.wid, self.hei) except: + raise # XXX Pager.close(self) else: if f is None: -- cgit 1.4.1-2-gfad0 From 37ea434a29e7919e5aca9a13374a6d5d380d4eb5 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 8 Oct 2010 22:11:11 +0200 Subject: ranger.core.loader: allow suppressing errors in CommandLoader --- ranger/core/loader.py | 32 +++++++++++++++++++------------- ranger/fsobject/file.py | 2 +- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/ranger/core/loader.py b/ranger/core/loader.py index c03ecc29..1ea50f4d 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -55,27 +55,33 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware): object is removed from the queue (type ^C in ranger) """ finished = False - def __init__(self, args, descr): + def __init__(self, args, descr, silent=False): SignalDispatcher.__init__(self) Loadable.__init__(self, self.generate(), descr) self.args = args + self.silent = silent def generate(self): self.process = process = Popen(self.args, stdout=PIPE, stderr=PIPE) self.signal_emit('before', process=process) - while process.poll() is None: - try: - rd, _, __ = select.select( - [process.stderr], [], [], 0.05) - if rd: - error = process.stderr.readline().decode('utf-8') - if error: - self.fm.notify(error, bad=True) - except select.error: - pass -# sleep(0.02) - yield + if self.silent: + while process.poll() is None: + yield + sleep(0.02) + else: + while process.poll() is None: + yield + try: + rd, _, __ = select.select( + [process.stderr], [], [], 0.03) + if rd: + error = process.stderr.readline().decode('utf-8') + if error: + self.fm.notify(error, bad=True) + except select.error: + pass + sleep(0.01) self.finished = True self.signal_emit('after', process=process) diff --git a/ranger/fsobject/file.py b/ranger/fsobject/file.py index 2701537c..4076b136 100644 --- a/ranger/fsobject/file.py +++ b/ranger/fsobject/file.py @@ -111,7 +111,7 @@ class File(FileSystemObject): self.preview_loading = True loadable = CommandLoader(args=[self.fm.settings.preview_script, self.path, str(width), str(height)], - descr="Getting preview of %s" % self.path) + silent=True, descr="Getting preview of %s" % self.path) loadable.signal_bind('after', self._update_preview, weak=True) self.fm.loader.add(loadable) return None -- cgit 1.4.1-2-gfad0 From 91cfd75cf564605dc01826a7980087b4a4ee6871 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 8 Oct 2010 22:14:36 +0200 Subject: data/scope.sh: sort archive extensions --- ranger/data/scope.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh index 80fd2ea7..9ce35fd9 100755 --- a/ranger/data/scope.sh +++ b/ranger/data/scope.sh @@ -25,8 +25,8 @@ extension=$(echo "$1" | grep '\.' | grep -o '[^.]\+$') case "$extension" in # Archive extensions: - tar|gz|tgz|bz|tbz|bz2|tbz2|Z|tZ|lzo|tzo|lz|tlz|xz|txz|7z|t7z|\ - zip|jar|war|rar|lha|lzh|alz|ace|a|arj|arc|rpm|cab|lzma|rz|cpio) + 7z|a|ace|alz|arc|arj|bz|bz2|cab|cpio|gz|jar|lha|lz|lzh|lzma|lzo\ + |rar|rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xz|Z|zip) atool -l "$1" || exit 1 exit 0;; # HTML Pages: -- cgit 1.4.1-2-gfad0 From 6ccd4598961587723b7e277964f5f78ba3cb19bd Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 9 Oct 2010 16:05:51 +0200 Subject: core.loader: throw more signals --- ranger/core/loader.py | 9 ++++++--- ranger/fsobject/file.py | 10 ++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/ranger/core/loader.py b/ranger/core/loader.py index 1ea50f4d..5e652a28 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -55,6 +55,7 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware): object is removed from the queue (type ^C in ranger) """ finished = False + process = None def __init__(self, args, descr, silent=False): SignalDispatcher.__init__(self) Loadable.__init__(self, self.generate(), descr) @@ -68,7 +69,7 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware): if self.silent: while process.poll() is None: yield - sleep(0.02) + sleep(0.03) else: while process.poll() is None: yield @@ -80,8 +81,7 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware): if error: self.fm.notify(error, bad=True) except select.error: - pass - sleep(0.01) + sleep(0.03) self.finished = True self.signal_emit('after', process=process) @@ -89,13 +89,16 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware): if not self.finished and not self.paused: self.process.send_signal(20) Loadable.pause(self) + self.signal_emit('pause', process=self.process) def unpause(self): if not self.finished and self.paused: self.process.send_signal(18) Loadable.unpause(self) + self.signal_emit('unpause', process=self.process) def destroy(self): + self.signal_emit('destroy', process=self.process) if self.process: self.process.kill() diff --git a/ranger/fsobject/file.py b/ranger/fsobject/file.py index 4076b136..d7e667a2 100644 --- a/ranger/fsobject/file.py +++ b/ranger/fsobject/file.py @@ -95,7 +95,7 @@ class File(FileSystemObject): return False return True - def _update_preview(self, signal): + def _loader_after(self, signal): self.preview_known = True self.preview_data = None if not signal.process.poll(): @@ -104,6 +104,11 @@ class File(FileSystemObject): self.fm.ui.browser.pager.need_redraw = True self.fm.ui.browser.need_redraw = True + def _loader_destroy(self, signal): + self.preview_known = False + self.preview_loading = False + self.preview_data = None + def get_preview_source(self, width, height): if self.fm.settings.preview_script: if self.preview_known or self.preview_loading: @@ -112,7 +117,8 @@ class File(FileSystemObject): loadable = CommandLoader(args=[self.fm.settings.preview_script, self.path, str(width), str(height)], silent=True, descr="Getting preview of %s" % self.path) - loadable.signal_bind('after', self._update_preview, weak=True) + loadable.signal_bind('after', self._loader_after, weak=True) + loadable.signal_bind('destroy', self._loader_destroy, weak=True) self.fm.loader.add(loadable) return None return open(self.path, 'r') -- cgit 1.4.1-2-gfad0 From b76bb9a154d69abe7cbb2558faaddf1cc3c1397a Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 9 Oct 2010 21:15:52 +0200 Subject: core.actions: global index of previews (by path and size) --- ranger/core/actions.py | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ ranger/core/fm.py | 1 + ranger/fsobject/file.py | 27 +----------------------- 3 files changed, 58 insertions(+), 26 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 59eef2be..d0ccfe2b 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -51,6 +51,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): def reset(self): """Reset the filemanager, clearing the directory buffer""" old_path = self.env.cwd.path + self.previews = {} self.env.garbage_collect(-1) self.enter_dir(old_path) @@ -571,6 +572,61 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): pager = self.ui.open_embedded_pager() pager.set_source(self.env.cf.get_preview_source(pager.wid, pager.hei)) + # -------------------------- + # -- Previews + # -------------------------- + def get_preview(self, path, width, height): + if self.settings.preview_script: + # self.previews is a 2 dimensional dict: + # self.previews['/tmp/foo.jpg'][(80, 24)] = "the content..." + # self.previews['/tmp/foo.jpg']['loading'] = False + # A -1 in tuples means "any"; (80, -1) = wid. of 80 and any hei. + try: + data = self.previews[path] + except: + data = self.previews[path] = {'loading': False} + else: + if data['loading']: + return None + + found = data.get((-1, -1), data.get((width, -1), + data.get((-1, height), data.get((width, height), False)))) + if found == False: + data['loading'] = True + loadable = CommandLoader(args=[self.settings.preview_script, + path, str(width), str(height)], + silent=True, descr="Getting preview of %s" % path) + def on_after(signal): + exit = signal.process.poll() + content = signal.process.stdout.read() + if exit == 0: + data[(width, height)] = content + elif exit == 3: + data[(-1, height)] = content + elif exit == 4: + data[(width, -1)] = content + elif exit == 5: + data[(-1, -1)] = content + else: + data[(-1, -1)] = None # XXX + if self.env.cf.path == path: + self.ui.browser.pager.need_redraw = True + self.ui.browser.need_redraw = True + data['loading'] = False + def on_destroy(signal): + try: + del self.previews[path] + except: + pass + loadable.signal_bind('after', on_after) + loadable.signal_bind('destroy', on_destroy) + self.loader.add(loadable) + return None + else: + return found + else: + return open(path, 'r') + # -------------------------- # -- Tabs # -------------------------- diff --git a/ranger/core/fm.py b/ranger/core/fm.py index 84fea956..bfcebb92 100644 --- a/ranger/core/fm.py +++ b/ranger/core/fm.py @@ -50,6 +50,7 @@ class FM(Actions, SignalDispatcher): self.bookmarks = bookmarks self.tags = tags self.tabs = {} + self.previews = {} self.current_tab = 1 self.loader = Loader() diff --git a/ranger/fsobject/file.py b/ranger/fsobject/file.py index d7e667a2..33b4e3d3 100644 --- a/ranger/fsobject/file.py +++ b/ranger/fsobject/file.py @@ -95,30 +95,5 @@ class File(FileSystemObject): return False return True - def _loader_after(self, signal): - self.preview_known = True - self.preview_data = None - if not signal.process.poll(): - self.preview_data = signal.process.stdout.read() - if self.fm.env.cf.path == self.path: - self.fm.ui.browser.pager.need_redraw = True - self.fm.ui.browser.need_redraw = True - - def _loader_destroy(self, signal): - self.preview_known = False - self.preview_loading = False - self.preview_data = None - def get_preview_source(self, width, height): - if self.fm.settings.preview_script: - if self.preview_known or self.preview_loading: - return self.preview_data - self.preview_loading = True - loadable = CommandLoader(args=[self.fm.settings.preview_script, - self.path, str(width), str(height)], - silent=True, descr="Getting preview of %s" % self.path) - loadable.signal_bind('after', self._loader_after, weak=True) - loadable.signal_bind('destroy', self._loader_destroy, weak=True) - self.fm.loader.add(loadable) - return None - return open(self.path, 'r') + return self.fm.get_preview(self.realpath, width, height) -- cgit 1.4.1-2-gfad0 From fecebfe81e90d565f4af60c91d3ebc76f29d3c21 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 9 Oct 2010 22:57:49 +0200 Subject: data/scope.sh: improved --- ranger/data/scope.sh | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh index 9ce35fd9..c99f715b 100755 --- a/ranger/data/scope.sh +++ b/ranger/data/scope.sh @@ -3,15 +3,9 @@ # This script is called whenever you preview a file. # Its output is used as the preview. ANSI color codes are supported. -# NOTE: This is considered to be a configuration file. If you upgrade +# NOTES: This script is considered a configuration file. If you upgrade # ranger, it will be left untouched. (You must update it yourself) - -# Meanings of arguments: -# name | meaning -# -----+-------------------------------------------------------- -# $1 | Full filename of the selected file -# $2 | Width of the preview pane (number of fitting characters) -# $3 | Height of the preview pane (number of fitting characters) +# NEVER make this script interactive. (by starting mplayer or something) # Meanings of exit codes: # code | meaning | action of ranger @@ -19,31 +13,47 @@ # 0 | success | display stdout as preview # 1 | no preview | display no preview at all # 2 | plain text | display the plain content of the file +# 3 | fix width | success. Don't reload when width changes +# 4 | fix height | success. Don't reload when height changes +# 5 | fix both | success. Don't ever reload + +# 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) + +# Find out something about the file: +mimetype=$(file --mime-type -Lb "$path") +extension=$(echo "$path" | grep '\.' | grep -o '[^.]\+$') -mimetype=$(file --mime-type -Lb "$1") -extension=$(echo "$1" | grep '\.' | grep -o '[^.]\+$') +# Other useful stuff +maxln=200 # print up to $maxln lines +function have { type -P "$1" > /dev/null; } # test if program is installed case "$extension" in # Archive extensions: 7z|a|ace|alz|arc|arj|bz|bz2|cab|cpio|gz|jar|lha|lz|lzh|lzma|lzo\ |rar|rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xz|Z|zip) - atool -l "$1" || exit 1 - exit 0;; + atool -l "$path" | head -n $maxln && exit 3 + exit 1;; + pdf) + pdftotext "$path" | head -n $maxln && exit 3 + exit 1;; # HTML Pages: htm|html|xhtml) - lynx -dump "$1" || elinks -dump "$1" || exit 1 - exit 0;; + have lynx && lynx -dump "$path" | head -n $maxln && exit 5 + have elinks && elinks -dump "$path" | head -n $maxln && exit 5 + ;; # fall back to highlight/cat if theres no lynx/elinks esac case "$mimetype" in # Syntax highlight for text files: text/* | */xml) - highlight --ansi "$1" || cat "$1" || exit 1 - exit 0;; + (highlight --ansi "$path" || cat "$path") | head -n $maxln + exit 5;; # Ascii-previews of images: image/*) - img2txt -W "$2" "$1" || exit 1 - exit 0;; + img2txt --gamma=0.6 --width="$width" "$path" && exit 4 || exit 1;; esac exit 1 -- cgit 1.4.1-2-gfad0 From 796ca7d3b8fc22992d509ede49b12e60a100e54b Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 10 Oct 2010 01:21:55 +0200 Subject: core.shared: bugfix --- ranger/core/shared.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ranger/core/shared.py b/ranger/core/shared.py index 175395ec..1d0f96a0 100644 --- a/ranger/core/shared.py +++ b/ranger/core/shared.py @@ -55,8 +55,9 @@ class SettingsAware(Awareness): _colorscheme_name_to_class, priority=1) def postprocess_paths(signal): - import os - signal.value = os.path.expanduser(signal.value) + if isinstance(signal.value, str): + import os + signal.value = os.path.expanduser(signal.value) settings.signal_bind('setopt.preview_script', postprocess_paths, priority=1) -- cgit 1.4.1-2-gfad0 From ab3ecc707fa9f4243a02809c7de7a781dbf06b5a Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 10 Oct 2010 01:50:02 +0200 Subject: data/scope: fixed pdftotext invocation --- ranger/data/scope.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh index c99f715b..5a076130 100755 --- a/ranger/data/scope.sh +++ b/ranger/data/scope.sh @@ -37,7 +37,7 @@ case "$extension" in atool -l "$path" | head -n $maxln && exit 3 exit 1;; pdf) - pdftotext "$path" | head -n $maxln && exit 3 + pdftotext -q "$path" - | head -n $maxln && exit 3 exit 1;; # HTML Pages: htm|html|xhtml) -- cgit 1.4.1-2-gfad0 From 4f202b05fd31fbfeb85a382154f3c568467d4043 Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 10 Oct 2010 01:51:05 +0200 Subject: fsobject.file: fixed has_preview method --- ranger/fsobject/file.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ranger/fsobject/file.py b/ranger/fsobject/file.py index 33b4e3d3..d0002a56 100644 --- a/ranger/fsobject/file.py +++ b/ranger/fsobject/file.py @@ -77,6 +77,8 @@ class File(FileSystemObject): return False def has_preview(self): + if self.fm.settings.preview_script: + return True if not self.fm.settings.preview_files: return False if self.is_socket or self.is_fifo or self.is_device: @@ -84,7 +86,7 @@ class File(FileSystemObject): if not self.accessible: return False if self.image or self.container: - return True + return False if PREVIEW_WHITELIST.search(self.basename): return True if PREVIEW_BLACKLIST.search(self.basename): -- cgit 1.4.1-2-gfad0 From a7e9affcd6bbede10119031c1cb667486c8e6952 Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 10 Oct 2010 01:56:32 +0200 Subject: core.actions: catch exception in get_preview --- ranger/core/actions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index d0ccfe2b..c0d3e025 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -625,7 +625,10 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): else: return found else: - return open(path, 'r') + try: + return open(path, 'r') + except: + return None # -------------------------- # -- Tabs -- cgit 1.4.1-2-gfad0 From ae24898ad0625c2be9a5918ac0d9c775d6e8f0ad Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 10 Oct 2010 02:05:55 +0200 Subject: fsobject.file: tweak N_FIRST_BYTES --- ranger/fsobject/file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/fsobject/file.py b/ranger/fsobject/file.py index d0002a56..4de77d80 100644 --- a/ranger/fsobject/file.py +++ b/ranger/fsobject/file.py @@ -19,7 +19,7 @@ from subprocess import Popen, PIPE from ranger.core.runner import devnull from ranger.core.loader import CommandLoader -N_FIRST_BYTES = 20 +N_FIRST_BYTES = 256 control_characters = set(chr(n) for n in set(range(0, 9)) | set(range(14, 32))) -- cgit 1.4.1-2-gfad0 From 1a8e19243b4dd3bde17a19fb3c3e57d0cc9c0513 Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 10 Oct 2010 02:06:44 +0200 Subject: data/scope, fsobject: add .deb to archive extensions --- ranger/data/scope.sh | 4 ++-- ranger/fsobject/fsobject.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh index 5a076130..f4e91d64 100755 --- a/ranger/data/scope.sh +++ b/ranger/data/scope.sh @@ -32,8 +32,8 @@ function have { type -P "$1" > /dev/null; } # test if program is installed case "$extension" in # Archive extensions: - 7z|a|ace|alz|arc|arj|bz|bz2|cab|cpio|gz|jar|lha|lz|lzh|lzma|lzo\ - |rar|rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xz|Z|zip) + 7z|a|ace|alz|arc|arj|bz|bz2|cab|cpio|deb|gz|jar|lha|lz|lzh|lzma|lzo|\ + rar|rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xz|Z|zip) atool -l "$path" | head -n $maxln && exit 3 exit 1;; pdf) diff --git a/ranger/fsobject/fsobject.py b/ranger/fsobject/fsobject.py index fd886275..9d9f3db6 100644 --- a/ranger/fsobject/fsobject.py +++ b/ranger/fsobject/fsobject.py @@ -14,8 +14,8 @@ # along with this program. If not, see . CONTAINER_EXTENSIONS = ('7z', 'ace', 'ar', 'arc', 'bz', 'bz2', 'cab', 'cpio', - 'cpt', 'dgc', 'dmg', 'gz', 'iso', 'jar', 'msi', 'pkg', 'rar', 'shar', - 'tar', 'tbz', 'tgz', 'xar', 'xz', 'zip') + 'cpt', 'deb', 'dgc', 'dmg', 'gz', 'iso', 'jar', 'msi', 'pkg', 'rar', + 'shar', 'tar', 'tbz', 'tgz', 'xar', 'xz', 'zip') import re from os import access, listdir, lstat, readlink, stat -- cgit 1.4.1-2-gfad0 From 2e9365cf2df175427201b9894a1349d89a69cccd Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 10 Oct 2010 07:18:59 +0200 Subject: core.loader: Fixed pipe buffer overflow bug --- ranger/core/actions.py | 6 ++++-- ranger/core/loader.py | 43 +++++++++++++++++++++++++++++-------------- ranger/data/scope.sh | 2 +- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index c0d3e025..5adf42c9 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -594,11 +594,13 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): if found == False: data['loading'] = True loadable = CommandLoader(args=[self.settings.preview_script, - path, str(width), str(height)], + path, str(width), str(height)], read=True, silent=True, descr="Getting preview of %s" % path) def on_after(signal): + self.notify("%s complete" % path) exit = signal.process.poll() - content = signal.process.stdout.read() + content = signal.loader.stdout_buffer + content += signal.process.stdout.read() if exit == 0: data[(width, height)] = content elif exit == 3: diff --git a/ranger/core/loader.py b/ranger/core/loader.py index 5e652a28..28171042 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -56,49 +56,64 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware): """ finished = False process = None - def __init__(self, args, descr, silent=False): + def __init__(self, args, descr, silent=False, read=False): SignalDispatcher.__init__(self) Loadable.__init__(self, self.generate(), descr) self.args = args self.silent = silent + self.read = read + self.stdout_buffer = "" def generate(self): self.process = process = Popen(self.args, stdout=PIPE, stderr=PIPE) - self.signal_emit('before', process=process) - if self.silent: + self.signal_emit('before', process=process, loader=self) + if self.silent and not self.read: while process.poll() is None: yield sleep(0.03) else: + selectlist = [] + if self.read: + selectlist.append(process.stdout) + if not self.silent: + selectlist.append(process.stderr) while process.poll() is None: yield try: - rd, _, __ = select.select( - [process.stderr], [], [], 0.03) + rd, _, __ = select.select(selectlist, [], [], 0.03) if rd: - error = process.stderr.readline().decode('utf-8') - if error: - self.fm.notify(error, bad=True) + rd = rd[0] + read = rd.read(512) + if rd == process.stderr and read: + self.fm.notify(read, bad=True) + elif rd == process.stdout and read: + self.stdout_buffer += read except select.error: sleep(0.03) self.finished = True - self.signal_emit('after', process=process) + self.signal_emit('after', process=process, loader=self) def pause(self): if not self.finished and not self.paused: - self.process.send_signal(20) + try: + self.process.send_signal(20) + except: + pass Loadable.pause(self) - self.signal_emit('pause', process=self.process) + self.signal_emit('pause', process=self.process, loader=self) def unpause(self): if not self.finished and self.paused: - self.process.send_signal(18) + try: + self.process.send_signal(18) + except: + pass Loadable.unpause(self) - self.signal_emit('unpause', process=self.process) + self.signal_emit('unpause', process=self.process, loader=self) def destroy(self): - self.signal_emit('destroy', process=self.process) + self.signal_emit('destroy', process=self.process, loader=self) if self.process: self.process.kill() diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh index f4e91d64..788ec33c 100755 --- a/ranger/data/scope.sh +++ b/ranger/data/scope.sh @@ -41,7 +41,7 @@ case "$extension" in exit 1;; # HTML Pages: htm|html|xhtml) - have lynx && lynx -dump "$path" | head -n $maxln && exit 5 + have lynx && lynx -dump "$path" | head -n $maxln && exit 5 have elinks && elinks -dump "$path" | head -n $maxln && exit 5 ;; # fall back to highlight/cat if theres no lynx/elinks esac -- cgit 1.4.1-2-gfad0 From ba4731340e1dc9ddb9d7765aaff98c6a2ce58d46 Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 10 Oct 2010 07:52:38 +0200 Subject: core.actions: removed debug message --- ranger/core/actions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 5adf42c9..235be620 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -597,7 +597,6 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): path, str(width), str(height)], read=True, silent=True, descr="Getting preview of %s" % path) def on_after(signal): - self.notify("%s complete" % path) exit = signal.process.poll() content = signal.loader.stdout_buffer content += signal.process.stdout.read() -- cgit 1.4.1-2-gfad0 From 3744fed33e482856ffbdd692a4fba1e2ce3037dd Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 10 Oct 2010 07:52:55 +0200 Subject: data/scope, fsobject: added xpi to container extensions --- ranger/data/scope.sh | 2 +- ranger/fsobject/fsobject.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh index 788ec33c..30ae18ea 100755 --- a/ranger/data/scope.sh +++ b/ranger/data/scope.sh @@ -33,7 +33,7 @@ function have { type -P "$1" > /dev/null; } # test if program is installed case "$extension" in # Archive extensions: 7z|a|ace|alz|arc|arj|bz|bz2|cab|cpio|deb|gz|jar|lha|lz|lzh|lzma|lzo|\ - rar|rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xz|Z|zip) + rar|rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xpi|xz|Z|zip) atool -l "$path" | head -n $maxln && exit 3 exit 1;; pdf) diff --git a/ranger/fsobject/fsobject.py b/ranger/fsobject/fsobject.py index 9d9f3db6..e513fc21 100644 --- a/ranger/fsobject/fsobject.py +++ b/ranger/fsobject/fsobject.py @@ -15,7 +15,7 @@ CONTAINER_EXTENSIONS = ('7z', 'ace', 'ar', 'arc', 'bz', 'bz2', 'cab', 'cpio', 'cpt', 'deb', 'dgc', 'dmg', 'gz', 'iso', 'jar', 'msi', 'pkg', 'rar', - 'shar', 'tar', 'tbz', 'tgz', 'xar', 'xz', 'zip') + 'shar', 'tar', 'tbz', 'tgz', 'xar', 'xpi', 'xz', 'zip') import re from os import access, listdir, lstat, readlink, stat -- cgit 1.4.1-2-gfad0 From e3ad1e7ecdedcd3f8badec20b243ab2079262e98 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 11 Oct 2010 07:23:38 +0200 Subject: widgets.browserview: fix collapse_preview option --- ranger/core/actions.py | 7 ++++++- ranger/gui/widgets/browserview.py | 13 ++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 235be620..a815b111 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -581,6 +581,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): # self.previews['/tmp/foo.jpg'][(80, 24)] = "the content..." # self.previews['/tmp/foo.jpg']['loading'] = False # A -1 in tuples means "any"; (80, -1) = wid. of 80 and any hei. + # The key 'foundpreview' is added later. Values in (True, False) try: data = self.previews[path] except: @@ -600,6 +601,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): exit = signal.process.poll() content = signal.loader.stdout_buffer content += signal.process.stdout.read() + data['foundpreview'] = True if exit == 0: data[(width, height)] = content elif exit == 3: @@ -608,8 +610,11 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): data[(width, -1)] = content elif exit == 5: data[(-1, -1)] = content + elif exit == 1: + data[(-1, -1)] = None + data['foundpreview'] = False else: - data[(-1, -1)] = None # XXX + data[(-1, -1)] = None if self.env.cf.path == path: self.ui.browser.pager.need_redraw = True self.ui.browser.need_redraw = True diff --git a/ranger/gui/widgets/browserview.py b/ranger/gui/widgets/browserview.py index 142e7985..bbaee6a2 100644 --- a/ranger/gui/widgets/browserview.py +++ b/ranger/gui/widgets/browserview.py @@ -28,6 +28,7 @@ class BrowserView(Widget, DisplayableContainer): draw_bookmarks = False stretch_ratios = None need_clear = False + old_collapse = False def __init__(self, win, ratios, preview = True): DisplayableContainer.__init__(self, win) @@ -196,8 +197,18 @@ class BrowserView(Widget, DisplayableContainer): def _collapse(self): # Should the last column be cut off? (Because there is no preview) - return self.settings.collapse_preview and self.preview and \ + result = self.settings.collapse_preview and self.preview and \ not self.columns[-1].has_preview() and self.stretch_ratios + if result: + return True + if self.columns[-1].target: + target = self.columns[-1].target + try: + result = not self.fm.previews[target.realpath]['foundpreview'] + except: + return self.old_collapse + self.old_collapse = result + return result def resize(self, y, x, hei, wid): """Resize all the columns according to the given ratio""" -- cgit 1.4.1-2-gfad0 From 6ab2c84a6b3b8d015f9e808f6fbba5846677e9f7 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 11 Oct 2010 07:28:10 +0200 Subject: fsobject.file: fix preview_files option --- ranger/fsobject/file.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ranger/fsobject/file.py b/ranger/fsobject/file.py index 4de77d80..5fd90b96 100644 --- a/ranger/fsobject/file.py +++ b/ranger/fsobject/file.py @@ -77,14 +77,14 @@ class File(FileSystemObject): return False def has_preview(self): - if self.fm.settings.preview_script: - return True if not self.fm.settings.preview_files: return False if self.is_socket or self.is_fifo or self.is_device: return False if not self.accessible: return False + if self.fm.settings.preview_script: + return True if self.image or self.container: return False if PREVIEW_WHITELIST.search(self.basename): -- cgit 1.4.1-2-gfad0 From 2bcb509a0834828829b8ca01d5679ee22c416267 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 11 Oct 2010 07:34:50 +0200 Subject: widgets.browserview: again, fixed collapse_preview option --- ranger/gui/widgets/browserview.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ranger/gui/widgets/browserview.py b/ranger/gui/widgets/browserview.py index bbaee6a2..e222c582 100644 --- a/ranger/gui/widgets/browserview.py +++ b/ranger/gui/widgets/browserview.py @@ -197,12 +197,12 @@ class BrowserView(Widget, DisplayableContainer): def _collapse(self): # Should the last column be cut off? (Because there is no preview) - result = self.settings.collapse_preview and self.preview and \ - not self.columns[-1].has_preview() and self.stretch_ratios - if result: - return True - if self.columns[-1].target: - target = self.columns[-1].target + if not self.settings.collapse_preview or not self.preview \ + or not self.stretch_ratios: + return False + result = not self.columns[-1].has_preview() + target = self.columns[-1].target + if not result and target and target.is_file: try: result = not self.fm.previews[target.realpath]['foundpreview'] except: -- cgit 1.4.1-2-gfad0 From a0ca73c855d4925780253c8294d725892455cc51 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 11 Oct 2010 08:00:16 +0200 Subject: core.main: added --copy-config flag --- doc/ranger.1 | 4 ++++ ranger/core/fm.py | 22 ++++++++++++++++++---- ranger/core/helper.py | 2 ++ ranger/core/main.py | 6 +++++- ranger/help/invocation.py | 4 ++++ 5 files changed, 33 insertions(+), 5 deletions(-) diff --git a/doc/ranger.1 b/doc/ranger.1 index 968e601b..dad2642c 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -33,6 +33,10 @@ Activate the clean mode: Ranger will not access or create any configuration files nor will it leave any traces on your system. This is useful when your configuration is broken, when you want to avoid clutter, etc. .TP +--copy-config +Create copies of the default configuration files in your local configuration +directory. Existing ones will not be overwritten. +.TP --fail-unless-cd Return the exit code 1 if ranger is used to run a file, for example with `ranger --fail-unless-cd filename`. This can be useful for scripts. diff --git a/ranger/core/fm.py b/ranger/core/fm.py index bfcebb92..fce6b3b2 100644 --- a/ranger/core/fm.py +++ b/ranger/core/fm.py @@ -123,10 +123,24 @@ class FM(Actions, SignalDispatcher): return self.input_blocked def copy_config_files(self): - if not (ranger.arg.clean or os.path.exists(self.confpath('scope.sh'))): - import shutil - shutil.copy(self.relpath('data/scope.sh'), - self.confpath('scope.sh')) + if ranger.arg.clean: + sys.stderr.write("refusing to copy config files in clean mode\n") + return + import shutil + def copy(_from, to): + if os.path.exists(self.confpath(to)): + sys.stderr.write("already exists: %s\n" % self.confpath(to)) + else: + sys.stderr.write("creating: %s\n" % self.confpath(to)) + try: + shutil.copy(self.relpath(_from), self.confpath(to)) + except Exception as e: + sys.stderr.write(" ERROR: %s\n" % str(e)) + copy('defaults/apps.py', 'apps.py') + copy('defaults/commands.py', 'commands.py') + copy('defaults/keys.py', 'keys.py') + copy('defaults/options.py', 'options.py') + copy('data/scope.sh', 'scope.sh') def confpath(self, *paths): """returns the path relative to rangers configuration directory""" diff --git a/ranger/core/helper.py b/ranger/core/helper.py index 38460bb6..aa55b25b 100644 --- a/ranger/core/helper.py +++ b/ranger/core/helper.py @@ -51,6 +51,8 @@ def parse_arguments(): help="don't touch/require any config files. ") parser.add_option('--fail-if-run', action='store_true', # COMPAT help=SUPPRESS_HELP) + parser.add_option('--copy-config', action='store_true', # COMPAT + help="copy the default configs to the local config directory") parser.add_option('--fail-unless-cd', action='store_true', help="experimental: return the exit code 1 if ranger is" \ "used to run a file (with `ranger filename`)") diff --git a/ranger/core/main.py b/ranger/core/main.py index ed555a8d..19e28db3 100644 --- a/ranger/core/main.py +++ b/ranger/core/main.py @@ -42,6 +42,11 @@ def main(): os.environ['SHELL'] = 'bash' ranger.arg = arg = parse_arguments() + if arg.copy_config: + fm = FM() + fm.copy_config_files() + return 0 + SettingsAware._setup(clean=arg.clean) targets = arg.targets or ['.'] @@ -65,7 +70,6 @@ def main(): # Initialize objects EnvironmentAware.env = Environment(target) fm = FM() - fm.copy_config_files() fm.tabs = dict((n+1, os.path.abspath(path)) for n, path \ in enumerate(targets[:9])) load_settings(fm, arg.clean) diff --git a/ranger/help/invocation.py b/ranger/help/invocation.py index 27ab5a67..cad2a2b2 100644 --- a/ranger/help/invocation.py +++ b/ranger/help/invocation.py @@ -44,6 +44,10 @@ command line. This is useful when your configuration is broken, when you want to avoid clutter, etc. +--copy-config + Create copies of the default configuration files in your local + configuration directory. Existing ones will not be overwritten. + --fail-unless-cd Return the exit code 1 if ranger is used to run a file, for example with `ranger --fail-unless-cd filename`. This can be useful for scripts. -- cgit 1.4.1-2-gfad0 From 6dad7b9222168602c5c23fc001be290110721ec5 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 11 Oct 2010 08:11:22 +0200 Subject: Added boolean option use_preview_script --- ranger/container/settingobject.py | 1 + ranger/core/actions.py | 2 +- ranger/defaults/options.py | 6 ++++-- ranger/fsobject/file.py | 3 ++- ranger/gui/widgets/browserview.py | 4 +++- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/ranger/container/settingobject.py b/ranger/container/settingobject.py index c8bd8b49..51d6db1a 100644 --- a/ranger/container/settingobject.py +++ b/ranger/container/settingobject.py @@ -48,6 +48,7 @@ ALLOWED_SETTINGS = { 'sort': str, 'tilde_in_titlebar': bool, 'update_title': bool, + 'use_preview_script': bool, 'xterm_alt_key': bool, } diff --git a/ranger/core/actions.py b/ranger/core/actions.py index a815b111..2f6d2719 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -576,7 +576,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): # -- Previews # -------------------------- def get_preview(self, path, width, height): - if self.settings.preview_script: + 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..." # self.previews['/tmp/foo.jpg']['loading'] = False diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index 3c20c6fb..845e451f 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -40,12 +40,14 @@ hidden_filter = regexp( show_hidden = False # Which script is used to generate file previews? -#preview_script = None - # Ranger ships with scope.sh, a script that calls external programs (see # README for dependencies) to preview images, archives, etc. preview_script = '~/.config/ranger/scope.sh' +# Use that external preview script or display internal plain text previews? +# Set to False by default for performance and stability reasons +use_preview_script = False + # Show dotfiles in the bookmark preview box? show_hidden_bookmarks = True diff --git a/ranger/fsobject/file.py b/ranger/fsobject/file.py index 5fd90b96..9fce3255 100644 --- a/ranger/fsobject/file.py +++ b/ranger/fsobject/file.py @@ -83,7 +83,8 @@ class File(FileSystemObject): return False if not self.accessible: return False - if self.fm.settings.preview_script: + if self.fm.settings.preview_script and \ + self.fm.settings.use_preview_script: return True if self.image or self.container: return False diff --git a/ranger/gui/widgets/browserview.py b/ranger/gui/widgets/browserview.py index e222c582..8a8f6e14 100644 --- a/ranger/gui/widgets/browserview.py +++ b/ranger/gui/widgets/browserview.py @@ -202,7 +202,9 @@ class BrowserView(Widget, DisplayableContainer): return False result = not self.columns[-1].has_preview() target = self.columns[-1].target - if not result and target and target.is_file: + if not result and target and target.is_file and \ + self.fm.settings.preview_script and \ + self.fm.settings.use_preview_script: try: result = not self.fm.previews[target.realpath]['foundpreview'] except: -- cgit 1.4.1-2-gfad0 From eaed2443fa77e133809d9c47937ccd301f969fed Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 12 Oct 2010 03:33:26 +0200 Subject: core.actions: improved fm.paste() --- ranger/core/actions.py | 28 +++++++++++++++++++++------- ranger/core/loader.py | 13 ++++++++----- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 2f6d2719..50c322c4 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -748,8 +748,15 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): cwd = self.env.get_directory(original_path) cwd.load_content() - original_path = self.env.cwd.path + cwd = self.env.cwd + original_path = cwd.path one_file = copied_files[0] + if overwrite: + cp_flags = ['--backup=numbered', '-af', '--'] + mv_flags = ['--backup=numbered', '-f', '--'] + else: + cp_flags = ['--backup=numbered', '-a', '--'] + mv_flags = ['--backup=numbered', '--'] if self.env.cut: self.env.copy.clear() @@ -758,17 +765,24 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): descr = "moving: " + one_file.path else: descr = "moving files from: " + one_file.dirname - obj = CommandLoader(args=['mv', '--backup=existing', - '--suffix=_', '-ft', self.env.cwd.path] + \ - [f.path for f in copied_files], descr=descr) + obj = CommandLoader(args=['mv'] + mv_flags + + + [f.path for f in copied_files] + + [cwd.path], descr=descr) else: if len(copied_files) == 1: descr = "copying: " + one_file.path else: descr = "copying files from: " + one_file.dirname - obj = CommandLoader(args=['cp', '--backup=existing', '--archive', - '--suffix=_', '-frt', self.env.cwd.path] + \ - [f.path for f in self.env.copy], descr=descr) + if not overwrite and len(copied_files) == 1 \ + and one_file.dirname == cwd.path: + # Special case: yypp + # copying a file onto itself -> create a backup + obj = CommandLoader(args=['cp', '-f'] + cp_flags + + [one_file.path, one_file.path], descr=descr) + else: + obj = CommandLoader(args=['cp'] + cp_flags + + [f.path for f in copied_files] + + [cwd.path], descr=descr) obj.signal_bind('after', refresh) self.loader.add(obj) diff --git a/ranger/core/loader.py b/ranger/core/loader.py index 28171042..85033881 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -84,11 +84,14 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware): rd, _, __ = select.select(selectlist, [], [], 0.03) if rd: rd = rd[0] - read = rd.read(512) - if rd == process.stderr and read: - self.fm.notify(read, bad=True) - elif rd == process.stdout and read: - self.stdout_buffer += read + if rd == process.stderr: + read = rd.readline() + if read: + self.fm.notify(read, bad=True) + elif rd == process.stdout: + read = rd.read(512) + if read: + self.stdout_buffer += read except select.error: sleep(0.03) self.finished = True -- cgit 1.4.1-2-gfad0 From 06abefd30b86f20068786c6cc4b392e070923f2f Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 12 Oct 2010 03:36:11 +0200 Subject: Added key zv for toggling use_preview_script option --- ranger/defaults/keys.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 21a170f7..9e624982 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -189,11 +189,12 @@ map('E', fm.edit_file()) map('du', fm.execute_console('shell -p du --max-depth=1 -h --apparent-size')) # -------------------------------------------------- toggle options -map('z', fm.hint("[*cdfhimpPs*] show_*h*idden *p*review_files "\ +map('z', fm.hint("[*cdfhimpPsv*] show_*h*idden *p*review_files "\ "*P*review_dirs *f*ilter flush*i*nput *m*ouse")) map('zh', '', '', fm.toggle_boolean_option('show_hidden')) map('zp', fm.toggle_boolean_option('preview_files')) map('zP', fm.toggle_boolean_option('preview_directories')) +map('zv', fm.toggle_boolean_option('use_preview_script')) map('zi', fm.toggle_boolean_option('flushinput')) map('zd', fm.toggle_boolean_option('sort_directories_first')) map('zc', fm.toggle_boolean_option('collapse_preview')) -- cgit 1.4.1-2-gfad0 From f6e84977da884b54c1ad16d55dbdf1fa6485f252 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 12 Oct 2010 03:36:44 +0200 Subject: defaults.options: don't hide /~$/ files This would hide backups from typing yypp and confuse the user --- ranger/defaults/options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index 845e451f..b55ac22d 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -36,7 +36,7 @@ from ranger.api.options import * # 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') + r'^\.|\.(?:pyc|pyo|bak|swp)$|^lost\+found$') show_hidden = False # Which script is used to generate file previews? -- cgit 1.4.1-2-gfad0 From dcd610f7b768b75f57017c0a037b99540e5cb061 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 12 Oct 2010 04:07:21 +0200 Subject: core.loader: make sure to read everything from stdout/err --- ranger/core/actions.py | 1 - ranger/core/loader.py | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 50c322c4..6fd9ad09 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -600,7 +600,6 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): def on_after(signal): exit = signal.process.poll() content = signal.loader.stdout_buffer - content += signal.process.stdout.read() data['foundpreview'] = True if exit == 0: data[(width, height)] = content diff --git a/ranger/core/loader.py b/ranger/core/loader.py index 85033881..02e8c232 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -94,6 +94,11 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware): self.stdout_buffer += read except select.error: sleep(0.03) + if not self.silent: + for l in process.stderr.readlines(): + self.fm.notify(l, bad=True) + if self.read: + self.stdout_buffer += process.stdout.read() self.finished = True self.signal_emit('after', process=process, loader=self) -- cgit 1.4.1-2-gfad0 From 7137526a07bf98671a8398a2bc987cbd5623f676 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 12 Oct 2010 05:01:42 +0200 Subject: core.shared: added error message when typing zv and there is no preview_script --- ranger/core/shared.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/ranger/core/shared.py b/ranger/core/shared.py index 1d0f96a0..38dc7cdd 100644 --- a/ranger/core/shared.py +++ b/ranger/core/shared.py @@ -17,6 +17,7 @@ inherited, essentially acting like global variables.""" from ranger.ext.lazy_property import lazy_property +import os.path class Awareness(object): pass @@ -54,12 +55,19 @@ class SettingsAware(Awareness): settings.signal_bind('setopt.colorscheme', _colorscheme_name_to_class, priority=1) - def postprocess_paths(signal): + def after_setting_preview_script(signal): if isinstance(signal.value, str): - import os signal.value = os.path.expanduser(signal.value) + if not os.path.exists(signal.value): + signal.value = None settings.signal_bind('setopt.preview_script', - postprocess_paths, priority=1) + after_setting_preview_script, priority=1) + def after_setting_use_preview_script(signal): + if signal.fm.settings.preview_script is None and signal.value: + signal.fm.notify("Preview script undefined or not found!", + bad=True) + settings.signal_bind('setopt.use_preview_script', + after_setting_use_preview_script, priority=1) if not clean: # add the custom options to the list of setting sources -- cgit 1.4.1-2-gfad0