| 1 | ;;; imenu.el --- Framework for mode-specific buffer indexes. |
| 2 | |
| 3 | ;; Copyright (C) 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc. |
| 4 | |
| 5 | ;; Author: Ake Stenhoff <etxaksf@aom.ericsson.se> |
| 6 | ;; Lars Lindberg <lli@sypro.cap.se> |
| 7 | ;; Maintainer: FSF |
| 8 | ;; Created: 8 Feb 1994 |
| 9 | ;; Keywords: tools convenience |
| 10 | |
| 11 | ;; This file is part of GNU Emacs. |
| 12 | |
| 13 | ;; GNU Emacs is free software; you can redistribute it and/or modify |
| 14 | ;; it under the terms of the GNU General Public License as published by |
| 15 | ;; the Free Software Foundation; either version 2, or (at your option) |
| 16 | ;; any later version. |
| 17 | |
| 18 | ;; GNU Emacs is distributed in the hope that it will be useful, |
| 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 21 | ;; GNU General Public License for more details. |
| 22 | |
| 23 | ;; You should have received a copy of the GNU General Public License |
| 24 | ;; along with GNU Emacs; see the file COPYING. If not, write to the |
| 25 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| 26 | ;; Boston, MA 02111-1307, USA. |
| 27 | |
| 28 | ;;; Commentary: |
| 29 | |
| 30 | ;; Purpose of this package: |
| 31 | ;; To present a framework for mode-specific buffer indexes. |
| 32 | ;; A buffer index is an alist of names and buffer positions. |
| 33 | ;; For instance all functions in a C-file and their positions. |
| 34 | ;; |
| 35 | ;; It is documented in the Emacs Lisp manual. |
| 36 | ;; |
| 37 | ;; How it works: |
| 38 | |
| 39 | ;; A mode-specific function is called to generate the index. It is |
| 40 | ;; then presented to the user, who can choose from this index. |
| 41 | ;; |
| 42 | ;; The package comes with a set of example functions for how to |
| 43 | ;; utilize this package. |
| 44 | |
| 45 | ;; There are *examples* for index gathering functions/regular |
| 46 | ;; expressions for C/C++ and Lisp/Emacs Lisp but it is easy to |
| 47 | ;; customize for other modes. A function for jumping to the chosen |
| 48 | ;; index position is also supplied. |
| 49 | |
| 50 | ;;; History: |
| 51 | ;; Thanks go to |
| 52 | ;; [simon] - Simon Leinen simon@lia.di.epfl.ch |
| 53 | ;; [dean] - Dean Andrews ada@unison.com |
| 54 | ;; [alon] - Alon Albert al@mercury.co.il |
| 55 | ;; [greg] - Greg Thompson gregt@porsche.visix.COM |
| 56 | ;; [wolfgang] - Wolfgang Bangerth zcg51122@rpool1.rus.uni-stuttgart.de |
| 57 | ;; [kai] - Kai Grossjohann grossjoh@linus.informatik.uni-dortmund.de |
| 58 | ;; [david] - David M. Smith dsmith@stats.adelaide.edu.au |
| 59 | ;; [christian] - Christian Egli Christian.Egli@hcsd.hac.com |
| 60 | ;; [karl] - Karl Fogel kfogel@floss.life.uiuc.edu |
| 61 | |
| 62 | ;;; Code |
| 63 | |
| 64 | (eval-when-compile (require 'cl)) |
| 65 | |
| 66 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 67 | ;;; |
| 68 | ;;; Customizable variables |
| 69 | ;;; |
| 70 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 71 | |
| 72 | (defgroup imenu nil |
| 73 | "Mode-specific buffer indexes." |
| 74 | :group 'matching |
| 75 | :group 'frames |
| 76 | :group 'convenience |
| 77 | :link '(custom-manual "(elisp)Imenu")) |
| 78 | |
| 79 | (defcustom imenu-use-markers t |
| 80 | "*Non-nil means use markers instead of integers for Imenu buffer positions. |
| 81 | |
| 82 | Setting this to nil makes Imenu work a little faster but editing the |
| 83 | buffer will make the generated index positions wrong. |
| 84 | |
| 85 | This might not yet be honored by all index-building functions." |
| 86 | :type 'boolean |
| 87 | :group 'imenu) |
| 88 | |
| 89 | |
| 90 | (defcustom imenu-max-item-length 60 |
| 91 | "*If a number, truncate Imenu entries to that length." |
| 92 | :type '(choice integer |
| 93 | (const :tag "Unlimited")) |
| 94 | :group 'imenu) |
| 95 | |
| 96 | (defcustom imenu-auto-rescan nil |
| 97 | "*Non-nil means Imenu should always rescan the buffers." |
| 98 | :type 'boolean |
| 99 | :group 'imenu) |
| 100 | |
| 101 | (defcustom imenu-auto-rescan-maxout 60000 |
| 102 | "*Imenu auto-rescan is disabled in buffers larger than this size (in bytes). |
| 103 | This variable is buffer-local." |
| 104 | :type 'integer |
| 105 | :group 'imenu) |
| 106 | |
| 107 | (defcustom imenu-always-use-completion-buffer-p nil |
| 108 | "*Set this to non-nil for displaying the index in a completion buffer. |
| 109 | |
| 110 | `never' means never automatically display a listing of any kind. |
| 111 | A value of nil (the default) means display the index as a mouse menu |
| 112 | if the mouse was used to invoke `imenu'. |
| 113 | Another non-nil value means always display the index in a completion buffer." |
| 114 | :type '(choice (const :tag "On Mouse" nil) |
| 115 | (const :tag "Never" never) |
| 116 | (other :tag "Always" t)) |
| 117 | :group 'imenu) |
| 118 | |
| 119 | (defcustom imenu-after-jump-hook nil |
| 120 | "*Hooks called after jumping to a place in the buffer. |
| 121 | |
| 122 | Useful things to use here include `reposition-window', `recenter', and |
| 123 | \(lambda () (recenter 0)) to show at top of screen." |
| 124 | :type 'hook |
| 125 | :group 'imenu) |
| 126 | |
| 127 | ;;;###autoload |
| 128 | (defcustom imenu-sort-function nil |
| 129 | "*The function to use for sorting the index mouse-menu. |
| 130 | |
| 131 | Affects only the mouse index menu. |
| 132 | |
| 133 | Set this to nil if you don't want any sorting (faster). |
| 134 | The items in the menu are then presented in the order they were found |
| 135 | in the buffer. |
| 136 | |
| 137 | Set it to `imenu--sort-by-name' if you want alphabetic sorting. |
| 138 | |
| 139 | The function should take two arguments and return t if the first |
| 140 | element should come before the second. The arguments are cons cells; |
| 141 | \(NAME . POSITION). Look at `imenu--sort-by-name' for an example." |
| 142 | :type '(choice (const :tag "No sorting" nil) |
| 143 | (const :tag "Sort by name" imenu--sort-by-name) |
| 144 | (function :tag "Another function")) |
| 145 | :group 'imenu) |
| 146 | |
| 147 | (defcustom imenu-max-items 25 |
| 148 | "*Maximum number of elements in a mouse menu for Imenu." |
| 149 | :type 'integer |
| 150 | :group 'imenu) |
| 151 | |
| 152 | (defcustom imenu-scanning-message "Scanning buffer for index (%3d%%)" |
| 153 | "*Progress message during the index scanning of the buffer. |
| 154 | If non-nil, user gets a message during the scanning of the buffer. |
| 155 | |
| 156 | Relevant only if the mode-specific function that creates the buffer |
| 157 | index use `imenu-progress-message', and not useful if that is fast, in |
| 158 | which case you might as well set this to nil." |
| 159 | :type '(choice string |
| 160 | (const :tag "None" nil)) |
| 161 | :group 'imenu) |
| 162 | |
| 163 | (defcustom imenu-space-replacement "." |
| 164 | "*The replacement string for spaces in index names. |
| 165 | Used when presenting the index in a completion buffer to make the |
| 166 | names work as tokens." |
| 167 | :type 'string |
| 168 | :group 'imenu) |
| 169 | |
| 170 | (defcustom imenu-level-separator ":" |
| 171 | "*The separator between index names of different levels. |
| 172 | Used for making mouse-menu titles and for flattening nested indexes |
| 173 | with name concatenation." |
| 174 | :type 'string |
| 175 | :group 'imenu) |
| 176 | |
| 177 | ;;;###autoload |
| 178 | (defvar imenu-generic-expression nil |
| 179 | "The regex pattern to use for creating a buffer index. |
| 180 | |
| 181 | If non-nil this pattern is passed to `imenu--generic-function' |
| 182 | to create a buffer index. |
| 183 | |
| 184 | The value should be an alist with elements that look like this: |
| 185 | (MENU-TITLE REGEXP INDEX) |
| 186 | or like this: |
| 187 | (MENU-TITLE REGEXP INDEX FUNCTION ARGUMENTS...) |
| 188 | with zero or more ARGUMENTS. The former format creates a simple element in |
| 189 | the index alist when it matches; the latter creates a special element |
| 190 | of the form (NAME POSITION-MARKER FUNCTION ARGUMENTS...) |
| 191 | with FUNCTION and ARGUMENTS copied from `imenu-generic-expression'. |
| 192 | |
| 193 | MENU-TITLE is a string used as the title for the submenu or nil if the |
| 194 | entries are not nested. |
| 195 | |
| 196 | REGEXP is a regexp that should match a construct in the buffer that is |
| 197 | to be displayed in the menu; i.e., function or variable definitions, |
| 198 | etc. It contains a substring which is the name to appear in the |
| 199 | menu. See the info section on Regexps for more information. |
| 200 | |
| 201 | INDEX points to the substring in REGEXP that contains the name (of the |
| 202 | function, variable or type) that is to appear in the menu. |
| 203 | |
| 204 | The variable is buffer-local. |
| 205 | |
| 206 | The variable `imenu-case-fold-search' determines whether or not the |
| 207 | regexp matches are case sensitive. and `imenu-syntax-alist' can be |
| 208 | used to alter the syntax table for the search. |
| 209 | |
| 210 | For example, see the value of `lisp-imenu-generic-expression' used by |
| 211 | `lisp-mode' and `emacs-lisp-mode' with `imenu-syntax-alist' set |
| 212 | locally to give the characters which normally have \"punctuation\" |
| 213 | syntax \"word\" syntax during matching.") |
| 214 | |
| 215 | ;;;###autoload |
| 216 | (make-variable-buffer-local 'imenu-generic-expression) |
| 217 | |
| 218 | ;;;; Hooks |
| 219 | |
| 220 | ;;;###autoload |
| 221 | (defvar imenu-create-index-function 'imenu-default-create-index-function |
| 222 | "The function to use for creating a buffer index. |
| 223 | |
| 224 | It should be a function that takes no arguments and returns an index |
| 225 | of the current buffer as an alist. |
| 226 | |
| 227 | Simple elements in the alist look like (INDEX-NAME . INDEX-POSITION). |
| 228 | Special elements look like (INDEX-NAME INDEX-POSITION FUNCTION ARGUMENTS...). |
| 229 | A nested sub-alist element looks like (INDEX-NAME SUB-ALIST). |
| 230 | The function `imenu--subalist-p' tests an element and returns t |
| 231 | if it is a sub-alist. |
| 232 | |
| 233 | This function is called within a `save-excursion'. |
| 234 | |
| 235 | The variable is buffer-local.") |
| 236 | ;;;###autoload |
| 237 | (make-variable-buffer-local 'imenu-create-index-function) |
| 238 | |
| 239 | ;;;###autoload |
| 240 | (defvar imenu-prev-index-position-function 'beginning-of-defun |
| 241 | "Function for finding the next index position. |
| 242 | |
| 243 | If `imenu-create-index-function' is set to |
| 244 | `imenu-default-create-index-function', then you must set this variable |
| 245 | to a function that will find the next index, looking backwards in the |
| 246 | file. |
| 247 | |
| 248 | The function should leave point at the place to be connected to the |
| 249 | index and it should return nil when it doesn't find another index. |
| 250 | |
| 251 | This variable is local in all buffers.") |
| 252 | ;;;###autoload |
| 253 | (make-variable-buffer-local 'imenu-prev-index-position-function) |
| 254 | |
| 255 | ;;;###autoload |
| 256 | (defvar imenu-extract-index-name-function nil |
| 257 | "Function for extracting the index item name, given a position. |
| 258 | |
| 259 | This function is called after `imenu-prev-index-position-function' |
| 260 | finds a position for an index item, with point at that position. |
| 261 | It should return the name for that index item. |
| 262 | |
| 263 | This variable is local in all buffers.") |
| 264 | ;;;###autoload |
| 265 | (make-variable-buffer-local 'imenu-extract-index-name-function) |
| 266 | |
| 267 | ;;;###autoload |
| 268 | (defvar imenu-name-lookup-function nil |
| 269 | "Function to compare string with index item. |
| 270 | |
| 271 | This function will be called with two strings, and should return |
| 272 | non-nil if they match. |
| 273 | |
| 274 | If nil, comparison is done with `string='. |
| 275 | Set this to some other function for more advanced comparisons, |
| 276 | such as \"begins with\" or \"name matches and number of |
| 277 | arguments match\". |
| 278 | |
| 279 | This variable is local in all buffers.") |
| 280 | ;;;###autoload |
| 281 | (make-variable-buffer-local 'imenu-name-lookup-function) |
| 282 | |
| 283 | ;;;###autoload |
| 284 | (defvar imenu-default-goto-function 'imenu-default-goto-function |
| 285 | "The default function called when selecting an Imenu item. |
| 286 | The function in this variable is called when selecting a normal index-item.") |
| 287 | ;;;###autoload |
| 288 | (make-variable-buffer-local 'imenu-default-goto-function) |
| 289 | |
| 290 | |
| 291 | (defun imenu--subalist-p (item) |
| 292 | (and (consp (cdr item)) (listp (cadr item)) |
| 293 | (not (eq (car (cadr item)) 'lambda)))) |
| 294 | |
| 295 | ;; Macro to display a progress message. |
| 296 | ;; RELPOS is the relative position to display. |
| 297 | ;; If RELPOS is nil, then the relative position in the buffer |
| 298 | ;; is calculated. |
| 299 | ;; PREVPOS is the variable in which we store the last position displayed. |
| 300 | (defmacro imenu-progress-message (prevpos &optional relpos reverse) |
| 301 | `(and |
| 302 | imenu-scanning-message |
| 303 | (let ((pos ,(if relpos |
| 304 | relpos |
| 305 | `(imenu--relative-position ,reverse)))) |
| 306 | (if ,(if relpos t |
| 307 | `(> pos (+ 5 ,prevpos))) |
| 308 | (progn |
| 309 | (message imenu-scanning-message pos) |
| 310 | (setq ,prevpos pos)))))) |
| 311 | |
| 312 | |
| 313 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 314 | ;;;; |
| 315 | ;;;; Some examples of functions utilizing the framework of this |
| 316 | ;;;; package. |
| 317 | ;;;; |
| 318 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 319 | |
| 320 | ;; Return the current/previous sexp and the location of the sexp (its |
| 321 | ;; beginning) without moving the point. |
| 322 | (defun imenu-example--name-and-position () |
| 323 | (save-excursion |
| 324 | (forward-sexp -1) |
| 325 | ;; [ydi] modified for imenu-use-markers |
| 326 | (let ((beg (if imenu-use-markers (point-marker) (point))) |
| 327 | (end (progn (forward-sexp) (point)))) |
| 328 | (cons (buffer-substring beg end) |
| 329 | beg)))) |
| 330 | |
| 331 | ;;; |
| 332 | ;;; Lisp |
| 333 | ;;; |
| 334 | |
| 335 | (defun imenu-example--lisp-extract-index-name () |
| 336 | ;; Example of a candidate for `imenu-extract-index-name-function'. |
| 337 | ;; This will generate a flat index of definitions in a lisp file. |
| 338 | (save-match-data |
| 339 | (and (looking-at "(def") |
| 340 | (condition-case nil |
| 341 | (progn |
| 342 | (down-list 1) |
| 343 | (forward-sexp 2) |
| 344 | (let ((beg (point)) |
| 345 | (end (progn (forward-sexp -1) (point)))) |
| 346 | (buffer-substring beg end))) |
| 347 | (error nil))))) |
| 348 | |
| 349 | (defun imenu-example--create-lisp-index () |
| 350 | ;; Example of a candidate for `imenu-create-index-function'. |
| 351 | ;; It will generate a nested index of definitions. |
| 352 | (let ((index-alist '()) |
| 353 | (index-var-alist '()) |
| 354 | (index-type-alist '()) |
| 355 | (index-unknown-alist '()) |
| 356 | prev-pos) |
| 357 | (goto-char (point-max)) |
| 358 | (imenu-progress-message prev-pos 0) |
| 359 | ;; Search for the function |
| 360 | (while (beginning-of-defun) |
| 361 | (imenu-progress-message prev-pos nil t) |
| 362 | (save-match-data |
| 363 | (and (looking-at "(def") |
| 364 | (save-excursion |
| 365 | (down-list 1) |
| 366 | (cond |
| 367 | ((looking-at "def\\(var\\|const\\)") |
| 368 | (forward-sexp 2) |
| 369 | (push (imenu-example--name-and-position) |
| 370 | index-var-alist)) |
| 371 | ((looking-at "def\\(un\\|subst\\|macro\\|advice\\)") |
| 372 | (forward-sexp 2) |
| 373 | (push (imenu-example--name-and-position) |
| 374 | index-alist)) |
| 375 | ((looking-at "def\\(type\\|struct\\|class\\|ine-condition\\)") |
| 376 | (forward-sexp 2) |
| 377 | (if (= (char-after (1- (point))) ?\)) |
| 378 | (progn |
| 379 | (forward-sexp -1) |
| 380 | (down-list 1) |
| 381 | (forward-sexp 1))) |
| 382 | (push (imenu-example--name-and-position) |
| 383 | index-type-alist)) |
| 384 | (t |
| 385 | (forward-sexp 2) |
| 386 | (push (imenu-example--name-and-position) |
| 387 | index-unknown-alist))))))) |
| 388 | (imenu-progress-message prev-pos 100) |
| 389 | (and index-var-alist |
| 390 | (push (cons "Variables" index-var-alist) |
| 391 | index-alist)) |
| 392 | (and index-type-alist |
| 393 | (push (cons "Types" index-type-alist) |
| 394 | index-alist)) |
| 395 | (and index-unknown-alist |
| 396 | (push (cons "Syntax-unknown" index-unknown-alist) |
| 397 | index-alist)) |
| 398 | index-alist)) |
| 399 | |
| 400 | ;; Regular expression to find C functions |
| 401 | (defvar imenu-example--function-name-regexp-c |
| 402 | (concat |
| 403 | "^[a-zA-Z0-9]+[ \t]?" ; type specs; there can be no |
| 404 | "\\([a-zA-Z0-9_*]+[ \t]+\\)?" ; more than 3 tokens, right? |
| 405 | "\\([a-zA-Z0-9_*]+[ \t]+\\)?" |
| 406 | "\\([*&]+[ \t]*\\)?" ; pointer |
| 407 | "\\([a-zA-Z0-9_*]+\\)[ \t]*(" ; name |
| 408 | )) |
| 409 | |
| 410 | (defun imenu-example--create-c-index (&optional regexp) |
| 411 | (let ((index-alist '()) |
| 412 | prev-pos char) |
| 413 | (goto-char (point-min)) |
| 414 | (imenu-progress-message prev-pos 0) |
| 415 | ;; Search for the function |
| 416 | (save-match-data |
| 417 | (while (re-search-forward |
| 418 | (or regexp imenu-example--function-name-regexp-c) |
| 419 | nil t) |
| 420 | (imenu-progress-message prev-pos) |
| 421 | (backward-up-list 1) |
| 422 | (save-excursion |
| 423 | (goto-char (scan-sexps (point) 1)) |
| 424 | (setq char (following-char))) |
| 425 | ;; Skip this function name if it is a prototype declaration. |
| 426 | (if (not (eq char ?\;)) |
| 427 | (push (imenu-example--name-and-position) index-alist)))) |
| 428 | (imenu-progress-message prev-pos 100) |
| 429 | (nreverse index-alist))) |
| 430 | |
| 431 | |
| 432 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 433 | ;;; |
| 434 | ;;; Internal variables |
| 435 | ;;; |
| 436 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 437 | |
| 438 | ;; The item to use in the index for rescanning the buffer. |
| 439 | (defconst imenu--rescan-item '("*Rescan*" . -99)) |
| 440 | |
| 441 | ;; The latest buffer index. |
| 442 | ;; Buffer local. |
| 443 | (defvar imenu--index-alist nil |
| 444 | "The buffer index computed for this buffer in Imenu. |
| 445 | Simple elements in the alist look like (INDEX-NAME . INDEX-POSITION). |
| 446 | Special elements look like (INDEX-NAME INDEX-POSITION FUNCTION ARGUMENTS...). |
| 447 | A nested sub-alist element looks like (INDEX-NAME SUB-ALIST). |
| 448 | |
| 449 | This variable is local in all buffers, once set.") |
| 450 | |
| 451 | (make-variable-buffer-local 'imenu--index-alist) |
| 452 | |
| 453 | (defvar imenu--last-menubar-index-alist nil |
| 454 | "The latest buffer index used to update the menu bar menu.") |
| 455 | |
| 456 | (make-variable-buffer-local 'imenu--last-menubar-index-alist) |
| 457 | |
| 458 | ;; History list for 'jump-to-function-in-buffer'. |
| 459 | ;; Making this buffer local caused it not to work! |
| 460 | (defvar imenu--history-list nil) |
| 461 | |
| 462 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 463 | ;;; |
| 464 | ;;; Internal support functions |
| 465 | ;;; |
| 466 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 467 | |
| 468 | ;;; |
| 469 | ;;; Sort function |
| 470 | ;;; Sorts the items depending on their index name. |
| 471 | ;;; An item looks like (NAME . POSITION). |
| 472 | ;;; |
| 473 | (defun imenu--sort-by-name (item1 item2) |
| 474 | (string-lessp (car item1) (car item2))) |
| 475 | |
| 476 | (defun imenu--sort-by-position (item1 item2) |
| 477 | (< (cdr item1) (cdr item2))) |
| 478 | |
| 479 | (defun imenu--relative-position (&optional reverse) |
| 480 | ;; Support function to calculate relative position in buffer |
| 481 | ;; Beginning of buffer is 0 and end of buffer is 100 |
| 482 | ;; If REVERSE is non-nil then the beginning is 100 and the end is 0. |
| 483 | (let ((pos (point)) |
| 484 | (total (buffer-size))) |
| 485 | (and reverse (setq pos (- total pos))) |
| 486 | (if (> total 50000) |
| 487 | ;; Avoid overflow from multiplying by 100! |
| 488 | (/ (1- pos) (max (/ total 100) 1)) |
| 489 | (/ (* 100 (1- pos)) (max total 1))))) |
| 490 | |
| 491 | ;; Split LIST into sublists of max length N. |
| 492 | ;; Example (imenu--split '(1 2 3 4 5 6 7 8) 3)-> '((1 2 3) (4 5 6) (7 8)) |
| 493 | (defun imenu--split (list n) |
| 494 | (let ((remain list) |
| 495 | (result '()) |
| 496 | (sublist '()) |
| 497 | (i 0)) |
| 498 | (while remain |
| 499 | (push (pop remain) sublist) |
| 500 | (incf i) |
| 501 | (and (= i n) |
| 502 | ;; We have finished a sublist |
| 503 | (progn (push (nreverse sublist) result) |
| 504 | (setq i 0) |
| 505 | (setq sublist '())))) |
| 506 | ;; There might be a sublist (if the length of LIST mod n is != 0) |
| 507 | ;; that has to be added to the result list. |
| 508 | (and sublist |
| 509 | (push (nreverse sublist) result)) |
| 510 | (nreverse result))) |
| 511 | |
| 512 | ;;; Split the alist MENULIST into a nested alist, if it is long enough. |
| 513 | ;;; In any case, add TITLE to the front of the alist. |
| 514 | (defun imenu--split-menu (menulist title) |
| 515 | (let (keep-at-top tail) |
| 516 | (if (memq imenu--rescan-item menulist) |
| 517 | (setq keep-at-top (cons imenu--rescan-item nil) |
| 518 | menulist (delq imenu--rescan-item menulist))) |
| 519 | (setq tail menulist) |
| 520 | (while tail |
| 521 | (if (imenu--subalist-p (car tail)) |
| 522 | (setq keep-at-top (cons (car tail) keep-at-top) |
| 523 | menulist (delq (car tail) menulist))) |
| 524 | (setq tail (cdr tail))) |
| 525 | (if imenu-sort-function |
| 526 | (setq menulist |
| 527 | (sort |
| 528 | (let ((res nil) |
| 529 | (oldlist menulist)) |
| 530 | ;; Copy list method from the cl package `copy-list' |
| 531 | (while (consp oldlist) (push (pop oldlist) res)) |
| 532 | (if res ; in case, e.g. no functions defined |
| 533 | (prog1 (nreverse res) (setcdr res oldlist)))) |
| 534 | imenu-sort-function))) |
| 535 | (if (> (length menulist) imenu-max-items) |
| 536 | (let ((count 0)) |
| 537 | (setq menulist |
| 538 | (mapcar |
| 539 | (function |
| 540 | (lambda (menu) |
| 541 | (cons (format "From: %s" (caar menu)) menu))) |
| 542 | (imenu--split menulist imenu-max-items))))) |
| 543 | (cons title |
| 544 | (nconc (nreverse keep-at-top) menulist)))) |
| 545 | |
| 546 | ;;; Split up each long alist that are nested within ALIST |
| 547 | ;;; into nested alists. |
| 548 | (defun imenu--split-submenus (alist) |
| 549 | (mapcar (function |
| 550 | (lambda (elt) |
| 551 | (if (and (consp elt) |
| 552 | (stringp (car elt)) |
| 553 | (listp (cdr elt))) |
| 554 | (imenu--split-menu (cdr elt) (car elt)) |
| 555 | elt))) |
| 556 | alist)) |
| 557 | |
| 558 | ;;; Truncate all strings in MENULIST to imenu-max-item-length |
| 559 | (defun imenu--truncate-items (menulist) |
| 560 | (dolist (item menulist) |
| 561 | (cond |
| 562 | ((consp (cdr item)) |
| 563 | (imenu--truncate-items (cdr item))) |
| 564 | (t |
| 565 | ;; truncate if necessary |
| 566 | (if (and (numberp imenu-max-item-length) |
| 567 | (> (length (car item)) imenu-max-item-length)) |
| 568 | (setcar item (substring (car item) 0 imenu-max-item-length))))))) |
| 569 | |
| 570 | |
| 571 | (defun imenu--make-index-alist (&optional noerror) |
| 572 | "Create an index-alist for the definitions in the current buffer. |
| 573 | |
| 574 | Report an error if the list is empty unless NOERROR is supplied and |
| 575 | non-nil. |
| 576 | |
| 577 | Simple elements in the alist look like (INDEX-NAME . INDEX-POSITION). |
| 578 | Special elements look like (INDEX-NAME FUNCTION ARGUMENTS...). |
| 579 | A nested sub-alist element looks like (INDEX-NAME SUB-ALIST). |
| 580 | The function `imenu--subalist-p' tests an element and returns t |
| 581 | if it is a sub-alist. |
| 582 | |
| 583 | There is one simple element with negative POSITION; that's intended |
| 584 | as a way for the user to ask to recalculate the buffer's index alist." |
| 585 | (or (and imenu--index-alist |
| 586 | (or (not imenu-auto-rescan) |
| 587 | (and imenu-auto-rescan |
| 588 | (> (buffer-size) imenu-auto-rescan-maxout)))) |
| 589 | ;; Get the index; truncate if necessary |
| 590 | (progn |
| 591 | (setq imenu--index-alist |
| 592 | (save-excursion |
| 593 | (save-restriction |
| 594 | (widen) |
| 595 | (funcall imenu-create-index-function)))) |
| 596 | (imenu--truncate-items imenu--index-alist))) |
| 597 | (or imenu--index-alist noerror |
| 598 | (error "No items suitable for an index found in this buffer")) |
| 599 | (or imenu--index-alist |
| 600 | (setq imenu--index-alist (list nil))) |
| 601 | ;; Add a rescan option to the index. |
| 602 | (cons imenu--rescan-item imenu--index-alist)) |
| 603 | |
| 604 | ;;; Find all markers in alist and makes |
| 605 | ;;; them point nowhere. |
| 606 | ;;; The top-level call uses nil as the argument; |
| 607 | ;;; non-nil arguments are in recursivecalls. |
| 608 | (defvar imenu--cleanup-seen) |
| 609 | |
| 610 | (defun imenu--cleanup (&optional alist) |
| 611 | ;; If alist is provided use that list. |
| 612 | ;; If not, empty the table of lists already seen |
| 613 | ;; and use imenu--index-alist. |
| 614 | (if alist |
| 615 | (setq imenu--cleanup-seen (cons alist imenu--cleanup-seen)) |
| 616 | (setq alist imenu--index-alist imenu--cleanup-seen (list alist))) |
| 617 | |
| 618 | (and alist |
| 619 | (mapc |
| 620 | (lambda (item) |
| 621 | (cond |
| 622 | ((markerp (cdr item)) |
| 623 | (set-marker (cdr item) nil)) |
| 624 | ;; Don't process one alist twice. |
| 625 | ((memq (cdr item) imenu--cleanup-seen)) |
| 626 | ((imenu--subalist-p item) |
| 627 | (imenu--cleanup (cdr item))))) |
| 628 | alist) |
| 629 | t)) |
| 630 | |
| 631 | (defun imenu--create-keymap-2 (alist counter &optional commands) |
| 632 | (let ((map nil)) |
| 633 | (mapcar |
| 634 | (function |
| 635 | (lambda (item) |
| 636 | (cond |
| 637 | ((imenu--subalist-p item) |
| 638 | (append (list (setq counter (1+ counter)) |
| 639 | (car item) 'keymap (car item)) |
| 640 | (imenu--create-keymap-2 (cdr item) (+ counter 10) commands))) |
| 641 | (t |
| 642 | (let ((end (if commands `(lambda () (interactive) |
| 643 | (imenu--menubar-select ',item)) |
| 644 | (cons '(nil) item)))) |
| 645 | (cons (car item) |
| 646 | (cons (car item) end))))))) |
| 647 | alist))) |
| 648 | |
| 649 | ;; If COMMANDS is non-nil, make a real keymap |
| 650 | ;; with a real command used as the definition. |
| 651 | ;; If it is nil, make something suitable for x-popup-menu. |
| 652 | (defun imenu--create-keymap-1 (title alist &optional commands) |
| 653 | (append (list 'keymap title) (imenu--create-keymap-2 alist 0 commands))) |
| 654 | |
| 655 | (defun imenu--in-alist (str alist) |
| 656 | "Check whether the string STR is contained in multi-level ALIST." |
| 657 | (let (elt head tail res) |
| 658 | (setq res nil) |
| 659 | (while alist |
| 660 | (setq elt (car alist) |
| 661 | tail (cdr elt) |
| 662 | alist (cdr alist) |
| 663 | head (car elt)) |
| 664 | ;; A nested ALIST element looks like |
| 665 | ;; (INDEX-NAME (INDEX-NAME . INDEX-POSITION) ...) |
| 666 | ;; while a bottom-level element looks like |
| 667 | ;; (INDEX-NAME . INDEX-POSITION) |
| 668 | ;; We are only interested in the bottom-level elements, so we need to |
| 669 | ;; recurse if TAIL is a list. |
| 670 | (cond ((listp tail) |
| 671 | (if (setq res (imenu--in-alist str tail)) |
| 672 | (setq alist nil))) |
| 673 | ((if imenu-name-lookup-function |
| 674 | (funcall imenu-name-lookup-function str head) |
| 675 | (string= str head)) |
| 676 | (setq alist nil res elt)))) |
| 677 | res)) |
| 678 | |
| 679 | (defvar imenu-syntax-alist nil |
| 680 | "Alist of syntax table modifiers to use while executing `imenu--generic-function'. |
| 681 | |
| 682 | The car of the assocs may be either a character or a string and the |
| 683 | cdr is a syntax description appropriate fo `modify-syntax-entry'. For |
| 684 | a string, all the characters in the string get the specified syntax. |
| 685 | |
| 686 | This is typically used to give word syntax to characters which |
| 687 | normally have symbol syntax to simplify `imenu-expression' |
| 688 | and speed-up matching.") |
| 689 | (make-variable-buffer-local 'imenu-syntax-alist) |
| 690 | |
| 691 | (defun imenu-default-create-index-function () |
| 692 | "*Wrapper for index searching functions. |
| 693 | |
| 694 | Moves point to end of buffer and then repeatedly calls |
| 695 | `imenu-prev-index-position-function' and `imenu-extract-index-name-function'. |
| 696 | Their results are gathered into an index alist." |
| 697 | ;; These should really be done by setting imenu-create-index-function |
| 698 | ;; in these major modes. But save that change for later. |
| 699 | (cond ((and imenu-prev-index-position-function |
| 700 | imenu-extract-index-name-function) |
| 701 | (let ((index-alist '()) |
| 702 | prev-pos name) |
| 703 | (goto-char (point-max)) |
| 704 | (imenu-progress-message prev-pos 0 t) |
| 705 | ;; Search for the function |
| 706 | (while (funcall imenu-prev-index-position-function) |
| 707 | (imenu-progress-message prev-pos nil t) |
| 708 | (save-excursion |
| 709 | (setq name (funcall imenu-extract-index-name-function))) |
| 710 | (and (stringp name) |
| 711 | ;; [ydi] updated for imenu-use-markers |
| 712 | (push (cons name (if imenu-use-markers (point-marker) (point))) |
| 713 | index-alist))) |
| 714 | (imenu-progress-message prev-pos 100 t) |
| 715 | index-alist)) |
| 716 | ;; Use generic expression if possible. |
| 717 | ((and imenu-generic-expression) |
| 718 | (imenu--generic-function imenu-generic-expression)) |
| 719 | (t |
| 720 | (error "This buffer cannot use `imenu-default-create-index-function'")))) |
| 721 | |
| 722 | ;; Not used and would require cl at run time |
| 723 | ;;; (defun imenu--flatten-index-alist (index-alist &optional concat-names prefix) |
| 724 | ;;; ;; Takes a nested INDEX-ALIST and returns a flat index alist. |
| 725 | ;;; ;; If optional CONCAT-NAMES is non-nil, then a nested index has its |
| 726 | ;;; ;; name and a space concatenated to the names of the children. |
| 727 | ;;; ;; Third argument PREFIX is for internal use only. |
| 728 | ;;; (mapcan |
| 729 | ;;; (lambda (item) |
| 730 | ;;; (let* ((name (car item)) |
| 731 | ;;; (pos (cdr item)) |
| 732 | ;;; (new-prefix (and concat-names |
| 733 | ;;; (if prefix |
| 734 | ;;; (concat prefix imenu-level-separator name) |
| 735 | ;;; name)))) |
| 736 | ;;; (cond |
| 737 | ;;; ((or (markerp pos) (numberp pos)) |
| 738 | ;;; (list (cons new-prefix pos))) |
| 739 | ;;; (t |
| 740 | ;;; (imenu--flatten-index-alist pos new-prefix))))) |
| 741 | ;;; index-alist)) |
| 742 | |
| 743 | ;;; |
| 744 | ;;; Generic index gathering function. |
| 745 | ;;; |
| 746 | |
| 747 | (defvar imenu-case-fold-search t |
| 748 | "Defines whether `imenu--generic-function' should fold case when matching. |
| 749 | |
| 750 | This buffer-local variable should be set (only) by initialization code |
| 751 | for modes which use `imenu--generic-function'. If it is not set, that |
| 752 | function will use the current value of `case-fold-search' to match |
| 753 | patterns.") |
| 754 | ;;;###autoload |
| 755 | (make-variable-buffer-local 'imenu-case-fold-search) |
| 756 | |
| 757 | ;; Originally "Built on some ideas that Erik Naggum <erik@naggum.no> |
| 758 | ;; once posted to comp.emacs" but since substantially re-written. |
| 759 | (defun imenu--generic-function (patterns) |
| 760 | "Return an index of the current buffer as an alist. |
| 761 | |
| 762 | PATTERNS is an alist with elements that look like this: |
| 763 | (MENU-TITLE REGEXP INDEX). |
| 764 | |
| 765 | MENU-TITLE is a string used as the title for the submenu or nil if the |
| 766 | entries are not nested. |
| 767 | |
| 768 | REGEXP is a regexp that should match a construct in the buffer that is |
| 769 | to be displayed in the menu; i.e., function or variable definitions, |
| 770 | etc. It contains a substring which is the name to appear in the |
| 771 | menu. See the info section on Regexps for more information. |
| 772 | |
| 773 | INDEX points to the substring in REGEXP that contains the name (of the |
| 774 | function, variable or type) that is to appear in the menu. |
| 775 | |
| 776 | See `lisp-imenu-generic-expression' for an example of PATTERNS. |
| 777 | |
| 778 | Returns an index of the current buffer as an alist. The elements in |
| 779 | the alist look like: (INDEX-NAME . INDEX-POSITION). They may also be |
| 780 | nested index lists like (INDEX-NAME . INDEX-ALIST) depending on |
| 781 | PATTERNS." |
| 782 | |
| 783 | (let ((index-alist (list 'dummy)) |
| 784 | prev-pos beg |
| 785 | (case-fold-search imenu-case-fold-search) |
| 786 | (old-table (syntax-table)) |
| 787 | (table (copy-syntax-table (syntax-table))) |
| 788 | (slist imenu-syntax-alist)) |
| 789 | ;; Modify the syntax table used while matching regexps. |
| 790 | (while slist |
| 791 | ;; The character(s) to modify may be a single char or a string. |
| 792 | (if (numberp (caar slist)) |
| 793 | (modify-syntax-entry (caar slist) (cdar slist) table) |
| 794 | (dolist (c (caar slist)) |
| 795 | (modify-syntax-entry c (cdar slist) table))) |
| 796 | (setq slist (cdr slist))) |
| 797 | (goto-char (point-max)) |
| 798 | (imenu-progress-message prev-pos 0 t) |
| 799 | (unwind-protect ; for syntax table |
| 800 | (save-match-data |
| 801 | (set-syntax-table table) |
| 802 | ;; map over the elements of imenu-generic-expression |
| 803 | ;; (typically functions, variables ...) |
| 804 | (mapc |
| 805 | (lambda (pat) |
| 806 | (let ((menu-title (car pat)) |
| 807 | (regexp (nth 1 pat)) |
| 808 | (index (nth 2 pat)) |
| 809 | (function (nth 3 pat)) |
| 810 | (rest (nthcdr 4 pat))) |
| 811 | ;; Go backwards for convenience of adding items in order. |
| 812 | (goto-char (point-max)) |
| 813 | (while (re-search-backward regexp nil t) |
| 814 | (imenu-progress-message prev-pos nil t) |
| 815 | (setq beg (match-beginning index)) |
| 816 | ;; Add this sort of submenu only when we've found an |
| 817 | ;; item for it, avoiding empty, duff menus. |
| 818 | (unless (assoc menu-title index-alist) |
| 819 | (push (list menu-title) index-alist)) |
| 820 | (if imenu-use-markers |
| 821 | (setq beg (copy-marker beg))) |
| 822 | (let ((item |
| 823 | (if function |
| 824 | (nconc (list (match-string-no-properties index) |
| 825 | beg function) |
| 826 | rest) |
| 827 | (cons (match-string-no-properties index) |
| 828 | beg))) |
| 829 | ;; This is the desired submenu, |
| 830 | ;; starting with its title (or nil). |
| 831 | (menu (assoc menu-title index-alist))) |
| 832 | ;; Insert the item unless it is already present. |
| 833 | (unless (member item (cdr menu)) |
| 834 | (setcdr menu |
| 835 | (cons item (cdr menu)))))))) |
| 836 | patterns) |
| 837 | (set-syntax-table old-table))) |
| 838 | (imenu-progress-message prev-pos 100 t) |
| 839 | ;; Sort each submenu by position. |
| 840 | ;; This is in case one submenu gets items from two different regexps. |
| 841 | (let ((tail index-alist)) |
| 842 | (while tail |
| 843 | (if (listp (car tail)) |
| 844 | (setcdr (car tail) |
| 845 | (sort (cdr (car tail)) 'imenu--sort-by-position))) |
| 846 | (setq tail (cdr tail)))) |
| 847 | (let ((main-element (assq nil index-alist))) |
| 848 | (nconc (delq main-element (delq 'dummy index-alist)) |
| 849 | (cdr main-element))))) |
| 850 | |
| 851 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 852 | ;;; |
| 853 | ;;; The main functions for this package! |
| 854 | ;;; |
| 855 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 856 | |
| 857 | (defun imenu--completion-buffer (index-alist &optional prompt) |
| 858 | "Let the user select from INDEX-ALIST in a completion buffer with PROMPT. |
| 859 | |
| 860 | Returns t for rescan and otherwise a position number." |
| 861 | ;; Create a list for this buffer only when needed. |
| 862 | (let ((name (thing-at-point 'symbol)) |
| 863 | choice |
| 864 | (prepared-index-alist |
| 865 | (mapcar |
| 866 | (lambda (item) |
| 867 | (cons (subst-char-in-string ?\ (aref imenu-space-replacement 0) |
| 868 | (car item)) |
| 869 | (cdr item))) |
| 870 | index-alist))) |
| 871 | (cond (prompt) |
| 872 | ((and name (imenu--in-alist name prepared-index-alist)) |
| 873 | (setq prompt (format "Index item (default %s): " name))) |
| 874 | (t (setq prompt "Index item: "))) |
| 875 | (if (eq imenu-always-use-completion-buffer-p 'never) |
| 876 | (setq name (completing-read prompt |
| 877 | prepared-index-alist |
| 878 | nil t nil 'imenu--history-list name)) |
| 879 | (save-window-excursion |
| 880 | ;; Display the completion buffer |
| 881 | (with-output-to-temp-buffer "*Completions*" |
| 882 | (display-completion-list |
| 883 | (all-completions "" prepared-index-alist ))) |
| 884 | (let ((minibuffer-setup-hook |
| 885 | (function |
| 886 | (lambda () |
| 887 | (let ((buffer (current-buffer))) |
| 888 | (save-excursion |
| 889 | (set-buffer "*Completions*") |
| 890 | (setq completion-reference-buffer buffer))))))) |
| 891 | ;; Make a completion question |
| 892 | (setq name (completing-read prompt |
| 893 | prepared-index-alist |
| 894 | nil t nil 'imenu--history-list name))))) |
| 895 | (cond ((not (stringp name)) |
| 896 | nil) |
| 897 | ((string= name (car imenu--rescan-item)) |
| 898 | t) |
| 899 | (t |
| 900 | (setq choice (assoc name prepared-index-alist)) |
| 901 | (if (imenu--subalist-p choice) |
| 902 | (imenu--completion-buffer (cdr choice) prompt) |
| 903 | choice))))) |
| 904 | |
| 905 | (defun imenu--mouse-menu (index-alist event &optional title) |
| 906 | "Let the user select from a buffer index from a mouse menu. |
| 907 | |
| 908 | INDEX-ALIST is the buffer index and EVENT is a mouse event. |
| 909 | |
| 910 | Returns t for rescan and otherwise an element or subelement of INDEX-ALIST." |
| 911 | (setq index-alist (imenu--split-submenus index-alist)) |
| 912 | (let* ((menu (imenu--split-menu index-alist |
| 913 | (or title (buffer-name)))) |
| 914 | position) |
| 915 | (setq menu (imenu--create-keymap-1 (car menu) |
| 916 | (if (< 1 (length (cdr menu))) |
| 917 | (cdr menu) |
| 918 | (cdr (car (cdr menu)))))) |
| 919 | (setq position (x-popup-menu event menu)) |
| 920 | (cond ((eq position nil) |
| 921 | position) |
| 922 | ;; If one call to x-popup-menu handled the nested menus, |
| 923 | ;; find the result by looking down the menus here. |
| 924 | ((and (listp position) |
| 925 | (numberp (car position)) |
| 926 | (stringp (nth (1- (length position)) position))) |
| 927 | (let ((final menu)) |
| 928 | (while position |
| 929 | (setq final (assq (car position) final)) |
| 930 | (setq position (cdr position))) |
| 931 | (or (string= (car final) (car imenu--rescan-item)) |
| 932 | (nthcdr 3 final)))) |
| 933 | ;; If x-popup-menu went just one level and found a leaf item, |
| 934 | ;; return the INDEX-ALIST element for that. |
| 935 | ((and (consp position) |
| 936 | (stringp (car position)) |
| 937 | (null (cdr position))) |
| 938 | (or (string= (car position) (car imenu--rescan-item)) |
| 939 | (assq (car position) index-alist))) |
| 940 | ;; If x-popup-menu went just one level |
| 941 | ;; and found a non-leaf item (a submenu), |
| 942 | ;; recurse to handle the rest. |
| 943 | ((listp position) |
| 944 | (imenu--mouse-menu position event |
| 945 | (if title |
| 946 | (concat title imenu-level-separator |
| 947 | (car (rassq position index-alist))) |
| 948 | (car (rassq position index-alist)))))))) |
| 949 | |
| 950 | (defun imenu-choose-buffer-index (&optional prompt alist) |
| 951 | "Let the user select from a buffer index and return the chosen index. |
| 952 | |
| 953 | If the user originally activated this function with the mouse, a mouse |
| 954 | menu is used. Otherwise a completion buffer is used and the user is |
| 955 | prompted with PROMPT. |
| 956 | |
| 957 | If you call this function with index alist ALIST, then it lets the user |
| 958 | select from ALIST. |
| 959 | |
| 960 | With no index alist ALIST, it calls `imenu--make-index-alist' to |
| 961 | create the index alist. |
| 962 | |
| 963 | If `imenu-always-use-completion-buffer-p' is non-nil, then the |
| 964 | completion buffer is always used, no matter if the mouse was used or |
| 965 | not. |
| 966 | |
| 967 | The returned value is of the form (INDEX-NAME . INDEX-POSITION)." |
| 968 | (let (index-alist |
| 969 | (mouse-triggered (listp last-nonmenu-event)) |
| 970 | (result t) ) |
| 971 | ;; If selected by mouse, see to that the window where the mouse is |
| 972 | ;; really is selected. |
| 973 | (and mouse-triggered |
| 974 | (not (equal last-nonmenu-event '(menu-bar))) |
| 975 | (let ((window (posn-window (event-start last-nonmenu-event)))) |
| 976 | (or (framep window) (null window) (select-window window)))) |
| 977 | ;; Create a list for this buffer only when needed. |
| 978 | (while (eq result t) |
| 979 | (setq index-alist (if alist alist (imenu--make-index-alist))) |
| 980 | (setq result |
| 981 | (if (and mouse-triggered |
| 982 | (not imenu-always-use-completion-buffer-p)) |
| 983 | (imenu--mouse-menu index-alist last-nonmenu-event) |
| 984 | (imenu--completion-buffer index-alist prompt))) |
| 985 | (and (eq result t) |
| 986 | (imenu--cleanup) |
| 987 | (setq imenu--index-alist nil))) |
| 988 | result)) |
| 989 | |
| 990 | ;;;###autoload |
| 991 | (defun imenu-add-to-menubar (name) |
| 992 | "Add an `imenu' entry to the menu bar for the current buffer. |
| 993 | NAME is a string used to name the menu bar item. |
| 994 | See the command `imenu' for more information." |
| 995 | (interactive "sImenu menu item name: ") |
| 996 | (if (or (and imenu-prev-index-position-function |
| 997 | imenu-extract-index-name-function) |
| 998 | imenu-generic-expression |
| 999 | (not (eq imenu-create-index-function |
| 1000 | 'imenu-default-create-index-function))) |
| 1001 | (let ((newmap (make-sparse-keymap))) |
| 1002 | (set-keymap-parent newmap (current-local-map)) |
| 1003 | (setq imenu--last-menubar-index-alist nil) |
| 1004 | (define-key newmap [menu-bar index] |
| 1005 | `(menu-item ,name ,(make-sparse-keymap "Imenu"))) |
| 1006 | (use-local-map newmap) |
| 1007 | (add-hook 'menu-bar-update-hook 'imenu-update-menubar)) |
| 1008 | (error "The mode `%s' does not support Imenu" mode-name))) |
| 1009 | |
| 1010 | ;;;###autoload |
| 1011 | (defun imenu-add-menubar-index () |
| 1012 | "Add an Imenu \"Index\" entry on the menu bar for the current buffer. |
| 1013 | |
| 1014 | A trivial interface to `imenu-add-to-menubar' suitable for use in a hook." |
| 1015 | (interactive) |
| 1016 | (imenu-add-to-menubar "Index")) |
| 1017 | |
| 1018 | (defvar imenu-buffer-menubar nil) |
| 1019 | |
| 1020 | (defun imenu-update-menubar () |
| 1021 | (and (current-local-map) |
| 1022 | (keymapp (lookup-key (current-local-map) [menu-bar index])) |
| 1023 | (let ((index-alist (imenu--make-index-alist t))) |
| 1024 | ;; Don't bother updating if the index-alist has not changed |
| 1025 | ;; since the last time we did it. |
| 1026 | (or (equal index-alist imenu--last-menubar-index-alist) |
| 1027 | (let (menu menu1 old) |
| 1028 | (setq imenu--last-menubar-index-alist index-alist) |
| 1029 | (setq index-alist (imenu--split-submenus index-alist)) |
| 1030 | (setq menu (imenu--split-menu index-alist |
| 1031 | (buffer-name))) |
| 1032 | (setq menu1 (imenu--create-keymap-1 (car menu) |
| 1033 | (if (< 1 (length (cdr menu))) |
| 1034 | (cdr menu) |
| 1035 | (cdr (car (cdr menu)))) |
| 1036 | t)) |
| 1037 | (setq old (lookup-key (current-local-map) [menu-bar index])) |
| 1038 | (setcdr old (cdr menu1))))))) |
| 1039 | |
| 1040 | (defun imenu--menubar-select (item) |
| 1041 | "Use Imenu to select the function or variable named in this menu item." |
| 1042 | (if (equal item imenu--rescan-item) |
| 1043 | (progn |
| 1044 | (imenu--cleanup) |
| 1045 | (setq imenu--index-alist nil) |
| 1046 | (imenu-update-menubar)) |
| 1047 | (imenu item))) |
| 1048 | |
| 1049 | (defun imenu-default-goto-function (name position &optional rest) |
| 1050 | "Move the point to the given position. |
| 1051 | |
| 1052 | NAME is ignored. POSITION is where to move. REST is also ignored. |
| 1053 | The ignored args just make this function have the same interface as a |
| 1054 | function placed in a special index-item." |
| 1055 | (if (or (< position (point-min)) |
| 1056 | (> position (point-max))) |
| 1057 | ;; widen if outside narrowing |
| 1058 | (widen)) |
| 1059 | (goto-char position)) |
| 1060 | |
| 1061 | ;;;###autoload |
| 1062 | (defun imenu (index-item) |
| 1063 | "Jump to a place in the buffer chosen using a buffer menu or mouse menu. |
| 1064 | INDEX-ITEM specifies the position. See `imenu-choose-buffer-index' |
| 1065 | for more information." |
| 1066 | (interactive (list (imenu-choose-buffer-index))) |
| 1067 | ;; Convert a string to an alist element. |
| 1068 | (if (stringp index-item) |
| 1069 | (setq index-item (assoc index-item (imenu--make-index-alist)))) |
| 1070 | (and index-item |
| 1071 | (progn |
| 1072 | (push-mark) |
| 1073 | (let* ((is-special-item (listp (cdr index-item))) |
| 1074 | (function |
| 1075 | (if is-special-item |
| 1076 | (nth 2 index-item) imenu-default-goto-function)) |
| 1077 | (position (if is-special-item |
| 1078 | (cadr index-item) (cdr index-item))) |
| 1079 | (rest (if is-special-item (cddr index-item)))) |
| 1080 | (apply function (car index-item) position rest)))) |
| 1081 | (run-hooks 'imenu-after-jump-hook)) |
| 1082 | |
| 1083 | (dolist (mess |
| 1084 | '("^No items suitable for an index found in this buffer$" |
| 1085 | "^This buffer cannot use `imenu-default-create-index-function'$" |
| 1086 | "^The mode `.*' does not support Imenu$")) |
| 1087 | (add-to-list 'debug-ignored-errors mess)) |
| 1088 | |
| 1089 | (provide 'imenu) |
| 1090 | |
| 1091 | ;;; imenu.el ends here |