about summary refs log tree commit diff
path: root/configs/shared/emacs/.emacs.d/elpa/tablist-20170219.1935/tablist.el
diff options
context:
space:
mode:
authorWilliam Carroll <wpcarro@gmail.com>2018-09-10T18·51-0400
committerWilliam Carroll <wpcarro@gmail.com>2018-09-10T18·53-0400
commit17ee0e400bef47c371afcae76037f9ea6a44ad13 (patch)
tree0e5efee6f00e402890e91f3eceb4b29408a498b6 /configs/shared/emacs/.emacs.d/elpa/tablist-20170219.1935/tablist.el
parent8b2fadf4776b7ddb4a67b4bc8ff6463770e56028 (diff)
Support Vim, Tmux, Emacs with Stow
After moving off of Meta, Dotfiles has a greater responsibility to
manage configs. Vim, Tmux, and Emacs are now within Stow's purview.
Diffstat (limited to 'configs/shared/emacs/.emacs.d/elpa/tablist-20170219.1935/tablist.el')
-rw-r--r--configs/shared/emacs/.emacs.d/elpa/tablist-20170219.1935/tablist.el1941
1 files changed, 1941 insertions, 0 deletions
diff --git a/configs/shared/emacs/.emacs.d/elpa/tablist-20170219.1935/tablist.el b/configs/shared/emacs/.emacs.d/elpa/tablist-20170219.1935/tablist.el
new file mode 100644
index 000000000000..1fbfb313528e
--- /dev/null
+++ b/configs/shared/emacs/.emacs.d/elpa/tablist-20170219.1935/tablist.el
@@ -0,0 +1,1941 @@
+;;; tablist.el --- Extended tabulated-list-mode -*- lexical-binding: t -*-
+
+;; Copyright (C) 2013, 2014  Andreas Politz
+
+;; Author: Andreas Politz <politza@fh-trier.de>
+;; Keywords: extensions, lisp
+;; Package: tablist
+;; Version: 0.70
+;; Package-Requires: ((emacs "24.3"))
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; This package adds marks and filters to tabulated-list-mode.  It
+;; also kind of puts a dired face on tabulated list buffers.
+;;
+;; It can be used by deriving from tablist-mode and some features by
+;; using tablist-minor-mode inside a tabulated-list-mode buffer.
+;;
+
+;;; Code:
+
+(require 'cl-lib)
+(require 'ring)
+(require 'tabulated-list)
+(require 'dired)
+(require 'tablist-filter)
+
+;;
+;; *Macros
+;;
+
+(defmacro tablist-save-marks (&rest body)
+  "Eval body, while preserving all marks."
+  (let ((marks (make-symbol "marks")))
+    `(let (,marks)
+       (save-excursion
+         (goto-char (point-min))
+         (let ((re "^\\([^ ]\\)"))
+           (while (re-search-forward re nil t)
+             (push (cons (tabulated-list-get-id)
+                         (tablist-get-mark-state))
+                   ,marks))))
+       (unwind-protect
+           (progn ,@body)
+         (save-excursion
+           (dolist (m ,marks)
+             (let ((id (pop m)))
+               (goto-char (point-min))
+               (while (and id (not (eobp)))
+                 (when (equal id (tabulated-list-get-id))
+                   (tablist-put-mark-state m)
+                   (setq id nil))
+                 (forward-line)))))))))
+
+(defmacro tablist-with-remembering-entry (&rest body)
+  "Remember where body left of and restore previous position.
+
+If the current entry is still visible, move to it. Otherwise move
+to the next visible one after it.  If that also fails, goto to
+the beginning of the buffer.  Finally move point to the major
+column."
+  (declare (indent 0) (debug t))
+  (let ((re (make-symbol "re"))
+        (id (make-symbol "id"))
+        (col (make-symbol "col")))
+    `(let ((,re
+            (replace-regexp-in-string
+             "[\t ]+" "[\t ]*" (regexp-quote
+                                (or (thing-at-point 'line) ""))
+             t t))
+           (,id (tabulated-list-get-id))
+           (,col (tablist-current-column)))
+       (progn
+         ,@body
+         (let (success pos)
+           (goto-char (point-min))
+           (setq pos (point))
+           (while (and (setq success (re-search-forward ,re nil t))
+                       (> (point) (prog1 pos (setq pos (point))))
+                       (forward-line -1)
+                       (not (equal ,id (tabulated-list-get-id))))
+             (forward-line))
+           (unless success
+             (goto-char (point-min))
+             (while (and (not (eobp))
+                         (not success))
+               (if (equal (tabulated-list-get-id) ,id)
+                   (setq success t)
+                 (forward-line))))
+           (unless (and success (not (invisible-p (point))))
+             (goto-char (point-min)))
+           (tablist-skip-invisible-entries)
+           (tablist-move-to-column
+            (or ,col (car (tablist-major-columns))))
+           (dolist (win (get-buffer-window-list))
+             (set-window-point win (point))))))))
+
+(defmacro tablist-with-filter-displayed (&rest body)
+  "Display the current filter in the mode while evalling BODY."
+  (let ((state (make-symbol "state")))
+    `(let ((,state (tablist-display-filter 'state)))
+       (tablist-display-filter t)
+       (unwind-protect
+           (progn ,@body)
+         (tablist-display-filter ,state)))))
+
+;;
+;; *Mode Maps
+;;
+
+(defvar tablist-mode-filter-map
+  (let ((kmap (make-sparse-keymap)))
+    (define-key kmap "p" 'tablist-pop-filter)
+    (define-key kmap "r" 'tablist-push-regexp-filter)
+    (define-key kmap "=" 'tablist-push-equal-filter)
+    (define-key kmap "n" 'tablist-push-numeric-filter)
+    (define-key kmap "!" 'tablist-negate-filter)
+    (define-key kmap "t" 'tablist-toggle-first-filter-logic)
+    (define-key kmap "/" 'tablist-display-filter)
+    (define-key kmap "z" 'tablist-suspend-filter)
+
+    (define-key kmap "a" 'tablist-push-named-filter)
+    (define-key kmap "s" 'tablist-name-current-filter)
+    (define-key kmap "D" 'tablist-delete-named-filter)
+    (define-key kmap "d" 'tablist-deconstruct-named-filter)
+    (define-key kmap "e" 'tablist-edit-filter)
+    (define-key kmap "C" 'tablist-clear-filter)
+    kmap))
+
+(defvar tablist-mode-mark-map
+  (let ((kmap (make-sparse-keymap)))
+    (define-key kmap "c" 'tablist-change-marks)
+    (define-key kmap "!" 'tablist-unmark-all-marks)
+    (define-key kmap "r" 'tablist-mark-items-regexp)
+    (define-key kmap "n" 'tablist-mark-items-numeric)
+    (define-key kmap "m" 'tablist-mark-forward)
+    kmap))
+
+(defvar tablist-mode-regexp-map
+  (let ((kmap (make-sparse-keymap)))
+    ;; (define-key kmap "&" 'tablist-flag-gargabe-items)
+    (define-key kmap "m" 'tablist-mark-items-regexp)
+    kmap))
+
+(defvar tablist-minor-mode-map
+  (let ((kmap (make-sparse-keymap)))
+    (define-key kmap "m" 'tablist-mark-forward)
+    (define-key kmap (kbd "DEL") 'tablist-unmark-backward)
+    (define-key kmap "k" 'tablist-do-kill-lines)
+    (define-key kmap "U" 'tablist-unmark-all-marks)
+    (define-key kmap "u" 'tablist-unmark-forward)
+    (define-key kmap "t" 'tablist-toggle-marks)
+
+    (define-key kmap (kbd "TAB") 'tablist-forward-column)
+    (define-key kmap "\t" 'tablist-forward-column)
+    (define-key kmap [backtab] 'tablist-backward-column)
+
+    (define-key kmap "%" tablist-mode-regexp-map)
+    (define-key kmap "*" tablist-mode-mark-map)
+    (define-key kmap "/" tablist-mode-filter-map)
+
+    ;; (define-key kmap "e" 'tablist-edit-column)
+    ;; (define-key kmap "i" 'tablist-insert-entry)
+    (define-key kmap "s" 'tablist-sort)
+    (define-key kmap [remap back-to-indentation] 'tablist-move-to-major-column)
+    (define-key kmap [remap next-line] 'tablist-next-line)
+    (define-key kmap [remap previous-line] 'tablist-previous-line)
+    (define-key kmap "<" 'tablist-shrink-column)
+    (define-key kmap ">" 'tablist-enlarge-column)
+    (define-key kmap "q" 'tablist-quit)
+    (define-key kmap "G" 'tablist-revert)
+    (define-key kmap (kbd "C-c C-e") 'tablist-export-csv)
+    kmap))
+
+(defvar tablist-mode-map
+  (let ((kmap (copy-keymap tablist-minor-mode-map)))
+    (set-keymap-parent kmap tabulated-list-mode-map)
+    (define-key kmap "d" 'tablist-flag-forward)
+    (define-key kmap (kbd "RET") 'tablist-find-entry)
+    (define-key kmap "f" 'tablist-find-entry)
+    ;; (define-key kmap "~" 'tablist-flag-gargabe-items)
+    (define-key kmap "D" 'tablist-do-delete)
+    (define-key kmap "C" 'tablist-do-copy)
+    (define-key kmap "R" 'tablist-do-rename)
+    (define-key kmap "x" 'tablist-do-flagged-delete)
+    ;; (define-key kmap "F" 'tablist-find-marked-items)
+    ;; (define-key kmap (kbd "C-o") 'tablist-display-item)
+    kmap))
+
+;;
+;; *Variables
+;;
+
+;; Marking
+(defvar tablist-umark-filtered-entries t)
+(defvar tablist-marker-char dired-marker-char
+  "The character used for marking.")
+(defvar tablist-marker-face 'dired-mark
+  "The face used for the mark character.")
+(defvar tablist-marked-face  'dired-marked
+  "The face used for marked major columns.")
+
+;; Operations
+(defvar-local tablist-operations-function nil
+  "A function for handling operations on the entries.
+
+The function is called with varying number of arguments, while
+the first one is always a symbol describing one of the following
+operations.
+
+`supported-operations'
+
+This is the only mandatory operation. There are no other
+arguments and the function should return a list of symbols of
+supported operations.
+
+`delete'
+
+The 2nd argument will be a list of entry ID's.  The function
+should somehow delete these entries and update
+`tabulated-list-entries'.
+
+`find-entry'
+
+The 2nd argument is the ID of an entry.  The function should
+somehow find/display this entry, i.e. a kind of default
+operation.
+
+`edit-column'
+
+The function is called with 3 further arguments: ID, INDEX and
+NEW-COLUMN, where ID represents the entry to edit, INDEX is the index
+of the column and NEW-COLUMN is the proposed new value for this
+column.  It should either
+
+i.  return a new edited complete entry and update
+`tabulated-list-entries', or
+
+ii. throw an error, if NEW-COLUMN is not a valid value for this
+column.
+
+`complete'
+
+The function is called with 4 further arguments: ID, INDEX,
+STRING and POS, where ID represents an entry, INDEX is the index
+of the column to complete, STRING it's current value and POS an
+offset of the current position of point into STRING.
+
+The function should return a collection for this column, suitable
+as argument for the function `completion-in-region'.")
+
+;; Differentiating columns
+(defvar-local tablist-major-columns nil
+  "Columns used to mark and when querying.")
+
+;; Filter
+(defvar-local tablist-current-filter nil)
+(defvar-local tablist-filter-suspended nil)
+(defvar tablist-named-filter nil)
+
+;; History variables
+(defvar tablist-column-name-history nil)
+
+;; Hooks
+(defvar tablist-selection-changed-functions nil
+  "A hook run when ever point moves to a different entry.")
+
+;; Context Window
+(defvar-local tablist-context-window nil)
+(defvar-local tablist-context-window-function nil)
+(defvar tablist-context-window-display-action
+  `((display-buffer-reuse-window
+     tablist-display-buffer-split-below-and-attach)
+    (window-height . 0.25)
+    (inhibit-same-window . t)))
+
+;;
+;; *Setup
+;;
+
+(defvar savehist-additional-variables)
+(add-hook 'savehist-save-hook
+          (lambda nil
+            (add-to-list 'savehist-additional-variables 'tablist-named-filter)))
+
+;;;###autoload
+(define-minor-mode tablist-minor-mode
+  nil nil nil nil
+  (unless (derived-mode-p 'tabulated-list-mode)
+    (error "Buffer is not in Tabulated List Mode"))
+  (tablist-init (not tablist-minor-mode)))
+
+;;;###autoload
+(define-derived-mode tablist-mode tabulated-list-mode "TL"
+  (tablist-init))
+
+(defun tablist-init (&optional disable)
+  (let ((cleaned-misc (cl-remove 'tablist-current-filter
+                                 mode-line-misc-info :key 'car)))
+    (cond
+     ((not disable)
+      (set (make-local-variable 'mode-line-misc-info)
+           (append
+            (list
+             (list 'tablist-current-filter
+                   '(:eval (format " [%s]"
+                                   (if tablist-filter-suspended
+                                       "suspended"
+                                     "filtered")))))))
+      (add-hook 'post-command-hook
+                'tablist-selection-changed-handler nil t)
+      (add-hook 'tablist-selection-changed-functions
+                'tablist-context-window-update nil t))
+     (t
+      (setq mode-line-misc-info cleaned-misc)
+      (remove-hook 'post-command-hook
+                   'tablist-selection-changed-handler t)
+      (remove-hook 'tablist-selection-changed-functions
+                   'tablist-context-window-update t)))))
+
+(defun tablist-quit ()
+  (interactive)
+  (tablist-hide-context-window)
+  (quit-window))
+
+(defvar-local tablist-selected-id nil)
+(defvar tablist-edit-column-minor-mode)
+
+(defun tablist-selection-changed-handler ()
+  (unless tablist-edit-column-minor-mode
+    (let ((id tablist-selected-id)
+          (selected (tabulated-list-get-id)))
+      (unless (eq selected id)
+        (setq tablist-selected-id selected)
+        (run-hook-with-args
+         'tablist-selection-changed-functions
+         tablist-selected-id)))))
+
+(defvar tablist-context-window-update--timer nil)
+
+(defun tablist-context-window-update (&optional id)
+  (when (and tablist-context-window-function
+             (window-live-p tablist-context-window)
+             (not tablist-edit-column-minor-mode))
+    (unless id
+      (setq id (tabulated-list-get-id)))
+    (when (timerp tablist-context-window-update--timer)
+      (cancel-timer tablist-context-window-update--timer))
+    (setq tablist-context-window-update--timer
+          (run-with-idle-timer 0.1 nil
+                               (lambda (fn window)
+                                 (when (window-live-p window)
+                                   (with-selected-window window
+                                     (set-window-dedicated-p nil nil)
+                                     (save-selected-window
+                                       (funcall fn id))
+                                     (when (window-live-p (selected-window))
+                                       (set-window-dedicated-p nil t)))))
+                               tablist-context-window-function
+                               tablist-context-window))))
+
+(defun tablist-display-context-window ()
+  (interactive)
+  (unless tablist-context-window-function
+    (error "No function for handling a context is defined"))
+  (unless (window-live-p tablist-context-window)
+    (setq tablist-context-window
+          (display-buffer
+           (current-buffer)
+           tablist-context-window-display-action)))
+  (prog1
+      tablist-context-window
+    (tablist-context-window-update)))
+
+(defun tablist-hide-context-window ()
+  (interactive)
+  (when (window-live-p tablist-context-window)
+    (let ((ignore-window-parameters t))
+      (delete-window tablist-context-window)))
+  (setq tablist-context-window nil))
+
+(defun tablist-toggle-context-window ()
+  (interactive)
+  (if (window-live-p tablist-context-window)
+      (tablist-hide-context-window)
+    (tablist-display-context-window)))
+
+;;
+;; *Marking
+;;
+
+(defun tablist-revert ()
+  "Revert the list with marks preserved, position kept."
+  (interactive)
+  (tablist-save-marks
+   (tablist-with-remembering-entry
+     (tabulated-list-revert))))
+
+(defun tablist-major-columns ()
+  (if (null tablist-major-columns)
+      (number-sequence 0 (1- (length tabulated-list-format)))
+    (if (numberp tablist-major-columns)
+        (list tablist-major-columns)
+      tablist-major-columns)))
+
+(defun tablist-put-mark (&optional pos)
+  "Put a mark before the entry at POS.
+
+POS defaults to point. Use `tablist-marker-char',
+`tablist-marker-face', `tablist-marked-face' and
+`tablist-major-columns' to determine how to mark and what to put
+a face on."
+  (when (or (null tabulated-list-padding)
+            (< tabulated-list-padding 1))
+    (setq tabulated-list-padding 1)
+    (tabulated-list-revert))
+  (save-excursion
+    (and pos (goto-char pos))
+    (unless (tabulated-list-get-id)
+      (error "No entry at this position"))
+    (let ((inhibit-read-only t))
+      (tabulated-list-put-tag
+       (string tablist-marker-char))
+      (put-text-property
+       (point-at-bol)
+       (1+ (point-at-bol))
+       'face tablist-marker-face)
+      (let ((columns (tablist-column-offsets)))
+        (dolist (c (tablist-major-columns))
+          (when (and (>= c 0)
+                     (< c (length columns)))
+            (let ((beg (+ (point-at-bol)
+                          (nth c columns)))
+                  (end (if (= c (1- (length columns)))
+                           (point-at-eol)
+                         (+ (point-at-bol)
+                            (nth (1+ c) columns)))))
+              (cond
+               ((and tablist-marked-face
+                     (not (eq tablist-marker-char ?\s)))
+                (tablist--save-face-property beg end)
+                (put-text-property
+                 beg end 'face tablist-marked-face))
+               (t (tablist--restore-face-property beg end))))))))))
+
+(defun tablist-mark-forward (&optional arg interactive)
+  "Mark ARG entries forward.
+
+ARG is interpreted as a prefix-arg.  If interactive is non-nil,
+maybe use the active region instead of ARG.
+
+See `tablist-put-mark' for how entries are marked."
+  (interactive (list current-prefix-arg t))
+  (cond
+   ;; Mark files in the active region.
+   ((and interactive (use-region-p))
+    (save-excursion
+      (goto-char (region-beginning))
+      (beginning-of-line)
+      (tablist-repeat-over-lines
+       (1+ (count-lines
+            (point)
+            (save-excursion
+              (goto-char (region-end))
+              (beginning-of-line)
+              (point))))
+       'tablist-put-mark)))
+   ;; Mark the current (or next ARG) files.
+   (t
+    (tablist-repeat-over-lines
+     (prefix-numeric-value arg)
+     'tablist-put-mark))))
+
+(defun tablist-mark-backward (&optional arg interactive)
+  "Mark ARG entries backward.
+
+See `tablist-mark-forward'."
+  (interactive (list current-prefix-arg t))
+  (tablist-mark-forward (- (prefix-numeric-value arg))
+                        interactive))
+
+(defun tablist-unmark-forward (&optional arg interactive)
+  "Unmark ARG entries forward.
+
+See `tablist-mark-forward'."
+  (interactive (list current-prefix-arg t))
+  (let ((tablist-marker-char ?\s)
+        tablist-marked-face)
+    (tablist-mark-forward arg interactive)))
+
+(defun tablist-unmark-backward (&optional arg interactive)
+  "Unmark ARG entries backward.
+
+See `tablist-mark-forward'."
+  (interactive (list current-prefix-arg t))
+  (let ((tablist-marker-char ?\s)
+        tablist-marked-face)
+    (tablist-mark-backward arg interactive)))
+
+(defun tablist-flag-forward (&optional arg interactive)
+  "Flag ARG entries forward.
+
+See `tablist-mark-forward'."
+  (interactive (list current-prefix-arg t))
+  (let ((tablist-marker-char ?D)
+        (tablist-marked-face 'dired-flagged))
+    (tablist-mark-forward arg interactive)))
+
+(defun tablist-change-marks (old new)
+  "Change all OLD marks to NEW marks.
+
+OLD and NEW are both characters used to mark files."
+  (interactive
+   (let* ((cursor-in-echo-area t)
+          (old (progn (message "Change (old mark): ") (read-char)))
+          (new (progn (message  "Change %c marks to (new mark): " old)
+                      (read-char))))
+     (list old new)))
+  (when (eq new ?\n)
+    (error "Mark character \\n is not allowed"))
+  (let ((default-mark-p (equal tablist-marker-char new))
+        (tablist-marker-char old))
+    (save-excursion
+      (tablist-map-over-marks
+       (lambda nil
+         (pcase new
+           (?D
+            (tablist-flag-forward 1))
+           (_
+            (let ((tablist-marker-char new)
+                  (tablist-marked-face
+                   (and default-mark-p
+                        tablist-marked-face)))
+              (tablist-put-mark)))))))))
+
+(defun tablist-unmark-all-marks (&optional marks interactive)
+  "Remove alls marks in MARKS.
+
+MARKS should be a string of mark characters to match and defaults
+to all marks.  Interactively, remove all marks, unless a prefix
+arg was given, in which case ask about which ones to remove.
+Give a message, if interactive is non-nil.
+
+Returns the number of unmarked marks."
+  (interactive
+   (list (if current-prefix-arg
+             (read-string "Remove marks: ")) t))
+  (let ((re (if marks
+                (tablist-marker-regexp marks)
+              "^[^ ]"))
+        (removed 0))
+    (save-excursion
+      (goto-char (point-min))
+      (while (re-search-forward re nil t)
+        (let ((tablist-marker-char ?\s)
+              tablist-marker-face
+              tablist-marked-face)
+          (tablist-put-mark))
+        (cl-incf removed)))
+    (when interactive
+      (message "Removed %d marks" removed))
+    removed))
+
+(defun tablist-toggle-marks ()
+  "Unmark all marked and mark all unmarked entries.
+
+See `tablist-put-mark'."
+  (interactive)
+  (let ((marked-re (tablist-marker-regexp))
+        (not-marked-re
+         (let ((tablist-marker-char ?\s))
+           (tablist-marker-regexp))))
+    (save-excursion
+      (goto-char (point-min))
+      (tablist-skip-invisible-entries)
+      (while (not (eobp))
+        (cond
+         ((looking-at marked-re)
+          (save-excursion (tablist-unmark-backward -1)))
+         ((looking-at not-marked-re)
+          (tablist-put-mark)))
+        (tablist-forward-entry)))
+    (tablist-move-to-major-column)))
+
+(defun tablist-get-marked-items (&optional arg distinguish-one-marked)
+  "Return marked or ARG entries."
+  (let ((items (save-excursion
+                 (tablist-map-over-marks
+                  (lambda () (cons (tabulated-list-get-id)
+                                   (tabulated-list-get-entry)))
+                  arg nil distinguish-one-marked))))
+    (if (and distinguish-one-marked
+             (eq (car items) t))
+        items
+      (nreverse items))))
+
+(defun tablist-mark-items-regexp (column-name regexp)
+  "Mark entries matching REGEXP in column COLUMN-NAME."
+  (interactive
+   (tablist-read-regexp-filter "Mark" current-prefix-arg ))
+  (tablist-map-with-filter
+   'tablist-put-mark
+   `(=~  ,column-name ,regexp)))
+
+(defun tablist-mark-items-numeric (binop column-name operand)
+  "Mark items fulfilling BINOP with arg OPERAND in column COLUMN-NAME.
+
+First the column's value is coerced to a number N.  Then the test
+proceeds as \(BINOP N OPERAND\)."
+  (interactive
+   (tablist-read-numeric-filter "Mark" current-prefix-arg))
+  (tablist-map-with-filter
+   'tablist-put-mark
+   `(,binop ,column-name ,operand)))
+
+(defun tablist-map-over-marks (fn &optional arg show-progress
+                                  distinguish-one-marked)
+  (prog1
+      (cond
+       ((and arg (integerp arg))
+        (let (results)
+          (tablist-repeat-over-lines
+           arg
+           (lambda ()
+             (if show-progress (sit-for 0))
+             (push (funcall fn) results)))
+          (if (< arg 0)
+              (nreverse results)
+            results)))
+       (arg
+        ;; non-nil, non-integer ARG means use current item:
+        (tablist-skip-invisible-entries)
+        (unless (eobp)
+          (list (funcall fn))))
+       (t
+        (cl-labels ((search (re)
+                            (let (sucess)
+                              (tablist-skip-invisible-entries)
+                              (while (and (setq sucess
+                                                (re-search-forward re nil t))
+                                          (invisible-p (point)))
+                                (tablist-forward-entry))
+                              sucess)))
+          (let ((regexp (tablist-marker-regexp))
+                next-position results found)
+            (save-excursion
+              (goto-char (point-min))
+              ;; remember position of next marked file before BODY
+              ;; can insert lines before the just found file,
+              ;; confusing us by finding the same marked file again
+              ;; and again and...
+              (setq next-position (and (search regexp)
+                                       (point-marker))
+                    found (not (null next-position)))
+              (while next-position
+                (goto-char next-position)
+                (if show-progress (sit-for 0))
+                (push (funcall fn) results)
+                ;; move after last match
+                (goto-char next-position)
+                (forward-line 1)
+                (set-marker next-position nil)
+                (setq next-position (and (search regexp)
+                                         (point-marker)))))
+            (if (and distinguish-one-marked (= (length results) 1))
+                (setq results (cons t results)))
+            (if found
+                results
+              (unless (or (eobp) (invisible-p (point)))
+                (list (funcall fn))))))))
+    (tablist-move-to-major-column)))
+
+(defun tablist-marker-regexp (&optional marks)
+  "Return a regexp matching marks in MARKS.
+
+MARKS should be a string of mark characters to match and defaults
+to the current value of `tablist-marker-char' as a string."
+  (concat (format "^[%s]"
+                  (or marks (string tablist-marker-char)))))
+
+(defun tablist-get-mark-state ()
+  "Return the mark state of the entry at point."
+  (save-excursion
+    (beginning-of-line)
+    (when (looking-at "^\\([^ ]\\)")
+      (let ((mark (buffer-substring
+                   (match-beginning 1)
+                   (match-end 1))))
+        (tablist-move-to-major-column)
+        (list (aref mark 0)
+              (get-text-property 0 'face mark)
+              (get-text-property (point) 'face))))))
+
+(defun tablist-put-mark-state (state)
+  "Set the mark of the entry at point according to STATE.
+
+STATE is a return value of `tablist-get-mark-state'."
+  (cl-destructuring-bind (tablist-marker-char
+                          tablist-marker-face
+                          tablist-marked-face)
+      state
+    (tablist-put-mark)))
+
+(defun tablist-mark-prompt (arg items)
+  "Return a string suitable for use in a tablist prompt."
+  ;; distinguish-one-marked can cause the first element to be just t.
+  (if (eq (car items) t) (setq items (cdr items)))
+  (let ((count (length items)))
+    (if (= count 1)
+        (car items)
+      ;; more than 1 item:
+      (if (integerp arg)
+          ;; abs(arg) = count
+          ;; Perhaps this is nicer, but it also takes more screen space:
+          ;;(format "[%s %d items]" (if (> arg 0) "next" "previous")
+          ;;                        count)
+          (format "[next %d item%s]"
+                  arg (dired-plural-s arg))
+        (format "%c [%d item%s]" dired-marker-char count
+                (dired-plural-s count))))))
+
+;;
+;; *Movement
+;;
+
+(defun tablist-forward-entry (&optional n)
+  "Move past the next N unfiltered entries."
+  (unless n (setq n 1))
+  (while (and (> n 0)
+              (not (eobp)))
+    (forward-line)
+    (when (invisible-p (point))
+      (tablist-skip-invisible-entries))
+    (cl-decf n))
+  (while (and (< n 0)
+              (not (bobp)))
+    (forward-line -1)
+    (when (invisible-p (point))
+      (tablist-skip-invisible-entries t))
+    (cl-incf n))
+  n)
+
+(defun tablist-next-line (&optional n)
+  (interactive "p")
+  (when (and (< n 0)
+             (save-excursion
+               (end-of-line 0)
+               (tablist-skip-invisible-entries t)
+               (bobp)))
+    (signal 'beginning-of-buffer nil))
+  (when (and (> n 0)
+             (save-excursion
+               (tablist-forward-entry)
+               (eobp)))
+    (signal 'end-of-buffer nil))
+
+  (let ((col (tablist-current-column)))
+    (tablist-forward-entry (or n 1))
+    (if col
+        (tablist-move-to-column col)
+      (tablist-move-to-major-column))))
+
+(defun tablist-previous-line (&optional n)
+  (interactive "p")
+  (tablist-next-line (- (or n 1))))
+
+(defun tablist-repeat-over-lines (arg function)
+  "Call FUNCTION for the next ARG entries."
+  ;; Move out of potentially invisble area.
+  (tablist-skip-invisible-entries)
+  (let ((pos (make-marker)))
+    (while (and (> arg 0)
+                (not (eobp)))
+      (cl-decf arg)
+      (save-excursion
+        (tablist-forward-entry)
+        (move-marker pos (1+ (point))))
+      (unless (eobp)
+        (save-excursion (funcall function)))
+      ;; Advance to the next line--actually, to the line that *was* next.
+      ;; (If FUNCTION inserted some new lines in between, skip them.)
+      (goto-char pos))
+    (while (and (< arg 0) (not (bobp)))
+      (cl-incf arg)
+      (tablist-forward-entry -1)
+      (save-excursion (funcall function)))
+    (move-marker pos nil)
+    (tablist-move-to-major-column)))
+
+(defun tablist-move-to-column (n)
+  "Move to the N'th list column."
+  (interactive "p")
+  (when (tabulated-list-get-id)
+    (let ((columns (tablist-column-offsets)))
+      (when (or (< n 0)
+                (>= n (length columns)))
+        (error "No such column: %s" n))
+      (beginning-of-line)
+      (forward-char (nth n columns))
+      (when (and (plist-get (nthcdr 3 (elt tabulated-list-format n))
+                            :right-align)
+                 (not (= n (1- (length columns)))))
+        (forward-char (1- (car (cdr (elt tabulated-list-format n)))))))))
+
+(defun tablist-move-to-major-column (&optional first-skip-invisible-p)
+  "Move to the first major column."
+  (interactive (list t))
+  (when first-skip-invisible-p
+    (tablist-skip-invisible-entries))
+  (tablist-move-to-column (car (tablist-major-columns))))
+
+(defun tablist-forward-column (n)
+  "Move n columns forward, while wrapping around."
+  (interactive "p")
+  (unless (tabulated-list-get-id)
+    (error "No entry on this line"))
+  (let* ((columns (tablist-column-offsets))
+         (current (1- (length columns))))
+    ;; find current column
+    (while (and (>= current 0)
+                (> (nth current columns)
+                   (current-column)))
+      (cl-decf current))
+    ;; there may be an invisible spec here
+    (when (bolp)
+      (forward-char))
+    ;; before any columns
+    (when (< current 0)
+      (goto-char (+ (point-at-bol) (if (> n 0)
+                                       (car columns)
+                                     (car (last columns)))))
+      (setq n (* (cl-signum n) (1- (abs n)))))
+    (when (/= n 0)
+      (tablist-move-to-column
+       (mod (+ current n) (length columns))))))
+
+(defun tablist-backward-column (n)
+  "Move n columns backward, while wrapping around."
+  (interactive "p")
+  (tablist-forward-column (- n)))
+
+;;
+(defun tablist-skip-invisible-entries (&optional backward)
+  "Skip invisible entries BACKWARD or forward.
+
+Do nothing, if the entry at point is visible.  Otherwise move to
+the beginning of the next visible entry in the direction
+determined by BACKWARD.
+
+Return t, if point is now in a visible area."
+
+  (cond
+   ((and backward
+         (not (bobp))
+         (get-text-property (point) 'invisible))
+    (when (get-text-property (1- (point)) 'invisible)
+      (goto-char (previous-single-property-change
+                  (point)
+                  'invisible nil (point-min))))
+    (forward-line -1))
+   ((and (not backward)
+         (not (eobp))
+         (get-text-property (point) 'invisible))
+    (goto-char (next-single-property-change
+                (point)
+                'invisible nil (point-max)))))
+  (not (invisible-p (point))))
+
+;;
+;; *Operations
+;;
+
+(defun tablist-yes-or-no-p (operation arg items)
+  "Query the user whether to proceed with some operation.
+
+Operation should be a symbol or string describing the operation,
+ARG the prefix-arg of the command used in
+`tablist-get-marked-items' or elsewhere, to get the ITEMS."
+
+  (let ((pp-items (mapcar 'tablist-pretty-print-entry
+                          (mapcar 'cdr items)))
+        dired-no-confirm
+        (op-str (upcase-initials
+                 (if (stringp operation)
+                     operation
+                   (symbol-name operation)))))
+    (dired-mark-pop-up
+     (format " *%s*" op-str) nil
+     pp-items
+     dired-deletion-confirmer
+     (format "%s %s "
+             op-str
+             (tablist-mark-prompt arg pp-items)))))
+
+(defun tablist-operation-available-p (op)
+  (and (functionp tablist-operations-function)
+       (memq op (funcall tablist-operations-function
+                         'supported-operations))))
+
+(defun tablist-do-delete (&optional arg)
+  "Delete ARG entries."
+  (interactive "P")
+  (unless (tablist-operation-available-p 'delete)
+    (error "Deleting entries is not available in this buffer"))
+  (let ((items (tablist-get-marked-items arg)))
+    (when (tablist-yes-or-no-p 'delete arg items)
+      (tablist-do-kill-lines arg)
+      (funcall tablist-operations-function
+               'delete (mapcar 'car items))
+      (tablist-move-to-major-column))))
+
+(defun tablist-do-flagged-delete (&optional interactive)
+  "Delete all entries marked with a D."
+  (interactive "p")
+  (let* ((tablist-marker-char ?D))
+    (if (save-excursion
+          (goto-char (point-min))
+          (re-search-forward (tablist-marker-regexp) nil t))
+        (tablist-do-delete)
+      (or (not interactive)
+          (message "(No deletions requested)")))))
+
+(defun tablist-do-kill-lines (&optional arg interactive)
+  "Remove ARG lines from the display."
+  (interactive (list current-prefix-arg t))
+  (save-excursion
+    (let ((positions
+           (tablist-map-over-marks 'point arg))
+          (inhibit-read-only t))
+      (dolist (pos positions)
+        (goto-char pos)
+        (tabulated-list-delete-entry))
+      (when interactive
+        (message (format "Killed %d line%s"
+                         (length positions)
+                         (dired-plural-s (length positions))))))))
+
+(defun tablist-do-operation (arg fn operation &optional delete-p revert-p)
+  "Operate on marked items.
+
+ARG should be the `current-prefix-arg', FN is a function of two
+arguments \(ID ENTRY\) handling the operation.  It gets called
+repeatly with all marked items.  OPERATION is a symbol or string
+describing the operation, it is used for display.
+
+Optional non-nil DELETE-P means, remove the items from the display.
+Optional REVERT-P means, revert the display afterwards."
+  (let ((items (tablist-get-marked-items arg)))
+    (unless items
+      (error "No items marked"))
+    (when (tablist-yes-or-no-p operation arg items)
+      (when delete-p
+        (tablist-do-kill-lines arg))
+      (dolist (item items)
+        (funcall fn (car item)))
+      (when revert-p
+        (tablist-revert))
+      (tablist-move-to-major-column))))
+
+;;
+;; *Editing
+;;
+(defvar tablist-edit-column-minor-mode-map
+  (let ((kmap (make-sparse-keymap)))
+    (set-keymap-parent kmap (current-global-map))
+    (define-key kmap [remap self-insert-command] 'self-insert-command)
+    (define-key kmap "\r" 'tablist-edit-column-commit)
+    (define-key kmap (kbd "C-g") 'tablist-edit-column-quit)
+    (define-key kmap (kbd "C-c C-c") 'tablist-edit-column-commit)
+    (define-key kmap (kbd "C-c C-q") 'tablist-edit-column-quit)
+    (define-key kmap "\t" 'tablist-edit-column-complete)
+    (define-key kmap (kbd "TAB") 'tablist-edit-column-complete)
+    (define-key kmap [remap end-of-buffer] 'end-of-line)
+    (define-key kmap [remap beginning-of-buffer] 'beginning-of-line)
+    (define-key kmap [remap mark-whole-buffer] 'tablist-edit-column-mark-field)
+    kmap))
+
+(define-minor-mode tablist-edit-column-minor-mode
+  "" nil nil nil
+  (unless (or tablist-minor-mode
+              (derived-mode-p 'tablist-mode))
+    (error "Not in a tablist buffer"))
+  (cond
+   (tablist-edit-column-minor-mode
+    (add-to-list 'mode-line-misc-info
+                 '(tablist-edit-column-minor-mode "[edit]"))
+    (add-hook 'post-command-hook 'tablist-edit-column-constrain-point nil t)
+    (read-only-mode -1))
+   (t
+    (remove-hook 'post-command-hook 'tablist-edit-column-constrain-point t)
+    (read-only-mode 1))))
+
+(defun tablist-edit-column (&optional n)
+  (interactive "P")
+  (unless n (setq n (tablist-current-column)))
+  (tablist-assert-column-editable n)
+  (let* ((offsets (append (tablist-column-offsets)
+                          (list (- (point-at-eol)
+                                   (point-at-bol)))))
+         (beg (+ (point-at-bol)
+                 (nth n offsets)))
+         (end (+ (point-at-bol)
+                 (nth (1+ n) offsets)))
+         (entry (tabulated-list-get-entry beg))
+         (inhibit-read-only t)
+         (inhibit-field-text-motion t)
+         (alist `((entry . ,entry)
+                  (column . ,n)
+                  (id . ,(tabulated-list-get-id beg))))
+         ov)
+    (goto-char beg)
+    (delete-region beg end)
+    (add-text-properties
+     (point-at-bol) (point-at-eol)
+     '(read-only t field t))
+    (unless (= beg (point-at-bol))
+      (put-text-property (1- beg) beg 'rear-nonsticky t))
+    (save-excursion
+      ;; Keep one read-only space at the end for keeping text
+      ;; properties.
+      (insert
+       (propertize
+        (concat
+         (tablist-nth-entry n entry)
+         (propertize " "
+                     'display `(space :align-to ,(- end (point-at-bol)))))
+        'field nil
+        'front-sticky '(tablist-edit)
+        'rear-nonsticky '(read-only field)
+        'tablist-edit alist))
+      (setq end (point)))
+    (add-text-properties
+     (1- end) end '(read-only t field 'tablist-edit-end))
+    (setq ov (make-overlay beg end))
+    (overlay-put ov 'priority 9999)
+    (overlay-put ov 'face '(:background "deep sky blue" :foreground "white"))
+    (overlay-put ov 'evaporate t)
+    (overlay-put ov 'tablist-edit t)
+    (tablist-edit-column-minor-mode 1)))
+
+(defun tablist-edit-column-quit ()
+  (interactive)
+  (tablist-edit-column-commit t))
+
+(defun tablist-edit-column-commit (&optional abandon-edit)
+  (interactive (list current-prefix-arg))
+  (let ((inhibit-read-only t)
+        (inhibit-field-text-motion t)
+        bounds)
+    (condition-case nil
+        (setq bounds (tablist-edit-column-bounds))
+      (error
+       (tablist-edit-column-minor-mode -1)
+       (tabulated-list-revert)
+       (put-text-property (point-min) (point-max)
+                          'tablist-edit nil)
+       (error "Unable to complete the edit")))
+    (let* ((beg (car bounds))
+           (end (cdr bounds))
+           (alist (get-text-property beg 'tablist-edit))
+           (column (cdr (assq 'column alist)))
+           (id (cdr (assq 'id alist)))
+           (entry (cdr (assq 'entry alist)))
+           (item (buffer-substring-no-properties beg (1- end))))
+
+      (unless abandon-edit
+        ;; Throws an error, if item is invalid.
+        (setq entry (funcall tablist-operations-function
+                             'edit-column id column item)))
+      (tablist-edit-column-minor-mode -1)
+      (remove-overlays beg end 'tablist-edit t)
+      (put-text-property beg end 'tablist-edit nil)
+      (delete-region (point-at-bol) (1+ (point-at-eol)))
+      (save-excursion
+        (tabulated-list-print-entry id entry))
+      (forward-char (nth column (tablist-column-offsets))))))
+
+(defun tablist-edit-column-complete ()
+  (interactive)
+  (unless (tablist-operation-available-p 'complete)
+    (error "Completion not available"))
+  (cl-destructuring-bind (beg &rest end)
+      (tablist-edit-column-bounds t)
+    (let* ((string (buffer-substring-no-properties
+                    beg end))
+           (alist (get-text-property beg 'tablist-edit))
+           (completions (funcall tablist-operations-function
+                                 'complete
+                                 (cdr (assq 'id alist))
+                                 (cdr (assq 'column alist))
+                                 string
+                                 (- (point) beg))))
+      (unless completions
+        (error "No completions available"))
+      (completion-in-region beg end completions))))
+
+(defun tablist-column-editable (n)
+  (and (tablist-operation-available-p 'edit-column)
+       (not (tablist-column-property n :read-only))))
+
+(defun tablist-assert-column-editable (n)
+  (unless (and (>= n 0)
+               (< n (length tabulated-list-format)))
+    (error "Invalid column number: %s" n))
+  (unless (tablist-operation-available-p 'edit-column)
+    (error "Editing columns not enabled in this buffer"))
+  (when (tablist-column-property n :read-only)
+    (error "This column is read-only")))
+
+(defun tablist-edit-column-constrain-point ()
+  (unless tablist-edit-column-minor-mode
+    (error "Not editing a column"))
+  (unless (get-text-property (point) 'tablist-edit)
+    (let ((bounds (tablist-edit-column-bounds)))
+      (when bounds
+        (if (> (point) (cdr bounds))
+            (goto-char (1- (cdr bounds)))
+          (goto-char (car bounds)))
+        (point)))))
+
+(defun tablist-edit-column-bounds (&optional skip-final-space)
+  (unless tablist-edit-column-minor-mode
+    (error "Not editing a column"))
+  (let ((pos (next-single-property-change
+              (point) 'tablist-edit))
+        beg end)
+    (cond
+     ((null pos)
+      (setq end (previous-single-property-change
+                 (point-max) 'tablist-edit)
+            beg (previous-single-property-change
+                 end 'tablist-edit)))
+     ((get-text-property pos 'tablist-edit)
+      (setq beg pos
+            end (next-single-property-change
+                 pos 'tablist-edit)))
+     (pos
+      (setq end pos
+            beg (previous-single-property-change
+                 pos 'tablist-edit))))
+
+    (unless (and beg end (get-text-property beg 'tablist-edit))
+      (error "Unable to locate edited text"))
+    (cons beg (if skip-final-space (1- end) end))))
+
+(defun tablist-edit-column-mark-field ()
+  (interactive)
+  (push-mark (field-beginning))
+  (push-mark (field-end) nil t)
+  (goto-char (field-beginning)))
+
+(defun tablist-find-entry (&optional id)
+  (interactive)
+  (unless (tablist-operation-available-p 'find-entry)
+    (error "Finding entries not supported in this buffer"))
+  (funcall tablist-operations-function
+           'find-entry
+           (or id (tabulated-list-get-id))))
+
+;;
+;; *Utility
+;;
+
+(defun tablist-column-property (n prop)
+  (plist-get
+   (nthcdr 3 (aref tabulated-list-format n))
+   prop))
+
+(defun tablist-current-column ()
+  "Return the column number at point.
+
+Returns nil, if point is before the first column."
+  (let ((column
+         (1- (cl-position
+              (current-column)
+              (append (tablist-column-offsets)
+                      (list most-positive-fixnum))
+              :test (lambda (column offset) (> offset column))))))
+    (when (>= column 0)
+      column)))
+
+(defun tablist-column-offsets ()
+  "Return a list of column positions.
+
+This is a list of offsets from the beginning of the line."
+  (let ((cc tabulated-list-padding)
+        columns)
+    (dotimes (i (length tabulated-list-format))
+      (let* ((c (aref tabulated-list-format i))
+             (len (nth 1 c))
+             (pad (or (plist-get (nthcdr 3 c) :pad-right)
+                      1)))
+        (push cc columns)
+        (when (numberp len)
+          (cl-incf cc len))
+        (when pad
+          (cl-incf cc pad))))
+    (nreverse columns)))
+
+(defun tablist-pretty-print-entry (item)
+  (mapconcat (lambda (i)
+               (tablist-nth-entry i item))
+             (tablist-major-columns) " "))
+
+(defun tablist--save-face-property (beg end)
+  ;; We need to distinguish ,,not set'' from ''no face''.
+  (unless (and (text-property-not-all beg end 'face nil)
+               (< beg end))
+    (put-text-property beg (1+ beg) 'face 'default))
+  (unless (text-property-not-all beg end 'tablist-saved-face nil)
+    (tablist-copy-text-property beg end 'face 'tablist-saved-face)))
+
+(defun tablist--restore-face-property (beg end)
+  (when (text-property-not-all beg end 'tablist-saved-face nil)
+    (tablist-copy-text-property beg end 'tablist-saved-face 'face)))
+
+(defun tablist-copy-text-property (beg end from to)
+  "Copy text property FROM to TO in region BEG to END."
+  (let ((inhibit-read-only t))
+    (save-excursion
+      (while (< beg end)
+        (goto-char beg)
+        (put-text-property
+         (point)
+         (setq beg (next-single-property-change
+                    (point) from nil end))
+         to
+         (get-text-property (point) from))))))
+
+;;
+(defun tablist-read-column-name (arg &optional prompt default)
+  "Read the name of a column using ARG.
+
+If ARG is a number, return column ARG.
+If ARG is nil, return DEFAULT or the current column.
+Else ask the user, using PROMPT and DEFAULT."
+  (cond
+   ((numberp arg)
+    (or (tablist-column-name
+         (prefix-numeric-value arg))
+        (error "No such column: %d" (prefix-numeric-value arg))))
+   ((null arg)
+    (or default
+        (tablist-column-name
+         (or (tablist-current-column)
+             (car (tablist-major-columns))
+             0))))
+   (t
+    (let* ((default (or default
+                        (tablist-column-name
+                         (car (tablist-major-columns)))))
+           (result
+            (completing-read
+             (format "%s %s: "
+                     (or prompt "Use column")
+                     (if default
+                         (format "(default %s)"
+                                 default)
+                       ""))
+             (tablist-column-names)
+             nil t nil 'tablist-column-name-history)))
+      (if (> (length result) 0)
+          result
+        (if (not default)
+            (error "No column selected")
+          default))))))
+
+(defun tablist-column-name (n)
+  "Return the name of column N."
+  (when (and n
+             (>= n 0)
+             (< n (length tabulated-list-format)))
+    (substring-no-properties
+     (car (elt tabulated-list-format n)) 0)))
+
+(defun tablist-column-names ()
+  "Return a list of all column-names."
+  (mapcar 'tablist-column-name
+          (number-sequence 0 (1- (length tabulated-list-format)))))
+
+(defun tablist-nth-entry (n &optional entry)
+  (unless entry (setq entry (tabulated-list-get-entry)))
+  (when (and entry
+             (>= n 0)
+             (< n (length entry)))
+    (let ((str (elt entry n)))
+      (if (stringp str)
+          str
+        (car str)))))
+
+(defun tablist-major-column-name ()
+  "Return a list of the major column names."
+  (tablist-column-name (car (tablist-major-columns))))
+
+(defun tablist-export-csv (&optional separator always-quote-p
+                                     invisible-p out-buffer display-p)
+  "Export a tabulated list to a CSV format.
+
+Use SEPARATOR (or ;) and quote if necessary (or always if
+ALWAYS-QUOTE-P is non-nil).  Only consider non-filtered entries,
+unless invisible-p is non-nil.  Create a buffer for the output or
+insert it after point in OUT-BUFFER.  Finally if DISPLAY-P is
+non-nil, display this buffer.
+
+Return the output buffer."
+
+  (interactive (list nil t nil nil t))
+  (unless (derived-mode-p 'tabulated-list-mode)
+    (error "Not in Tabulated List Mode"))
+  (unless (stringp separator)
+    (setq separator (string (or separator ?\;))))
+  (let* ((outb (or out-buffer
+                   (get-buffer-create
+                    (format "%s.csv" (buffer-name)))))
+         (escape-re (format "[%s\"\n]" separator))
+         (header (tablist-column-names)))
+    (unless (buffer-live-p outb)
+      (error "Expected a live buffer: %s" outb))
+    (cl-labels
+        ((printit (entry)
+                  (insert
+                   (mapconcat
+                    (lambda (e)
+                      (unless (stringp e)
+                        (setq e (car e)))
+                      (if (or always-quote-p
+                              (string-match escape-re e))
+                          (concat "\""
+                                  (replace-regexp-in-string "\"" "\"\"" e t t)
+                                  "\"")
+                        e))
+                    entry separator))
+                  (insert ?\n)))
+      (with-current-buffer outb
+        (let ((inhibit-read-only t))
+          (erase-buffer)
+          (printit header)))
+      (save-excursion
+        (goto-char (point-min))
+        (unless invisible-p
+          (tablist-skip-invisible-entries))
+        (while (not (eobp))
+          (let* ((entry (tabulated-list-get-entry)))
+            (with-current-buffer outb
+              (let ((inhibit-read-only t))
+                (printit entry)))
+            (if invisible-p
+                (forward-line)
+              (tablist-forward-entry)))))
+      (if display-p
+          (display-buffer outb))
+      outb)))
+
+;;
+
+(defun tablist-enlarge-column (&optional column width)
+  "Enlarge column COLUMN by WIDTH.
+
+This function is lazy and therfore pretty slow."
+  (interactive
+   (list nil (* (prefix-numeric-value current-prefix-arg)
+                3)))
+  (unless column (setq column (tablist-current-column)))
+  (unless column
+    (error "No column given and no entry at point"))
+  (unless width (setq width 1))
+  (when (or (not (numberp column))
+            (< column 0)
+            (>= column (length tabulated-list-format)))
+    (error "No such column: %d" column))
+  (when (= column (1- (length tabulated-list-format)))
+    (error "Can't resize last column"))
+
+  (let* ((cur-width (cadr (elt tabulated-list-format column))))
+    (setcar (cdr (elt tabulated-list-format column))
+            (max 3 (+ cur-width width)))
+    (tablist-with-remembering-entry
+      (tablist-save-marks
+       (tabulated-list-init-header)
+       (tabulated-list-print)))))
+
+(defun tablist-shrink-column (&optional column width)
+  (interactive
+   (list nil (* (prefix-numeric-value current-prefix-arg)
+                3)))
+  (tablist-enlarge-column column (- (or width 1))))
+
+;; *Sorting
+;;
+
+(defun tablist-sort (&optional column)
+  "Sort the tabulated-list by COLUMN.
+
+COLUMN may be either a name or an index.  The default compare
+function is given by the `tabulated-list-format', which see.
+
+This function saves the current sort column and the inverse
+sort-direction in the variable `tabulated-list-sort-key', which
+also determines the default COLUMN and direction.
+
+The main difference to `tabulated-list-sort' is, that this
+function sorts the buffer in-place and it ignores a nil sort
+entry in `tabulated-list-format' and sorts on the column
+anyway (why not ?)."
+
+  (interactive
+   (list
+    (if (null current-prefix-arg)
+        (tablist-column-name
+         (or (tablist-current-column)
+             (car (tablist-major-columns))
+             0))
+      (tablist-read-column-name
+       '(4) "Sort by column"
+       (tablist-column-name (car (tablist-major-columns)))))))
+
+  (unless column
+    (setq column (or (car tabulated-list-sort-key)
+                     (tablist-column-name (car (tablist-major-columns)))
+                     (tablist-column-name 0))))
+  (when (numberp column)
+    (let ((column-name (tablist-column-name column)))
+      (unless column-name
+        (error "No such column: %d" column))
+      (setq column column-name)))
+
+  (setq tabulated-list-sort-key
+        (cons column
+              (if (equal column (car tabulated-list-sort-key))
+                  (cdr tabulated-list-sort-key))))
+
+  (let* ((entries (if (functionp tabulated-list-entries)
+                      (funcall tabulated-list-entries)
+                    tabulated-list-entries))
+         (reverse (cdr tabulated-list-sort-key))
+         (n (tabulated-list--column-number ;;errors if column is n/a
+             (car tabulated-list-sort-key)))
+         (compare-fn (nth 2 (aref tabulated-list-format n))))
+
+    (when (or (null compare-fn)
+              (eq compare-fn t))
+      (setq compare-fn
+            (lambda (a b)
+              (setq a (aref (cadr a) n))
+              (setq b (aref (cadr b) n))
+              (string< (if (stringp a) a (car a))
+                       (if (stringp b) b (car b))))))
+
+    (unless compare-fn
+      (error "This column cannot be sorted: %s" column))
+
+    (setcdr tabulated-list-sort-key (not reverse))
+    ;; Presort the entries and hash the result and sort the buffer.
+    (setq entries (sort (copy-sequence entries) compare-fn))
+    (let ((hash (make-hash-table :test 'equal)))
+      (dotimes (i (length entries))
+        (puthash (caar entries) i hash)
+        (setq entries (cdr entries)))
+      (tablist-with-remembering-entry
+        (goto-char (point-min))
+        (tablist-skip-invisible-entries)
+        (let ((inhibit-read-only t))
+          (sort-subr
+           nil 'tablist-forward-entry 'end-of-line
+           (lambda ()
+             (gethash (tabulated-list-get-id) hash 0))
+           nil (if reverse '< '>))))
+      (tablist-move-to-column n)
+      ;; Make the sort arrows display.
+      (tabulated-list-init-header))))
+
+;;
+;; *Filter
+;;
+
+(defun tablist-read-filter-name (prompt)
+  (let ((filter (cdr (assq major-mode tablist-named-filter))))
+    (unless filter
+      (error "No filter defined for %s mode" mode-name))
+    (let ((name (completing-read
+                 (format "%s: " prompt)
+                 filter
+                 nil t)))
+      (unless (> (length name) 0)
+        (error "No filter selected"))
+      name)))
+
+(defun tablist-apply-filter (&optional filter)
+  "Apply FILTER to the current tabulated list.
+
+FILTER defaults to `tablist-current-filter'."
+  (unless filter (setq filter tablist-current-filter))
+  (tablist-filter-unhide-buffer)
+  (when (and filter
+             (null tablist-filter-suspended))
+    (tablist-with-remembering-entry
+      (tablist-map-with-filter
+       (lambda nil
+         (if tablist-umark-filtered-entries
+             (save-excursion (tablist-unmark-forward)))
+         (tablist-filter-hide-entry))
+       (tablist-filter-negate filter))))
+  (force-mode-line-update))
+
+(defadvice tabulated-list-print (after tabulated-list activate)
+  "Reapply the filter."
+  (when (or tablist-minor-mode
+            (derived-mode-p 'tablist-mode))
+    (tablist-apply-filter)))
+
+(defun tablist-eval-filter (filter)
+  (tablist-filter-eval
+   filter
+   (tabulated-list-get-id)
+   (tabulated-list-get-entry)
+   (cdr (assq major-mode tablist-named-filter))))
+
+(defun tablist-map-with-filter (fn filter &optional show-progress
+                                   distinguish-one-marked)
+  "Call FN for every unfiltered entry matching FILTER."
+  (prog1
+      (cl-labels ((search ()
+                          (tablist-skip-invisible-entries)
+                          (while (and (not (eobp))
+                                      (not (tablist-eval-filter filter)))
+                            (tablist-forward-entry))
+                          (unless (eobp)
+                            (point-marker))))
+        (let (next-position results)
+          (save-excursion
+            (goto-char (point-min))
+            (setq next-position (search))
+            (while next-position
+              (goto-char next-position)
+              (if show-progress (sit-for 0))
+              (push (funcall fn) results)
+              ;; move after last match
+              (goto-char next-position)
+              (forward-line 1)
+              (set-marker next-position nil)
+              (setq next-position (search)))
+            (if (and distinguish-one-marked (= (length results) 1))
+                (setq results (cons t results))))))))
+
+;;
+;; **Filter Commands
+;;
+(defun tablist-push-filter (filter &optional interactive or-p)
+  (setq tablist-current-filter
+        (tablist-filter-push
+         tablist-current-filter
+         filter or-p))
+  (tablist-apply-filter)
+  (when interactive
+    (tablist-display-filter-temporarily)))
+
+(defun tablist-pop-filter (&optional n interactive)
+  "Remove the first N filter components."
+  (interactive (list (prefix-numeric-value current-prefix-arg) t))
+  (while (and tablist-current-filter
+              (> n 0))
+    (setq tablist-current-filter
+          (tablist-filter-pop
+           tablist-current-filter))
+    (cl-decf n))
+  (tablist-apply-filter)
+  (when interactive
+    (when (> n 0)
+      (message "The filter is empty."))
+    (tablist-display-filter-temporarily))
+  n)
+
+(defun tablist-negate-filter (&optional interactive)
+  "Negate the current filter."
+  (interactive (list t))
+  (setq tablist-current-filter
+        (tablist-filter-negate
+         tablist-current-filter))
+  (tablist-apply-filter)
+  (when interactive
+    (tablist-display-filter-temporarily)))
+
+(defun tablist-toggle-first-filter-logic ()
+  "Toggle between and/or for the first filter operand."
+  (interactive)
+  (setq tablist-current-filter
+        (pcase tablist-current-filter
+          (`(or ,x1 ,x2)
+           `(and ,x1 ,x2))
+          (`(and ,x1 ,x2)
+           `(or ,x1 ,x2))
+          (`(not ,x) x)
+          (x `(not ,x))))
+  (tablist-apply-filter)
+  (tablist-display-filter-temporarily))
+
+(defun tablist-suspend-filter (&optional flag)
+  "Temporarily disable filtering according to FLAG.
+
+Interactively, this command toggles filtering."
+  (interactive
+   (list (not tablist-filter-suspended)))
+  (let ((state tablist-filter-suspended))
+    (unless (eq (not (not state))
+                (not (not flag)))
+      (set (make-local-variable 'tablist-filter-suspended) flag)
+      (tablist-apply-filter))))
+
+(defun tablist-read-regexp-filter (operation arg)
+  (let ((column-name (tablist-read-column-name arg)))
+    (list
+     column-name
+     (let ((re
+            (read-regexp (format "%s where %s matches: " operation column-name))))
+       (unless (> (length re) 0)
+         (error "No regexp given"))
+       re))))
+
+(defun tablist-read-equal-filter (operation arg)
+  (let ((column-name (tablist-read-column-name arg)))
+    (list
+     column-name
+     (read-string (format "%s where %s equals: " operation column-name)))))
+
+(defun tablist-read-numeric-filter (operation arg)
+  (let* ((entry (tabulated-list-get-entry 1))
+         (default (cl-some
+                   (lambda (idx)
+                     (let ((value (tablist-nth-entry idx entry)))
+                       (when (or (not (eq 0 (string-to-number value)))
+                                 (equal "0" value))
+                         (tablist-column-name idx))))
+                   (number-sequence 0 (length entry))))
+         (column-name (tablist-read-column-name arg nil default))
+         (op (completing-read
+              (format "%s %s matching binary op: " operation column-name)
+              '("=" "<" ">" "<=" ">=") nil t))
+         oper)
+
+    (when (equal "" op)
+      (error "No operation selected"))
+    (setq op (intern op))
+    (setq oper (number-to-string
+                (read-number
+                 (format "%s where %s %s " operation column-name op))))
+
+    (list op column-name oper)))
+
+(defun tablist-push-regexp-filter (column-name regexp)
+  "Add a new filter matching REGEXP in COLUMN-NAME.
+
+The filter is and'ed with the current filter.  Use
+`tablist-toggle-first-filter-logic' to change this."
+  (interactive
+   (tablist-with-filter-displayed
+    (tablist-read-regexp-filter "Filter" current-prefix-arg)))
+  (tablist-push-filter
+   `(=~ ,column-name ,regexp)
+   (called-interactively-p 'any)))
+
+(defun tablist-push-equal-filter (column-name string)
+  "Add a new filter whre string equals COLUMN-NAME's value.
+
+The filter is and'ed with the current filter.  Use
+`tablist-toggle-first-filter-logic' to change this."
+  (interactive
+   (tablist-with-filter-displayed
+    (tablist-read-equal-filter "Filter" current-prefix-arg)))
+  (tablist-push-filter
+   `(== ,column-name ,string)
+   (called-interactively-p 'any)))
+
+(defun tablist-push-numeric-filter (op column-name 2nd-arg)
+  "Add a new filter matching a numeric predicate.
+
+The filter is and'ed with the current filter.  Use
+`tablist-toggle-first-filter-logic' to change this."
+  (interactive
+   (tablist-with-filter-displayed
+    (tablist-read-numeric-filter "Filter" current-prefix-arg)))
+  (tablist-push-filter
+   `(,op ,column-name ,2nd-arg)
+   (called-interactively-p 'any)))
+
+(defun tablist-push-named-filter (name)
+  "Add a named filter called NAME.
+
+Named filter are saved in the variable `tablist-named-filter'."
+  (interactive
+   (tablist-with-filter-displayed
+    (list
+     (tablist-read-filter-name "Add filter"))))
+  (when (and name (symbolp name))
+    (setq name (symbol-name name)))
+  (tablist-push-filter name (called-interactively-p 'any)))
+
+(defun tablist-delete-named-filter (name &optional mode)
+  (interactive
+   (tablist-with-filter-displayed
+    (list
+     (tablist-read-filter-name "Delete filter"))))
+  (setq tablist-current-filter
+        (tablist-filter-map
+         (lambda (f)
+           (when (equal f name)
+             (setq f (tablist-get-named-filter f)))
+           f)
+         tablist-current-filter))
+  (unless mode (setq mode major-mode))
+  (let ((mode-filter
+         (assq mode tablist-named-filter)))
+    (when mode-filter
+      (setcdr mode-filter
+              (cl-remove name (cdr mode-filter)
+                         :test 'equal :key 'car)))))
+
+(defun tablist-name-current-filter (name)
+  (interactive
+   (list (tablist-with-filter-displayed
+          (read-string
+           "Add name for current filter: "))))
+  (unless tablist-current-filter
+    (error "Filter is empty"))
+  (unless (> (length name) 0)
+    (error "No name given"))
+  (tablist-put-named-filter
+   name (if (stringp tablist-current-filter)
+            (tablist-get-named-filter
+             tablist-current-filter)
+          tablist-current-filter))
+  (setq tablist-current-filter name)
+  (force-mode-line-update))
+
+(defun tablist-deconstruct-named-filter ()
+  (interactive)
+  (let (found)
+    (setq tablist-current-filter
+          (tablist-filter-map
+           (lambda (f)
+             (when (and (not found)
+                        (stringp f))
+               (setq found t)
+               (let ((df (tablist-get-named-filter f)))
+                 (unless df
+                   (error "Filter is not defined: %s" f))
+                 (setq f df)))
+             f)
+           tablist-current-filter))
+    (unless found
+      (error "No named filter found"))
+    (force-mode-line-update)))
+
+(defun tablist-filter-names (&optional mode)
+  (mapcar 'car (cdr (assq (or mode major-mode)
+                          tablist-named-filter))))
+
+(defun tablist-get-named-filter (name &optional mode)
+  (cdr (assoc name
+              (cdr (assq (or mode major-mode)
+                         tablist-named-filter)))))
+
+(defun tablist-put-named-filter (name filter &optional mode)
+  (unless mode (setq mode major-mode))
+  (let ((mode-filter
+         (assq mode tablist-named-filter)))
+    (unless mode-filter
+      (setq mode-filter (cons mode nil))
+      (push mode-filter tablist-named-filter))
+    (let ((entry (assoc name mode-filter)))
+      (if entry
+          (setcdr entry filter)
+        (setcdr mode-filter
+                (list (cons name filter)))))))
+
+(defun tablist-validate-named-filter (filter)
+  (tablist-filter-map
+   (lambda (f)
+     (when (and (stringp f)
+                (null (tablist-get-named-filter f)))
+       (error "Undefined named filter: %s (defined: %s)" f
+              (mapconcat 'identity (tablist-filter-names) ","))))
+   filter))
+
+(defun tablist-edit-filter ()
+  (interactive)
+  (setq tablist-current-filter
+        (tablist-with-filter-displayed
+         (tablist-filter-edit-filter
+          "Edit filter: "
+          tablist-current-filter
+          nil
+          'tablist-validate-named-filter)))
+  (tablist-apply-filter))
+
+(defun tablist-clear-filter ()
+  (interactive)
+  (setq tablist-current-filter nil)
+  (tablist-apply-filter))
+
+;; **Displaying filter
+;;
+
+(defconst tablist-display-filter-mode-line-tag nil)
+
+(defun tablist-display-filter (&optional flag)
+  "Display the current filter according to FLAG.
+
+If FLAG has the value 'toggle, toggle it's visibility.
+If FLAG has the 'state, then do nothing but return the current
+visibility."
+  (interactive (list 'toggle))
+  (let* ((tag 'tablist-display-filter-mode-line-tag)
+         (displayed-p (not (not (assq tag mode-line-format)))))
+    (if (eq flag 'state)
+        displayed-p
+      (let ((display-p (not (not (if (eq flag 'toggle)
+                                     (not displayed-p)
+                                   flag)))))
+        (unless (eq displayed-p display-p)
+          (setq mode-line-format
+                (if display-p
+                    (list (cons tag mode-line-format)
+                          '(:eval
+                            (replace-regexp-in-string
+                             "%" "%%"
+                             (concat
+                              (propertize "Filter: " 'face 'minibuffer-prompt)
+                              (and tablist-filter-suspended
+                                   "[suspended] ")
+                              (if tablist-current-filter
+                                  (tablist-filter-unparse
+                                   tablist-current-filter t)
+                                "[none]")))))
+                  (cdr (assq tag mode-line-format)))))
+        (force-mode-line-update)
+        display-p))))
+
+(defun tablist-display-filter-temporarily ()
+  (tablist-with-filter-displayed
+   (sit-for 9999)))
+
+;;
+;; **Hiding/Unhiding Entries
+;;
+(defun tablist-filter-set-entry-hidden (flag &optional pos)
+  (save-excursion
+    (when pos (goto-char pos))
+    (beginning-of-line)
+    (let ((inhibit-read-only t))
+      (add-text-properties
+       (point-at-bol)
+       (1+ (point-at-eol))
+       `(invisible ,flag)))))
+
+(defun tablist-filter-hide-entry (&optional pos)
+  (interactive)
+  (tablist-filter-set-entry-hidden t pos))
+
+(defun tablist-filter-unhide-entry (&optional pos)
+  (tablist-filter-set-entry-hidden nil pos))
+
+(defun tablist-filter-unhide-buffer ()
+  (let ((inhibit-read-only t))
+    (remove-text-properties
+     (point-min) (point-max)
+     '(invisible))))
+
+(defun tablist-window-attach (awindow &optional window)
+  "Attach AWINDOW to WINDOW.
+
+This has the following effect.  Whenever WINDOW, defaulting to
+the selected window, stops displaying the buffer it currently
+displays (e.g., by switching buffers or because it was deleted)
+AWINDOW is deleted."
+  (unless window (setq window (selected-window)))
+  (let ((buffer (window-buffer window))
+        (hook (make-symbol "window-attach-hook")))
+    (fset hook
+          (lambda ()
+            (when (or (not (window-live-p window))
+                      (not (eq buffer (window-buffer window))))
+              (remove-hook 'window-configuration-change-hook
+                           hook)
+              ;; Deleting windows inside wcch may cause errors in
+              ;; windows.el .
+              (run-with-timer
+               0 nil (lambda (win)
+                       (when (and (window-live-p win)
+                                  (not (eq win (selected-window))))
+                         (delete-window win)))
+               awindow))))
+    (add-hook 'window-configuration-change-hook hook)))
+
+(defun tablist-display-buffer-split-below-and-attach (buf alist)
+  "Display buffer action using `tablist-window-attach'."
+  (let ((window (selected-window))
+        (height (cdr (assq 'window-height alist)))
+        newwin)
+    (when height
+      (when (floatp height)
+        (setq height (round (* height (frame-height)))))
+      (setq height (- (max height window-min-height))))
+    (setq newwin (window--display-buffer
+                  buf
+                  (split-window-below height)
+                  'window alist display-buffer-mark-dedicated))
+    (tablist-window-attach newwin window)
+    newwin))
+
+(defun tablist-generate-sorter (column compare-fn &optional read-fn)
+  "Generate a sort function for `tabulated-list' entries.
+
+Example:
+
+     \(tablist-generate-sorter 0 '< 'string-to-number\)
+
+would create a sort function sorting `tabulated-list-entries' on
+the 0-th column as numbers by the less-than relation."
+
+  (lambda (e1 e2)
+    (funcall compare-fn
+             (funcall (or read-fn 'identity)
+                      (aref (cadr e1) column))
+             (funcall read-fn
+                      (aref (cadr e2) column)))))
+
+(provide 'tablist)
+;;; tablist.el ends here