(defvar which-func-unknown "???"
"String to display in the mode line when current function is unknown.")
(defgroup which-func nil
"Mode to display the current function name in the modeline."
:group 'tools
:version "20.3")
(defcustom which-func-modes
'(emacs-lisp-mode c-mode c++-mode perl-mode cperl-mode makefile-mode
sh-mode fortran-mode f90-mode ada-mode)
"List of major modes for which Which Function mode should be used.
For other modes it is disabled. If this is equal to t,
then Which Function mode is enabled in any major mode that supports it."
:group 'which-func
:type '(choice (const :tag "All modes" t)
(repeat (symbol :tag "Major mode"))))
(defcustom which-func-non-auto-modes nil
"List of major modes where Which Function mode is inactive till Imenu is used.
This means that Which Function mode won't really do anything
until you use Imenu, in these modes. Note that files
larger than `which-func-maxout' behave in this way too;
Which Function mode doesn't do anything until you use Imenu."
:group 'which-func
:type '(repeat (symbol :tag "Major mode")))
(defcustom which-func-maxout 500000
"Don't automatically compute the Imenu menu if buffer is this big or bigger.
Zero means compute the Imenu menu regardless of size."
:group 'which-func
:type 'integer)
(defvar which-func-keymap
(let ((map (make-sparse-keymap)))
(define-key map [mode-line mouse-1] 'beginning-of-defun)
(define-key map [mode-line mouse-2]
(lambda ()
(interactive)
(if (eq (point-min) 1)
(narrow-to-defun)
(widen))))
(define-key map [mode-line mouse-3] 'end-of-defun)
map)
"Keymap to display on mode line which-func.")
(defface which-func
'( (((class color) (min-colors 88) (background light))
:inherit font-lock-function-name-face)
(((class grayscale mono) (background dark))
:inherit font-lock-function-name-face)
(((class color) (background light))
:inherit font-lock-function-name-face)
(((class color) (min-colors 88) (background dark))
:foreground "Blue1")
(((background dark))
:foreground "Blue1")
(t
:foreground "LightSkyBlue"))
"Face used to highlight mode line function names."
:group 'which-func)
(defcustom which-func-format
`("["
(:propertize which-func-current
local-map ,which-func-keymap
face which-func
help-echo "mouse-1: go to beginning, mouse-2: toggle rest visibility, mouse-3: go to end")
"]")
"Format for displaying the function in the mode line."
:group 'which-func
:type 'sexp)
(defvar which-func-cleanup-function nil
"Function to transform a string before displaying it in the mode line.
The function is called with one argument, the string to display.
Its return value is displayed in the modeline.
If nil, no function is called. The default value is nil.
This feature can be useful if Imenu is set up to make more
detailed entries (e.g., containing the argument list of a function),
and you want to simplify them for the mode line
\(e.g., removing the parameter list to just have the function name.)")
(require 'imenu)
(defvar which-func-table (make-hash-table :test 'eq :weakness 'key))
(defconst which-func-current
'(:eval (gethash (selected-window) which-func-table which-func-unknown)))
(defvar which-func-mode nil
"Non-nil means display current function name in mode line.
This makes a difference only if `which-function-mode' is non-nil.")
(make-variable-buffer-local 'which-func-mode)
(add-hook 'find-file-hook 'which-func-ff-hook t)
(defun which-func-ff-hook ()
"File find hook for Which Function mode.
It creates the Imenu index for the buffer, if necessary."
(setq which-func-mode
(and which-function-mode
(or (eq which-func-modes t)
(member major-mode which-func-modes))))
(condition-case nil
(if (and which-func-mode
(not (member major-mode which-func-non-auto-modes))
(or (null which-func-maxout)
(< buffer-saved-size which-func-maxout)
(= which-func-maxout 0)))
(setq imenu--index-alist
(save-excursion (funcall imenu-create-index-function))))
(error
(setq which-func-mode nil))))
(defun which-func-update ()
(which-func-update-1 (selected-window)))
(defun which-func-update-1 (window)
"Update the Which Function mode display for window WINDOW."
(with-selected-window window
(when which-func-mode
(condition-case info
(let ((current (which-function)))
(unless (equal current (gethash window which-func-table))
(puthash window current which-func-table)
(force-mode-line-update)))
(error
(setq which-func-mode nil)
(error "Error in which-func-update: %s" info))))))
(defalias 'which-func-mode 'which-function-mode)
(defvar which-func-update-timer nil)
(define-minor-mode which-function-mode
"Toggle Which Function mode, globally.
When Which Function mode is enabled, the current function name is
continuously displayed in the mode line, in certain major modes.
With prefix ARG, turn Which Function mode on iff arg is positive,
and off otherwise."
:global t :group 'which-func
(if which-function-mode
(progn
(setq which-func-update-timer
(run-with-idle-timer idle-update-delay t 'which-func-update))
(dolist (buf (buffer-list))
(with-current-buffer buf
(setq which-func-mode
(or (eq which-func-modes t)
(member major-mode which-func-modes))))))
(when (timerp which-func-update-timer)
(cancel-timer which-func-update-timer))
(setq which-func-update-timer nil)
(dolist (buf (buffer-list))
(with-current-buffer buf (setq which-func-mode nil)))))
(defvar which-function-imenu-failed nil
"Locally t in a buffer if `imenu--make-index-alist' found nothing there.")
(defvar which-func-functions nil
"List of functions for `which-function' to call with no arguments.
It calls them sequentially, and if any returns non-nil,
`which-function' uses that name and stops looking for the name.")
(defun which-function ()
"Return current function name based on point.
Uses `which-func-functions', `imenu--index-alist'
or `add-log-current-defun-function'.
If no function name is found, return nil."
(let ((name
(run-hook-with-args-until-success 'which-func-functions)))
(when (and (null name)
(boundp 'imenu--index-alist) (null imenu--index-alist)
(null which-function-imenu-failed))
(imenu--make-index-alist t)
(unless imenu--index-alist
(make-local-variable 'which-function-imenu-failed)
(setq which-function-imenu-failed t)))
(when (and (null name)
(boundp 'imenu--index-alist) imenu--index-alist)
(let ((alist imenu--index-alist)
(minoffset (point-max))
offset elem pair mark)
(while alist
(setq elem (car-safe alist)
alist (cdr-safe alist))
(unless (listp (cdr elem))
(setq elem (list elem)))
(while elem
(setq pair (car elem)
elem (cdr elem))
(and (consp pair)
(number-or-marker-p (setq mark (cdr pair)))
(if (>= (setq offset (- (point) mark)) 0)
(if (< offset minoffset) (setq minoffset offset
name (car pair)))
(setq elem nil)))))))
(when (and (null name) (boundp 'add-log-current-defun-function)
add-log-current-defun-function)
(setq name (funcall add-log-current-defun-function)))
(when name
(if which-func-cleanup-function
(funcall which-func-cleanup-function name)
name))))
(provide 'which-func)