+(defun ibuffer-generate-filter-groups (bmarklist)
+ (let ((filter-group-alist (append ibuffer-filter-groups
+ (list (cons "Default" nil)))))
+;; (dolist (hidden ibuffer-hidden-filter-groups)
+;; (setq filter-group-alist (ibuffer-delete-alist
+;; hidden filter-group-alist)))
+ (let ((vec (make-vector (length filter-group-alist) nil))
+ (i 0))
+ (dolist (filtergroup filter-group-alist)
+ (let ((filterset (cdr filtergroup)))
+ (multiple-value-bind (hip-crowd lamers)
+ (ibuffer-split-list (lambda (bufmark)
+ (ibuffer-included-in-filters-p (car bufmark)
+ filterset))
+ bmarklist)
+ (aset vec i hip-crowd)
+ (incf i)
+ (setq bmarklist lamers))))
+ (let ((ret nil))
+ (dotimes (j i ret)
+ (push (cons (car (nth j filter-group-alist))
+ (aref vec j))
+ ret))))))
+
+;;;###autoload
+(defun ibuffer-filters-to-filter-group (name)
+ "Make the current filters into a filtering group."
+ (interactive "sName for filtering group: ")
+ (when (null ibuffer-filtering-qualifiers)
+ (error "No filters in effect"))
+ (push (cons name ibuffer-filtering-qualifiers) ibuffer-filter-groups)
+ (ibuffer-filter-disable))
+
+;;;###autoload
+(defun ibuffer-set-filter-groups-by-mode ()
+ "Set the current filter groups to filter by mode."
+ (interactive)
+ (setq ibuffer-filter-groups
+ (mapcar (lambda (mode)
+ (cons (format "%s" mode) `((mode . ,mode))))
+ (let ((modes
+ (ibuffer-remove-duplicates
+ (mapcar (lambda (buf)
+ (with-current-buffer buf major-mode))
+ (buffer-list)))))
+ (if ibuffer-view-ibuffer
+ modes
+ (delq 'ibuffer-mode modes)))))
+ (ibuffer-update nil t))
+
+;;;###autoload
+(defun ibuffer-pop-filter-group ()
+ "Remove the first filter group."
+ (interactive)
+ (when (null ibuffer-filter-groups)
+ (error "No filter groups active"))
+ (setq ibuffer-hidden-filter-groups
+ (delete (pop ibuffer-filter-groups)
+ ibuffer-hidden-filter-groups))
+ (ibuffer-update nil t))
+
+(defun ibuffer-read-filter-group-name (msg &optional nodefault noerror)
+ (when (and (not noerror) (null ibuffer-filter-groups))
+ (error "No filter groups active"))
+ (let ((groups (mapcar #'car ibuffer-filter-groups)))
+ (completing-read msg (if nodefault
+ groups
+ (cons "Default" groups))
+ nil t)))
+
+;;;###autoload
+(defun ibuffer-decompose-filter-group (group)
+ "Decompose the filter group GROUP into active filters."
+ (interactive
+ (list (ibuffer-read-filter-group-name "Decompose filter group: " t)))
+ (let ((data (cdr (assoc group ibuffer-filter-groups))))
+ (setq ibuffer-filter-groups (ibuffer-delete-alist
+ group ibuffer-filter-groups)
+ ibuffer-filtering-qualifiers data))
+ (ibuffer-update nil t))
+
+;;;###autoload
+(defun ibuffer-clear-filter-groups ()
+ "Remove all filter groups."
+ (interactive)
+ (setq ibuffer-filter-groups nil
+ ibuffer-hidden-filter-groups nil)
+ (ibuffer-update nil t))
+
+(defun ibuffer-current-filter-groups-with-position ()
+ (save-excursion
+ (goto-char (point-min))
+ (let ((pos nil)
+ (result nil))
+ (while (and (not (eobp))
+ (setq pos (next-single-property-change
+ (point) 'ibuffer-filter-group-name)))
+ (goto-char pos)
+ (push (cons (get-text-property (point) 'ibuffer-filter-group-name)
+ pos)
+ result)
+ (goto-char (next-single-property-change
+ pos 'ibuffer-filter-group-name)))
+ (nreverse result))))
+
+;;;###autoload
+(defun ibuffer-jump-to-filter-group (name)
+ "Move point to the filter group whose name is NAME."
+ (interactive
+ (list (ibuffer-read-filter-group-name "Jump to filter group: ")))
+ (ibuffer-aif (assoc name (ibuffer-current-filter-groups-with-position))
+ (goto-char (cdr it))
+ (error "No filter group with name %s" name)))
+
+;;;###autoload
+(defun ibuffer-kill-filter-group (name)
+ "Kill the filter group named NAME.
+The group will be added to `ibuffer-filter-group-kill-ring'."
+ (interactive (list (ibuffer-read-filter-group-name "Kill filter group: " t)))
+ (when (equal name "Default")
+ (error "Can't kill default filter group"))
+ (ibuffer-aif (assoc name ibuffer-filter-groups)
+ (progn
+ (push (copy-tree it) ibuffer-filter-group-kill-ring)
+ (setq ibuffer-filter-groups (ibuffer-delete-alist
+ name ibuffer-filter-groups))
+ (setq ibuffer-hidden-filter-groups
+ (delete name ibuffer-hidden-filter-groups)))
+ (error "No filter group with name \"%s\"" name))
+ (ibuffer-update nil t))
+
+;;;###autoload
+(defun ibuffer-kill-line (&optional arg interactive-p)
+ "Kill the filter group at point.
+See also `ibuffer-kill-filter-group'."
+ (interactive "P\np")
+ (ibuffer-aif (save-excursion
+ (ibuffer-forward-line 0)
+ (get-text-property (point) 'ibuffer-filter-group-name))
+ (progn
+ (ibuffer-kill-filter-group it))
+ (funcall (if interactive-p #'call-interactively #'funcall)
+ #'kill-line arg)))
+
+(defun ibuffer-insert-filter-group-before (newgroup group)
+ (let* ((found nil)
+ (pos (let ((groups (mapcar #'car ibuffer-filter-groups))
+ (res 0))
+ (while groups
+ (if (equal (car groups) group)
+ (setq found t
+ groups nil)
+ (incf res)
+ (setq groups (cdr groups))))
+ res)))
+ (cond ((not found)
+ (setq ibuffer-filter-groups
+ (nconc ibuffer-filter-groups (list newgroup))))
+ ((zerop pos)
+ (push newgroup ibuffer-filter-groups))
+ (t
+ (let ((cell (nthcdr pos ibuffer-filter-groups)))
+ (setf (cdr cell) (cons (car cell) (cdr cell)))
+ (setf (car cell) newgroup))))))
+
+;;;###autoload
+(defun ibuffer-yank ()
+ "Yank the last killed filter group before group at point."
+ (interactive)
+ (ibuffer-yank-filter-group
+ (or (get-text-property (point) 'ibuffer-filter-group-name)
+ (get-text-property (point) 'ibuffer-filter-group)
+ (error "No filter group at point"))))
+
+;;;###autoload
+(defun ibuffer-yank-filter-group (name)
+ "Yank the last killed filter group before group named NAME."
+ (interactive (list (ibuffer-read-filter-group-name
+ "Yank filter group before group: ")))
+ (unless ibuffer-filter-group-kill-ring
+ (error "The Ibuffer filter group kill-ring is empty"))
+ (save-excursion
+ (ibuffer-forward-line 0)
+ (ibuffer-insert-filter-group-before (pop ibuffer-filter-group-kill-ring)
+ name))
+ (ibuffer-update nil t))
+
+;;;###autoload
+(defun ibuffer-save-filter-groups (name groups)
+ "Save all active filter groups GROUPS as NAME.
+They are added to `ibuffer-saved-filter-groups'. Interactively,
+prompt for NAME, and use the current filters."
+ (interactive
+ (if (null ibuffer-filter-groups)
+ (error "No filter groups active")
+ (list
+ (read-from-minibuffer "Save current filter groups as: ")
+ ibuffer-filter-groups)))
+ (ibuffer-aif (assoc name ibuffer-saved-filter-groups)
+ (setcdr it groups)
+ (push (cons name groups) ibuffer-saved-filter-groups))
+ (ibuffer-maybe-save-stuff)
+ (ibuffer-update-mode-name))
+
+;;;###autoload
+(defun ibuffer-delete-saved-filter-groups (name)
+ "Delete saved filter groups with NAME.
+They are removed from `ibuffer-saved-filter-groups'."
+ (interactive
+ (list
+ (if (null ibuffer-saved-filter-groups)
+ (error "No saved filter groups")
+ (completing-read "Delete saved filter group: "
+ ibuffer-saved-filter-groups nil t))))
+ (setq ibuffer-saved-filter-groups
+ (ibuffer-delete-alist name ibuffer-saved-filter-groups))
+ (ibuffer-maybe-save-stuff)
+ (ibuffer-update nil t))
+
+;;;###autoload
+(defun ibuffer-switch-to-saved-filter-groups (name)
+ "Set this buffer's filter groups to saved version with NAME.
+The value from `ibuffer-saved-filters' is used.
+If prefix argument ADD is non-nil, then add the saved filters instead
+of replacing the current filters."
+ (interactive
+ (list
+ (if (null ibuffer-saved-filter-groups)
+ (error "No saved filters")
+ (completing-read "Switch to saved filter group: "
+ ibuffer-saved-filter-groups nil t))))
+ (setq ibuffer-filter-groups (cdr (assoc name ibuffer-saved-filter-groups))
+ ibuffer-hidden-filter-groups nil)
+ (ibuffer-update nil t))
+