- ztree
- =====
+ # ztree
+ Ztree is a project dedicated to implementation of several text-tree applications inside [GNU Emacs](http://www.gnu.org/software/emacs/). It consists of 2 subprojects: **ztree-diff** and **ztree-dir** (the basis of **ztree-diff**). Available in [GNU ELPA](https://elpa.gnu.org/) and [MELPA](http://melpa.org/#/).
- 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**.
+ ## Installation
- 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.
+ ### Using ELPA
+ Press `M-x` in GNU Emacs and write `list-packages`. Find the `ztree` in the list of packages and press `i` to select this package, `x` to install the package.
- 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.
+ ### Using MELPA
+ Add to your `.emacs` or `.emacs.d/init.el` following lines:
- If one wants to have a stand-alone application, consider the (WIP)[zdircmp](https://github.com/fourier/zdircmp) project based on **ztree-diff**.
+ ```scheme
+ (setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/")
+ ("melpa" . "http://melpa.milkbox.net/packages/")))
+ ```
+
+ Follow the installation instructions for the GNU ELPA above.
+ ### Manual
Add the following to your .emacs file:
```scheme
(push (substitute-in-file-name "path-to-ztree-directory") load-path)
- (require 'ztree-diff)
+ (require 'ztree)
```
+ ## 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**.
+
Call the `ztree-diff` interactive function:
```
```
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:
+ ### Hotkeys supported
+ * 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.
* `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
+ * `H` key to toggle show/hide hidden/ignored 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:
+ ### Customizations
+ By default all files starting with dot (like `.gitignore`) are not shown and excluded from the difference status for directories. One can add an additional regexps to the list `ztree-diff-filter-list`.
+
+ ### 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
- ---------
**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
```
+ ### Hotkeys supported
* 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.
+ * To visit a file, press `Space` key.
+ * To open file in other window, use `RET` key.
+
+ ### Customizations
+ Set the `ztree-dir-move-focus` variable to `t` in order to move focus to the other window when the `RET` key is pressed; the default behavior is to keep focus in `ztree-dir` window.
![ztree emacsapp](https://github.com/fourier/ztree/raw/screenshots/screenshots/emacs_app.png "Emacs App with ztree-dir")
"Message showing while constructing the diff tree.")
(make-variable-buffer-local 'ztree-diff-model-wait-message)
+ (defvar ztree-diff-model-ignore-fun nil
+ "Function which determines if the node should be excluded from comparison.")
+ (make-variable-buffer-local 'ztree-diff-model-ignore-fun)
(defun ztree-diff-model-update-wait-message ()
"Update the wait mesage with one more '.' progress indication."
(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 fields and getters/setters
;; here:
;; parent - parent node
;; different = {nil, 'new, 'diff} - means comparison status
(ztree-defrecord ztree-diff-node (parent left-path right-path short-name right-short-name children different))
+ (defun ztree-diff-model-ignore-p (node)
+ "Determine if the NODE should be excluded from comparison results."
+ (when ztree-diff-model-ignore-fun
+ (funcall ztree-diff-model-ignore-fun node)))
+
(defun ztree-diff-node-to-string (node)
"Construct the string with contents of the NODE given."
(let* ((string-or-nil #'(lambda (x) (if x
(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))))
+ (unless (ztree-diff-model-ignore-p child)
+ (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)
(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)
+ ;; 2.3 update difference status for the whole comparison
+ ;; depending if the node should participate in overall result
+ (unless (ztree-diff-model-ignore-p node)
+ (setq different-dir (ztree-diff-model-update-diff different-dir different)))
;; push the created node to the result list
(push node result)))
;; second - adding entries from the right directory which are not present
simple-name)
(eq isdir (file-directory-p x)))))))
;; if it is not in the first directory, add it as a node
- (when (not file1)
+ (unless 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)
+ ;; update the different status for the whole comparison
+ ;; depending if the node should participate in overall result
+ (unless (ztree-diff-model-ignore-p node)
+ (setq different-dir (ztree-diff-model-update-diff different-dir 'new)))
;; 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))
+ (defun ztree-diff-model-create (dir1 dir2 &optional ignore-p)
+ "Create a node based on DIR1 and DIR2.
+ IGNORE-P is the optional filtering function, taking node as
+ an argument, which determines if the node should be excluded
+ from comparison."
+ (unless (file-directory-p dir1)
(error "Path %s is not a directory" dir1))
- (when (not (file-directory-p dir2))
+ (unless (file-directory-p dir2)
(error "Path %s is not a directory" dir2))
+ (setf ztree-diff-model-ignore-fun ignore-p)
(setq ztree-diff-model-wait-message (concat "Comparing " dir1 " and " dir2 " ..."))
(let* ((model
(ztree-diff-node-create nil dir1 dir2
"Show or not equal files/directories on both sides.")
(make-variable-buffer-local 'ztree-diff-show-equal-files)
+ (defvar ztree-diff-show-filtered-files nil
+ "Show or not files from the filtered list.")
+
;;;###autoload
(define-minor-mode ztreediff-mode
"A minor mode for displaying the difference of the directory trees in text mode."
`(
(,(kbd "C") . ztree-diff-copy)
(,(kbd "h") . ztree-diff-toggle-show-equal-files)
+ (,(kbd "H") . ztree-diff-toggle-show-filtered-files)
(,(kbd "D") . ztree-diff-delete-file)
(,(kbd "v") . ztree-diff-view-file)
(,(kbd "d") . ztree-diff-simple-diff-files)
remove-path))
(let* ((delete-command
(if (file-directory-p remove-path)
- '(delete-directory remove-path t)
- '(delete-file remove-path t)))
+ #'delete-directory
+ #'delete-file))
(children (ztree-diff-node-children parent))
(err
(condition-case error-trap
(progn
- (eval delete-command)
+ (funcall delete-command remove-path t)
nil)
(error error-trap))))
- (if err (message (concat "Error: " (nth 2 err)))
+ (if err
+ (progn
+ (message (concat "Error: " (nth 2 err)))
+ ;; when error happened while deleting the
+ ;; directory, rescan the node
+ ;; and update the parents with a new status
+ ;; of this node
+ (when (file-directory-p remove-path)
+ (ztree-diff-model-partial-rescan node)
+ (ztree-diff-node-update-all-parents-diff node)))
+ ;; if everything ok
(progn
+ ;; remove the node from children
(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-diff-model-partial-rescan node)
(ztree-refresh-buffer (line-number-at-pos))))))))))
- (defun ztree-node-is-in-filter-list (node)
+ (defun ztree-diff-node-ignore-p (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))))
+ If the node is in the filter list it shall not be visible,
+ unless it is a parent node."
+ (let ((name (ztree-diff-node-short-name node)))
+ ;; ignore then
+ ;; not a root and is in filter list
+ (and (ztree-diff-node-parent node)
+ (ztree-find ztree-diff-filter-list #'(lambda (rx) (string-match rx name))))))
(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))))
+ ;; visible then
+ ;; 1) either it is a parent
+ (or (not (ztree-diff-node-parent node)) ; parent is always visible
+ (and
+ ;; 2.1) or it is not in ignore list and
+ (or ztree-diff-show-filtered-files ; show filtered files regardless
+ (not (ztree-diff-node-ignore-p node)))
+ ;; 2.2) it has different status
+ (or ztree-diff-show-equal-files ; show equal files regardless
+ (ztree-diff-node-different node)))))
(defun ztree-diff-toggle-show-equal-files ()
"Toggle visibility of the equal files."
(setq ztree-diff-show-equal-files (not ztree-diff-show-equal-files))
(ztree-refresh-buffer))
+ (defun ztree-diff-toggle-show-filtered-files ()
+ "Toggle visibility of the filtered files."
+ (interactive)
+ (setq ztree-diff-show-filtered-files (not ztree-diff-show-filtered-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))
+ (let* ((difference (ztree-diff-model-create dir1 dir2 #'ztree-diff-node-ignore-p))
(buf-name (concat "*"
(ztree-diff-node-short-name difference)
" <--> "
;;
;; Created: 2013-11-1l
;;
- ;; Version: 1.0.1
+ ;; Version: 1.0.2
;;
;; Keywords: files tools
;; URL: https://github.com/fourier/ztree