;;; manage-minor-mode.el --- Manage your minor-modes easily -*- lexical-binding: t -*- ;; Copyright (C) 2014 by Shingo Fukuyama ;; Copyright (C) 2020-2021 Shen, Jen-Chieh ;; Version: 1.1 ;; Package-Version: 20210108.1832 ;; Package-Commit: e1af20253fbc5a91034ccd01cf00141130c11863 ;; Author: Shingo Fukuyama - http://fukuyama.co ;; Maintainer: Jen-Chieh Shen ;; URL: https://github.com/ShingoFukuyama/manage-minor-mode ;; Created: Mar 8 2014 ;; Keywords: tools minor-mode manage emacs ;; 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 2 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. ;;; Commentary: ;; Usage: ;; 1. M-x manage-minor-mode ;; 2. Minor modes list buffer will appear ;; 3. Then type "RET" key on any minor-mode you want to toggle active/inactive ;;; Code: (require 'cl-lib) (defun manage-minor-mode--active-list () "Get a list of which minor modes are enabled in the current buffer." (let ($list) (mapc (lambda ($mode) (condition-case nil (if (and (symbolp $mode) (symbol-value $mode)) (setq $list (cons $mode $list))) (error nil))) minor-mode-list) (sort $list 'string<))) (defun manage-minor-mode--inactive-list () "Get a list of which minor modes are disabled in the current buffer." (let ($list) (mapc (lambda ($mode) (condition-case nil (if (and (symbolp $mode) (not (symbol-value $mode))) (setq $list (cons $mode $list))) (error nil))) minor-mode-list) (sort $list 'string<))) (defgroup manage-minor-mode nil "Group for manage minor modes" :prefix "manage-minor-mode-" :group 'convenience) (defvar manage-minor-mode-buffer "*manage-minor-mode*") (defface manage-minor-mode-face-active '((t :inherit success :foreground "#33eeee")) "Face for active minor modes" :group 'manage-minor-mode) (defface manage-minor-mode-face-inactive '((t :inherit warning :foreground "#ff6666")) "Face for inactive minor modes" :group 'manage-minor-mode) (defface manage-minor-mode-face-changed '((t :inherit underline :foreground "#eeee00")) "Face for last changed minor modes" :group 'manage-minor-mode) (defcustom manage-minor-mode-always-show-keybind t "Show keybind on the top of the buffer." :group 'manage-minor-mode :type 'boolean) (defvar manage-minor-mode-map (let (($map (make-sparse-keymap))) (define-key $map (kbd "RET") 'manage-minor-mode-toggle) (define-key $map (kbd "") 'manage-minor-mode-toggle) (define-key $map (kbd "g") 'manage-minor-mode-refresh) (define-key $map (kbd "e") 'manage-minor-mode-whole-session-enable) (define-key $map (kbd "d") 'manage-minor-mode-whole-session-disable) (define-key $map (kbd "r") 'manage-minor-mode-whole-session-release) $map)) (defun manage-minor-mode--goto-line ($line) "Go to $LINE considering narrowing." (goto-char (point-min)) (forward-line (- $line 1))) (defsubst manage-minor-mode--enable ($mode) "Enable $MODE." (ignore-errors (funcall $mode 1))) (defsubst manage-minor-mode--disable ($mode) "Disable $MODE." (ignore-errors (funcall $mode 0))) (defvar manage-minor-mode-default nil "Set `minor-mode' active/inactive when `major-mode' changed. ;; Value structure: '((global (on minor-mode minor-mode ...) (off minor-mode minor-mode ...)) (major-mode1 (on minor-mode minor-mode ...) (off minor-mode minor-mode ...)) (major-mode2 (on minor-mode minor-mode ...) (off minor-mode minor-mode ...)) ...) ;; Example (setq manage-minor-mode-default '((global (on rainbow-mode hl-line-mode) (off line-number-mode)) (emacs-lisp-mode (on rainbow-delimiters-mode eldoc-mode show-paren-mode)) (js2-mode (on color-identifiers-mode) (off flycheck-mode))))") ;;;###autoload (defun manage-minor-mode-set () "Manage minor mode set." (when manage-minor-mode-default (let* (($global (assoc-default 'global manage-minor-mode-default)) ($global-on (assoc-default 'on $global)) ($global-off (assoc-default 'off $global)) ($major (assoc-default major-mode manage-minor-mode-default)) ($major-on (assoc-default 'on $major)) ($major-off (assoc-default 'off $major)) ($on (append $global-on $major-on)) ($off (append $global-off $major-off))) (mapc (lambda ($m) (manage-minor-mode--enable $m)) $on) (mapc (lambda ($m) (manage-minor-mode--disable $m)) $off)))) (add-hook 'after-change-major-mode-hook 'manage-minor-mode-set t) (defun manage-minor-mode-whole-session-enable () "Enable minor-mode on the whole session." (interactive) (let (($po (if (eolp) (- (point) 1) (point))) $mode) (when (get-text-property $po 'manage-minor-mode) (setq $mode (intern (thing-at-point 'symbol))) (mapc (lambda ($buf) (with-current-buffer $buf (manage-minor-mode--enable $mode))) (buffer-list)) (let* (($global (assoc-default 'global manage-minor-mode-default)) ($on (assq 'on $global)) ($off (assq 'off $global))) (cond ((not $global) (setq manage-minor-mode-default (cons '(global (on) (off)) manage-minor-mode-default))) ((and $global (not $on) $off) (setf (cdr (assq 'global manage-minor-mode-default)) `((on) ,$off))) ((and $global (not $off) $on) (setf (cdr (assq 'global manage-minor-mode-default)) `(,$on (off))))) (setf (cdr (assq 'on (assoc-default 'global manage-minor-mode-default))) (cons $mode (cdr $on))) (setf (cdr (assq 'off (assoc-default 'global manage-minor-mode-default))) (delq $mode (cdr $off)))) (manage-minor-mode-refresh $mode)))) (defun manage-minor-mode-whole-session-disable () "Disable minor-mode on the whole session." (interactive) (let (($po (if (eolp) (- (point) 1) (point))) $mode) (when (get-text-property $po 'manage-minor-mode) (setq $mode (intern (thing-at-point 'symbol))) (mapc (lambda ($buf) (with-current-buffer $buf (manage-minor-mode--disable $mode))) (buffer-list)) (let* (($global (assoc-default 'global manage-minor-mode-default)) ($on (assq 'on $global)) ($off (assq 'off $global))) (cond ((not $global) (setq manage-minor-mode-default (cons '(global (on) (off)) manage-minor-mode-default))) ((and $global (not $on) $off) (setf (cdr (assq 'global manage-minor-mode-default)) `((on) ,$off))) ((and $global (not $off) $on) (setf (cdr (assq 'global manage-minor-mode-default)) `(,$on (off))))) (setf (cdr (assq 'on (assoc-default 'global manage-minor-mode-default))) (delq $mode (cdr $on))) (setf (cdr (assq 'off (assoc-default 'global manage-minor-mode-default))) (cons $mode (cdr $off)))) (manage-minor-mode-refresh $mode)))) (defun manage-minor-mode-whole-session-release () "Disable minor-mode on the whole session." (interactive) (let ($mode) (when (get-text-property (point) 'manage-minor-mode) (setq $mode (intern (thing-at-point 'symbol))) (let* (($global (assoc-default 'global manage-minor-mode-default)) ($on (assq 'on $global)) ($off (assq 'off $global))) (cond ((not $global) (setq manage-minor-mode-default (cons '(global (on) (off)) manage-minor-mode-default))) ((and $global (not $on) $off) (setf (cdr (assq 'global manage-minor-mode-default)) `((on) ,$off))) ((and $global (not $off) $on) (setf (cdr (assq 'global manage-minor-mode-default)) `(,$on (off))))) (setf (cdr (assq 'on (assoc-default 'global manage-minor-mode-default))) (delq $mode (cdr $on))) (setf (cdr (assq 'off (assoc-default 'global manage-minor-mode-default))) (delq $mode (cdr $off)))) (manage-minor-mode-refresh)))) (defun manage-minor-mode-whole-session-show () "Show globally enable/disable list." (let* (($global (assoc-default 'global manage-minor-mode-default)) ($enable (assoc-default 'on $global)) ($disable (assoc-default 'off $global))) (when $enable (mapc (lambda ($m) (goto-char (point-min)) (when (re-search-forward (concat "\\_<" (format "%s" $m) "\\_>") nil t) (let (($ov (make-overlay (match-beginning 0) (match-end 0)))) (if (not (save-excursion (goto-char (match-end 0)) (eolp))) (delete-char 3)) (overlay-put $ov 'after-string (propertize "[E]" 'face '(:foreground "#ffff00" :background "#555555")))))) $enable)) (when $disable (mapc (lambda ($m) (goto-char (point-min)) (when (re-search-forward (concat "\\_<" (format "%s" $m) "\\_>") nil t) (let (($ov (make-overlay (match-beginning 0) (match-end 0)))) (overlay-put $ov 'after-string (propertize "[D]" 'face '(:foreground "#eeee00" :background "#333333")))))) $disable)))) (defvar manage-minor-mode-window-config nil) (defun manage-minor-mode--buffer-p () "Return non-nil if buffer-p." (equal (buffer-name (current-buffer)) manage-minor-mode-buffer)) (defvar manage-minor-mode-target-info nil) (defun manage-minor-mode--get-target-info ($key) "Get target info $KEY." (car (assoc-default $key manage-minor-mode-target-info))) (defun manage-minor-mode-refresh (&optional $mode-for-highlight $line $column) "Redraw the `*manabe-minor-mode*' buffer." (interactive) (let (($li (or $line (line-number-at-pos))) ($cl (or $column (current-column)))) (set-window-configuration manage-minor-mode-window-config) (if (manage-minor-mode--buffer-p) (with-current-buffer (manage-minor-mode--get-target-info 'buffer) (manage-minor-mode $mode-for-highlight)) (manage-minor-mode $mode-for-highlight)) (manage-minor-mode--goto-line $li) (line-move-to-column $cl) (when (called-interactively-p 'any) (message (format "Refreshed '%s'" manage-minor-mode-buffer))))) (defun manage-minor-mode-toggle () "Toggle a minor-mode status under the cursor." (interactive) (let (($po (if (eolp) (- (point) 1) (point))) ($li (line-number-at-pos)) ($cl (current-column)) ($buf (manage-minor-mode--get-target-info 'buffer))) (cl-case (get-text-property $po 'manage-minor-mode) (active (let (($mode (intern (thing-at-point 'symbol)))) (with-current-buffer $buf (manage-minor-mode--disable $mode) (manage-minor-mode-refresh $mode $li $cl)))) (inactive (let (($mode (intern (thing-at-point 'symbol)))) (with-current-buffer $buf (manage-minor-mode--enable $mode) (manage-minor-mode-refresh $mode $li $cl))))))) (defun manage-minor-mode--kill-buffer ($after-select-window) "Kill $AFTER-SELECT-WINDOW." (when (get-buffer manage-minor-mode-buffer) (kill-buffer manage-minor-mode-buffer) (select-window $after-select-window))) ;;;###autoload (defun manage-minor-mode (&optional $last-toggled-item) "The minor mode. Record for the $LAST-TOGGLED-ITEM." (interactive) (and $last-toggled-item (not (listp $last-toggled-item)) (setq $last-toggled-item (cons $last-toggled-item nil))) (setq manage-minor-mode-window-config (current-window-configuration)) (let* (($act (manage-minor-mode--active-list)) ($inact (manage-minor-mode--inactive-list)) ($max-line (+ (length $act) (length $inact))) ($current-buf (current-buffer)) ($current-win (get-buffer-window $current-buf)) ($major-mode major-mode)) (manage-minor-mode--kill-buffer $current-win) (pop-to-buffer manage-minor-mode-buffer) (setq truncate-lines t) (set (make-local-variable 'manage-minor-mode-target-info) `((buffer ,$current-buf) (window ,$current-win) (major-mode ,$major-mode))) (cl-dotimes (ignored $max-line) (insert (format "| \n"))) ;; Insert inactive minor-modes (goto-char (point-min)) (mapc (lambda ($m) (goto-char (point-at-eol)) (insert (propertize (format "%s" $m) 'face 'manage-minor-mode-face-inactive 'manage-minor-mode 'inactive 'pointer 'hand)) (forward-line)) $inact) ;; Insert active minor-modes (goto-char (point-min)) (mapc (lambda ($m) (insert (concat (propertize (format "%s" $m) 'face 'manage-minor-mode-face-active 'manage-minor-mode 'active 'pointer 'hand) " " ;; 5 spaces )) (forward-line)) $act) ;; header (goto-char (point-min)) (setq header-line-format (concat "buffer: " (propertize (format "%s" $current-buf) 'face 'manage-minor-mode-face-active) " major-mode: " (propertize (format "%s" $major-mode) 'face 'manage-minor-mode-face-active))) (if manage-minor-mode-always-show-keybind (insert (concat "g: Refresh this buffer\n" "e: Enable in the whole session [E]\n" "d: Disable in the whole session [D]\n" "r: Release out of the enable/disable list\n" "RET: Toggle active/inactive\n" "\n"))) (insert (concat "Active" " | " "Inactive" "\n")) ;; Adjust buffer (goto-char (point-min)) (while (re-search-forward "^\\s-*|\\s-*$" nil t) (delete-region (match-beginning 0) (match-end 0))) (align-regexp (point-min) (point-max) "\\(\\s-*\\)|") (manage-minor-mode-whole-session-show) ;; highlight last toggled mode (when $last-toggled-item (mapc (lambda ($m) (goto-char (point-min)) (when (re-search-forward (concat "\\_<" (format "%s" $m) "\\_>") nil t) (put-text-property (match-beginning 0) (match-end 0) 'face 'manage-minor-mode-face-changed))) $last-toggled-item)) (goto-char (point-min)) (when (re-search-forward "^Active" nil t) (goto-char (point-at-bol))) (read-only-mode) (use-local-map manage-minor-mode-map))) (defvar manage-minor-mode-modes-before-bals nil) (defvar manage-minor-mode-bals-exclude-list nil "Minor-modes list evade from the eradication by `manage-minor-mode-bals'. ;; Value structure: '((global (minor-mode1 minor-mode2 ...)) (major-mode (minor-mode1 minor-mode2 ...)) (major-mode (minor-mode1 minor-mode2 ...)) ...) ;; Example (setq manage-minor-mode-bals-exclude-list '((global (recentf-mode global-font-lock-mode delete-selection-mode transient-mark-mode tabbar-mode)) (text-mode (line-number-mode)) (org-mode (line-number-mode blink-cursor-mode))))") ;;;###autoload (defun manage-minor-mode-bals () "Eradicate all minor-modes in the current buffer. This command may cause unexpected effect even to other buffers. However, don't worry, restore command exists: `manage-minor-mode-restore-from-bals'." (interactive) (let (($restore-variable (manage-minor-mode--active-list)) $switched) (cl-flet ((set-restore-variable () (set (make-local-variable 'manage-minor-mode-modes-before-bals) $restore-variable)) (bals () (let (($excludes (append (car (assoc-default 'global manage-minor-mode-bals-exclude-list)) (car (assoc-default (if (manage-minor-mode--buffer-p) (manage-minor-mode--get-target-info 'major-mode) major-mode) manage-minor-mode-bals-exclude-list))))) (mapc (lambda ($m) (unless (memq $m $excludes) (manage-minor-mode--disable $m) (setq $switched (cons $m $switched)))) $restore-variable)))) (if (manage-minor-mode--buffer-p) (progn (with-current-buffer (manage-minor-mode--get-target-info 'buffer) (bals) (set-restore-variable)) (manage-minor-mode-refresh $switched 2 1) (set-restore-variable)) (set-restore-variable) (bals))))) (defun manage-minor-mode-restore-from-bals () "Restore minor modes to before `manage-minor-mode-bals' happened." (interactive) (if (manage-minor-mode--buffer-p) (let ($switched) (with-current-buffer (manage-minor-mode--get-target-info 'buffer) (mapc (lambda ($m) (manage-minor-mode--enable $m) (setq $switched (cons $m $switched))) manage-minor-mode-modes-before-bals)) (manage-minor-mode-refresh $switched 2 1)) (mapc (lambda ($m) (manage-minor-mode--enable $m)) manage-minor-mode-modes-before-bals))) ;; Mouse click minor-mode in mode-line to popup "Manage minor modes" (defadvice popup-menu (before manage-minor-mode-add-for-popup-menu disable) "Advice `popup-menu'." (ad-set-arg 0 (append (ad-get-arg 0) '((manage-minor-mode menu-item "Manage minor modes" (lambda () (interactive) (manage-minor-mode))))))) (defadvice minor-mode-menu-from-indicator (around manage-minor-mode-add-mode-line-menu activate) "Advice for `minor-mode-menu-from-indicator'." (ad-enable-advice 'popup-menu 'before 'manage-minor-mode-add-for-popup-menu) (ad-activate 'popup-menu) ad-do-it (ad-disable-advice 'popup-menu 'before 'manage-minor-mode-add-for-popup-menu) (ad-activate 'popup-menu)) (provide 'manage-minor-mode) ;;; manage-minor-mode.el ends here