[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master 30984c4 8/8: Merge branch 'master' of git+ssh://git.sv.gnu
From: |
Artur Malabarba |
Subject: |
[elpa] master 30984c4 8/8: Merge branch 'master' of git+ssh://git.sv.gnu.org/srv/git/emacs/elpa |
Date: |
Thu, 11 Jun 2015 21:56:46 +0000 |
branch: master
commit 30984c4c4be84d1f2b71ab6f8a57886cc630f080
Merge: b9e7d42 1dbb290
Author: Artur Malabarba <address@hidden>
Commit: Artur Malabarba <address@hidden>
Merge branch 'master' of git+ssh://git.sv.gnu.org/srv/git/emacs/elpa
---
GNUmakefile | 4 +-
packages/ace-window/README.md | 42 +++
packages/ace-window/ace-window.el | 272 ++++++++++------
packages/csv-mode/csv-mode.el | 35 +-
packages/let-alist/let-alist.el | 10 +-
packages/nlinum/nlinum.el | 42 ++-
packages/ztree/README.md | 72 ++++
packages/ztree/ztree-diff-model.el | 349 +++++++++++++++++++
packages/ztree/ztree-diff.el | 455 +++++++++++++++++++++++++
packages/ztree/ztree-dir.el | 118 +++++++
packages/ztree/ztree-pkg.el | 2 +
packages/ztree/ztree-util.el | 133 ++++++++
packages/ztree/ztree-view.el | 650 ++++++++++++++++++++++++++++++++++++
packages/ztree/ztree.el | 39 +++
14 files changed, 2086 insertions(+), 137 deletions(-)
diff --git a/GNUmakefile b/GNUmakefile
index e35b82d..03044dc 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -15,7 +15,9 @@ check_copyrights:
@echo "Compute exceptions >$(CR_EXCEPTIONS)~"
@export LANG=C; \
(cd packages; \
- find . -name '.git' -prune -o -name '*.el' -print0 | \
+ find . -name '.git' -prune -o \
+ -name 'test' -prune -o \
+ -name '*.el' -print0 | \
xargs -0 grep -L 'Free Software Foundation, Inc' | \
grep -v '\(\.dir-locals\|.-\(pkg\|autoloads\)\)\.el$$'; \
find . -name '.git' -prune -o -name '*.el' -print | \
diff --git a/packages/ace-window/README.md b/packages/ace-window/README.md
index 1b2f808..d70548a 100644
--- a/packages/ace-window/README.md
+++ b/packages/ace-window/README.md
@@ -44,6 +44,20 @@ always be `1`.
- You can delete the selected window by calling `ace-window` with a double
prefix argument, i.e. <kbd>C-u C-u</kbd>.
+## Change the action midway
+
+You can also start by calling `ace-window` and then decide to switch the
action to `delete` or `swap` etc. By default the bindings are:
+
+- <kbd>x</kbd> - delete window
+- <kbd>m</kbd> - swap (move) window
+- <kbd>v</kbd> - split window vertically
+- <kbd>b</kbd> - split window horizontally
+- <kbd>n</kbd> - select the previous window
+- <kbd>i</kbd> - maximize window (select which window)
+- <kbd>o</kbd> - maximize current window
+
+In order for it to work, these keys *must not* be in `aw-keys` and you have to
have `aw-dispatch-always` set to `t`.
+
## Customization
Aside from binding `ace-window`:
@@ -76,3 +90,31 @@ where to look, i.e. the top-left corners of each window.
So you can turn off the gray background with:
(setq aw-background nil)
+
+### `aw-dispatch-always`
+
+When non-nil, `ace-window` will issue a `read-char` even for one window.
+This will make `ace-window` act differently from `other-window` for one
+or two windows. This is useful to change the action midway
+and execute other action other than the *jump* default.
+By default is set to `nil`
+
+### `aw-dispatch-alist`
+
+This is the list of actions that you can trigger from `ace-window` other than
the
+*jump* default.
+By default is:
+
+ (defvar aw-dispatch-alist
+ '((?x aw-delete-window " Ace - Delete Window")
+ (?m aw-swap-window " Ace - Swap Window")
+ (?n aw-flip-window)
+ (?v aw-split-window-vert " Ace - Split Vert Window")
+ (?b aw-split-window-horz " Ace - Split Horz Window")
+ (?i delete-other-windows " Ace - Maximize Window")
+ (?o delete-other-windows))
+ "List of actions for `aw-dispatch-default'.")
+
+If the pair key-action is followed by a string, then `ace-window` will be
+invoked again to be able to select on which window you want to select the
+action. Otherwise the current window is selected.
diff --git a/packages/ace-window/ace-window.el
b/packages/ace-window/ace-window.el
index 791b34d..a1c12ed 100644
--- a/packages/ace-window/ace-window.el
+++ b/packages/ace-window/ace-window.el
@@ -5,8 +5,8 @@
;; Author: Oleh Krehel <address@hidden>
;; Maintainer: Oleh Krehel <address@hidden>
;; URL: https://github.com/abo-abo/ace-window
-;; Version: 0.8.1
-;; Package-Requires: ((avy "0.1.0"))
+;; Version: 0.9.0
+;; Package-Requires: ((avy "0.2.0"))
;; Keywords: window, location
;; This file is part of GNU Emacs.
@@ -60,7 +60,7 @@
;; deleted instead.
;;; Code:
-(require 'avy-jump)
+(require 'avy)
(require 'ring)
;;* Customization
@@ -101,6 +101,12 @@ Use M-0 `ace-window' to toggle this value."
(const :tag "single char" 'char)
(const :tag "full path" 'path)))
+(defcustom aw-dispatch-always nil
+ "When non-nil, `ace-window' will issue a `read-char' even for one window.
+This will make `ace-window' act different from `other-window' for
+ one or two windows."
+ :type 'boolean)
+
(defface aw-leading-char-face
'((((class color)) (:foreground "red"))
(((background dark)) (:foreground "gray100"))
@@ -130,15 +136,11 @@ Use M-0 `ace-window' to toggle this value."
(sort
(cl-remove-if
(lambda (w)
- (let ((f (window-frame w))
- (b (window-buffer w)))
+ (let ((f (window-frame w)))
(or (not (and (frame-live-p f)
(frame-visible-p f)))
(string= "initial_terminal" (terminal-name f))
- (aw-ignored-p w)
- (with-current-buffer b
- (and buffer-read-only
- (= 0 (buffer-size b)))))))
+ (aw-ignored-p w))))
(cl-case aw-scope
(global
(cl-mapcan #'window-list (frame-list)))
@@ -159,49 +161,63 @@ Use M-0 `ace-window' to toggle this value."
(nconc minor-mode-alist
(list '(ace-window-mode ace-window-mode))))
+(defvar aw-empty-buffers-list nil
+ "Store the read-only empty buffers which had to be modified.
+Modify them back eventually.")
+
(defun aw--done ()
"Clean up mode line and overlays."
;; mode line
- (setq ace-window-mode nil)
- (force-mode-line-update)
+ (aw-set-mode-line nil)
;; background
(mapc #'delete-overlay aw-overlays-back)
(setq aw-overlays-back nil)
- (avy--remove-leading-chars))
+ (avy--remove-leading-chars)
+ (dolist (b aw-empty-buffers-list)
+ (with-current-buffer b
+ (when (string= (buffer-string) " ")
+ (let ((inhibit-read-only t))
+ (delete-region (point-min) (point-max))))))
+ (setq aw-empty-buffers-list nil))
(defun aw--lead-overlay (path leaf)
"Create an overlay using PATH at LEAF.
LEAF is (PT . WND)."
- (let* ((pt (car leaf))
- (wnd (cdr leaf))
- (ol (make-overlay pt (1+ pt) (window-buffer wnd)))
- (old-str (or
- (ignore-errors
- (with-selected-window wnd
- (buffer-substring pt (1+ pt))))
- ""))
- (new-str
- (concat
- (cl-case aw-leading-char-style
- (char
- (apply #'string (last path)))
- (path
- (apply #'string (reverse path)))
- (t
- (error "Bad `aw-leading-char-style': %S"
- aw-leading-char-style)))
- (cond ((string-equal old-str "\t")
- (make-string (1- tab-width) ?\ ))
- ((string-equal old-str "\n")
- "\n")
+ (let ((wnd (cdr leaf)))
+ (with-selected-window wnd
+ (when (= 0 (buffer-size))
+ (push (current-buffer) aw-empty-buffers-list)
+ (let ((inhibit-read-only t))
+ (insert " ")))
+ (let* ((pt (car leaf))
+ (ol (make-overlay pt (1+ pt) (window-buffer wnd)))
+ (old-str (or
+ (ignore-errors
+ (with-selected-window wnd
+ (buffer-substring pt (1+ pt))))
+ ""))
+ (new-str
+ (concat
+ (cl-case aw-leading-char-style
+ (char
+ (apply #'string (last path)))
+ (path
+ (apply #'string (reverse path)))
(t
- (make-string
- (max 0 (1- (string-width old-str)))
- ?\ ))))))
- (overlay-put ol 'face 'aw-leading-char-face)
- (overlay-put ol 'window wnd)
- (overlay-put ol 'display new-str)
- (push ol avy--overlays-lead)))
+ (error "Bad `aw-leading-char-style': %S"
+ aw-leading-char-style)))
+ (cond ((string-equal old-str "\t")
+ (make-string (1- tab-width) ?\ ))
+ ((string-equal old-str "\n")
+ "\n")
+ (t
+ (make-string
+ (max 0 (1- (string-width old-str)))
+ ?\ ))))))
+ (overlay-put ol 'face 'aw-leading-char-face)
+ (overlay-put ol 'window wnd)
+ (overlay-put ol 'display new-str)
+ (push ol avy--overlays-lead)))))
(defun aw--make-backgrounds (wnd-list)
"Create a dim background overlay for each window on WND-LIST."
@@ -216,91 +232,122 @@ LEAF is (PT . WND)."
ol))
wnd-list))))
-(defvar aw--flip-keys nil
- "Pre-processed `aw-flip-keys'.")
-
-(defcustom aw-flip-keys '("n")
- "Keys which should select the last window."
- :set (lambda (sym val)
- (set sym val)
- (setq aw--flip-keys
- (mapcar (lambda (x) (aref (kbd x) 0)) val))))
-
-(defun aw-select (mode-line)
+(define-obsolete-variable-alias
+ 'aw-flip-keys 'aw--flip-keys "0.1.0"
+ "Use `aw-dispatch-alist' instead.")
+
+(defvar aw-dispatch-function 'aw-dispatch-default
+ "Function to call when a character not in `aw-keys' is pressed.")
+
+(defvar aw-action nil
+ "Function to call at the end of `aw-select'.")
+
+(defun aw-set-mode-line (str)
+ "Set mode line indicator to STR."
+ (setq ace-window-mode str)
+ (force-mode-line-update))
+
+(defvar aw-dispatch-alist
+ '((?x aw-delete-window " Ace - Delete Window")
+ (?m aw-swap-window " Ace - Swap Window")
+ (?n aw-flip-window)
+ (?v aw-split-window-vert " Ace - Split Vert Window")
+ (?b aw-split-window-horz " Ace - Split Horz Window")
+ (?i delete-other-windows " Ace - Maximize Window")
+ (?o delete-other-windows))
+ "List of actions for `aw-dispatch-default'.")
+
+(defun aw-dispatch-default (char)
+ "Perform an action depending on CHAR."
+ (let ((val (cdr (assoc char aw-dispatch-alist))))
+ (if val
+ (if (and (car val) (cadr val))
+ (prog1 (setq aw-action (car val))
+ (aw-set-mode-line (cadr val)))
+ (funcall (car val))
+ (throw 'done 'exit))
+ (avy-handler-default char))))
+
+(defun aw-select (mode-line &optional action)
"Return a selected other window.
Amend MODE-LINE to the mode line for the duration of the selection."
+ (setq aw-action action)
(let ((start-window (selected-window))
(next-window-scope (cl-case aw-scope
('global 'visible)
('frame 'frame)))
(wnd-list (aw-window-list))
- final-window)
- (cl-case (length wnd-list)
- (0
- start-window)
- (1
- (car wnd-list))
- (2
- (setq final-window (next-window nil nil next-window-scope))
- (while (and (aw-ignored-p final-window)
- (not (equal final-window start-window)))
- (setq final-window (next-window final-window nil next-window-scope)))
- final-window)
- (t
- (let ((candidate-list
- (mapcar (lambda (wnd)
- ;; can't jump if the buffer is empty
- (with-current-buffer (window-buffer wnd)
- (when (= 0 (buffer-size))
- (insert " ")))
- (cons (aw-offset wnd) wnd))
- wnd-list)))
- (aw--make-backgrounds wnd-list)
- (setq ace-window-mode mode-line)
- (force-mode-line-update)
- ;; turn off helm transient map
- (remove-hook 'post-command-hook 'helm--maybe-update-keymap)
- (unwind-protect
- (condition-case err
- (or (cdr (avy-read (avy-tree candidate-list aw-keys)
- #'aw--lead-overlay
- #'avy--remove-leading-chars))
- start-window)
- (error
- (if (memq (nth 2 err) aw--flip-keys)
- (aw--pop-window)
- (signal (car err) (cdr err)))))
- (aw--done)))))))
+ window)
+ (setq window
+ (cond ((<= (length wnd-list) 1)
+ (when aw-dispatch-always
+ (setq aw-action
+ (unwind-protect
+ (catch 'done
+ (funcall aw-dispatch-function (read-char)))
+ (aw--done)))
+ (when (eq aw-action 'exit)
+ (setq aw-action nil)))
+ (or (car wnd-list) start-window))
+ ((and (= (length wnd-list) 2)
+ (not aw-dispatch-always)
+ (not aw-ignore-current))
+ (let ((wnd (next-window nil nil next-window-scope)))
+ (while (and (aw-ignored-p wnd)
+ (not (equal wnd start-window)))
+ (setq wnd (next-window wnd nil next-window-scope)))
+ wnd))
+ (t
+ (let ((candidate-list
+ (mapcar (lambda (wnd)
+ (cons (aw-offset wnd) wnd))
+ wnd-list)))
+ (aw--make-backgrounds wnd-list)
+ (aw-set-mode-line mode-line)
+ ;; turn off helm transient map
+ (remove-hook 'post-command-hook 'helm--maybe-update-keymap)
+ (unwind-protect
+ (let* ((avy-handler-function aw-dispatch-function)
+ (res (avy-read (avy-tree candidate-list aw-keys)
+ #'aw--lead-overlay
+ #'avy--remove-leading-chars)))
+ (if (eq res 'exit)
+ (setq aw-action nil)
+ (or (cdr res)
+ start-window)))
+ (aw--done))))))
+ (if aw-action
+ (funcall aw-action window)
+ window)))
;;* Interactive
;;;###autoload
(defun ace-select-window ()
"Ace select window."
(interactive)
- (aw-switch-to-window
- (aw-select " Ace - Window")))
+ (aw-select " Ace - Window"
+ #'aw-switch-to-window))
;;;###autoload
(defun ace-delete-window ()
"Ace delete window."
(interactive)
- (aw-delete-window
- (aw-select " Ace - Delete Window")))
+ (aw-select " Ace - Delete Window"
+ #'aw-delete-window))
;;;###autoload
(defun ace-swap-window ()
"Ace swap window."
(interactive)
- (aw-swap-window
- (aw-select " Ace - Swap Window")))
+ (aw-select " Ace - Swap Window"
+ #'aw-swap-window))
;;;###autoload
(defun ace-maximize-window ()
"Ace maximize window."
(interactive)
- (select-window
- (aw-select " Ace - Maximize Window"))
- (delete-other-windows))
+ (aw-select " Ace - Maximize Window"
+ #'delete-other-windows))
;;;###autoload
(defun ace-window (arg)
@@ -360,10 +407,15 @@ Windows are numbered top down, left to right."
"Return the removed top of `aw--window-ring'."
(let (res)
(condition-case nil
- (while (not (window-live-p
- (setq res (ring-remove aw--window-ring 0)))))
+ (while (or (not (window-live-p
+ (setq res (ring-remove aw--window-ring 0))))
+ (equal res (selected-window))))
(error
- (error "No previous windows stored")))
+ (if (= (length (aw-window-list)) 2)
+ (progn
+ (other-window 1)
+ (setq res (selected-window)))
+ (error "No previous windows stored"))))
res))
(defun aw-switch-to-window (window)
@@ -395,6 +447,10 @@ Windows are numbered top down, left to right."
(delete-window window)
(error "Got a dead window %S" window)))))
+(defcustom aw-swap-invert nil
+ "When non-nil, the other of the two swapped windows gets the point."
+ :type 'boolean)
+
(defun aw-swap-window (window)
"Swap buffers of current window and WINDOW."
(cl-labels ((swap-windows (window1 window2)
@@ -412,7 +468,19 @@ Windows are numbered top down, left to right."
(when (and (window-live-p window)
(not (eq window this-window)))
(aw--push-window this-window)
- (swap-windows this-window window)))))
+ (if aw-swap-invert
+ (swap-windows window this-window)
+ (swap-windows this-window window))))))
+
+(defun aw-split-window-vert (window)
+ "Split WINDOW vertically."
+ (select-window window)
+ (split-window-vertically))
+
+(defun aw-split-window-horz (window)
+ "Split WINDOW horizontally."
+ (select-window window)
+ (split-window-horizontally))
(defun aw-offset (window)
"Return point in WINDOW that's closest to top left corner.
diff --git a/packages/csv-mode/csv-mode.el b/packages/csv-mode/csv-mode.el
index effc72e..692579d 100644
--- a/packages/csv-mode/csv-mode.el
+++ b/packages/csv-mode/csv-mode.el
@@ -5,7 +5,7 @@
;; Author: Francis J. Wright <F.J.Wright at qmul.ac.uk>
;; Time-stamp: <23 August 2004>
;; URL: http://centaur.maths.qmul.ac.uk/Emacs/
-;; Version: 1.3
+;; Version: 1.4
;; Keywords: convenience
;; This package is free software; you can redistribute it and/or modify
@@ -332,24 +332,25 @@ It must be either a string or nil."
(list (edit-and-eval-command
"Comment start (string or nil): " csv-comment-start)))
;; Paragraph means a group of contiguous records:
- (setq csv-comment-start string)
(set (make-local-variable 'paragraph-separate) "[:space:]*$") ; White space.
(set (make-local-variable 'paragraph-start) "\n");Must include \n explicitly!
- (if string
- (progn
- (setq paragraph-separate (concat paragraph-separate "\\|" string)
- paragraph-start (concat paragraph-start "\\|" string))
- (set (make-local-variable 'comment-start) string)
- (modify-syntax-entry
- (string-to-char string) "<" csv-mode-syntax-table)
- (modify-syntax-entry ?\n ">" csv-mode-syntax-table))
- (with-syntax-table text-mode-syntax-table
- (modify-syntax-entry (string-to-char string)
- (string (char-syntax (string-to-char string)))
- csv-mode-syntax-table)
- (modify-syntax-entry ?\n
- (string (char-syntax ?\n))
- csv-mode-syntax-table))))
+ ;; Remove old comment-start/end if available
+ (with-syntax-table text-mode-syntax-table
+ (when comment-start
+ (modify-syntax-entry (string-to-char comment-start)
+ (string (char-syntax (string-to-char comment-start)))
+ csv-mode-syntax-table))
+ (modify-syntax-entry ?\n
+ (string (char-syntax ?\n))
+ csv-mode-syntax-table))
+ (when string
+ (setq paragraph-separate (concat paragraph-separate "\\|" string)
+ paragraph-start (concat paragraph-start "\\|" string))
+ (set (make-local-variable 'comment-start) string)
+ (modify-syntax-entry
+ (string-to-char string) "<" csv-mode-syntax-table)
+ (modify-syntax-entry ?\n ">" csv-mode-syntax-table))
+ (setq csv-comment-start string))
;;;###autoload
(add-to-list 'auto-mode-alist '("\\.[Cc][Ss][Vv]\\'" . csv-mode))
diff --git a/packages/let-alist/let-alist.el b/packages/let-alist/let-alist.el
index c490a9f..ca7a904 100644
--- a/packages/let-alist/let-alist.el
+++ b/packages/let-alist/let-alist.el
@@ -4,7 +4,7 @@
;; Author: Artur Malabarba <address@hidden>
;; Maintainer: Artur Malabarba <address@hidden>
-;; Version: 1.0.3
+;; Version: 1.0.4
;; Keywords: extensions lisp
;; Prefix: let-alist
;; Separator: -
@@ -72,12 +72,12 @@ symbol, and each cdr is the same symbol without the `.'."
;; Return the cons cell inside a list, so it can be appended
;; with other results in the clause below.
(list (cons data (intern (replace-match "" nil nil name)))))))
- ((not (listp data)) nil)
- (t (apply #'append
- (mapcar #'let-alist--deep-dot-search data)))))
+ ((not (consp data)) nil)
+ (t (append (let-alist--deep-dot-search (car data))
+ (let-alist--deep-dot-search (cdr data))))))
(defun let-alist--access-sexp (symbol variable)
- "Return a sexp used to acess SYMBOL inside VARIABLE."
+ "Return a sexp used to access SYMBOL inside VARIABLE."
(let* ((clean (let-alist--remove-dot symbol))
(name (symbol-name clean)))
(if (string-match "\\`\\." name)
diff --git a/packages/nlinum/nlinum.el b/packages/nlinum/nlinum.el
index 2505c98..98c9cbc 100644
--- a/packages/nlinum/nlinum.el
+++ b/packages/nlinum/nlinum.el
@@ -82,18 +82,36 @@ Linum mode is a buffer-local minor mode."
width)))))
(defun nlinum--setup-window ()
- (let ((width (if (display-graphic-p)
- (ceiling
- (let ((width (nlinum--face-width 'linum)))
- (if width
- (/ (* nlinum--width 1.0 width)
- (frame-char-width))
- (/ (* nlinum--width 1.0
- (nlinum--face-height 'linum))
- (frame-char-height)))))
- nlinum--width)))
- (set-window-margins nil (if nlinum-mode width)
- (cdr (window-margins)))))
+ ;; FIXME: The interaction between different uses of the margin is
+ ;; problematic. We should have a way for different packages to indicate (and
+ ;; change) their preference independently.
+ (let* ((width (if (display-graphic-p)
+ (ceiling
+ (let ((width (nlinum--face-width 'linum)))
+ (if width
+ (/ (* nlinum--width 1.0 width)
+ (frame-char-width))
+ (/ (* nlinum--width 1.0
+ (nlinum--face-height 'linum))
+ (frame-char-height)))))
+ nlinum--width))
+ (cur-margins (window-margins))
+ (cur-margin (car cur-margins))
+ ;; (EXT . OURS) keeps track of the size of the margin, where EXT is
the
+ ;; size chosen by external code and OURS is the size we last set.
+ ;; OURS is used to detect when someone else modifies the margin.
+ (margin-settings (window-parameter nil 'linum--margin)))
+ (if margin-settings
+ (unless (eq (cdr margin-settings) cur-margin)
+ ;; Damn! The margin is not what it used to be! => Update EXT!
+ (setcar margin-settings cur-margin))
+ (set-window-parameter nil 'linum--margin
+ (setq margin-settings (list cur-margin))))
+ (and (car margin-settings) width
+ (setq width (max width (car margin-settings))))
+ (setcdr margin-settings width)
+ (set-window-margins nil (if nlinum-mode width (car margin-settings))
+ (cdr cur-margins))))
(defun nlinum--setup-windows ()
(dolist (win (get-buffer-window-list nil nil t))
diff --git a/packages/ztree/README.md b/packages/ztree/README.md
new file mode 100644
index 0000000..30443a2
--- /dev/null
+++ b/packages/ztree/README.md
@@ -0,0 +1,72 @@
+ztree
+=====
+
+Ztree is a project dedicated to implementation of several text-tree
applications inside Emacs. It consists of 2 subprojects: **ztree-diff** and
**ztree-dir**(the basis of **ztree-diff**). Available in **GNU ELPA** and
**MELPA**.
+
+ztree-diff
+==========
+**ztree-diff** is a directory-diff tool for Emacs inspired by commercial tools
like Beyond Compare or Araxis Merge. It supports showing the difference between
two directories; calling **Ediff** for not matching files, copying between
directories, deleting file/directories, hiding/showing equal files/directories.
+
+The comparison itself performed with the external **GNU diff** tool, so make
sure to have one in the executable path. Verified on OSX and Linux.
+
+If one wants to have a stand-alone application, consider the
(WIP)[zdircmp](https://github.com/fourier/zdircmp) project based on
**ztree-diff**.
+
+Add the following to your .emacs file:
+
+```scheme
+(push (substitute-in-file-name "path-to-ztree-directory") load-path)
+(require 'ztree-diff)
+```
+
+Call the `ztree-diff` interactive function:
+
+```
+M-x ztree-diff
+```
+Then you need to specify the left and right directories to compare.
+
+###Hotkeys supported
+The basic hotkeys are the same as in the **ztree-dir**. Additionally:
+ * `RET` on different files starts the **Ediff** (or open file if one absent
or the same)
+ * `Space` show the simple diff window for the current file instead of
**Ediff** (or view file if one absent or the same)
+ * `TAB` to fast switch between panels
+ * `h` key to toggle show/hide identical files/directories
+ * `C` key to copy current file or directory to the left or right panel
+ * `D` key to delete current file or directory
+ * `v` key to quick view the current file
+ * `r` initiates the rescan/refresh of current file or subdirectory
+ * `F5` forces the full rescan.
+
+Screenshots:
+
+![ztreediff
emacsx11](https://github.com/fourier/ztree/raw/screenshots/screenshots/emacs_diff_xterm.png
"Emacs in xterm with ztree-diff")
+
+![ztreediff-diff
emacsx11](https://github.com/fourier/ztree/raw/screenshots/screenshots/emacs_diff_simplediff_xterm.png
"Emacs in xterm with ztree-diff and simple diff")
+
+
+ztree-dir
+---------
+**ztree-dir** is a simple text-mode directory tree for Emacs. See screenshots
below for the GUI and the terminal versions of the **ztree-dir**.
+
+As above Add the following to your .emacs file:
+
+```scheme
+(push (substitute-in-file-name "path-to-ztree-directory") load-path)
+(require 'ztree-dir)
+```
+
+Call the `ztree-dir` interactive function:
+
+```
+M-x ztree-dir
+```
+
+* Open/close directories with double-click, `RET` or `Space` keys.
+* To jump to the parent directory, hit the `Backspace` key.
+* To toggle open/closed state of the subtree of the current directory, hit the
`x` key.
+
+
+![ztree
emacsapp](https://github.com/fourier/ztree/raw/screenshots/screenshots/emacs_app.png
"Emacs App with ztree-dir")
+
+![ztree
emacsx11](https://github.com/fourier/ztree/raw/screenshots/screenshots/emacs_xterm.png
"Emacs in xterm with ztree-dir")
+
diff --git a/packages/ztree/ztree-diff-model.el
b/packages/ztree/ztree-diff-model.el
new file mode 100644
index 0000000..572d976
--- /dev/null
+++ b/packages/ztree/ztree-diff-model.el
@@ -0,0 +1,349 @@
+;;; ztree-diff-model.el --- diff model for directory trees
+
+;; Copyright (C) 2013-2015 Free Software Foundation, Inc.
+;;
+;; Author: Alexey Veretennikov <alexey dot veretennikov at gmail dot com>
+;;
+;; Created: 2013-11-1l
+;;
+;; Keywords: files tools
+;; URL: https://github.com/fourier/ztree
+;; Compatibility: GNU Emacs GNU Emacs 24.x
+;;
+;; This file is part of GNU Emacs.
+;;
+;; GNU Emacs 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.
+;;
+;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+;;
+;;; Commentary:
+
+;; Diff model
+
+;;; Code:
+(require 'ztree-util)
+
+(defvar ztree-diff-model-wait-message nil
+ "Message showing while constructing the diff tree.")
+(make-variable-buffer-local 'ztree-diff-model-wait-message)
+
+
+(defun ztree-diff-model-update-wait-message ()
+ "Update the wait mesage with one more '.' progress indication."
+ (when ztree-diff-model-wait-message
+ (setq ztree-diff-model-wait-message (concat ztree-diff-model-wait-message
"."))
+ (message ztree-diff-model-wait-message)))
+
+
+
+;; Create a record ztree-diff-node with defined fielsd and getters/setters
+;; here:
+;; parent - parent node
+;; left-path is the full path on the left side of the diff window,
+;; right-path is the full path of the right side,
+;; short-name - is the file or directory name
+;; children - list of nodes - files or directories if the node is a directory
+;; different = {nil, 'new, 'diff} - means comparison status
+(defrecord ztree-diff-node (parent left-path right-path short-name
right-short-name children different))
+
+(defun ztree-diff-node-to-string (node)
+ "Construct the string with contents of the NODE given."
+ (let* ((string-or-nil #'(lambda (x) (if x
+ (cond ((stringp x) x)
+ ((eq x 'new) "new")
+ ((eq x 'diff) "different")
+ (t (ztree-diff-node-short-name
x)))
+ "(empty)")))
+ (children (ztree-diff-node-children node))
+ (ch-str ""))
+ (dolist (x children)
+ (setq ch-str (concat ch-str "\n * " (ztree-diff-node-short-name x))))
+ (concat "Node: " (ztree-diff-node-short-name node)
+ "\n"
+ ;; " * Parent: " (let ((parent (ztree-diff-node-parent node)))
+ ;; (if parent (ztree-diff-node-short-name parent)
"nil"))
+ " * Parent: " (funcall string-or-nil (ztree-diff-node-parent node))
+ "\n"
+ " * Left path: " (funcall string-or-nil (ztree-diff-node-left-path
node))
+ "\n"
+ " * Right path: " (funcall string-or-nil
(ztree-diff-node-right-path node))
+ "\n"
+ " * Children: " ch-str
+ "\n")))
+
+
+(defun ztree-diff-node-short-name-wrapper (node &optional right-side)
+ "Return the short name of the NODE given.
+If the RIGHT-SIDE is true, take the right leaf"
+ (if (not right-side)
+ (ztree-diff-node-short-name node)
+ (ztree-diff-node-right-short-name node)))
+
+
+(defun ztree-diff-node-is-directory (node)
+ "Determines if the NODE is a directory."
+ (let ((left (ztree-diff-node-left-path node))
+ (right (ztree-diff-node-right-path node)))
+ (if left
+ (file-directory-p left)
+ (file-directory-p right))))
+
+(defun ztree-diff-node-side (node)
+ "Determine the side there the file is present for NODE.
+Return BOTH if the file present on both sides;
+LEFT if only on the left side and
+RIGHT if only on the right side."
+ (let ((left (ztree-diff-node-left-path node))
+ (right (ztree-diff-node-right-path node)))
+ (if (and left right) 'both
+ (if left 'left 'right))))
+
+(defun ztree-diff-node-equal (node1 node2)
+ "Determines if NODE1 and NODE2 are equal."
+ (and (string-equal (ztree-diff-node-short-name node1)
+ (ztree-diff-node-short-name node2))
+ (string-equal (ztree-diff-node-left-path node1)
+ (ztree-diff-node-left-path node2))
+ (string-equal (ztree-diff-node-right-path node1)
+ (ztree-diff-node-right-path node1))))
+
+(defun ztree-diff-untrampify-filename (file)
+ "Return FILE as the local file name."
+ (require 'tramp)
+ (if (not (tramp-tramp-file-p file))
+ file
+ (tramp-file-name-localname (tramp-dissect-file-name file))))
+
+(defun ztree-diff-modef-quotify-string (x)
+ "Surround string X with quotes."
+ (concat "\"" x "\""))
+
+(defun ztree-diff-model-files-equal (file1 file2)
+ "Compare files FILE1 and FILE2 using external diff.
+Returns t if equal."
+ (let* ((file1-untrampified (ztree-diff-untrampify-filename
(ztree-diff-modef-quotify-string file1)))
+ (file2-untrampified (ztree-diff-untrampify-filename
(ztree-diff-modef-quotify-string file2)))
+ (diff-command (concat "diff -q" " " file1-untrampified " "
file2-untrampified))
+ (diff-output (shell-command-to-string diff-command)))
+ (not (> (length diff-output) 2))))
+
+(defun ztree-directory-files (dir)
+ "Return the list of full paths of files in a directory DIR.
+Filters out . and .."
+ (ztree-filter #'(lambda (file) (let ((simple-name (file-short-name file)))
+ (not (or (string-equal simple-name ".")
+ (string-equal simple-name "..")))))
+ (directory-files dir 'full)))
+
+(defun ztree-diff-model-partial-rescan (node)
+ "Rescan the NODE."
+ ;; assuming what parent is always exists
+ ;; otherwise the UI shall force the full rescan
+ (let ((parent (ztree-diff-node-parent node))
+ (isdir (ztree-diff-node-is-directory node))
+ (left (ztree-diff-node-left-path node))
+ (right (ztree-diff-node-right-path node)))
+ ;; if node is a directory - traverse
+ (when (and left right
+ (file-exists-p left)
+ (file-exists-p right))
+ (if isdir
+ (let ((traverse (ztree-diff-node-traverse
+ node
+ left
+ right)))
+ (ztree-diff-node-set-different node (car traverse))
+ (ztree-diff-node-set-children node (cdr traverse)))
+ ;; node is a file
+ (ztree-diff-node-set-different
+ node
+ (if (ztree-diff-model-files-equal left right)
+ nil
+ 'diff))))))
+
+(defun ztree-diff-model-subtree (parent path side)
+ "Create a subtree with given PARENT for the given PATH.
+Argument SIDE either 'left or 'right side."
+ (let ((files (ztree-directory-files path))
+ (result nil))
+ (dolist (file files)
+ (if (file-directory-p file)
+ (let* ((node (ztree-diff-node-create
+ parent
+ (when (eq side 'left) file)
+ (when (eq side 'right) file)
+ (file-short-name file)
+ (file-short-name file)
+ nil
+ 'new))
+ (children (ztree-diff-model-subtree node file side)))
+ (ztree-diff-node-set-children node children)
+ (push node result))
+ (push (ztree-diff-node-create
+ parent
+ (when (eq side 'left) file)
+ (when (eq side 'right) file)
+ (file-short-name file)
+ (file-short-name file)
+ nil
+ 'new)
+ result)))
+ result))
+
+(defun ztree-diff-node-update-diff-from-children (node)
+ "Set the diff status for the NODE based on its children."
+ (let ((children (ztree-diff-node-children node))
+ (diff nil))
+ (dolist (child children)
+ (setq diff
+ (ztree-diff-model-update-diff
+ diff
+ (ztree-diff-node-different child))))
+ (ztree-diff-node-set-different node diff)))
+
+(defun ztree-diff-node-update-all-parents-diff (node)
+ "Recursively update all parents diff status for the NODE."
+ (let ((parent node))
+ (while (setq parent (ztree-diff-node-parent parent))
+ (ztree-diff-node-update-diff-from-children parent))))
+
+
+(defun ztree-diff-model-update-diff (old new)
+ "Get the diff status depending if OLD or NEW is not nil."
+ (if new
+ (if (or (not old)
+ (eq old 'new))
+ new
+ old)
+ old))
+
+(defun ztree-diff-node-traverse (parent path1 path2)
+ "Traverse 2 paths creating the list nodes with PARENT defined and diff
status.
+Function traversing 2 paths PATH1 and PATH2 returning the list where the
+first element is the difference status (nil, 'diff, 'new') and
+the rest is the combined list of nodes."
+ (let ((list1 (ztree-directory-files path1))
+ (list2 (ztree-directory-files path2))
+ (different-dir nil)
+ (result nil))
+ (ztree-diff-model-update-wait-message)
+ ;; first - adding all entries from left directory
+ (dolist (file1 list1)
+ ;; for every entry in the first directory
+ ;; we are creating the node
+ (let* ((simple-name (file-short-name file1))
+ (isdir (file-directory-p file1))
+ (children nil)
+ (different nil)
+ ;; create the current node to be set as parent to
+ ;; subdirectories
+ (node (ztree-diff-node-create parent file1 nil simple-name
simple-name nil nil))
+ ;; 1. find if the file is in the second directory and the type
+ ;; is the same - i.e. both are directories or both are files
+ (file2 (ztree-find list2
+ #'(lambda (x) (and (string-equal
(file-short-name x)
+ simple-name)
+ (eq isdir (file-directory-p
x)))))))
+ ;; 2. if it is not in the second directory, add it as a node
+ (if (not file2)
+ (progn
+ ;; 2.1 if it is a directory, add the whole subtree
+ (when (file-directory-p file1)
+ (setq children (ztree-diff-model-subtree node file1 'left)))
+ ;; 2.2 update the difference status for this entry
+ (setq different 'new))
+ ;; 3. if it is found in second directory and of the same type
+ ;; 3.1 if it is a file
+ (if (not (file-directory-p file1))
+ ;; 3.1.1 set difference status to this entry
+ (setq different (if (ztree-diff-model-files-equal file1 file2)
nil 'diff))
+ ;; 3.2 if it is the directory
+ ;; 3.2.1 get the result of the directories comparison together
with status
+ (let ((traverse (ztree-diff-node-traverse node file1 file2)))
+ ;; 3.2.2 update the difference status for whole comparison from
+ ;; difference result from the 2 subdirectories comparison
+ (setq different (car traverse))
+ ;; 3.2.3 set the children list from the 2 subdirectories
comparison
+ (setq children (cdr traverse)))))
+ ;; 2.3 update difference status for the whole comparison
+ (setq different-dir (ztree-diff-model-update-diff different-dir
different))
+ ;; update calculated parameters of the node
+ (ztree-diff-node-set-right-path node file2)
+ (ztree-diff-node-set-children node children)
+ (ztree-diff-node-set-different node different)
+ ;; push the created node to the result list
+ (push node result)))
+ ;; second - adding entries from the right directory which are not present
+ ;; in the left directory
+ (dolist (file2 list2)
+ ;; for every entry in the second directory
+ ;; we are creating the node
+ (let* ((simple-name (file-short-name file2))
+ (isdir (file-directory-p file2))
+ (children nil)
+ ;; create the node to be added to the results list
+ (node (ztree-diff-node-create parent nil file2 simple-name
simple-name nil 'new))
+ ;; 1. find if the file is in the first directory and the type
+ ;; is the same - i.e. both are directories or both are files
+ (file1 (ztree-find list1
+ #'(lambda (x) (and (string-equal
(file-short-name x)
+ simple-name)
+ (eq isdir (file-directory-p
x)))))))
+ ;; if it is not in the first directory, add it as a node
+ (when (not file1)
+ ;; if it is a directory, set the whole subtree to children
+ (when (file-directory-p file2)
+ (setq children (ztree-diff-model-subtree node file2 'right)))
+ ;; update the different status for the whole comparison
+ (setq different-dir (ztree-diff-model-update-diff different-dir
'new))
+ ;; set calculated children to the node
+ (ztree-diff-node-set-children node children)
+ ;; push the created node to the result list
+ (push node result))))
+ ;; result is a pair: difference status and nodes list
+ (cons different-dir result)))
+
+(defun ztree-diff-model-create (dir1 dir2)
+ "Create a node based on DIR1 and DIR2."
+ (when (not (file-directory-p dir1))
+ (error "Path %s is not a directory" dir1))
+ (when (not (file-directory-p dir2))
+ (error "Path %s is not a directory" dir2))
+ (setq ztree-diff-model-wait-message (concat "Comparing " dir1 " and " dir2 "
..."))
+ (let* ((model
+ (ztree-diff-node-create nil dir1 dir2
+ (file-short-name dir1)
+ (file-short-name dir2)
+ nil
+ nil))
+ (traverse (ztree-diff-node-traverse model dir1 dir2)))
+ (ztree-diff-node-set-children model (cdr traverse))
+ (ztree-diff-node-set-different model (car traverse))
+ (message "Done.")
+ model))
+
+(defun ztree-diff-model-update-node (node)
+ "Refresh the NODE."
+ (setq ztree-diff-model-wait-message
+ (concat "Updating " (ztree-diff-node-short-name node) " ..."))
+ (let ((traverse (ztree-diff-node-traverse node
+ (ztree-diff-node-left-path node)
+ (ztree-diff-node-right-path
node))))
+ (ztree-diff-node-set-children node (cdr traverse))
+ (ztree-diff-node-set-different node (car traverse))
+ (message "Done.")))
+
+
+
+(provide 'ztree-diff-model)
+
+;;; ztree-diff-model.el ends here
diff --git a/packages/ztree/ztree-diff.el b/packages/ztree/ztree-diff.el
new file mode 100644
index 0000000..8d1d9d0
--- /dev/null
+++ b/packages/ztree/ztree-diff.el
@@ -0,0 +1,455 @@
+;;; ztree-diff.el --- Text mode diff for directory trees
+
+;; Copyright (C) 2013-2015 Free Software Foundation, Inc.
+;;
+;; Author: Alexey Veretennikov <alexey dot veretennikov at gmail dot com>
+;;
+;; Created: 2013-11-1l
+;;
+;; Keywords: files tools
+;; URL: https://github.com/fourier/ztree
+;; Compatibility: GNU Emacs GNU Emacs 24.x
+;;
+;; This file is part of GNU Emacs.
+;;
+;; GNU Emacs 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.
+;;
+;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+;;
+;;; Commentary:
+
+;;; Code:
+(require 'ztree-view)
+(require 'ztree-diff-model)
+
+(defconst ztree-diff-hidden-files-regexp "^\\."
+ "Hidden files regexp.
+By default all filest starting with dot '.', including . and ..")
+
+(defface ztreep-diff-header-face
+ '((((type tty pc) (class color)) :foreground "lightblue" :weight bold)
+ (((background dark)) (:height 1.2 :foreground "lightblue" :weight bold))
+ (t :height 1.2 :foreground "darkblue" :weight bold))
+ "*Face used for the header in Ztree Diff buffer."
+ :group 'Ztree-diff :group 'font-lock-highlighting-faces)
+(defvar ztreep-diff-header-face 'ztreep-diff-header-face)
+
+(defface ztreep-diff-header-small-face
+ '((((type tty pc) (class color)) :foreground "lightblue" :weight bold)
+ (((background dark)) (:foreground "lightblue" :weight bold))
+ (t :weight bold :foreground "darkblue"))
+ "*Face used for the header in Ztree Diff buffer."
+ :group 'Ztree-diff :group 'font-lock-highlighting-faces)
+(defvar ztreep-diff-header-small-face 'ztreep-diff-header-small-face)
+
+(defface ztreep-diff-model-diff-face
+ '((t (:foreground "red")))
+ "*Face used for different files in Ztree-diff."
+ :group 'Ztree-diff :group 'font-lock-highlighting-faces)
+(defvar ztreep-diff-model-diff-face 'ztreep-diff-model-diff-face)
+
+(defface ztreep-diff-model-add-face
+ '((t (:foreground "blue")))
+ "*Face used for added files in Ztree-diff."
+ :group 'Ztree-diff :group 'font-lock-highlighting-faces)
+(defvar ztreep-diff-model-add-face 'ztreep-diff-model-add-face)
+
+(defface ztreep-diff-model-normal-face
+ '((t (:foreground "#7f7f7f")))
+ "*Face used for non-modified files in Ztree-diff."
+ :group 'Ztree-diff :group 'font-lock-highlighting-faces)
+(defvar ztreep-diff-model-normal-face 'ztreep-diff-model-normal-face)
+
+
+(defvar ztree-diff-filter-list (list ztree-diff-hidden-files-regexp)
+ "List of regexp file names to filter out.
+By default paths starting with dot (like .git) are ignored")
+(make-variable-buffer-local 'ztree-diff-filter-list)
+
+(defvar ztree-diff-dirs-pair nil
+ "Pair of the directories stored. Used to perform the full rescan.")
+(make-variable-buffer-local 'ztree-diff-dirs-pair)
+
+(defvar ztree-diff-show-equal-files t
+ "Show or not equal files/directories on both sides.")
+(make-variable-buffer-local 'ztree-diff-show-equal-files)
+
+;;;###autoload
+(define-minor-mode ztreediff-mode
+ "A minor mode for displaying the difference of the directory trees in text
mode."
+ ;; initial value
+ nil
+ ;; modeline name
+ " Diff"
+ ;; The minor mode keymap
+ `(
+ (,(kbd "C") . ztree-diff-copy)
+ (,(kbd "h") . ztree-diff-toggle-show-equal-files)
+ (,(kbd "D") . ztree-diff-delete-file)
+ (,(kbd "v") . ztree-diff-view-file)
+ (,(kbd "d") . ztree-diff-simple-diff-files)
+ (,(kbd "r") . ztree-diff-partial-rescan)
+ ([f5] . ztree-diff-full-rescan)))
+
+
+(defun ztree-diff-node-face (node)
+ "Return the face for the NODE depending on diff status."
+ (let ((diff (ztree-diff-node-different node)))
+ (cond ((eq diff 'diff) ztreep-diff-model-diff-face)
+ ((eq diff 'new) ztreep-diff-model-add-face)
+ (t ztreep-diff-model-normal-face))))
+
+(defun ztree-diff-insert-buffer-header ()
+ "Insert the header to the ztree buffer."
+ (insert-with-face "Differences tree" ztreep-diff-header-face)
+ (newline-and-begin)
+ (when ztree-diff-dirs-pair
+ (insert-with-face (concat "Left: " (car ztree-diff-dirs-pair))
+ ztreep-diff-header-small-face)
+ (newline-and-begin)
+ (insert-with-face (concat "Right: " (cdr ztree-diff-dirs-pair))
+ ztreep-diff-header-small-face)
+ (newline-and-begin))
+ (insert-with-face "Legend:" ztreep-diff-header-small-face)
+ (newline-and-begin)
+ (insert-with-face " Normal file " ztreep-diff-model-normal-face)
+ (insert-with-face "- same on both sides" ztreep-diff-header-small-face)
+ (newline-and-begin)
+ (insert-with-face " Orphan file " ztreep-diff-model-add-face)
+ (insert-with-face "- does not exist on other side"
ztreep-diff-header-small-face)
+ (newline-and-begin)
+ (insert-with-face " Mismatch file " ztreep-diff-model-diff-face)
+ (insert-with-face "- different from other side"
ztreep-diff-header-small-face)
+ (newline-and-begin)
+ (insert-with-face "==============" ztreep-diff-header-face)
+ (newline-and-begin))
+
+(defun ztree-diff-full-rescan ()
+ "Force full rescan of the directory trees."
+ (interactive)
+ (when (and ztree-diff-dirs-pair
+ (yes-or-no-p (format "Force full rescan?")))
+ (ztree-diff (car ztree-diff-dirs-pair) (cdr ztree-diff-dirs-pair))))
+
+
+
+(defun ztree-diff-existing-common (node)
+ "Return the NODE if both left and right sides exist."
+ (let ((left (ztree-diff-node-left-path node))
+ (right (ztree-diff-node-right-path node)))
+ (if (and left right
+ (file-exists-p left)
+ (file-exists-p right))
+ node
+ nil)))
+
+(defun ztree-diff-existing-common-parent (node)
+ "Return the first node in up in hierarchy of the NODE which has both sides."
+ (let ((common (ztree-diff-existing-common node)))
+ (if common
+ common
+ (ztree-diff-existing-common-parent (ztree-diff-node-parent node)))))
+
+(defun ztree-diff-do-partial-rescan (node)
+ "Partly rescan the NODE."
+ (let* ((common (ztree-diff-existing-common-parent node))
+ (parent (ztree-diff-node-parent common)))
+ (if (not parent)
+ (when ztree-diff-dirs-pair
+ (ztree-diff (car ztree-diff-dirs-pair) (cdr ztree-diff-dirs-pair)))
+ (progn
+ (ztree-diff-model-partial-rescan common)
+ (ztree-diff-node-update-all-parents-diff node)
+ (ztree-refresh-buffer (line-number-at-pos))))))
+
+
+(defun ztree-diff-partial-rescan ()
+ "Perform partial rescan on the current node."
+ (interactive)
+ (let ((found (ztree-find-node-at-point)))
+ (when found
+ (ztree-diff-do-partial-rescan (car found)))))
+
+
+(defun ztree-diff-simple-diff (node)
+ "Create a simple diff buffer for files from left and right panels.
+Argument NODE node containing paths to files to call a diff on."
+ (let* ((node-left (ztree-diff-node-left-path node))
+ (node-right (ztree-diff-node-right-path node)))
+ (when (and
+ node-left
+ node-right
+ (not (file-directory-p node-left)))
+ ;; show the diff window on the bottom
+ ;; to not to crush tree appearance
+ (let ((split-width-threshold nil))
+ (diff node-left node-right)))))
+
+
+(defun ztree-diff-simple-diff-files ()
+ "Create a simple diff buffer for files from left and right panels."
+ (interactive)
+ (let ((found (ztree-find-node-at-point)))
+ (when found
+ (let ((node (car found)))
+ (ztree-diff-simple-diff node)))))
+
+(defun ztree-diff-node-action (node hard)
+ "Perform action on NODE:
+1 if both left and right sides present:
+ 1.1 if they are differend
+ 1.1.1 if HARD ediff
+ 1.1.2 simple diff otherwiste
+ 1.2 if they are the same - view left
+2 if left or right present - view left or rigth"
+ (let ((left (ztree-diff-node-left-path node))
+ (right (ztree-diff-node-right-path node))
+ (open-f '(lambda (path) (if hard (find-file path)
+ (let ((split-width-threshold nil))
+ (view-file-other-window path))))))
+ (cond ((and left right)
+ (if (not (ztree-diff-node-different node))
+ (funcall open-f left)
+ (if hard
+ (ediff left right)
+ (ztree-diff-simple-diff node))))
+ (left (funcall open-f left))
+ (right (funcall open-f right))
+ (t nil))))
+
+
+
+(defun ztree-diff-copy-file (node source-path destination-path copy-to-right)
+ "Update the NODE status and copy the file.
+File copied from SOURCE-PATH to DESTINATION-PATH.
+COPY-TO-RIGHT specifies which side of the NODE to update."
+ (let ((target-path (concat
+ (file-name-as-directory destination-path)
+ (file-name-nondirectory
+ (directory-file-name source-path)))))
+ (let ((err (condition-case error-trap
+ (progn
+ ;; don't ask for overwrite
+ ;; keep time stamp
+ (copy-file source-path target-path t t)
+ nil)
+ (error error-trap))))
+ ;; error message if failed
+ (if err (message (concat "Error: " (nth 2 err)))
+ (progn ; otherwise:
+ ;; assuming all went ok when left and right nodes are the same
+ ;; set both as not different
+ (ztree-diff-node-set-different node nil)
+ ;; update left/right paths
+ (if copy-to-right
+ (ztree-diff-node-set-right-path node target-path)
+ (ztree-diff-node-set-left-path node target-path))
+ (ztree-diff-node-update-all-parents-diff node)
+ (ztree-refresh-buffer (line-number-at-pos)))))))
+
+
+(defun ztree-diff-copy-dir (node source-path destination-path copy-to-right)
+ "Update the NODE status and copy the directory.
+Directory copied from SOURCE-PATH to DESTINATION-PATH.
+COPY-TO-RIGHT specifies which side of the NODE to update."
+ (let* ((src-path (file-name-as-directory source-path))
+ (target-path (file-name-as-directory destination-path))
+ (target-full-path (concat
+ target-path
+ (file-name-nondirectory
+ (directory-file-name source-path)))))
+ (let ((err (condition-case error-trap
+ (progn
+ ;; keep time stamp
+ ;; ask for overwrite
+ (copy-directory src-path target-path t t)
+ nil)
+ (error error-trap))))
+ ;; error message if failed
+ (if err (message (concat "Error: " (nth 1 err)))
+ (progn
+ (message target-full-path)
+ (if copy-to-right
+ (ztree-diff-node-set-right-path node
+ target-full-path)
+ (ztree-diff-node-set-left-path node
+ target-full-path))
+ (ztree-diff-model-update-node node)
+ (ztree-diff-node-update-all-parents-diff node)
+ (ztree-refresh-buffer (line-number-at-pos)))))))
+
+
+(defun ztree-diff-copy ()
+ "Copy the file under the cursor to other side."
+ (interactive)
+ (let ((found (ztree-find-node-at-point)))
+ (when found
+ (let* ((node (car found))
+ (side (cdr found))
+ (node-side (ztree-diff-node-side node))
+ (copy-to-right t) ; copy from left to right
+ (node-left (ztree-diff-node-left-path node))
+ (node-right (ztree-diff-node-right-path node))
+ (source-path nil)
+ (destination-path nil)
+ (parent (ztree-diff-node-parent node)))
+ (when parent ; do not copy the root node
+ ;; determine a side to copy from/to
+ ;; algorithm:
+ ;; 1) if both side are present, use the side
+ ;; variable
+ (setq copy-to-right (if (eq node-side 'both)
+ (eq side 'left)
+ ;; 2) if one of sides is absent, copy from
+ ;; the side where the file is present
+ (eq node-side 'left)))
+ ;; 3) in both cases determine if the destination
+ ;; directory is in place
+ (setq source-path (if copy-to-right node-left node-right)
+ destination-path (if copy-to-right
+ (ztree-diff-node-right-path parent)
+ (ztree-diff-node-left-path parent)))
+ (when (and source-path destination-path
+ (yes-or-no-p (format "Copy [%s]%s to [%s]%s/ ?"
+ (if copy-to-right "LEFT" "RIGHT")
+ (ztree-diff-node-short-name node)
+ (if copy-to-right "RIGHT" "LEFT")
+ destination-path)))
+ (if (file-directory-p source-path)
+ (ztree-diff-copy-dir node
+ source-path
+ destination-path
+ copy-to-right)
+ (ztree-diff-copy-file node
+ source-path
+ destination-path
+ copy-to-right))))))))
+
+(defun ztree-diff-view-file ()
+ "View file at point, depending on side."
+ (interactive)
+ (let ((found (ztree-find-node-at-point)))
+ (when found
+ (let* ((node (car found))
+ (side (cdr found))
+ (node-side (ztree-diff-node-side node))
+ (node-left (ztree-diff-node-left-path node))
+ (node-right (ztree-diff-node-right-path node)))
+ (when (or (eq node-side 'both)
+ (eq side node-side))
+ (cond ((and (eq side 'left)
+ node-left)
+ (view-file node-left))
+ ((and (eq side 'right)
+ node-right)
+ (view-file node-right))))))))
+
+
+(defun ztree-diff-delete-file ()
+ "Delete the file under the cursor."
+ (interactive)
+ (let ((found (ztree-find-node-at-point)))
+ (when found
+ (let* ((node (car found))
+ (side (cdr found))
+ (node-side (ztree-diff-node-side node))
+ (delete-from-left t)
+ (remove-path nil)
+ (parent (ztree-diff-node-parent node)))
+ (when parent ; do not delete the root node
+ ;; algorithm for determining what to delete similar to copy:
+ ;; 1. if the file is present on both sides, delete
+ ;; from the side currently selected
+ (setq delete-from-left (if (eq node-side 'both)
+ (eq side 'left)
+ ;; 2) if one of sides is absent, delete
+ ;; from the side where the file is present
+ (eq node-side 'left)))
+ (setq remove-path (if delete-from-left
+ (ztree-diff-node-left-path node)
+ (ztree-diff-node-right-path node)))
+ (when (yes-or-no-p (format "Delete the file [%s]%s ?"
+ (if delete-from-left "LEFT" "RIGHT")
+ remove-path))
+ (let* ((delete-command
+ (if (file-directory-p remove-path)
+ '(delete-directory remove-path t)
+ '(delete-file remove-path t)))
+ (children (ztree-diff-node-children parent))
+ (err
+ (condition-case error-trap
+ (progn
+ (eval delete-command)
+ nil)
+ (error error-trap))))
+ (if err (message (concat "Error: " (nth 2 err)))
+ (progn
+ (setq children (ztree-filter
+ #'(lambda (x) (not (ztree-diff-node-equal x
node)))
+ children))
+ (ztree-diff-node-set-children parent children))
+ (ztree-diff-node-update-all-parents-diff node)
+ (ztree-refresh-buffer (line-number-at-pos))))))))))
+
+
+
+(defun ztree-node-is-in-filter-list (node)
+ "Determine if the NODE is in filter list.
+If the node is in the filter list it shall not be visible"
+ (ztree-find ztree-diff-filter-list #'(lambda (rx) (string-match rx node))))
+
+
+(defun ztree-node-is-visible (node)
+ "Determine if the NODE should be visible."
+ (and (ztree-diff-node-parent node) ; parent is always visible
+ (not (ztree-node-is-in-filter-list (ztree-diff-node-short-name node)))
+ (or ztree-diff-show-equal-files
+ (ztree-diff-node-different node))))
+
+(defun ztree-diff-toggle-show-equal-files ()
+ "Toggle visibility of the equal files."
+ (interactive)
+ (setq ztree-diff-show-equal-files (not ztree-diff-show-equal-files))
+ (ztree-refresh-buffer))
+
+;;;###autoload
+(defun ztree-diff (dir1 dir2)
+ "Create an interactive buffer with the directory tree of the path given.
+Argument DIR1 left directory.
+Argument DIR2 right directory."
+ (interactive "DLeft directory \nDRight directory ")
+ (let* ((difference (ztree-diff-model-create dir1 dir2))
+ (buf-name (concat "*"
+ (ztree-diff-node-short-name difference)
+ " <--> "
+ (ztree-diff-node-right-short-name difference)
+ "*")))
+ (ztree-view buf-name
+ difference
+ 'ztree-node-is-visible
+ 'ztree-diff-insert-buffer-header
+ 'ztree-diff-node-short-name-wrapper
+ 'ztree-diff-node-is-directory
+ 'ztree-diff-node-equal
+ 'ztree-diff-node-children
+ 'ztree-diff-node-face
+ 'ztree-diff-node-action
+ 'ztree-diff-node-side)
+ (ztreediff-mode)
+ (setq ztree-diff-dirs-pair (cons dir1 dir2))
+ (ztree-refresh-buffer)))
+
+
+
+
+(provide 'ztree-diff)
+;;; ztree-diff.el ends here
diff --git a/packages/ztree/ztree-dir.el b/packages/ztree/ztree-dir.el
new file mode 100644
index 0000000..47a57cd
--- /dev/null
+++ b/packages/ztree/ztree-dir.el
@@ -0,0 +1,118 @@
+;;; ztree-dir.el --- Text mode directory tree
+
+;; Copyright (C) 2013-2015 Free Software Foundation, Inc.
+;;
+;; Author: Alexey Veretennikov <alexey dot veretennikov at gmail dot com>
+;;
+;; Created: 2013-11-1l
+;;
+;; Keywords: files tools
+;; URL: https://github.com/fourier/ztree
+;; Compatibility: GNU Emacs GNU Emacs 24.x
+;;
+;; This file is part of GNU Emacs.
+;;
+;; GNU Emacs 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.
+;;
+;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+;;
+;;; Commentary:
+;;
+;; Add the following to your .emacs file:
+;;
+;; (push (substitute-in-file-name "path-to-ztree-directory") load-path)
+;; (require 'ztree-dir)
+;;
+;; Call the ztree interactive function:
+;; M-x ztree-dir
+;; Open/close directories with double-click, Enter or Space keys
+;;
+;;; Issues:
+;;
+;;; TODO:
+;; 1) Add some file-handling and marking abilities
+;;
+;;; Code:
+
+(require 'ztree-util)
+(require 'ztree-view)
+
+;;
+;; Constants
+;;
+
+(defconst ztree-hidden-files-regexp "^\\."
+ "Hidden files regexp.
+By default all filest starting with dot '.', including . and ..")
+
+
+;;
+;; Faces
+;;
+
+(defface ztreep-header-face
+ '((((type tty pc) (class color)) :foreground "lightblue" :weight bold)
+ (((background dark)) (:height 1.2 :foreground "lightblue" :weight bold))
+ (t :height 1.2 :foreground "darkblue" :weight bold))
+ "*Face used for the header in Ztree buffer."
+ :group 'Ztree :group 'font-lock-highlighting-faces)
+(defvar ztreep-header-face 'ztreep-header-face)
+
+
+;;
+;; File bindings to the directory tree control
+;;
+
+(defun ztree-insert-buffer-header ()
+ "Insert the header to the ztree buffer."
+ (let ((start (point)))
+ (insert "Directory tree")
+ (newline-and-begin)
+ (insert "==============")
+ (set-text-properties start (point) '(face ztreep-header-face)))
+ (newline-and-begin))
+
+(defun ztree-file-not-hidden (filename)
+ "Determines if the file with FILENAME should be visible."
+ (not (string-match ztree-hidden-files-regexp
+ (file-short-name filename))))
+
+(defun ztree-find-file (node hard)
+ "Find the file at NODE.
+
+If HARD is non-nil, the file is opened in another window.
+Otherwise, the ztree window is used to find the file."
+ (when (and (stringp node) (file-readable-p node))
+ (if hard
+ (save-selected-window (find-file-other-window node))
+ (find-file node))))
+
+;;;###autoload
+(defun ztree-dir (path)
+ "Create an interactive buffer with the directory tree of the PATH given."
+ (interactive "DDirectory: ")
+ (when (and (file-exists-p path) (file-directory-p path))
+ (let ((buf-name (concat "*Directory " path " tree*")))
+ (ztree-view buf-name
+ (expand-file-name (substitute-in-file-name path))
+ 'ztree-file-not-hidden
+ 'ztree-insert-buffer-header
+ 'file-short-name
+ 'file-directory-p
+ 'string-equal
+ '(lambda (x) (directory-files x 'full))
+ nil ; face
+ 'ztree-find-file)))) ; action
+
+
+(provide 'ztree-dir)
+;;; ztree-dir.el ends here
diff --git a/packages/ztree/ztree-pkg.el b/packages/ztree/ztree-pkg.el
new file mode 100644
index 0000000..2ee40ca
--- /dev/null
+++ b/packages/ztree/ztree-pkg.el
@@ -0,0 +1,2 @@
+;; Generated package description from ztree.el
+(define-package "ztree" "1.0.1" "Text mode directory tree" 'nil :url
"https://github.com/fourier/ztree" :keywords '("files" "tools"))
diff --git a/packages/ztree/ztree-util.el b/packages/ztree/ztree-util.el
new file mode 100644
index 0000000..f5d3506
--- /dev/null
+++ b/packages/ztree/ztree-util.el
@@ -0,0 +1,133 @@
+;;; ztree-util.el --- Auxulary utilities for the ztree package
+
+;; Copyright (C) 2013-2015 Free Software Foundation, Inc.
+;;
+;; Author: Alexey Veretennikov <alexey dot veretennikov at gmail dot com>
+;;
+;; Created: 2013-11-1l
+;;
+;; Keywords: files tools
+;; URL: https://github.com/fourier/ztree
+;; Compatibility: GNU Emacs GNU Emacs 24.x
+;;
+;; This file is part of GNU Emacs.
+;;
+;; GNU Emacs 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.
+;;
+;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+;;
+;;; Commentary:
+
+;;; Code:
+(defun ztree-find (where which)
+ "Find element of the list WHERE matching predicate WHICH."
+ (catch 'found
+ (dolist (elt where)
+ (when (funcall which elt)
+ (throw 'found elt)))
+ nil))
+
+(defun ztree-filter (condp lst)
+ "Filter out elements not satisfying predicate CONDP in the list LST.
+Taken from http://www.emacswiki.org/emacs/ElispCookbook#toc39"
+ (delq nil
+ (mapcar (lambda (x) (and (funcall condp x) x)) lst)))
+
+
+(defun printable-string (string)
+ "Strip newline character from file names, like 'Icon\n.
+Argument STRING string to process.'."
+ (replace-regexp-in-string "\n" "" string))
+
+(defun file-short-name (file)
+ "By given FILE name return base file/directory name.
+Taken from http://lists.gnu.org/archive/html/emacs-devel/2011-01/msg01238.html"
+ (printable-string (file-name-nondirectory (directory-file-name file))))
+
+
+(defun newline-and-begin ()
+ "Move a point to the beginning of the next line."
+ (newline)
+ (beginning-of-line))
+
+(defun car-atom (value)
+ "Return VALUE if value is an atom, otherwise (car value) or nil.
+Used since `car-safe' returns nil for atoms"
+ (if (atom value) value (car value)))
+
+
+(defun insert-with-face (text face)
+ "Insert TEXT with the FACE provided."
+ (let ((start (point)))
+ (insert text)
+ (put-text-property start (point) 'face face)))
+
+
+(defmacro defrecord (record-name record-fields)
+ "Create a record (structure) and getters/setters.
+
+Record is the following set of functions:
+ - Record constructor with name \"RECORD-NAME\"-create and list of
+arguments which will be assigned to RECORD-FIELDS
+ - Record getters with names \"record-name\"-\"field\" accepting one
+argument - the record; \"field\" is from \"record-fields\" symbols
+ - Record setters with names \"record-name\"-set-\"field\" accepting two
+arguments - the record and the field value
+
+Example:
+\(defrecord person (name age))
+
+will be expanded to the following functions:
+
+\(defun person-create (name age) (...)
+\(defun person-name (record) (...)
+\(defun person-age (record) (...)
+\(defun person-set-name (record value) (...)
+\(defun person-set-age (record value) (...)"
+ (let ((ctor-name (intern (concat (symbol-name record-name) "-create")))
+ (rec-var (make-symbol "record")))
+ `(progn
+ ;; constructor with the name "record-name-create"
+ ;; with arguments list "record-fields" expanded
+ (defun ,ctor-name (,@record-fields)
+ (let ((,rec-var))
+ ,@(mapcar #'(lambda (x)
+ (list 'setq rec-var (list 'plist-put rec-var (list
'quote x) x)))
+ record-fields)))
+ ;; getters with names "record-name-field" where the "field"
+ ;; is from record-fields
+ ,@(mapcar #'(lambda (x)
+ (let ((getter-name (intern (concat (symbol-name
record-name)
+ "-"
+ (symbol-name x)))))
+ `(progn
+ (defun ,getter-name (,rec-var)
+ (plist-get ,rec-var ',x)
+ ))))
+ record-fields)
+ ;; setters wit names "record-name-set-field where the "field"
+ ;; is from record-fields
+ ;; arguments for setters: (record value)
+ ,@(mapcar #'(lambda (x)
+ (let ((setter-name (intern (concat (symbol-name
record-name)
+ "-set-"
+ (symbol-name x)))))
+ `(progn
+ (defun ,setter-name (,rec-var value)
+ (setq ,rec-var (plist-put ,rec-var ',x value))
+ ))))
+ record-fields))))
+
+
+(provide 'ztree-util)
+
+;;; ztree-util.el ends here
diff --git a/packages/ztree/ztree-view.el b/packages/ztree/ztree-view.el
new file mode 100644
index 0000000..c623bd6
--- /dev/null
+++ b/packages/ztree/ztree-view.el
@@ -0,0 +1,650 @@
+;;; ztree-view.el --- Text mode tree view (buffer)
+
+;; Copyright (C) 2013-2015 Free Software Foundation, Inc.
+;;
+;; Author: Alexey Veretennikov <alexey dot veretennikov at gmail dot com>
+;;
+;; Created: 2013-11-1l
+;;
+;; Keywords: files tools
+;; URL: https://github.com/fourier/ztree
+;; Compatibility: GNU Emacs GNU Emacs 24.x
+;;
+;; This file is part of GNU Emacs.
+;;
+;; GNU Emacs 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.
+;;
+;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+;;
+;;; Commentary:
+;;
+;; Add the following to your .emacs file:
+;;
+;; (push (substitute-in-file-name "path-to-ztree-directory") load-path)
+;; (require 'ztree-view)
+;;
+;; Call the ztree interactive function:
+;; Use the following function: ztree-view
+;;
+;;; Issues:
+;;
+;;; TODO:
+;;
+;;
+;;; Code:
+
+(require 'ztree-util)
+
+;;
+;; Globals
+;;
+
+(defvar ztree-expanded-nodes-list nil
+ "A list of Expanded nodes (i.e. directories) entries.")
+(make-variable-buffer-local 'ztree-expanded-nodes-list)
+
+(defvar ztree-start-node nil
+ "Start node(i.e. directory) for the window.")
+(make-variable-buffer-local 'ztree-start-node)
+
+(defvar ztree-line-to-node-table nil
+ "List of tuples with full node(i.e. file/directory name and the line.")
+(make-variable-buffer-local 'ztree-line-to-node-table)
+
+(defvar ztree-start-line nil
+ "Index of the start line - the root.")
+(make-variable-buffer-local 'ztree-start-line)
+
+(defvar ztree-parent-lines-array nil
+ "Array of parent lines.
+The ith value of the array is the parent line for line i.
+If ith value is i - it is the root line")
+(make-variable-buffer-local 'ztree-parent-lines-array)
+
+(defvar ztree-count-subsequent-bs nil
+ "Counter for the subsequest BS keys (to identify double BS).
+Used in order to not to use cl package and `lexical-let'")
+(make-variable-buffer-local 'ztree-count-subsequent-bs)
+
+(defvar ztree-line-tree-properties nil
+ "Hash with key - line number, value - property ('left, 'right, 'both).
+Used for 2-side trees, to determine if the node exists on left or right
+or both sides")
+(make-variable-buffer-local 'ztree-line-tree-properties)
+
+(defvar ztree-tree-header-fun nil
+ "Function inserting the header into the tree buffer.
+MUST inster newline at the end!")
+(make-variable-buffer-local 'ztree-tree-header-fun)
+
+(defvar ztree-node-short-name-fun nil
+ "Function which creates a pretty-printable short string from the node.")
+(make-variable-buffer-local 'ztree-node-short-name-fun)
+
+(defvar ztree-node-is-expandable-fun nil
+ "Function which determines if the node is expandable.
+For example if the node is a directory")
+(make-variable-buffer-local 'ztree-node-is-expandable-fun)
+
+(defvar ztree-node-equal-fun nil
+ "Function which determines if the 2 nodes are equal.")
+(make-variable-buffer-local 'ztree-node-equal-fun)
+
+(defvar ztree-node-contents-fun nil
+ "Function returning list of node contents.")
+(make-variable-buffer-local 'ztree-node-contents-fun)
+
+(defvar ztree-node-side-fun nil
+ "Function returning position of the node: 'left, 'right or 'both.
+If not defined(by default) - using single screen tree, otherwise
+the buffer is split to 2 trees")
+(make-variable-buffer-local 'ztree-node-side-fun)
+
+(defvar ztree-node-face-fun nil
+ "Function returning face for the node.")
+(make-variable-buffer-local 'ztree-node-face-fun)
+
+(defvar ztree-node-action-fun nil
+ "Function called when Enter/Space pressed on the node.")
+(make-variable-buffer-local 'ztree-node-action-fun)
+
+(defvar ztree-node-showp-fun nil
+ "Function called to decide if the node should be visible.")
+(make-variable-buffer-local 'ztree-node-showp-fun)
+
+
+;;
+;; Major mode definitions
+;;
+
+(defvar ztree-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "\r") 'ztree-perform-action)
+ (define-key map (kbd "SPC") 'ztree-perform-soft-action)
+ (define-key map [double-mouse-1] 'ztree-perform-action)
+ (define-key map (kbd "TAB") 'ztree-jump-side)
+ (define-key map (kbd "g") 'ztree-refresh-buffer)
+ (define-key map (kbd "x") 'ztree-toggle-expand-subtree)
+ (if window-system
+ (define-key map (kbd "<backspace>") 'ztree-move-up-in-tree)
+ (define-key map "\177" 'ztree-move-up-in-tree))
+ map)
+ "Keymap for `ztree-mode'.")
+
+
+(defface ztreep-node-face
+ '((((background dark)) (:foreground "#ffffff"))
+ (((type nil)) (:inherit 'font-lock-function-name-face))
+ (t (:foreground "Blue")))
+ "*Face used for expandable entries(directories etc) in Ztree buffer."
+ :group 'Ztree :group 'font-lock-highlighting-faces)
+(defvar ztreep-node-face 'ztreep-node-face)
+
+(defface ztreep-leaf-face
+ '((((background dark)) (:foreground "cyan1"))
+ (((type nil)) (:inherit 'font-lock-variable-name-face))
+ (t (:foreground "darkblue")))
+ "*Face used for not expandable nodes(leafs, i.e. files) in Ztree buffer."
+ :group 'Ztree :group 'font-lock-highlighting-faces)
+(defvar ztreep-leaf-face 'ztreep-leaf-face)
+
+(defface ztreep-arrow-face
+ '((((background dark)) (:foreground "#7f7f7f"))
+ (t (:foreground "#8d8d8d")))
+ "*Face used for arrows in Ztree buffer."
+ :group 'Ztree :group 'font-lock-highlighting-faces)
+(defvar ztreep-arrow-face 'ztreep-arrow-face)
+
+(defface ztreep-expand-sign-face
+ '((((background dark)) (:foreground "#7f7fff"))
+ (t (:foreground "#8d8d8d")))
+ "*Face used for expand sign [+] in Ztree buffer."
+ :group 'Ztree :group 'font-lock-highlighting-faces)
+(defvar ztreep-expand-sign-face 'ztreep-expand-sign-face)
+
+
+;;;###autoload
+(define-derived-mode ztree-mode special-mode "Ztree"
+ "A major mode for displaying the directory tree in text mode."
+ ;; only spaces
+ (setq indent-tabs-mode nil)
+ ;; fix for electric-indent-mode
+ ;; for emacs 24.4
+ (if (fboundp 'electric-indent-local-mode)
+ (electric-indent-local-mode -1)
+ ;; for emacs 24.3 or less
+ (add-hook 'electric-indent-functions
+ (lambda (arg) 'no-indent) nil 'local)))
+
+
+(defun ztree-find-node-in-line (line)
+ "Return the node for the LINE specified.
+Search through the array of node-line pairs."
+ (gethash line ztree-line-to-node-table))
+
+(defun ztree-find-node-at-point ()
+ "Find the node at point.
+Returns cons pair (node, side) for the current point
+or nil if there is no node"
+ (let ((center (/ (window-width) 2))
+ (node (ztree-find-node-in-line (line-number-at-pos))))
+ (when node
+ (cons node (if (> (current-column) center) 'right 'left)))))
+
+
+(defun ztree-is-expanded-node (node)
+ "Find if the NODE is in the list of expanded nodes."
+ (ztree-find ztree-expanded-nodes-list
+ #'(lambda (x) (funcall ztree-node-equal-fun x node))))
+
+
+(defun ztree-set-parent-for-line (line parent)
+ "For given LINE set the PARENT in the global array."
+ (aset ztree-parent-lines-array (- line ztree-start-line) parent))
+
+(defun ztree-get-parent-for-line (line)
+ "For given LINE return a parent."
+ (when (and (>= line ztree-start-line)
+ (< line (+ (length ztree-parent-lines-array) ztree-start-line)))
+ (aref ztree-parent-lines-array (- line ztree-start-line))))
+
+(defun scroll-to-line (line)
+ "Recommended way to set the cursor to specified LINE."
+ (goto-char (point-min))
+ (forward-line (1- line)))
+
+
+(defun ztree-do-toggle-expand-subtree-iter (node state)
+ "Iteration in expanding subtree.
+Argument NODE current node.
+Argument STATE node state."
+ (when (funcall ztree-node-is-expandable-fun node)
+ (let ((children (funcall ztree-node-contents-fun node)))
+ (ztree-do-toggle-expand-state node state)
+ (dolist (child children)
+ (ztree-do-toggle-expand-subtree-iter child state)))))
+
+
+(defun ztree-do-toggle-expand-subtree ()
+ "Implements the subtree expand."
+ (let* ((line (line-number-at-pos))
+ (node (ztree-find-node-in-line line))
+ ;; save the current window start position
+ (current-pos (window-start)))
+ ;; only for expandable nodes
+ (when (funcall ztree-node-is-expandable-fun node)
+ ;; get the current expand state and invert it
+ (let ((do-expand (not (ztree-is-expanded-node node))))
+ (ztree-do-toggle-expand-subtree-iter node do-expand))
+ ;; refresh buffer and scroll back to the saved line
+ (ztree-refresh-buffer line)
+ ;; restore window start position
+ (set-window-start (selected-window) current-pos))))
+
+
+(defun ztree-do-perform-action (hard)
+ "Toggle expand/collapsed state for nodes or perform an action.
+HARD specifies (t or nil) if the hard action, binded on RET,
+should be performed on node."
+ (let* ((line (line-number-at-pos))
+ (node (ztree-find-node-in-line line)))
+ (when node
+ (if (funcall ztree-node-is-expandable-fun node)
+ ;; only for expandable nodes
+ (ztree-toggle-expand-state node)
+ ;; perform action
+ (when ztree-node-action-fun
+ (funcall ztree-node-action-fun node hard)))
+ ;; save the current window start position
+ (let ((current-pos (window-start)))
+ ;; refresh buffer and scroll back to the saved line
+ (ztree-refresh-buffer line)
+ ;; restore window start position
+ (set-window-start (selected-window) current-pos)))))
+
+
+(defun ztree-perform-action ()
+ "Toggle expand/collapsed state for nodes or perform the action.
+Performs the hard action, binded on RET, on node."
+ (interactive)
+ (ztree-do-perform-action t))
+
+(defun ztree-perform-soft-action ()
+ "Toggle expand/collapsed state for nodes or perform the action.
+Performs the soft action, binded on Space, on node."
+ (interactive)
+ (ztree-do-perform-action nil))
+
+
+(defun ztree-toggle-expand-subtree()
+ "Toggle Expanded/Collapsed state on all nodes of the subtree"
+ (interactive)
+ (ztree-do-toggle-expand-subtree))
+
+(defun ztree-do-toggle-expand-state (node do-expand)
+ "Set the expanded state of the NODE to DO-EXPAND."
+ (if (not do-expand)
+ (setq ztree-expanded-nodes-list
+ (ztree-filter
+ #'(lambda (x) (not (funcall ztree-node-equal-fun node x)))
+ ztree-expanded-nodes-list))
+ (push node ztree-expanded-nodes-list)))
+
+
+(defun ztree-toggle-expand-state (node)
+ "Toggle expanded/collapsed state for NODE."
+ (ztree-do-toggle-expand-state node (not (ztree-is-expanded-node node))))
+
+
+(defun ztree-move-up-in-tree ()
+ "Action on Backspace key.
+Jump to the line of a parent node. If previous key was Backspace
+then close the node."
+ (interactive)
+ (when ztree-parent-lines-array
+ (let* ((line (line-number-at-pos (point)))
+ (parent (ztree-get-parent-for-line line)))
+ (when parent
+ (if (and (equal last-command 'ztree-move-up-in-tree)
+ (not ztree-count-subsequent-bs))
+ (let ((node (ztree-find-node-in-line line)))
+ (when (ztree-is-expanded-node node)
+ (ztree-toggle-expand-state node))
+ (setq ztree-count-subsequent-bs t)
+ (ztree-refresh-buffer line))
+ (progn (setq ztree-count-subsequent-bs nil)
+ (scroll-to-line parent)))))))
+
+
+(defun ztree-get-splitted-node-contens (node)
+ "Return pair of 2 elements: list of expandable nodes and list of leafs.
+Argument NODE node which contents will be returned."
+ (let ((nodes (funcall ztree-node-contents-fun node))
+ (comp #'(lambda (x y)
+ (string< (funcall ztree-node-short-name-fun x)
+ (funcall ztree-node-short-name-fun y)))))
+ (cons (sort (ztree-filter
+ #'(lambda (f) (funcall ztree-node-is-expandable-fun f))
+ nodes) comp)
+ (sort (ztree-filter
+ #'(lambda (f) (not (funcall ztree-node-is-expandable-fun f)))
+ nodes) comp))))
+
+
+(defun ztree-draw-char (c x y &optional face)
+ "Draw char C at the position (1-based) (X Y).
+Optional argument FACE face to use to draw a character."
+ (save-excursion
+ (scroll-to-line y)
+ (beginning-of-line)
+ (goto-char (+ x (-(point) 1)))
+ (delete-char 1)
+ (insert-char c 1)
+ (put-text-property (1- (point)) (point) 'face (if face face
'ztreep-arrow-face))))
+
+(defun ztree-draw-vertical-line (y1 y2 x &optional face)
+ "Draw a vertical line of '|' characters from Y1 row to Y2 in X column.
+Optional argument FACE face to draw line with."
+ (let ((count (abs (- y1 y2))))
+ (if (> y1 y2)
+ (progn
+ (dotimes (y count)
+ (ztree-draw-char ?\| x (+ y2 y) face))
+ (ztree-draw-char ?\| x (+ y2 count) face))
+ (progn
+ (dotimes (y count)
+ (ztree-draw-char ?\| x (+ y1 y) face))
+ (ztree-draw-char ?\| x (+ y1 count) face)))))
+
+(defun ztree-draw-vertical-rounded-line (y1 y2 x &optional face)
+ "Draw a vertical line of '|' characters finishing with '`' character.
+Draws the line from Y1 row to Y2 in X column.
+Optional argument FACE facet to draw the line with."
+ (let ((count (abs (- y1 y2))))
+ (if (> y1 y2)
+ (progn
+ (dotimes (y count)
+ (ztree-draw-char ?\| x (+ y2 y) face))
+ (ztree-draw-char ?\` x (+ y2 count) face))
+ (progn
+ (dotimes (y count)
+ (ztree-draw-char ?\| x (+ y1 y) face))
+ (ztree-draw-char ?\` x (+ y1 count) face)))))
+
+
+(defun ztree-draw-horizontal-line (x1 x2 y)
+ "Draw the horizontal line from column X1 to X2 in the row Y."
+ (if (> x1 x2)
+ (dotimes (x (1+ (- x1 x2)))
+ (ztree-draw-char ?\- (+ x2 x) y))
+ (dotimes (x (1+ (- x2 x1)))
+ (ztree-draw-char ?\- (+ x1 x) y))))
+
+
+(defun ztree-draw-tree (tree depth start-offset)
+ "Draw the TREE of lines with parents.
+Argument DEPTH current depth.
+Argument START-OFFSET column to start drawing from."
+ (if (atom tree)
+ nil
+ (let* ((root (car tree))
+ (children (cdr tree))
+ (offset (+ start-offset (* depth 4)))
+ (line-start (+ 3 offset))
+ (line-end-leaf (+ 7 offset))
+ (line-end-node (+ 4 offset))
+ ;; determine if the line is visible. It is always the case
+ ;; for 1-sided trees; however for 2 sided trees
+ ;; it depends on which side is the actual element
+ ;; and which tree (left with offset 0 or right with offset > 0
+ ;; we are drawing
+ (visible #'(lambda (line) ()
+ (if (not ztree-node-side-fun) t
+ (let ((side
+ (gethash line ztree-line-tree-properties)))
+ (cond ((eq side 'left) (= start-offset 0))
+ ((eq side 'right) (> start-offset 0))
+ (t t)))))))
+ (when children
+ ;; draw the line to the last child
+ ;; since we push'd children to the list, it's the first visible line
+ ;; from the children list
+ (let ((last-child (ztree-find children
+ #'(lambda (x)
+ (funcall visible (car-atom x)))))
+ (x-offset (+ 2 offset)))
+ (when last-child
+ (ztree-draw-vertical-rounded-line (1+ root)
+ (car-atom last-child)
+ x-offset)))
+ ;; draw recursively
+ (dolist (child children)
+ (ztree-draw-tree child (1+ depth) start-offset)
+ (let ((end (if (listp child) line-end-node line-end-leaf)))
+ (when (funcall visible (car-atom child))
+ (ztree-draw-horizontal-line line-start
+ end
+ (car-atom child)))))))))
+
+(defun ztree-fill-parent-array (tree)
+ "Set the root lines array.
+Argument TREE nodes tree to create an array of lines from."
+ (let ((root (car tree))
+ (children (cdr tree)))
+ (dolist (child children)
+ (ztree-set-parent-for-line (car-atom child) root)
+ (when (listp child)
+ (ztree-fill-parent-array child)))))
+
+
+(defun ztree-insert-node-contents (path)
+ "Insert node contents with initial depth 0.
+`ztree-insert-node-contents-1' return the tree of line
+numbers to determine who is parent line of the
+particular line. This tree is used to draw the
+graph.
+Argument PATH start node."
+ (let ((tree (ztree-insert-node-contents-1 path 0))
+ ;; number of 'rows' in tree is last line minus start line
+ (num-of-items (- (line-number-at-pos (point)) ztree-start-line)))
+ ;; create a parents array to store parents of lines
+ ;; parents array used for navigation with the BS
+ (setq ztree-parent-lines-array (make-vector num-of-items 0))
+ ;; set the root node in lines parents array
+ (ztree-set-parent-for-line ztree-start-line ztree-start-line)
+ ;; fill the parent arrray from the tree
+ (ztree-fill-parent-array tree)
+ ;; draw the tree starting with depth 0 and offset 0
+ (ztree-draw-tree tree 0 0)
+ ;; for the 2-sided tree we need to draw the vertical line
+ ;; and an additional tree
+ (if ztree-node-side-fun ; 2-sided tree
+ (let ((width (window-width)))
+ ;; draw the vertical line in the middle of the window
+ (ztree-draw-vertical-line ztree-start-line
+ (1- (+ num-of-items ztree-start-line))
+ (/ width 2)
+ 'vertical-border)
+ (ztree-draw-tree tree 0 (1+ (/ width 2)))))))
+
+
+(defun ztree-insert-node-contents-1 (node depth)
+ "Recursively insert contents of the NODE with current DEPTH."
+ (let* ((expanded (ztree-is-expanded-node node))
+ ;; insert node entry with defined depth
+ (root-line (ztree-insert-entry node depth expanded))
+ ;; children list is the list of lines which are children
+ ;; of the root line
+ (children nil))
+ (when expanded ;; if expanded we need to add all subnodes
+ (let* ((contents (ztree-get-splitted-node-contens node))
+ ;; contents is the list of 2 elements:
+ (nodes (car contents)) ; expandable entries - nodes
+ (leafs (cdr contents))) ; leafs - which doesn't have subleafs
+ ;; iterate through all expandable entries to insert them first
+ (dolist (node nodes)
+ ;; if it is not in the filter list
+ (when (funcall ztree-node-showp-fun node)
+ ;; insert node on the next depth level
+ ;; and push the returning result (in form (root children))
+ ;; to the children list
+ (push (ztree-insert-node-contents-1 node (1+ depth))
+ children)))
+ ;; now iterate through all the leafs
+ (dolist (leaf leafs)
+ ;; if not in filter list
+ (when (funcall ztree-node-showp-fun leaf)
+ ;; insert the leaf and add it to children
+ (push (ztree-insert-entry leaf (1+ depth) nil)
+ children)))))
+ ;; result value is the list - head is the root line,
+ ;; rest are children
+ (cons root-line children)))
+
+(defun ztree-insert-entry (node depth expanded)
+ "Inselt the NODE to the current line with specified DEPTH and EXPANDED
state."
+ (let ((line (line-number-at-pos))
+ (expandable (funcall ztree-node-is-expandable-fun node))
+ (short-name (funcall ztree-node-short-name-fun node)))
+ (if ztree-node-side-fun ; 2-sided tree
+ (let ((right-short-name (funcall ztree-node-short-name-fun node t))
+ (side (funcall ztree-node-side-fun node))
+ (width (window-width)))
+ (when (eq side 'left) (setq right-short-name ""))
+ (when (eq side 'right) (setq short-name ""))
+ (ztree-insert-single-entry short-name depth
+ expandable expanded 0
+ (when ztree-node-face-fun
+ (funcall ztree-node-face-fun node)))
+ (ztree-insert-single-entry right-short-name depth
+ expandable expanded (1+ (/ width 2))
+ (when ztree-node-face-fun
+ (funcall ztree-node-face-fun node)))
+ (puthash line side ztree-line-tree-properties))
+ (ztree-insert-single-entry short-name depth expandable expanded 0))
+ (puthash line node ztree-line-to-node-table)
+ (newline-and-begin)
+ line))
+
+(defun ztree-insert-single-entry (short-name depth
+ expandable expanded
+ offset
+ &optional face)
+ "Writes a SHORT-NAME in a proper position with the type given.
+Writes a string with given DEPTH, prefixed with [ ] if EXPANDABLE
+and [-] or [+] depending on if it is EXPANDED from the specified OFFSET.
+Optional argument FACE face to write text with."
+ (let ((node-sign #'(lambda (exp)
+ (insert "[" (if exp "-" "+") "]")
+ (set-text-properties (- (point) 3)
+ (point)
+ '(face ztreep-expand-sign-face)))))
+ (move-to-column offset t)
+ (delete-region (point) (line-end-position))
+ (when (> depth 0)
+ (dotimes (i depth)
+ (insert " ")
+ (insert-char ?\s 3))) ; insert 3 spaces
+ (when (> (length short-name) 0)
+ (if expandable
+ (progn
+ (funcall node-sign expanded) ; for expandable nodes insert
"[+/-]"
+ (insert " ")
+ (put-text-property 0 (length short-name)
+ 'face (if face face 'ztreep-node-face)
short-name)
+ (insert short-name))
+ (progn
+ (insert " ")
+ (put-text-property 0 (length short-name)
+ 'face (if face face 'ztreep-leaf-face) short-name)
+ (insert short-name))))))
+
+(defun ztree-jump-side ()
+ "Jump to another side for 2-sided trees."
+ (interactive)
+ (when ztree-node-side-fun ; 2-sided tree
+ (let ((center (/ (window-width) 2)))
+ (cond ((< (current-column) center)
+ (move-to-column (1+ center)))
+ ((> (current-column) center)
+ (move-to-column 1))
+ (t nil)))))
+
+
+
+(defun ztree-refresh-buffer (&optional line)
+ "Refresh the buffer.
+Optional argument LINE scroll to the line given."
+ (interactive)
+ (when (and (equal major-mode 'ztree-mode)
+ (boundp 'ztree-start-node))
+ (setq ztree-line-to-node-table (make-hash-table))
+ ;; create a hash table of node properties for line
+ ;; used in 2-side tree mode
+ (when ztree-node-side-fun
+ (setq ztree-line-tree-properties (make-hash-table)))
+ (toggle-read-only)
+ (erase-buffer)
+ (funcall ztree-tree-header-fun)
+ (setq ztree-start-line (line-number-at-pos (point)))
+ (ztree-insert-node-contents ztree-start-node)
+ (scroll-to-line (if line line ztree-start-line))
+ (toggle-read-only)))
+
+
+(defun ztree-view (
+ buffer-name
+ start-node
+ filter-fun
+ header-fun
+ short-name-fun
+ expandable-p
+ equal-fun
+ children-fun
+ face-fun
+ action-fun
+ &optional node-side-fun
+ )
+ "Create a ztree view buffer configured with parameters given.
+Argument BUFFER-NAME Name of the buffer created.
+Argument START-NODE Starting node - the root of the tree.
+Argument FILTER-FUN Function which will define if the node should not be
+visible.
+Argument HEADER-FUN Function which inserts the header into the buffer
+before drawing the tree.
+Argument SHORT-NAME-FUN Function which return the short name for a node given.
+Argument EXPANDABLE-P Function to determine if the node is expandable.
+Argument EQUAL-FUN An equality function for nodes.
+Argument CHILDREN-FUN Function to get children from the node.
+Argument FACE-FUN Function to determine face of the node.
+Argument ACTION-FUN an action to perform when the Return is pressed.
+Optional argument NODE-SIDE-FUN Determines the side of the node."
+ (let ((buf (get-buffer-create buffer-name)))
+ (switch-to-buffer buf)
+ (ztree-mode)
+ ;; configure ztree-view
+ (setq ztree-start-node start-node)
+ (setq ztree-expanded-nodes-list (list ztree-start-node))
+ (setq ztree-node-showp-fun filter-fun)
+ (setq ztree-tree-header-fun header-fun)
+ (setq ztree-node-short-name-fun short-name-fun)
+ (setq ztree-node-is-expandable-fun expandable-p)
+ (setq ztree-node-equal-fun equal-fun)
+ (setq ztree-node-contents-fun children-fun)
+ (setq ztree-node-face-fun face-fun)
+ (setq ztree-node-action-fun action-fun)
+ (setq ztree-node-side-fun node-side-fun)
+ (ztree-refresh-buffer)))
+
+
+(provide 'ztree-view)
+;;; ztree-view.el ends here
diff --git a/packages/ztree/ztree.el b/packages/ztree/ztree.el
new file mode 100644
index 0000000..3958daa
--- /dev/null
+++ b/packages/ztree/ztree.el
@@ -0,0 +1,39 @@
+;;; ztree.el --- Text mode directory tree
+
+;; Copyright (C) 2013-2015 Free Software Foundation, Inc.
+;;
+;; Author: Alexey Veretennikov <alexey dot veretennikov at gmail dot com>
+;;
+;; Created: 2013-11-1l
+;;
+;; Version: 1.0.1
+;;
+;; Keywords: files tools
+;; URL: https://github.com/fourier/ztree
+;; Compatibility: GNU Emacs GNU Emacs 24.x
+;;
+;; This file is part of GNU Emacs.
+;;
+;; GNU Emacs 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.
+;;
+;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+;;
+;;; Commentary:
+;;
+;;
+;;; Code:
+
+(require 'ztree-dir)
+(require 'ztree-diff)
+
+(provide 'ztree)
+;;; ztree.el ends here
- [elpa] master updated (1dbb290 -> 30984c4), Artur Malabarba, 2015/06/11
- [elpa] master 2d6f130 1/8: New abbrevs, Artur Malabarba, 2015/06/11
- [elpa] master 9d89c3b 3/8: Add URL header, Artur Malabarba, 2015/06/11
- [elpa] master 231d2c3 2/8: Better abbrevs, Artur Malabarba, 2015/06/11
- [elpa] master a19f12e 4/8: Merge pull request #2 from xuchunyang/patch-1, Artur Malabarba, 2015/06/11
- [elpa] master a65c820 5/8: Version 1.0, Artur Malabarba, 2015/06/11
- [elpa] master b9e7d42 7/8: Merge commit '1b1896fcd7885280f390b37bf3572b74fbfcc3cf', Artur Malabarba, 2015/06/11
- [elpa] master 1b1896f 6/8: EOL, Artur Malabarba, 2015/06/11
- [elpa] master 30984c4 8/8: Merge branch 'master' of git+ssh://git.sv.gnu.org/srv/git/emacs/elpa,
Artur Malabarba <=