summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Makefile20
-rw-r--r--README110
-rw-r--r--doc/TODO31
l---------doc/help1
-rw-r--r--doc/ranger.11097
-rw-r--r--doc/ranger.pod992
-rwxr-xr-xranger.py31
-rw-r--r--ranger/api/apps.py19
-rw-r--r--ranger/api/commands.py127
-rw-r--r--ranger/api/keys.py131
-rw-r--r--ranger/colorschemes/__init__.py15
-rw-r--r--ranger/colorschemes/default.py15
-rw-r--r--ranger/container/__init__.py20
-rw-r--r--ranger/container/bookmarks.py2
-rw-r--r--ranger/container/keybuffer.py180
-rw-r--r--ranger/container/keymap.py164
-rw-r--r--ranger/container/settingobject.py5
-rw-r--r--ranger/container/tags.py3
-rw-r--r--ranger/core/actions.py154
-rw-r--r--ranger/core/environment.py12
-rw-r--r--ranger/core/fm.py10
-rw-r--r--ranger/core/helper.py78
-rw-r--r--ranger/core/main.py13
-rw-r--r--ranger/core/runner.py7
-rw-r--r--ranger/core/shared.py3
-rw-r--r--ranger/defaults/apps.py90
-rw-r--r--ranger/defaults/commands.py201
-rw-r--r--ranger/defaults/keys.py439
-rw-r--r--ranger/defaults/options.py10
-rw-r--r--ranger/defaults/rc.conf360
-rw-r--r--ranger/ext/keybinding_parser.py20
-rw-r--r--ranger/ext/keybindings.py111
-rw-r--r--ranger/ext/tree.py136
-rw-r--r--ranger/ext/utfwidth.py113
-rw-r--r--ranger/ext/waitpid_no_intr.py30
-rw-r--r--ranger/ext/widestring.py167
-rw-r--r--ranger/fsobject/__init__.py21
-rw-r--r--ranger/gui/bar.py37
-rw-r--r--ranger/gui/context.py4
-rw-r--r--ranger/gui/defaultui.py136
-rw-r--r--ranger/gui/ui.py166
-rw-r--r--ranger/gui/widgets/__init__.py15
-rw-r--r--ranger/gui/widgets/browsercolumn.py2
-rw-r--r--ranger/gui/widgets/browserview.py101
-rw-r--r--ranger/gui/widgets/console.py57
-rw-r--r--ranger/gui/widgets/pager.py59
-rw-r--r--ranger/gui/widgets/statusbar.py2
-rw-r--r--ranger/gui/widgets/taskview.py25
-rw-r--r--ranger/gui/widgets/titlebar.py4
-rw-r--r--ranger/help/__init__.py47
-rw-r--r--ranger/help/console.py157
-rw-r--r--ranger/help/fileop.py107
-rw-r--r--ranger/help/index.py80
-rw-r--r--ranger/help/invocation.py125
-rw-r--r--ranger/help/movement.py232
-rw-r--r--ranger/help/starting.py121
-rwxr-xr-xsetup.py5
-rwxr-xr-xtest/all_benchmarks.py56
-rwxr-xr-xtest/all_tests.py37
-rw-r--r--test/bm_human_readable.py51
-rw-r--r--test/bm_loader.py174
-rw-r--r--test/tc_ansi.py40
-rw-r--r--test/tc_bookmarks.py92
-rw-r--r--test/tc_colorscheme.py53
-rw-r--r--test/tc_direction.py92
-rw-r--r--test/tc_directory.py124
-rw-r--r--test/tc_displayable.py167
-rw-r--r--test/tc_ext.py150
-rw-r--r--test/tc_history.py82
-rw-r--r--test/tc_human_readable.py51
-rw-r--r--test/tc_keyapi.py45
-rw-r--r--test/tc_loader.py79
-rw-r--r--test/tc_newkeys.py620
-rw-r--r--test/tc_relative_symlink.py47
-rw-r--r--test/tc_signal.py139
-rw-r--r--test/tc_ui.py69
-rw-r--r--test/tc_utfwidth.py46
-rw-r--r--test/testdir/largefile.txt1
l---------test/testdir/symlink1
-rw-r--r--test/testdir/textfile.txt4
-rw-r--r--test/testdir/zerobytes0
-rw-r--r--test/testlib.py43
82 files changed, 3262 insertions, 5421 deletions
diff --git a/Makefile b/Makefile
index fd525721..6c5ed88c 100644
--- a/Makefile
+++ b/Makefile
@@ -23,7 +23,6 @@ SETUPOPTS ?= '--record=install_log.txt'
 DOCDIR ?= doc/pydoc
 DESTDIR ?= /
 PYOPTIMIZE ?= 1
-BMCOUNT ?= 5  # how often to run the benchmarks?
 
 CWD = $(shell pwd)
 
@@ -43,7 +42,6 @@ help:
 	@echo 'make clean: Remove the compiled files (*.pyc, *.pyo)'
 	@echo 'make cleandoc: Remove the pydoc documentation'
 	@echo 'make snapshot: Create a tar.gz of the current git revision'
-	@echo 'make test: Run all unittests.'
 
 install:
 	$(PYTHON) setup.py install $(SETUPOPTS) \
@@ -53,7 +51,8 @@ compile: clean
 	PYTHONOPTIMIZE=$(PYOPTIMIZE) $(PYTHON) -m compileall -q ranger
 
 clean:
-	find . -regex .\*.py[co]\$$ -exec rm -f -- {} \;
+	find ranger -regex .\*.py[co]\$$ -delete
+	find ranger -depth -name __pycache__ -type d -exec rm -rf -- {} \;
 
 doc: cleandoc
 	mkdir -p $(DOCDIR)
@@ -63,16 +62,17 @@ doc: cleandoc
 		pydoc.writedocs("$(CWD)")'
 	find . -name \*.html -exec sed -i 's|'$(CWD)'|../..|g' -- {} \;
 
-cleandoc:
-	test -d $(DOCDIR) && rm -f -- $(DOCDIR)/*.html || true
+man:
+	pod2man --stderr --center='ranger manual' --date='$(NAME)-$(VERSION)' \
+		--release=$(shell date +%x) doc/ranger.pod doc/ranger.1
 
-test:
-	@$(PYTHON) test/all_tests.py 1
+manhtml:
+	pod2html doc/ranger.pod --outfile=doc/ranger.1.html
 
-bm:
-	@$(PYTHON) test/all_benchmarks.py $(BMCOUNT)
+cleandoc:
+	test -d $(DOCDIR) && rm -f -- $(DOCDIR)/*.html || true
 
 snapshot:
 	git archive --prefix='$(NAME)-$(VERSION)/' --format=tar HEAD | gzip > $(SNAPSHOT_NAME)
 
-.PHONY: default options compile clean doc cleandoc test bm snapshot install
+.PHONY: default options compile clean doc cleandoc snapshot install man
diff --git a/README b/README
index e11a7b99..94aa19c6 100644
--- a/README
+++ b/README
@@ -14,6 +14,25 @@ The program is written in Python (2.6 or 3.1) and uses curses for the
 text-based user interface.
 
 
+Getting Started
+---------------
+
+Ranger can be started without installing:  Just run ranger.py.  If you want to
+install it anyway, the INSTALL file contains instructions.
+
+After starting ranger, you can use the Arrow Keys (or hjkl) to navigate, Enter
+to open a file or type Q to quit.  The column on the right shows a preview of
+the current file.  The second from the right is the main column and the others
+show parent directories.
+
+The manual page of ranger contains a documentation of rangers functions,
+keybindings, commands and options.  You can access it by typing "?" in ranger,
+running "man ranger" or view it online.
+
+An easy way to customize ranger is running "ranger --copy-config=all"
+and editing the new files in ~/.config/ranger/.
+
+
 About
 -----
 
@@ -46,6 +65,7 @@ Features
 * Multi-column display (Miller Columns)
 * Preview of the selected file/directory
 * Common file operations (create/chmod/copy/delete/...)
+* Renaming multiple files at once
 * VIM-like console and hotkeys
 * Automatically determine file types and run them with correct programs
 * Change the directory of your shell after exiting ranger
@@ -55,8 +75,8 @@ Features
 Dependencies
 ------------
 
-* A *nix-like operating system
-* Python 2.6 or Python 3.1 with the curses module
+* Python 2.6, 2.7 or Python 3.1 with the curses module
+(Later versions might work too, but 2.5 definitely won't.)
 
 Optional:
 * The "file" program
@@ -68,89 +88,3 @@ For scope.sh: (enhanced file previews)
 * highlight for syntax highlighting of code
 * atool for previews of archives
 * lynx or elinks for previews of html pages
-
-
-Getting Started
----------------
-
-Ranger can be started without installing.  Just run the executable (in
-a terminal.)  The switch "--clean" will prevent it from creating or
-accessing configuration files.
-
-Follow the instructions in the INSTALL file for installing ranger.
-
-After starting ranger, you should see 4 columns. The third one is the main
-column, the directory where you're currently at.  To the left you see the
-parent directories and to the right there's a preview of the object you're
-pointing at.  Now use the Arrow Keys to navigate, Enter to open a file
-or type Q to quit.
-
-To customize ranger, copy the files from ranger/defaults/ to ~/.config/ranger/
-and modify them according to your wishes.
-
-
-Combine Ranger With Other Applications
---------------------------------------
-
-1. bash:
-
-Add this to your ~/.bashrc to use ranger as a directory switcher:
-
-function ranger-cd {
-  ranger --choosedir=/tmp/chosen
-  if [ -f /tmp/chosen -a "$(cat /tmp/chosen)" != "$(pwd | tr -d "\n")" ]; then
-    cd "$(cat /tmp/chosen)"
-  fi
-  rm -f /tmp/chosen
-}
-bind '"\C-o":"ranger-cd\C-m"'
-
-Now when you run ranger-cd, browse and quit, the directory of the bash process
-you started ranger in will change to the last directroy in ranger.
-To change back to the previous directory, you can type: cd -
-Also, the line with "bind" will map the key <CTRL-O> to start ranger.
-
-2. vim:
-
-Add this function to your ~/.vimrc:
-
-fun Ranger()
-  silent !ranger --choosefile=/tmp/chosen
-  if filereadable('/tmp/chosen')
-    exec 'edit ' . system('cat /tmp/chosen')
-    call system('rm /tmp/chosen')
-  endif
-  redraw!
-endfun
-map <leader>r :call Ranger()
-
-This starts ranger when you type <leader>r (usually \r) and if you open a file
-in ranger it will be opened in the original vim process.
-
-
-Troubleshooting, Getting Help
------------------------------
-
-If you encounter an error, try running ranger with --debug.  This will
-sometimes display more detailed information about the error.  Also, try
-deactivating optimization:
-
-PYTHONOPTIMIZE="" ranger --debug
-
-Report bugs on savannah:  (please include as much information as possible)
-http://savannah.nongnu.org/bugs/?func=additem&group=ranger
-
-Ask questions on the mailing list:
-http://lists.nongnu.org/mailman/listinfo/ranger-users
-
-
-Further Reading
----------------
-
-Check the man page for information on common features and hotkeys.
-
-The most detailed manual is accessible by pressing "?" from inside ranger.
-It is also available at ranger/help/, contained in the *.py files.
-
-The file ranger/defaults/keys.py contains all key combinations, so that's
-another place you may want to check out.
diff --git a/doc/TODO b/doc/TODO
index 1577f97a..ce1fee6c 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -27,16 +27,18 @@ General
    (X) #32  10/01/08  place the (hidden) cursor to a meaningful position
    (X) #34  10/01/09  display free disk space
    (X) #35  10/01/09  display disk usage of files in current directory
-   ( ) #36  10/01/11  help coloring is terribly inefficient
+   (X) #36  10/01/11  help coloring is terribly inefficient
    (X) #37  10/01/13  better tab completion for OpenConsole
-   ( ) #38  10/01/16  searching in pager
+   (X) #38  10/01/16  searching in pager
+          won't do this
    (X) #39  10/01/17  flushinput not always good
    (X) #42  10/01/17  memorize directory for `` when using :cd
    (X) #43  10/01/18  internally treat the bookmarks ` and ' the same
    ( ) #44  10/01/18  more error messages :P
    (X) #47  10/01/19  less restricive auto preview
    (X) #48  10/01/19  abbreviate commands with first unambiguous substring
-   ( ) #50  10/01/19  add more unit tests
+   (X) #50  10/01/19  add more unit tests
+          won't do this
    ( ) #51  10/01/21  remove directory.marked_items ?
    (X) #55  10/01/24  allow change of filename when pasting
           you're given the choice between overwriting or appending a "_"
@@ -50,14 +52,15 @@ General
    (X) #70  10/03/14  mouse handler for titlebar
    (X) #71  10/03/21  previews: black/whitelist + read file
    (X) #79  10/04/08  tab number zero
-   ( ) #80  10/04/08  when closing tabs, avoid gaps?
+   (X) #80  10/04/08  when closing tabs, avoid gaps?
+          won't do this
    (X) #81  10/04/15  system crash when previewing /proc/kcore with root permissions
    (X) #83  10/04/19  better ways to mark files. eg by regexp, filetype,..
-   ( ) #86  10/04/21  narg for move_parent
+   (X) #86  10/04/21  narg for move_parent
    ( ) #60  10/02/05  utf support improvable
-   ( ) #91  10/05/12  in keys.py, fm.move(right=N) should run with mode=N
-   ( ) #92  10/05/14  allow to enter the realpath of a directory
-   ( ) #93  10/05/15  pause after running program
+   (X) #91  10/05/12  in keys.py, fm.move(right=N) should run with mode=N
+   (X) #92  10/05/14  allow to enter the realpath of a directory
+   (X) #93  10/05/15  pause after running program
 
 
 Bugs
@@ -107,13 +110,9 @@ Ideas
    ( ) #75  10/03/28  navigate in history
    (X) #76  10/03/28  save history between sessions
    (X) #77  10/03/28  colorscheme overlay in options.py
-   ( ) #82  10/04/19  :s command for batch renaming
-   ( ) #84  10/04/25  use pygments for syntax highlighting
+   (X) #82  10/04/19  :s command for batch renaming
+          implemented with :bulkrename
+   (X) #84  10/04/25  use pygments for syntax highlighting
+          implemented with scope.sh
    ( ) #89  10/05/10  branch view
 
-
-Blocking
-
-   ( ) #60  10/02/05  utf support improvable
-   (X) #81  10/04/15  system crash when previewing /proc/kcore with root permissions
-
diff --git a/doc/help b/doc/help
deleted file mode 120000
index 09ad0c73..00000000
--- a/doc/help
+++ /dev/null
@@ -1 +0,0 @@
-../ranger/help
\ No newline at end of file
diff --git a/doc/ranger.1 b/doc/ranger.1
index 08776fc7..7bf94b41 100644
--- a/doc/ranger.1
+++ b/doc/ranger.1
@@ -1,235 +1,900 @@
-.TH RANGER 1 ranger-1.4.4
-.SH NAME
-ranger - visual file manager
-.\"-----------------------------------------
-.SH SYNOPSIS
-.B ranger
-.R [OPTIONS] [FILE]
-.\"-----------------------------------------
-.SH DESCRIPTION
-Ranger is a file manager with an ncurses frontend written in Python.
-.P
+.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07)
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings.  \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote.  \*(C+ will
+.\" give a nicer C++.  Capital omega is used to do unbreakable dashes and
+.\" therefore won't be available.  \*(C` and \*(C' expand to `' in nroff,
+.\" nothing in troff, for use with C<>.
+.tr \(*W-
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+.    ds -- \(*W-
+.    ds PI pi
+.    if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.    if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\"  diablo 12 pitch
+.    ds L" ""
+.    ds R" ""
+.    ds C` ""
+.    ds C' ""
+'br\}
+.el\{\
+.    ds -- \|\(em\|
+.    ds PI \(*p
+.    ds L" ``
+.    ds R" ''
+'br\}
+.\"
+.\" Escape single quotes in literal strings from groff's Unicode transform.
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
+.\" entries marked with X<> in POD.  Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.ie \nF \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
+..
+.    nr % 0
+.    rr F
+.\}
+.el \{\
+.    de IX
+..
+.\}
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear.  Run.  Save yourself.  No user-serviceable parts.
+.    \" fudge factors for nroff and troff
+.if n \{\
+.    ds #H 0
+.    ds #V .8m
+.    ds #F .3m
+.    ds #[ \f1
+.    ds #] \fP
+.\}
+.if t \{\
+.    ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+.    ds #V .6m
+.    ds #F 0
+.    ds #[ \&
+.    ds #] \&
+.\}
+.    \" simple accents for nroff and troff
+.if n \{\
+.    ds ' \&
+.    ds ` \&
+.    ds ^ \&
+.    ds , \&
+.    ds ~ ~
+.    ds /
+.\}
+.if t \{\
+.    ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+.    ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+.    ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+.    ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+.    ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+.    ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+.    \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+.    \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+.    \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+.    ds : e
+.    ds 8 ss
+.    ds o a
+.    ds d- d\h'-1'\(ga
+.    ds D- D\h'-1'\(hy
+.    ds th \o'bp'
+.    ds Th \o'LP'
+.    ds ae ae
+.    ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "RANGER 1"
+.TH RANGER 1 "ranger-1.4.3" "10/01/2011" "ranger manual"
+.\" For nroff, turn off justification.  Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.if n .ad l
+.nh
+.SH "NAME"
+ranger \- visual file manager
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+\&\fBranger\fR [\fIoptions\fR] [\fIpath/filename\fR]
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+Ranger is a file manager with an ncurses front-end written in Python.
+.PP
 It is designed to give you a broader overview of the file system by displaying
-previews and backviews, dividing the screen into several columns.
-The keybindings are similar to those of other console programs like
-.BR vim ", " mutt " or " ncmpcpp
-so the usage will be intuitive and efficient.
-.\"-----------------------------------------
-.SH OPTIONS
-.TP
---version
+previews and backviews, dividing the screen into several columns.  The
+key bindings are similar to those of other console programs like \fBvim\fR,
+\&\fBmutt\fR or \fBncmpcpp\fR so the usage will be intuitive and efficient.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+.IP "\fB\-\-verison\fR" 14
+.IX Item "--verison"
 Print the version and exit.
-.TP
--h, --help
+.IP "\fB\-h\fR, \fB\-\-help\fR" 14
+.IX Item "-h, --help"
 Print a list of options and exit.
-.TP
--d, --debug
-Activate the debug mode:  Whenever an error occurs, ranger will exit and
-print a full backtrace.  The default behaviour is to merely print the
-name of the exception in the statusbar/log and to try to keep running.
-.TP
--c, --clean
+.IP "\fB\-d\fR, \fB\-\-debug\fR" 14
+.IX Item "-d, --debug"
+Activate the debug mode: Whenever an error occurs, ranger will exit and print a
+full traceback.  The default behavior is to merely print the name of the
+exception in the statusbar/log and try to keep running.
+.IP "\fB\-c\fR, \fB\-\-clean\fR" 14
+.IX Item "-c, --clean"
 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
---choosefile=\fItargetfile\fR
+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.
+.IP "\fB\-\-choosefile\fR=\fItargetfile\fR" 14
+.IX Item "--choosefile=targetfile"
 Allows you to pick a file with ranger.  This changes the behavior so that when
 you open a file, ranger will exit and write the name of that file into
-\fItargetfile\fR
-.TP
---choosedir=\fItargetfile\fR
+\&\fItargetfile\fR.
+.IP "\fB\-\-choosedir\fR=\fItargetfile\fR" 14
+.IX Item "--choosedir=targetfile"
 Allows you to pick a directory with ranger.  When you exit ranger, it will
-write the last visited directory into \fItargetfile\fR
-.TP
---copy-config=\fIwhich\fR
+write the last visited directory into \fItargetfile\fR.
+.IP "\fB\-\-copy\-config\fR=\fIfile\fR" 14
+.IX Item "--copy-config=file"
 Create copies of the default configuration files in your local configuration
-directory.  Existing ones will not be overwritten.  Possible values:
-all, apps, commands, keys, options, scope.
-.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.
-.TP
--r \fIdir\fR, --confdir=\fIdir\fR
-Define a different configuration directory.  The default is
-$XDG_CONFIG_HOME/ranger (which defaults to ~/.config/ranger)
-.TP
--m \fIn\fR, --mode=\fIn\fR
-When a filename is supplied, make it run in mode \fIn\fR. Check the
-documentation for more information on modes.
-.TP
--f \fIflags\fR, --flags=\fIflags\fR
-When a filename is supplied, make it run with the flags \fIflags\fR. Check the
-documentation for more information on flags.
-.\"-----------------------------------------
-.SH USAGE
-.\"-----------------------------------------
-.SS General Keybindings
-Many keybindings take an additional numeric argument.  Type \fI5j\fR to move
-down 5 lines, \fI10<Space>\fR to mark 10 files or \fI3?\fR to read the
-third chapter of the documentation.
-.TP
-h, j, k, l
-Move left, down, up, right
-.TP
-^D or J, ^U or K
+directory.  Existing ones will not be overwritten.  Possible values: \fIall\fR,
+\&\fIapps\fR, \fIcommands\fR, \fIkeys\fR, \fIoptions\fR, \fIscope\fR.
+.IP "\fB\-\-list\-unused\-keys\fR" 14
+.IX Item "--list-unused-keys"
+List common keys which are not bound to any action in the \*(L"browser\*(R" context.
+This list is not complete, you can bind any key that is supported by curses:
+use the key code returned by \f(CW\*(C`getch()\*(C'\fR.
+.IP "\fB\-\-fail\-unless\-c\fRd" 14
+.IX Item "--fail-unless-cd"
+Return the exit code 1 if ranger is used to run a file instead of used for file
+browsing. (For example, \*(L"ranger \-\-fail\-unless\-cd test.txt\*(R" returns 1.)
+.IP "\fB\-m\fR \fIn\fR, \fB\-\-mode\fR=\fIn\fR" 14
+.IX Item "-m n, --mode=n"
+When a filename is supplied, run it in mode \fIn\fR.  This has no effect unless
+the execution of this file type is explicitly handled in the configuration.
+.IP "\fB\-f\fR \fIflags\fR, \fB\-\-flags\fR=\fIflags\fR" 14
+.IX Item "-f flags, --flags=flags"
+When a filename is supplied, run it with the given \fIflags\fR to modify
+behavior.  The execution of this file type is explicitly handled in the
+configuration.
+.SH "CONCEPTS"
+.IX Header "CONCEPTS"
+.SS "\s-1TAGS\s0"
+.IX Subsection "TAGS"
+Tags are single characters which are displayed left of a filename.  You can use
+tags however you want.  Press \*(L"t\*(R" to toggle tags and \*(L"T\*(R" to remove any tags of
+the selection. The default tag is an Asterisk (\*(L"*\*(R"), but you can use any tag by
+typing \fI"<tagname>\fR.
+.SS "\s-1PREVIEWS\s0"
+.IX Subsection "PREVIEWS"
+By default, only text files are previewed, but you can enable external preview
+scripts by creating \fI~/.config/ranger/scope.sh\fR (see preview_script option.)
+This script will then be executed each time you attempt to preview a file.
+.PP
+Fetch the default scope.sh (from \fIranger/data/scope.sh\fR) by running
+\&\f(CW\*(C`ranger \-\-copy\-config=scope\*(C'\fR
+.PP
+This default script contains more documentation and calls to the programs
+\&\fIlynx\fR and \fIelinks\fR for html, \fIhighlight\fR for text/code, \fIimg2txt\fR for
+images, \fIatool\fR for archives, \fIpdftotext\fR for PDFs and \fImediainfo\fR for video
+and audio
+files.
+.PP
+Install these programs (just the ones you need) and scope.sh will automatically
+use them.  Make sure to also have the options \*(L"use_preview_script\*(R" and
+\&\*(L"preview_files\*(R" turned on.
+.SS "\s-1SELECTION\s0"
+.IX Subsection "SELECTION"
+The \fIselection\fR is defined as \*(L"All marked files \s-1IF\s0 \s-1THERE\s0 \s-1ARE\s0 \s-1ANY\s0, otherwise
+the current file.\*(R"  Be aware of this when using the :delete command, which
+deletes all files in the selection.
+.PP
+You can mark files by pressing <Space>, v, etc.  A yellow \fBMrk\fR symbol at the
+bottom right indicates that there are marked files in this directory.
+.SS "\s-1MACROS\s0"
+.IX Subsection "MACROS"
+Macros can be used in commands to abbreviate things.
+.PP
+.Vb 5
+\& %f   the highlighted file
+\& %d   the path of the current directory
+\& %s   the selected files in the current directory.
+\& %t   all tagged files in the current directory
+\& %c   the full paths of the currently copied/cut files
+.Ve
+.PP
+The macros \f(CW%f\fR, \f(CW%d\fR and \f(CW%s\fR also have upper case variants, \f(CW%F\fR, \f(CW%D\fR and \f(CW%S\fR,
+which refer to the next tab.  To refer to specific tabs, add a number in
+between.  (%7s = selection of the seventh tab.)
+.PP
+\&\f(CW%c\fR is the only macro which ranges out of the current directory. So you may
+\&\*(L"abuse\*(R" the copying function for other purposes, like diffing two files which
+are in different directories:
+.PP
+.Vb 2
+\& Yank the file A (type yy), move to the file B, then type
+\& @diff %c %f
+.Ve
+.PP
+Macros for file paths are generally shell-escaped so they can be used in the
+:shell command.
+.SS "\s-1BOOKMARKS\s0"
+.IX Subsection "BOOKMARKS"
+Type \fBm<key>\fR to bookmark the current directory. You can re-enter this
+directory by typing \fB`<key>\fR. <key> can be any letter or digit.  Unlike vim,
+both lowercase and uppercase bookmarks are persistent.
+.PP
+Each time you jump to a bookmark, the special bookmark at key ` will be set
+to the last directory. So typing \*(L"``\*(R" gets you back to where you were before.
+.PP
+Bookmarks are selectable when tabbing in the :cd command.
+.PP
+Note: The bookmarks ' (Apostrophe) and ` (Backtick) are the same.
+.SS "\s-1FLAGS\s0"
+.IX Subsection "FLAGS"
+Flags give you a way to modify the behaviour of the spawned process.  They are
+used in the commands :open_with (key \*(L"r\*(R") and :shell (key \*(L"!\*(R").
+.PP
+.Vb 5
+\& s   Silent mode.  Output will be discarded.
+\& d   Detach the process.  (Run in background)
+\& p   Redirect output to the pager
+\& w   Wait for an Enter\-press when the process is done
+\& c   Run the current file only, instead of the selection
+.Ve
+.PP
+By default, all the flags are off unless specified otherwise in the \fIapps.py\fR
+configuration file.  You can specify as many flags as you want.  An uppercase
+flag negates the effect: \*(L"ddcccDs\*(R" is equivalent to \*(L"cs\*(R".
+.PP
+Examples: \f(CW\*(C`:open_with p\*(C'\fR will pipe the output of that process into
+the pager.  \f(CW\*(C`:shell \-w df\*(C'\fR will run \*(L"df\*(R" and wait for you to press Enter before
+switching back to ranger.
+.SS "\s-1MODES\s0"
+.IX Subsection "MODES"
+By specifying a mode (a positive integer), you can tell ranger what to do with
+a file when running it. You can specify which mode to use by typing <mode>l or
+<mode><Enter> or :open_with <mode>.  The default mode is 0.
+.PP
+Examples: \f(CW\*(C`l\*(C'\fR (mode zero) to list the contents of an archive, \f(CW\*(C`1l\*(C'\fR (mode one)
+to extract an archive.  See the \fIapps.py\fR configuration file for all programs
+and modes.
+.SH "KEY BINDINGS"
+.IX Header "KEY BINDINGS"
+Key bindings are defined in the file \fIranger/defaults/rc.conf\fR.  Check this
+file for a list of all key bindings.  You can copy it to your local
+configuration directory with the \-\-copy\-config=rc option.
+.PP
+Many key bindings take an additional numeric argument.  Type \fI5j\fR to move
+down 5 lines, \fI2l\fR to open a file in mode 2, \fI10<Space>\fR to mark 10 files.
+.PP
+This list contains the most useful bindings:
+.SS "\s-1MAIN\s0 \s-1BINDINGS\s0"
+.IX Subsection "MAIN BINDINGS"
+.IP "h, j, k, l" 14
+.IX Item "h, j, k, l"
+Move left, down, up or right
+.IP "^D or J, ^U or K" 14
+.IX Item "^D or J, ^U or K"
 Move a half page down, up
-.TP
-H, L
+.IP "H, L" 14
+.IX Item "H, L"
 Move back and forward in the history
-.TP
-gg
+.IP "gg" 14
+.IX Item "gg"
 Move to the top
-.TP
-G
+.IP "G" 14
+.IX Item "G"
 Move to the bottom
-.TP
-^R
+.IP "^R" 14
+.IX Item "^R"
 Reload everything
-.TP
-^L
+.IP "^L" 14
+.IX Item "^L"
 Redraw the screen
-.TP
-S
+.IP "S" 14
+.IX Item "S"
 Open a shell in the current directory
-.TP
-yy
-Yank the selection to the "copy" buffer and mark them as to be copied
-.TP
-dd
-Cut the selection to the "copy" buffer and mark them as to be moved
-.TP
-pp
-Paste the files from the "copy" buffer here (by moving or copying, depending
-on how they are marked.) By default, this will not overwrite existing files.
-To overwrite them, use \fBpo\fR.
-.TP
-m\fIX\fR
+.IP "?" 14
+Opens this man page
+.IP "yy" 14
+.IX Item "yy"
+Yank the selection to the \*(L"copy\*(R" buffer and mark them as to be copied
+.IP "dd" 14
+.IX Item "dd"
+Cut the selection to the \*(L"copy\*(R" buffer and mark them as to be moved
+.IP "pp" 14
+.IX Item "pp"
+Paste the files from the \*(L"copy\*(R" buffer here (by moving or copying, depending on
+how they are marked.) By default, this will not overwrite existing files.  To
+overwrite them, use \fIpo\fR.
+.IP "m\fIX\fR" 14
+.IX Item "mX"
 Create a bookmark with the name \fIX\fR
-.TP
-`\fIX\fR
+.IP "`\fIX\fR" 14
+.IX Item "`X"
 Move to the bookmark with the name \fIX\fR
-.TP
-n, N
-Find the next file, the previous file.  You can define what to look for
-by typing c\fIX\fR.  If nothing is specified, pressing n will get you to
-the newest file in the directory.
-.TP
-o\fIX\fR
+.IP "n, N" 14
+.IX Item "n, N"
+Find the next file.  By default, this gets you to the newest file in the
+directory, but if you search something using the keys /, cm, ct, ..., it will
+get you to the next found entry.
+.IP "N" 14
+.IX Item "N"
+Find the previous file.
+.IP "o\fIX\fR" 14
+.IX Item "oX"
 Change the sort method (like in mutt)
-.TP
-z\fIX\fR
-Change settings
-.TP
-f
-Quickly navigate by entering a part of the filename
-.TP
-Space
-Mark a file
-.TP
-v, V
-Toggle the mark-status of all files, unmark all files
-.TP
-/
-Open the search console
-.TP
-:
-Open the command console
-.TP
-?
-Opens the help screen with more keybindings and documentation
-.\"-----------------------------------------
-.SS Keybindings for using Tabs
-Tabs are used to work in different directories in the same Ranger instance.
-.TP
-g\fIN\fR
-Open a tab. N has to be a number from 0 to 9. If the tab doesn't exist yet,
-it will be created.
-.TP
-gn, ^N
+.IP "z\fIX\fR" 14
+.IX Item "zX"
+Change settings.  See the settings section for a list of settings and their
+hotkey.
+.IP "f" 14
+.IX Item "f"
+Quickly navigate by entering a part of the filename.
+.IP "Space" 14
+.IX Item "Space"
+Mark a file.
+.IP "v" 14
+.IX Item "v"
+Toggle the mark-status of all files, unmark all files.
+.IP "V, uv" 14
+.IX Item "V, uv"
+Unmark all files
+.IP "/" 14
+Search for files in the current directory.
+.IP ":" 14
+Open the console.
+.IP "Alt\-\fIN\fR" 14
+.IX Item "Alt-N"
+Open a tab. N has to be a number from 0 to 9. If the tab doesn't exist yet, it
+will be created.
+.IP "gn, ^N" 14
+.IX Item "gn, ^N"
 Create a new tab.
-.TP
-gt, gT
-Go to the next or previous tab.  You can also use TAB and SHIFT+TAB.
-.TP
-gc, ^W
-Close the current tab.  The last tab cannot be closed.
-.P
-.\"-----------------------------------------
-.SS Mouse Usage
-.TP
-Left Mouse Button
-Click on something and you'll move there.
-To run a file, "enter" it, like a directory, by clicking on the preview.
-.TP
-Right Mouse Button
-Enter a directory
-.TP
-Scroll Wheel
-Scroll
-.\"-----------------------------------------
-.SS Commands
-.TP
-:delete
-Destroy all files in the selection with a roundhouse kick.  Ranger will
-ask for a confirmation if you attempt to delete multiple (marked) files or
-non-empty directories.
-.TP
-:rename \fInewname\fR
-Rename the current file.  Also try the keybinding A for appending something
-to a file name.
-.TP
-:quit
-Quit ranger.  The current directory will be bookmarked as ' so you can
-re-enter it by typing `` or '' the next time you start ranger.
-.\"-----------------------------------------
-.SH TIPS
-.SS
-Change the directory after exit
-A script like this in your bashrc would make you change the directory
-of your parent shell after exiting ranger:
-.nf
-
-ranger() {
-    command ranger --fail-unless-cd $@ &&
-    cd "$(grep \\^\\' ~/.config/ranger/bookmarks | cut -b3-)"
-}
-.\"-----------------------------------------
-.SH CONFIGURATION
-The files in
-.B ranger/defaults/
-can be copied into your configuration directory (by default, this is
-~/.config/ranger) and customized according to your wishes.
-Most files don't have to be copied completely though: Just define those
-settings you want to add or change and they will override the defauls.
-Colorschemes can be placed in ~/.config/ranger/colorschemes.
-.P
-All configuration is done in Python.
-Each configuration file should contain sufficient documentation.
-.\"-----------------------------------------
-.SH COPYRIGHT
-Copyright \(co
-2009, 2010
-Roman Zimbelmann
-.P
-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.
-
-There is NO warranty;
-not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-.\"-----------------------------------------
-.SH SEE ALSO
-The project page:
-.RB < http://ranger.nongnu.org/ >
-.P
-The mailing list:
-.RB < http://savannah.nongnu.org/mail/?group=ranger >
-.\"-----------------------------------------
-.SH BUGS
-Please report them here and include as much relevant information
-as possible:
-.P
-.RB < http://savannah.nongnu.org/bugs/?group=ranger >
+.IP "gt, gT" 14
+.IX Item "gt, gT"
+Go to the next or previous tab. You can also use \s-1TAB\s0 and \s-1SHIFT+TAB\s0 instead.
+.IP "gc, ^W" 14
+.IX Item "gc, ^W"
+Close the current tab.  The last tab cannot be closed this way.
+.SS "\s-1MIDNIGHT\s0 COMMANDER-LIKE \s-1BINDINGS\s0"
+.IX Subsection "MIDNIGHT COMMANDER-LIKE BINDINGS"
+.IP "<F1>" 14
+.IX Item "<F1>"
+Display Help.
+.IP "<F3>" 14
+.IX Item "<F3>"
+Display the file.
+.IP "<F4>" 14
+.IX Item "<F4>"
+Edit the file.
+.IP "<F5>" 14
+.IX Item "<F5>"
+Copy the file.
+.IP "<F6>" 14
+.IX Item "<F6>"
+Cut the file.
+.IP "<F7>" 14
+.IX Item "<F7>"
+Open the console with \*(L":mkdir \*(R".
+.IP "<F8>" 14
+.IX Item "<F8>"
+Prompt for deletion of the selected files.
+.IP "<F10>" 14
+.IX Item "<F10>"
+Exit ranger.
+.SS "READLINE-LIKE \s-1BINDINGS\s0 \s-1IN\s0 \s-1THE\s0 \s-1CONSOLE\s0"
+.IX Subsection "READLINE-LIKE BINDINGS IN THE CONSOLE"
+.IP "^B, ^F" 14
+.IX Item "^B, ^F"
+Move left and right (B for back, F for forward)
+.IP "^P, ^N" 14
+.IX Item "^P, ^N"
+Move up and down (P for previous, N for Next)
+.IP "^A, ^E" 14
+.IX Item "^A, ^E"
+Move to the start or to the end
+.IP "^D" 14
+.IX Item "^D"
+Delete the current character.
+.IP "^H" 14
+.IX Item "^H"
+Backspace.
+.SH "MOUSE BUTTONS"
+.IX Header "MOUSE BUTTONS"
+.IP "Left Mouse Button" 4
+.IX Item "Left Mouse Button"
+Click on something and you'll move there.  To run a file, \*(L"enter\*(R" it, like a
+directory, by clicking on the preview.
+.IP "Right Mouse Button" 4
+.IX Item "Right Mouse Button"
+Enter a directory or run a file.
+.IP "Scroll Wheel" 4
+.IX Item "Scroll Wheel"
+Scrolls up or down.  You can point at the column of the parent directory to
+switch directories.
+.SH "SETTINGS"
+.IX Header "SETTINGS"
+This section lists all built-in settings of ranger.  The valid types for the
+value are in [brackets].  The hotkey to toggle the setting is in <brokets>, if
+a hotkey exists.
+.PP
+Settings can be changed in the file \fI~/.config/ranger/options.py\fR or on the
+fly with the command \fB:set option value\fR.  Examples:
+ :set column_ratios (1,2,3)
+ :set show_hidden=True
+.IP "autosave_bookmarks [bool]" 4
+.IX Item "autosave_bookmarks [bool]"
+Save bookmarks (used with mX and `X) instantly?  This helps to synchronize
+bookmarks between multiple ranger instances but leads to *slight* performance
+loss.  When false, bookmarks are saved when ranger is exited.
+.IP "collapse_preview [bool] <zc>" 4
+.IX Item "collapse_preview [bool] <zc>"
+When no preview is visible, should the last column be squeezed to make use of
+the whitespace?
+.IP "colorscheme_overlay [function, None]" 4
+.IX Item "colorscheme_overlay [function, None]"
+An overlay function for colorschemes.  See the default options.py for an
+explanation and an example.
+.IP "colorscheme [string]" 4
+.IX Item "colorscheme [string]"
+Which colorscheme to use?  These colorschemes are available by default:
+\&\fBdefault\fR, \fBdefault88\fR, \fBtexas\fR, \fBjungle\fR, \fBsnow\fR. Snow is monochrome,
+texas and default88 use 88 colors.
+.IP "column_ratios [tuple, list]" 4
+.IX Item "column_ratios [tuple, list]"
+How many columns are there, and what are their relative widths?  For example, a
+value of (1, 1, 1) would mean 3 even sized columns. (1, 1, 1, 1, 4) means 5 columns
+with the preview column being as large as the other columns combined.
+.IP "dirname_in_tabs [bool]" 4
+.IX Item "dirname_in_tabs [bool]"
+Display the directory name in tabs?
+.IP "display_size_in_main_column [bool]" 4
+.IX Item "display_size_in_main_column [bool]"
+Display the file size in the main column?
+.IP "display_size_in_status_bar [bool]" 4
+.IX Item "display_size_in_status_bar [bool]"
+Display the file size in the status bar?
+.IP "display_tags_in_all_columns [bool]" 4
+.IX Item "display_tags_in_all_columns [bool]"
+Display tags in all columns?
+.IP "draw_bookmark_borders [bool]" 4
+.IX Item "draw_bookmark_borders [bool]"
+Draw borders around the bookmark window?
+.IP "draw_borders [bool]" 4
+.IX Item "draw_borders [bool]"
+Draw borders around columns?
+.IP "flushinput [bool] <zi>" 4
+.IX Item "flushinput [bool] <zi>"
+Flush the input after each key hit?  One advantage is that when scrolling down
+with \*(L"j\*(R", ranger stops scrolling instantly when you release the key.  One
+disadvantage is that when you type commands blindly, some keys might get lost.
+.IP "hidden_filter [regexp]" 4
+.IX Item "hidden_filter [regexp]"
+A regular expression pattern for files which should be hidden.
+.IP "max_console_history_size [integer, None]" 4
+.IX Item "max_console_history_size [integer, None]"
+How many console commands should be kept in history?
+.IP "max_history_size [integer, None]" 4
+.IX Item "max_history_size [integer, None]"
+How many directory changes should be kept in history?
+.IP "mouse_enabled [bool] <zm>" 4
+.IX Item "mouse_enabled [bool] <zm>"
+Enable mouse input?
+.IP "padding_right [bool]" 4
+.IX Item "padding_right [bool]"
+When collapse_preview is on and there is no preview, should there remain a
+little padding on the right?  This allows you to click into that space to run
+the file.
+.IP "preview_directories [bool] <zP>" 4
+.IX Item "preview_directories [bool] <zP>"
+Preview directories in the preview column?
+.IP "preview_files [bool] <zp>" 4
+.IX Item "preview_files [bool] <zp>"
+Preview files in the preview column?
+.IP "preview_script [string, None]" 4
+.IX Item "preview_script [string, None]"
+Which script should handle generating previews?  If the file doesn't exist, or
+use_preview_script is off, ranger will handle previews itself by just printing
+the content.
+.IP "save_console_history [bool]" 4
+.IX Item "save_console_history [bool]"
+Should the console history be saved on exit?  If disabled, the console history
+is reset when you restart ranger.
+.IP "scroll_offset [integer]" 4
+.IX Item "scroll_offset [integer]"
+Try to keep this much space between the top/bottom border when scrolling.
+.IP "shorten_title [integer, bool]" 4
+.IX Item "shorten_title [integer, bool]"
+Trim the title of the window if it gets long?  The number defines how many
+directories are displayed at once, False turns off this feature.
+.IP "show_cursor [bool]" 4
+.IX Item "show_cursor [bool]"
+Always show the terminal cursor?
+.IP "show_hidden_bookmarks [bool]" 4
+.IX Item "show_hidden_bookmarks [bool]"
+Show dotfiles in the bookmark preview window? (Type ')
+.IP "show_hidden [bool] <zh>, <^H>" 4
+.IX Item "show_hidden [bool] <zh>, <^H>"
+Show hidden files?
+.IP "sort_case_insensitive [bool] <zc>" 4
+.IX Item "sort_case_insensitive [bool] <zc>"
+Sort case-insensitively?  If true, \*(L"a\*(R" will be listed before \*(L"B\*(R" even though
+its \s-1ASCII\s0 value is higher.
+.IP "sort_directories_first [bool] <zd>" 4
+.IX Item "sort_directories_first [bool] <zd>"
+Sort directories first?
+.IP "sort_reverse [bool] <or>" 4
+.IX Item "sort_reverse [bool] <or>"
+Sort reversed?
+.IP "sort [string] <oa>, <ob>, <oc>, <om>, <on>, <ot>, <os>" 4
+.IX Item "sort [string] <oa>, <ob>, <oc>, <om>, <on>, <ot>, <os>"
+Which sorting mechanism should be used?  Choose one of \fBatime\fR, \fBbasename\fR,
+\&\fBctime\fR, \fBmtime\fR, \fBnatural\fR, \fBtype\fR, \fBsize\fR
+.Sp
+Note: You can reverse the order by using an uppercase O in the key combination.
+.IP "tilde_in_titlebar [bool]" 4
+.IX Item "tilde_in_titlebar [bool]"
+Abbreviate \f(CW$HOME\fR with ~ in the title bar (first line) of ranger?
+.IP "unicode_ellipsis [bool]" 4
+.IX Item "unicode_ellipsis [bool]"
+Use a unicode \*(L"...\*(R" character instead of \*(L"~\*(R" to mark cut-off filenames?
+.IP "update_title [bool]" 4
+.IX Item "update_title [bool]"
+Set a window title?
+.IP "use_preview_script [bool] <zv>" 4
+.IX Item "use_preview_script [bool] <zv>"
+Use the preview script defined in the setting \fIpreview_script\fR?
+.IP "xterm_alt_key [bool]" 4
+.IX Item "xterm_alt_key [bool]"
+Enable this if key combinations with the Alt Key don't work for you.
+(Especially on xterm)
+.SH "COMMANDS"
+.IX Header "COMMANDS"
+You can enter the commands in the console which is opened by pressing \*(L":\*(R".
+.PP
+There are additional commands which are directly translated to python
+functions, one for every method in the ranger.core.actions.Actions class.
+They are not documented here, since they are mostly for key bindings, not to be
+typed in by a user.  Read the source if you are interested in them.
+.IP "bulkrename" 2
+.IX Item "bulkrename"
+This command opens a list of selected files in an external editor.  After you
+edit and save the file, it will generate a shell script which does bulk
+renaming according to the changes you did in the file.
+.Sp
+This shell script is opened in an editor for you to review.  After you close
+it, it will be executed.
+.IP "cd [\fIdirectory\fR]" 2
+.IX Item "cd [directory]"
+The cd command changes the directory.  The command \f(CW\*(C`:cd \-\*(C'\fR is equivalent to
+typing ``.
+.IP "chain \fIcommand1\fR[; \fIcommand2\fR[; \fIcommand3\fR...]]" 2
+.IX Item "chain command1[; command2[; command3...]]"
+Combines multiple commands into one, separated by columns.
+.IP "chmod \fIoctal_number\fR" 2
+.IX Item "chmod octal_number"
+Sets the permissions of the selection to the octal number.
+.Sp
+The octal number is between 000 and 777. The digits specify the permissions for
+the user, the group and others.  A 1 permits execution, a 2 permits writing, a
+4 permits reading.  Add those numbers to combine them. So a 7 permits
+everything.
+.Sp
+Key bindings in the form of [\-+]<who><what> and =<octal> also exist.  For
+example, \fB+ar\fR allows reading for everyone, \-ow forbids others to write and
+=777 allows everything.
+.Sp
+See also: man 1 chmod
+.IP "cmap \fIkey\fR \fIcommand\fR" 2
+.IX Item "cmap key command"
+Binds keys for the console. Works like the \f(CW\*(C`map\*(C'\fR command.
+.IP "console [\-p\fIN\fR] \fIcommand\fR" 2
+.IX Item "console [-pN] command"
+Opens the console with the command already typed in.  The cursor is placed at
+\&\fIN\fR.
+.IP "delete [\fIconfirmation\fR]" 2
+.IX Item "delete [confirmation]"
+Destroy all files in the selection with a roundhouse kick.  Ranger will ask for
+a confirmation if you attempt to delete multiple (marked) files or non-empty
+directories.
+.Sp
+When asking for confirmation, this command will only proceed if the last given
+word starts with a `y'.
+.IP "edit [\fIfilename\fR]" 2
+.IX Item "edit [filename]"
+Edit the current file or the file in the argument.
+.IP "eval [\fI\-q\fR] \fIpython_code\fR" 2
+.IX Item "eval [-q] python_code"
+Evaluates the python code.  `fm' is a reference to the \s-1FM\s0 instance.  To display
+text, use the function `p'.  The result is displayed on the screen unless you
+use the \*(L"\-q\*(R" option.
+.Sp
+Examples:
+ :eval fm
+ :eval len(fm.env.directories)
+ :eval p(\*(L"Hello World!\*(R")
+.IP "filter [\fIstring\fR]" 2
+.IX Item "filter [string]"
+Displays only the files which contain the \fIstring\fR in their basename.
+.IP "find \fIpattern\fR" 2
+.IX Item "find pattern"
+Search files in the current directory that match the given (case-insensitive)
+regular expression pattern as you type.  Once there is an unambiguous result,
+it will be run immediately. (Or entered, if it's a directory.)
+.IP "grep \fIpattern\fR" 2
+.IX Item "grep pattern"
+Looks for a string in all marked files or directories.
+.IP "load_copy_buffer" 2
+.IX Item "load_copy_buffer"
+Load the copy buffer from \fI~/.config/ranger/copy_buffer\fR.  This can be used to
+pass the list of copied files to another ranger instance.
+.IP "map \fIkey\fR \fIcommand\fR" 2
+.IX Item "map key command"
+Assign the key combination to the given command.  Whenever you type the
+key/keys, the command will be executed.  Additionally, if you use a quantifier
+when typing the key, like 5j, it will be passed to the command as the attribute
+\&\*(L"self.quantifier\*(R".
+.Sp
+The keys you bind with this command are accessible in the file browser only,
+not in the console, task view or pager.  To bind keys there, use the commands
+\&\*(L"cmap\*(R", \*(L"tmap\*(R" or \*(L"pmap\*(R".
+.IP "mark \fIpattern\fR" 2
+.IX Item "mark pattern"
+Mark all files matching the regular expression pattern.
+.IP "mkdir \fIdirname\fR" 2
+.IX Item "mkdir dirname"
+Creates a directory with the name \fIdirname\fR.
+.IP "open_with [\fIapplication\fR] [\fIflags\fR] [\fImode\fR]" 2
+.IX Item "open_with [application] [flags] [mode]"
+Open the selected files with the given application, unless it is omitted, in
+which case the default application is used.  \fIflags\fR are characters out of
+\&\*(L"sdpcwSDPCW\*(R" and \fImode\fR is any positive integer. Their meanings are discussed
+in their own sections.
+.IP "pmap \fIkey\fR \fIcommand\fR" 2
+.IX Item "pmap key command"
+Binds keys for the pager. Works like the \f(CW\*(C`map\*(C'\fR command.
+.IP "quit" 2
+.IX Item "quit"
+Like quit!, but closes only this tab if multiple tabs are open.
+.IP "quit!" 2
+.IX Item "quit!"
+Quit ranger.  The current directory will be bookmarked as ' so you can re-enter
+it by typing `` or '' the next time you start ranger.
+.IP "rename \fInewname\fR" 2
+.IX Item "rename newname"
+Rename the current file.  If a file with that name already exists, the renaming
+will fail.  Also try the key binding A for appending something to a file name.
+.IP "save_copy_buffer" 2
+.IX Item "save_copy_buffer"
+Save the copy buffer from \fI~/.config/ranger/copy_buffer\fR.  This can be used to
+pass the list of copied files to another ranger instance.
+.IP "search \fIpattern\fR" 2
+.IX Item "search pattern"
+Search files in the current directory that match the given (case insensitive)
+regular expression pattern.
+.IP "search_inc \fIpattern\fR" 2
+.IX Item "search_inc pattern"
+Search files in the current directory that match the given (case insensitive)
+regular expression pattern.  This command gets you to matching files as you
+type.
+.IP "set \fIoption\fR=\fIvalue\fR" 2
+.IX Item "set option=value"
+Assigns a new value to an option.  Valid options are listed in the settings
+section.  Use tab completion to get the current value of an option, though this
+doesn't work for functions and regular expressions. Valid values are:
+.Sp
+.Vb 8
+\& None           None
+\& bool           True or False
+\& integer        0 or 1 or \-1 or 2 etc.
+\& list           [1, 2, 3]
+\& tuple          1, 2, 3 or (1, 2, 3)
+\& function       lambda <arguments>: <expression>
+\& regexp         regexp(\*(Aq<pattern>\*(Aq)
+\& string         Anything
+.Ve
+.IP "shell [\-\fIflags\fR] \fIcommand\fR" 2
+.IX Item "shell [-flags] command"
+Run a shell command.  \fIflags\fR are discussed in their own section.
+.IP "terminal" 2
+.IX Item "terminal"
+Spawns the \fIx\-terminal-emulator\fR starting in the current directory.
+.IP "touch \fIfilename\fR" 2
+.IX Item "touch filename"
+Creates an empty file with the name \fIfilename\fR, unless it already exists.
+.IP "tmap \fIkey\fR \fIcommand\fR" 2
+.IX Item "tmap key command"
+Binds keys for the taskview. Works like the \f(CW\*(C`map\*(C'\fR command.
+.IP "unmark \fIpattern\fR" 2
+.IX Item "unmark pattern"
+Unmark all files matching a regular expression pattern.
+.SH "FILES"
+.IX Header "FILES"
+ranger reads several configuration files which are located in
+\&\fI\f(CI$HOME\fI/.config/ranger\fR or \fI\f(CI$XDG_CONFIG_HOME\fI/ranger\fR if \f(CW$XDG_CONFIG_HOME\fR is
+defined.  The configuration is done mostly in python.  When removing a
+configuration file, remove its compiled version too.  (Python automatically
+compiles modules.  Since python3 they are saved in the _\|_pycache_\|_ directory,
+earlier versions store them with the .pyc extension in the same directory.)
+.PP
+Use the \-\-copy\-config option to obtain the default configuration files.  They
+include further documentation and it's too much to put here.
+.PP
+You don't need to copy the whole file though, most configuration files are
+overlaid on top of the defaults (\fIoptions.py\fR, \fIcommand.py\fR, \fIrc.conf\fR) or
+can be sub-classed (\fIapps.py\fR, \fIcolorschemes\fR).
+.PP
+When starting ranger with the \fB\-\-clean\fR option, it will not access or create
+any of these files.
+.SS "\s-1CONFIGURATION\s0"
+.IX Subsection "CONFIGURATION"
+.IP "apps.py" 10
+.IX Item "apps.py"
+Controls which applications are used to open files.
+.IP "commands.py" 10
+.IX Item "commands.py"
+Defines commands which can be used by typing \*(L":\*(R".
+.IP "rc.conf" 10
+.IX Item "rc.conf"
+Contains a list of commands which are executed on startup.  Mostly key bindings
+are defined here.
+.IP "options.py" 10
+.IX Item "options.py"
+Sets a handful of basic options.
+.IP "scope.sh" 10
+.IX Item "scope.sh"
+This is a script that handles file previews.  When the options
+\&\fIuse_preview_script\fR and \fIpreview_files\fR or, respectively,
+\&\fIpreview_directories\fR are set, the program specified in the option
+\&\fIpreview_script\fR is run and its output and/or exit code determines rangers
+reaction.
+.IP "colorschemes/" 10
+.IX Item "colorschemes/"
+Colorschemes can be placed here.
+.SS "\s-1STORAGE\s0"
+.IX Subsection "STORAGE"
+.IP "bookmarks" 10
+.IX Item "bookmarks"
+This file contains a list of bookmarks.  The syntax is /^(.):(.*)$/. The first
+character is the bookmark key and the rest after the colon is the path to the
+file.  In ranger, bookmarks can be set by typing m<key>, accessed by typing
+\&'<key> and deleted by typing um<key>.
+.IP "copy_buffer" 10
+.IX Item "copy_buffer"
+When running the command :save_copy_buffer, the paths of all currently copied
+files are saved in this file.  You can later run :load_copy_buffer to copy the
+same files again, pass them to another ranger instance or process them in a
+script.
+.IP "history" 10
+.IX Item "history"
+Contains a list of commands that have been previously typed in.
+.IP "tagged" 10
+.IX Item "tagged"
+Contains a list of tagged files. The syntax is /^(.:)?(.*)$/ where the first
+letter is the optional name of the tag and the rest after the optional colon is
+the path to the file.  In ranger, tags can be set by pressing t and removed
+with T.  To assign a named tag, type "<tagname>.
+.SH "ENVIRONMENT"
+.IX Header "ENVIRONMENT"
+These environment variables have an effect on ranger:
+.IP "\s-1EDITOR\s0" 8
+.IX Item "EDITOR"
+Defines the editor to be used for the \*(L"E\*(R" key.  Defaults to the first installed
+program out of \*(L"vim\*(R", \*(L"emacs\*(R" and \*(L"nano\*(R".
+.IP "\s-1SHELL\s0" 8
+.IX Item "SHELL"
+Defines the shell that ranger is going to use with the :shell command and
+the \*(L"S\*(R" key.  Defaults to \*(L"bash\*(R".
+.IP "\s-1XDG_CONFIG_HOME\s0" 8
+.IX Item "XDG_CONFIG_HOME"
+Specifies the directory for configuration files. Defaults to \fI\f(CI$HOME\fI/.config\fR.
+.IP "\s-1PYTHONOPTIMIZE\s0" 8
+.IX Item "PYTHONOPTIMIZE"
+This variable determines the optimize level of python.
+.Sp
+Using PYTHONOPTIMIZE=1 (like python \-O) will make python discard assertion
+statements.  You will gain efficiency at the cost of losing some debug info.
+.Sp
+Using PYTHONOPTIMIZE=2 (like python \-OO) will additionally discard any
+docstrings.  Using this will disable the <F1> key on commands.
+.SH "EXAMPLES"
+.IX Header "EXAMPLES"
+.SS "\s-1VIM:\s0 File Chooser"
+.IX Subsection "VIM: File Chooser"
+This is a vim function which allows you to use ranger to select a file for
+opening in your current vim session.
+.PP
+.Vb 9
+\& fun! RangerChooser()
+\&   silent !ranger \-\-choosefile=/tmp/chosenfile \`[ \-z \*(Aq%\*(Aq ] && echo \-n . || dirname %\`
+\&   if filereadable(\*(Aq/tmp/chosenfile\*(Aq)
+\&     exec \*(Aqedit \*(Aq . system(\*(Aqcat /tmp/chosenfile\*(Aq)
+\&     call system(\*(Aqrm /tmp/chosenfile\*(Aq)
+\&   endif
+\&   redraw!
+\& endfun
+\& map ,r :call RangerChooser()<CR>
+.Ve
+.SS "Bash: cd to last path after exit"
+.IX Subsection "Bash: cd to last path after exit"
+This is a bash function (for \fI~/.bashrc\fR) to change the directory to the last
+visited one after ranger quits.  You can always type \f(CW\*(C`cd \-\*(C'\fR to go back to the
+original one.
+.PP
+.Vb 9
+\& function ranger\-cd {
+\&   tempfile=\*(Aq/tmp/chosendir\*(Aq
+\&   /usr/bin/ranger \-\-choosedir="$tempfile" "${@:\-$(pwd)}"
+\&   test \-f "$tempfile" &&
+\&   if [ "$(cat \-\- "$tempfile")" != "$(echo \-n \`pwd\`)" ]; then
+\&     cd \-\- "$(cat "$tempfile")"
+\&   fi
+\&   rm \-f \-\- "$tempfile"
+\& }
+\&
+\& # This binds Ctrl\-O to ranger\-cd:
+\& bind \*(Aq"\eC\-o":"ranger\-cd\eC\-m"\*(Aq
+.Ve
+.SH "LICENSE"
+.IX Header "LICENSE"
+\&\s-1GNU\s0 General Public License 3 or (at your option) any later version.
+.SH "LINKS"
+.IX Header "LINKS"
+.IP "Download: <http://ranger.nongnu.org/ranger\-stable.tar.gz>" 4
+.IX Item "Download: <http://ranger.nongnu.org/ranger-stable.tar.gz>"
+.PD 0
+.IP "The project page: <http://ranger.nongnu.org/>" 4
+.IX Item "The project page: <http://ranger.nongnu.org/>"
+.IP "The mailing list: <http://savannah.nongnu.org/mail/?group=ranger>" 4
+.IX Item "The mailing list: <http://savannah.nongnu.org/mail/?group=ranger>"
+.PD
+.PP
+ranger is maintained with the git version control system.  To fetch a fresh
+copy, run:
+.PP
+.Vb 1
+\& git clone git://git.savannah.nongnu.org/ranger.git
+.Ve
+.SH "BUGS"
+.IX Header "BUGS"
+Report bugs here: <http://savannah.nongnu.org/bugs/?group=ranger>
+.PP
+Please include as much relevant information as possible.  For the most
+diagnostic output, run ranger like this: \f(CW\*(C`PYTHONOPTIMIZE= ranger \-\-debug\*(C'\fR
diff --git a/doc/ranger.pod b/doc/ranger.pod
new file mode 100644
index 00000000..b8d779c1
--- /dev/null
+++ b/doc/ranger.pod
@@ -0,0 +1,992 @@
+=head1 NAME
+
+ranger - visual file manager
+
+
+
+
+=head1 SYNOPSIS
+
+B<ranger> [I<options>] [I<path/filename>]
+
+
+
+
+=head1 DESCRIPTION
+
+Ranger is a file manager with an ncurses front-end written in Python.
+
+It is designed to give you a broader overview of the file system by displaying
+previews and backviews, dividing the screen into several columns.  The
+key bindings are similar to those of other console programs like B<vim>,
+B<mutt> or B<ncmpcpp> so the usage will be intuitive and efficient.
+
+
+
+
+=head1 OPTIONS
+
+=over 14
+
+=item B<--verison>
+
+Print the version and exit.
+
+=item B<-h>, B<--help>
+
+Print a list of options and exit.
+
+=item B<-d>, B<--debug>
+
+Activate the debug mode: Whenever an error occurs, ranger will exit and print a
+full traceback.  The default behavior is to merely print the name of the
+exception in the statusbar/log and try to keep running.
+
+=item B<-c>, B<--clean>
+
+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.
+
+=item B<--choosefile>=I<targetfile>
+
+Allows you to pick a file with ranger.  This changes the behavior so that when
+you open a file, ranger will exit and write the name of that file into
+I<targetfile>.
+
+=item B<--choosedir>=I<targetfile>
+
+Allows you to pick a directory with ranger.  When you exit ranger, it will
+write the last visited directory into I<targetfile>.
+
+=item B<--copy-config>=I<file>
+
+Create copies of the default configuration files in your local configuration
+directory.  Existing ones will not be overwritten.  Possible values: I<all>,
+I<apps>, I<commands>, I<keys>, I<options>, I<scope>.
+
+=item B<--list-unused-keys>
+
+List common keys which are not bound to any action in the "browser" context.
+This list is not complete, you can bind any key that is supported by curses:
+use the key code returned by C<getch()>.
+
+=item B<--fail-unless-c>d
+
+Return the exit code 1 if ranger is used to run a file instead of used for file
+browsing. (For example, "ranger --fail-unless-cd test.txt" returns 1.)
+
+=item B<-m> I<n>, B<--mode>=I<n>
+
+When a filename is supplied, run it in mode I<n>.  This has no effect unless
+the execution of this file type is explicitly handled in the configuration.
+
+=item B<-f> I<flags>, B<--flags>=I<flags>
+
+When a filename is supplied, run it with the given I<flags> to modify
+behavior.  The execution of this file type is explicitly handled in the
+configuration.
+
+=back
+
+
+
+
+=head1 CONCEPTS
+
+=head2 TAGS
+
+Tags are single characters which are displayed left of a filename.  You can use
+tags however you want.  Press "t" to toggle tags and "T" to remove any tags of
+the selection. The default tag is an Asterisk ("*"), but you can use any tag by
+typing I<"<tagnameE<gt>>.
+
+=head2 PREVIEWS
+
+By default, only text files are previewed, but you can enable external preview
+scripts by creating F<~/.config/ranger/scope.sh> (see preview_script option.)
+This script will then be executed each time you attempt to preview a file.
+
+Fetch the default scope.sh (from F<ranger/data/scope.sh>) by running
+C<ranger --copy-config=scope>
+
+This default script contains more documentation and calls to the programs
+I<lynx> and I<elinks> for html, I<highlight> for text/code, I<img2txt> for
+images, I<atool> for archives, I<pdftotext> for PDFs and I<mediainfo> for video
+and audio
+files.
+
+Install these programs (just the ones you need) and scope.sh will automatically
+use them.  Make sure to also have the options "use_preview_script" and
+"preview_files" turned on.
+
+=head2 SELECTION
+
+The I<selection> is defined as "All marked files IF THERE ARE ANY, otherwise
+the current file."  Be aware of this when using the :delete command, which
+deletes all files in the selection.
+
+You can mark files by pressing <Space>, v, etc.  A yellow B<Mrk> symbol at the
+bottom right indicates that there are marked files in this directory.
+
+=head2 MACROS
+
+Macros can be used in commands to abbreviate things.
+
+ %f   the highlighted file
+ %d   the path of the current directory
+ %s   the selected files in the current directory.
+ %t   all tagged files in the current directory
+ %c   the full paths of the currently copied/cut files
+
+The macros %f, %d and %s also have upper case variants, %F, %D and %S,
+which refer to the next tab.  To refer to specific tabs, add a number in
+between.  (%7s = selection of the seventh tab.)
+
+%c is the only macro which ranges out of the current directory. So you may
+"abuse" the copying function for other purposes, like diffing two files which
+are in different directories:
+
+ Yank the file A (type yy), move to the file B, then type
+ @diff %c %f
+
+Macros for file paths are generally shell-escaped so they can be used in the
+:shell command.
+
+=head2 BOOKMARKS
+
+Type B<m<keyE<gt>> to bookmark the current directory. You can re-enter this
+directory by typing B<`<keyE<gt>>. <key> can be any letter or digit.  Unlike vim,
+both lowercase and uppercase bookmarks are persistent.
+
+Each time you jump to a bookmark, the special bookmark at key ` will be set
+to the last directory. So typing "``" gets you back to where you were before.
+
+Bookmarks are selectable when tabbing in the :cd command.
+
+Note: The bookmarks ' (Apostrophe) and ` (Backtick) are the same.
+
+=head2 FLAGS
+
+Flags give you a way to modify the behaviour of the spawned process.  They are
+used in the commands :open_with (key "r") and :shell (key "!").
+
+ s   Silent mode.  Output will be discarded.
+ d   Detach the process.  (Run in background)
+ p   Redirect output to the pager
+ w   Wait for an Enter-press when the process is done
+ c   Run the current file only, instead of the selection
+
+By default, all the flags are off unless specified otherwise in the F<apps.py>
+configuration file.  You can specify as many flags as you want.  An uppercase
+flag negates the effect: "ddcccDs" is equivalent to "cs".
+
+Examples: C<:open_with p> will pipe the output of that process into
+the pager.  C<:shell -w df> will run "df" and wait for you to press Enter before
+switching back to ranger.
+
+=head2 MODES
+
+By specifying a mode (a positive integer), you can tell ranger what to do with
+a file when running it. You can specify which mode to use by typing <mode>l or
+<mode><Enter> or :open_with <mode>.  The default mode is 0.
+
+Examples: C<l> (mode zero) to list the contents of an archive, C<1l> (mode one)
+to extract an archive.  See the F<apps.py> configuration file for all programs
+and modes.
+
+
+
+
+=head1 KEY BINDINGS
+
+Key bindings are defined in the file F<ranger/defaults/rc.conf>.  Check this
+file for a list of all key bindings.  You can copy it to your local
+configuration directory with the --copy-config=rc option.
+
+Many key bindings take an additional numeric argument.  Type I<5j> to move
+down 5 lines, I<2l> to open a file in mode 2, I<10<SpaceE<gt>> to mark 10 files.
+
+This list contains the most useful bindings:
+
+=head2 MAIN BINDINGS
+
+=over 14
+
+=item h, j, k, l
+
+Move left, down, up or right
+
+=item ^D or J, ^U or K
+
+Move a half page down, up
+
+=item H, L
+
+Move back and forward in the history
+
+=item gg
+
+Move to the top
+
+=item G
+
+Move to the bottom
+
+=item ^R
+
+Reload everything
+
+=item ^L
+
+Redraw the screen
+
+=item S
+
+Open a shell in the current directory
+
+=item ?
+
+Opens this man page
+
+=item yy
+
+Yank the selection to the "copy" buffer and mark them as to be copied
+
+=item dd
+
+Cut the selection to the "copy" buffer and mark them as to be moved
+
+=item pp
+
+Paste the files from the "copy" buffer here (by moving or copying, depending on
+how they are marked.) By default, this will not overwrite existing files.  To
+overwrite them, use I<po>.
+
+=item mI<X>
+
+Create a bookmark with the name I<X>
+
+=item `I<X>
+
+Move to the bookmark with the name I<X>
+
+=item n, N
+
+Find the next file.  By default, this gets you to the newest file in the
+directory, but if you search something using the keys /, cm, ct, ..., it will
+get you to the next found entry.
+
+=item N
+
+Find the previous file.
+
+=item oI<X>
+
+Change the sort method (like in mutt)
+
+=item zI<X>
+
+Change settings.  See the settings section for a list of settings and their
+hotkey.
+
+=item f
+
+Quickly navigate by entering a part of the filename.
+
+=item Space
+
+Mark a file.
+
+=item v
+
+Toggle the mark-status of all files, unmark all files.
+
+=item V, uv
+
+Unmark all files
+
+=item /
+
+Search for files in the current directory.
+
+=item :
+
+Open the console.
+
+
+=item Alt-I<N>
+
+Open a tab. N has to be a number from 0 to 9. If the tab doesn't exist yet, it
+will be created.
+
+=item gn, ^N
+
+Create a new tab.
+
+=item gt, gT
+
+Go to the next or previous tab. You can also use TAB and SHIFT+TAB instead.
+
+=item gc, ^W
+
+Close the current tab.  The last tab cannot be closed this way.
+
+=back
+
+=head2 MIDNIGHT COMMANDER-LIKE BINDINGS
+
+=over 14
+
+=item <F1>
+
+Display Help.
+
+=item <F3>
+
+Display the file.
+
+=item <F4>
+
+Edit the file.
+
+=item <F5>
+
+Copy the file.
+
+=item <F6>
+
+Cut the file.
+
+=item <F7>
+
+Open the console with ":mkdir ".
+
+=item <F8>
+
+Prompt for deletion of the selected files.
+
+=item <F10>
+
+Exit ranger.
+
+=back
+
+=head2 READLINE-LIKE BINDINGS IN THE CONSOLE
+
+=over 14
+
+=item ^B, ^F
+
+Move left and right (B for back, F for forward)
+
+=item ^P, ^N
+
+Move up and down (P for previous, N for Next)
+
+=item ^A, ^E
+
+Move to the start or to the end
+
+=item ^D
+
+Delete the current character.
+
+=item ^H
+
+Backspace.
+
+=back
+
+
+=head1 MOUSE BUTTONS
+
+=over
+
+=item Left Mouse Button
+
+Click on something and you'll move there.  To run a file, "enter" it, like a
+directory, by clicking on the preview.
+
+=item Right Mouse Button
+
+Enter a directory or run a file.
+
+=item Scroll Wheel
+
+Scrolls up or down.  You can point at the column of the parent directory to
+switch directories.
+
+=back
+
+
+
+
+=head1 SETTINGS
+
+This section lists all built-in settings of ranger.  The valid types for the
+value are in [brackets].  The hotkey to toggle the setting is in <brokets>, if
+a hotkey exists.
+
+Settings can be changed in the file F<~/.config/ranger/options.py> or on the
+fly with the command B<:set option value>.  Examples:
+ :set column_ratios (1,2,3)
+ :set show_hidden=True
+
+=over
+
+=item autosave_bookmarks [bool]
+
+Save bookmarks (used with mX and `X) instantly?  This helps to synchronize
+bookmarks between multiple ranger instances but leads to *slight* performance
+loss.  When false, bookmarks are saved when ranger is exited.
+
+=item collapse_preview [bool] <zc>
+
+When no preview is visible, should the last column be squeezed to make use of
+the whitespace?
+
+=item colorscheme_overlay [function, None]
+
+An overlay function for colorschemes.  See the default options.py for an
+explanation and an example.
+
+=item colorscheme [string]
+
+Which colorscheme to use?  These colorschemes are available by default:
+B<default>, B<default88>, B<texas>, B<jungle>, B<snow>. Snow is monochrome,
+texas and default88 use 88 colors.
+
+=item column_ratios [tuple, list]
+
+How many columns are there, and what are their relative widths?  For example, a
+value of (1, 1, 1) would mean 3 even sized columns. (1, 1, 1, 1, 4) means 5 columns
+with the preview column being as large as the other columns combined.
+
+=item dirname_in_tabs [bool]
+
+Display the directory name in tabs?
+
+=item display_size_in_main_column [bool]
+
+Display the file size in the main column?
+
+=item display_size_in_status_bar [bool]
+
+Display the file size in the status bar?
+
+=item display_tags_in_all_columns [bool]
+
+Display tags in all columns?
+
+=item draw_bookmark_borders [bool]
+
+Draw borders around the bookmark window?
+
+=item draw_borders [bool]
+
+Draw borders around columns?
+
+=item flushinput [bool] <zi>
+
+Flush the input after each key hit?  One advantage is that when scrolling down
+with "j", ranger stops scrolling instantly when you release the key.  One
+disadvantage is that when you type commands blindly, some keys might get lost.
+
+=item hidden_filter [regexp]
+
+A regular expression pattern for files which should be hidden.
+
+=item max_console_history_size [integer, None]
+
+How many console commands should be kept in history?
+
+=item max_history_size [integer, None]
+
+How many directory changes should be kept in history?
+
+=item mouse_enabled [bool] <zm>
+
+Enable mouse input?
+
+=item padding_right [bool]
+
+When collapse_preview is on and there is no preview, should there remain a
+little padding on the right?  This allows you to click into that space to run
+the file.
+
+=item preview_directories [bool] <zP>
+
+Preview directories in the preview column?
+
+=item preview_files [bool] <zp>
+
+Preview files in the preview column?
+
+=item preview_script [string, None]
+
+Which script should handle generating previews?  If the file doesn't exist, or
+use_preview_script is off, ranger will handle previews itself by just printing
+the content.
+
+=item save_console_history [bool]
+
+Should the console history be saved on exit?  If disabled, the console history
+is reset when you restart ranger.
+
+=item scroll_offset [integer]
+
+Try to keep this much space between the top/bottom border when scrolling.
+
+=item shorten_title [integer, bool]
+
+Trim the title of the window if it gets long?  The number defines how many
+directories are displayed at once, False turns off this feature.
+
+=item show_cursor [bool]
+
+Always show the terminal cursor?
+
+=item show_hidden_bookmarks [bool]
+
+Show dotfiles in the bookmark preview window? (Type ')
+
+=item show_hidden [bool] <zh>, <^H>
+
+Show hidden files?
+
+=item sort_case_insensitive [bool] <zc>
+
+Sort case-insensitively?  If true, "a" will be listed before "B" even though
+its ASCII value is higher.
+
+=item sort_directories_first [bool] <zd>
+
+Sort directories first?
+
+=item sort_reverse [bool] <or>
+
+Sort reversed?
+
+=item sort [string] <oa>, <ob>, <oc>, <om>, <on>, <ot>, <os>
+
+Which sorting mechanism should be used?  Choose one of B<atime>, B<basename>,
+B<ctime>, B<mtime>, B<natural>, B<type>, B<size>
+
+Note: You can reverse the order by using an uppercase O in the key combination.
+
+=item tilde_in_titlebar [bool]
+
+Abbreviate $HOME with ~ in the title bar (first line) of ranger?
+
+=item unicode_ellipsis [bool]
+
+Use a unicode "..." character instead of "~" to mark cut-off filenames?
+
+=item update_title [bool]
+
+Set a window title?
+
+=item use_preview_script [bool] <zv>
+
+Use the preview script defined in the setting I<preview_script>?
+
+=item xterm_alt_key [bool]
+
+Enable this if key combinations with the Alt Key don't work for you.
+(Especially on xterm)
+
+=back
+
+
+=head1 COMMANDS
+
+You can enter the commands in the console which is opened by pressing ":".
+
+There are additional commands which are directly translated to python
+functions, one for every method in the ranger.core.actions.Actions class.
+They are not documented here, since they are mostly for key bindings, not to be
+typed in by a user.  Read the source if you are interested in them.
+
+=over 2
+
+=item bulkrename
+
+This command opens a list of selected files in an external editor.  After you
+edit and save the file, it will generate a shell script which does bulk
+renaming according to the changes you did in the file.
+
+This shell script is opened in an editor for you to review.  After you close
+it, it will be executed.
+
+=item cd [I<directory>]
+
+The cd command changes the directory.  The command C<:cd -> is equivalent to
+typing ``.
+
+=item chain I<command1>[; I<command2>[; I<command3>...]]
+
+Combines multiple commands into one, separated by columns.
+
+=item chmod I<octal_number>
+
+Sets the permissions of the selection to the octal number.
+
+The octal number is between 000 and 777. The digits specify the permissions for
+the user, the group and others.  A 1 permits execution, a 2 permits writing, a
+4 permits reading.  Add those numbers to combine them. So a 7 permits
+everything.
+
+Key bindings in the form of [-+]<who><what> and =<octal> also exist.  For
+example, B<+ar> allows reading for everyone, -ow forbids others to write and
+=777 allows everything.
+
+See also: man 1 chmod
+
+=item cmap I<key> I<command>
+
+Binds keys for the console. Works like the C<map> command.
+
+=item console [-pI<N>] I<command>
+
+Opens the console with the command already typed in.  The cursor is placed at
+I<N>.
+
+=item delete [I<confirmation>]
+
+Destroy all files in the selection with a roundhouse kick.  Ranger will ask for
+a confirmation if you attempt to delete multiple (marked) files or non-empty
+directories.
+
+When asking for confirmation, this command will only proceed if the last given
+word starts with a `y'.
+
+=item edit [I<filename>]
+
+Edit the current file or the file in the argument.
+
+=item eval [I<-q>] I<python_code>
+
+Evaluates the python code.  `fm' is a reference to the FM instance.  To display
+text, use the function `p'.  The result is displayed on the screen unless you
+use the "-q" option.
+
+Examples:
+ :eval fm
+ :eval len(fm.env.directories)
+ :eval p("Hello World!")
+
+=item filter [I<string>]
+
+Displays only the files which contain the I<string> in their basename.
+
+=item find I<pattern>
+
+Search files in the current directory that match the given (case-insensitive)
+regular expression pattern as you type.  Once there is an unambiguous result,
+it will be run immediately. (Or entered, if it's a directory.)
+
+=item grep I<pattern>
+
+Looks for a string in all marked files or directories.
+
+=item load_copy_buffer
+
+Load the copy buffer from F<~/.config/ranger/copy_buffer>.  This can be used to
+pass the list of copied files to another ranger instance.
+
+=item map I<key> I<command>
+
+Assign the key combination to the given command.  Whenever you type the
+key/keys, the command will be executed.  Additionally, if you use a quantifier
+when typing the key, like 5j, it will be passed to the command as the attribute
+"self.quantifier".
+
+The keys you bind with this command are accessible in the file browser only,
+not in the console, task view or pager.  To bind keys there, use the commands
+"cmap", "tmap" or "pmap".
+
+=item mark I<pattern>
+
+Mark all files matching the regular expression pattern.
+
+=item mkdir I<dirname>
+
+Creates a directory with the name I<dirname>.
+
+=item open_with [I<application>] [I<flags>] [I<mode>]
+
+Open the selected files with the given application, unless it is omitted, in
+which case the default application is used.  I<flags> are characters out of
+"sdpcwSDPCW" and I<mode> is any positive integer. Their meanings are discussed
+in their own sections.
+
+=item pmap I<key> I<command>
+
+Binds keys for the pager. Works like the C<map> command.
+
+=item quit
+
+Like quit!, but closes only this tab if multiple tabs are open.
+
+=item quit!
+
+Quit ranger.  The current directory will be bookmarked as ' so you can re-enter
+it by typing `` or '' the next time you start ranger.
+
+=item rename I<newname>
+
+Rename the current file.  If a file with that name already exists, the renaming
+will fail.  Also try the key binding A for appending something to a file name.
+
+=item save_copy_buffer
+
+Save the copy buffer from I<~/.config/ranger/copy_buffer>.  This can be used to
+pass the list of copied files to another ranger instance.
+
+=item search I<pattern>
+
+Search files in the current directory that match the given (case insensitive)
+regular expression pattern.
+
+=item search_inc I<pattern>
+
+Search files in the current directory that match the given (case insensitive)
+regular expression pattern.  This command gets you to matching files as you
+type.
+
+=item set I<option>=I<value>
+
+Assigns a new value to an option.  Valid options are listed in the settings
+section.  Use tab completion to get the current value of an option, though this
+doesn't work for functions and regular expressions. Valid values are:
+
+ None           None
+ bool           True or False
+ integer        0 or 1 or -1 or 2 etc.
+ list           [1, 2, 3]
+ tuple          1, 2, 3 or (1, 2, 3)
+ function       lambda <arguments>: <expression>
+ regexp         regexp('<pattern>')
+ string         Anything
+
+=item shell [-I<flags>] I<command>
+
+Run a shell command.  I<flags> are discussed in their own section.
+
+=item terminal
+
+Spawns the I<x-terminal-emulator> starting in the current directory.
+
+=item touch I<filename>
+
+Creates an empty file with the name I<filename>, unless it already exists.
+
+=item tmap I<key> I<command>
+
+Binds keys for the taskview. Works like the C<map> command.
+
+=item unmark I<pattern>
+
+Unmark all files matching a regular expression pattern.
+
+=back
+
+
+
+
+=head1 FILES
+
+ranger reads several configuration files which are located in
+F<$HOME/.config/ranger> or F<$XDG_CONFIG_HOME/ranger> if $XDG_CONFIG_HOME is
+defined.  The configuration is done mostly in python.  When removing a
+configuration file, remove its compiled version too.  (Python automatically
+compiles modules.  Since python3 they are saved in the __pycache__ directory,
+earlier versions store them with the .pyc extension in the same directory.)
+
+Use the --copy-config option to obtain the default configuration files.  They
+include further documentation and it's too much to put here.
+
+You don't need to copy the whole file though, most configuration files are
+overlaid on top of the defaults (F<options.py>, F<command.py>, F<rc.conf>) or
+can be sub-classed (F<apps.py>, F<colorschemes>).
+
+When starting ranger with the B<--clean> option, it will not access or create
+any of these files.
+
+=head2 CONFIGURATION
+
+=over 10
+
+=item apps.py
+
+Controls which applications are used to open files.
+
+=item commands.py
+
+Defines commands which can be used by typing ":".
+
+=item rc.conf
+
+Contains a list of commands which are executed on startup.  Mostly key bindings
+are defined here.
+
+=item options.py
+
+Sets a handful of basic options.
+
+=item scope.sh
+
+This is a script that handles file previews.  When the options
+I<use_preview_script> and I<preview_files> or, respectively,
+I<preview_directories> are set, the program specified in the option
+I<preview_script> is run and its output and/or exit code determines rangers
+reaction.
+
+=item colorschemes/
+
+Colorschemes can be placed here.
+
+=back
+
+=head2 STORAGE
+
+=over 10
+
+=item bookmarks
+
+This file contains a list of bookmarks.  The syntax is /^(.):(.*)$/. The first
+character is the bookmark key and the rest after the colon is the path to the
+file.  In ranger, bookmarks can be set by typing m<key>, accessed by typing
+'<key> and deleted by typing um<key>.
+
+=item copy_buffer
+
+When running the command :save_copy_buffer, the paths of all currently copied
+files are saved in this file.  You can later run :load_copy_buffer to copy the
+same files again, pass them to another ranger instance or process them in a
+script.
+
+=item history
+
+Contains a list of commands that have been previously typed in.
+
+=item tagged
+
+Contains a list of tagged files. The syntax is /^(.:)?(.*)$/ where the first
+letter is the optional name of the tag and the rest after the optional colon is
+the path to the file.  In ranger, tags can be set by pressing t and removed
+with T.  To assign a named tag, type "<tagname>.
+
+=back
+
+
+
+
+=head1 ENVIRONMENT
+
+These environment variables have an effect on ranger:
+
+=over 8
+
+=item EDITOR
+
+Defines the editor to be used for the "E" key.  Defaults to the first installed
+program out of "vim", "emacs" and "nano".
+
+=item SHELL
+
+Defines the shell that ranger is going to use with the :shell command and
+the "S" key.  Defaults to "bash".
+
+=item XDG_CONFIG_HOME
+
+Specifies the directory for configuration files. Defaults to F<$HOME/.config>.
+
+=item PYTHONOPTIMIZE
+
+This variable determines the optimize level of python.
+
+Using PYTHONOPTIMIZE=1 (like python -O) will make python discard assertion
+statements.  You will gain efficiency at the cost of losing some debug info.
+
+Using PYTHONOPTIMIZE=2 (like python -OO) will additionally discard any
+docstrings.  Using this will disable the <F1> key on commands.
+
+=back
+
+
+
+
+=head1 EXAMPLES
+
+=head2 VIM: File Chooser
+
+This is a vim function which allows you to use ranger to select a file for
+opening in your current vim session.
+
+ fun! RangerChooser()
+   silent !ranger --choosefile=/tmp/chosenfile `[ -z '%' ] && echo -n . || dirname %`
+   if filereadable('/tmp/chosenfile')
+     exec 'edit ' . system('cat /tmp/chosenfile')
+     call system('rm /tmp/chosenfile')
+   endif
+   redraw!
+ endfun
+ map ,r :call RangerChooser()<CR>
+
+=head2 Bash: cd to last path after exit
+
+This is a bash function (for F<~/.bashrc>) to change the directory to the last
+visited one after ranger quits.  You can always type C<cd -> to go back to the
+original one.
+
+ function ranger-cd {
+   tempfile='/tmp/chosendir'
+   /usr/bin/ranger --choosedir="$tempfile" "${@:-$(pwd)}"
+   test -f "$tempfile" &&
+   if [ "$(cat -- "$tempfile")" != "$(echo -n `pwd`)" ]; then
+     cd -- "$(cat "$tempfile")"
+   fi
+   rm -f -- "$tempfile"
+ }
+
+ # This binds Ctrl-O to ranger-cd:
+ bind '"\C-o":"ranger-cd\C-m"'
+
+
+
+
+=head1 LICENSE
+
+GNU General Public License 3 or (at your option) any later version.
+
+
+
+
+=head1 LINKS
+
+=over
+
+=item Download: L<http://ranger.nongnu.org/ranger-stable.tar.gz>
+
+=item The project page: L<http://ranger.nongnu.org/>
+
+=item The mailing list: L<http://savannah.nongnu.org/mail/?group=ranger>
+
+=back
+
+ranger is maintained with the git version control system.  To fetch a fresh
+copy, run:
+
+ git clone git://git.savannah.nongnu.org/ranger.git
+
+
+
+
+=head1 BUGS
+
+Report bugs here: L<http://savannah.nongnu.org/bugs/?group=ranger>
+
+Please include as much relevant information as possible.  For the most
+diagnostic output, run ranger like this: C<PYTHONOPTIMIZE= ranger --debug>
diff --git a/ranger.py b/ranger.py
index 53fd8bdb..44fc298f 100755
--- a/ranger.py
+++ b/ranger.py
@@ -1,7 +1,5 @@
 #!/usr/bin/python -O
 # -*- coding: utf-8 -*-
-#
-# Ranger: Explore your forest of files from inside your terminal
 # Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
 #
 # This program is free software: you can redistribute it and/or modify
@@ -21,11 +19,14 @@
 # after you exit ranger.  Run it with the command: source ranger ranger
 """":
 if [ ! -z "$1" ]; then
-	$@ --fail-unless-cd &&
-	if [ -z "$XDG_CONFIG_HOME" ]; then
-		cd "$(grep \^\' ~/.config/ranger/bookmarks | cut -b3-)"
-	else
-		cd "$(grep \^\' "$XDG_CONFIG_HOME"/ranger/bookmarks | cut -b3-)"
+	tempfile='/tmp/chosendir'
+	ranger="$1"
+	shift
+	"$ranger" --choosedir="$tempfile" "${@:-$(pwd)}"
+	test -f "$tempfile" &&
+	if [ "$(cat -- "$tempfile")" != "$(echo -n `pwd`)" ]; then
+		cd "$(cat "$tempfile")"
+		rm -f -- "$tempfile"
 	fi && return 0
 else
 	echo "usage: source path/to/ranger.py path/to/ranger.py"
@@ -34,25 +35,19 @@ return 1
 """
 
 import sys
-import os.path
+from os.path import exists, abspath
 
 # Need to find out whether or not the flag --clean was used ASAP,
 # because --clean is supposed to disable bytecode compilation
-try:
-	argv = sys.argv[0:sys.argv.index('--')]
-except:
-	argv = sys.argv
+argv = sys.argv[1:sys.argv.index('--')] if '--' in sys.argv else sys.argv[1:]
 sys.dont_write_bytecode = '-c' in argv or '--clean' in argv
 
 # Set the actual docstring
 __doc__ = """Ranger - file browser for the unix terminal"""
 
-# Don't import ./ranger when running an installed binary at /usr/bin/ranger
-if os.path.exists('ranger') and '/' in os.path.normpath(__file__):
-	try:
-		sys.path.remove(os.path.abspath('.'))
-	except:
-		pass
+# Don't import ./ranger when running an installed binary at /usr/.../ranger
+if __file__[:4] == '/usr' and exists('ranger') and abspath('.') in sys.path:
+	sys.path.remove(abspath('.'))
 
 # Start ranger
 import ranger
diff --git a/ranger/api/apps.py b/ranger/api/apps.py
index 45432705..86c7b017 100644
--- a/ranger/api/apps.py
+++ b/ranger/api/apps.py
@@ -17,6 +17,7 @@ import os, sys, re
 from ranger.api import *
 from ranger.ext.iter_tools import flatten
 from ranger.ext.get_executables import get_executables
+from ranger.core.runner import Context
 from ranger.core.shared import FileManagerAware
 
 
@@ -49,10 +50,10 @@ class Applications(FileManagerAware):
 			return self.app_editor(context)
 
 	def app_pager(self, context):
-		return ('less', ) + tuple(context)
+		return 'less', context
 
 	def app_editor(self, context):
-		return ('vim', ) + tuple(context)
+		return ('vim', context)
 	"""
 
 	def _meets_dependencies(self, fnc):
@@ -101,7 +102,17 @@ class Applications(FileManagerAware):
 			if app in get_executables():
 				return _generic_app(app, context)
 			handler = self.app_default
-		return handler(context)
+		arguments = handler(context)
+		# flatten
+		if isinstance(arguments, str):
+			return (arguments, )
+		result = []
+		for obj in arguments:
+			if isinstance(obj, (tuple, list, Context)):
+				result.extend(obj)
+			else:
+				result.append(obj)
+		return result
 
 	def has(self, app):
 		"""Returns whether an application is defined"""
@@ -147,7 +158,7 @@ def depends_on(*args):
 def _generic_app(name, context, flags=''):
 	"""Use this function when no other information is given"""
 	context.flags += flags
-	return tup(name, *context)
+	return name, context
 
 
 def _generic_wrapper(name, flags=''):
diff --git a/ranger/api/commands.py b/ranger/api/commands.py
index 9a353eef..a22fd0b3 100644
--- a/ranger/api/commands.py
+++ b/ranger/api/commands.py
@@ -14,9 +14,11 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import os
+import ranger
 from collections import deque
 from ranger.api import *
 from ranger.core.shared import FileManagerAware
+from ranger.ext.lazy_property import lazy_property
 from ranger.ext.command_parser import LazyParser as parse
 
 # A dummy that allows the generation of docstrings in ranger.defaults.commands
@@ -38,7 +40,11 @@ class CommandContainer(object):
 		for varname, var in vars(module).items():
 			try:
 				if issubclass(var, Command) and var != Command:
-					self.commands[var.name or varname] = var
+					classdict = var.__mro__[0].__dict__
+					if 'name' in classdict and classdict['name']:
+						self.commands[var.name] = var
+					else:
+						self.commands[varname] = var
 			except TypeError:
 				pass
 		for new, old in self.aliases.items():
@@ -47,6 +53,18 @@ class CommandContainer(object):
 			except:
 				pass
 
+	def load_commands_from_object(self, obj, filtr):
+		for attribute_name in dir(obj):
+			if attribute_name[0] == '_' or attribute_name not in filtr:
+				continue
+			attribute = getattr(obj, attribute_name)
+			if hasattr(attribute, '__call__'):
+				cmd = type(attribute_name, (FunctionCommand, ), dict())
+				cmd._based_function = attribute
+				cmd._function_name = attribute.__name__
+				cmd._object_name = obj.__class__.__name__
+				self.commands[attribute_name] = cmd
+
 	def get_command(self, name, abbrev=True):
 		if abbrev:
 			lst = [cls for cmd, cls in self.commands.items() \
@@ -73,8 +91,14 @@ class Command(FileManagerAware):
 	"""Abstract command class"""
 	name = None
 	allow_abbrev = True
-	def __init__(self, line):
+	resolve_macros = True
+	quantifier = None
+	_shifted = 0
+
+	def __init__(self, line, quantifier=None):
 		self.line = line
+		self.args = line.split()
+		self.quantifier = quantifier
 
 	def execute(self):
 		"""Override this"""
@@ -88,6 +112,51 @@ class Command(FileManagerAware):
 	def cancel(self):
 		"""Override this"""
 
+	# Easy ways to get information
+	def arg(self, n):
+		"""Returns the nth space separated word"""
+		try:
+			return self.args[n]
+		except IndexError:
+			return ""
+
+	def rest(self, n):
+		"""Returns everything from and after arg(n)"""
+		got_space = False
+		word_count = 0
+		for i in range(len(self.line)):
+			if self.line[i] == " ":
+				if not got_space:
+					got_space = True
+					word_count += 1
+			elif got_space:
+				got_space = False
+				if word_count == n + self._shifted:
+					return self.line[i:]
+		return ""
+
+	def start(self, n):
+		"""Returns everything until (inclusively) arg(n)"""
+		return ' '.join(self.args[:n]) + " " # XXX
+
+	def shift(self):
+		del self.args[0]
+		self._shifted += 1
+
+	def tabinsert(self, word):
+		return ''.join([self._tabinsert_left, word, self._tabinsert_right])
+
+	@lazy_property
+	def _tabinsert_left(self):
+		try:
+			return self.line[:self.line[0:self.pos].rindex(' ') + 1]
+		except ValueError:
+			return ''
+
+	@lazy_property
+	def _tabinsert_right(self):
+		return self.line[self.pos:]
+
 	# COMPAT: this is still used in old commands.py configs
 	def _tab_only_directories(self):
 		from os.path import dirname, basename, expanduser, join, isdir
@@ -187,3 +256,57 @@ class Command(FileManagerAware):
 			# more than one result. append no slash, so the user can
 			# manually type in the slash to advance into that directory
 			return (line.start(1) + join(rel_dirname, name) for name in names)
+
+
+class FunctionCommand(Command):
+	_based_function = None
+	_object_name = ""
+	_function_name = "unknown"
+	def execute(self):
+		if not self._based_function:
+			return
+		if len(self.args) == 1:
+			try:
+				return self._based_function(**{'narg':self.quantifier})
+			except TypeError:
+				return self._based_function()
+
+		args, keywords = list(), dict()
+		for arg in self.args[1:]:
+			equal_sign = arg.find("=")
+			value = arg if (equal_sign is -1) else arg[equal_sign + 1:]
+			try:
+				value = int(value)
+			except:
+				if value in ('True', 'False'):
+					value = (value == 'True')
+				else:
+					try:
+						value = float(value)
+					except:
+						pass
+
+			if equal_sign == -1:
+				args.append(value)
+			else:
+				keywords[arg[:equal_sign]] = value
+
+		if self.quantifier is not None:
+			keywords['narg'] = self.quantifier
+
+		try:
+			if self.quantifier is None:
+				return self._based_function(*args, **keywords)
+			else:
+				try:
+					return self._based_function(*args, **keywords)
+				except TypeError:
+					del keywords['narg']
+					return self._based_function(*args, **keywords)
+		except TypeError:
+			if ranger.arg.debug:
+				raise
+			else:
+				self.fm.notify("Bad arguments for %s.%s: %s, %s" %
+						(self._object_name, self._function_name,
+							repr(args), repr(keywords)), bad=True)
diff --git a/ranger/api/keys.py b/ranger/api/keys.py
deleted file mode 100644
index 75de6237..00000000
--- a/ranger/api/keys.py
+++ /dev/null
@@ -1,131 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import os
-from curses import *
-from curses.ascii import *
-from inspect import getargspec, ismethod
-
-from ranger import RANGERDIR
-from ranger.api import *
-from ranger.container.bookmarks import ALLOWED_KEYS as ALLOWED_BOOKMARK_KEYS
-from ranger.container.tags import ALLOWED_KEYS as ALLOWED_TAGS_KEYS
-from ranger.container.keymap import KeyMap, Direction, KeyMapWithDirections
-
-# A dummy that allows the generation of docstrings in ranger.defaults.keys
-class DummyKeyManager(object):
-	def get_context(self, _):
-		class Dummy(object):
-			def __getattr__(self, *_, **__):
-				return Dummy()
-			__call__ = __getattr__
-		return Dummy()
-keymanager = DummyKeyManager()
-
-class Wrapper(object):
-	def __init__(self, firstattr):
-		self.__firstattr__ = firstattr
-
-	def __getattr__(self, attr):
-		if attr.startswith('_'):
-			raise AttributeError
-		def wrapper(*real_args, **real_keywords):
-			def function(command_argument):
-				args, kws = real_args, real_keywords
-				number = command_argument.n
-				direction = command_argument.direction
-				obj = getattr(command_argument, self.__firstattr__)
-				fnc = getattr(obj, attr)
-				if number is not None or direction is not None:
-					args, kws = replace_narg(number, direction, fnc, args, kws)
-				return fnc(*args, **kws)
-			return function
-		return wrapper
-
-# fm.enter_dir('~') is translated into lambda arg: arg.fm.enter_dir('~')
-# this makes things like this possible:
-# bind('gh', fm.enter_dir('~'))
-#
-# but NOT: (note the 2 dots)
-# bind('H', fm.history.go(-1))
-#
-# for something like that, use the long version:
-# bind('H', lambda arg: arg.fm.history.go(-1))
-#
-# If the method has an argument named "narg", pressing a number before
-# the key will pass that number as the narg argument. If you want the
-# same behaviour in a custom lambda function, you can write:
-# bind('gg', fm.move(to=0))
-# as:
-# bind('gg', lambda arg: narg(arg.n, arg.fm.move, to=0))
-
-fm = Wrapper('fm')
-wdg = Wrapper('wdg')
-
-
-DIRARG_KEYWORD = 'dirarg'
-NARG_KEYWORD = 'narg'
-
-def narg(number_, function_, *args_, **keywords_):
-	"""
-	This applies the replace_narg function to the arguments and keywords
-	and directly runs this function.
-
-	Example:
-	def foo(xyz, narg): return hash((xyz, narg))
-
-	narg(50, foo, 123) == foo(123, narg=50)
-	"""
-	args, keywords = replace_narg(number_, function_, args_, keywords_)
-	return function_(*args, **keywords)
-
-def replace_narg(number, direction, function, args, keywords):
-	"""
-	This function returns (args, keywords) with one little change:
-	if <function> has a named argument called "narg", args and keywords
-	will be modified so that the value of "narg" will be <number>.
-
-	def foo(xyz, narg): pass
-
-	replace_narg(666, foo, (), {'narg': 10, 'xyz': 5})
-	=> (), {'narg': 666, 'xyz': 5}
-
-	replace_narg(666, foo, (1, 2), {})
-	=> (1, 666), {}
-	"""
-	argspec = getargspec(function).args
-	args = list(args)
-	if number is not None and NARG_KEYWORD in argspec:
-		try:
-			# is narg in args?
-			index = argspec.index(NARG_KEYWORD)
-			if ismethod(function):
-				index -= 1  # because of 'self'
-			args[index] = number
-		except (ValueError, IndexError):
-			# is narg in keywords?
-			keywords = dict(keywords)
-			keywords[NARG_KEYWORD] = number
-	if direction is not None and DIRARG_KEYWORD in argspec:
-		try:
-			index = argspec.index(DIRARG_KEYWORD)
-			if ismethod(function):
-				index -= 1  # because of 'self'
-			args[index] = direction
-		except (ValueError, IndexError):
-			# is narg in keywords?
-			keywords = dict(keywords)
-			keywords[DIRARG_KEYWORD] = direction
-	return args, keywords
diff --git a/ranger/colorschemes/__init__.py b/ranger/colorschemes/__init__.py
index 8b7a21a9..a442d9c7 100644
--- a/ranger/colorschemes/__init__.py
+++ b/ranger/colorschemes/__init__.py
@@ -1,18 +1,3 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
 """
 Colorschemes are required to be located here or in CONFDIR/colorschemes/
 """
diff --git a/ranger/colorschemes/default.py b/ranger/colorschemes/default.py
index 317e8258..03122c59 100644
--- a/ranger/colorschemes/default.py
+++ b/ranger/colorschemes/default.py
@@ -103,21 +103,6 @@ class Default(ColorScheme):
 					attr |= bold
 					fg = red
 
-		if context.in_pager or context.help_markup:
-			if context.seperator:
-				fg = red
-			elif context.link:
-				fg = cyan
-			elif context.bars:
-				fg = black
-				attr |= bold
-			elif context.key:
-				fg = green
-			elif context.special:
-				fg = cyan
-			elif context.title:
-				attr |= bold
-
 		if context.text:
 			if context.highlight:
 				attr |= reverse
diff --git a/ranger/container/__init__.py b/ranger/container/__init__.py
index 3351cc63..21a336bc 100644
--- a/ranger/container/__init__.py
+++ b/ranger/container/__init__.py
@@ -1,22 +1,6 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-"""This package includes container-objects which are
+"""
+This package includes container-objects which are
 used to manage stored data
 """
 from ranger.container.history import History
-from .keymap import KeyMap, KeyManager
-from .keybuffer import KeyBuffer
 from .bookmarks import Bookmarks
diff --git a/ranger/container/bookmarks.py b/ranger/container/bookmarks.py
index f115c753..0dcfcbc3 100644
--- a/ranger/container/bookmarks.py
+++ b/ranger/container/bookmarks.py
@@ -89,7 +89,7 @@ class Bookmarks(object):
 		if key in self.dct:
 			return self.dct[key]
 		else:
-			raise KeyError("Nonexistant Bookmark!")
+			raise KeyError("Nonexistant Bookmark: `%s'!" % key)
 
 	def __setitem__(self, key, value):
 		"""Bookmark <value> to the key <key>.
diff --git a/ranger/container/keybuffer.py b/ranger/container/keybuffer.py
deleted file mode 100644
index 23b82a16..00000000
--- a/ranger/container/keybuffer.py
+++ /dev/null
@@ -1,180 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import curses.ascii
-from collections import deque
-from string import digits
-from ranger.ext.keybinding_parser import parse_keybinding, \
-		DIRKEY, ANYKEY, PASSIVE_ACTION
-from ranger.container.keymap import Binding, KeyMap # mainly for assertions
-
-MAX_ALIAS_RECURSION = 20
-digitlist = set(ord(n) for n in digits)
-
-class KeyBuffer(object):
-	"""The evaluator and storage for pressed keys"""
-	def __init__(self, keymap, direction_keys):
-		self.assign(keymap, direction_keys)
-
-	def assign(self, keymap, direction_keys):
-		"""Change the keymap and direction keys of the keybuffer"""
-		self.keymap = keymap
-		self.direction_keys = direction_keys
-
-	def add(self, key):
-		"""Add a key and evaluate it"""
-		assert isinstance(key, int)
-		assert key >= 0
-		self.all_keys.append(key)
-		self.key_queue.append(key)
-		while self.key_queue:
-			key = self.key_queue.popleft()
-
-			# evaluate quantifiers
-			if self.eval_quantifier and self._do_eval_quantifier(key):
-				return
-
-			# evaluate the command
-			if self.eval_command and self._do_eval_command(key):
-				return
-
-			# evaluate (the first number of) the direction-quantifier
-			if self.eval_quantifier and self._do_eval_quantifier(key):
-				return
-
-			# evaluate direction keys {j,k,gg,pagedown,...}
-			if not self.eval_command:
-				self._do_eval_direction(key)
-
-	def _do_eval_direction(self, key):
-		try:
-			assert isinstance(self.dir_tree_pointer, dict)
-			self.dir_tree_pointer = self.dir_tree_pointer[key]
-		except KeyError:
-			self.failure = True
-		else:
-			self._direction_try_to_finish()
-
-	def _direction_try_to_finish(self):
-		if self.max_alias_recursion <= 0:
-			self.failure = True
-			return None
-		match = self.dir_tree_pointer
-		assert isinstance(match, (Binding, dict, KeyMap))
-		if isinstance(match, KeyMap):
-			self.dir_tree_pointer = self.dir_tree_pointer._tree
-			match = self.dir_tree_pointer
-		if isinstance(self.dir_tree_pointer, Binding):
-			if match.alias:
-				self.key_queue.extend(parse_keybinding(match.alias))
-				self.dir_tree_pointer = self.direction_keys._tree
-				self.max_alias_recursion -= 1
-			else:
-				direction = match.actions['dir'].copy()
-				if self.direction_quant is not None:
-					direction.multiply(self.direction_quant)
-				self.directions.append(direction)
-				self.direction_quant = None
-				self.eval_command = True
-				self._try_to_finish()
-
-	def _do_eval_quantifier(self, key):
-		if self.eval_command:
-			tree = self.tree_pointer
-		else:
-			tree = self.dir_tree_pointer
-		if key in digitlist and ANYKEY not in tree:
-			attr = self.eval_command and 'quant' or 'direction_quant'
-			if getattr(self, attr) is None:
-				setattr(self, attr, 0)
-			setattr(self, attr, getattr(self, attr) * 10 + key - 48)
-		else:
-			self.eval_quantifier = False
-			return None
-		return True
-
-	def _do_eval_command(self, key):
-		assert isinstance(self.tree_pointer, dict), self.tree_pointer
-		try:
-			self.tree_pointer = self.tree_pointer[key]
-		except TypeError:
-			self.failure = True
-			return None
-		except KeyError:
-			try:
-				key in digitlist or self.direction_keys._tree[key]
-				self.tree_pointer = self.tree_pointer[DIRKEY]
-			except KeyError:
-				try:
-					self.tree_pointer = self.tree_pointer[ANYKEY]
-				except KeyError:
-					self.failure = True
-					return None
-				else:
-					self.matches.append(key)
-					assert isinstance(self.tree_pointer, (Binding, dict))
-					self._try_to_finish()
-			else:
-				assert isinstance(self.tree_pointer, (Binding, dict))
-				self.eval_command = False
-				self.eval_quantifier = True
-				self.dir_tree_pointer = self.direction_keys._tree
-		else:
-			if isinstance(self.tree_pointer, dict):
-				try:
-					self.command = self.tree_pointer[PASSIVE_ACTION]
-				except (KeyError, TypeError):
-					self.command = None
-			self._try_to_finish()
-
-	def _try_to_finish(self):
-		if self.max_alias_recursion <= 0:
-			self.failure = True
-			return None
-		assert isinstance(self.tree_pointer, (Binding, dict, KeyMap))
-		if isinstance(self.tree_pointer, KeyMap):
-			self.tree_pointer = self.tree_pointer._tree
-		if isinstance(self.tree_pointer, Binding):
-			if self.tree_pointer.alias:
-				keys = parse_keybinding(self.tree_pointer.alias)
-				self.key_queue.extend(keys)
-				self.tree_pointer = self.keymap._tree
-				self.max_alias_recursion -= 1
-			else:
-				self.command = self.tree_pointer
-				self.done = True
-
-	def clear(self):
-		"""Reset the keybuffer.  Do this once before the first usage."""
-		self.max_alias_recursion = MAX_ALIAS_RECURSION
-		self.failure = False
-		self.done = False
-		self.quant = None
-		self.matches = []
-		self.command = None
-		self.direction_quant = None
-		self.directions = []
-		self.all_keys = []
-		self.tree_pointer = self.keymap._tree
-		self.dir_tree_pointer = self.direction_keys._tree
-
-		self.key_queue = deque()
-
-		self.eval_quantifier = True
-		self.eval_command = True
-
-	def __str__(self):
-		"""returns a concatenation of all characters"""
-		return "".join("{0:c}".format(c) for c in self.all_keys)
diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py
deleted file mode 100644
index d52a5215..00000000
--- a/ranger/container/keymap.py
+++ /dev/null
@@ -1,164 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-from ranger.ext.tree import Tree
-from ranger.ext.direction import Direction
-from ranger.ext.keybinding_parser import parse_keybinding, DIRKEY, ANYKEY
-
-FUNC = 'func'
-DIRARG = 'dir'
-ALIASARG = 'alias'
-
-class CommandArgs(object):
-	"""
-	A CommandArgs object is passed to the keybinding function.
-
-	This object simply aggregates information about the pressed keys
-	and the current environment.
-
-	Attributes:
-	fm: the FM instance
-	wdg: the currently focused widget (or fm, if none is focused)
-	keybuffer: the keybuffer object
-	n: the prefixed number, eg 5 in the command "5yy"
-	directions: a list of directions which are entered for "<dir>"
-	direction: the first direction object from that list
-	keys: a string representation of the keybuffer
-	matches: all keys which are entered for "<any>"
-	match: the first match
-	binding: the used Binding object
-	"""
-	def __init__(self, fm, widget, keybuf):
-		self.fm = fm
-		self.wdg = widget
-		self.keybuffer = keybuf
-		self.n = keybuf.quant
-		self.direction = keybuf.directions and keybuf.directions[0] or None
-		self.directions = keybuf.directions
-		self.keys = str(keybuf)
-		self.matches = keybuf.matches
-		self.match = keybuf.matches and keybuf.matches[0] or None
-		self.binding = keybuf.command
-
-	@staticmethod
-	def from_widget(widget):
-		return CommandArgs(widget.fm, \
-				widget, widget.env.keybuffer)
-
-
-class KeyMap(Tree):
-	"""Contains a tree with all the keybindings"""
-	def map(self, *args, **keywords):
-		if keywords:
-			return self._add_binding(*args, **keywords)
-		firstarg = args[-1]
-		if hasattr(firstarg, '__call__'):
-			keywords[FUNC] = firstarg
-			return self._add_binding(*args[:-1], **keywords)
-		def decorator_function(func):
-			keywords = {FUNC:func}
-			self.map(*args, **keywords)
-			return func
-		return decorator_function
-
-	__call__ = map
-
-	def _add_binding(self, *keys, **actions):
-		assert keys
-		bind = Binding(keys, actions)
-		for key in keys:
-			self.set(parse_keybinding(key), bind)
-
-	def unmap(self, *keys):
-		for key in keys:
-			self.unset(parse_keybinding(key))
-
-	def __getitem__(self, key):
-		return self.traverse(parse_keybinding(key))
-
-
-class KeyMapWithDirections(KeyMap):
-	def __init__(self, *args, **keywords):
-		Tree.__init__(self, *args, **keywords)
-		self.directions = KeyMap()
-
-	def merge(self, other):
-		assert hasattr(other, 'directions'), 'Merging with wrong type?'
-		Tree.merge(self, other)
-		Tree.merge(self.directions, other.directions)
-
-	def dir(self, *args, **keywords):
-		if ALIASARG in keywords:
-			self.directions.map(*args, **keywords)
-		else:
-			self.directions.map(*args, dir=Direction(**keywords))
-
-
-class KeyManager(object):
-	def __init__(self, keybuffer, contexts):
-		self._keybuffer = keybuffer
-		self._list_of_contexts = contexts
-		self.clear()
-
-	def clear(self):
-		self.contexts = dict()
-		for context in self._list_of_contexts:
-			self.contexts[context] = KeyMapWithDirections()
-
-	def map(self, context, *args, **keywords):
-		self.get_context(context).map(*args, **keywords)
-
-	def dir(self, context, *args, **keywords):
-		self.get_context(context).dir(*args, **keywords)
-
-	def unmap(self, context, *args, **keywords):
-		self.get_context(context).unmap(*args, **keywords)
-
-	def merge_all(self, keymapwithdirection):
-		for context, keymap in self.contexts.items():
-			keymap.merge(keymapwithdirection)
-
-	def get_context(self, context):
-		assert isinstance(context, str)
-		assert context in self.contexts, "no such context: " + context
-		return self.contexts[context]
-
-	def use_context(self, context):
-		context = self.get_context(context)
-		if self._keybuffer.keymap is not context:
-			self._keybuffer.assign(context, context.directions)
-			self._keybuffer.clear()
-
-
-class Binding(object):
-	"""The keybinding object"""
-	def __init__(self, keys, actions):
-		assert hasattr(keys, '__iter__')
-		assert isinstance(actions, dict)
-		self.actions = actions
-		try:
-			self.function = self.actions[FUNC]
-		except KeyError:
-			self.function = None
-		try:
-			self.direction = self.actions[DIRARG]
-		except KeyError:
-			self.direction = None
-		try:
-			alias = self.actions[ALIASARG]
-		except KeyError:
-			self.alias = None
-		else:
-			self.alias = tuple(parse_keybinding(alias))
diff --git a/ranger/container/settingobject.py b/ranger/container/settingobject.py
index d036245f..f7507b3e 100644
--- a/ranger/container/settingobject.py
+++ b/ranger/container/settingobject.py
@@ -22,7 +22,7 @@ ALLOWED_SETTINGS = {
 	'collapse_preview': bool,
 	'colorscheme_overlay': (type(None), type(lambda:0)),
 	'colorscheme': str,
-	'column_ratios': (tuple, list, set),
+	'column_ratios': (tuple, list),
 	'dirname_in_tabs': bool,
 	'display_size_in_main_column': bool,
 	'display_size_in_status_bar': bool,
@@ -31,6 +31,7 @@ ALLOWED_SETTINGS = {
 	'draw_borders': bool,
 	'flushinput': bool,
 	'hidden_filter': lambda x: isinstance(x, str) or hasattr(x, 'match'),
+	'load_default_rc': (bool, type(None)),
 	'max_console_history_size': (int, type(None)),
 	'max_history_size': (int, type(None)),
 	'mouse_enabled': bool,
@@ -81,6 +82,8 @@ class SettingObject(SignalDispatcher, FileManagerAware):
 	def __getattr__(self, name):
 		assert name in ALLOWED_SETTINGS or name in self._settings, \
 				"No such setting: {0}!".format(name)
+		if name.startswith('_'):
+			return self.__dict__[name]
 		try:
 			return self._settings[name]
 		except:
diff --git a/ranger/container/tags.py b/ranger/container/tags.py
index c2fe3067..24fbd0a5 100644
--- a/ranger/container/tags.py
+++ b/ranger/container/tags.py
@@ -57,6 +57,9 @@ class Tags(object):
 			tag = others['tag']
 		else:
 			tag = self.default_tag
+		tag = str(tag)
+		if tag not in ALLOWED_KEYS:
+			return
 		self.sync()
 		for item in items:
 			try:
diff --git a/ranger/core/actions.py b/ranger/core/actions.py
index 163cc3d6..15e6dd74 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -24,6 +24,7 @@ from inspect import cleandoc
 import ranger
 from ranger.ext.direction import Direction
 from ranger.ext.relative_symlink import relative_symlink
+from ranger.ext.keybinding_parser import key_to_string
 from ranger.ext.shell_escape import shell_quote
 from ranger import fsobject
 from ranger.core.shared import FileManagerAware, EnvironmentAware, \
@@ -34,7 +35,6 @@ from ranger.core.loader import CommandLoader
 class _MacroTemplate(string.Template):
 	"""A template for substituting macros in commands"""
 	delimiter = '%'
-	idpattern = '\d?[a-z]'
 
 class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 	search_method = 'ctime'
@@ -70,8 +70,19 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 			bad = True
 		text = str(text)
 		self.log.appendleft(text)
-		if hasattr(self.ui, 'notify'):
-			self.ui.notify(text, duration=duration, bad=bad)
+		if self.ui and self.ui.is_on:
+			self.ui.status.notify(text, duration=duration, bad=bad)
+		else:
+			print(text)
+
+	def abort(self):
+		try:
+			item = self.loader.queue[0]
+		except:
+			self.notify("Type Q or :quit<Enter> to exit Ranger")
+		else:
+			self.notify("Aborting: " + item.get_description())
+			self.loader.remove(index=0)
 
 	def redraw_window(self):
 		"""Redraw the window"""
@@ -79,17 +90,31 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 
 	def open_console(self, string='', prompt=None, position=None):
 		"""Open the console if the current UI supports that"""
-		if hasattr(self.ui, 'open_console'):
-			self.ui.open_console(string, prompt=prompt, position=position)
+		self.ui.open_console(string, prompt=prompt, position=position)
 
-	def execute_console(self, string=''):
+	def execute_console(self, string='', wildcards=[], quantifier=None):
 		"""Execute a command for the console"""
-		self.open_console(string=string)
-		self.ui.console.line = string
-		self.ui.console.execute()
+		command_name = string.split()[0]
+		try:
+			cmd_class = self.commands.get_command(command_name)
+		except:
+			self.notify("Command not found: `%s'" % command_name, bad=True)
+		else:
+			cmd = cmd_class(string)
+			if cmd.resolve_macros and _MacroTemplate.delimiter in string:
+				macros = dict(('any%d'%i, key_to_string(char)) \
+						for i, char in enumerate(wildcards))
+				if 'any0' in macros:
+					macros['any'] = macros['any0']
+				string = self.substitute_macros(string, additional=macros)
+			try:
+				cmd_class(string, quantifier=quantifier).execute()
+			except Exception as error:
+				self.notify(error)
 
-	def substitute_macros(self, string):
-		return _MacroTemplate(string).safe_substitute(self._get_macros())
+	def substitute_macros(self, string, additional=dict()):
+		return _MacroTemplate(string).safe_substitute(self._get_macros(),
+				**additional)
 
 	def _get_macros(self):
 		macros = {}
@@ -150,6 +175,18 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 
 		return macros
 
+	def source_cmdlist(self, filename, narg=None):
+		for line in open(filename, 'r'):
+			line = line.rstrip("\r\n")
+			if line.startswith("#") or not line.strip():
+				continue
+			try:
+				self.execute_console(line)
+			except Exception as e:
+				if ranger.arg.debug:
+					raise
+				else:
+					self.notify('Error in line `%s\':\n  %s' % (line, str(e)), bad=True)
 
 	def execute_file(self, files, **kw):
 		"""Execute a file.
@@ -226,23 +263,26 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 						pagesize=self.ui.browser.hei)
 				cwd.move(to=newpos)
 
-	def move_parent(self, n):
+	def move_parent(self, n, narg=None):
+		if narg is not None:
+			n *= narg
 		parent = self.env.at_level(-1)
-		if parent.pointer + n < 0:
-			n = 0 - parent.pointer
-		try:
-			self.env.enter_dir(parent.files[parent.pointer+n])
-		except IndexError:
-			pass
+		if parent is not None:
+			if parent.pointer + n < 0:
+				n = 0 - parent.pointer
+			try:
+				self.env.enter_dir(parent.files[parent.pointer+n])
+			except IndexError:
+				pass
 
 	def history_go(self, relative):
 		"""Move back and forth in the history"""
-		self.env.history_go(relative)
+		self.env.history_go(int(relative))
 
 	def scroll(self, relative):
 		"""Scroll down by <relative> lines"""
-		if hasattr(self.ui, 'scroll'):
-			self.ui.scroll(relative)
+		if self.ui.browser and self.ui.browser.main_column:
+			self.ui.browser.main_column.scroll(relative)
 			self.env.cf = self.env.cwd.pointed_obj
 
 	def enter_dir(self, path, remember=False, history=True):
@@ -294,10 +334,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 			return
 		self.execute_file(file, app = 'editor')
 
-	def hint(self, text):
-		self.ui.hint(text)
-
-	def toggle_boolean_option(self, string):
+	def toggle_option(self, string):
 		"""Toggle a boolean option named <string>"""
 		if isinstance(self.env.settings[string], bool):
 			self.env.settings[string] ^= True
@@ -319,7 +356,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 		except:
 			pass
 
-	def mark(self, all=False, toggle=False, val=None, movedown=None, narg=1):
+	def mark_files(self, all=False, toggle=False, val=None, movedown=None, narg=1):
 		"""
 		A wrapper for the directory.mark_xyz functions.
 
@@ -360,10 +397,8 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 		if movedown:
 			self.move(down=narg)
 
-		if hasattr(self.ui, 'redraw_main_column'):
-			self.ui.redraw_main_column()
-		if hasattr(self.ui, 'status'):
-			self.ui.status.need_redraw = True
+		self.ui.redraw_main_column()
+		self.ui.status.need_redraw = True
 
 	def mark_in_direction(self, val=True, dirarg=None):
 		cwd = self.env.cwd
@@ -386,9 +421,9 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 			except:
 				return False
 		self.env.last_search = text
-		self.search(order='search', offset=offset)
+		self.search_next(order='search', offset=offset)
 
-	def search(self, order=None, offset=1, forward=True):
+	def search_next(self, order=None, offset=1, forward=True):
 		original_order = order
 		if self.search_forward:
 			direction = bool(forward)
@@ -464,8 +499,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 		if movedown:
 			self.move(down=1)
 
-		if hasattr(self.ui, 'redraw_main_column'):
-			self.ui.redraw_main_column()
+		self.ui.redraw_main_column()
 
 	def tag_remove(self, paths=None, movedown=None):
 		self.tag_toggle(paths=paths, value=False, movedown=movedown)
@@ -512,9 +546,6 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 	# These commands open the built-in pager and set specific sources.
 
 	def display_command_help(self, console_widget):
-		if not hasattr(self.ui, 'open_pager'):
-			return
-
 		try:
 			command = console_widget._get_cmd_class()
 		except:
@@ -534,40 +565,17 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 		lines = cleandoc(command.__doc__).split('\n')
 		pager.set_source(lines)
 
-	def display_help(self, topic='index', narg=None):
-		if not hasattr(self.ui, 'open_pager'):
-			return
-
-		from ranger.help import get_help, get_help_by_index
-
-		scroll_to_line = 0
-		if narg is not None:
-			chapter, subchapter = int(str(narg)[0]), str(narg)[1:]
-			help_text = get_help_by_index(chapter)
-			lines = help_text.split('\n')
-			if chapter:
-				chapternumber = str(chapter) + '.' + subchapter + '. '
-				skip_to_content = True
-				for line_number, line in enumerate(lines):
-					if skip_to_content:
-						if line[:10] == '==========':
-							skip_to_content = False
-					else:
-						if line.startswith(chapternumber):
-							scroll_to_line = line_number
-		else:
-			help_text = get_help(topic)
-			lines = help_text.split('\n')
-
-		pager = self.ui.open_pager()
-		pager.set_source(lines)
-		pager.markup = 'help'
-		pager.move(down=scroll_to_line)
+	def display_help(self, narg=None):
+		manualpath = self.relpath('../doc/ranger.1')
+		if os.path.exists(manualpath):
+			process = self.run(['man', manualpath])
+			if process.poll() != 16:
+				return
+		process = self.run(['man', 'ranger'])
+		if process.poll() == 16:
+			self.notify("Could not find manpage.", bad=True)
 
 	def display_log(self):
-		if not hasattr(self.ui, 'open_pager'):
-			return
-
 		pager = self.ui.open_pager()
 		if self.log:
 			pager.set_source(["Message Log:"] + list(self.log))
@@ -575,8 +583,6 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 			pager.set_source(["Message Log:", "No messages!"])
 
 	def display_file(self):
-		if not hasattr(self.ui, 'open_embedded_pager'):
-			return
 		if not self.env.cf or not self.env.cf.is_file:
 			return
 
@@ -694,10 +700,10 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 		if newtab != self.current_tab:
 			self.tab_open(newtab)
 
-	def tab_new(self):
+	def tab_new(self, path=None):
 		for i in range(1, 10):
 			if not i in self.tabs:
-				self.tab_open(i)
+				self.tab_open(i, path)
 				break
 
 	def _get_tab_list(self):
@@ -845,6 +851,6 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 			src = src.path
 
 		try:
-			os.rename(src, dest)
+			os.renames(src, dest)
 		except OSError as err:
 			self.notify(err)
diff --git a/ranger/core/environment.py b/ranger/core/environment.py
index cf140410..90f0fefa 100644
--- a/ranger/core/environment.py
+++ b/ranger/core/environment.py
@@ -20,13 +20,11 @@ import socket
 from os.path import abspath, normpath, join, expanduser, isdir
 
 from ranger.fsobject import Directory
-from ranger.container import KeyBuffer, KeyManager, History
+from ranger.ext.keybindings import KeyBuffer, KeyMaps
+from ranger.container import History
 from ranger.ext.signals import SignalDispatcher
 from ranger.core.shared import SettingsAware
 
-ALLOWED_CONTEXTS = ('browser', 'pager', 'embedded_pager', 'taskview',
-		'console')
-
 class Environment(SettingsAware, SignalDispatcher):
 	"""
 	A collection of data which is relevant for more than one class.
@@ -42,8 +40,6 @@ class Environment(SettingsAware, SignalDispatcher):
 	last_search = None
 	pathway = None
 	path = None
-	keybuffer = None
-	keymanager = None
 
 	def __init__(self, path):
 		SignalDispatcher.__init__(self)
@@ -51,8 +47,8 @@ class Environment(SettingsAware, SignalDispatcher):
 		self._cf = None
 		self.pathway = ()
 		self.directories = {}
-		self.keybuffer = KeyBuffer(None, None)
-		self.keymanager = KeyManager(self.keybuffer, ALLOWED_CONTEXTS)
+		self.keybuffer = KeyBuffer()
+		self.keymaps = KeyMaps(self.keybuffer)
 		self.copy = set()
 		self.history = History(self.settings.max_history_size, unique=False)
 
diff --git a/ranger/core/fm.py b/ranger/core/fm.py
index fa972b50..c292e0c9 100644
--- a/ranger/core/fm.py
+++ b/ranger/core/fm.py
@@ -27,7 +27,7 @@ import sys
 import ranger
 from ranger.core.actions import Actions
 from ranger.container.tags import Tags
-from ranger.gui.defaultui import DefaultUI
+from ranger.gui.ui import UI
 from ranger.container import Bookmarks
 from ranger.core.runner import Runner
 from ranger.ext.get_executables import get_executables
@@ -90,7 +90,7 @@ class FM(Actions, SignalDispatcher):
 			self.tags = Tags(self.confpath('tagged'))
 
 		if self.ui is None:
-			self.ui = DefaultUI()
+			self.ui = UI()
 			self.ui.initialize()
 
 		def mylogfunc(text):
@@ -142,8 +142,8 @@ class FM(Actions, SignalDispatcher):
 			copy('defaults/apps.py', 'apps.py')
 		if which == 'commands' or which == 'all':
 			copy('defaults/commands.py', 'commands.py')
-		if which == 'keys' or which == 'all':
-			copy('defaults/keys.py', 'keys.py')
+		if which == 'rc' or which == 'all':
+			copy('defaults/rc.conf', 'rc.conf')
 		if which == 'options' or which == 'all':
 			copy('defaults/options.py', 'options.py')
 		if which == 'scope' or which == 'all':
@@ -151,7 +151,7 @@ class FM(Actions, SignalDispatcher):
 			os.chmod(self.confpath('scope.sh'),
 				os.stat(self.confpath('scope.sh')).st_mode | stat.S_IXUSR)
 		if which not in \
-				('all', 'apps', 'scope', 'commands', 'keys', 'options'):
+				('all', 'apps', 'scope', 'commands', 'rc', 'options'):
 			sys.stderr.write("Unknown config file `%s'\n" % which)
 
 	def confpath(self, *paths):
diff --git a/ranger/core/helper.py b/ranger/core/helper.py
index ad5541f5..d8081b01 100644
--- a/ranger/core/helper.py
+++ b/ranger/core/helper.py
@@ -73,6 +73,8 @@ def parse_arguments():
 	parser.add_option('--choosedir', type='string', metavar='TARGET',
 			help="Makes ranger act like a directory chooser. When ranger quits"
 			", it will write the name of the last visited directory to TARGET")
+	parser.add_option('--list-unused-keys', action='store_true',
+			help="List common keys which are not bound to any action.")
 
 	options, positional = parser.parse_args()
 	arg = OpenStruct(options.__dict__, targets=positional)
@@ -85,14 +87,17 @@ def parse_arguments():
 
 
 def load_settings(fm, clean):
+	from ranger.core.actions import Actions
 	import ranger.core.shared
 	import ranger.api.commands
-	import ranger.api.keys
 	if not clean:
 		allow_access_to_confdir(ranger.arg.confdir, True)
 
 		# Load commands
 		comcont = ranger.api.commands.CommandContainer()
+		exclude = ['settings']
+		include = [name for name in dir(Actions) if name not in exclude]
+		comcont.load_commands_from_object(fm, include)
 		ranger.api.commands.alias = comcont.alias
 		try:
 			import commands
@@ -101,35 +106,74 @@ def load_settings(fm, clean):
 			pass
 		from ranger.defaults import commands
 		comcont.load_commands_from_module(commands)
-		commands = comcont
+		fm.commands = comcont
 
 		# Load apps
 		try:
 			import apps
 		except ImportError:
 			from ranger.defaults import apps
-
-		# Load keys
-		keymanager = ranger.core.shared.EnvironmentAware.env.keymanager
-		ranger.api.keys.keymanager = keymanager
-		from ranger.defaults import keys
+		fm.apps = apps.CustomApplications()
+
+		# Load rc.conf
+		custom_conf = fm.confpath('rc.conf')
+		default_conf = fm.relpath('defaults', 'rc.conf')
+		load_default_rc = fm.settings.load_default_rc
+
+		# If load_default_rc is None, think hard:  If the users rc.conf is
+		# about as large as the default rc.conf, he probably copied it as a whole
+		# and doesn't want to load the default rc.conf anymore.
+		if load_default_rc is None:
+			try:
+				custom_conf_size = os.stat(custom_conf).st_size
+			except:
+				load_default_rc = True
+			else:
+				default_conf_size = os.stat(default_conf).st_size
+				load_default_rc = custom_conf_size < default_conf_size - 2048
+
+		if load_default_rc:
+			fm.source_cmdlist(default_conf)
+		if os.access(custom_conf, os.R_OK):
+			fm.source_cmdlist(custom_conf)
+
+		# Load plugins
 		try:
-			import keys
-		except ImportError:
+			plugindir = fm.confpath('plugins')
+			plugins = [p[:-3] for p in os.listdir(plugindir) \
+					if p.endswith('.py') and not p.startswith('_')]
+		except:
 			pass
+		else:
+			if not os.path.exists(fm.confpath('plugins', '__init__.py')):
+				f = open(fm.confpath('plugins', '__init__.py'), 'w')
+				f.close()
+			import types
+			ranger.fm = fm
+			for plugin in sorted(plugins):
+				try:
+					mod = __import__('plugins', fromlist=[plugin])
+					fm.log.append("Loaded plugin '%s'." % module)
+				except Exception as e:
+					fm.log.append("Error in plugin '%s'" % plugin)
+					import traceback
+					for line in traceback.format_exception_only(type(e), e):
+						fm.log.append(line)
+			ranger.fm = None
+
 		allow_access_to_confdir(ranger.arg.confdir, False)
 	else:
 		comcont = ranger.api.commands.CommandContainer()
 		ranger.api.commands.alias = comcont.alias
-		from ranger.api import keys
-		keymanager = ranger.core.shared.EnvironmentAware.env.keymanager
-		ranger.api.keys.keymanager = keymanager
-		from ranger.defaults import commands, keys, apps
+		from ranger.defaults import commands, apps
+		comcont = ranger.api.commands.CommandContainer()
+		exclude = ['settings']
+		include = [name for name in dir(Actions) if name not in exclude]
+		comcont.load_commands_from_object(fm, include)
 		comcont.load_commands_from_module(commands)
-		commands = comcont
-	fm.commands = commands
-	fm.keys = keys
-	fm.apps = apps.CustomApplications()
+		fm.commands = comcont
+		fm.source_cmdlist(fm.relpath('defaults', 'rc.conf'))
+		fm.apps = apps.CustomApplications()
 
 
 def load_apps(fm, clean):
diff --git a/ranger/core/main.py b/ranger/core/main.py
index e6392387..6595fbf2 100644
--- a/ranger/core/main.py
+++ b/ranger/core/main.py
@@ -77,6 +77,19 @@ def main():
 		fm.tabs = dict((n+1, os.path.abspath(path)) for n, path \
 				in enumerate(targets[:9]))
 		load_settings(fm, arg.clean)
+
+		if arg.list_unused_keys:
+			from ranger.ext.keybinding_parser import special_keys
+			maps = fm.env.keymaps['browser']
+			reversed_special_keys = dict((v,k) for k,v in special_keys.items())
+			for key in sorted(special_keys.values(), key=lambda x: str(x)):
+				if key not in maps:
+					print("<%s>" % reversed_special_keys[key])
+			for key in range(33, 127):
+				if key not in maps:
+					print(chr(key))
+			return 1 if arg.fail_unless_cd else 0
+
 		if fm.env.username == 'root':
 			fm.settings.preview_files = False
 			fm.settings.use_preview_script = False
diff --git a/ranger/core/runner.py b/ranger/core/runner.py
index 53bede29..ad4ca558 100644
--- a/ranger/core/runner.py
+++ b/ranger/core/runner.py
@@ -28,13 +28,14 @@ List of allowed flags:
 s: silent mode. output will be discarded.
 d: detach the process.
 p: redirect output to the pager
-(An uppercase key ensures that a certain flag will not be used.)
+c: run only the current file (not handled here)
+w: wait for enter-press afterwards
+(An uppercase key negates the respective lower case flag)
 """
 
 import os
 import sys
 from subprocess import Popen, PIPE
-from ranger.ext.waitpid_no_intr import waitpid_no_intr
 
 
 ALLOWED_FLAGS = 'sdpwcSDPWC'
@@ -199,7 +200,7 @@ class Runner(object):
 				self._log("Failed to run: " + str(action))
 			else:
 				if context.wait:
-					waitpid_no_intr(process.pid)
+					process.wait()
 				if wait_for_enter:
 					press_enter()
 		finally:
diff --git a/ranger/core/shared.py b/ranger/core/shared.py
index 38dc7cdd..ce7c3184 100644
--- a/ranger/core/shared.py
+++ b/ranger/core/shared.py
@@ -63,7 +63,8 @@ class SettingsAware(Awareness):
 		settings.signal_bind('setopt.preview_script',
 				after_setting_preview_script, priority=1)
 		def after_setting_use_preview_script(signal):
-			if signal.fm.settings.preview_script is None and signal.value:
+			if signal.fm.settings.preview_script is None and signal.value \
+					and not signal.previous:
 				signal.fm.notify("Preview script undefined or not found!",
 						bad=True)
 		settings.signal_bind('setopt.use_preview_script',
diff --git a/ranger/defaults/apps.py b/ranger/defaults/apps.py
index ffa828c0..77bc9c2b 100644
--- a/ranger/defaults/apps.py
+++ b/ranger/defaults/apps.py
@@ -28,21 +28,21 @@ This example modifies the behaviour of "feh" and adds a custom media player:
 	from ranger.api.apps import *
 			
 	class CustomApplications(DefaultApps):
-		def app_kaffeine(self, c):
-			return tup('kaffeine', *c)
+		def app_kaffeine(self, context):
+			return 'kaffeine', context
 
-		def app_feh_fullscreen_by_default(self, c):
-			return tup('feh', '-F', *c)
+		def app_feh_fullscreen_by_default(self, context):
+			return 'feh', '-F', context
 
-		def app_default(self, c):
-			f = c.file #shortcut
+		def app_default(self, context):
+			f = context.file #shortcut
 			if f.video or f.audio:
-				return self.app_kaffeine(c)
+				return self.app_kaffeine(context)
 
-			if f.image and c.mode == 0:
-				return self.app_feh_fullscreen_by_default(c)
+			if f.image and context.mode == 0:
+				return self.app_feh_fullscreen_by_default(context)
 
-			return DefaultApps.app_default(self, c)
+			return DefaultApps.app_default(self, context)
 #### end of the example
 """
 
@@ -96,7 +96,10 @@ class CustomApplications(Applications):
 			return self.either(c, 'mplayer', 'smplayer', 'vlc', 'totem')
 
 		if f.image:
-			return self.either(c, 'feh', 'eog', 'mirage')
+			if c.mode in (11, 12, 13, 14):
+				return self.either(c, 'set_bg_with_feh')
+			else:
+				return self.either(c, 'sxiv', 'feh', 'eog', 'mirage')
 
 		if f.document or f.filetype.startswith('text') or f.size == 0:
 			return self.either(c, 'editor')
@@ -109,7 +112,7 @@ class CustomApplications(Applications):
 	# ----------------------------------------- application definitions
 	# Note: Trivial application definitions are at the bottom
 	def app_pager(self, c):
-		return tup('less', '-R', *c)
+		return 'less', '-R', c
 
 	def app_editor(self, c):
 		try:
@@ -132,53 +135,66 @@ class CustomApplications(Applications):
 	@depends_on('mplayer')
 	def app_mplayer(self, c):
 		if c.mode is 1:
-			return tup('mplayer', '-fs', *c)
+			return 'mplayer', '-fs', c
 
 		elif c.mode is 2:
 			args = "mplayer -fs -sid 0 -vfm ffmpeg -lavdopts " \
 					"lowres=1:fast:skiploopfilter=all:threads=8".split()
 			args.extend(c)
-			return tup(*args)
+			return args
 
 		elif c.mode is 3:
-			return tup('mplayer', '-mixer', 'software', *c)
+			return 'mplayer', '-mixer', 'software', c
 
 		else:
-			return tup('mplayer', *c)
+			return 'mplayer', c
 
 	@depends_on('feh')
-	def app_feh(self, c):
-		arg = {1: '--bg-scale', 2: '--bg-tile', 3: '--bg-center'}
-
+	def app_set_bg_with_feh(self, c):
 		c.flags += 'd'
+		arg = {11: '--bg-scale', 12: '--bg-tile', 13: '--bg-center',
+				14: '--bg-fill'}
+		if c.mode in arg:
+			return 'feh', arg[c.mode], c.file.path
+		return 'feh', arg[11], c.file.path
 
-		if c.mode in arg: # mode 1, 2 and 3 will set the image as the background
-			return tup('feh', arg[c.mode], c.file.path)
-		if c.mode is 11 and len(c.files) is 1: # view all files in the cwd
+	@depends_on('feh')
+	def app_feh(self, c):
+		c.flags += 'd'
+		if c.mode is 0 and len(c.files) is 1: # view all files in the cwd
 			images = (f.basename for f in self.fm.env.cwd.files if f.image)
-			return tup('feh', '--start-at', c.file.basename, *images)
-		return tup('feh', *c)
+			return 'feh', '--start-at', c.file.basename, images
+		return 'feh', c
+
+	@depends_on('sxiv')
+	def app_sxiv(self, c):
+		c.flags = 'd' + c.flags
+		if len(c.files) is 1:
+			images = [f.basename for f in self.fm.env.cwd.files if f.image]
+			position = images.index(c.file.basename) + 1
+			return 'sxiv', '-n', str(position), images
+		return 'sxiv', c
 
 	@depends_on('aunpack')
 	def app_aunpack(self, c):
 		if c.mode is 0:
 			c.flags += 'p'
-			return tup('aunpack', '-l', c.file.path)
-		return tup('aunpack', c.file.path)
+			return 'aunpack', '-l', c.file.path
+		return 'aunpack', c.file.path
 
 	@depends_on('file-roller')
 	def app_file_roller(self, c):
 		c.flags += 'd'
-		return tup('file-roller', c.file.path)
+		return 'file-roller', c.file.path
 
 	@depends_on('make')
 	def app_make(self, c):
 		if c.mode is 0:
-			return tup("make")
+			return "make"
 		if c.mode is 1:
-			return tup("make", "install")
+			return "make", "install"
 		if c.mode is 2:
-			return tup("make", "clear")
+			return "make", "clear"
 
 	@depends_on('java')
 	def app_java(self, c):
@@ -187,30 +203,30 @@ class CustomApplications(Applications):
 				return file.path[:file.path.index('.')]
 			return file.path
 		files_without_extensions = map(strip_extensions, c.files)
-		return tup("java", files_without_extensions)
+		return "java", files_without_extensions
 
 	@depends_on('totem')
 	def app_totem(self, c):
 		if c.mode is 0:
-			return tup("totem", *c)
+			return "totem", c
 		if c.mode is 1:
-			return tup("totem", "--fullscreen", *c)
+			return "totem", "--fullscreen", c
 
 	@depends_on('mimeopen')
 	def app_mimeopen(self, c):
 		if c.mode is 0:
-			return tup("mimeopen", *c)
+			return "mimeopen", c
 		if c.mode is 1: 
 			# Will ask user to select program
 			# aka "Open with..."
-			return tup("mimeopen", "--ask", *c)
+			return "mimeopen", "--ask", c
 
 # Often a programs invocation is trivial.  For example:
 #    vim test.py readme.txt [...]
 # This could be implemented like:
 #    @depends_on("vim")
-#    def app_vim(self, c):
-#        return tup("vim", *c.files)
+#    def app_vim(self, context):
+#        return "vim", context
 # Instead of creating such a generic function for each program, just add
 # its name here and it will be automatically done for you.
 CustomApplications.generic('vim', 'fceux', 'elinks', 'wine',
diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py
index ad3fa0e5..a909cee9 100644
--- a/ranger/defaults/commands.py
+++ b/ranger/defaults/commands.py
@@ -66,15 +66,21 @@ alias('qall', 'quitall')
 
 class cd(Command):
 	"""
-	:cd <dirname>
+	:cd [-r] <dirname>
 
 	The cd command changes the directory.
 	The command 'cd -' is equivalent to typing ``.
+	Using the option "-r" will get you to the real path.
 	"""
 
 	def execute(self):
-		line = parse(self.line)
-		destination = line.rest(1)
+		if self.arg(1) == '-r':
+			import os.path
+			self.shift()
+			destination = os.path.realpath(self.rest(1))
+		else:
+			destination = self.rest(1)
+
 		if not destination:
 			destination = '~'
 
@@ -137,6 +143,16 @@ class cd(Command):
 			return (line.start(1) + join(rel_dirname, dirname) for dirname in dirnames)
 
 
+class chain(Command):
+	"""
+	:chain <command1>; <command2>; ...
+	Calls multiple commands at once, separated by semicolons.
+	"""
+	def execute(self):
+		for command in self.rest(1).split(";"):
+			self.fm.execute_console(command)
+
+
 class search(Command):
 	def execute(self):
 		self.fm.search_file(parse(self.line).rest(1), regexp=True)
@@ -356,6 +372,7 @@ class set_(Command):
 		name = line.chunk(1)
 		name, value, _ = line.parse_setting_line()
 		if name and value:
+			from re import compile as regexp
 			try:
 				value = eval(value)
 			except:
@@ -491,6 +508,23 @@ class mark(Command):
 		self.fm.ui.need_redraw = True
 
 
+class console(Command):
+	"""
+	:console <command>
+
+	Open the console with the given command.
+	"""
+	def execute(self):
+		position = None
+		if self.arg(1)[0:2] == '-p':
+			try:
+				position = int(self.arg(1)[2:])
+				self.shift()
+			except:
+				pass
+		self.fm.open_console(self.rest(1), position=position)
+
+
 class load_copy_buffer(Command):
 	"""
 	:load_copy_buffer
@@ -502,9 +536,11 @@ class load_copy_buffer(Command):
 		from ranger.fsobject import File
 		from os.path import exists
 		try:
-			f = open(self.fm.confpath(self.copy_buffer_filename), 'r')
+			fname = self.fm.confpath(self.copy_buffer_filename)
+			f = open(fname, 'r')
 		except:
-			return self.fm.notify("Cannot open file %s" % fname, bad=True)
+			return self.fm.notify("Cannot open %s" % \
+					(fname or self.copy_buffer_filename), bad=True)
 		self.fm.env.copy = set(File(g) \
 			for g in f.read().split("\n") if exists(g))
 		f.close()
@@ -519,10 +555,13 @@ class save_copy_buffer(Command):
 	"""
 	copy_buffer_filename = 'copy_buffer'
 	def execute(self):
+		fname = None
 		try:
-			f = open(self.fm.confpath(self.copy_buffer_filename), 'w')
+			fname = self.fm.confpath(self.copy_buffer_filename)
+			f = open(fname, 'w')
 		except:
-			return self.fm.notify("Cannot open file %s" % fname, bad=True)
+			return self.fm.notify("Cannot open %s" % \
+					(fname or self.copy_buffer_filename), bad=True)
 		f.write("\n".join(f.path for f in self.fm.env.copy))
 		f.close()
 
@@ -569,7 +608,7 @@ class touch(Command):
 		line = parse(self.line)
 		fname = join(self.fm.env.cwd.path, expanduser(line.rest(1)))
 		if not lexists(fname):
-			open(fname, 'a')
+			open(fname, 'a').close()
 		else:
 			self.fm.notify("file/directory exists!", bad=True)
 
@@ -594,7 +633,7 @@ class edit(Command):
 
 class eval_(Command):
 	"""
-	:eval <python code>
+	:eval [-q] <python code>
 
 	Evaluates the python code.
 	`fm' is a reference to the FM instance.
@@ -606,18 +645,28 @@ class eval_(Command):
 	:eval p("Hello World!")
 	"""
 	name = 'eval'
+	resolve_macros = False
 
 	def execute(self):
-		code = parse(self.line).rest(1)
+		if self.arg(1) == '-q':
+			code = self.rest(2)
+			quiet = True
+		else:
+			code = self.rest(1)
+			quiet = False
+		import ranger
+		global cmd, fm, p, quantifier
 		fm = self.fm
+		cmd = self.fm.execute_console
 		p = fm.notify
+		quantifier = self.quantifier
 		try:
 			try:
 				result = eval(code)
 			except SyntaxError:
 				exec(code)
 			else:
-				if result:
+				if result and not quiet:
 					p(result)
 		except Exception as err:
 			p(err)
@@ -632,11 +681,23 @@ class rename(Command):
 
 	def execute(self):
 		from ranger.fsobject import File
+		from os import access
+		from os.path import join
+
 		line = parse(self.line)
-		if not line.rest(1):
+		new_name = line.rest(1)
+
+		if not new_name:
 			return self.fm.notify('Syntax: rename <newname>', bad=True)
-		self.fm.rename(self.fm.env.cf, line.rest(1))
-		f = File(line.rest(1))
+
+		if new_name == self.fm.env.cf.basename:
+			return
+
+		if access(new_name, os.F_OK):
+			return self.fm.notify("Can't rename: file already exists!", bad=True)
+
+		self.fm.rename(self.fm.env.cf, new_name)
+		f = File(new_name)
 		self.fm.env.cwd.pointed_obj = f
 		self.fm.env.cf = f
 
@@ -658,8 +719,9 @@ class chmod(Command):
 	"""
 
 	def execute(self):
-		line = parse(self.line)
-		mode = line.rest(1)
+		mode = self.rest(1)
+		if not mode:
+			mode = str(self.quantifier)
 
 		try:
 			mode = int(mode, 8)
@@ -683,6 +745,113 @@ class chmod(Command):
 			pass
 
 
+class bulkrename(Command):
+	"""
+	:bulkrename
+
+	This command opens a list of selected files in an external editor.
+	After you edit and save the file, it will generate a shell script
+	which does bulk renaming according to the changes you did in the file.
+
+	This shell script is opened in an editor for you to review.
+	After you close it, it will be executed.
+	"""
+	def execute(self):
+		import sys
+		import tempfile
+		from ranger.fsobject.file import File
+		from ranger.ext.shell_escape import shell_escape as esc
+		py3 = sys.version > "3"
+
+		# Create and edit the file list
+		filenames = [f.basename for f in self.fm.env.get_selection()]
+		listfile = tempfile.NamedTemporaryFile()
+
+		if py3:
+			listfile.write("\n".join(filenames).encode("utf-8"))
+		else:
+			listfile.write("\n".join(filenames))
+		listfile.flush()
+		self.fm.execute_file([File(listfile.name)], app='editor')
+		listfile.seek(0)
+		if py3:
+			new_filenames = listfile.read().decode("utf-8").split("\n")
+		else:
+			new_filenames = listfile.read().split("\n")
+		listfile.close()
+		if all(a == b for a, b in zip(filenames, new_filenames)):
+			self.fm.notify("No renaming to be done!")
+			return
+
+		# Generate and execute script
+		cmdfile = tempfile.NamedTemporaryFile()
+		cmdfile.write(b"# This file will be executed when you close the editor.\n")
+		cmdfile.write(b"# Please double-check everything, clear the file to abort.\n")
+		if py3:
+			cmdfile.write("\n".join("mv -vi " + esc(old) + " " + esc(new) \
+					for old, new in zip(filenames, new_filenames) if old != new).encode("utf-8"))
+		else:
+			cmdfile.write("\n".join("mv -vi " + esc(old) + " " + esc(new) \
+					for old, new in zip(filenames, new_filenames) if old != new))
+		cmdfile.flush()
+		self.fm.run(['vim', cmdfile.name])
+		self.fm.run(['/bin/sh', cmdfile.name], flags='w')
+		cmdfile.close()
+
+
+class help_(Command):
+	"""
+	:help
+	
+	Display ranger's manual page.
+	"""
+	name = 'help'
+	def execute(self):
+		self.fm.display_help()
+
+
+class map_(Command):
+	"""
+	:map <keysequence> <command>
+	Maps a command to a keysequence in the "browser" context.
+
+	Example:
+	map j move down
+	map J move down 10
+	"""
+	name = 'map'
+	context = 'browser'
+	resolve_macros = False
+
+	def execute(self):
+		self.fm.env.keymaps.bind(self.context, self.arg(1), self.rest(2))
+
+
+class cmap(map_):
+	""":cmap <keysequence> <command>
+	Maps a command to a keysequence in the "console" context.
+
+	Example:
+	cmap <ESC> console_close
+	cmap <C-x> console_type test
+	"""
+	context = 'console'
+
+
+class tmap(map_):
+	""":tmap <keysequence> <command>
+	Maps a command to a keysequence in the "taskview" context.
+	"""
+	context = 'taskview'
+
+
+class pmap(map_):
+	""":pmap <keysequence> <command>
+	Maps a command to a keysequence in the "pager" context.
+	"""
+	context = 'pager'
+
+
 class filter(Command):
 	"""
 	:filter <string>
diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py
deleted file mode 100644
index 07591691..00000000
--- a/ranger/defaults/keys.py
+++ /dev/null
@@ -1,439 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-"""
-This is the default key configuration file of ranger.
-Syntax for binding keys: map(*keys, fnc)
-
-Examples for keys: "x", "gg", "<C-J><A-4>", "<tab>", "<down><up><right>"
-
-fnc is a function which is called with the CommandArgs object.
-
-The CommandArgs object has these attributes:
-arg.fm: the file manager instance
-arg.wdg: the current widget
-arg.n: the number typed before the key combination (if allowed)
-arg.direction: the direction object (if applicable)
-arg.keys: the string representation of the used key combination
-arg.keybuffer: the keybuffer instance
-
-Direction keys are special.  They must be mapped with: map.dir(*keys, **args)
-where args is a dict of values such as up, down, to, absolute, relative...
-Example: map.dir('gg', to=0)
-Direction keys can be accessed in a mapping that contians "<dir>".
-Other special keys are "<any>" which matches any single key and "<bg>"
-which will run the function passively, without clearing the keybuffer.
-
-Additionally, there are shortcuts for accessing methods of the current
-file manager and widget instance:
-map('xyz', fm.method(foo=bar))
-will be translated to:
-map('xyz', lamdba arg: arg.fm.method(foo=bar))
-If possible, arg.n and arg.direction are automatically inserted.
-
-
-Example scenario
-----------------
-If this keys are defined:
-map("dd", "d<dir>", fm.cut(foo=bar))
-map.dir("gg", to=0)
-
-Type in the keys on the left and the function on the right will be executed:
-dd        => fm.cut(foo=bar)
-5dd       => fm.cut(foo=bar, narg=5)
-dgg       => fm.cut(foo=bar, dirarg=Direction(to=0))
-5dgg      => fm.cut(foo=bar, narg=5, dirarg=Direction(to=0))
-5d3gg     => fm.cut(foo=bar, narg=5, dirarg=Direction(to=3))
-
-Example ~/.config/ranger/keys.py
--------------------------
-from ranger.api.keys import *
-
-keymanager.map("browser", "d", fm.move(down=0.5, pages=True))
-
-# Add less-like d/u keys to the "browser" context:
-map = keymanager.get_context('browser')
-map("d", fm.move(down=0.5, pages=True))
-map("u", fm.move(up=0.5, pages=True))
-
-# Add keys to all contexts
-map = KeyMapWithDirections()  # create new empty keymap.
-map("q", fm.exit())
-map.dir("<down>", down=3)     # I'm quick, I want to move 3 at once!
-keymanager.merge_all(map)     # merge the new map into all existing ones.
-"""
-
-from ranger.api.keys import *
-
-# ===================================================================
-# == Define keys for everywhere:
-# ===================================================================
-map = global_keys = KeyMapWithDirections()
-map('Q', fm.exit())
-map('<C-L>', fm.redraw_window())
-map('<backspace2>', alias='<backspace>')  # Backspace is bugged sometimes
-
-#map('<dir>', wdg.move())
-@map('<dir>') # move around with direction keys
-def move(arg):
-	arg.wdg.move(narg=arg.n, **arg.direction)
-
-# -------------------------------------------------- direction keys
-map.dir('<down>', down=1)
-map.dir('<up>', up=1)
-map.dir('<left>', left=1)
-map.dir('<right>', right=1)
-map.dir('<home>', down=0, absolute=True)
-map.dir('<end>', down=-1, absolute=True)
-map.dir('<pagedown>', down=1, pages=True)
-map.dir('<pageup>', down=-1, pages=True)
-map.dir('%', down=50, percentage=True, absolute=True)
-
-
-# ===================================================================
-# == Define aliases
-# ===================================================================
-map = vim_aliases = KeyMapWithDirections()
-map.dir('j', alias='<down>')
-map.dir('k', alias='<up>')
-map.dir('h', alias='<left>')
-map.dir('l', alias='<right>')
-map.dir('gg', alias='<home>')
-map.dir('G', alias='<end>')
-map.dir('<C-F>', alias='<pagedown>')
-map.dir('<C-B>', alias='<pageup>')
-
-map = readline_aliases = KeyMapWithDirections()
-map.dir('<C-B>', alias='<left>')
-map.dir('<C-F>', alias='<right>')
-map.dir('<C-A>', alias='<home>')
-map.dir('<C-E>', alias='<end>')
-map.dir('<C-D>', alias='<delete>')
-map.dir('<C-H>', alias='<backspace>')
-
-map = midnight_commander_fkeys = KeyMapWithDirections()
-map('<F1>', fm.display_help())
-map('<F3>', fm.display_file())
-map('<F4>', fm.edit_file())
-map('<F5>', fm.copy())
-map('<F6>', fm.cut())
-map('<F7>', fm.open_console('mkdir '))
-map('<F8>', fm.open_console(DELETE_WARNING))
-map('<F10>', fm.exit())
-
-# ===================================================================
-# == Define keys in "browser" context:
-# ===================================================================
-map = keymanager.get_context('browser')
-map.merge(global_keys)
-map.merge(vim_aliases)
-map.merge(midnight_commander_fkeys)
-
-# -------------------------------------------------------- movement
-map('gg', fm.move(to=0))
-map('<enter>', wdg.move(right=0))  # run with mode=0
-map('<C-D>', 'J', fm.move(down=0.5, pages=True))
-map('<C-U>', 'K', fm.move(up=0.5, pages=True))
-map(']', fm.move_parent(1))
-map('[', fm.move_parent(-1))
-map('}', fm.traverse())
-map('{', fm.history_go(-1))
-
-# --------------------------------------------------------- history
-map('H', fm.history_go(-1))
-map('L', fm.history_go(1))
-
-# ----------------------------------------------- tagging / marking
-map('t', fm.tag_toggle())
-map('T', fm.tag_remove())
-for key in ALLOWED_TAGS_KEYS:
-		map('"' + key, fm.tag_toggle(tag=key))
-
-map(' ', fm.mark(toggle=True))
-map('v', fm.mark(all=True, toggle=True))
-map('V', 'uv', fm.mark(all=True, val=False))
-map('<C-V><dir>', fm.mark_in_direction(val=True))
-map('u<C-V><dir>', fm.mark_in_direction(val=False))
-
-# ------------------------------------------ file system operations
-map('y<bg>', fm.hint('*copy:* cop*y* *a*dd *r*emove ' \
-	'*p*ath_to_xsel *d*irpath_to_xsel base*n*ame_to_xsel'))
-map('yy', 'y<dir>', fm.copy())
-map('ya', fm.copy(mode='add'))
-map('yr', fm.copy(mode='remove'))
-map('yp', fm.execute_console('shell -d echo -n %d/%f | xsel -i'))
-map('yd', fm.execute_console('shell -d echo -n %d | xsel -i'))
-map('yn', fm.execute_console('shell -d echo -n %f | xsel -i'))
-map('d<bg>', fm.hint('disk_*u*sage *cut:* *d*:cut *a*dd *r*emove'))
-map('dd', 'd<dir>', fm.cut())
-map('da', fm.cut(mode='add'))
-map('dr', fm.cut(mode='remove'))
-map('p<bg>', fm.hint('*paste:* *p*aste *o*verwrite sym*l*inks ' \
-		'*h*ardlinks relative_sym*L*inks'))
-map('pp', fm.paste())
-map('po', fm.paste(overwrite=True))
-map('pl', fm.paste_symlink(relative=False))
-map('pL', fm.paste_symlink(relative=True))
-map('ph<bg>', fm.hint('*paste:* hard*l*inks'))
-map('phl', fm.paste_hardlink())
-
-map('u<bg>', fm.hint("un*y*ank, unbook*m*ark, unselect:*v*"))
-map('ud', 'uy', fm.uncut())
-
-# ------------------------------------ changing of file permissions
-# type "+ow" for "chmod o+w %s" and so on
-from itertools import product
-for mode in product('ugoa', 'rwxXst'):
-	map('-%s%s' % mode, fm.execute_console('shell chmod %s-%s %%s' % mode))
-	map('+%s%s' % mode, fm.execute_console('shell chmod %s+%s %%s' % mode))
-	map('=%s%s' % mode, fm.execute_console('shell chmod %s+%s %%s' % mode))
-
-# hints:
-template = '%s %s to *r*ead, *w*rite, e*x*ecute'
-for who, name in zip('ugoa', ('user', 'group', 'others', 'all')):
-	map('-%s<bg>' % who, fm.hint(template % ('forbid', name)))
-	map('+%s<bg>' % who, fm.hint(template % ('allow', name)))
-	map('=%s<bg>' % who, fm.hint(template % ('allow', name)))
-map('-<bg>', '+<bg>', '=<bg>', fm.hint('change permission for *u*ser, '
-	'*g*roup, *o*thers, *a*ll'))
-
-# ---------------------------------------------------- run programs
-map('S', fm.execute_command(os.environ['SHELL']))
-map('E', fm.edit_file())
-map('du', fm.execute_console('shell -p du --max-depth=1 -h --apparent-size'))
-
-# -------------------------------------------------- toggle options
-map('z<bg>', fm.hint('*f*ilter *options:* *d*irectories_first *c*ollape_preview ' \
-		'*s*ort_case_insensitive show_*h*idden *p*review_files '\
-		'*P*review_dirs use_pre*v*iew_script flush*i*nput *m*ouse'))
-map('zh', '<C-h>', 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'))
-map('zs', fm.toggle_boolean_option('sort_case_insensitive'))
-map('zm', fm.toggle_boolean_option('mouse_enabled'))
-map('zf', fm.open_console('filter '))
-
-# ------------------------------------------------------------ sort
-map('o<bg>', 'O<bg>', fm.hint('*sort by:* *s*ize *b*asename *m*time' \
-	' *c*time *a*time *t*ype *r*everse *n*atural'))
-sort_dict = {
-	's': 'size',
-	'b': 'basename',
-	'n': 'natural',
-	'm': 'mtime',
-	'c': 'ctime',
-	'a': 'atime',
-	't': 'type',
-}
-
-for key, val in sort_dict.items():
-	for key, is_capital in ((key, False), (key.upper(), True)):
-		# reverse if any of the two letters is capital
-		map('o' + key, fm.sort(func=val, reverse=is_capital))
-		map('O' + key, fm.sort(func=val, reverse=True))
-
-map('or', 'Or', 'oR', 'OR', lambda arg: \
-		arg.fm.sort(reverse=not arg.fm.settings.sort_reverse))
-
-# ----------------------------------------------- console shortcuts
-@map("A")
-def append_to_filename(arg):
-	command = 'rename ' + arg.fm.env.cf.basename
-	arg.fm.open_console(command)
-
-@map("I")
-def insert_before_filename(arg):
-	command = 'rename ' + arg.fm.env.cf.basename
-	arg.fm.open_console(command, position=len('rename '))
-
-map('cw', fm.open_console('rename '))
-map('cd', fm.open_console('cd '))
-map('f', fm.open_console('find '))
-map('@', fm.open_console('shell  %s', position=len('shell ')))
-map('#', fm.open_console('shell -p '))
-
-# --------------------------------------------- jump to directories
-map('g<bg>', fm.hint('*goto:* */* *d*ev *e*tc *h*:~ *m*edia ' \
-		'*M*nt *o*pt *s*rv *u*sr *v*ar *R*anger *link:* *l*:target_path ' \
-		'rea*L*_path *tab:* *c*lose *n*ew nex*t* *T*:prev'))
-map('gh', fm.cd('~'))
-map('ge', fm.cd('/etc'))
-map('gu', fm.cd('/usr'))
-map('gd', fm.cd('/dev'))
-map('gl', lambda arg: arg.fm.cd(os.path.realpath(arg.fm.env.cwd.path)))
-map('gL', lambda arg: arg.fm.cd(
-		os.path.dirname(os.path.realpath(arg.fm.env.cf.path))))
-map('go', fm.cd('/opt'))
-map('gv', fm.cd('/var'))
-map('gr', 'g/', fm.cd('/'))
-map('gm', fm.cd('/media'))
-map('gM', fm.cd('/mnt'))
-map('gs', fm.cd('/srv'))
-map('gR', fm.cd(RANGERDIR))
-
-# ------------------------------------------------------------ tabs
-map('gc', '<C-W>', fm.tab_close())
-map('gt', '<TAB>', '<A-Right>', fm.tab_move(1))
-map('gT', '<S-TAB>', '<A-Left>', fm.tab_move(-1))
-@map('gn', '<C-N>')
-def newtab_and_gohome(arg):
-	arg.fm.tab_new()
-	arg.fm.cd('~')   # To return to the original directory, type ``
-for n in range(1, 10):
-	map('g' + str(n), fm.tab_open(n))
-	map('<A-' + str(n) + '>', fm.tab_open(n))
-
-# ------------------------------------------------------- searching
-map('/', fm.open_console('search '))
-
-map('n', fm.search())
-map('N', fm.search(forward=False))
-
-map('c<bg>', fm.hint('*w*:rename ch*d*ir *search order:* ' \
-		'*a*time *c*time *M*time *m*imetype *s*ize *t*ag'))
-map('ct', fm.search(order='tag'))
-map('cc', fm.search(order='ctime'))
-map('ca', fm.search(order='atime'))
-map('cM', fm.search(order='mtime'))
-map('cm', fm.search(order='mimetype'))
-map('cs', fm.search(order='size'))
-
-# ------------------------------------------------------- bookmarks
-for key in ALLOWED_BOOKMARK_KEYS:
-	map("`" + key, "'" + key, fm.enter_bookmark(key))
-	map("m" + key, fm.set_bookmark(key))
-	map("um" + key, fm.unset_bookmark(key))
-map("`<bg>", "'<bg>", "m<bg>", fm.draw_bookmarks())
-map('um<bg>', lambda arg: (arg.fm.draw_bookmarks(),
-	arg.fm.hint("delete which bookmark?")))
-
-# ---------------------------------------------------- change views
-map('i', fm.display_file())
-map('W', fm.display_log())
-map('?', fm.display_help())
-map('w', lambda arg: arg.fm.ui.open_taskview())
-
-# ------------------------------------------------ system functions
-map('ZZ', 'ZQ', fm.exit())
-map('<C-R>', fm.reset())
-map('R', fm.reload_cwd())
-@map('<C-C>')
-def ctrl_c(arg):
-	try:
-		item = arg.fm.loader.queue[0]
-	except:
-		arg.fm.notify("Type Q or :quit<Enter> to exit Ranger")
-	else:
-		arg.fm.notify("Aborting: " + item.get_description())
-		arg.fm.loader.remove(index=0)
-
-map(':', ';', fm.open_console(''))
-map('!', fm.open_console('shell '))
-map('s', fm.open_console('shell '))
-map('r', fm.open_console('open_with '))
-
-
-# ===================================================================
-# == Define keys for the pager
-# ===================================================================
-map = pager_keys = KeyMapWithDirections()
-map.merge(global_keys)
-map.merge(vim_aliases)
-
-# -------------------------------------------------------- movement
-map('<left>', 'h', wdg.move(left=4))
-map('<right>', 'l', wdg.move(right=4))
-map('<C-D>', 'd', wdg.move(down=0.5, pages=True))
-map('<C-U>', 'u', wdg.move(up=0.5, pages=True))
-map('<C-F>', 'f', '<pagedown>', wdg.move(down=1, pages=True))
-map('<C-B>', 'b', '<pageup>', wdg.move(up=1, pages=True))
-map('<space>', wdg.move(down=0.8, pages=True))
-map('<cr>', wdg.move(down=1))
-
-# ---------------------------------------------------------- others
-map('E', fm.edit_file())
-map('?', fm.display_help())
-
-# --------------------------------------------------- bind the keys
-# There are two different kinds of pagers, each have a different
-# method for exiting:
-
-map = keymanager.get_context('pager')
-map.merge(pager_keys)
-map('q', 'i', '<esc>', '<F3>', lambda arg: arg.fm.ui.close_pager())
-
-map = keymanager.get_context('embedded_pager')
-map.merge(pager_keys)
-map('q', 'i', '<esc>', '<F3>', lambda arg: arg.fm.ui.close_embedded_pager())
-
-
-# ===================================================================
-# == Define keys for the taskview
-# ===================================================================
-map = keymanager.get_context('taskview')
-map.merge(global_keys)
-map.merge(vim_aliases)
-map('K', wdg.task_move(0))
-map('J', wdg.task_move(-1))
-map('dd', wdg.task_remove())
-
-map('?', fm.display_help())
-map('w', 'q', ESC, ctrl('d'), ctrl('c'),
-		lambda arg: arg.fm.ui.close_taskview())
-
-
-# ===================================================================
-# == Define keys for the console
-# ===================================================================
-map = keymanager.get_context('console')
-map.merge(global_keys)
-map.merge(readline_aliases)
-
-map('<up>', '<C-P>', wdg.history_move(-1))
-map('<down>', '<C-N>', wdg.history_move(1))
-map('<home>', '<C-A>', wdg.move(right=0, absolute=True))
-map('<end>', '<C-E>', wdg.move(right=-1, absolute=True))
-map('<tab>', wdg.tab())
-map('<s-tab>', wdg.tab(-1))
-map('<C-C>', '<C-D>', '<ESC>', wdg.close())
-map('<CR>', '<c-j>', wdg.execute())
-map('<F1>', lambda arg: arg.fm.display_command_help(arg.wdg))
-
-map('<backspace>', '<C-H>', wdg.delete(-1))
-map('<delete>', '<C-D>', wdg.delete(0))
-map('<C-W>', wdg.delete_word())
-map('<C-K>', wdg.delete_rest(1))
-map('<C-U>', wdg.delete_rest(-1))
-map('<C-Y>', wdg.paste())
-
-# Any key which is still undefined will simply be typed in.
-@map('<any>')
-def type_key(arg):
-	arg.wdg.type_key(arg.match)
-
-# Allow typing in numbers:
-def type_chr(n):
-	return lambda arg: arg.wdg.type_key(str(n))
-for number in range(10):
-	map(str(number), type_chr(number))
-
-# Unmap some global keys so we can type them:
-map.unmap('Q')
-map.directions.unmap('%')
diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py
index c9cbf8ad..fefdf39d 100644
--- a/ranger/defaults/options.py
+++ b/ranger/defaults/options.py
@@ -33,6 +33,13 @@ of the values stay the same.
 
 from ranger.api.options import *
 
+# Load the deault rc.conf file?  If you've copied it to your configuration
+# direcory, then you should deactivate this option.  "None" means guess.
+load_default_rc = None
+
+# How many columns are there, and what are their relative widths?
+column_ratios = (1, 3, 4)
+
 # Which files should be hidden?  Toggle this by typing `zh' or
 # changing the setting `show_hidden'
 hidden_filter = regexp(
@@ -74,9 +81,6 @@ draw_bookmark_borders = True
 # Display the directory name in tabs?
 dirname_in_tabs = False
 
-# How many columns are there, and what are their relative widths?
-column_ratios = (1, 1, 4, 3)
-
 # Enable the mouse support?
 mouse_enabled = True
 
diff --git a/ranger/defaults/rc.conf b/ranger/defaults/rc.conf
new file mode 100644
index 00000000..aabdbb30
--- /dev/null
+++ b/ranger/defaults/rc.conf
@@ -0,0 +1,360 @@
+# ===================================================================
+# This file contains the default startup commands for ranger.
+# To change them, it is recommended to create the file
+# ~/.config/ranger/rc.conf and add your custom commands there.
+#
+# If you copy this whole file there, add this line to your options.py
+# so it is not loaded twice:
+#
+#     load_default_rc = False
+#
+# The purpose of this file is mainly to define keybindings.  For
+# changing settings or running more complex python code, use the
+# configuation file "options.py" or define commands in "commands.py".
+#
+# Each line is a command that will be run before the user interface
+# is initialized.  As a result, you can not use commands which rely
+# on the UI such as :delete or :mark.
+# ===================================================================
+
+
+# ===================================================================
+# == Define keys for the browser
+# ===================================================================
+
+# Basic
+map Q quit!
+map q quit
+map ZZ quit
+map ZQ quit
+
+map R     reload_cwd
+map <C-r> reset
+map <C-l> redraw_window
+map <C-c> abort
+
+map i display_file
+map ? help
+map W display_log
+map w eval fm.ui.open_taskview()
+map S shell $SHELL
+
+map :  console
+map ;  console
+map !  console shell 
+map @  console -p6 shell  %%s
+map #  console shell -p 
+map s  console shell 
+map r  console open_with 
+map f  console find 
+map cd console cd 
+
+# Tagging / Marking
+map t       tag_toggle
+map T       tag_remove
+map "<any>  tag_toggle tag=%any
+map <Space> mark_files toggle=True
+map v       mark_files all=True val=True
+map V       mark_files all=True val=False
+map uv      mark_files all=True val=False
+
+# VIM-like
+map gg    move to=0
+map G     move to=-1
+map j     move down=1
+map k     move up=1
+map h     move left=1
+map l     move right=1
+map J     move down=0.5 pages=True
+map K     move up=0.5   pages=True
+map <C-d> move down=0.5 pages=True
+map <C-u> move up=0.5   pages=True
+map <C-f> move down=1   pages=True
+map <C-b> move up=1     pages=True
+
+# For the nostalgics: Midnight Commander bindings
+map <F1> help
+map <F3> display_file
+map <F4> edit
+map <F5> copy
+map <F6> cut
+map <F7> console mkdir 
+map <F8> console delete seriously? 
+map <F10> exit
+
+# In case you work on a keyboard with dvorak layout
+map <UP>       move up=1
+map <DOWN>     move down=1
+map <LEFT>     move left=1
+map <RIGHT>    move right=1
+map <HOME>     move to=0
+map <END>      move to=-1
+map <PAGEDOWN> move down=1   pages=True
+map <PAGEUP>   move up=1     pages=True
+map <CR>       move right=1
+map <DEL>      console delete
+map <INS>      console touch
+
+# Jumping around
+map H     history_go -1
+map L     history_go 1
+map ]     move_parent 1
+map [     move_parent -1
+map }     traverse
+
+map gh cd ~
+map ge cd /etc
+map gu cd /usr
+map gd cd /dev
+map gl cd -r .
+map gL cd -r %f
+map go cd /opt
+map gv cd /var
+map gm cd /media
+map gM cd /mnt
+map gs cd /srv
+map gr cd /
+map gR eval fm.cd(ranger.RANGERDIR)
+map g/ cd /
+
+# External Programs
+map E  edit
+map du shell -p du --max-depth=1 -h --apparent-size
+map dU shell -p du --max-depth=1 -h --apparent-size | sort -rh
+map yp shell -d echo -n %d/%f | xsel -i
+map yd shell -d echo -n %d    | xsel -i
+map yn shell -d echo -n %f    | xsel -i
+
+# Filesystem Operations
+map =  chmod
+
+map cw console rename 
+map A  eval fm.open_console('rename ' + fm.env.cf.basename)
+map I  eval fm.open_console('rename ' + fm.env.cf.basename, position=7)
+
+map pp paste
+map po paste overwrite=True
+map pl paste_symlink relative=False
+map pL paste_symlink relative=True
+map phl paste_hardlink
+
+map dd cut
+map ud uncut
+map da cut mode=add
+map dr cut mode=remove
+
+map yy copy
+map uy uncut
+map ya copy mode=add
+map yr copy mode=remove
+
+# Temporary workarounds
+map dgg eval fm.cut(dirarg=dict(to=0), narg=quantifier)
+map dG  eval fm.cut(dirarg=dict(to=-1), narg=quantifier)
+map dj  eval fm.cut(dirarg=dict(down=1), narg=quantifier)
+map dk  eval fm.cut(dirarg=dict(up=1), narg=quantifier)
+map ygg eval fm.copy(dirarg=dict(to=0), narg=quantifier)
+map yG  eval fm.copy(dirarg=dict(to=-1), narg=quantifier)
+map yj  eval fm.copy(dirarg=dict(down=1), narg=quantifier)
+map yk  eval fm.copy(dirarg=dict(up=1), narg=quantifier)
+
+# Searching
+map /  console search 
+map n  search_next
+map N  search_next forward=False
+map ct search_next order=tag
+map cs search_next order=size
+map ci search_next order=mimetype
+map cc search_next order=ctime
+map cm search_next order=mtime
+map ca search_next order=atime
+
+# Tabs
+map <C-n>     tab_new ~
+map <C-w>     tab_close
+map <TAB>     tab_move 1
+map <S-TAB>   tab_move -1
+map <A-Right> tab_move 1
+map <A-Left>  tab_move -1
+map gt        tab_move 1
+map gT        tab_move -1
+map gn        tab_new ~
+map gc        tab_close
+map <a-1>     tab_open 1
+map <a-2>     tab_open 2
+map <a-3>     tab_open 3
+map <a-4>     tab_open 4
+map <a-5>     tab_open 5
+map <a-6>     tab_open 6
+map <a-7>     tab_open 7
+map <a-8>     tab_open 8
+map <a-9>     tab_open 9
+
+# Sorting
+map or eval fm.settings.sort_reverse ^= True
+map os chain set sort=size;      set sort_reverse=False
+map ob chain set sort=basename;  set sort_reverse=False
+map on chain set sort=natural;   set sort_reverse=False
+map om chain set sort=mtime;     set sort_reverse=False
+map oc chain set sort=ctime;     set sort_reverse=False
+map oa chain set sort=atime;     set sort_reverse=False
+map ot chain set sort=type;      set sort_reverse=False
+
+map oS chain set sort=size;      set sort_reverse=True
+map oB chain set sort=basename;  set sort_reverse=True
+map oN chain set sort=natural;   set sort_reverse=True
+map oM chain set sort=mtime;     set sort_reverse=True
+map oC chain set sort=ctime;     set sort_reverse=True
+map oA chain set sort=atime;     set sort_reverse=True
+map oT chain set sort=type;      set sort_reverse=True
+
+# Settings
+map zc    toggle_option collapse_preview
+map zd    toggle_option sort_directories_first
+map zh    toggle_option show_hidden
+map <C-h> toggle_option show_hidden
+map zi    toggle_option flushinput
+map zm    toggle_option mouse_enabled
+map zp    toggle_option preview_files
+map zP    toggle_option preview_directories
+map zs    toggle_option sort_case_insensitive
+map zv    toggle_option use_preview_script
+map zf    console filter 
+
+# Bookmarks
+map `<bg>   draw_bookmarks
+map '<bg>   draw_bookmarks
+map m<bg>   draw_bookmarks
+map um<bg>  draw_bookmarks
+map `<any>  enter_bookmark %any
+map '<any>  enter_bookmark %any
+map m<any>  set_bookmark %any
+map um<any> unset_bookmark %any
+
+# Beware. I haven't figured out how to make these keybindings pretty yet:
+
+# map +ow shell -d chmod o+w (one mapping for each combination)
+eval import itertools; [cmd("map +%s%s shell -d chmod %s+%s %%s" % (mode+mode)) for mode in itertools.product("ugoa", "rwxXst")]
+
+# map -ow shell -d chmod o+w (one mapping for each combination)
+eval import itertools; [cmd("map -%s%s shell -d chmod %s-%s %%s" % (mode+mode)) for mode in itertools.product("ugoa", "rwxXst")]
+
+
+
+# ===================================================================
+# == Define keys for the console
+# ===================================================================
+# Note: Unmapped keys are passed directly to the console.
+
+# Basic
+cmap <tab>   eval fm.ui.console.tab()
+cmap <s-tab> eval fm.ui.console.tab(-1)
+cmap <C-c>   eval fm.ui.console.close()
+cmap <C-d>   eval fm.ui.console.close()
+cmap <ESC>   eval fm.ui.console.close()
+cmap <CR>    eval fm.ui.console.execute()
+cmap <C-j>   eval fm.ui.console.execute()
+cmap <C-l>   redraw_window
+
+# This special expression allows typing in numerals:
+cmap <allow_quantifiers> false
+
+# Move around
+cmap <left>  eval fm.ui.console.move(left=1)
+cmap <right> eval fm.ui.console.move(right=1)
+cmap <home>  eval fm.ui.console.move(right=0, absolute=True)
+cmap <end>   eval fm.ui.console.move(right=-1, absolute=True)
+cmap <up>    eval fm.ui.console.history_move(-1)
+cmap <down>  eval fm.ui.console.history_move(1)
+
+# And of course the emacs way
+cmap <C-b>   eval fm.ui.console.move(left=1)
+cmap <C-f>   eval fm.ui.console.move(right=1)
+cmap <C-a>   eval fm.ui.console.move(right=0, absolute=True)
+cmap <C-e>   eval fm.ui.console.move(right=-1, absolute=True)
+cmap <C-p>   eval fm.ui.console.history_move(-1)
+cmap <C-n>   eval fm.ui.console.history_move(1)
+
+# Line Editing
+# Note: There are multiple ways to express backspaces.  <backspace> (code 263)
+# and <backspace2> (code 127).  To be sure, use both.
+cmap <backspace>  eval fm.ui.console.delete(-1)
+cmap <backspace2> eval fm.ui.console.delete(-1)
+cmap <delete>     eval fm.ui.console.delete(0)
+cmap <C-h>        eval fm.ui.console.delete(-1)
+cmap <C-d>        eval fm.ui.console.delete(0)
+cmap <C-w>        eval fm.ui.console.delete_word()
+cmap <C-k>        eval fm.ui.console.delete_rest(1)
+cmap <C-u>        eval fm.ui.console.delete_rest(-1)
+cmap <C-y>        eval fm.ui.console.paste()
+
+
+# ===================================================================
+# == Pager Keybindings
+# ===================================================================
+
+# Movement
+pmap j       eval -q fm.ui.browser.pager.move(down=1)
+pmap k       eval -q fm.ui.browser.pager.move(up=1)
+pmap gg      eval -q fm.ui.browser.pager.move(down=0, absolute=True)
+pmap G       eval -q fm.ui.browser.pager.move(down=-1, absolute=True)
+pmap <cr>    eval -q fm.ui.browser.pager.move(down=1)
+pmap <down>  eval -q fm.ui.browser.pager.move(down=1)
+pmap <up>    eval -q fm.ui.browser.pager.move(up=1)
+pmap <home>  eval -q fm.ui.browser.pager.move(down=0, absolute=True)
+pmap <end>   eval -q fm.ui.browser.pager.move(down=-1, absolute=True)
+pmap <C-n>   eval -q fm.ui.browser.pager.move(down=1)
+pmap <C-p>   eval -q fm.ui.browser.pager.move(up=1)
+
+pmap d          eval -q fm.ui.browser.pager.move(down=0.5, pages=True)
+pmap <C-d>      eval -q fm.ui.browser.pager.move(down=0.5, pages=True)
+pmap u          eval -q fm.ui.browser.pager.move(up=  0.5, pages=True)
+pmap <C-u>      eval -q fm.ui.browser.pager.move(up=  0.5, pages=True)
+pmap n          eval -q fm.ui.browser.pager.move(down=1.0, pages=True)
+pmap f          eval -q fm.ui.browser.pager.move(down=1.0, pages=True)
+pmap <pagedown> eval -q fm.ui.browser.pager.move(down=1.0, pages=True)
+pmap p          eval -q fm.ui.browser.pager.move(up=  1.0, pages=True)
+pmap b          eval -q fm.ui.browser.pager.move(up=  1.0, pages=True)
+pmap <pageup>   eval -q fm.ui.browser.pager.move(up=  1.0, pages=True)
+pmap <space>    eval -q fm.ui.browser.pager.move(down=0.8, pages=True)
+
+pmap h       eval -q fm.ui.browser.pager.move(left=4)
+pmap l       eval -q fm.ui.browser.pager.move(right=4)
+pmap <left>  eval -q fm.ui.browser.pager.move(left=4)
+pmap <right> eval -q fm.ui.browser.pager.move(right=4)
+
+# Basic
+pmap q      eval -q fm.ui.close_pager(); fm.ui.close_embedded_pager()
+pmap i      eval -q fm.ui.close_pager(); fm.ui.close_embedded_pager()
+pmap <ESC>  eval -q fm.ui.close_pager(); fm.ui.close_embedded_pager()
+pmap <F3>   eval -q fm.ui.close_pager(); fm.ui.close_embedded_pager()
+pmap E      edit_file
+
+# ===================================================================
+# == Taskview Keybindings
+# ===================================================================
+
+# Movement
+tmap j      eval -q fm.ui.taskview.move(down=1)
+tmap k      eval -q fm.ui.taskview.move(up=1)
+tmap gg     eval -q fm.ui.taskview.move(down=0, absolute=True)
+tmap G      eval -q fm.ui.taskview.move(down=-1, absolute=True)
+tmap <down> eval -q fm.ui.taskview.move(down=1)
+tmap <up>   eval -q fm.ui.taskview.move(up=1)
+tmap <home> eval -q fm.ui.taskview.move(down=0, absolute=True)
+tmap <end>  eval -q fm.ui.taskview.move(down=-1, absolute=True)
+
+# Changing priority and deleting tasks
+tmap J          eval -q fm.ui.taskview.task_move(-1)
+tmap K          eval -q fm.ui.taskview.task_move(0)
+tmap dd         eval -q fm.ui.taskview.task_remove()
+tmap <pagedown> eval -q fm.ui.taskview.task_move(-1)
+tmap <pageup>   eval -q fm.ui.taskview.task_move(0)
+tmap <delete>   eval -q fm.ui.taskview.task_remove()
+
+# A bunch of aliases for closing
+tmap w eval -q fm.ui.close_taskview()
+tmap q eval -q fm.ui.close_taskview()
+tmap Q eval -q fm.ui.close_taskview()
+tmap <esc> eval -q fm.ui.close_taskview()
+tmap <c-c> eval -q fm.ui.close_taskview()
diff --git a/ranger/ext/keybinding_parser.py b/ranger/ext/keybinding_parser.py
index bfd1b6d4..7dbf8c25 100644
--- a/ranger/ext/keybinding_parser.py
+++ b/ranger/ext/keybinding_parser.py
@@ -21,7 +21,7 @@ def parse_keybinding(obj):
 	Translate a keybinding to a sequence of integers
 
 	Example:
-	lol<CR>   =>   (ord('l'), ord('o'), ord('l'), ord('\n'))
+	lol<CR>   =>   (ord('l'), ord('o'), ord('l'), ord('\\n'))
 	          =>   (108, 111, 108, 10)
 	x<A-Left> =>   (120, (27, curses.KEY_LEFT))
 	"""
@@ -63,16 +63,34 @@ def parse_keybinding(obj):
 			for c in bracket_content:
 				yield ord(c)
 
+def construct_keybinding(iterable):
+	"""
+	Does the reverse of parse_keybinding
+	"""
+	result = []
+	for c in iterable:
+		result.append(key_to_string(c))
+	return ''.join(result)
+
+def key_to_string(key):
+	try:
+		return chr(key)
+	except:
+		return '?'
+
 # Arbitrary numbers which are not used with curses.KEY_XYZ
 DIRKEY = 9001
 ANYKEY = 9002
 PASSIVE_ACTION = 9003
 ALT_KEY = 9004
+QUANT_KEY = 9005
+HINT_KEY = 9006
 
 very_special_keys = {
 	'dir': DIRKEY,
 	'any': ANYKEY,
 	'bg': PASSIVE_ACTION,
+	'allow_quantifiers': QUANT_KEY,
 }
 
 special_keys = {
diff --git a/ranger/ext/keybindings.py b/ranger/ext/keybindings.py
new file mode 100644
index 00000000..133da83b
--- /dev/null
+++ b/ranger/ext/keybindings.py
@@ -0,0 +1,111 @@
+# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+from ranger.ext.keybinding_parser import (parse_keybinding,
+	ANYKEY, PASSIVE_ACTION, QUANT_KEY)
+import sys
+
+PY3 = sys.version > '3'
+
+digits = set(range(ord('0'), ord('9')+1))
+
+class KeyMaps(dict):
+	def __init__(self, keybuffer=None):
+		dict.__init__(self)
+		self.keybuffer = keybuffer
+		self.used_keymap = None
+
+	def bind(self, context, keys, leaf):
+		try:
+			pointer = self[context]
+		except:
+			self[context] = pointer = dict()
+		if PY3:
+			keys = keys.encode('utf-8').decode('latin-1')
+		keys = list(parse_keybinding(keys))
+		if not keys:
+			return
+		last_key = keys[-1]
+		for key in keys[:-1]:
+			try:
+				pointer = pointer[key]
+			except:
+				pointer[key] = pointer = dict()
+		pointer[last_key] = leaf
+
+	def use_keymap(self, keymap_name):
+		self.keybuffer.keymap = self.get(keymap_name, dict())
+		if self.used_keymap != keymap_name:
+			self.used_keymap = keymap_name
+			self.keybuffer.clear()
+
+
+class KeyBuffer(object):
+	any_key             = ANYKEY
+	passive_key         = PASSIVE_ACTION
+	quantifier_key      = QUANT_KEY
+	exclude_from_anykey = [27]
+
+	def __init__(self, keymap=None):
+		self.keymap = keymap
+		self.clear()
+
+	def clear(self):
+		self.keys = []
+		self.wildcards = []
+		self.pointer = self.keymap
+		self.result = None
+		self.quantifier = None
+		self.finished_parsing_quantifier = False
+		self.finished_parsing = False
+		self.parse_error = False
+
+		if self.keymap and self.quantifier_key in self.keymap:
+			if self.keymap[self.quantifier_key] == 'false':
+				self.finished_parsing_quantifier = True
+
+	def add(self, key):
+		self.keys.append(key)
+		self.result = None
+		if not self.finished_parsing_quantifier and key in digits:
+			if self.quantifier is None:
+				self.quantifier = 0
+			self.quantifier = self.quantifier * 10 + key - 48 # (48 = ord(0))
+		else:
+			self.finished_parsing_quantifier = True
+
+			moved = True
+			if key in self.pointer:
+				self.pointer = self.pointer[key]
+			elif self.any_key in self.pointer and \
+					key not in self.exclude_from_anykey:
+				self.wildcards.append(key)
+				self.pointer = self.pointer[self.any_key]
+			else:
+				moved = False
+
+			if moved:
+				if isinstance(self.pointer, dict):
+					if self.passive_key in self.pointer:
+						self.result = self.pointer[self.passive_key]
+				else:
+					self.result = self.pointer
+					self.finished_parsing = True
+			else:
+				self.finished_parsing = True
+				self.parse_error = True
+
+	def __str__(self):
+		return "".join("{0:c}".format(c) for c in self.keys)
diff --git a/ranger/ext/tree.py b/ranger/ext/tree.py
deleted file mode 100644
index a954136b..00000000
--- a/ranger/ext/tree.py
+++ /dev/null
@@ -1,136 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-class Tree(object):
-	def __init__(self, dictionary=None, parent=None, key=None):
-		if dictionary is None:
-			self._tree = dict()
-		else:
-			self._tree = dictionary
-		self.key = key
-		self.parent = parent
-
-	def copy(self):
-		"""Create a deep copy"""
-		def deep_copy_dict(dct):
-			dct = dct.copy()
-			for key, val in dct.items():
-				if isinstance(val, dict):
-					dct[key] = deep_copy_dict(val)
-			return dct
-		newtree = Tree()
-		if isinstance(self._tree, dict):
-			newtree._tree = deep_copy_dict(self._tree)
-		else:
-			newtree._tree = self._tree
-		return newtree
-
-	def merge(self, other, copy=False):
-		"""Merge another Tree into a copy of self"""
-		def deep_merge(branch, otherbranch):
-			assert isinstance(otherbranch, dict)
-			if not isinstance(branch, dict):
-				branch = dict()
-			elif copy:
-				branch = branch.copy()
-			for key, val in otherbranch.items():
-				if isinstance(val, dict):
-					if key not in branch:
-						branch[key] = None
-					branch[key] = deep_merge(branch[key], val)
-				else:
-					branch[key] = val
-			return branch
-
-		if isinstance(self._tree, dict) and isinstance(other._tree, dict):
-			content = deep_merge(self._tree, other._tree)
-		elif copy and hasattr(other._tree, 'copy'):
-			content = other._tree.copy()
-		else:
-			content = other._tree
-		return type(self)(content)
-
-	def set(self, keys, value, force=True):
-		"""Sets the element at the end of the path to <value>."""
-		if not isinstance(keys, (list, tuple)):
-			keys = tuple(keys)
-		if len(keys) == 0:
-			self.replace(value)
-		else:
-			fnc = force and self.plow or self.traverse
-			subtree = fnc(keys)
-			subtree.replace(value)
-
-	def unset(self, iterable):
-		chars = list(iterable)
-		first = True
-
-		while chars:
-			if first or isinstance(subtree, Tree) and subtree.empty():
-				top = chars.pop()
-				subtree = self.traverse(chars)
-				assert top in subtree._tree, "no such key: " + chr(top)
-				del subtree._tree[top]
-			else:
-				break
-			first = False
-
-	def empty(self):
-		return len(self._tree) == 0
-
-	def replace(self, value):
-		if self.parent:
-			self.parent[self.key] = value
-		self._tree = value
-
-	def plow(self, iterable):
-		"""Move along a path, creating nonexistant subtrees"""
-		tree = self._tree
-		last_tree = None
-		char = None
-		for char in iterable:
-			try:
-				newtree = tree[char]
-				if not isinstance(newtree, dict):
-					raise KeyError()
-			except KeyError:
-				newtree = dict()
-				tree[char] = newtree
-			last_tree = tree
-			tree = newtree
-		if isinstance(tree, dict):
-			return type(self)(tree, parent=last_tree, key=char)
-		else:
-			return tree
-
-	def traverse(self, iterable):
-		"""Move along a path, raising exceptions when failed"""
-		tree = self._tree
-		last_tree = tree
-		char = None
-		for char in iterable:
-			last_tree = tree
-			try:
-				tree = tree[char]
-			except TypeError:
-				raise KeyError("trying to enter leaf")
-			except KeyError:
-				raise KeyError(repr(char) + " not in tree " + str(tree))
-		if isinstance(tree, dict):
-			return type(self)(tree, parent=last_tree, key=char)
-		else:
-			return tree
-
-	__getitem__ = traverse
diff --git a/ranger/ext/utfwidth.py b/ranger/ext/utfwidth.py
deleted file mode 100644
index 0976fee1..00000000
--- a/ranger/ext/utfwidth.py
+++ /dev/null
@@ -1,113 +0,0 @@
-# -*- encoding: utf8 -*-
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-# Copyright (C) 2004, 2005  Timo Hirvonen
-#
-# 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 <http://www.gnu.org/licenses/>.
-#
-# ----
-# This file contains portions of code from cmus (uchar.c).
-
-NARROW = 1
-WIDE = 2
-
-def uwid(string):
-	"""Return the width of a string"""
-	end = len(string)
-	i = 0
-	width = 0
-	while i < end:
-		bytelen = utf_byte_length(string[i:])
-		width += utf_char_width(string[i:i+bytelen])
-		i += bytelen
-	return width
-
-def uchars(string):
-	"""Return a list with one string for each character"""
-	end = len(string)
-	i = 0
-	result = []
-	while i < end:
-		bytelen = utf_byte_length(string[i:])
-		result.append(string[i:i+bytelen])
-		i += bytelen
-	return result
-
-def utf_byte_length(string):
-	"""Return the byte length of one utf character"""
-	firstord = ord(string[0])
-	if firstord < 0b01111111:
-		return 1
-	if firstord < 0b10111111:
-		return 1  # invalid
-	if firstord < 0b11011111:
-		return 2
-	if firstord < 0b11101111:
-		return 3
-	if firstord < 0b11110100:
-		return 4
-	return 1  # invalid
-
-
-def utf_char_width(string):
-	"""Return the width of a single character"""
-	u = _utf_char_to_int(string)
-	return utf_char_width_(u)
-
-def utf_char_width_(u):
-	if u < 0x1100:
-		return NARROW
-	# Hangul Jamo init. constonants
-	if u <= 0x115F:
-		return WIDE
-	# Angle Brackets
-	if u == 0x2329 or u == 0x232A:
-		return WIDE
-	if u < 0x2e80:
-		return NARROW
-	# CJK ... Yi
-	if u < 0x302A:
-		return WIDE
-	if u <= 0x302F:
-		return NARROW
-	if u == 0x303F or u == 0x3099 or u == 0x309a:
-		return NARROW
-	# CJK ... Yi
-	if u <= 0xA4CF:
-		return WIDE
-	# Hangul Syllables
-	if u >= 0xAC00 and u <= 0xD7A3:
-		return WIDE
-	# CJK Compatibility Ideographs
-	if u >= 0xF900 and u <= 0xFAFF:
-		return WIDE
-	# CJK Compatibility Forms
-	if u >= 0xFE30 and u <= 0xFE6F:
-		return WIDE
-	# Fullwidth Forms
-	if u >= 0xFF00 and u <= 0xFF60 or u >= 0xFFE0 and u <= 0xFFE6:
-		return WIDE
-	# CJK Extra Stuff
-	if u >= 0x20000 and u <= 0x2FFFD:
-		return WIDE
-	# ?
-	if u >= 0x30000 and u <= 0x3FFFD:
-		return WIDE
-	return NARROW  # invalid (?)
-
-def _utf_char_to_int(string):
-	# Squash the last 6 bits of each byte together to an integer
-	u = 0
-	for c in string:
-		u = (u << 6) | (ord(c) & 0b00111111)
-	return u
diff --git a/ranger/ext/waitpid_no_intr.py b/ranger/ext/waitpid_no_intr.py
deleted file mode 100644
index 12fbcbce..00000000
--- a/ranger/ext/waitpid_no_intr.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-from errno import EINTR
-from os import waitpid
-
-def waitpid_no_intr(pid):
-	"""catch interrupts which occur while using os.waitpid"""
-	while True:
-		try:
-			return waitpid(pid, 0)
-		except KeyboardInterrupt:
-			continue
-		except OSError as e:
-			if e.errno == EINTR:
-				continue
-			else:
-				raise
diff --git a/ranger/ext/widestring.py b/ranger/ext/widestring.py
index c7230806..8986be61 100644
--- a/ranger/ext/widestring.py
+++ b/ranger/ext/widestring.py
@@ -1,6 +1,5 @@
 # -*- encoding: utf8 -*-
 # Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-# Copyright (C) 2004, 2005  Timo Hirvonen
 #
 # 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
@@ -14,135 +13,46 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-# ----
-# This file contains portions of code from cmus (uchar.c).
 
 import sys
+from unicodedata import east_asian_width
 
+PY3 = sys.version > '3'
 ASCIIONLY = set(chr(c) for c in range(1, 128))
 NARROW = 1
 WIDE = 2
+WIDE_SYMBOLS = set('WF')
 
-def _utf_char_to_int(string):
-	# Squash the last 6 bits of each byte together to an integer
-	if sys.version > '3':
-		return ord(string)
-	else:
-		# THIS CODE IS INCORRECT
-		u = 0
-		for c in string:
-			u = (u << 6) | (ord(c) & 0b00111111)
-		return u
-
-def utf_char_width_(u):
-	if u < 0x1100:
-		return NARROW
-	# Hangul Jamo init. constonants
-	if u <= 0x115F:
-		return WIDE
-	# Angle Brackets
-	if u == 0x2329 or u == 0x232A:
-		return WIDE
-	if u < 0x2e80:
-		return NARROW
-	# CJK ... Yi
-	if u < 0x302A:
-		return WIDE
-	if u <= 0x302F:
-		return NARROW
-	if u == 0x303F or u == 0x3099 or u == 0x309a:
-		return NARROW
-	# CJK ... Yi
-	if u <= 0xA4CF:
-		return WIDE
-	# Hangul Syllables
-	if u >= 0xAC00 and u <= 0xD7A3:
-		return WIDE
-	# CJK Compatibility Ideographs
-	if u >= 0xF900 and u <= 0xFAFF:
-		return WIDE
-	# CJK Compatibility Forms
-	if u >= 0xFE30 and u <= 0xFE6F:
-		return WIDE
-	# Fullwidth Forms
-	if u >= 0xFF00 and u <= 0xFF60 or u >= 0xFFE0 and u <= 0xFFE6:
-		return WIDE
-	# CJK Extra Stuff
-	if u >= 0x20000 and u <= 0x2FFFD:
-		return WIDE
-	# ?
-	if u >= 0x30000 and u <= 0x3FFFD:
-		return WIDE
-	return NARROW  # invalid (?)
-
-def uchars(string):
-	if sys.version >= '3':
-		return list(string)
-	end = len(string)
-	i = 0
-	result = []
-	while i < end:
-		bytelen = utf_byte_length(string[i:])
-		result.append(string[i:i+bytelen])
-		i += bytelen
-	return result
-
-
-def utf_byte_length(string):
-	"""Return the byte length of one utf character"""
-	if sys.version >= '3':
-		firstord = string.encode("utf-8")[0]
-	else:
-		firstord = ord(string[0])
-	if firstord < 0b01111111:
-		return 1
-	if firstord < 0b10111111:
-		return 1  # invalid
-	if firstord < 0b11011111:
-		return 2
-	if firstord < 0b11101111:
-		return 3
-	if firstord < 0b11110100:
-		return 4
-	return 1  # invalid
+def uwid(string):
+	"""Return the width of a string"""
+	if not PY3:
+		string = string.decode('utf-8', 'ignore')
+	return sum(utf_char_width(c) for c in string)
 
 
 def utf_char_width(string):
 	"""Return the width of a single character"""
-	u = _utf_char_to_int(string)
-	return utf_char_width_(u)
-
-
-def width(string):
-	"""Return the width of a string"""
-	end = len(string)
-	i = 0
-	width = 0
-	while i < end:
-		bytelen = utf_byte_length(string[i:])
-		width += utf_char_width(string[i:i+bytelen])
-		i += bytelen
-	return width
+	if east_asian_width(string) in WIDE_SYMBOLS:
+		return WIDE
+	return NARROW
 
 
 def string_to_charlist(string):
+	"""Return a list of characters with extra empty strings after wide chars"""
 	if not set(string) - ASCIIONLY:
 		return list(string)
-	end = len(string)
-	i = 0
 	result = []
-	py3 = sys.version > '3'
-	while i < end:
-		if py3:
-			result.append(string[i:i+1])
-			i += 1
-		else:
-			bytelen = utf_byte_length(string[i:])
-			result.append(string[i:i+bytelen])
-			i += bytelen
-		if utf_char_width_(_utf_char_to_int(result[-1])) == WIDE:
-			result.append('')
+	if PY3:
+		for c in string:
+			result.append(c)
+			if east_asian_width(c) in WIDE_SYMBOLS:
+				result.append('')
+	else:
+		string = string.decode('utf-8', 'ignore')
+		for c in string:
+			result.append(c.encode('utf-8'))
+			if east_asian_width(c) in WIDE_SYMBOLS:
+				result.append('')
 	return result
 
 
@@ -186,37 +96,6 @@ class WideString(object):
 	def __repr__(self):
 		return '<' + self.__class__.__name__ + " '" + self.string + "'>"
 
-	#def __getslice__(self, a, z):
-		#"""
-		#>>> WideString("asdf")[1:3]
-		#<WideString 'sd'>
-		#>>> WideString("モヒカン")[2:4]
-		#<WideString 'ヒ'>
-		#>>> WideString("モヒカン")[2:5]
-		#<WideString 'ヒ '>
-		#>>> WideString("モヒカン")[1:5]
-		#<WideString ' ヒ '>
-		#>>> WideString("モヒカン")[:]
-		#<WideString 'モヒカン'>
-		#>>> WideString("asdfモ")[0:6]
-		#<WideString 'asdfモ'>
-		#>>> WideString("asdfモ")[0:5]
-		#<WideString 'asdf '>
-		#>>> WideString("asdfモ")[0:4]
-		#<WideString 'asdf'>
-		#"""
-		#if z is None or z >= len(self.chars):
-			#z = len(self.chars) - 1
-		#if a is None or a < 0:
-			#a = 0
-		#if z < len(self.chars) - 1 and self.chars[z] == '':
-			#if self.chars[a] == '':
-				#return WideString(' ' + ''.join(self.chars[a:z - 1]) + ' ')
-			#return WideString(''.join(self.chars[a:z - 1]) + ' ')
-		#if self.chars[a] == '':
-			#return WideString(' ' + ''.join(self.chars[a:z - 1]))
-		#return WideString(''.join(self.chars[a:z]))
-
 	def __getslice__(self, a, z):
 		"""
 		>>> WideString("asdf")[1:3]
diff --git a/ranger/fsobject/__init__.py b/ranger/fsobject/__init__.py
index 5fb4b877..f3de0fcf 100644
--- a/ranger/fsobject/__init__.py
+++ b/ranger/fsobject/__init__.py
@@ -1,20 +1,7 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-"""FileSystemObjects are representation of files and directories
-with fast access to their properties through caching"""
+"""
+FileSystemObjects are representation of files and directories
+with fast access to their properties through caching
+"""
 
 BAD_INFO = '?'
 
diff --git a/ranger/gui/bar.py b/ranger/gui/bar.py
index aa5c9ab4..f2b1571a 100644
--- a/ranger/gui/bar.py
+++ b/ranger/gui/bar.py
@@ -13,7 +13,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from ranger.ext.utfwidth import uwid
+from ranger.ext.widestring import WideString, utf_char_width
+import sys
+PY3 = sys.version > '3'
 
 class Bar(object):
 	left = None
@@ -45,7 +47,7 @@ class Bar(object):
 		# remove elemets from the left until it fits
 		if sumsize > wid:
 			while len(self.left) > 0:
-				leftsize -= len(self.left.pop(-1).string)
+				leftsize -= len(self.left.pop(-1))
 				if leftsize + rightsize <= wid:
 					break
 			sumsize = leftsize + rightsize
@@ -53,7 +55,7 @@ class Bar(object):
 			# remove elemets from the right until it fits
 			if sumsize > wid:
 				while len(self.right) > 0:
-					rightsize -= len(self.right.pop(0).string)
+					rightsize -= len(self.right.pop(0))
 					if leftsize + rightsize <= wid:
 						break
 				sumsize = leftsize + rightsize
@@ -67,18 +69,18 @@ class Bar(object):
 			raise ValueError("Cannot shrink down to that size by cutting")
 		leftsize = self.left.sumsize()
 		rightsize = self.right.sumsize()
-		oversize = leftsize + rightsize - wid - 1
+		oversize = leftsize + rightsize - wid
 		if oversize <= 0:
 			return self.fill_gap(' ', wid, gapwidth=False)
 		nonfixed_items = self.left.nonfixed_items()
 
-		# Shrink items to a minimum size of 1 until there is enough room.
+		# Shrink items to a minimum size until there is enough room.
 		for item in self.left:
 			if not item.fixed:
 				itemlen = len(item)
-				if oversize > itemlen - 1:
-					item.cut_off_to(1)
-					oversize -= (itemlen - 1)
+				if oversize > itemlen - item.min_size:
+					item.cut_off_to(item.min_size)
+					oversize -= (itemlen - item.min_size)
 				else:
 					item.cut_off(oversize)
 					break
@@ -117,7 +119,7 @@ class BarSide(list):
 			if item.fixed:
 				n += len(item)
 			else:
-				n += 1
+				n += item.min_size
 		return n
 
 	def nonfixed_items(self):
@@ -126,19 +128,28 @@ class BarSide(list):
 
 class ColoredString(object):
 	def __init__(self, string, *lst):
-		self.string = string
+		self.string = WideString(string)
 		self.lst = lst
 		self.fixed = False
+		if not len(string):
+			self.min_size = 0
+		elif PY3:
+			self.min_size = utf_char_width(string[0])
+		else:
+			self.min_size = utf_char_width(self.string.chars[0].decode('utf-8'))
 
 	def cut_off(self, n):
 		if n >= 1:
 			self.string = self.string[:-n]
 
 	def cut_off_to(self, n):
-		self.string = self.string[:n]
+		if n < self.min_size:
+			self.string = self.string[:self.min_size]
+		elif n < len(self.string):
+			self.string = self.string[:n]
 
 	def __len__(self):
-		return uwid(self.string)
+		return len(self.string)
 
 	def __str__(self):
-		return self.string
+		return str(self.string)
diff --git a/ranger/gui/context.py b/ranger/gui/context.py
index 20ce2817..a4219806 100644
--- a/ranger/gui/context.py
+++ b/ranger/gui/context.py
@@ -24,8 +24,8 @@ CONTEXT_KEYS = ['reset', 'error', 'badinfo',
 		'space', 'permissions', 'owner', 'group', 'mtime', 'nlink',
 		'scroll', 'all', 'bot', 'top', 'percentage', 'filter',
 		'marked', 'tagged', 'tag_marker', 'cut', 'copied',
-		'help_markup',
-		'seperator', 'key', 'special', 'border',
+		'help_markup', # COMPAT
+		'seperator', 'key', 'special', 'border', # COMPAT
 		'title', 'text', 'highlight', 'bars', 'quotes', 'tab',
 		'keybuffer']
 
diff --git a/ranger/gui/defaultui.py b/ranger/gui/defaultui.py
deleted file mode 100644
index 933b56f7..00000000
--- a/ranger/gui/defaultui.py
+++ /dev/null
@@ -1,136 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-from ranger.gui.ui import UI
-from ranger.gui.widgets.browserview import BrowserView
-from ranger.gui.widgets.titlebar import TitleBar
-from ranger.gui.widgets.console import Console
-from ranger.gui.widgets.statusbar import StatusBar
-from ranger.gui.widgets.taskview import TaskView
-from ranger.gui.widgets.pager import Pager
-
-class DefaultUI(UI):
-	def setup(self):
-		"""Build up the UI by initializing widgets."""
-		# Create a title bar
-		self.titlebar = TitleBar(self.win)
-		self.add_child(self.titlebar)
-
-		# Create the browser view
-		self.browser = BrowserView(self.win, self.settings.column_ratios)
-		self.settings.signal_bind('setopt.column_ratios',
-				self.browser.change_ratios)
-		self.add_child(self.browser)
-
-		# Create the process manager
-		self.taskview = TaskView(self.win)
-		self.taskview.visible = False
-		self.add_child(self.taskview)
-
-		# Create the status bar
-		self.status = StatusBar(self.win, self.browser.main_column)
-		self.add_child(self.status)
-
-		# Create the console
-		self.console = Console(self.win)
-		self.add_child(self.console)
-		self.console.visible = False
-
-		# Create the pager
-		self.pager = Pager(self.win)
-		self.pager.visible = False
-		self.add_child(self.pager)
-
-	def update_size(self):
-		"""resize all widgets"""
-		UI.update_size(self)
-		y, x = self.env.termsize
-
-		self.browser.resize(1, 0, y - 2, x)
-		self.taskview.resize(1, 0, y - 2, x)
-		self.pager.resize(1, 0, y - 2, x)
-		self.titlebar.resize(0, 0, 1, x)
-		self.status.resize(y - 1, 0, 1, x)
-		self.console.resize(y - 1, 0, 1, x)
-
-	def notify(self, *a, **k):
-		return self.status.notify(*a, **k)
-
-	def close_pager(self):
-		if self.console.visible:
-			self.console.focused = True
-		self.pager.close()
-		self.pager.visible = False
-		self.pager.focused = False
-		self.browser.visible = True
-
-	def open_pager(self):
-		if self.console.focused:
-			self.console.focused = False
-		self.pager.open()
-		self.pager.visible = True
-		self.pager.focused = True
-		self.browser.visible = False
-		return self.pager
-
-	def open_embedded_pager(self):
-		self.browser.open_pager()
-		return self.browser.pager
-
-	def close_embedded_pager(self):
-		self.browser.close_pager()
-
-	def open_console(self, string='', prompt=None, position=None):
-		if self.console.open(string, prompt=prompt, position=position):
-			self.status.msg = None
-			self.console.on_close = self.close_console
-			self.console.visible = True
-			self.status.visible = False
-
-	def close_console(self):
-		self.console.visible = False
-		self.status.visible = True
-		self.close_pager()
-
-	def open_taskview(self):
-		self.pager.close()
-		self.pager.visible = False
-		self.pager.focused = False
-		self.console.visible = False
-		self.browser.visible = False
-		self.taskview.visible = True
-		self.taskview.focused = True
-		self.fm.hint('*tasks:* *dd*:remove *J*:move_down *H*:move_up')
-
-	def redraw_main_column(self):
-		self.browser.main_column.need_redraw = True
-
-	def close_taskview(self):
-		self.taskview.visible = False
-		self.browser.visible = True
-		self.taskview.focused = False
-
-	def scroll(self, relative):
-		if self.browser and self.browser.main_column:
-			self.browser.main_column.scroll(relative)
-
-	def throbber(self, string='.', remove=False):
-		if remove:
-			self.titlebar.throbber = type(self.titlebar).throbber
-		else:
-			self.titlebar.throbber = string
-
-	def hint(self, text=None):
-		self.status.hint = text
diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py
index cc2871af..93f3da8e 100644
--- a/ranger/gui/ui.py
+++ b/ranger/gui/ui.py
@@ -20,7 +20,6 @@ import _curses
 
 from .displayable import DisplayableContainer
 from ranger.gui.curses_shortcuts import ascii_only
-from ranger.container.keymap import CommandArgs
 from .mouse_event import MouseEvent
 from ranger.ext.keybinding_parser import ALT_KEY
 
@@ -48,6 +47,7 @@ def _setup_mouse(signal):
 class UI(DisplayableContainer):
 	is_set_up = False
 	load_mode = False
+	is_on = False
 	def __init__(self, env=None, fm=None):
 		self._draw_title = os.environ["TERM"] in TERMINALS_WITH_TITLE
 		os.environ['ESCDELAY'] = '25'   # don't know a cleaner way
@@ -58,8 +58,7 @@ class UI(DisplayableContainer):
 			self.fm = fm
 
 		self.win = curses.initscr()
-		self.env.keymanager.use_context('browser')
-		self.env.keybuffer.clear()
+		self.env.keymaps.use_keymap('browser')
 
 		DisplayableContainer.__init__(self, None)
 
@@ -86,6 +85,7 @@ class UI(DisplayableContainer):
 			self.is_set_up = True
 			self.setup()
 		self.update_size()
+		self.is_on = True
 
 	def suspend(self):
 		"""Turn off curses"""
@@ -99,6 +99,7 @@ class UI(DisplayableContainer):
 		if self.settings.mouse_enabled:
 			_setup_mouse(dict(value=False))
 		curses.endwin()
+		self.is_on = False
 
 	def set_load_mode(self, boolean):
 		boolean = bool(boolean)
@@ -135,37 +136,32 @@ class UI(DisplayableContainer):
 
 		if key < 0:
 			self.env.keybuffer.clear()
-			return
 
-		if DisplayableContainer.press(self, key):
-			return
+		elif not DisplayableContainer.press(self, key):
+			self.env.keymaps.use_keymap('browser')
+			self.press(key)
 
+	def press(self, key):
+		keybuffer = self.env.keybuffer
 		self.status.clear_message()
 
-		self.env.keymanager.use_context('browser')
-		self.env.key_append(key)
-		kbuf = self.env.keybuffer
-		cmd = kbuf.command
-
+		keybuffer.add(key)
 		self.fm.hide_bookmarks()
+		self.browser.draw_hints = not keybuffer.finished_parsing \
+				and keybuffer.finished_parsing_quantifier
 
-		if kbuf.failure:
-			kbuf.clear()
-			return
-		elif not cmd:
-			return
-
-		self.env.cmd = cmd
-
-		if cmd.function:
+		if keybuffer.result is not None:
 			try:
-				cmd.function(CommandArgs.from_widget(self.fm))
-			except Exception as error:
-				self.fm.notify(error)
-			if kbuf.done:
-				kbuf.clear()
-		else:
-			kbuf.clear()
+				self.fm.execute_console(keybuffer.result,
+						wildcards=keybuffer.wildcards,
+						quantifier=keybuffer.quantifier)
+			finally:
+				if keybuffer.finished_parsing:
+					keybuffer.clear()
+		elif keybuffer.finished_parsing:
+			keybuffer.clear()
+			return False
+		return True
 
 	def handle_keys(self, *keys):
 		for key in keys:
@@ -210,10 +206,42 @@ class UI(DisplayableContainer):
 						self.handle_key(key)
 
 	def setup(self):
-		"""
-		Called after an initialize() call.
-		Override this!
-		"""
+		"""Build up the UI by initializing widgets."""
+		from ranger.gui.widgets.browserview import BrowserView
+		from ranger.gui.widgets.titlebar import TitleBar
+		from ranger.gui.widgets.console import Console
+		from ranger.gui.widgets.statusbar import StatusBar
+		from ranger.gui.widgets.taskview import TaskView
+		from ranger.gui.widgets.pager import Pager
+
+		# Create a title bar
+		self.titlebar = TitleBar(self.win)
+		self.add_child(self.titlebar)
+
+		# Create the browser view
+		self.browser = BrowserView(self.win, self.settings.column_ratios)
+		self.settings.signal_bind('setopt.column_ratios',
+				self.browser.change_ratios)
+		self.add_child(self.browser)
+
+		# Create the process manager
+		self.taskview = TaskView(self.win)
+		self.taskview.visible = False
+		self.add_child(self.taskview)
+
+		# Create the status bar
+		self.status = StatusBar(self.win, self.browser.main_column)
+		self.add_child(self.status)
+
+		# Create the console
+		self.console = Console(self.win)
+		self.add_child(self.console)
+		self.console.visible = False
+
+		# Create the pager
+		self.pager = Pager(self.win)
+		self.pager.visible = False
+		self.add_child(self.pager)
 
 	def redraw(self):
 		"""Redraw all widgets"""
@@ -230,11 +258,16 @@ class UI(DisplayableContainer):
 		self.need_redraw = True
 
 	def update_size(self):
-		"""
-		Update self.env.termsize.
-		Extend this method to resize all widgets!
-		"""
+		"""resize all widgets"""
 		self.env.termsize = self.win.getmaxyx()
+		y, x = self.env.termsize
+
+		self.browser.resize(1, 0, y - 2, x)
+		self.taskview.resize(1, 0, y - 2, x)
+		self.pager.resize(1, 0, y - 2, x)
+		self.titlebar.resize(0, 0, 1, x)
+		self.status.resize(y - 1, 0, 1, x)
+		self.console.resize(y - 1, 0, 1, x)
 
 	def draw(self):
 		"""Draw all objects in the container"""
@@ -258,3 +291,66 @@ class UI(DisplayableContainer):
 		"""Finalize every object in container and refresh the window"""
 		DisplayableContainer.finalize(self)
 		self.win.refresh()
+
+	def close_pager(self):
+		if self.console.visible:
+			self.console.focused = True
+		self.pager.close()
+		self.pager.visible = False
+		self.pager.focused = False
+		self.browser.visible = True
+
+	def open_pager(self):
+		if self.console.focused:
+			self.console.focused = False
+		self.pager.open()
+		self.pager.visible = True
+		self.pager.focused = True
+		self.browser.visible = False
+		return self.pager
+
+	def open_embedded_pager(self):
+		self.browser.open_pager()
+		return self.browser.pager
+
+	def close_embedded_pager(self):
+		self.browser.close_pager()
+
+	def open_console(self, string='', prompt=None, position=None):
+		if self.console.open(string, prompt=prompt, position=position):
+			self.status.msg = None
+			self.console.on_close = self.close_console
+			self.console.visible = True
+			self.status.visible = False
+
+	def close_console(self):
+		self.console.visible = False
+		self.status.visible = True
+		self.close_pager()
+
+	def open_taskview(self):
+		self.pager.close()
+		self.pager.visible = False
+		self.pager.focused = False
+		self.console.visible = False
+		self.browser.visible = False
+		self.taskview.visible = True
+		self.taskview.focused = True
+		self.fm.hint('*tasks:* *dd*:remove *J*:move_down *H*:move_up')
+
+	def redraw_main_column(self):
+		self.browser.main_column.need_redraw = True
+
+	def close_taskview(self):
+		self.taskview.visible = False
+		self.browser.visible = True
+		self.taskview.focused = False
+
+	def throbber(self, string='.', remove=False):
+		if remove:
+			self.titlebar.throbber = type(self.titlebar).throbber
+		else:
+			self.titlebar.throbber = string
+
+	def hint(self, text=None):
+		self.status.hint = text
diff --git a/ranger/gui/widgets/__init__.py b/ranger/gui/widgets/__init__.py
index 950b670a..82e592ee 100644
--- a/ranger/gui/widgets/__init__.py
+++ b/ranger/gui/widgets/__init__.py
@@ -1,18 +1,3 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
 from ranger.gui.displayable import Displayable
 
 class Widget(Displayable):
diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py
index 801b79fd..2c865cbe 100644
--- a/ranger/gui/widgets/browsercolumn.py
+++ b/ranger/gui/widgets/browsercolumn.py
@@ -21,6 +21,7 @@ from time import time
 from . import Widget
 from .pager import Pager
 from ranger.fsobject import BAD_INFO
+from ranger.ext.widestring import WideString
 
 class BrowserColumn(Pager):
 	main_column = False
@@ -276,7 +277,6 @@ class BrowserColumn(Pager):
 				this_color.append(drawn.exists and 'good' or 'bad')
 
 			string = drawn.basename
-			from ranger.ext.widestring import WideString
 			wtext = WideString(text)
 			if len(wtext) > space:
 				wtext = wtext[:space - 1] + ellipsis
diff --git a/ranger/gui/widgets/browserview.py b/ranger/gui/widgets/browserview.py
index 09944108..12e1d9bd 100644
--- a/ranger/gui/widgets/browserview.py
+++ b/ranger/gui/widgets/browserview.py
@@ -16,6 +16,7 @@
 """The BrowserView manages a set of BrowserColumns."""
 import curses
 from ranger.ext.signals import Signal
+from ranger.ext.keybinding_parser import key_to_string
 from . import Widget
 from .browsercolumn import BrowserColumn
 from .pager import Pager
@@ -29,6 +30,7 @@ class BrowserView(Widget, DisplayableContainer):
 	stretch_ratios = None
 	need_clear = False
 	old_collapse = False
+	draw_hints = False
 
 	def __init__(self, win, ratios, preview = True):
 		DisplayableContainer.__init__(self, win)
@@ -92,16 +94,17 @@ class BrowserView(Widget, DisplayableContainer):
 		self.need_clear = True
 
 	def draw(self):
+		if self.need_clear:
+			self.win.erase()
+			self.need_redraw = True
+			self.need_clear = False
+		DisplayableContainer.draw(self)
+		if self.settings.draw_borders:
+			self._draw_borders()
 		if self.draw_bookmarks:
 			self._draw_bookmarks()
-		else:
-			if self.need_clear:
-				self.win.erase()
-				self.need_redraw = True
-				self.need_clear = False
-			DisplayableContainer.draw(self)
-			if self.settings.draw_borders:
-				self._draw_borders()
+		elif self.draw_hints:
+			self._draw_hints()
 
 	def finalize(self):
 		if self.pager.visible:
@@ -118,37 +121,6 @@ class BrowserView(Widget, DisplayableContainer):
 			except:
 				pass
 
-	def _draw_bookmarks(self):
-		self.fm.bookmarks.update_if_outdated()
-		self.color_reset()
-		self.need_clear = True
-
-		sorted_bookmarks = sorted(item for item in self.fm.bookmarks \
-			if self.settings.show_hidden_bookmarks or '/.' not in item[1].path)
-
-		def generator():
-			return zip(range(self.hei-1), sorted_bookmarks)
-
-		try:
-			maxlen = max(len(item[1].path) for i, item in generator())
-		except ValueError:
-			return
-		maxlen = min(maxlen + 5, self.wid)
-
-		whitespace = " " * maxlen
-		for line, items in generator():
-			key, mark = items
-			string = " " + key + ": " + mark.path
-			self.addstr(line, 0, whitespace)
-			self.addnstr(line, 0, string, self.wid)
-
-		if self.settings.draw_bookmark_borders:
-			self.win.hline(line+1, 0, curses.ACS_HLINE, maxlen)
-
-			if maxlen < self.wid:
-				self.win.vline(0, maxlen, curses.ACS_VLINE, line+1)
-				self.addch(line+1, maxlen, curses.ACS_LRCORNER)
-
 	def _draw_borders(self):
 		win = self.win
 		self.color('in_browser', 'border')
@@ -196,6 +168,57 @@ class BrowserView(Widget, DisplayableContainer):
 		self.addch(0, right_end, curses.ACS_URCORNER)
 		self.addch(self.hei - 1, right_end, curses.ACS_LRCORNER)
 
+	def _draw_bookmarks(self):
+		self.fm.bookmarks.update_if_outdated()
+		self.color_reset()
+		self.need_clear = True
+
+		sorted_bookmarks = sorted((item for item in self.fm.bookmarks \
+			if self.fm.settings.show_hidden_bookmarks or \
+			'/.' not in item[1].path), key=lambda t: t[0].lower())
+
+		hei = min(self.hei - 1, len(sorted_bookmarks))
+		ystart = self.hei - hei
+
+		maxlen = self.wid
+		self.addnstr(ystart - 1, 0, "mark  path".ljust(self.wid), self.wid)
+
+		whitespace = " " * maxlen
+		for line, items in zip(range(self.hei-1), sorted_bookmarks):
+			key, mark = items
+			string = " " + key + "   " + mark.path
+			self.addstr(ystart + line, 0, whitespace)
+			self.addnstr(ystart + line, 0, string, self.wid)
+
+		self.win.chgat(ystart - 1, 0, curses.A_UNDERLINE)
+
+	def _draw_hints(self):
+		self.need_clear = True
+		hints = []
+		for k, v in self.fm.env.keybuffer.pointer.items():
+			k = key_to_string(k)
+			if isinstance(v, dict):
+				text = '...'
+			else:
+				text = v
+			if text.startswith('hint') or text.startswith('chain hint'):
+				continue
+			hints.append((k, text))
+		hints.sort(key=lambda t: t[1])
+
+		hei = min(self.hei - 1, len(hints))
+		ystart = self.hei - hei
+		self.addnstr(ystart - 1, 0, "key          command".ljust(self.wid),
+				self.wid)
+		self.win.chgat(ystart - 1, 0, curses.A_UNDERLINE)
+		whitespace = " " * self.wid
+		i = ystart
+		for key, cmd in hints:
+			string = " " + key.ljust(11) + " " + cmd
+			self.addstr(i, 0, whitespace)
+			self.addnstr(i, 0, string, self.wid)
+			i += 1
+
 	def _collapse(self):
 		# Should the last column be cut off? (Because there is no preview)
 		if not self.settings.collapse_preview or not self.preview \
diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py
index 00d6828b..127bd7ad 100644
--- a/ranger/gui/widgets/console.py
+++ b/ranger/gui/widgets/console.py
@@ -23,9 +23,8 @@ import re
 from collections import deque
 
 from . import Widget
-from ranger.container.keymap import CommandArgs
 from ranger.ext.direction import Direction
-from ranger.ext.utfwidth import uwid, uchars, utf_char_width_
+from ranger.ext.widestring import uwid
 from ranger.container import History
 from ranger.container.history import HistoryEmptyException
 import ranger
@@ -88,12 +87,8 @@ class Console(Widget):
 
 	def finalize(self):
 		try:
-			if self.fm.py3:
-				xpos = sum(utf_char_width_(ord(c)) for c in self.line[0:self.pos]) \
-					+ len(self.prompt)
-			else:
-				xpos = uwid(self.line[0:self.pos]) + len(self.prompt)
-			self.fm.ui.win.move(self.y, self.x + min(self.wid-1, xpos))
+			pos = uwid(self.line[0:self.pos]) + len(self.prompt)
+			self.fm.ui.win.move(self.y, self.x + min(self.wid-1, pos))
 		except:
 			pass
 
@@ -151,28 +146,9 @@ class Console(Widget):
 		self.line = ''
 
 	def press(self, key):
-		self.env.keymanager.use_context('console')
-		self.env.key_append(key)
-		kbuf = self.env.keybuffer
-		cmd = kbuf.command
-
-		if kbuf.failure:
-			kbuf.clear()
-			return
-		elif not cmd:
-			return
-
-		self.env.cmd = cmd
-
-		if cmd.function:
-			try:
-				cmd.function(CommandArgs.from_widget(self))
-			except Exception as error:
-				self.fm.notify(error)
-			if kbuf.done:
-				kbuf.clear()
-		else:
-			kbuf.clear()
+		self.env.keymaps.use_keymap('console')
+		if not self.fm.ui.press(key):
+			self.type_key(key)
 
 	def type_key(self, key):
 		self.tab_deque = None
@@ -238,14 +214,18 @@ class Console(Widget):
 						maximum=len(self.line) + 1,
 						current=self.pos)
 			else:
-				uc = uchars(self.line)
-				upos = len(uchars(self.line[:self.pos]))
+				if self.fm.py3:
+					uc = list(self.line)
+					upos = len(self.line[:self.pos])
+				else:
+					uc = list(self.line.decode('utf-8', 'ignore'))
+					upos = len(self.line[:self.pos].decode('utf-8', 'ignore'))
 				newupos = direction.move(
 						direction=direction.right(),
 						minimum=0,
 						maximum=len(uc) + 1,
 						current=upos)
-				self.pos = len(''.join(uc[:newupos]))
+				self.pos = len(''.join(uc[:newupos]).encode('utf-8', 'ignore'))
 
 	def delete_rest(self, direction):
 		self.tab_deque = None
@@ -303,11 +283,11 @@ class Console(Widget):
 			self.pos = len(left_part)
 			self.line = left_part + self.line[self.pos + 1:]
 		else:
-			uc = uchars(self.line)
-			upos = len(uchars(self.line[:self.pos])) + mod
-			left_part = ''.join(uc[:upos])
+			uc = list(self.line.decode('utf-8', 'ignore'))
+			upos = len(self.line[:self.pos].decode('utf-8', 'ignore')) + mod
+			left_part = ''.join(uc[:upos]).encode('utf-8', 'ignore')
 			self.pos = len(left_part)
-			self.line = left_part + ''.join(uc[upos+1:])
+			self.line = left_part + ''.join(uc[upos+1:]).encode('utf-8', 'ignore')
 		self.on_line_change()
 
 	def execute(self, cmd=None):
@@ -329,7 +309,8 @@ class Console(Widget):
 			command_class = self._get_cmd_class()
 		except KeyError:
 			if not quiet:
-				self.fm.notify("Invalid command! Press ? for help.", bad=True)
+				error = "Command not found: `%s'" % self.line.split()[0]
+				self.fm.notify(error, bad=True)
 		except:
 			return None
 		else:
diff --git a/ranger/gui/widgets/pager.py b/ranger/gui/widgets/pager.py
index d1bf5918..a95d3254 100644
--- a/ranger/gui/widgets/pager.py
+++ b/ranger/gui/widgets/pager.py
@@ -21,12 +21,6 @@ import re
 from . import Widget
 from ranger.gui import ansi
 from ranger.ext.direction import Direction
-from ranger.container.keymap import CommandArgs
-
-BAR_REGEXP = re.compile(r'\|\d+\?\|')
-QUOTES_REGEXP = re.compile(r'"[^"]+?"')
-SPECIAL_CHARS_REGEXP = re.compile(r'<\w+>|\^[A-Z]')
-TITLE_REGEXP = re.compile(r'^\d+\.')
 
 class Pager(Widget):
 	source = None
@@ -81,35 +75,6 @@ class Pager(Widget):
 	def _draw_line(self, i, line):
 		if self.markup is None:
 			self.addstr(i, 0, line)
-		elif self.markup is 'help':
-			self.addstr(i, 0, line)
-
-			baseclr = ('in_pager', 'help_markup')
-
-			if line.startswith('===='):
-				self.color_at(i, 0, len(line), 'seperator', *baseclr)
-				return
-
-			if line.startswith('        ') and \
-				len(line) >= 16 and line[15] == ' ':
-				self.color_at(i, 0, 16, 'key', *baseclr)
-
-			for m in BAR_REGEXP.finditer(line):
-				start, length = m.start(), m.end() - m.start()
-				self.color_at(i, start, length, 'bars', *baseclr)
-				self.color_at(i, start + 1, length - 2, 'link', *baseclr)
-
-			for m in QUOTES_REGEXP.finditer(line):
-				start, length = m.start(), m.end() - m.start()
-				self.color_at(i, start, length, 'quotes', *baseclr)
-				self.color_at(i, start + 1, length - 2, 'text', *baseclr)
-
-			for m in SPECIAL_CHARS_REGEXP.finditer(line):
-				start, length = m.start(), m.end() - m.start()
-				self.color_at(i, start, length, 'special', *baseclr)
-
-			if TITLE_REGEXP.match(line):
-				self.color_at(i, 0, -1, 'title', *baseclr)
 		elif self.markup == 'ansi':
 			try:
 				self.win.move(i, 0)
@@ -144,28 +109,8 @@ class Pager(Widget):
 					offset=-self.hei + 1)
 
 	def press(self, key):
-		self.env.keymanager.use_context(self.embedded and 'embedded_pager' or 'pager')
-		self.env.key_append(key)
-		kbuf = self.env.keybuffer
-		cmd = kbuf.command
-
-		if kbuf.failure:
-			kbuf.clear()
-			return
-		elif not cmd:
-			return
-
-		self.env.cmd = cmd
-
-		if cmd.function:
-			try:
-				cmd.function(CommandArgs.from_widget(self))
-			except Exception as error:
-				self.fm.notify(error)
-			if kbuf.done:
-				kbuf.clear()
-		else:
-			kbuf.clear()
+		self.env.keymaps.use_keymap('pager')
+		self.fm.ui.press(key)
 
 	def set_source(self, source, strip=False):
 		if self.source and self.source_is_stream:
diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py
index b7ab123c..d8704af3 100644
--- a/ranger/gui/widgets/statusbar.py
+++ b/ranger/gui/widgets/statusbar.py
@@ -267,7 +267,7 @@ class StatusBar(Widget):
 		self.win.move(0, 0)
 		for part in result:
 			self.color(*part.lst)
-			self.addstr(part.string)
+			self.addstr(str(part))
 		self.color_reset()
 
 class Message(object):
diff --git a/ranger/gui/widgets/taskview.py b/ranger/gui/widgets/taskview.py
index e988b08c..805fa270 100644
--- a/ranger/gui/widgets/taskview.py
+++ b/ranger/gui/widgets/taskview.py
@@ -22,7 +22,6 @@ from collections import deque
 
 from . import Widget
 from ranger.ext.accumulator import Accumulator
-from ranger.container.keymap import CommandArgs
 
 class TaskView(Widget, Accumulator):
 	old_lst = None
@@ -96,28 +95,8 @@ class TaskView(Widget, Accumulator):
 		self.fm.loader.move(_from=i, to=to)
 
 	def press(self, key):
-		self.env.keymanager.use_context('taskview')
-		self.env.key_append(key)
-		kbuf = self.env.keybuffer
-		cmd = kbuf.command
-
-		if kbuf.failure:
-			kbuf.clear()
-			return
-		elif not cmd:
-			return
-
-		self.env.cmd = cmd
-
-		if cmd.function:
-			try:
-				cmd.function(CommandArgs.from_widget(self))
-			except Exception as error:
-				self.fm.notify(error)
-			if kbuf.done:
-				kbuf.clear()
-		else:
-			kbuf.clear()
+		self.env.keymaps.use_keymap('taskview')
+		self.fm.ui.press(key)
 
 	def get_list(self):
 		return self.fm.loader.queue
diff --git a/ranger/gui/widgets/titlebar.py b/ranger/gui/widgets/titlebar.py
index d87a0803..c1994b5b 100644
--- a/ranger/gui/widgets/titlebar.py
+++ b/ranger/gui/widgets/titlebar.py
@@ -77,7 +77,7 @@ class TitleBar(Widget):
 
 		pos = 0
 		for i, part in enumerate(self.result):
-			pos += len(part.string)
+			pos += len(part)
 			if event.x < pos:
 				if i < 2:
 					self.fm.enter_dir("~")
@@ -159,5 +159,5 @@ class TitleBar(Widget):
 		self.win.move(0, 0)
 		for part in result:
 			self.color(*part.lst)
-			self.addstr(part.string)
+			self.addstr(str(part))
 		self.color_reset()
diff --git a/ranger/help/__init__.py b/ranger/help/__init__.py
deleted file mode 100644
index f304c7bc..00000000
--- a/ranger/help/__init__.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-"""Help files are located here."""
-
-from inspect import cleandoc
-
-NO_TOPIC = """The help topic was not found."""
-
-NO_HELP = """No help was found.
-
-Possibly the program was invoked with "python -OO" which
-discards all documentation."""
-
-HELP_TOPICS = ('index', 'movement', 'starting', 'console', 'fileop',
-		'invocation')
-
-def get_docstring_of_module(path, module_name):
-	imported = __import__(path, fromlist=[module_name])
-	return getattr(imported, module_name).__doc__
-
-def get_help(topic):
-	try:
-		doc = get_docstring_of_module('ranger.help', topic)
-	except (ImportError, AttributeError):
-		return NO_TOPIC
-	if isinstance(doc, str):
-		return cleandoc(doc)
-	return NO_HELP
-
-def get_help_by_index(i):
-	try:
-		return get_help(HELP_TOPICS[i])
-	except IndexError:
-		return NO_TOPIC
diff --git a/ranger/help/console.py b/ranger/help/console.py
deleted file mode 100644
index 2f3a75c8..00000000
--- a/ranger/help/console.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-"""
-3. The Console
-
-3.1. General Information
-3.2. List of Commands
-3.3. Macros
-3.4. The more complicated Commands in Detail
-
-==============================================================================
-3.1. General Information
-
-The console is opened by pressing ":".  Press <TAB> to cycle through all
-available commands and press <F1> to view help about the current command.
-
-All commands are defined in the file ranger/defaults/commands.py, which
-also contains a detailed specification.
-
-
-==============================================================================
-3.2. List of Commands
-
-All commands except for ":delete" can be abbreviated with the shortest
-unambiguous name, e.g. ":chmod" can be written as ":ch" but not as ":c" since
-it conflicts with ":cd".
-
-
-:cd <dirname>
-      Changes the directory to <dirname>
-
-:chmod <octal_number>
-      Sets the permissions of the selection to the octal number.
-
-:delete
-      Deletes the current selection.
-      "Selection" is defined as all the "marked files" (by default, you
-      can mark files with space or v).  If there are no marked files,
-      use the "current file" (where the cursor is)
-
-:edit <filename>
-      Opens the specified file in the text editor.
-
-:eval <python_code>
-      Evaluates the given code inside ranger. `fm' is a reference to
-      the filemanager instance, `p' is a function to print text.
-
-:filter <string>
-      Displays only files which contain <string> in their basename.
-
-:find <regexp>
-      Quickly find files that match the regexp and execute the first
-	  unambiguous match.
-
-:grep <string>
-      Looks for a string in all marked files or directory.
-      (equivalent to "!grep [some options] -e <string> -r %s | less")
-
-:mark <regexp>
-      Mark all files matching a regular expression.
-
-:unmark <regexp>
-      Unmark all files matching a regular expression.
-
-:mkdir <dirname>
-      Creates a directory with the name <dirname>
-
-:open_with [<program>] [<flags>] [<mode>]
-      Open the current file with the program, flags and mode. |24?| |25?|
-      All arguments are optional.  If none is given, its equivalent to
-      pressing <Enter>
-
-:quit
-      Exits ranger
-
-:rename <newname>
-      Changes the name of the currently highlighted file to <newname>
-
-:search <regexp>
-      Search for a regexp in all file names, like the / key in vim.
-
-:shell [-<flags>] <command>
-      Run the command, optionally with some flags.  |25?|
-      Example: shell -d firefox -safe-mode %s
-      opens (detached from ranger) the selection in firefox' safe-mode
-
-:terminal
-      Spawns "x-terminal-emulator" starting in the current directory.
-
-:touch <filename>
-      Creates a file with the name <filename>
-
-
-==============================================================================
-3.3. Macros
-
-Like in similar filemanagers there are some macros.  Use them in
-commands and they will be replaced with a list of files.
-	%f	the highlighted file
-	%d	the path of the current directory
-	%s	the selected files in the current directory.  If no files are
-		selected, it defaults to the same as %f
-	%t	all tagged files in the current directory
-	%c	the full paths of the currently copied/cut files
-
-The macros %f, %d and %s also have upper case variants, %F, %D and %S,
-which refer to the next tab.  To refer to specific tabs, add a number in
-between. Examples:
-	%D	The path of the directory in the next tab
-	%7s	The selection of the seventh tab
-
-%c is the only macro which ranges out of the current directory. So you may
-"abuse" the copying function for other purposes, like diffing two files which
-are in different directories:
-
-	Yank the file A (type yy), move to the file B and use:
-	:shell -p diff %c %f
-
-
-==============================================================================
-3.4. The more complicated Commands in Detail
-
-3.4.1. "find"
-The find command is different than others: it doesn't require you to
-press <RETURN>.  To speed things up, it tries to guess when you're
-done typing and executes the command right away.
-The key "f" opens the console with ":find "
-
-3.4.2. "shell"
-The shell command accepts flags |25?| as the first argument. This example
-will use the "p"-flag, which pipes the output to the pager:
-	:shell -p cat somefile.txt
-
-There are some shortcuts which open the console with the shell command:
-	"!" opens ":shell "
-	"@" opens ":shell  %s"
-	"#" opens ":shell -p "
-
-3.4.3. "open_with"
-The open_with command is explained in detail in chapter 2.2. |22?|
-
-==============================================================================
-"""
-# vim:tw=78:sw=8:sts=8:ts=8:ft=help
diff --git a/ranger/help/fileop.py b/ranger/help/fileop.py
deleted file mode 100644
index ac23c6d4..00000000
--- a/ranger/help/fileop.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-"""
-4. File Operations
-
-4.1. Destructive Operations
-4.2. The Selection
-4.3. Copying and Pasting
-4.4. Task View
-
-
-==============================================================================
-4.1. Destructive Operations
-
-These are all the operations which can change, and with misuse, possibly
-harm your files:
-
-:chmod <number>    Change the rights of the selection
-:delete            DELETES ALL FILES IN THE SELECTION
-:rename <newname>  Change the name of the current file
-pp, pl, pL, po     Pastes the copied files in different ways
-
-Think twice before using these commands or key combinations.
-
-
-==============================================================================
-4.2. The Selection
-
-Many commands operate on the selection, so it's important to know what
-it is:
-
-If there are marked files:
-    The selection contains all the marked files.
-Otherwise:
-    The selection contains only the highlighted file.
-
-"Marked files" are the files which are slightly indented and marked in
-yellow (in the default color scheme.) You can mark files by typing "v" or
-<space>.
-
-The "highlighted file", or the "current file", is the one below the cursor.
-
-
-==============================================================================
-4.3. Copying and Pasting
-
-	yy	copy the selection
-	dd	cut the selection
-
-	ya, da	add the selection to the copied/cut files
-	yr, dr	remove the selection from the copied/cut files
-
-	pp	paste the copied/cut files. No file will be overwritten.
-		Instead, a "_" character will be appended to the new filename.
-	po	paste the copied/cut files. Existing files are overwritten.
-	pl	create symbolic links to the copied/cut files.
-	pL	create relative symbolic links to the copied/cut files.
-
-The difference between copying and cutting should be intuitive:
-
-When pasting files which are copied, the original file remains unchanged
-in any case.
-
-When pasting files which are cut, the original file will be renamed.
-If renaming is not possible because the source and the destination are
-on separate devices, it will be copied and eventually the source is deleted.
-This implies that a file can only be cut + pasted once.
-
-The files are either copied or cut, never mixed even if you mix "da" and "ya"
-keys (in which case the last command is decisive about whether they are copied
-or cut.)
-
-==============================================================================
-4.4. Task View
-
-The task view lets you manage IO tasks like copying, moving and
-loading directories by changing their priority or stop them.
-
-	w	open or close the task view
-	dd	stop the task
-	J	decrease the priority of the task
-	K	increase the priority of the task
-
-The execution of tasks is not parallel but sequential.  Only the
-topmost task is executed.  Ranger constantly switches between
-handling GUI and executing tasks.  One movement of the throbber at
-the top right represents such a switch, so while the throbber is
-standing still, ranger is locked by a Input/Output operation and
-you will not be able to input any commands.
-
-
-==============================================================================
-"""
-# vim:tw=78:sw=4:sts=8:ts=8:ft=help
diff --git a/ranger/help/index.py b/ranger/help/index.py
deleted file mode 100644
index a10a8406..00000000
--- a/ranger/help/index.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-"""
-                    ranger %s - main help file
-                                                                     k
-    Move around:  Use the cursor keys, or "h" to go left,          h   l
-                  "j" to go down, "k" to go up, "l" to go right.     j
-   Close Ranger:  Type "Q"
-  Specific help:  Type "?", prepended with a number:
-
-	|0?|	This index
-	|1?|	Basic movement and browsing
-	|2?|	Running Files
-	|3?|	The console
-	|4?|	File operations
-	|5?|	Ranger invocation
-
-
-==============================================================================
-0.1. About ranger
-
-Ranger is a free console file manager that gives you greater flexibility
-and a good overview of your files without having to leave your *nix console.
-It visualizes the directory tree in two dimensions: the directory hierarchy
-on one, lists of files on the other, with a preview to the right so you know
-where you'll be going.
-
-The default keys are similar to those of Vim, Emacs and Midnight Commander,
-though Ranger is easily controllable with just the arrow keys or the mouse.
-
-The program is written in Python (2.6 or 3.1) and uses curses for the
-text-based user interface.
-
-
-==============================================================================
-0.2. About these help pages
-
-Annotations like |1?| indicate that the topic is explained in more
-detail in chapter 1. You can type 1? to view it.
-You can type 16? to open chapter 1, paragraph 6.
-
-
-==============================================================================
-0.3. Copying
-
-Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-
-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 <http://www.gnu.org/licenses/>.
-
-
-==============================================================================
-"""
-
-import ranger
-__doc__ %= ranger.__version__
-# vim:tw=78:sw=4:sts=8:ts=8:ft=help
diff --git a/ranger/help/invocation.py b/ranger/help/invocation.py
deleted file mode 100644
index afb1cd27..00000000
--- a/ranger/help/invocation.py
+++ /dev/null
@@ -1,125 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-"""
-5. Ranger invocation
-
-5.1. Command Line Arguments
-5.2. Python Options
-
-
-==============================================================================
-5.1. Command Line Arguments
-
-These options can be passed to ranger when starting it from the
-command line.
-
---version
-      Print the version and exit.
-
--h, --help
-      Print a list of options and exit.
-
--d, --debug
-      Activate the debug mode:  Whenever an error occurs, ranger
-      will exit and print a full backtrace.  The default behaviour
-      is to merely print the name of the exception in the statusbar/log
-      and to try to keep running.
-
--c, --clean
-      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.
-
---copy-config
-      Create copies of the default configuration files in your local
-      configuration directory.  Existing ones will not be overwritten.
-      Possible values: all, apps, commands, keys, options, scope.
-
---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.
-      (This option used to be called --fail-if-run)
-
--r <dir>, --confdir=<dir>
-      Define a different configuration directory.  The default is
-      $HOME/.ranger.
-
--m <n>, --mode=<n>
-      When a filename is supplied, make it run in mode <n> |2|
-
--f <flags>, --flags=<flags>
-      When a filename is supplied, run it with the flags <flags> |2|
-
---choosefile=<target>
-      Makes ranger act like a file chooser. When opneing a file, it will
-      quit and write the name of the selected file to the filename specified
-      as <target>. This file can be read in a script and used to open a
-      certain file which has been chosen with ranger.
-
---choosedir=<target>
-      Makes ranger act like a directory chooser. When ranger quits, it will
-      write the name of the last visited directory to <target>
-
-(Optional) Positional Argument
-      The positional argument should be a path to the directory you
-      want ranger to start in, or the file which you want to run.
-      Only one positional argument is accepted as of now.
-
---
-      Stop looking for options.  All following arguments are treated as
-      positional arguments.
-
-Examples:
-      ranger episode1.avi
-      ranger --debug /usr/bin
-      ranger --confdir=~/.config/ranger --fail-unless-cd
-
-See the README on how to integrate ranger with various external programs.
-
-
-==============================================================================
-5.2. Python Options
-
-Ranger makes use of python optimize flags.  To use them, run ranger like this:
-      PYTHONOPTIMIZE=1 ranger
-An alternative is:
-      python -O `which ranger`
-Or you could change the first line of the ranger script and add -O/-OO.
-The first way is the recommended one.  Of course you can make an alias or
-a shell fuction to save typing.
-
-Using PYTHONOPTIMIZE=1 (-O) will make python discard assertion statements.
-Assertions are little pieces of code which are helpful for finding errors,
-but unless you're touching sensitive parts of ranger, you may want to
-disable them to save some computing power.
-
-Using PYTHONOPTIMIZE=2 (-OO) will additionally discard any docstrings.
-In ranger, most built-in documentation (F1/? keys) is implemented with
-docstrings.  Use this option if you don't need the documentation.
-
-Examples:
-      PYTHONOPTIMIZE=1 ranger episode1.avi
-      PYTHONOPTIMIZE=2 ranger --debug /usr/bin
-      python -OO `which ranger` --confdir=~/.config/ranger --fail-unless-cd
-
-Note: The author expected "-OO" to reduce the memory usage, but that
-doesn't seem to happen.
-
-
-==============================================================================
-"""
-# vim:tw=78:sw=8:sts=8:ts=8:ft=help
diff --git a/ranger/help/movement.py b/ranger/help/movement.py
deleted file mode 100644
index e7c3a87c..00000000
--- a/ranger/help/movement.py
+++ /dev/null
@@ -1,232 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-"""
-1. Basic movement and browsing
-
-1.1. Move around
-1.2. Browser control
-1.3. Searching
-1.4. Sorting
-1.5. Bookmarks
-1.6. Tabs
-1.7. Mouse usage
-1.8. Misc keys
-1.9. Previews
-
-
-==============================================================================
-1.1. Ranger has similar movement keys as vim:
-
-Note: A ^ stands for the Ctrl key.
-
-	k	move up
-	j	move down
-	h	move left (in browser: move one directory up)
-	l	move right (in browser: enter this directory, or run this file)
-
-	^U	move half the screen up
-	^D	move half the screen down
-	H	in browser: move back in history
-	L	in browser: move forward in history
-
-	gg	move to the top
-	G	move to the bottom
-	%	move to the middle
-
-By prefixing a number, you can give more precise commands, eg:
-
-	2^D	move 2 pages down
-	5gg	move to the 5th line
-	3h	move 3 characters to the left, or move 3 directories up
-	30%	move to 30% of the screen
-
-Using arrow keys is equivalent of using h/j/k/l in most cases.
-An exception to this is the console, where you can move around with
-arrow keys and pressing letters will insert the letter into the console.
-
-Special keys like Home, Page Up,.. work as expected.
-
-These keys work like in vim:
-
-	^U      move half the screen up
-	^D      move half the screen down
-	^B      move up by one screen
-	^F      move down by one screen
-
-This keys can be used to make movements beyond the current directory
-
-	]	move down in the parent directory
-	[	move up in the parent directory
-
-	}	traverse the directory tree, visiting each directory
-	{	traverse in the other direction. (not implemented yet,
-		currently this only moves back in history)
-
-	gl	move to the real path of the current directory (resolving symlinks)
-	gL	move to the real path of the selected file or directory
-
-
-==============================================================================
-1.2. Browser control
-
-	?	view the help screen
-	R	reload the current directory
-	^R	clear the cache and reload the view
-	^L	redraw the window
-	:	open the console |3?|
-	z	toggle options
-	u	undo certain things (unyank, unmark,...)
-
-	i	inspect the content of the file
-	E	edit the file
-	S	open a shell, starting in the current directory
-
-Marking files allows you to use operations on multiple files at once.
-If there are any marked files in this directory, "yy" will copy them instead
-of the file you're pointing at.
-
-	<Space> mark a file
-	v	toggle all marks
-	V, uv	remove all marks
-	^V	mark files in a specific direction
-		e.g. ^Vgg marks all files from the current to the top
-	u^V	unmark files in a specific direction
-
-By "tagging" files, you can highlight them and mark them to be
-special in whatever context you want.  Tags are persistent across sessions.
-
-	t	tag/untag the selection
-	T	untag the selection
-
-Midnight Commander lovers will find that the function keys work similarly.
-There is no menu or drop down though.
-
-	<F1>	view the help screen
-	<F3>	view the file
-	<F4>	edit the file
-	<F5>	copy the selection
-	<F6>	cut the selection
-	<F7>	create a directory
-	<F8>	delete the selection
-	<F10>	exit ranger
-
-
-==============================================================================
-1.3. Searching
-
-Use "/" to open the search console. |3?|
-Enter a string and press <Enter> to search for it in all currently
-visible files. Pressing "n" will move you to the next occurance,
-"N" to the previous one.
-
-You can search for more than just strings:
-	cc	cycle through all files by their ctime (last inode change)
-	cm	cycle by mime type, connecting similar files
-	cs	cycle by size, large items first
-	ct	search tagged files
-
-
-==============================================================================
-1.4. Sorting
-
-To sort files, type "o" suffixed with a key that stands for a certain
-sorting mode. By typing any of those keys in upper case, the order will
-be reversed.
-
-	os	sort by size
-	ob, on	sort by basename
-	om	sort by mtime (last modification)
-	ot	sort by mime type
-	or	reverse order
-
-
-==============================================================================
-1.5. Bookmarks
-
-Type "m<key>" to bookmark the current directory. You can re-enter this
-directory by typing "`<key>". <key> can be any letter or digit.  Unlike vim,
-both lowercase and uppercase bookmarks are persistent.
-
-Each time you jump to a bookmark, the special bookmark at key ` will be set
-to the last directory. So typing "``" gets you back to where you were before.
-
-Note: The ' key is equivalent to `.
-
-
-==============================================================================
-1.6. Tabs
-
-Tabs are used to work in different directories in the same Ranger instance.
-In Ranger, tabs are very simple though and only store the directory path.
-
-	gt	Go to the next tab. (also TAB)
-	gT	Go to the previous tab. (also Shift+TAB)
-	gn, ^N	Create a new tab
-	g<N>	Open a tab. N has to be a number from 1 to 9.
-		If the tab doesn't exist yet, it will be created.
-		On most terminals, Alt-1, Alt-2, etc., also work.
-	gc, ^W	Close the current tab.  The last tab cannot be closed.
-
-
-==============================================================================
-1.7. Mouse usage
-
-The mouse can be used to quickly enter directories which you point at,
-or to scroll around with the mouse wheel. The implementation of the mouse
-wheel is not stable due to problems with the ncurses library, but "it works
-on my machine".
-
-Clicking into the preview window will usually run the file. |2?|
-
-
-==============================================================================
-1.8. Misc keys
-
-	W	Display the message log
-	du	Display the disk usage of the current directory
-	cd	Open the console with ":cd "
-	cw	Open the console with ":rename "
-	A	Open the console with ":rename <current filename>"
-	I	Same as A, put the cursor at the beginning of the filename
-	yp	Copy the path of the file (with xsel)
-	yn	Copy the base name of the file (with xsel)
-	yd	Copy the directory name of the file (with xsel)
-
-
-==============================================================================
-1.9. Previews
-
-By default, only text files are previewed, but you can enable external
-preview scripts by creating ~/.config/ranger/scope.sh (see preview_script
-option.)  This script will then be executed each time you attempt to
-preview a file.
-
-Fetch the default scope.sh (from ranger/data/scope.sh) by running
-	ranger --copy-config=scope
-
-This default script contains more documentation and calls to the
-programs "lynx" and "elinks" for html, "highlight" for text/code,
-"img2txt" for images, "atool" for archives, "pdftotext" for PDFs and
-"mediainfo" for video and audio files.
-
-Install these programs (just the ones you need) and scope.sh will
-automatically use them.  Make sure to also have the options
-"use_preview_script" and "preview_files" turned on.
-
-
-==============================================================================
-"""
-# vim:tw=78:sw=4:sts=8:ts=8:ft=help
diff --git a/ranger/help/starting.py b/ranger/help/starting.py
deleted file mode 100644
index dbc6b6b5..00000000
--- a/ranger/help/starting.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-"""
-2. Running Files
-
-2.1. How to run files
-2.2. The "open_with" command
-2.2. Programs
-2.4. Modes
-2.5. Flags
-
-
-==============================================================================
-2.1. How to run files
-
-While highlighting a file, press the "l" key to fire up the automatic
-filetype detection mechanism and attempt to start the file.
-
-	l	run the selection
-	r	open the console with ":open_with"
-
-Note: The selection means, if there are marked files in this directory,
-use them.  Otherwise use the file under the cursor.
-
-
-==============================================================================
-2.2. The "open_with" command
-
-If the automatic filetype detection fails or starts the file in a wrong
-way, you can press "r" to manually tell ranger how to run it.
-
-The programs and modes can be defined in the apps.py, giving you a
-high level interface for running files.
-
-Syntax: :open_with <program> <flags> <mode>
-You can leave out parameters or change the order.
-
-Examples:
-Open this file with vim:
-	:open_with vim
-Run this file like with "./file":
-	:open_with self
-Open this file as usual but pipe the output to "less"
-	:open_with p
-Open this file with mplayer with the "detached" flag:
-	:open_with mplayer d
-Open this file with totem in mode 1, will not detach the process (flag D)
-but discard the output (flag s).
-	:open_with totem 1 Ds
-
-The parameters <program>, <flags> and <mode> are explained in the
-following paragraphs
-
-
-==============================================================================
-2.3. Programs
-
-Programs have to be defined in ranger/defaults/apps.py.  Each function
-in the class CustomApplications which starts with "app_" can be used
-as a program in the "open_with" command.
-
-You're encouraged to add your own program definitions to the list.  Refer to
-the existing examples in the apps.py, it should be easy to adapt it for your
-purposes.
-
-
-==============================================================================
-2.4. Modes
-
-Sometimes there are multiple variants to open a file.  For example, ranger
-gives you 2 ways of opening a video (by default):
-
-	0	windowed
-	1	fullscreen
-
-By specifying a mode, you can select one of those.  The "l" key will
-start a file in mode 0. "4l" will start the file in mode 4 etc.
-You can specify a mode in the "open_with" command by simply adding
-the number.  Eg: ":open_with mplayer 1" or ":open_with 1"
-
-For a list of all programs and modes, see ranger/defaults/apps.py
-
-
-==============================================================================
-2.5. Flags
-
-Flags give you a way to modify the behaviour of the spawned process.
-
-	s	Silent mode.  Output will be discarded.
-	d	Detach the process.  (Run in background)
-	p	Redirect output to the pager
-	w	Wait for an enter-press when the process is done
-	c	Run the current file only, even when more files are marked
-
-For example, ":open_with p" will pipe the output of that process into
-the pager.
-
-An uppercase flag has the opposite effect.  If a program will be detached by
-default, use ":open_with D" to not detach it.
-
-Note: Some combinations don't make sense, eg: "vim d" would open the file in
-vim and detach it.  Since vim is a console application, you loose grip
-of that process when you detach it.  It's up to you to do such sanity checks.
-
-
-==============================================================================
-"""
-# vim:tw=78:sw=4:sts=8:ts=8:ft=help
diff --git a/setup.py b/setup.py
index e63e28d2..0d72978b 100755
--- a/setup.py
+++ b/setup.py
@@ -29,7 +29,7 @@ if __name__ == '__main__':
 		url='http://savannah.nongnu.org/projects/ranger',
 		scripts=['scripts/ranger'],
 		data_files=[('share/man/man1', ['doc/ranger.1'])],
-		package_data={'ranger': ['data/*']},
+		package_data={'ranger': ['data/*', 'defaults/rc.conf']},
 		packages=('ranger',
 		          'ranger.api',
 		          'ranger.colorschemes',
@@ -39,5 +39,4 @@ if __name__ == '__main__':
 		          'ranger.ext',
 		          'ranger.fsobject',
 		          'ranger.gui',
-		          'ranger.gui.widgets',
-		          'ranger.help'))
+		          'ranger.gui.widgets'))
diff --git a/test/all_benchmarks.py b/test/all_benchmarks.py
deleted file mode 100755
index a3612701..00000000
--- a/test/all_benchmarks.py
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env python
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-"""
-Run all the benchmarks inside this directory.
-Usage: ./all_benchmarks.py [count] [regexp-filters...]
-"""
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-import re
-import time
-
-if __name__ == '__main__':
-	count   = int(sys.argv[1]) if len(sys.argv) > 1 else 10
-	regexes = [re.compile(fltr) for fltr in sys.argv[2:]]
-	modules = (fname[:-3] for fname in os.listdir(sys.path[0]) \
-			if fname[:3] == 'bm_' and fname[-3:] == '.py')
-
-	def run_benchmark(cls, methodname):
-		full_method_name = "{0}.{1}".format(cls.__name__, methodname)
-		if all(re.search(full_method_name) for re in regexes):
-			method = getattr(cls(), methodname)
-			t1 = time.time()
-			try:
-				method(count)
-			except:
-				print("{0} failed!".format(full_method_name))
-				raise
-			else:
-				t2 = time.time()
-				print("{0:60}: {1:10}s".format(full_method_name, t2 - t1))
-
-	for val in [__import__(module) for module in modules]:
-		for cls in vars(val).values():
-			if type(cls) == type:
-				for methodname in vars(cls):
-					if methodname.startswith('bm_'):
-						run_benchmark(cls, methodname)
diff --git a/test/all_tests.py b/test/all_tests.py
deleted file mode 100755
index 0c184df5..00000000
--- a/test/all_tests.py
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env python
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-"""
-Run all the tests inside this directory as a test suite.
-Usage: ./all_tests.py [verbosity]
-"""
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-import unittest
-
-if __name__ == '__main__':
-	verbosity = int(sys.argv[1]) if len(sys.argv) > 1 else 1
-	tests     = (fname[:-3] for fname in os.listdir(sys.path[0]) \
-	             if fname[:3] == 'tc_' and fname[-3:] == '.py')
-	suite     = unittest.TestLoader().loadTestsFromNames(tests)
-	result    = unittest.TextTestRunner(verbosity=verbosity).run(suite)
-	if len(result.errors + result.failures) > 0:
-		sys.exit(1)
diff --git a/test/bm_human_readable.py b/test/bm_human_readable.py
deleted file mode 100644
index ef400774..00000000
--- a/test/bm_human_readable.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-from ranger.ext.human_readable import *
-
-# The version before 2010/06/24:
-import math
-UNITS = 'BKMGTP'
-MAX_EXPONENT = len(UNITS) - 1
-def human_readable_old(byte, seperator=' '):
-	if not byte:
-		return '0'
-
-	exponent = int(math.log(byte, 2) / 10)
-	flt = round(float(byte) / (1 << (10 * exponent)), 2)
-
-	if exponent > MAX_EXPONENT:
-		return '>9000' # off scale
-
-	if int(flt) == flt:
-		return '%.0f%s%s' % (flt, seperator, UNITS[exponent])
-
-	else:
-		return '%.2f%s%s' % (flt, seperator, UNITS[exponent])
-
-class benchmark_human_readable(object):
-	def bm_current(self, n):
-		for i in range(n):
-			human_readable((128 * i) % 2**50)
-
-	def bm_old(self, n):
-		for i in range(n):
-			human_readable_old((128 * i) % 2**50)
diff --git a/test/bm_loader.py b/test/bm_loader.py
deleted file mode 100644
index 552954a7..00000000
--- a/test/bm_loader.py
+++ /dev/null
@@ -1,174 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-from ranger.core.loader import Loader
-from ranger.fsobject import Directory, File
-from ranger.ext.openstruct import OpenStruct
-import os.path
-from ranger.shared import FileManagerAware, SettingsAware
-from testlib import Fake
-from os.path import realpath, join, dirname
-from subprocess import Popen, PIPE
-TESTDIR = realpath(join(dirname(__file__), '/usr/include'))
-
-def skip(x):
-	return
-
-def raw_load_content(self):
-	"""
-	The method which is used in a Directory object to load stuff.
-	Keep this up to date!
-	"""
-
-	from os.path import join, isdir, basename
-	from os import listdir
-	import ranger.ext.mount_path
-
-	self.loading = True
-	self.load_if_outdated()
-
-	try:
-		if self.exists and self.runnable:
-			# 0.003s:
-			self.mount_path = ranger.ext.mount_path.mount_path(self.path)
-
-			# 0.1s:
-			filenames = []
-			for fname in listdir(self.path):
-				if not self.settings.show_hidden:
-					hfilter = self.settings.hidden_filter
-					if hfilter:
-						if isinstance(hfilter, str) and hfilter in fname:
-							continue
-						if hasattr(hfilter, 'search') and \
-							hfilter.search(fname):
-							continue
-				if isinstance(self.filter, str) and self.filter \
-						and self.filter not in fname:
-					continue
-				filenames.append(join(self.path, fname))
-			# ---
-
-			self.load_content_mtime = os.stat(self.path).st_mtime
-
-			marked_paths = [obj.path for obj in self.marked_items]
-
-			# 2.85s:
-			files = []
-			for name in filenames:
-				if isdir(name):
-					try:
-						item = self.fm.env.get_directory(name)
-					except:
-						item = Directory(name)
-				else:
-					item = File(name)
-				item.load_if_outdated()
-				files.append(item)
-
-			# 0.2s
-			self.disk_usage = sum(f.size for f in files if f.is_file)
-
-			self.scroll_offset = 0
-			self.filenames = filenames
-			self.files = files
-
-			self._clear_marked_items()
-			for item in self.files:
-				if item.path in marked_paths:
-					self.mark_item(item, True)
-				else:
-					self.mark_item(item, False)
-
-			self.sort()
-
-			if len(self.files) > 0:
-				if self.pointed_obj is not None:
-					self.sync_index()
-				else:
-					self.move(to=0)
-		else:
-			self.filenames = None
-			self.files = None
-
-		self.cycle_list = None
-		self.content_loaded = True
-		self.determine_infostring()
-		self.correct_pointer()
-
-	finally:
-		self.loading = False
-
-
-class benchmark_load(object):
-	def __init__(self):
-		self.loader = Loader()
-		fm = OpenStruct(loader=self.loader)
-		SettingsAware.settings = Fake()
-		FileManagerAware.fm = fm
-		self.dir = Directory(TESTDIR)
-
-	def bm_run(self, n):
-		for _ in range(n):
-			self.dir.load_content(schedule=True)
-			while self.loader.has_work():
-				self.loader.work()
-
-
-@skip
-class benchmark_raw_load(object):
-	def __init__(self):
-		SettingsAware.settings = Fake()
-		self.dir = Directory(TESTDIR)
-
-	def bm_run(self, n):
-		generator = self.dir.load_bit_by_bit()
-		for _ in range(n):
-			raw_load_content(self.dir)
-
-def bm_loader(n):
-	"""Do some random calculation"""
-	tloader = benchmark_load(N)
-	traw = benchmark_raw_load(N)
-
-class benchmark_load_varieties(object):
-	def bm_ls(self, n):
-		for _ in range(n):
-			Popen(["ls", '-l', TESTDIR], stdout=open(os.devnull, 'w')).wait()
-
-	def bm_os_listdir_stat(self, n):
-		for _ in range(n):
-			for f in os.listdir(TESTDIR):
-				path = os.path.join(TESTDIR, f)
-				os.stat(path)
-
-	def bm_os_listdir(self, n):
-		for _ in range(n):
-			for f in os.listdir(TESTDIR):
-				path = os.path.join(TESTDIR, f)
-
-	def bm_os_listdir_stat_listdir(self, n):
-		for _ in range(n):
-			for f in os.listdir(TESTDIR):
-				path = os.path.join(TESTDIR, f)
-				os.stat(path)
-				if os.path.isdir(path):
-					os.listdir(path)
diff --git a/test/tc_ansi.py b/test/tc_ansi.py
deleted file mode 100644
index 0a6ad8b1..00000000
--- a/test/tc_ansi.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright (C) 2010  David Barnett <davidbarnett2@gmail.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-if __name__ == '__main__': from __init__ import init; init()
-
-import unittest
-from ranger.gui import ansi
-
-class TestDisplayable(unittest.TestCase):
-	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"
-		expected = "X"
-		self.assertEqual(ansi.char_slice(ansi_string, 0, 1), expected)
-
-if __name__ == '__main__':
-	unittest.main()
diff --git a/test/tc_bookmarks.py b/test/tc_bookmarks.py
deleted file mode 100644
index 59435f06..00000000
--- a/test/tc_bookmarks.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-from os.path import realpath, join, dirname
-import unittest
-import os
-import time
-
-from ranger.container.bookmarks import Bookmarks
-
-TESTDIR = realpath(join(dirname(__file__), 'testdir'))
-BMFILE = join(TESTDIR, 'bookmarks')
-
-class TestDisplayable(unittest.TestCase):
-	def setUp(self):
-		try:
-			os.remove(BMFILE)
-		except:
-			pass
-
-	def tearDown(self):
-		try:
-			os.remove(BMFILE)
-		except:
-			pass
-	
-	def test_adding_bookmarks(self):
-		bm = Bookmarks(BMFILE, str, autosave=False)
-		bm.load()
-		bm['a'] = 'fooo'
-		self.assertEqual(bm['a'], 'fooo')
-
-	def test_sharing_bookmarks_between_instances(self):
-		bm = Bookmarks(BMFILE, str, autosave=True)
-		bm2 = Bookmarks(BMFILE, str, autosave=True)
-
-		bm.load()
-		bm2.load()
-		bm['a'] = 'fooo'
-		self.assertRaises(KeyError, bm2.__getitem__, 'a')
-
-		bm.save()
-		bm2.load()
-		self.assertEqual(bm['a'], bm2['a'])
-
-		bm2['a'] = 'bar'
-
-		bm.save()
-		bm2.save()
-		bm.load()
-		bm2.load()
-
-		self.assertEqual(bm['a'], bm2['a'])
-	
-	def test_messing_around(self):
-		bm = Bookmarks(BMFILE, str, autosave=False)
-		bm2 = Bookmarks(BMFILE, str, autosave=False)
-
-		bm.load()
-		bm['a'] = 'car'
-
-		bm2.load()
-		self.assertRaises(KeyError, bm2.__getitem__, 'a')
-
-		bm2.save()
-		bm.update()
-		bm.save()
-		bm.load()
-		bm2.load()
-
-		self.assertEqual(bm['a'], bm2['a'])
-
-if __name__ == '__main__':
-	unittest.main()
diff --git a/test/tc_colorscheme.py b/test/tc_colorscheme.py
deleted file mode 100644
index eefb1e4f..00000000
--- a/test/tc_colorscheme.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-from unittest import TestCase, main
-import random
-import ranger.colorschemes
-from ranger.gui.colorscheme import ColorScheme
-from ranger.gui.context import CONTEXT_KEYS
-
-class Test(TestCase):
-	def setUp(self):
-		import random
-		import curses
-		curses.COLORS = 88
-		schemes = []
-		for key, mod in vars(ranger.colorschemes).items():
-			if type(mod) == type(random):
-				for key, var in vars(mod).items():
-					if type(var) == type and issubclass(var, ColorScheme) \
-							and var != ColorScheme:
-						schemes.append(var)
-		self.schemes = set(schemes)
-
-	def test_colorschemes(self):
-		def test(scheme):
-			scheme.get()  # test with no arguments
-
-			for i in range(300):  # test with a bunch of random (valid) arguments
-				sample = random.sample(CONTEXT_KEYS, random.randint(2, 9))
-				scheme.get(*sample)
-
-		for scheme in self.schemes:
-			test(scheme())
-
-if __name__ == '__main__': main()
diff --git a/test/tc_direction.py b/test/tc_direction.py
deleted file mode 100644
index 16c26dab..00000000
--- a/test/tc_direction.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-import unittest
-from ranger.ext.direction import Direction
-from ranger.ext.openstruct import OpenStruct
-
-class TestDirections(unittest.TestCase):
-	def test_symmetry(self):
-		d1 = Direction(right=4, down=7, relative=True)
-		d2 = Direction(left=-4, up=-7, absolute=False)
-
-		def subtest(d):
-			self.assertEqual(4, d.right())
-			self.assertEqual(7, d.down())
-			self.assertEqual(-4, d.left())
-			self.assertEqual(-7, d.up())
-			self.assertEqual(True, d.relative())
-			self.assertEqual(False, d.absolute())
-
-			self.assertTrue(d.horizontal())
-			self.assertTrue(d.vertical())
-
-		subtest(d1)
-		subtest(d2)
-
-	def test_conflicts(self):
-		d3 = Direction(right=5, left=2, up=3, down=6,
-				absolute=True, relative=True)
-		self.assertEqual(d3.right(), -d3.left())
-		self.assertEqual(d3.left(), -d3.right())
-		self.assertEqual(d3.up(), -d3.down())
-		self.assertEqual(d3.down(), -d3.up())
-		self.assertEqual(d3.absolute(), not d3.relative())
-		self.assertEqual(d3.relative(), not d3.absolute())
-
-	def test_copy(self):
-		d = Direction(right=5)
-		c = d.copy()
-		self.assertEqual(c.right(), d.right())
-		d['right'] += 3
-		self.assertNotEqual(c.right(), d.right())
-		c['right'] += 3
-		self.assertEqual(c.right(), d.right())
-
-		self.assertFalse(d.vertical())
-		self.assertTrue(d.horizontal())
-
-#	Doesn't work in python2?
-#	def test_duck_typing(self):
-#		dct = dict(right=7, down=-3)
-#		self.assertEqual(-7, Direction.left(dct))
-#		self.assertEqual(3, Direction.up(dct))
-
-	def test_move(self):
-		d = Direction(pages=True)
-		self.assertEqual(3, d.move(direction=3))
-		self.assertEqual(5, d.move(direction=3, current=2))
-		self.assertEqual(15, d.move(direction=3, pagesize=5))
-		self.assertEqual(9, d.move(direction=3, pagesize=5, maximum=10))
-		self.assertEqual(18, d.move(direction=9, override=2))
-		d2 = Direction(absolute=True)
-		self.assertEqual(5, d2.move(direction=9, override=5))
-
-	def test_select(self):
-		d = Direction(down=3)
-		lst = list(range(100))
-		self.assertEqual((6, [3,4,5,6]), d.select(current=3, pagesize=10, override=None, lst=lst))
-		d = Direction(down=3, pages=True)
-		self.assertEqual((9, [3,4,5,6,7,8,9]), d.select(current=3, pagesize=2, override=None, lst=lst))
-
-if __name__ == '__main__':
-	unittest.main()
-
diff --git a/test/tc_directory.py b/test/tc_directory.py
deleted file mode 100644
index a43ac89d..00000000
--- a/test/tc_directory.py
+++ /dev/null
@@ -1,124 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-from os.path import realpath, join, dirname
-
-from ranger import fsobject
-from ranger.fsobject.file import File
-from ranger.fsobject.directory import Directory
-from ranger.core.shared import SettingsAware
-
-SettingsAware._setup()
-
-TESTDIR = realpath(join(dirname(__file__), 'testdir'))
-TESTFILE = join(TESTDIR, 'testfile5234148')
-NONEXISTANT_DIR = join(TESTDIR, 'nonexistant')
-
-import unittest
-class Test1(unittest.TestCase):
-	def test_initial_condition(self):
-		# Check for the expected initial condition
-		dir = Directory(TESTDIR)
-
-		self.assertEqual(dir.path, TESTDIR)
-		self.assertFalse(dir.content_loaded)
-		self.assertEqual(dir.filenames, None)
-		self.assertEqual(dir.files, None)
-		if not sys.flags.optimize:  # asserts are ignored with python -O
-			self.assertRaises(AssertionError, len, dir)
-
-	def test_after_content_loaded(self):
-		import os
-		# Check whether the directory has the correct list of filenames.
-		dir = Directory(TESTDIR)
-		dir.load_content(schedule=False)
-
-		self.assertTrue(dir.exists)
-		self.assertEqual(type(dir.filenames), list)
-
-		# Get the filenames you expect it to have and sort both before
-		# comparing. I don't expect any order after only loading the filenames.
-		assumed_filenames = os.listdir(TESTDIR)
-		assumed_filenames = list(map(lambda str: os.path.join(TESTDIR, str),
-			assumed_filenames))
-		assumed_filenames.sort()
-		dir.filenames.sort()
-
-		self.assertTrue(len(dir) > 0)
-		self.assertEqual(dir.filenames, assumed_filenames)
-
-		# 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(name)
-			f.load()
-			for dirfile in dir.files:
-				if (f.path == dirfile.path and f.stat == dirfile.stat):
-					break
-			else:
-				self.fail("couldn't find file {0}".format(name))
-
-	def test_nonexistant_dir(self):
-		dir = Directory(NONEXISTANT_DIR)
-		dir.load_content(schedule=False)
-
-		self.assertTrue(dir.content_loaded)
-		self.assertFalse(dir.exists)
-		self.assertFalse(dir.accessible)
-		self.assertEqual(dir.filenames, None)
-		if not sys.flags.optimize:  # asserts are ignored with python -O
-			self.assertRaises(AssertionError, len, dir)
-
-	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 reload
-
-		def modify_dir():
-			open(TESTFILE, 'w').close()
-			os.unlink(TESTFILE)
-
-		def mtime():
-			return os.stat(TESTDIR).st_mtime
-
-		dir = Directory(TESTDIR)
-		dir.load()
-
-		# If the modification happens to be in the same second as the
-		# last modification, it will result in mtime having the same
-		# integer value. So we wait until the resolution is exceeded
-		# and mtime differs.
-		old_mtime = mtime()
-		for i in range(50):
-			modify_dir()
-			if old_mtime != mtime(): break
-			time.sleep(0.1)
-		else:
-			# fail after 5 seconds of trying
-			self.fail(
-					"Cannot perform test: mtime of TESTDIR is not being updated.")
-
-		self.assertTrue(dir.load_if_outdated())
-
-if __name__ == '__main__':
-	unittest.main()
-
diff --git a/test/tc_displayable.py b/test/tc_displayable.py
deleted file mode 100644
index 72e0507d..00000000
--- a/test/tc_displayable.py
+++ /dev/null
@@ -1,167 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-import unittest
-import curses
-from random import randint
-
-from ranger.gui.displayable import Displayable, DisplayableContainer
-from testlib import Fake, OK, raise_ok, TODO
-
-class TestWithFakeCurses(unittest.TestCase):
-	def setUp(self):
-		self.win = Fake()
-		self.fm = Fake()
-		self.env = Fake()
-		self.settings = Fake()
-		self.initdict = {'win': self.win, 'settings': self.settings,
-				'fm': self.fm, 'env': self.env}
-
-		self.disp = Displayable(**self.initdict)
-		self.disc = DisplayableContainer(**self.initdict)
-		self.disc.add_child(self.disp)
-
-		hei, wid = 100, 100
-		self.env.termsize = (hei, wid)
-
-	def tearDown(self):
-		self.disp.destroy()
-		self.disc.destroy()
-
-	def test_colorscheme(self):
-		# Using a color method implies change of window attributes
-		disp = self.disp
-
-		disp.win.chgat = raise_ok
-		disp.win.attrset = raise_ok
-
-		self.assertRaises(OK, disp.color, 'a', 'b')
-		self.assertRaises(OK, disp.color_at, 0, 0, 0, 'a', 'b')
-		self.assertRaises(OK, disp.color_reset)
-
-	def test_focused_object(self):
-		d1 = Displayable(**self.initdict)
-		d2 = DisplayableContainer(**self.initdict)
-		for obj in (Displayable(**self.initdict) for x in range(5)):
-			d2.add_child(obj)
-		d3 = DisplayableContainer(**self.initdict)
-		for obj in (Displayable(**self.initdict) for x in range(5)):
-			d3.add_child(obj)
-
-		for obj in (d1, d2, d3):
-			self.disc.add_child(obj)
-
-		d3.container[3].focused = True
-
-		self.assertEqual(self.disc._get_focused_obj(), d3.container[3])
-
-		d3.container[3].focused = False
-		d2.container[0].focused = True
-
-		self.assertEqual(self.disc._get_focused_obj(), d2.container[0])
-
-gWin = None
-
-class TestDisplayableWithCurses(unittest.TestCase):
-	def setUp(self):
-		global gWin
-		if not gWin:
-			gWin = curses.initscr()
-		self.win = gWin
-		curses.cbreak()
-		curses.noecho()
-		curses.start_color()
-		curses.use_default_colors()
-
-		self.fm = Fake()
-		self.env = Fake()
-		self.settings = Fake()
-		self.initdict = {'win': self.win, 'settings': self.settings,
-				'fm': self.fm, 'env': self.env}
-		self.disp = Displayable(**self.initdict)
-		self.disc = DisplayableContainer(**self.initdict)
-		self.disc.add_child(self.disp)
-
-		self.env.termsize = self.win.getmaxyx()
-
-	def tearDown(self):
-		self.disp.destroy()
-		curses.nocbreak()
-		curses.echo()
-		curses.endwin()
-
-	@TODO
-	def test_boundaries(self):
-		disp = self.disp
-		hei, wid = self.env.termsize
-
-		self.assertRaises(ValueError, disp.resize, 0, 0, hei + 1, wid)
-		self.assertRaises(ValueError, disp.resize, 0, 0, hei, wid + 1)
-		self.assertRaises(ValueError, disp.resize, -1, 0, hei, wid)
-		self.assertRaises(ValueError, disp.resize, 0, -1, hei, wid)
-
-		for i in range(1000):
-			box = [int(randint(0, hei) * 0.2), int(randint(0, wid) * 0.2)]
-			box.append(randint(0, hei - box[0]))
-			box.append(randint(0, wid - box[1]))
-
-			def in_box(y, x):
-				return (y >= box[1] and y < box[1] + box[3]) and \
-						(x >= box[0] and x < box[0] + box[2])
-
-			disp.resize(*box)
-			self.assertEqual(box, [disp.y, disp.x, disp.hei, disp.wid],
-					"Resizing failed for some reason on loop " + str(i))
-
-			for y, x in zip(range(10), range(10)):
-				is_in_box = in_box(y, x)
-
-				point1 = (y, x)
-				self.assertEqual(is_in_box, point1 in disp)
-
-				point2 = Fake()
-				point2.x = x
-				point2.y = y
-				self.assertEqual(is_in_box, point2 in disp)
-
-	def test_click(self):
-		self.disp.click = raise_ok
-
-		hei, wid = self.env.termsize
-
-		for i in range(50):
-			winwid = randint(2, wid-1)
-			winhei = randint(2, hei-1)
-			self.disc.resize(0, 0, hei, wid)
-			self.disp.resize(0, 0, winhei, winwid)
-			fakepos = Fake()
-
-			fakepos.x = winwid - 2
-			fakepos.y = winhei - 2
-			self.assertRaises(OK, self.disc.click, fakepos)
-
-			fakepos.x = winwid
-			fakepos.y = winhei
-			self.disc.click(fakepos)
-
-
-if __name__ == '__main__':
-	unittest.main()
diff --git a/test/tc_ext.py b/test/tc_ext.py
deleted file mode 100644
index 495591a1..00000000
--- a/test/tc_ext.py
+++ /dev/null
@@ -1,150 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-import unittest
-from collections import deque
-
-from ranger.ext.iter_tools import *
-
-class TestCases(unittest.TestCase):
-	def test_flatten(self):
-		def f(x):
-			return list(flatten(x))
-
-		self.assertEqual(
-			[1,2,3,4,5],
-			f([1,2,3,4,5]))
-		self.assertEqual(
-			[1,2,3,4,5],
-			f([1,[2,3],4,5]))
-		self.assertEqual(
-			[1,2,3,4,5],
-			f([[1,[2,3]],4,5]))
-		self.assertEqual(
-			[],
-			f([[[[]]]]))
-		self.assertEqual(
-			['a', 'b', 'fskldfjl'],
-			f(['a', ('b', 'fskldfjl')]))
-		self.assertEqual(
-			['a', 'b', 'fskldfjl'],
-			f(['a', deque(['b', 'fskldfjl'])]))
-		self.assertEqual(
-			set([3.5, 4.3, 5.2, 6.0]),
-			set(f([6.0, set((3.5, 4.3)), (5.2, )])))
-
-	def test_unique(self):
-		def u(x):
-			return list(unique(x))
-
-		self.assertEqual(
-			[1,2,3],
-			u([1,2,3]))
-		self.assertEqual(
-			[1,2,3],
-			u([1,2,3,2,1]))
-		self.assertEqual(
-			[1,2,3],
-			u([1,2,3,1,2,3,2,2,3,1,2,3,1,2,3,2,3,2,1]))
-		self.assertEqual(
-			[1,[2,3]],
-			u([1,[2,3],1,[2,3],[2,3],1,[2,3],1,[2,3],[2,3],1]))
-
-	def test_unique_keeps_type(self):
-		def u(x):
-			return unique(x)
-
-		self.assertEqual(
-			[1,2,3],
-			u([1,2,3,1]))
-		self.assertEqual(
-			(1,2,3),
-			u((1,2,3,1)))
-		self.assertEqual(
-			set((1,2,3)),
-			u(set((1,2,3,1))))
-		self.assertEqual(
-			deque((1,2,3)),
-			u(deque((1,2,3,1))))
-
-	def test_mount_path(self):
-		# assuming ismount() is used
-
-		def my_ismount(path):
-			depth = path.count('/')
-			if path.startswith('/media'):
-				return depth == 0 or depth == 2
-			return depth <= 1
-
-		from ranger.ext import mount_path
-		original_ismount = mount_path.ismount
-		mount_path.ismount = my_ismount
-		try:
-			mp = mount_path.mount_path
-
-			self.assertEqual('/home', mp('/home/hut/porn/bondage'))
-			self.assertEqual('/', mp('/'))
-			self.assertEqual('/media/sdb1', mp('/media/sdb1/foo/bar'))
-			self.assertEqual('/media/sdc2', mp('/media/sdc2/a/b/c/d/e'))
-		finally:
-			mount_path.ismount = original_ismount
-
-		# TODO: links are not tested but I don't see how its possible
-		# without messing around with mounts.
-		# self.assertEqual('/media/foo',
-		#     mount_path('/media/bar/some_link_to_a_foo_subdirectory'))
-
-	def test_openstruct(self):
-		from ranger.ext.openstruct import OpenStruct
-		from random import randint, choice
-		from string import ascii_letters
-
-		os = OpenStruct(a='a')
-		self.assertEqual(os.a, 'a')
-		self.assertRaises(AttributeError, getattr, os, 'b')
-
-		dictionary = {'foo': 'bar', 'zoo': 'zar'}
-		os = OpenStruct(dictionary)
-		self.assertEqual(os.foo, 'bar')
-		self.assertEqual(os.zoo, 'zar')
-		self.assertRaises(AttributeError, getattr, os, 'sdklfj')
-
-		for i in range(100):
-			attr_name = ''.join(choice(ascii_letters) \
-				for x in range(randint(3,9)))
-			value = randint(100,999)
-			if not attr_name in os:
-				self.assertRaises(AttributeError, getattr, os, attr_name)
-			setattr(os, attr_name, value)
-			value2 = randint(100,999)
-			setattr(os, attr_name, value2)
-			self.assertEqual(value2, getattr(os, attr_name))
-
-	def test_shell_escape(self):
-		from ranger.ext.shell_escape import shell_escape, shell_quote
-		self.assertEqual(r"'luigi'\''s pizza'", shell_quote("luigi's pizza"))
-		self.assertEqual(r"luigi\'s\ pizza", shell_escape("luigi's pizza"))
-		self.assertEqual(r"\$lol/foo\\xyz\|\>\<\]\[",
-				shell_escape(r"$lol/foo\xyz|><]["))
-
-
-if __name__ == '__main__':
-	unittest.main()
diff --git a/test/tc_history.py b/test/tc_history.py
deleted file mode 100644
index 02a8bb9f..00000000
--- a/test/tc_history.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-from ranger.container import History
-from unittest import TestCase, main
-import unittest
-
-class Test(TestCase):
-	def test_history(self):
-		hist = History(3)
-		for i in range(6):
-			hist.add(i)
-		self.assertEqual([3,4,5], list(hist))
-
-		hist.back()
-
-		self.assertEqual(4, hist.current())
-		self.assertEqual([3,4], list(hist._left()))
-
-		self.assertEqual(5, hist.top())
-
-		hist.back()
-		self.assertEqual(3, hist.current())
-		self.assertEqual([3], list(hist._left()))
-
-		# no change if current == bottom
-		self.assertEqual(hist.current(), hist.bottom())
-		last = hist.current()
-		hist.back()
-		self.assertEqual(hist.current(), last)
-
-		self.assertEqual(5, hist.top())
-
-		hist.forward()
-		hist.forward()
-		self.assertEqual(5, hist.current())
-		self.assertEqual([3,4,5], list(hist._left()))
-
-
-		self.assertEqual(3, hist.bottom())
-		hist.add(6)
-		self.assertEqual(4, hist.bottom())
-		self.assertEqual([4,5,6], list(hist._left()))
-
-		hist.back()
-		hist.fast_forward()
-		self.assertEqual([4,5,6], list(hist._left()))
-		hist.back()
-		hist.back()
-		hist.fast_forward()
-		self.assertEqual([4,5,6], list(hist._left()))
-		hist.back()
-		hist.back()
-		hist.back()
-		hist.fast_forward()
-		self.assertEqual([4,5,6], list(hist._left()))
-		hist.back()
-		hist.back()
-		hist.back()
-		hist.back()
-		hist.fast_forward()
-		self.assertEqual([4,5,6], list(hist._left()))
-
-if __name__ == '__main__': main()
diff --git a/test/tc_human_readable.py b/test/tc_human_readable.py
deleted file mode 100644
index 493e6d3a..00000000
--- a/test/tc_human_readable.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-import unittest
-from ranger.ext.human_readable import human_readable as hr
-
-class HumanReadableTest(unittest.TestCase):
-	def test_basic(self):
-		self.assertEqual("0", hr(0))
-		self.assertEqual("1 B", hr(1))
-		self.assertEqual("1 K", hr(2 ** 10))
-		self.assertEqual("1 M", hr(2 ** 20))
-		self.assertEqual("1 G", hr(2 ** 30))
-		self.assertEqual(">9000", hr(2 ** 100))
-
-	def test_big(self):
-		self.assertEqual("1023 G", hr(2 ** 30 * 1023))
-		self.assertEqual("1024 G", hr(2 ** 40 - 1))
-		self.assertEqual("1 T",    hr(2 ** 40))
-
-	def test_small(self):
-		self.assertEqual("1000 B", hr(1000))
-		self.assertEqual("1.66 M", hr(1.66 * 2 ** 20))
-		self.assertEqual("1.46 K", hr(1500))
-		self.assertEqual("1.5 K",  hr(2 ** 10 + 2 ** 9))
-		self.assertEqual("1.5 K",  hr(2 ** 10 + 2 ** 9 - 1))
-
-	def test_no_exponent(self):
-		for i in range(2 ** 10, 2 ** 20, 512):
-			self.assertTrue('e' not in hr(i), "%d => %s" % (i, hr(i)))
-
-if __name__ == '__main__':
-	unittest.main()
diff --git a/test/tc_keyapi.py b/test/tc_keyapi.py
deleted file mode 100644
index 79d89fa5..00000000
--- a/test/tc_keyapi.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-from unittest import TestCase, main
-
-class Test(TestCase):
-	def test_wrapper(self):
-		from ranger.api.keys import Wrapper
-
-		class dummyfm(object):
-			def move(self, relative):
-				return "I move down by {0}".format(relative)
-
-		class commandarg(object):
-			def __init__(self):
-				self.fm = dummyfm()
-				self.n = None
-				self.direction = None
-
-		arg = commandarg()
-
-		do = Wrapper('fm')
-		command = do.move(relative=4)
-
-		self.assertEqual(command(arg), 'I move down by 4')
-
-if __name__ == '__main__': main()
diff --git a/test/tc_loader.py b/test/tc_loader.py
deleted file mode 100644
index a679a629..00000000
--- a/test/tc_loader.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-import unittest
-import os
-from os.path import realpath, join, dirname
-
-from testlib import Fake
-from ranger.core.shared import FileManagerAware, SettingsAware
-from ranger.core.loader import Loader
-from ranger.fsobject import Directory, File
-from ranger.ext.openstruct import OpenStruct
-
-TESTDIR = realpath(join(dirname(__file__), 'testdir'))
-#TESTDIR = "/usr/sbin"
-
-class Test1(unittest.TestCase):
-	def test_loader(self):
-		loader = Loader()
-		fm = OpenStruct(loader=loader)
-		SettingsAware.settings = Fake()
-		FileManagerAware.fm = fm
-
-		# initially, the loader has nothing to do
-		self.assertFalse(loader.has_work())
-
-		dir = Directory(TESTDIR)
-		self.assertEqual(None, dir.files)
-		self.assertFalse(loader.has_work())
-
-		# Calling load_content() will enqueue the loading operation.
-		# dir is not loaded yet, but the loader has work
-		dir.load_content(schedule=True)
-		self.assertEqual(None, dir.files)
-		self.assertTrue(loader.has_work())
-
-		iterations = 0
-		while loader.has_work():
-			iterations += 1
-			loader.work()
-		#print(iterations)
-		self.assertNotEqual(None, dir.files)
-		self.assertFalse(loader.has_work())
-#
-#	def test_get_overhead_of_loader(self):
-#		N = 5
-#		tloader = benchmark_load(N)
-#		traw = benchmark_raw_load(N)
-#		#traw1k = 250.0
-#		#traw = traw1k * N / 1000.0
-#		#print("Loader: {0}s".format(tloader))
-#		#print("Raw:    {0}s".format(traw))
-#		self.assertTrue(tloader > traw)
-#		overhead = tloader * 100 / traw - 100
-#		self.assertTrue(overhead < 2, "overhead of loader too high: {0}" \
-#				.format(overhead))
-#		#print("Overhead: {0:.5}%".format(overhead))
-
-
-if __name__ == '__main__':
-	unittest.main()
diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py
deleted file mode 100644
index c9597201..00000000
--- a/test/tc_newkeys.py
+++ /dev/null
@@ -1,620 +0,0 @@
-# coding=utf-8
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-sys.path[1:1] = ['..']
-
-from unittest import TestCase, main
-
-from testlib import TODO
-from ranger.ext.tree import Tree
-from ranger.container.keymap import *
-from ranger.container.keybuffer import KeyBuffer
-from ranger.ext.keybinding_parser import parse_keybinding
-
-def simulate_press(self, string):
-	for char in parse_keybinding(string):
-		self.add(char)
-		if self.done:
-			return self.command
-		if self.failure:
-			break
-	return self.command
-
-class PressTestCase(TestCase):
-	"""Some useful methods for the actual test"""
-	def _mkpress(self, keybuffer, _=0):
-		def press(keys):
-			keybuffer.clear()
-			match = simulate_press(keybuffer, keys)
-			self.assertFalse(keybuffer.failure,
-					"parsing keys '"+keys+"' did fail!")
-			self.assertTrue(keybuffer.done,
-					"parsing keys '"+keys+"' did not complete!")
-			arg = CommandArgs(None, None, keybuffer)
-			self.assert_(match.function, "No function found! " + \
-					str(match.__dict__))
-			return match.function(arg)
-		return press
-
-	def assertPressFails(self, kb, keys):
-		kb.clear()
-		simulate_press(kb, keys)
-		self.assertTrue(kb.failure, "Keypress did not fail as expected")
-		kb.clear()
-
-	def assertPressIncomplete(self, kb, keys):
-		kb.clear()
-		simulate_press(kb, keys)
-		self.assertFalse(kb.failure, "Keypress failed, expected incomplete")
-		self.assertFalse(kb.done, "Keypress done which was unexpected")
-		kb.clear()
-
-class Test(PressTestCase):
-	"""The test cases"""
-	def test_passive_action(self):
-		km = KeyMap()
-		directions = KeyMap()
-		kb = KeyBuffer(km, directions)
-		def n(value):
-			"""return n or value"""
-			def fnc(arg=None):
-				if arg is None or arg.n is None:
-					return value
-				return arg.n
-			return fnc
-
-		km.map('ppp', n(5))
-		km.map('pp<bg>', n(8))
-		km.map('pp<dir>', n(2))
-		directions.map('j', dir=Direction(down=1))
-
-		press = self._mkpress(kb, km)
-		self.assertEqual(5, press('ppp'))
-		self.assertEqual(3, press('3ppp'))
-
-		self.assertEqual(2, press('ppj'))
-
-		kb.clear()
-		match = simulate_press(kb, 'pp')
-		args = CommandArgs(0, 0, kb)
-		self.assert_(match)
-		self.assert_(match.function)
-		self.assertEqual(8, match.function(args))
-
-	def test_translate_keys(self):
-		def test(string, *args):
-			if not args:
-				args = (string, )
-			self.assertEqual(ordtuple(*args), tuple(parse_keybinding(string)))
-
-		def ordtuple(*args):
-			lst = []
-			for arg in args:
-				if isinstance(arg, str):
-					lst.extend(ord(c) for c in arg)
-				else:
-					lst.append(arg)
-			return tuple(lst)
-
-		# 1 argument means: assume nothing is translated.
-		test('k')
-		test('kj')
-		test('k<dir>', 'k', DIRKEY)
-		test('k<ANY>z<any>', 'k', ANYKEY, 'z', ANYKEY)
-		test('k<anY>z<dir>', 'k', ANYKEY, 'z', DIRKEY)
-		test('<cr>', "\n")
-		test('<tab><tab><cr>', "\t\t\n")
-		test('<')
-		test('>')
-		test('<C-a>', 1)
-		test('<C-b>', 2)
-		for i in range(1, 26):
-			test('<C-' + chr(i+ord('a')-1) + '>', i)
-		test('<A-x>', 27, ord('x'))
-		test('<a-o>', 27, ord('o'))
-		test('k<a')
-		test('k<anz>')
-		test('k<a<nz>')
-		test('k<a<nz>')
-		test('k<a<>nz>')
-		test('>nz>')
-
-	def test_alias(self):
-		def add_dirs(arg):
-			return sum(dir.down() for dir in arg.directions)
-		def return5(_):
-			return 5
-
-		directions = KeyMap()
-		directions.map('j', dir=Direction(down=1))
-		directions.map('k', dir=Direction(down=-1))
-		directions.map('<CR>', alias='j')
-		directions.map('@', alias='<CR>')
-
-		base = KeyMap()
-		base.map('a<dir>', add_dirs)
-		base.map('b<dir>', add_dirs)
-		base.map('x<dir>x<dir>', add_dirs)
-		base.map('f', return5)
-		base.map('yy', alias='y')
-		base.map('!', alias='!')
-
-		other = KeyMap()
-		other.map('b<dir>b<dir>', alias='x<dir>x<dir>')
-		other.map('c<dir>', add_dirs)
-		other.map('g', alias='f')
-
-		km = base.merge(other, copy=True)
-		kb = KeyBuffer(km, directions)
-
-		press = self._mkpress(kb, km)
-
-		self.assertEqual(1, press('aj'))
-		self.assertEqual(2, press('bjbj'))
-		self.assertEqual(1, press('cj'))
-		self.assertEqual(1, press('c<CR>'))
-
-		self.assertEqual(5, press('f'))
-		self.assertEqual(5, press('g'))
-		self.assertEqual(press('c<CR>'), press('c@'))
-		self.assertEqual(press('c<CR>'), press('c@'))
-		self.assertEqual(press('c<CR>'), press('c@'))
-
-		for n in range(1, 10):
-			self.assertPressIncomplete(kb, 'y' * n)
-
-		for n in range(1, 5):
-			self.assertPressFails(kb, '!' * n)
-
-	def test_tree(self):
-		t = Tree()
-		t.set('abcd', "Yes")
-		self.assertEqual("Yes", t.traverse('abcd'))
-		self.assertRaises(KeyError, t.traverse, 'abcde')
-		self.assertRaises(KeyError, t.traverse, 'xyz')
-		self.assert_(isinstance(t.traverse('abc'), Tree))
-
-		t2 = Tree()
-		self.assertRaises(KeyError, t2.set, 'axy', "Lol", force=False)
-		t2.set('axx', 'ololol')
-		t2.set('axyy', "Lol")
-		self.assertEqual("Yes", t.traverse('abcd'))
-		self.assertRaises(KeyError, t2.traverse, 'abcd')
-		self.assertEqual("Lol", t2.traverse('axyy'))
-		self.assertEqual("ololol", t2.traverse('axx'))
-
-		t2.unset('axyy')
-		self.assertEqual("ololol", t2.traverse('axx'))
-		self.assertRaises(KeyError, t2.traverse, 'axyy')
-		self.assertRaises(KeyError, t2.traverse, 'axy')
-
-		t2.unset('a')
-		self.assertRaises(KeyError, t2.traverse, 'abcd')
-		self.assertRaises(KeyError, t2.traverse, 'a')
-		self.assert_(t2.empty())
-
-	def test_merge_trees(self):
-		def makeTreeA():
-			t = Tree()
-			t.set('aaaX', 1)
-			t.set('aaaY', 2)
-			t.set('aaaZ', 3)
-			t.set('bbbA', 11)
-			t.set('bbbB', 12)
-			t.set('bbbC', 13)
-			t.set('bbbD', 14)
-			t.set('bP', 21)
-			t.set('bQ', 22)
-			return t
-
-		def makeTreeB():
-			u = Tree()
-			u.set('aaaX', 0)
-			u.set('bbbC', 'Yes')
-			u.set('bbbD', None)
-			u.set('bbbE', 15)
-			u.set('bbbF', 16)
-			u.set('bQ', 22)
-			u.set('bR', 23)
-			u.set('ffff', 1337)
-			return u
-
-		# test 1
-		t = Tree('a')
-		u = Tree('b')
-		merged = t.merge(u, copy=True)
-		self.assertEqual('b', merged._tree)
-
-		# test 2
-		t = Tree('a')
-		u = makeTreeA()
-		merged = t.merge(u, copy=True)
-		self.assertEqual(u._tree, merged._tree)
-
-		# test 3
-		t = makeTreeA()
-		u = makeTreeB()
-		v = t.merge(u, copy=True)
-
-		self.assertEqual(0, v['aaaX'])
-		self.assertEqual(2, v['aaaY'])
-		self.assertEqual(3, v['aaaZ'])
-		self.assertEqual(11, v['bbbA'])
-		self.assertEqual('Yes', v['bbbC'])
-		self.assertEqual(None, v['bbbD'])
-		self.assertEqual(15, v['bbbE'])
-		self.assertEqual(16, v['bbbF'])
-		self.assertRaises(KeyError, t.__getitem__, 'bbbG')
-		self.assertEqual(21, v['bP'])
-		self.assertEqual(22, v['bQ'])
-		self.assertEqual(23, v['bR'])
-		self.assertEqual(1337, v['ffff'])
-
-		# merge shouldn't be destructive
-		self.assertEqual(makeTreeA()._tree, t._tree)
-		self.assertEqual(makeTreeB()._tree, u._tree)
-
-		v['fff'].replace('Lolz')
-		self.assertEqual('Lolz', v['fff'])
-
-		v['aaa'].replace('Very bad')
-		v.plow('qqqqqqq').replace('eww.')
-
-		self.assertEqual(makeTreeA()._tree, t._tree)
-		self.assertEqual(makeTreeB()._tree, u._tree)
-
-	def test_add(self):
-		c = KeyMap()
-		c.map('aa', 'b', lambda *_: 'lolz')
-		self.assert_(c['aa'].function(), 'lolz')
-		@c.map('a', 'c')
-		def test():
-			return 5
-		self.assert_(c['b'].function(), 'lolz')
-		self.assert_(c['c'].function(), 5)
-		self.assert_(c['a'].function(), 5)
-
-	def test_quantifier(self):
-		km = KeyMap()
-		directions = KeyMap()
-		kb = KeyBuffer(km, directions)
-		def n(value):
-			"""return n or value"""
-			def fnc(arg=None):
-				if arg is None or arg.n is None:
-					return value
-				return arg.n
-			return fnc
-		km.map('p', n(5))
-		press = self._mkpress(kb, km)
-		self.assertEqual(5, press('p'))
-		self.assertEqual(3, press('3p'))
-		self.assertEqual(6223, press('6223p'))
-
-	def test_direction(self):
-		km = KeyMap()
-		directions = KeyMap()
-		kb = KeyBuffer(km, directions)
-		directions.map('j', dir=Direction(down=1))
-		directions.map('k', dir=Direction(down=-1))
-		def nd(arg):
-			""" n * direction """
-			n = arg.n is None and 1 or arg.n
-			dir = arg.direction is None and Direction(down=1) \
-					or arg.direction
-			return n * dir.down()
-		km.map('d<dir>', nd)
-		km.map('dd', func=nd)
-
-		press = self._mkpress(kb, km)
-
-		self.assertPressIncomplete(kb, 'd')
-		self.assertEqual(  1, press('dj'))
-		self.assertEqual(  3, press('3ddj'))
-		self.assertEqual( 15, press('3d5j'))
-		self.assertEqual(-15, press('3d5k'))
-		# supporting this kind of key combination would be too confusing:
-		# self.assertEqual( 15, press('3d5d'))
-		self.assertEqual(  3, press('3dd'))
-		self.assertEqual(  33, press('33dd'))
-		self.assertEqual(  1, press('dd'))
-
-		km.map('x<dir>', nd)
-		km.map('xxxx', func=nd)
-
-		self.assertEqual(1, press('xxxxj'))
-		self.assertEqual(1, press('xxxxjsomeinvalitchars'))
-
-		# these combinations should break:
-		self.assertPressFails(kb, 'xxxj')
-		self.assertPressFails(kb, 'xxj')
-		self.assertPressFails(kb, 'xxkldfjalksdjklsfsldkj')
-		self.assertPressFails(kb, 'xyj')
-		self.assertPressIncomplete(kb, 'x') # direction missing
-
-	def test_any_key(self):
-		km = KeyMap()
-		directions = KeyMap()
-		kb = KeyBuffer(km, directions)
-		directions.map('j', dir=Direction(down=1))
-		directions.map('k', dir=Direction(down=-1))
-
-		directions.map('g<any>', dir=Direction(down=-1))
-
-		def cat(arg):
-			n = arg.n is None and 1 or arg.n
-			return ''.join(chr(c) for c in arg.matches) * n
-
-		km.map('return<any>', cat)
-		km.map('cat4<any><any><any><any>', cat)
-		km.map('foo<dir><any>', cat)
-
-		press = self._mkpress(kb, km)
-
-		self.assertEqual('x', press('returnx'))
-		self.assertEqual('abcd', press('cat4abcd'))
-		self.assertEqual('abcdabcd', press('2cat4abcd'))
-		self.assertEqual('55555', press('5return5'))
-
-		self.assertEqual('x', press('foojx'))
-		self.assertPressFails(kb, 'fooggx')  # ANYKEY forbidden in DIRECTION
-
-		km.map('<any>', lambda _: Ellipsis)
-		self.assertEqual('x', press('returnx'))
-		self.assertEqual('abcd', press('cat4abcd'))
-		self.assertEqual(Ellipsis, press('2cat4abcd'))
-		self.assertEqual(Ellipsis, press('5return5'))
-		self.assertEqual(Ellipsis, press('g'))
-		self.assertEqual(Ellipsis, press('ß'))
-		self.assertEqual(Ellipsis, press('ア'))
-		self.assertEqual(Ellipsis, press('9'))
-
-	def test_multiple_directions(self):
-		km = KeyMap()
-		directions = KeyMap()
-		kb = KeyBuffer(km, directions)
-		directions.map('j', dir=Direction(down=1))
-		directions.map('k', dir=Direction(down=-1))
-
-		def add_dirs(arg):
-			return sum(dir.down() for dir in arg.directions)
-
-		km.map('x<dir>y<dir>', add_dirs)
-		km.map('four<dir><dir><dir><dir>', add_dirs)
-
-		press = self._mkpress(kb, km)
-
-		self.assertEqual(2, press('xjyj'))
-		self.assertEqual(0, press('fourjkkj'))
-		self.assertEqual(2, press('four2j4k2j2j'))
-		self.assertEqual(10, press('four1j2j3j4j'))
-		self.assertEqual(10, press('four1j2j3j4jafslkdfjkldj'))
-
-	def test_corruptions(self):
-		km = KeyMap()
-		directions = KeyMap()
-		kb = KeyBuffer(km, directions)
-		press = self._mkpress(kb, km)
-		directions.map('j', dir=Direction(down=1))
-		directions.map('k', dir=Direction(down=-1))
-		km.map('xxx', lambda _: 1)
-
-		self.assertEqual(1, press('xxx'))
-
-		# corrupt the tree
-		tup = tuple(parse_keybinding('xxx'))
-		x = ord('x')
-		km._tree[x][x][x] = "Boo"
-
-		self.assertPressFails(kb, 'xxy')
-		self.assertPressFails(kb, 'xzy')
-		self.assertPressIncomplete(kb, 'xx')
-		self.assertPressIncomplete(kb, 'x')
-		if not sys.flags.optimize:  # asserts are ignored with python -O
-			self.assertRaises(AssertionError, simulate_press, kb, 'xxx')
-		kb.clear()
-
-	def test_directions_as_functions(self):
-		km = KeyMap()
-		directions = KeyMap()
-		kb = KeyBuffer(km, directions)
-		press = self._mkpress(kb, km)
-
-		def move(arg):
-			return arg.direction.down()
-
-		directions.map('j', dir=Direction(down=1))
-		directions.map('s', alias='j')
-		directions.map('k', dir=Direction(down=-1))
-		km.map('<dir>', func=move)
-
-		self.assertEqual(1, press('j'))
-		self.assertEqual(1, press('j'))
-		self.assertEqual(1, press('j'))
-		self.assertEqual(1, press('j'))
-		self.assertEqual(1, press('j'))
-		self.assertEqual(1, press('s'))
-		self.assertEqual(1, press('s'))
-		self.assertEqual(1, press('s'))
-		self.assertEqual(1, press('s'))
-		self.assertEqual(1, press('s'))
-		self.assertEqual(-1, press('k'))
-		self.assertEqual(-1, press('k'))
-		self.assertEqual(-1, press('k'))
-
-		km.map('k', func=lambda _: 'love')
-
-		self.assertEqual(1, press('j'))
-		self.assertEqual('love', press('k'))
-
-		self.assertEqual(1, press('40j'))
-		self.assertEqual(40, kb.quant)
-
-		km.map('<dir><dir><any><any>', func=move)
-
-		self.assertEqual(1, press('40jkhl'))
-		self.assertEqual(40, kb.quant)
-
-	def test_tree_deep_copy(self):
-		t = Tree()
-		s = t.plow('abcd')
-		s.replace('X')
-		u = t.copy()
-		self.assertEqual(t._tree, u._tree)
-		s = t.traverse('abc')
-		s.replace('Y')
-		self.assertNotEqual(t._tree, u._tree)
-
-	def test_keymanager(self):
-		def func(arg):
-			return 5
-		def getdown(arg):
-			return arg.direction.down()
-
-		buffer = KeyBuffer(None, None)
-		press = self._mkpress(buffer)
-		keymanager = KeyManager(buffer, ['foo', 'bar'])
-
-		map = keymanager.get_context('foo')
-		map('a', func)
-		map('b', func)
-		map = keymanager.get_context('bar')
-		map('c', func)
-		map('<dir>', getdown)
-
-		keymanager.dir('foo', 'j', down=1)
-		keymanager.dir('bar', 'j', down=1)
-
-		keymanager.use_context('foo')
-		self.assertEqual(5, press('a'))
-		self.assertEqual(5, press('b'))
-		self.assertPressFails(buffer, 'c')
-
-		keymanager.use_context('bar')
-		self.assertPressFails(buffer, 'a')
-		self.assertPressFails(buffer, 'b')
-		self.assertEqual(5, press('c'))
-		self.assertEqual(1, press('j'))
-		keymanager.use_context('foo')
-		keymanager.use_context('foo')
-		keymanager.use_context('foo')
-		keymanager.use_context('bar')
-		keymanager.use_context('foo')
-		keymanager.use_context('bar')
-		keymanager.use_context('bar')
-		self.assertEqual(1, press('j'))
-
-	def test_alias_to_direction(self):
-		def func(arg):
-			return arg.direction.down()
-
-		km = KeyMapWithDirections()
-		kb = KeyBuffer(km, km.directions)
-		press = self._mkpress(kb)
-
-		km.map('<dir>', func)
-		km.map('d<dir>', func)
-		km.dir('j', down=42)
-		km.dir('k', alias='j')
-		self.assertEqual(42, press('j'))
-
-		km.dir('o', alias='j')
-		km.dir('ick', alias='j')
-		self.assertEqual(42, press('o'))
-		self.assertEqual(42, press('dj'))
-		self.assertEqual(42, press('dk'))
-		self.assertEqual(42, press('do'))
-		self.assertEqual(42, press('dick'))
-		self.assertPressFails(kb, 'dioo')
-
-	def test_both_directory_and_any_key(self):
-		def func(arg):
-			return arg.direction.down()
-		def func2(arg):
-			return "yay"
-
-		km = KeyMap()
-		directions = KeyMap()
-		kb = KeyBuffer(km, directions)
-		press = self._mkpress(kb)
-
-		km.map('abc<dir>', func)
-		directions.map('j', dir=Direction(down=42))
-		self.assertEqual(42, press('abcj'))
-
-		km.unmap('abc<dir>')
-
-		km.map('abc<any>', func2)
-		self.assertEqual("yay", press('abcd'))
-
-		km.map('abc<dir>', func)
-
-		km.map('abc<any>', func2)
-		self.assertEqual("yay", press('abcd'))
-
-	def test_map_collision(self):
-		def add_dirs(arg):
-			return sum(dir.down() for dir in arg.directions)
-		def return5(_):
-			return 5
-
-
-		directions = KeyMap()
-		directions.map('gg', dir=Direction(down=1))
-
-
-		km = KeyMap()
-		km.map('gh', return5)
-		km.map('agh', return5)
-		km.map('a<dir>', add_dirs)
-
-		kb = KeyBuffer(km, directions)
-		press = self._mkpress(kb, km)
-
-		self.assertEqual(5, press('gh'))
-		self.assertEqual(5, press('agh'))
-#		self.assertPressFails(kb, 'agh')
-
-	@TODO
-	def test_map_collision2(self):
-		directions = KeyMap()
-		directions.map('gg', dir=Direction(down=1))
-		km = KeyMap()
-		km.map('agh', lambda _: 1)
-		km.map('a<dir>', lambda _: 2)
-		kb = KeyBuffer(km, directions)
-		press = self._mkpress(kb, km)
-		self.assertEqual(1, press('agh'))
-		self.assertEqual(2, press('agg'))
-
-	def test_keymap_with_dir(self):
-		def func(arg):
-			return arg.direction.down()
-
-		km = KeyMapWithDirections()
-		kb = KeyBuffer(km, km.directions)
-
-		press = self._mkpress(kb)
-
-		km.map('abc<dir>', func)
-		km.dir('j', down=42)
-		self.assertEqual(42, press('abcj'))
-
-if __name__ == '__main__': main()
diff --git a/test/tc_relative_symlink.py b/test/tc_relative_symlink.py
deleted file mode 100644
index a202513d..00000000
--- a/test/tc_relative_symlink.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-import unittest
-from ranger.ext.relative_symlink import *
-rel = get_relative_source_file
-
-class Test(unittest.TestCase):
-	def test_foo(self):
-		self.assertEqual('../foo', rel('/foo', '/x/bar'))
-		self.assertEqual('../../foo', rel('/foo', '/x/y/bar'))
-		self.assertEqual('../../a/b/foo', rel('/a/b/foo', '/x/y/bar'))
-		self.assertEqual('../../x/b/foo', rel('/x/b/foo', '/x/y/bar',
-			common_base='/'))
-		self.assertEqual('../b/foo', rel('/x/b/foo', '/x/y/bar'))
-		self.assertEqual('../b/foo', rel('/x/b/foo', '/x/y/bar'))
-
-	def test_get_common_base(self):
-		self.assertEqual('/', get_common_base('', ''))
-		self.assertEqual('/', get_common_base('', '/'))
-		self.assertEqual('/', get_common_base('/', ''))
-		self.assertEqual('/', get_common_base('/', '/'))
-		self.assertEqual('/', get_common_base('/bla/bar/x', '/foo/bar/a'))
-		self.assertEqual('/foo/bar/', get_common_base('/foo/bar/x', '/foo/bar/a'))
-		self.assertEqual('/foo/', get_common_base('/foo/bar/x', '/foo/baz/a'))
-		self.assertEqual('/foo/', get_common_base('/foo/bar/x', '/foo/baz/a'))
-		self.assertEqual('/', get_common_base('//foo/bar/x', '/foo/baz/a'))
-
-if __name__ == '__main__': unittest.main()
diff --git a/test/tc_signal.py b/test/tc_signal.py
deleted file mode 100644
index 6547bbc3..00000000
--- a/test/tc_signal.py
+++ /dev/null
@@ -1,139 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-import unittest
-import gc
-from ranger.ext.signals import *
-
-class TestSignal(unittest.TestCase):
-	def setUp(self):
-		self.sd = SignalDispatcher()
-
-	def test_signal_register_emit(self):
-		sd = self.sd
-		def poo(sig):
-			self.assert_('works' in sig)
-			self.assertEqual('yes', sig.works)
-		handler = sd.signal_bind('x', poo)
-
-		sd.signal_emit('x', works='yes')
-		sd.signal_unbind(handler)
-		sd.signal_emit('x')
-
-	def test_signal_order(self):
-		sd = self.sd
-		lst = []
-		def addn(n):
-			return lambda _: lst.append(n)
-
-		sd.signal_bind('x', addn(6))
-		sd.signal_bind('x', addn(3), priority=1)
-		sd.signal_bind('x', addn(2), priority=1)
-		sd.signal_bind('x', addn(9), priority=0)
-		sd.signal_bind('x', addn(1337), priority=0.7)
-		sd.signal_emit('x')
-
-		self.assert_(lst.index(3) < lst.index(6))
-		self.assert_(lst.index(2) < lst.index(6))
-		self.assert_(lst.index(6) < lst.index(9))
-		self.assert_(lst.index(1337) < lst.index(6))
-		self.assert_(lst.index(1337) < lst.index(9))
-		self.assert_(lst.index(1337) > lst.index(2))
-
-	def test_modifying_arguments(self):
-		sd = self.sd
-		lst = []
-		def modify(s):
-			s.number = 5
-		def set_number(s):
-			lst.append(s.number)
-		def stopit(s):
-			s.stop()
-
-		sd.signal_bind('setnumber', set_number)
-		sd.signal_emit('setnumber', number=100)
-		self.assertEqual(100, lst[-1])
-
-		sd.signal_bind('setnumber', modify, priority=1)
-		sd.signal_emit('setnumber', number=100)
-		self.assertEqual(5, lst[-1])
-
-		lst.append(None)
-		sd.signal_bind('setnumber', stopit, priority=1)
-		sd.signal_emit('setnumber', number=100)
-		self.assertEqual(None, lst[-1])
-
-	def test_weak_refs(self):
-		sd = self.sd
-		is_deleted = [False]
-
-		class Foo(object):
-			def __init__(self):
-				self.alphabet = ['a']
-			def calc(self, signal):
-				self.alphabet.append(chr(ord(self.alphabet[-1]) + 1))
-			def __del__(self):
-				is_deleted[0] = True
-
-		foo = Foo()
-		alphabet = foo.alphabet
-		calc = foo.calc
-
-		del foo
-		self.assertEqual('a', ''.join(alphabet))
-		sd.signal_bind('mysignal', calc, weak=True)
-		sd.signal_emit('mysignal')
-		self.assertEqual('ab', ''.join(alphabet))
-		self.assertFalse(is_deleted[0])
-
-		del calc
-		self.assertTrue(is_deleted[0])
-
-	def test_weak_refs_dead_on_arrival(self):
-		sd = self.sd
-		is_deleted = [False]
-
-		class Foo(object):
-			def __init__(self):
-				self.alphabet = ['a']
-			def calc(self, signal):
-				self.alphabet.append(chr(ord(self.alphabet[-1]) + 1))
-			def __del__(self):
-				is_deleted[0] = True
-
-		foo = Foo()
-		alphabet = foo.alphabet
-
-		self.assertEqual('a', ''.join(alphabet))
-		sd.signal_bind('mysignal', foo.calc, weak=True)
-
-		sd.signal_emit('mysignal')
-		self.assertEqual('ab', ''.join(alphabet))
-		self.assertFalse(is_deleted[0])
-
-		del foo
-
-		sd.signal_emit('mysignal')
-		self.assertEqual('ab', ''.join(alphabet))
-		self.assertTrue(is_deleted[0])
-
-if __name__ == '__main__':
-	unittest.main()
diff --git a/test/tc_ui.py b/test/tc_ui.py
deleted file mode 100644
index fa2bdcac..00000000
--- a/test/tc_ui.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-
-import unittest
-import curses
-
-from ranger.gui import ui
-
-from testlib import Fake, OK, raise_ok
-
-ui.curses = Fake()
-
-class Test(unittest.TestCase):
-	def setUp(self):
-
-		self.fm = Fake()
-		self.ui = ui.UI(env=Fake(), fm=self.fm)
-
-		def fakesetup():
-			self.ui.widget = Fake()
-			self.ui.add_child(self.ui.widget)
-		self.ui.setup = fakesetup
-
-		self.ui.initialize()
-
-	def tearDown(self):
-		self.ui.destroy()
-
-	def test_passing(self):
-		# Test whether certain method calls are passed to widgets
-		widget = self.ui.widget
-
-		widget.draw = raise_ok
-		self.assertRaises(OK, self.ui.draw)
-		widget.__clear__()
-
-		widget.finalize = raise_ok
-		self.assertRaises(OK, self.ui.finalize)
-		widget.__clear__()
-
-		widget.press = raise_ok
-		random_key = 123
-		self.assertRaises(OK, self.ui.handle_key, random_key)
-		widget.__clear__()
-
-		widget.destroy = raise_ok
-		self.assertRaises(OK, self.ui.destroy)
-		widget.__clear__()
-
-if __name__ == '__main__':
-	unittest.main()
diff --git a/test/tc_utfwidth.py b/test/tc_utfwidth.py
deleted file mode 100644
index 0288c17b..00000000
--- a/test/tc_utfwidth.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# -*- encoding: utf8 -*-
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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
-
-import os.path
-import sys
-rangerpath = os.path.join(os.path.dirname(__file__), '..')
-if sys.path[1] != rangerpath:
-	sys.path[1:1] = [rangerpath]
-sys.path[1:1] = ['..']
-
-from unittest import TestCase, main
-from ranger.ext.utfwidth import *
-
-a_ascii = "a"      # width = 1, bytes = 1
-a_umlaut = "ä"     # width = 1, bytes = 2
-a_katakana = "ア"  # width = 2, bytes = 3
-# need one with width = 1 & bytes = 3
-
-class Test(TestCase):
-	def test_utf_byte_length(self):
-		self.assertEqual(1, utf_byte_length(a_ascii))
-		self.assertEqual(2, utf_byte_length(a_umlaut))
-		self.assertEqual(3, utf_byte_length(a_katakana))
-
-	def test_uwid(self):
-		self.assertEqual(1, uwid(a_ascii))
-		self.assertEqual(1, uwid(a_umlaut))
-		self.assertEqual(2, uwid(a_katakana))
-		self.assertEqual(3, uwid(a_katakana + a_umlaut))
-		self.assertEqual(4, uwid("asdf"))
-		self.assertEqual(5, uwid("löööl"))
-		self.assertEqual(6, uwid("バババ"))
-
-if __name__ == '__main__': main()
diff --git a/test/testdir/largefile.txt b/test/testdir/largefile.txt
deleted file mode 100644
index 0eb8c64f..00000000
--- a/test/testdir/largefile.txt
+++ /dev/null
@@ -1 +0,0 @@
-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
diff --git a/test/testdir/symlink b/test/testdir/symlink
deleted file mode 120000
index 5cbc1596..00000000
--- a/test/testdir/symlink
+++ /dev/null
@@ -1 +0,0 @@
-textfile.txt
\ No newline at end of file
diff --git a/test/testdir/textfile.txt b/test/testdir/textfile.txt
deleted file mode 100644
index 45a23497..00000000
--- a/test/testdir/textfile.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
-Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
-Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
-Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
diff --git a/test/testdir/zerobytes b/test/testdir/zerobytes
deleted file mode 100644
index e69de29b..00000000
--- a/test/testdir/zerobytes
+++ /dev/null
diff --git a/test/testlib.py b/test/testlib.py
deleted file mode 100644
index 29dd9e07..00000000
--- a/test/testlib.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-def TODO(fnc):
-	def result(*arg, **kw):
-		try:
-			fnc(*arg, **kw)
-		except:
-			pass # failure expected
-	return result
-
-class Fake(object):
-	def __getattr__(self, attrname):
-		val = Fake()
-		self.__dict__[attrname] = val
-		return val
-
-	def __call__(self, *_, **__):
-		return Fake()
-
-	def __clear__(self):
-		self.__dict__.clear()
-
-	def __iter__(self):
-		return iter(())
-
-class OK(Exception):
-	pass
-
-def raise_ok(*_, **__):
-	raise OK()