From 9506fb8e79f2d04a1ab78039bacdbee7b22109b5 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 27 Nov 2009 10:49:48 +0100 Subject: more VROOM --- code/__init__.py | 0 code/__init__.pyc | Bin 125 -> 0 bytes code/debug.py | 12 ------ code/defaultui.py | 16 ------- code/directory.py | 70 ------------------------------- code/directory.rb | 66 ----------------------------- code/environment.py | 38 ----------------- code/file.py | 6 --- code/fm.py | 65 ----------------------------- code/fsobject.py | 87 --------------------------------------- code/fstype.py | 5 --- code/options.py | 5 --- code/ui.py | 48 --------------------- code/wdisplay.py | 34 --------------- code/widget.py | 39 ------------------ ranger | 34 --------------- ranger.py | 38 +++++++++++++++++ ranger/__init__.py | 0 ranger/cli.py | 32 ++++++++++++++ ranger/command.py | 77 ++++++++++++++++++++++++++++++++++ ranger/debug.py | 12 ++++++ ranger/defaultui.py | 39 ++++++++++++++++++ ranger/directory.py | 74 +++++++++++++++++++++++++++++++++ ranger/directory.rb | 66 +++++++++++++++++++++++++++++ ranger/environment.py | 73 ++++++++++++++++++++++++++++++++ ranger/file.py | 6 +++ ranger/fm.py | 42 +++++++++++++++++++ ranger/fsobject.py | 103 ++++++++++++++++++++++++++++++++++++++++++++++ ranger/fstype.py | 5 +++ ranger/keys.py | 15 +++++++ ranger/options.py | 5 +++ ranger/ui.py | 68 ++++++++++++++++++++++++++++++ ranger/wdisplay.py | 51 +++++++++++++++++++++++ ranger/widget.py | 43 +++++++++++++++++++ ranger/wstatusbar.py | 12 ++++++ test/dirsize_benchmark.py | 2 + test/tc_directory.py | 32 ++++++++------ test4.py | 36 ++++++++++++++++ 38 files changed, 819 insertions(+), 537 deletions(-) delete mode 100644 code/__init__.py delete mode 100644 code/__init__.pyc delete mode 100644 code/debug.py delete mode 100644 code/defaultui.py delete mode 100644 code/directory.py delete mode 100644 code/directory.rb delete mode 100644 code/environment.py delete mode 100644 code/file.py delete mode 100644 code/fm.py delete mode 100644 code/fsobject.py delete mode 100644 code/fstype.py delete mode 100644 code/options.py delete mode 100644 code/ui.py delete mode 100644 code/wdisplay.py delete mode 100644 code/widget.py delete mode 100755 ranger create mode 100755 ranger.py create mode 100644 ranger/__init__.py create mode 100644 ranger/cli.py create mode 100644 ranger/command.py create mode 100644 ranger/debug.py create mode 100644 ranger/defaultui.py create mode 100644 ranger/directory.py create mode 100644 ranger/directory.rb create mode 100644 ranger/environment.py create mode 100644 ranger/file.py create mode 100644 ranger/fm.py create mode 100644 ranger/fsobject.py create mode 100644 ranger/fstype.py create mode 100644 ranger/keys.py create mode 100644 ranger/options.py create mode 100644 ranger/ui.py create mode 100644 ranger/wdisplay.py create mode 100644 ranger/widget.py create mode 100644 ranger/wstatusbar.py create mode 100644 test4.py diff --git a/code/__init__.py b/code/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/code/__init__.pyc b/code/__init__.pyc deleted file mode 100644 index 2479f90e..00000000 Binary files a/code/__init__.pyc and /dev/null differ diff --git a/code/debug.py b/code/debug.py deleted file mode 100644 index 12d5d654..00000000 --- a/code/debug.py +++ /dev/null @@ -1,12 +0,0 @@ -# a module to faciliate debuggin - -LOGFILE = '/tmp/errorlog' - -def log(txt): - f = open(LOGFILE, 'a') - f.write("r1: ") - f.write(str(txt)) - f.write("\n") - f.close() - - diff --git a/code/defaultui.py b/code/defaultui.py deleted file mode 100644 index d46c89c8..00000000 --- a/code/defaultui.py +++ /dev/null @@ -1,16 +0,0 @@ -import ui -import widget, wdisplay - -class DefaultUI(ui.UI): - def setup(self): - self.main_display = wdisplay.WDisplay(self.win, 0) - self.add_widget(self.main_display) - self.left_display = wdisplay.WDisplay(self.win, -1) - self.add_widget(self.left_display) - - def resize(self): - ui.UI.resize(self) - y, x = self.win.getmaxyx() - self.main_display.setdim(1, 40, 3, 37) - self.left_display.setdim(1, 0, 3, 37) - diff --git a/code/directory.py b/code/directory.py deleted file mode 100644 index 4109ae2d..00000000 --- a/code/directory.py +++ /dev/null @@ -1,70 +0,0 @@ -import fsobject -import file, debug - -class Directory(fsobject.FSObject): - def __init__(self, path): - fsobject.FSObject.__init__(self, path) - self.content_loaded = False - self.scheduled = False - self.enterable = False - - self.filenames = None - self.files = None - self.filter = None - self.pointed_index = None - self.pointed_file = None - self.index = None - - def load_content(self): - self.stop_if_frozen() - self.load_if_outdated() - self.content_loaded = True - import os - if self.exists: - basenames = os.listdir(self.path) - mapped = map(lambda name: os.path.join(self.path, name), basenames) - self.filenames = list(mapped) - self.infostring = ' %d' % len(self.filenames) # update the infostring - self.files = [] - for name in self.filenames: - if os.path.isdir(name): - f = Directory(name) - else: - f = file.File(name) - f.load() - self.files.append(f) - - def load_content_once(self): - self.stop_if_frozen() - if not self.content_loaded: - self.load_content() - return True - return False - - def load_content_if_outdated(self): - self.stop_if_frozen() - if self.load_content_once(): return True - - import os - real_mtime = os.stat(self.path).st_mtime - cached_mtime = self.stat.st_mtime - - if real_mtime != cached_mtime: - self.load_content() - return True - return False - - def __len__(self): - if not self.accessible: raise fsobject.NotLoadedYet() - return len(self.filenames) - - def __getitem__(self, key): - if not self.accessible: raise fsobject.NotLoadedYet() - return self.files[key] - -if __name__ == '__main__': - d = Directory('.') - d.load_filenames() - print(d.filenames) - print(d[1]) - diff --git a/code/directory.rb b/code/directory.rb deleted file mode 100644 index 5c6e84c1..00000000 --- a/code/directory.rb +++ /dev/null @@ -1,66 +0,0 @@ -# A Class that contains data about directories -class Directory - class LoadStatus - # @n contains a three bit number: x3x2x1 - # x1: - # 0 = not scheduled - # 1 = scheduled - # x3x2: - # 00 = nothing loaded - # 01 = got the list of files - # 10 = - # 11 = got the list of files and entry objects - def initialize(n = 0) - @n = 0 - end - - def got_files? - # is bit 2 nd 3 == 01 - return n & 2 == 2 - end - - def scheduled? - # is the first bit 1? - return n & 1 == 1 - end - - def got_objects? - return n & 4 == 4 - end - attr_accessor :n - end - - def initialize(path) - @path = path - @status = LoadStatus.new(0) - @files = [] - @sort_time = nil - @mtime = nil -# @width = 1000 - @read = false - @free_space = nil - @empty = true - @scheduled = false - end - - # {{{ Trivial - def inspect - return "" - end - alias to_s inspect - - def size - return @files.size - end - - def not_loaded? - return @level == 0 - end - def file_list_loaded? - return @level >= 1 - end - def ready? - return @level >= 2 - end - # }}} -end diff --git a/code/environment.py b/code/environment.py deleted file mode 100644 index 515fc8c6..00000000 --- a/code/environment.py +++ /dev/null @@ -1,38 +0,0 @@ -import directory - -class Vector(): - def __init__(self, x, y): - self.x = x - self.y = y - -class Environment(): - # A collection of data which is relevant for more than - # one class. - def __init__(self, opt): - self.opt = opt - self.path = None - self.pathway = () - self.directories = {} - self.pwd = None # current directory - self.cf = None # current file - self.keybuffer = '' - self.copy = None - self.termsize = Vector(80, 24) - - def at_level(self, level): - if level <= 0: - try: - return self.pathway[level - 1] - except IndexError: - return None - else: - return self.cf - - def get_directory(self, path): - import os - path = os.path.abspath(path) - try: - return self.directories[path] - except KeyError: - self.directories[path] = directory.Directory(path) - return self.directories[path] diff --git a/code/file.py b/code/file.py deleted file mode 100644 index 9d813aad..00000000 --- a/code/file.py +++ /dev/null @@ -1,6 +0,0 @@ -import fsobject -class File(fsobject.FSObject): - pass -# def __init__(self, path): -# fsobject.FSObject.__init__(self, path) - diff --git a/code/fm.py b/code/fm.py deleted file mode 100644 index 924f6efc..00000000 --- a/code/fm.py +++ /dev/null @@ -1,65 +0,0 @@ -import sys, os -import ui, debug, file, directory, fstype - -class FM(): - def __init__(self, environment): - self.env = environment - - def feed(self, path, ui): - self.ui = ui - self.env.path = path - self.enter_dir(path) - - def enter_dir(self, path): - # get the absolute path - path = os.path.normpath(os.path.join(self.env.path, path)) - - self.env.path = path - self.env.pwd = self.env.get_directory(path) - - self.env.pwd.load_content() - - # build the pathway, a tuple of directory objects which lie - # on the path to the current directory. - pathway = [] - currentpath = '/' - for dir in path.split('/'): - currentpath = os.path.join(currentpath, dir) - debug.log(currentpath) - pathway.append(self.env.get_directory(currentpath)) - self.env.pathway = tuple(pathway) - - # set the current file. - if len(self.env.pwd) > 0: - self.env.cf = self.env.pwd[0] - else: - self.env.cf = None - - def run(self): - while 1: - try: - self.ui.draw() - except KeyboardInterrupt: - self.interrupt() - except: - raise - - try: - key = self.ui.get_next_key() - self.press(key) - except KeyboardInterrupt: - self.interrupt() - - def press(self, key): - if (key == ord('q')): - raise SystemExit() - elif (key == ord('h')): - self.enter_dir('..') - elif (key == ord('l')): - self.enter_dir(self.env.cf.path) - - def interrupt(self): - import time - self.buffer = "" - time.sleep(0.2) - diff --git a/code/fsobject.py b/code/fsobject.py deleted file mode 100644 index f4268ef0..00000000 --- a/code/fsobject.py +++ /dev/null @@ -1,87 +0,0 @@ -import fstype - -class FrozenException(Exception): pass -class NotLoadedYet(Exception): pass - -class FSObject(object): - def __init__(self, path): - if type(self) == FSObject: - raise TypeError("FSObject is an abstract class and cannot be initialized.") - self.path = path - self.exists = False - self.accessible = False - self.marked = False - self.tagged = False - self.frozen = False - self.loaded = False - self.islink = False - self.brokenlink = False - self.stat = None - self.infostring = None - self.permissions = None - self.type = fstype.Unknown - - # load() reads useful information about the file from the file system - # and caches it in instance attributes. - def load(self): - self.stop_if_frozen() - self.loaded = True - - import os - try: - self.stat = os.stat(self.path) - self.islink = os.path.islink(self.path) - self.exists = True - self.accessible = True - - if os.path.isdir(self.path): - self.type = fstype.Directory - self.infostring = ' %d' % len(os.listdir(self.path)) - elif os.path.isfile(self.path): - self.type = fstype.File - self.infostring = ' %d' % self.stat.st_size - else: - self.type = fstype.Unknown - self.infostring = None - - except OSError: - self.islink = False - self.infostring = None - self.type = fstype.Nonexistent - self.exists = False - self.accessible = False - - def load_once(self): - self.stop_if_frozen() - if not self.loaded: - self.load() - return True - return False - - def load_if_outdated(self): - self.stop_if_frozen() - if self.load_once(): return True - - import os - real_mtime = os.stat(self.path).st_mtime - cached_mtime = self.stat.st_mtime - - if real_mtime != cached_mtime: - self.load() - return True - return False - - def clone(self): - clone = type(self)(self.path) - for key in iter(self.__dict__): - clone.__dict__[key] = self.__dict__[key] - return clone - - def frozen_clone(self): - clone = self.clone() - clone.frozen = True - return clone - - def stop_if_frozen(self): - if self.frozen: raise FrozenException('Cannot modify datastructure while it is frozen') - diff --git a/code/fstype.py b/code/fstype.py deleted file mode 100644 index 4bd0988d..00000000 --- a/code/fstype.py +++ /dev/null @@ -1,5 +0,0 @@ -class File: pass -class Directory: pass -class Nonexistent: pass -class Unknown: pass - diff --git a/code/options.py b/code/options.py deleted file mode 100644 index eed79f1e..00000000 --- a/code/options.py +++ /dev/null @@ -1,5 +0,0 @@ -def get(): - return [] - -def dummy(): - return [] diff --git a/code/ui.py b/code/ui.py deleted file mode 100644 index d9c1de4e..00000000 --- a/code/ui.py +++ /dev/null @@ -1,48 +0,0 @@ -import curses, debug -class UI(): - def __init__(self, env): - self.env = env - - self.widgets = [] - self.win = curses.initscr() - self.win.leaveok(1) - curses.noecho() - curses.halfdelay(3) - - self.setup() - self.resize() - - def setup(self): - pass - - def resize(self): - self.env.termsize = self.win.getmaxyx() - - def add_widget(self, widg): - self.widgets.append(widg) - - def feed_env(self, env): - self.env = env - - def exit(self): - curses.nocbreak() - curses.echo() - curses.endwin() - - def draw(self): - self.win.erase() - for widg in self.widgets: - widg.feed_env(self.env) - widg.draw() - self.win.refresh() - -# for i in range(1, len(self.env.pwd)): -# f = self.env.pwd.files[i] -# self.win.addstr(i, 0, f.path) -# if f.infostring: self.win.addstr(i, 50, f.infostring) - - def get_next_key(self): - key = self.win.getch() - curses.flushinp() - return key - diff --git a/code/wdisplay.py b/code/wdisplay.py deleted file mode 100644 index a564a6cb..00000000 --- a/code/wdisplay.py +++ /dev/null @@ -1,34 +0,0 @@ -import widget -import curses -import file, directory - -class WDisplay(widget.Widget): - def __init__(self, win, level): - widget.Widget.__init__(self,win) - self.level = level - - def feed_env(self, env): - self.target = env.at_level(self.level) - - def draw(self): - if type(self.target) == file.File: - self.draw_file() - elif type(self.target) == directory.Directory: - self.draw_directory() - elif self.target is None: - self.win.addnstr(self.y, self.x, "---", self.wid) - else: - self.win.addnstr(self.y, self.x, "unknown type.", self.wid) - - def draw_file(self): - self.win.addnstr(self.y, self.x, "this is a file.", self.wid) - - def draw_directory(self): - self.target.load_content_once() - for i in range(self.hei): - try: - f = self.target[i] - except IndexError: - break - self.win.addnstr(self.y + i, self.x, self.target[i].path, self.wid) - diff --git a/code/widget.py b/code/widget.py deleted file mode 100644 index e95e6a9d..00000000 --- a/code/widget.py +++ /dev/null @@ -1,39 +0,0 @@ -import curses - -class OutOfBoundsException(Exception): pass - -class Widget(): - def __init__(self, win): - self.win = win - self.setdim(0, 0, 0, 0) - - def setdim(self, y, x, hei=None, wid=None): - maxy, maxx = self.win.getmaxyx() - wid = wid or maxx - x - hei = hei or maxy - y - if x + wid > maxx or y + hei > maxy: - raise OutOfBoundsException() - - self.x = x - self.y = y - self.wid = wid - self.hei = hei - - def contains_point(self, y, x): - return (x >= self.x and x < self.x + self.wid) and \ - (y >= self.y and y < self.y + self.hei) - - def feed_env(self): - pass - - def feed(self): - pass - - def click(self): - pass - - def draw(self): - pass - - def destroy(self): - pass diff --git a/ranger b/ranger deleted file mode 100755 index d83e8a3e..00000000 --- a/ranger +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/python -# coding=utf-8 - -# TODO: cd after exit - -from code import debug, fm, defaultui, options, environment - -# TODO: find out the real name of this script and include files relative to here - -# TODO: Parse arguments - -# TODO: load config - -def main(): - import locale, os - os.stat_float_times(True) - locale.setlocale(locale.LC_ALL, 'en_US.utf8') - - try: - path = '/srv/music/compilations/' - opt = options.get() - env = environment.Environment(opt) - - my_ui = defaultui.DefaultUI(env) - my_fm = fm.FM(env) - my_fm.feed(path, my_ui) - my_fm.run() - - except: - my_ui.exit() - raise - - -if __name__ == "__main__": main() diff --git a/ranger.py b/ranger.py new file mode 100755 index 00000000..65388727 --- /dev/null +++ b/ranger.py @@ -0,0 +1,38 @@ +#!/usr/bin/python +# coding=utf-8 + +# TODO: cd after exit + +from ranger import debug, fm, options, environment, command, keys +from ranger.defaultui import DefaultUI as UI + +# TODO: find out the real name of this script and include files relative to here + +# TODO: Parse arguments + +# TODO: load config + +def main(): + import locale, os + os.stat_float_times(True) + locale.setlocale(locale.LC_ALL, 'en_US.utf8') + + try: + path = os.path.abspath('.') + opt = options.get() + env = environment.Environment(opt) + commandlist = command.CommandList() + keys.initialize_commands(commandlist) + + my_ui = UI(env, commandlist) + my_fm = fm.FM(env) + my_fm.feed(path, my_ui) + my_fm.run() + + except BaseException as original_error: + try: my_ui.exit() + except: pass + + raise original_error + +if __name__ == "__main__": main() diff --git a/ranger/__init__.py b/ranger/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ranger/cli.py b/ranger/cli.py new file mode 100644 index 00000000..7f8fd77f --- /dev/null +++ b/ranger/cli.py @@ -0,0 +1,32 @@ +import curses +import _thread + +class CLIError(): pass + +class CLI(): + def __init__(self): + self.lock = _thread.allocalte_lock() + self.running = False + + def start(self): + with self.lock: + stdscr = curses.initscr() + self.running = True + + def exit(self): + self.stop_unless_running() + with self.lock: + self.running = False + curses.nocbreak() + stdscr.keypad(1) + curses.endwin() + + def stop_unless_running(self): + if not self.running: + raise CLIError("This function needs the cli to be runnig!") + + def print(self, text, x=0, y=0, attr=None): + with self.lock: + + + diff --git a/ranger/command.py b/ranger/command.py new file mode 100644 index 00000000..c57edba0 --- /dev/null +++ b/ranger/command.py @@ -0,0 +1,77 @@ +class CommandDummy(): + pass + +class CommandList(): + def __init__(self): + self.commandlist = [] + self.paths = {} + self.dummies_in_paths = False + self.dummy_object = CommandDummy + + def rebuild_paths(self): + paths = self.paths + + if self.dummies_in_paths: + self.remove_dummies() + + for cmd in self.commandlist: + for key in cmd.keys: + path = [] + for path in self.keypath(key): + try: paths[path] + except KeyError: + paths[path] = self.dummy_object + + self.dummies_in_paths = True + + def keypath(self, tup): + current = [] + all = [] + + for i in range(len(tup) - 1): + current.append(tup[i]) + all.append(tuple(current)) + + return all + + def remove_dummies(self): + for k in tuple(paths.keys()): + if paths[k] == self.dummy_object: del paths[k] + self.dummies_in_paths = False + + + def str_to_tuple(self, obj): + """splits a string into a tuple of integers""" + if type(obj) == tuple: + return obj + elif type(obj) == str: + return tuple(map(ord, list(obj))) + else: + raise TypeError('need a str or a tuple for str_to_tuple') + + def bind(self, fnc, *keys): + keys = tuple(map(self.str_to_tuple, keys)) + cmd = Command(fnc, keys) + cmd.commandlist = self + self.commandlist.append(cmd) + for key in keys: + self.paths[key] = cmd + +class Command(): + def __init__(self, fnc, keys): + self.fnc = fnc + self.keys = keys + self.commandlist = None + +# def rebind(keys): +# self.keys = keys +# self.commandlist.rebuild_paths() + + def execute(self, fm): + self.fnc(fm) + +if __name__ == '__main__': + cl = CommandList() + cl.initialize_commands() + + print(cl.paths) diff --git a/ranger/debug.py b/ranger/debug.py new file mode 100644 index 00000000..12d5d654 --- /dev/null +++ b/ranger/debug.py @@ -0,0 +1,12 @@ +# a module to faciliate debuggin + +LOGFILE = '/tmp/errorlog' + +def log(txt): + f = open(LOGFILE, 'a') + f.write("r1: ") + f.write(str(txt)) + f.write("\n") + f.close() + + diff --git a/ranger/defaultui.py b/ranger/defaultui.py new file mode 100644 index 00000000..70ce75ff --- /dev/null +++ b/ranger/defaultui.py @@ -0,0 +1,39 @@ +import ranger.ui +from ranger.wdisplay import WDisplay +from ranger.wstatusbar import WStatusBar + +class DefaultUI(ranger.ui.UI): + def setup(self): + self.statusbar = WStatusBar(self.win) + self.add_widget(self.statusbar) + + self.displays = [ + WDisplay(self.win, -2), + WDisplay(self.win, -1), + WDisplay(self.win, 0), + WDisplay(self.win, 1) ] + self.displays[2].display_infostring = True + self.displays[2].main_display = True + for disp in self.displays: + self.add_widget(disp) + + RATIO = ( 0.15, 0.15, 0.4, 0.3 ) + + def resize(self): + ranger.ui.UI.resize(self) + y, x = self.win.getmaxyx() + + leftborder = 0 + + i = 0 + for ratio in DefaultUI.RATIO: + wid = int(ratio * x) + try: + self.displays[i].setdim(1, leftborder, y-1, wid - 1) + except KeyError: + pass + leftborder += wid + i += 1 + + self.statusbar.setdim(0, 0, 1, x) + diff --git a/ranger/directory.py b/ranger/directory.py new file mode 100644 index 00000000..392f67bc --- /dev/null +++ b/ranger/directory.py @@ -0,0 +1,74 @@ +import ranger.fsobject +from ranger import file, debug + +class Directory(ranger.fsobject.FSObject): + def __init__(self, path): + ranger.fsobject.FSObject.__init__(self, path) + self.content_loaded = False + self.scheduled = False + self.enterable = False + + self.filenames = None + self.files = None + self.filter = None + self.pointed_index = None + self.pointed_file = None + self.index = None + + def load_content(self): + self.stop_if_frozen() + self.load_if_outdated() + self.content_loaded = True + import os + if self.exists and self.runnable: + basenames = os.listdir(self.path) + mapped = map(lambda name: os.path.join(self.path, name), basenames) + self.filenames = list(mapped) + self.infostring = ' %d' % len(self.filenames) # update the infostring + self.files = [] + for name in self.filenames: + if os.path.isdir(name): + f = Directory(name) + else: + f = file.File(name) + f.load() + self.files.append(f) + else: + self.filenames = None + self.files = None + self.infostring = ranger.fsobject.FSObject.BAD_INFO + + def load_content_once(self): + self.stop_if_frozen() + if not self.content_loaded: + self.load_content() + return True + return False + + def load_content_if_outdated(self): + self.stop_if_frozen() + if self.load_content_once(): return True + + import os + real_mtime = os.stat(self.path).st_mtime + cached_mtime = self.stat.st_mtime + + if real_mtime != cached_mtime: + self.load_content() + return True + return False + + def __len__(self): + if not self.accessible: raise ranger.fsobject.NotLoadedYet() + return len(self.filenames) + + def __getitem__(self, key): + if not self.accessible: raise ranger.fsobject.NotLoadedYet() + return self.files[key] + +if __name__ == '__main__': + d = Directory('.') + d.load_filenames() + print(d.filenames) + print(d[1]) + diff --git a/ranger/directory.rb b/ranger/directory.rb new file mode 100644 index 00000000..5c6e84c1 --- /dev/null +++ b/ranger/directory.rb @@ -0,0 +1,66 @@ +# A Class that contains data about directories +class Directory + class LoadStatus + # @n contains a three bit number: x3x2x1 + # x1: + # 0 = not scheduled + # 1 = scheduled + # x3x2: + # 00 = nothing loaded + # 01 = got the list of files + # 10 = + # 11 = got the list of files and entry objects + def initialize(n = 0) + @n = 0 + end + + def got_files? + # is bit 2 nd 3 == 01 + return n & 2 == 2 + end + + def scheduled? + # is the first bit 1? + return n & 1 == 1 + end + + def got_objects? + return n & 4 == 4 + end + attr_accessor :n + end + + def initialize(path) + @path = path + @status = LoadStatus.new(0) + @files = [] + @sort_time = nil + @mtime = nil +# @width = 1000 + @read = false + @free_space = nil + @empty = true + @scheduled = false + end + + # {{{ Trivial + def inspect + return "" + end + alias to_s inspect + + def size + return @files.size + end + + def not_loaded? + return @level == 0 + end + def file_list_loaded? + return @level >= 1 + end + def ready? + return @level >= 2 + end + # }}} +end diff --git a/ranger/environment.py b/ranger/environment.py new file mode 100644 index 00000000..b5b3888c --- /dev/null +++ b/ranger/environment.py @@ -0,0 +1,73 @@ +import os +from ranger.directory import Directory + +class Vector(): + def __init__(self, x, y): + self.x = x + self.y = y + +class Environment(): + # A collection of data which is relevant for more than + # one class. + def __init__(self, opt): + self.opt = opt + self.path = None + self.pathway = () + self.directories = {} + self.pwd = None # current directory + self.cf = None # current file + self.keybuffer = () + self.copy = None + self.termsize = Vector(80, 24) + + def key_append(self, key): + self.keybuffer += (key, ) + + def key_clear(self): + self.keybuffer = () + + def at_level(self, level): + if level <= 0: + try: + return self.pathway[level - 1] + except IndexError: + return None + else: + return self.cf + + def get_directory(self, path): + import os + path = os.path.abspath(path) + try: + return self.directories[path] + except KeyError: + self.directories[path] = Directory(path) + return self.directories[path] + + def enter_dir(self, path): + # get the absolute path + path = os.path.normpath(os.path.join(self.path, path)) + + self.path = path + self.pwd = self.get_directory(path) + + self.pwd.load_content() + + # build the pathway, a tuple of directory objects which lie + # on the path to the current directory. + if path == '/': + self.pathway = (self.get_directory('/'), ) + else: + pathway = [] + currentpath = '/' + for dir in path.split('/'): + currentpath = os.path.join(currentpath, dir) +# debug.log(currentpath) + pathway.append(self.get_directory(currentpath)) + self.pathway = tuple(pathway) + + # set the current file. + if len(self.pwd) > 0: + self.cf = self.pwd[0] + else: + self.cf = None diff --git a/ranger/file.py b/ranger/file.py new file mode 100644 index 00000000..94c62f9a --- /dev/null +++ b/ranger/file.py @@ -0,0 +1,6 @@ +import ranger.fsobject +class File(ranger.fsobject.FSObject): + pass +# def __init__(self, path): +# fsobject.FSObject.__init__(self, path) + diff --git a/ranger/fm.py b/ranger/fm.py new file mode 100644 index 00000000..d616523e --- /dev/null +++ b/ranger/fm.py @@ -0,0 +1,42 @@ +import sys +#LOGFILE = '/tmp/errorlog' +#f = open(LOGFILE, 'a') +#f.write(str(tuple(sys.path)) + "\n") +#f.close() +#import code.ui, code.debug, code.file, code.directory, code.fstype + + +class FM(): + def __init__(self, environment): + self.env = environment + + def feed(self, path, ui): + self.ui = ui + self.env.path = path + self.env.enter_dir(path) + + def run(self): + import time + while 1: + try: + self.ui.draw() + key = self.ui.get_next_key() + self.ui.press(key, self) + except KeyboardInterrupt: + self.env.key_clear() + time.sleep(0.2) + except: + raise + + def exit(self): + raise SystemExit() + + def move_left(self): + self.env.enter_dir('..') + + def move_right(self): + self.env.enter_dir(self.env.cf.path) + + def move_relative(self): + pass + diff --git a/ranger/fsobject.py b/ranger/fsobject.py new file mode 100644 index 00000000..a21818c0 --- /dev/null +++ b/ranger/fsobject.py @@ -0,0 +1,103 @@ +import ranger.fstype + +class FrozenException(Exception): pass +class NotLoadedYet(Exception): pass + +class FSObject(object): + BAD_INFO = '' + def __init__(self, path): + if type(self) == FSObject: + raise TypeError("FSObject is an abstract class and cannot be initialized.") + from os.path import basename + self.path = path + self.basename = basename(path) + self.exists = False + self.accessible = False + self.marked = False + self.tagged = False + self.frozen = False + self.loaded = False + self.runnable = False + self.islink = False + self.brokenlink = False + self.stat = None + self.infostring = None + self.permissions = None + self.type = ranger.fstype.Unknown + + def __str__(self): + return str(self.path) + + # load() reads useful information about the file from the file system + # and caches it in instance attributes. + def load(self): + self.stop_if_frozen() + self.loaded = True + + import os +# try: + if os.access(self.path, os.F_OK): + self.stat = os.stat(self.path) + self.islink = os.path.islink(self.path) + self.exists = True + self.accessible = True + + if os.path.isdir(self.path): + self.type = ranger.fstype.Directory + try: + self.infostring = ' %d' % len(os.listdir(self.path)) + self.runnable = True + except OSError: + self.infostring = FSObject.BAD_INFO + self.runnable = False + self.accessible = False + elif os.path.isfile(self.path): + self.type = ranger.fstype.File + self.infostring = ' %d' % self.stat.st_size + else: + self.type = ranger.fstype.Unknown + self.infostring = None + + else: +# except OSError: + self.islink = False + self.infostring = None + self.type = ranger.fstype.Nonexistent + self.exists = False + self.runnable = False + self.accessible = False + + def load_once(self): + self.stop_if_frozen() + if not self.loaded: + self.load() + return True + return False + + def load_if_outdated(self): + self.stop_if_frozen() + if self.load_once(): return True + + import os + real_mtime = os.stat(self.path).st_mtime + cached_mtime = self.stat.st_mtime + + if real_mtime != cached_mtime: + self.load() + return True + return False + + def clone(self): + clone = type(self)(self.path) + for key in iter(self.__dict__): + clone.__dict__[key] = self.__dict__[key] + return clone + + def frozen_clone(self): + clone = self.clone() + clone.frozen = True + return clone + + def stop_if_frozen(self): + if self.frozen: raise FrozenException('Cannot modify datastructure while it is frozen') + diff --git a/ranger/fstype.py b/ranger/fstype.py new file mode 100644 index 00000000..4bd0988d --- /dev/null +++ b/ranger/fstype.py @@ -0,0 +1,5 @@ +class File: pass +class Directory: pass +class Nonexistent: pass +class Unknown: pass + diff --git a/ranger/keys.py b/ranger/keys.py new file mode 100644 index 00000000..b710a2aa --- /dev/null +++ b/ranger/keys.py @@ -0,0 +1,15 @@ +def initialize_commands(command_list): + from ranger.fm import FM + + cl = command_list + + # note: the bound function will be called with one parameter, which + # is the FM instance. To use functions with multiple parameters, use: + # lambda fm: myfunction(fm, param1, param2, ...) as the function + + cl.bind(FM.move_left, 'h', 'back') + cl.bind(FM.move_right, 'l', 'forward') + cl.bind(FM.exit, 'q') + + cl.rebuild_paths() + diff --git a/ranger/options.py b/ranger/options.py new file mode 100644 index 00000000..eed79f1e --- /dev/null +++ b/ranger/options.py @@ -0,0 +1,5 @@ +def get(): + return [] + +def dummy(): + return [] diff --git a/ranger/ui.py b/ranger/ui.py new file mode 100644 index 00000000..6bc06b19 --- /dev/null +++ b/ranger/ui.py @@ -0,0 +1,68 @@ +import curses +from ranger.debug import log +class UI(): + def __init__(self, env, commandlist): + self.env = env + self.commandlist = commandlist + + self.widgets = [] + self.win = curses.initscr() + self.win.leaveok(1) + curses.noecho() + curses.halfdelay(3) + + self.setup() + self.resize() + + def setup(self): + pass + + def resize(self): + self.env.termsize = self.win.getmaxyx() + + def add_widget(self, widg): + self.widgets.append(widg) + + def feed_env(self, env): + self.env = env + + def press(self, key, fm): + self.env.key_append(key) + log(self.env.keybuffer) + + try: + cmd = self.commandlist.paths[self.env.keybuffer] + except KeyError: + self.env.key_clear() + return + + if cmd == self.commandlist.dummy_object: + return + + cmd.execute(fm) + self.env.key_clear() + + def exit(self): + curses.nocbreak() + curses.echo() + curses.endwin() + + def draw(self): + from ranger.debug import log + self.win.erase() + for widg in self.widgets: + widg.feed_env(self.env) + widg.draw() + self.win.refresh() + log(tuple(map(str, self.env.pathway))) + +# for i in range(1, len(self.env.pwd)): +# f = self.env.pwd.files[i] +# self.win.addstr(i, 0, f.path) +# if f.infostring: self.win.addstr(i, 50, f.infostring) + + def get_next_key(self): + key = self.win.getch() + curses.flushinp() + return key + diff --git a/ranger/wdisplay.py b/ranger/wdisplay.py new file mode 100644 index 00000000..4bd84044 --- /dev/null +++ b/ranger/wdisplay.py @@ -0,0 +1,51 @@ +import ranger.widget +import curses + +class WDisplay(ranger.widget.Widget): + def __init__(self, win, level): + ranger.widget.Widget.__init__(self,win) + self.level = level + self.main_display = False + self.display_infostring = False + + def feed_env(self, env): + self.target = env.at_level(self.level) + + def draw(self): + from ranger.file import File + from ranger.directory import Directory + + if self.target is None: + pass +# self.win.addnstr(self.y, self.x, "---", self.wid) + elif type(self.target) == File: + self.draw_file() + elif type(self.target) == Directory: + self.draw_directory() + else: + self.win.addnstr(self.y, self.x, "unknown type.", self.wid) + + def draw_file(self): + if not self.target.accessible: + self.win.addnstr(self.y, self.x, "not accessible", self.wid) + return + self.win.addnstr(self.y, self.x, "this is a file.", self.wid) + + def draw_directory(self): + self.target.load_content_once() + if not self.target.accessible: + self.win.addnstr(self.y, self.x, "not accessible", self.wid) + return + for i in range(self.hei): + try: + drawed = self.target[i] + except IndexError: + break + self.win.addnstr(self.y + i, self.x, drawed.basename, self.wid) + if self.display_infostring and drawed.infostring: + info = drawed.infostring + x = self.x + self.wid - 1 - len(info) + if x > self.x: + self.win.addstr(self.y + i, x, str(info) + ' ') + + diff --git a/ranger/widget.py b/ranger/widget.py new file mode 100644 index 00000000..725810ec --- /dev/null +++ b/ranger/widget.py @@ -0,0 +1,43 @@ +import curses + +class OutOfBoundsException(Exception): pass + +class Widget(): + def __init__(self, win): + self.win = win + self.setdim(0, 0, 0, 0) + + def setdim(self, y, x, hei=None, wid=None): + maxy, maxx = self.win.getmaxyx() + wid = wid or maxx - x + hei = hei or maxy - y + if x + wid > maxx and y + hei > maxy: + raise OutOfBoundsException("X and Y out of bounds!") + if x + wid > maxx: + raise OutOfBoundsException("X out of bounds!") + if y + hei > maxy: + raise OutOfBoundsException("Y out of bounds!") + + self.x = x + self.y = y + self.wid = wid + self.hei = hei + + def contains_point(self, y, x): + return (x >= self.x and x < self.x + self.wid) and \ + (y >= self.y and y < self.y + self.hei) + + def feed_env(self): + pass + + def feed(self): + pass + + def click(self): + pass + + def draw(self): + pass + + def destroy(self): + pass diff --git a/ranger/wstatusbar.py b/ranger/wstatusbar.py new file mode 100644 index 00000000..e68fb241 --- /dev/null +++ b/ranger/wstatusbar.py @@ -0,0 +1,12 @@ +import curses +import ranger.widget + +class WStatusBar(ranger.widget.Widget): + def feed_env(self, env): + self.pathway = env.pathway + + def draw(self): + self.win.move(self.y, self.x) + for path in self.pathway: + currentx = self.win.getyx()[1] + self.win.addnstr(path.basename + ' / ', (self.wid - currentx)) diff --git a/test/dirsize_benchmark.py b/test/dirsize_benchmark.py index 38f0bfd7..5784ee80 100644 --- a/test/dirsize_benchmark.py +++ b/test/dirsize_benchmark.py @@ -24,3 +24,5 @@ for key in paths.keys(): for i in range(4): assert Dirsize.__dict__[algo](key) == paths[key] print("algorithm %s: %20s: %f" % (algo, key, time.time() - t)) + +# a !! diff --git a/test/tc_directory.py b/test/tc_directory.py index 88c7a99e..275e3129 100644 --- a/test/tc_directory.py +++ b/test/tc_directory.py @@ -1,17 +1,22 @@ -import unittest -import sys, os, time +if __name__ == '__main__': + from os.path import abspath, join + import sys + sys.path.append(abspath(join(sys.path[0], '..'))) -sys.path.append(os.path.normpath(os.path.join(os.path.dirname(__file__), '../code'))) -import fsobject, file, directory +from ranger import fsobject +from ranger.file import File +from ranger.directory import Directory -TESTDIR = os.path.realpath(os.path.join(os.path.dirname(__file__), 'testdir')) -TESTFILE = os.path.join(TESTDIR, 'testfile5234148') +from os.path import realpath, join, dirname +TESTDIR = realpath(join(dirname(__file__), 'testdir')) +TESTFILE = join(TESTDIR, 'testfile5234148') NONEXISTANT_DIR = '/this/directory/will/most/certainly/not/exist' +import unittest class Test1(unittest.TestCase): def test_initial_condition(self): # Check for the expected initial condition - dir = directory.Directory(TESTDIR) + dir = Directory(TESTDIR) self.assertEqual(dir.path, TESTDIR) self.assertFalse(dir.content_loaded) @@ -21,8 +26,9 @@ class Test1(unittest.TestCase): self.assertRaises(fsobject.NotLoadedYet, dir.__getitem__, 0) def test_after_content_loaded(self): + import os # Check whether the directory has the correct list of filenames. - dir = directory.Directory(TESTDIR) + dir = Directory(TESTDIR) dir.load_content() self.assertTrue(dir.exists) @@ -41,7 +47,7 @@ class Test1(unittest.TestCase): # build a file object for each file in the list assumed_filenames # and find exactly one equivalent in dir.files for name in assumed_filenames: - f = file.File(name) + f = File(name) f.load() equal = 0 for dirfile in dir.files: @@ -50,7 +56,7 @@ class Test1(unittest.TestCase): self.assertEqual(equal, 1) def test_nonexistant_dir(self): - dir = directory.Directory(NONEXISTANT_DIR) + dir = Directory(NONEXISTANT_DIR) dir.load_content() self.assertTrue(dir.content_loaded) @@ -61,7 +67,7 @@ class Test1(unittest.TestCase): self.assertRaises(fsobject.NotLoadedYet, dir.__getitem__, 0) def test_modify_frozen_clone(self): - dir = directory.Directory(TESTDIR) + dir = Directory(TESTDIR) clone = dir.frozen_clone() # assert that their attributes are equal, except for frozen, which @@ -79,6 +85,8 @@ class Test1(unittest.TestCase): self.assertRaises(fsobject.FrozenException, clone.load_content) def test_load_if_outdated(self): + import os + import time # modify the directory. If the time between the last modification # was within the filesystems resolution of mtime, we should have a re-load. @@ -89,7 +97,7 @@ class Test1(unittest.TestCase): def mtime(): return os.stat(TESTDIR).st_mtime - dir = directory.Directory(TESTDIR) + dir = Directory(TESTDIR) dir.load() # If the modification happens to be in the same second as the diff --git a/test4.py b/test4.py new file mode 100644 index 00000000..b2390a03 --- /dev/null +++ b/test4.py @@ -0,0 +1,36 @@ +import random, time + +class DelValue(): + def a(d): + return dict((k, v) for k, v in d.items() if v is not 0) + + def b(d): + for k, v in d.copy().items(): + if v == 0: del d[k] + return d + + def c(d): + for k in tuple(d.keys()): + if d[k] == 0: del d[k] + return d + + def d(d): + for k, v in tuple(d.items()): + if v == 0: del d[k] + return d + + +basedict = {} +for i in range(200): + basedict[i] = random.randint(0, 1) + +expected = DelValue.a(basedict.copy()) + +for algo in ['a', 'b', 'c', 'd']: + copy = basedict.copy() + t = time.time() + for i in range(100): + assert DelValue.__dict__[algo](copy) == expected + print("algorithm %s: %f" % (algo, time.time() - t)) + +# c it is, although b is faster with smaller dictionaries -- cgit 1.4.1-2-gfad0