| 1 | ;;; auto-complete.el --- Auto completion with popup menu |
|---|
| 2 | |
|---|
| 3 | ;; Copyright (C) 2008 MATSUYAMA Tomohiro |
|---|
| 4 | |
|---|
| 5 | ;; Author: MATSUYAMA Tomohiro <t.matsuyama.pub@gmail.com> |
|---|
| 6 | ;; Keywords: convenience |
|---|
| 7 | ;; Version: 0.1.0 |
|---|
| 8 | |
|---|
| 9 | ;; This program is free software; you can redistribute it and/or modify |
|---|
| 10 | ;; it under the terms of the GNU General Public License as published by |
|---|
| 11 | ;; the Free Software Foundation, either version 3 of the License, or |
|---|
| 12 | ;; (at your option) any later version. |
|---|
| 13 | |
|---|
| 14 | ;; This program is distributed in the hope that it will be useful, |
|---|
| 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 17 | ;; GNU General Public License for more details. |
|---|
| 18 | |
|---|
| 19 | ;; You should have received a copy of the GNU General Public License |
|---|
| 20 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 21 | |
|---|
| 22 | ;;; Commentary: |
|---|
| 23 | |
|---|
| 24 | ;; This extension provides a way to select a completion with |
|---|
| 25 | ;; popup menu. |
|---|
| 26 | ;; |
|---|
| 27 | ;; I checked that this extension can work properly on GNU Emacs 22 or higher. |
|---|
| 28 | |
|---|
| 29 | ;; To use this extension, locate this file to load-path directory, |
|---|
| 30 | ;; and add the following code to your .emacs. |
|---|
| 31 | ;; ------------------------------ |
|---|
| 32 | ;; (require 'auto-complete) |
|---|
| 33 | ;; (global-auto-complete-mode t) |
|---|
| 34 | ;; ------------------------------ |
|---|
| 35 | |
|---|
| 36 | ;; After installation, you can try this: |
|---|
| 37 | ;; |
|---|
| 38 | ;; 1. Switch to emacs-lisp-mode buffer such as .emacs |
|---|
| 39 | ;; 2. Goto anywhere |
|---|
| 40 | ;; 3. Type "def" |
|---|
| 41 | ;; 4. You may see a pop menu after the cursor like: |
|---|
| 42 | ;; def-!- |
|---|
| 43 | ;; +-----------------+ |
|---|
| 44 | ;; |defun | <- highlight |
|---|
| 45 | ;; |defvar | |
|---|
| 46 | ;; |defmacro | |
|---|
| 47 | ;; | ... | |
|---|
| 48 | ;; +-----------------+ |
|---|
| 49 | ;; 5. You can complete by seleting the menu item |
|---|
| 50 | ;; by pressing TAB, <down>, <up>, and RET. |
|---|
| 51 | |
|---|
| 52 | ;;; Tips: |
|---|
| 53 | ;; |
|---|
| 54 | ;; ================================ |
|---|
| 55 | ;; Use C-n/C-p to select candidates |
|---|
| 56 | ;; ================================ |
|---|
| 57 | ;; |
|---|
| 58 | ;; Add following code to your .emacs. |
|---|
| 59 | ;; |
|---|
| 60 | ;; ------------------------------ |
|---|
| 61 | ;; (define-key ac-complete-mode-map "\C-n" 'ac-next) |
|---|
| 62 | ;; (define-key ac-complete-mode-map "\C-p" 'ac-previous) |
|---|
| 63 | ;; ------------------------------ |
|---|
| 64 | ;; |
|---|
| 65 | ;; |
|---|
| 66 | ;; ==================================== |
|---|
| 67 | ;; Don't start completion automatically |
|---|
| 68 | ;; ==================================== |
|---|
| 69 | ;; |
|---|
| 70 | ;; Add following code to your .emacs. |
|---|
| 71 | ;; |
|---|
| 72 | ;; ------------------------------ |
|---|
| 73 | ;; (setq ac-auto-start nil) |
|---|
| 74 | ;; (global-set-key "\M-/" 'ac-start) |
|---|
| 75 | ;; ------------------------------ |
|---|
| 76 | ;; |
|---|
| 77 | ;; Or |
|---|
| 78 | ;; |
|---|
| 79 | ;; ------------------------------ |
|---|
| 80 | ;; ;; start completion when entered 3 characters |
|---|
| 81 | ;; (setq ac-auto-start 3) |
|---|
| 82 | ;; ------------------------------ |
|---|
| 83 | ;; |
|---|
| 84 | ;; |
|---|
| 85 | ;; ================= |
|---|
| 86 | ;; Completion by TAB |
|---|
| 87 | ;; ================= |
|---|
| 88 | ;; |
|---|
| 89 | ;; Add following code to your .emacs. |
|---|
| 90 | ;; |
|---|
| 91 | ;; ------------------------------ |
|---|
| 92 | ;; (define-key ac-complete-mode-map "\t" 'ac-complete) |
|---|
| 93 | ;; (define-key ac-complete-mode-map "\r" nil) |
|---|
| 94 | ;; ------------------------------ |
|---|
| 95 | ;; |
|---|
| 96 | ;; |
|---|
| 97 | ;; =================== |
|---|
| 98 | ;; Do What I Mean mode |
|---|
| 99 | ;; =================== |
|---|
| 100 | ;; |
|---|
| 101 | ;; If DWIM (Do What I Mean) mode is enabled, |
|---|
| 102 | ;; the following features is available: |
|---|
| 103 | ;; |
|---|
| 104 | ;; a. TAB (ac-expand) behave as completion (ac-complete) |
|---|
| 105 | ;; when only one candidate is left |
|---|
| 106 | ;; b. TAB (ac-expand) behave as completion (ac-complete) |
|---|
| 107 | ;; after you select candidate |
|---|
| 108 | ;; |
|---|
| 109 | ;; DWIM mode is enabled by default. |
|---|
| 110 | ;; You can disable this feature by |
|---|
| 111 | ;; setting `ac-dwim' to nil. |
|---|
| 112 | |
|---|
| 113 | ;; This extension is so simple that you can extend |
|---|
| 114 | ;; how Emacs find a target and how Emacs enumerate |
|---|
| 115 | ;; candidates. |
|---|
| 116 | ;; I don't have intention to implement heavy functions :-) |
|---|
| 117 | ;; |
|---|
| 118 | ;; Enjoy! |
|---|
| 119 | |
|---|
| 120 | ;;; History: |
|---|
| 121 | |
|---|
| 122 | ;; 2008-11-26 MATSUYAMA Tomohiro <t.matsuyama.pub@gmail.com> |
|---|
| 123 | ;; |
|---|
| 124 | ;; * auto-complete.el 0.1.0 released |
|---|
| 125 | ;; |
|---|
| 126 | ;; 2008-11-19 MATSUYAMA Tomohiro <t.matsuyama.pub@gmail.com> |
|---|
| 127 | ;; |
|---|
| 128 | ;; * thanks for Taiki SUGAWARA <buzz.taiki@gmail.com> |
|---|
| 129 | ;; * added source ac-source-abbrev |
|---|
| 130 | ;; * added source ac-source-symbols |
|---|
| 131 | ;; * added ac-expand-common to expand common part |
|---|
| 132 | ;; |
|---|
| 133 | ;; 2008-11-18 MATSUYAMA Tomohiro <t.matsuyama.pub@gmail.com> |
|---|
| 134 | ;; |
|---|
| 135 | ;; * added ac-auto-start switch |
|---|
| 136 | ;; * added ac-dwim switch |
|---|
| 137 | ;; * changed menu popup behavior at end of window |
|---|
| 138 | ;; * thanks rubikitch <rubikitch@ruby-lang.org>, kazu-yamamoto. |
|---|
| 139 | ;; * fixed canceler bug |
|---|
| 140 | ;; * changed to use overriding-local-map instead of minor mode map |
|---|
| 141 | ;; * changed default key bindings |
|---|
| 142 | ;; |
|---|
| 143 | ;; 2008-11-16 MATSUYAMA Tomohiro <t.matsuyama.pub@gmail.com> |
|---|
| 144 | ;; |
|---|
| 145 | ;; * supported candidates by using sources |
|---|
| 146 | ;; * added automatically start swtich |
|---|
| 147 | ;; * fixed some bug |
|---|
| 148 | ;; * added source ac-source-files-in-current-dir |
|---|
| 149 | ;; * added source ac-source-words-in-buffer |
|---|
| 150 | ;; * added source ac-source-yasnippet |
|---|
| 151 | ;; * renamed ac-enum-candidates-function to ac-candidate-function |
|---|
| 152 | ;; * renamed ac-find-target-function to ac-find-function |
|---|
| 153 | ;; * ac-find-function and ac-candidate-function is not buffer local variable now |
|---|
| 154 | ;; * made candidates visible when you are end of line |
|---|
| 155 | ;; |
|---|
| 156 | ;; 2008-11-11 MATSUYAMA Tomohiro <t.matsuyama.pub@gmail.com> |
|---|
| 157 | ;; |
|---|
| 158 | ;; * by reporting from rubikitch <rubikitch@ruby-lang.org> |
|---|
| 159 | ;; * renamed hook name |
|---|
| 160 | ;; * registered backward-delete-char as special command |
|---|
| 161 | ;; * fixed code for creating candidates |
|---|
| 162 | ;; * made auto-complete disabled when isearch-mode enabled |
|---|
| 163 | ;; * added some major-mode into ac-modes |
|---|
| 164 | ;; |
|---|
| 165 | ;; 2008-11-09 MATSUYAMA Tomohiro <t.matsuyama.pub@gmail.com> |
|---|
| 166 | ;; |
|---|
| 167 | ;; * auto-complete.el 0.0.1 released |
|---|
| 168 | ;; * fixed double-width character displaying problem |
|---|
| 169 | ;; * fixed menu position following tab character |
|---|
| 170 | ;; * made candidates visible when you are end of window |
|---|
| 171 | |
|---|
| 172 | ;;; TODO: |
|---|
| 173 | ;; |
|---|
| 174 | ;; - performance issue (cache issue) |
|---|
| 175 | ;; - single source mode |
|---|
| 176 | ;; - fix narrowing bug (reported by Yuto Hayamizu <y.hayamizu@gmail.com>) |
|---|
| 177 | ;; - care about undo (buffer-disable-undo) |
|---|
| 178 | ;; - support scroll in menu |
|---|
| 179 | ;; - use double candidate menu |
|---|
| 180 | ;; - omni completion |
|---|
| 181 | ;; - show description |
|---|
| 182 | ;; - demo movie (YouTube) |
|---|
| 183 | ;; - backward menu in tiny buffer |
|---|
| 184 | ;; - dictionary |
|---|
| 185 | ;; - semantic |
|---|
| 186 | |
|---|
| 187 | ;;; Code: |
|---|
| 188 | |
|---|
| 189 | |
|---|
| 190 | |
|---|
| 191 | (defgroup auto-complete nil |
|---|
| 192 | "Auto completion with popup menu" |
|---|
| 193 | :group 'convenience |
|---|
| 194 | :prefix "auto-complete-") |
|---|
| 195 | |
|---|
| 196 | (defcustom ac-candidate-menu-width 25 |
|---|
| 197 | "Max width of candidate menu." |
|---|
| 198 | :type 'number |
|---|
| 199 | :group 'auto-complete) |
|---|
| 200 | |
|---|
| 201 | (defcustom ac-candidate-menu-height 10 |
|---|
| 202 | "Max height of candidate menu." |
|---|
| 203 | :type 'number |
|---|
| 204 | :group 'auto-complete) |
|---|
| 205 | |
|---|
| 206 | (defcustom ac-candidate-max 10 |
|---|
| 207 | "Max of number of candidates." |
|---|
| 208 | :type 'number |
|---|
| 209 | :group 'auto-complete) |
|---|
| 210 | |
|---|
| 211 | (defcustom ac-modes |
|---|
| 212 | '(emacs-lisp-mode lisp-interaction-mode |
|---|
| 213 | c-mode cc-mode c++-mode java-mode |
|---|
| 214 | perl-mode cperl-mode python-mode ruby-mode |
|---|
| 215 | ecmascript-mode javascript-mode php-mode css-mode |
|---|
| 216 | makefile-mode sh-mode fortran-mode f90-mode ada-mode |
|---|
| 217 | xml-mode sgml-mode) |
|---|
| 218 | "Major modes `auto-complete-mode' can run on." |
|---|
| 219 | :type '(list symbol) |
|---|
| 220 | :group 'auto-complete) |
|---|
| 221 | |
|---|
| 222 | (defcustom ac-auto-start t |
|---|
| 223 | "Non-nil means completion will be started automatically. |
|---|
| 224 | Positive integer means if a length of a word you entered is larger than the value, |
|---|
| 225 | completion will be started automatically. |
|---|
| 226 | If you specify `nil', never be started automatically." |
|---|
| 227 | :group 'auto-complete) |
|---|
| 228 | |
|---|
| 229 | (defcustom ac-dwim t |
|---|
| 230 | "Non-nil means `auto-complete' works based on Do What I Mean." |
|---|
| 231 | :type 'boolean |
|---|
| 232 | :group 'auto-complete) |
|---|
| 233 | |
|---|
| 234 | (defface ac-selection-face |
|---|
| 235 | '((t (:background "blue" :foreground "white"))) |
|---|
| 236 | "Face for the selected candidate." |
|---|
| 237 | :group 'auto-complete) |
|---|
| 238 | |
|---|
| 239 | (defface ac-menu-face |
|---|
| 240 | '((t (:background "lightgray" :foreground "black"))) |
|---|
| 241 | "Face for candidate menu." |
|---|
| 242 | :group 'auto-complete) |
|---|
| 243 | |
|---|
| 244 | (defvar auto-complete-mode-hook nil |
|---|
| 245 | "Hook for `auto-complete-mode'.") |
|---|
| 246 | |
|---|
| 247 | (defvar ac-menu nil |
|---|
| 248 | "Menu instance.") |
|---|
| 249 | |
|---|
| 250 | (defvar ac-menu-direction 1 |
|---|
| 251 | "Positive integer means `ac-menu' grows forward. |
|---|
| 252 | Or, `ac-menu' grows backward.") |
|---|
| 253 | |
|---|
| 254 | (defvar ac-menu-offset 0 |
|---|
| 255 | "Offset to contents.") |
|---|
| 256 | |
|---|
| 257 | (defvar ac-completing nil |
|---|
| 258 | "Non-nil means `auto-complete-mode' is now working on completion.") |
|---|
| 259 | |
|---|
| 260 | (defvar ac-saved-window-start nil |
|---|
| 261 | "Saved window start value for restore.") |
|---|
| 262 | |
|---|
| 263 | (defvar ac-saved-window-hscroll nil |
|---|
| 264 | "Saved window hscroll value for restore.") |
|---|
| 265 | |
|---|
| 266 | (defvar ac-point nil |
|---|
| 267 | "Start point of target.") |
|---|
| 268 | |
|---|
| 269 | (defvar ac-target nil |
|---|
| 270 | "Target string.") |
|---|
| 271 | |
|---|
| 272 | (defvar ac-limit 0 |
|---|
| 273 | "Limit of number of candidates.") |
|---|
| 274 | |
|---|
| 275 | (defvar ac-candidates nil |
|---|
| 276 | "Current candidates.") |
|---|
| 277 | |
|---|
| 278 | (defvar ac-selection nil |
|---|
| 279 | "Current candidate index.") |
|---|
| 280 | |
|---|
| 281 | (defvar ac-dwim-enable nil |
|---|
| 282 | "Non-nil means DWIM completion will be allowed.") |
|---|
| 283 | |
|---|
| 284 | (defvar ac-find-function 'ac-default-find |
|---|
| 285 | "When `auto-complete-mode' finds it can start completion |
|---|
| 286 | or update candidates, it will call this function to find a |
|---|
| 287 | start point of the completion target. |
|---|
| 288 | |
|---|
| 289 | If this function returns an integer, `auto-complete-mode' |
|---|
| 290 | will set the substring between the point and current point to `ac-target'. |
|---|
| 291 | And also it will start completion or update candidates by using |
|---|
| 292 | the `ac-target'. |
|---|
| 293 | |
|---|
| 294 | If this function returns `nil', `auto-complete-mode' |
|---|
| 295 | ignore starting completion or stop completing.") |
|---|
| 296 | |
|---|
| 297 | (defvar ac-init-function 'ac-sources-init |
|---|
| 298 | "This function will be called when candidate menu is setupped.") |
|---|
| 299 | |
|---|
| 300 | (defvar ac-candidate-function 'ac-sources-candidate |
|---|
| 301 | "This function can return candidates as list by |
|---|
| 302 | using the `TARGET' that is given as a first argument.") |
|---|
| 303 | |
|---|
| 304 | (defvar ac-complete-mode-map |
|---|
| 305 | (let ((map (make-sparse-keymap))) |
|---|
| 306 | (define-key map "\t" 'ac-expand) |
|---|
| 307 | (define-key map "\r" 'ac-complete) |
|---|
| 308 | |
|---|
| 309 | (define-key map [down] 'ac-next) |
|---|
| 310 | (define-key map [up] 'ac-previous) |
|---|
| 311 | |
|---|
| 312 | map) |
|---|
| 313 | "Keymap for completion.") |
|---|
| 314 | |
|---|
| 315 | (or (assq 'ac-completing minor-mode-map-alist) |
|---|
| 316 | (push (cons 'ac-completing ac-complete-mode-map) minor-mode-map-alist)) |
|---|
| 317 | |
|---|
| 318 | (defvar ac-saved-local-map nil |
|---|
| 319 | "Old keymap before `auto-complete' activated.") |
|---|
| 320 | |
|---|
| 321 | |
|---|
| 322 | |
|---|
| 323 | ;;;; Auto completion |
|---|
| 324 | |
|---|
| 325 | (defun ac-setup (point) |
|---|
| 326 | "Setup popup menu." |
|---|
| 327 | (save-excursion |
|---|
| 328 | (goto-char point) |
|---|
| 329 | (let ((column (current-column)) |
|---|
| 330 | (line (line-number-at-pos))) |
|---|
| 331 | (setq ac-saved-window-start (window-start)) |
|---|
| 332 | (setq ac-saved-window-hscroll (window-hscroll)) |
|---|
| 333 | (setq ac-menu-direction |
|---|
| 334 | (if (and (> line ac-candidate-max) |
|---|
| 335 | (> ac-candidate-max |
|---|
| 336 | (- |
|---|
| 337 | (max 1 (- (window-height) |
|---|
| 338 | (if mode-line-format 1 0) |
|---|
| 339 | (if header-line-format 1 0))) |
|---|
| 340 | (1+ (count-lines (window-start) (point)))))) |
|---|
| 341 | -1 |
|---|
| 342 | 1)) |
|---|
| 343 | (let ((window-width (window-width)) |
|---|
| 344 | (right (- (+ column ac-candidate-menu-width) |
|---|
| 345 | (window-hscroll)))) |
|---|
| 346 | (if (and (> right window-width) |
|---|
| 347 | (>= right ac-candidate-menu-width)) |
|---|
| 348 | (setq column (- column ac-candidate-menu-width)))) |
|---|
| 349 | (if (> ac-menu-direction 0) |
|---|
| 350 | (progn |
|---|
| 351 | (forward-line) |
|---|
| 352 | (if (eq line (line-number-at-pos)) |
|---|
| 353 | (newline) |
|---|
| 354 | (forward-line -1)) |
|---|
| 355 | (setq ac-menu (ac-menu-create (1+ line) column ac-candidate-menu-width ac-candidate-menu-height)) |
|---|
| 356 | (setq ac-point point)) |
|---|
| 357 | (setq ac-menu (ac-menu-create (- line ac-candidate-max) column ac-candidate-menu-width ac-candidate-menu-height)) |
|---|
| 358 | (setq ac-point point))))) |
|---|
| 359 | |
|---|
| 360 | (defun ac-cleanup () |
|---|
| 361 | "Destroy popup menu." |
|---|
| 362 | (ac-deactivate-mode-map) |
|---|
| 363 | (when ac-menu |
|---|
| 364 | (ac-menu-delete ac-menu) |
|---|
| 365 | (set-window-start (selected-window) ac-saved-window-start) |
|---|
| 366 | (set-window-hscroll (selected-window) ac-saved-window-hscroll)) |
|---|
| 367 | (setq ac-menu nil) |
|---|
| 368 | (setq ac-completing nil) |
|---|
| 369 | (setq ac-point nil) |
|---|
| 370 | (setq ac-candidates nil) |
|---|
| 371 | (setq ac-selection 0)) |
|---|
| 372 | |
|---|
| 373 | (defun ac-activate-mode-map () |
|---|
| 374 | "Activate `ac-complete-mode-map'." |
|---|
| 375 | (setq ac-saved-local-map overriding-terminal-local-map) |
|---|
| 376 | (if (eq ac-saved-local-map ac-complete-mode-map) |
|---|
| 377 | ;; maybe never reach here |
|---|
| 378 | (setq ac-saved-local-map nil)) |
|---|
| 379 | (setq overriding-terminal-local-map ac-complete-mode-map)) |
|---|
| 380 | |
|---|
| 381 | (defun ac-deactivate-mode-map () |
|---|
| 382 | "Deactivate `ac-complete-mode-map'." |
|---|
| 383 | (when (eq overriding-terminal-local-map ac-complete-mode-map) |
|---|
| 384 | (setq overriding-terminal-local-map ac-saved-local-map) |
|---|
| 385 | (setq ac-saved-local-map nil))) |
|---|
| 386 | |
|---|
| 387 | (defun ac-next () |
|---|
| 388 | "Select next candidate." |
|---|
| 389 | (interactive) |
|---|
| 390 | (if (interactive-p) |
|---|
| 391 | (setq ac-dwim-enable t)) |
|---|
| 392 | (if ac-candidates |
|---|
| 393 | (ac-select-candidate |
|---|
| 394 | (let ((selection (1+ ac-selection))) |
|---|
| 395 | (if (= selection (+ ac-menu-offset (min ac-candidate-menu-height (length ac-candidates)))) |
|---|
| 396 | ac-menu-offset |
|---|
| 397 | selection))))) |
|---|
| 398 | |
|---|
| 399 | (defun ac-previous () |
|---|
| 400 | "Select previous candidate." |
|---|
| 401 | (interactive) |
|---|
| 402 | (if (interactive-p) |
|---|
| 403 | (setq ac-dwim-enable t)) |
|---|
| 404 | (if ac-candidates |
|---|
| 405 | (ac-select-candidate |
|---|
| 406 | (let ((selection (1- ac-selection))) |
|---|
| 407 | (if (< selection ac-menu-offset) |
|---|
| 408 | (1- (+ ac-menu-offset (min ac-candidate-menu-height (length ac-candidates)))) |
|---|
| 409 | selection))))) |
|---|
| 410 | |
|---|
| 411 | (defun ac-expand-1 () |
|---|
| 412 | "Try expansion." |
|---|
| 413 | (let ((string (overlay-get (ac-menu-line-overlay ac-menu ac-selection) 'real-string))) |
|---|
| 414 | (delete-region ac-point (point)) |
|---|
| 415 | (insert string) |
|---|
| 416 | (setq ac-target string))) |
|---|
| 417 | |
|---|
| 418 | (defun ac-expand () |
|---|
| 419 | "Try expansion but select next if expanded twice." |
|---|
| 420 | (interactive) |
|---|
| 421 | (if (and ac-dwim ac-dwim-enable) |
|---|
| 422 | (ac-complete) |
|---|
| 423 | (let ((target ac-target) |
|---|
| 424 | (string (ac-expand-1))) |
|---|
| 425 | (when (equal target string) |
|---|
| 426 | (ac-next) |
|---|
| 427 | (ac-expand-1))))) |
|---|
| 428 | |
|---|
| 429 | (defun ac-expand-common () |
|---|
| 430 | "Try expansion common part." |
|---|
| 431 | (interactive) |
|---|
| 432 | (let ((common (try-completion ac-target ac-candidates))) |
|---|
| 433 | (when (stringp common) |
|---|
| 434 | (delete-region ac-point (point)) |
|---|
| 435 | (insert common) |
|---|
| 436 | (setq ac-target common)))) |
|---|
| 437 | |
|---|
| 438 | (defun ac-complete () |
|---|
| 439 | "Try completion." |
|---|
| 440 | (interactive) |
|---|
| 441 | (let* ((string (overlay-get (ac-menu-line-overlay ac-menu ac-selection) 'real-string)) |
|---|
| 442 | (source (get-text-property 0 'source string)) |
|---|
| 443 | (complete-function (and source (cdr-safe (assq 'action source))))) |
|---|
| 444 | (ac-expand-1) |
|---|
| 445 | (if complete-function |
|---|
| 446 | (funcall complete-function)) |
|---|
| 447 | (ac-abort))) |
|---|
| 448 | |
|---|
| 449 | (defun ac-abort () |
|---|
| 450 | "Abort completion." |
|---|
| 451 | (ac-cleanup)) |
|---|
| 452 | |
|---|
| 453 | (defun ac-update-candidates (candidates) |
|---|
| 454 | "Update candidates of popup menu." |
|---|
| 455 | (setq ac-menu-offset (if (> ac-menu-direction 0) |
|---|
| 456 | 0 |
|---|
| 457 | (- ac-candidate-menu-height |
|---|
| 458 | (min ac-candidate-menu-height |
|---|
| 459 | (length candidates))))) |
|---|
| 460 | (setq ac-selection ac-menu-offset) |
|---|
| 461 | (setq ac-candidates candidates) |
|---|
| 462 | (setq ac-dwim-enable (= (length candidates) 1)) |
|---|
| 463 | (if candidates |
|---|
| 464 | (progn |
|---|
| 465 | (setq ac-completing t) |
|---|
| 466 | (ac-activate-mode-map)) |
|---|
| 467 | (setq ac-completing nil) |
|---|
| 468 | (ac-deactivate-mode-map)) |
|---|
| 469 | (let ((i ac-menu-offset)) |
|---|
| 470 | ;; show line and set string to the line |
|---|
| 471 | (mapcar |
|---|
| 472 | (lambda (candidate) |
|---|
| 473 | (when (< i ac-candidate-menu-height) |
|---|
| 474 | (ac-menu-show-line ac-menu i) |
|---|
| 475 | (ac-menu-set-line-string ac-menu i candidate (if (= i ac-selection) 'ac-selection-face)) |
|---|
| 476 | (setq i (1+ i)))) |
|---|
| 477 | candidates) |
|---|
| 478 | ;; ensure lines visible |
|---|
| 479 | (if (and (> ac-menu-direction 0) |
|---|
| 480 | (> i (- |
|---|
| 481 | (max 1 (- (window-height) |
|---|
| 482 | (if mode-line-format 1 0) |
|---|
| 483 | (if header-line-format 1 0))) |
|---|
| 484 | (1+ (count-lines (window-start) (point)))))) |
|---|
| 485 | (recenter (- (1+ i)))) |
|---|
| 486 | (if (> i ac-menu-offset) |
|---|
| 487 | (let ((window-width (window-width)) |
|---|
| 488 | (right (- (+ (ac-menu-column ac-menu) ac-candidate-menu-width) |
|---|
| 489 | (window-hscroll)))) |
|---|
| 490 | (if (> right window-width) |
|---|
| 491 | (scroll-left (- right window-width))))) |
|---|
| 492 | ;; hide remaining lines |
|---|
| 493 | (if (> ac-menu-direction 0) |
|---|
| 494 | (while (< i ac-candidate-menu-height) |
|---|
| 495 | (ac-menu-hide-line ac-menu i) |
|---|
| 496 | (setq i (1+ i))) |
|---|
| 497 | (dotimes (i ac-menu-offset) |
|---|
| 498 | (ac-menu-hide-line ac-menu i))))) |
|---|
| 499 | |
|---|
| 500 | (defun ac-select-candidate (selection) |
|---|
| 501 | "Select candidate pointed by `SELECTION'." |
|---|
| 502 | (when ac-candidates |
|---|
| 503 | (ac-menu-set-line-string ac-menu ac-selection (nth (- ac-selection ac-menu-offset) ac-candidates)) |
|---|
| 504 | (ac-menu-set-line-string ac-menu selection (nth (- selection ac-menu-offset) ac-candidates) 'ac-selection-face) |
|---|
| 505 | (setq ac-selection selection))) |
|---|
| 506 | |
|---|
| 507 | (defun ac-default-find () |
|---|
| 508 | "Default implemention for `ac-find-function'." |
|---|
| 509 | (require 'thingatpt) |
|---|
| 510 | (car-safe (bounds-of-thing-at-point 'symbol))) |
|---|
| 511 | |
|---|
| 512 | (defun ac-start () |
|---|
| 513 | "Start completion." |
|---|
| 514 | (interactive) |
|---|
| 515 | (let ((point (funcall ac-find-function))) |
|---|
| 516 | (if (or (null point) |
|---|
| 517 | (and ac-menu |
|---|
| 518 | (/= point ac-point))) |
|---|
| 519 | (ac-abort) |
|---|
| 520 | (when (null ac-menu) |
|---|
| 521 | (ac-setup point) |
|---|
| 522 | (funcall ac-init-function)) |
|---|
| 523 | (setq ac-target (buffer-substring-no-properties point (point))) |
|---|
| 524 | (setq ac-limit ac-candidate-max) |
|---|
| 525 | (ac-update-candidates |
|---|
| 526 | (if (or ac-completing |
|---|
| 527 | (not (integerp ac-auto-start)) |
|---|
| 528 | (>= (length ac-target) ac-auto-start)) |
|---|
| 529 | (funcall ac-candidate-function)))))) |
|---|
| 530 | |
|---|
| 531 | (defun ac-trigger-command-p () |
|---|
| 532 | "Return non-nil if `this-command' is a trigger command." |
|---|
| 533 | (or (eq this-command 'self-insert-command) |
|---|
| 534 | (and ac-completing |
|---|
| 535 | (memq this-command |
|---|
| 536 | '(delete-backward-char |
|---|
| 537 | backward-delete-char |
|---|
| 538 | backward-delete-char-untabify))))) |
|---|
| 539 | |
|---|
| 540 | (defun ac-on-pre-command () |
|---|
| 541 | (if (and (not (ac-trigger-command-p)) |
|---|
| 542 | (or (not (symbolp this-command)) |
|---|
| 543 | (not (string-match "^ac-" (symbol-name this-command))))) |
|---|
| 544 | (ac-abort))) |
|---|
| 545 | |
|---|
| 546 | (defun ac-on-post-command () |
|---|
| 547 | (if (and ac-auto-start |
|---|
| 548 | (not isearch-mode) |
|---|
| 549 | (ac-trigger-command-p)) |
|---|
| 550 | (ac-start))) |
|---|
| 551 | |
|---|
| 552 | (defun auto-complete-mode-maybe () |
|---|
| 553 | "What buffer `auto-complete-mode' prefers." |
|---|
| 554 | (if (and (not (minibufferp (current-buffer))) |
|---|
| 555 | (memq major-mode ac-modes)) |
|---|
| 556 | (auto-complete-mode 1))) |
|---|
| 557 | |
|---|
| 558 | (require 'easy-mmode) |
|---|
| 559 | |
|---|
| 560 | (define-minor-mode auto-complete-mode |
|---|
| 561 | "AutoComplete mode" |
|---|
| 562 | :lighter " AC" |
|---|
| 563 | :group 'auto-complete |
|---|
| 564 | (if auto-complete-mode |
|---|
| 565 | (progn |
|---|
| 566 | (add-hook 'post-command-hook 'ac-on-post-command nil t) |
|---|
| 567 | (add-hook 'pre-command-hook 'ac-on-pre-command nil t) |
|---|
| 568 | (run-hooks 'auto-complete-mode-hook)) |
|---|
| 569 | (remove-hook 'post-command-hook 'ac-on-post-command t) |
|---|
| 570 | (remove-hook 'pre-command-hook 'ac-on-pre-command t) |
|---|
| 571 | (ac-abort))) |
|---|
| 572 | |
|---|
| 573 | (if (fboundp 'define-global-minor-mode) |
|---|
| 574 | (define-global-minor-mode global-auto-complete-mode |
|---|
| 575 | auto-complete-mode auto-complete-mode-maybe |
|---|
| 576 | :group 'auto-complete)) |
|---|
| 577 | |
|---|
| 578 | |
|---|
| 579 | |
|---|
| 580 | ;;;; Sources implementation |
|---|
| 581 | |
|---|
| 582 | (defvar ac-sources '(ac-source-words-in-buffer) |
|---|
| 583 | "Sources.") |
|---|
| 584 | |
|---|
| 585 | (defun ac-sources-init () |
|---|
| 586 | "Implementation for `ac-init-function' by sources." |
|---|
| 587 | (dolist (source ac-sources) |
|---|
| 588 | (if (symbolp source) |
|---|
| 589 | (setq source (symbol-value source))) |
|---|
| 590 | (let ((init-function (cdr-safe (assq 'init source)))) |
|---|
| 591 | (if init-function |
|---|
| 592 | (funcall init-function))))) |
|---|
| 593 | |
|---|
| 594 | (defun ac-sources-candidate () |
|---|
| 595 | "Implementation for `ac-cadidates-function' by sources." |
|---|
| 596 | (when (> (length ac-target) 0) |
|---|
| 597 | (let (candidates) |
|---|
| 598 | (dolist (source ac-sources) |
|---|
| 599 | (if (symbolp source) |
|---|
| 600 | (setq source (symbol-value source))) |
|---|
| 601 | (let* ((ac-limit (or (cdr-safe (assq 'limit source)) ac-limit)) |
|---|
| 602 | (requires (cdr-safe (assq 'requires source))) |
|---|
| 603 | cand) |
|---|
| 604 | (if (or (null requires) |
|---|
| 605 | (>= (length ac-target) requires)) |
|---|
| 606 | (setq cand |
|---|
| 607 | (delq nil |
|---|
| 608 | (mapcar (lambda (candidate) |
|---|
| 609 | (propertize candidate 'source source)) |
|---|
| 610 | (funcall (cdr (assq 'candidates source))))))) |
|---|
| 611 | (if (and (> ac-limit 1) |
|---|
| 612 | (> (length cand) ac-limit)) |
|---|
| 613 | (setcdr (nthcdr (1- ac-limit) cand) nil)) |
|---|
| 614 | (setq candidates (append candidates cand)))) |
|---|
| 615 | (delete-dups candidates)))) |
|---|
| 616 | |
|---|
| 617 | (defun ac-candidate-words-in-buffer () |
|---|
| 618 | "Default implemention for `ac-candidate-function'." |
|---|
| 619 | (if (> (length ac-target) 0) |
|---|
| 620 | (let ((i 0) |
|---|
| 621 | candidate |
|---|
| 622 | candidates |
|---|
| 623 | (regexp (concat "\\b" (regexp-quote ac-target) "\\(\\s_\\|\\sw\\)*\\b"))) |
|---|
| 624 | (save-excursion |
|---|
| 625 | ;; search backward |
|---|
| 626 | (goto-char ac-point) |
|---|
| 627 | (while (and (< i ac-limit) |
|---|
| 628 | (re-search-backward regexp nil t)) |
|---|
| 629 | (setq candidate (match-string-no-properties 0)) |
|---|
| 630 | (unless (member candidate candidates) |
|---|
| 631 | (push candidate candidates) |
|---|
| 632 | (setq i (1+ i)))) |
|---|
| 633 | ;; search backward |
|---|
| 634 | (goto-char (+ ac-point (length ac-target))) |
|---|
| 635 | (while (and (< i ac-limit) |
|---|
| 636 | (re-search-forward regexp nil t)) |
|---|
| 637 | (setq candidate (match-string-no-properties 0)) |
|---|
| 638 | (unless (member candidate candidates) |
|---|
| 639 | (push candidate candidates) |
|---|
| 640 | (setq i (1+ i)))) |
|---|
| 641 | (nreverse candidates))))) |
|---|
| 642 | |
|---|
| 643 | (defvar ac-source-words-in-buffer |
|---|
| 644 | '((candidates . ac-candidate-words-in-buffer)) |
|---|
| 645 | "Simple source like dabbrev.") |
|---|
| 646 | |
|---|
| 647 | (defvar ac-source-symbols |
|---|
| 648 | '((candidates |
|---|
| 649 | . (lambda () |
|---|
| 650 | (all-completions ac-target obarray)))) |
|---|
| 651 | "Source for Emacs lisp symbols.") |
|---|
| 652 | |
|---|
| 653 | (defvar ac-source-abbrev |
|---|
| 654 | `((candidates |
|---|
| 655 | . (lambda () |
|---|
| 656 | (all-completions ac-target local-abbrev-table))) |
|---|
| 657 | (action |
|---|
| 658 | . expand-abbrev)) |
|---|
| 659 | "Source for abbrev.") |
|---|
| 660 | |
|---|
| 661 | (defvar ac-source-files-in-current-dir |
|---|
| 662 | '((candidates |
|---|
| 663 | . (lambda () (all-completions ac-target (directory-files default-directory))))) |
|---|
| 664 | "Source for listing files in current directory.") |
|---|
| 665 | |
|---|
| 666 | (defvar ac-imenu-index nil |
|---|
| 667 | "Imenu index.") |
|---|
| 668 | |
|---|
| 669 | (defun ac-imenu-candidate () |
|---|
| 670 | (require 'imenu) |
|---|
| 671 | (let ((i 0) |
|---|
| 672 | (stack ac-imenu-index) |
|---|
| 673 | candidates |
|---|
| 674 | node) |
|---|
| 675 | (while (and stack |
|---|
| 676 | (< i ac-limit)) |
|---|
| 677 | (setq node (pop stack)) |
|---|
| 678 | (when (consp node) |
|---|
| 679 | (let ((car (car node)) |
|---|
| 680 | (cdr (cdr node))) |
|---|
| 681 | (if (consp cdr) |
|---|
| 682 | (mapcar (lambda (child) |
|---|
| 683 | (push child stack)) |
|---|
| 684 | cdr) |
|---|
| 685 | (when (and (stringp car) |
|---|
| 686 | (string-match (concat "^" (regexp-quote ac-target)) car)) |
|---|
| 687 | (push car candidates) |
|---|
| 688 | (setq i (1+ i))))))) |
|---|
| 689 | (nreverse candidates))) |
|---|
| 690 | |
|---|
| 691 | (defvar ac-source-imenu |
|---|
| 692 | '((init |
|---|
| 693 | . (lambda () |
|---|
| 694 | (require 'imenu) |
|---|
| 695 | (setq ac-imenu-index |
|---|
| 696 | (condition-case nil |
|---|
| 697 | (imenu--make-index-alist) |
|---|
| 698 | (error nil))))) |
|---|
| 699 | (candidates . ac-imenu-candidate)) |
|---|
| 700 | "Source for imenu.") |
|---|
| 701 | |
|---|
| 702 | (defun ac-yasnippet-candidate-1 (table) |
|---|
| 703 | (let ((hashtab (yas/snippet-table-hash table)) |
|---|
| 704 | (parent (yas/snippet-table-parent table)) |
|---|
| 705 | candidates) |
|---|
| 706 | (maphash (lambda (key value) |
|---|
| 707 | (push key candidates)) |
|---|
| 708 | hashtab) |
|---|
| 709 | (setq candidates (all-completions ac-target (nreverse candidates))) |
|---|
| 710 | (if parent |
|---|
| 711 | (setq candidates |
|---|
| 712 | (append candidates (ac-yasnippet-candidate-1 parent)))) |
|---|
| 713 | candidates)) |
|---|
| 714 | |
|---|
| 715 | (defun ac-yasnippet-candidate () |
|---|
| 716 | (require 'yasnippet) |
|---|
| 717 | (let ((table (yas/snippet-table major-mode))) |
|---|
| 718 | (if table |
|---|
| 719 | (ac-yasnippet-candidate-1 table)))) |
|---|
| 720 | |
|---|
| 721 | (defvar ac-source-yasnippet |
|---|
| 722 | '((candidates . ac-yasnippet-candidate) |
|---|
| 723 | (action . yas/expand) |
|---|
| 724 | (limit . 3)) |
|---|
| 725 | "Source for Yasnippet.") |
|---|
| 726 | |
|---|
| 727 | |
|---|
| 728 | |
|---|
| 729 | ;;;; Popup menu |
|---|
| 730 | |
|---|
| 731 | (defun ac-menu-line (menu) |
|---|
| 732 | "Line number of `MENU'." |
|---|
| 733 | (nth 0 menu)) |
|---|
| 734 | |
|---|
| 735 | (defun ac-menu-column (menu) |
|---|
| 736 | "Column of `MENU'." |
|---|
| 737 | (nth 1 menu)) |
|---|
| 738 | |
|---|
| 739 | (defun ac-menu-width (menu) |
|---|
| 740 | "Popup menu width of `MENU'." |
|---|
| 741 | (nth 2 menu)) |
|---|
| 742 | |
|---|
| 743 | (defun ac-menu-height (menu) |
|---|
| 744 | "Popup menu height of `MENU'." |
|---|
| 745 | (nth 3 menu)) |
|---|
| 746 | |
|---|
| 747 | (defun ac-menu-overlays (menu) |
|---|
| 748 | "Overlays that `MENU' contains." |
|---|
| 749 | (nth 4 menu)) |
|---|
| 750 | |
|---|
| 751 | (defun ac-menu-line-overlay (menu line) |
|---|
| 752 | "Return a overlay of `MENU' at `LINE'." |
|---|
| 753 | (aref (ac-menu-overlays menu) line)) |
|---|
| 754 | |
|---|
| 755 | (defun ac-menu-hide-line (menu line) |
|---|
| 756 | "Hide `LINE' in `MENU'." |
|---|
| 757 | (let ((overlay (ac-menu-line-overlay menu line))) |
|---|
| 758 | (overlay-put overlay 'invisible nil) |
|---|
| 759 | (overlay-put overlay 'after-string nil))) |
|---|
| 760 | |
|---|
| 761 | (defun ac-menu-show-line (menu line) |
|---|
| 762 | "Show `LINE' in `MENU'." |
|---|
| 763 | (let ((overlay (ac-menu-line-overlay menu line))) |
|---|
| 764 | (overlay-put overlay 'invisible t))) |
|---|
| 765 | |
|---|
| 766 | (defun ac-menu-set-line-string (menu line string &optional face) |
|---|
| 767 | "Set contents of `LINE' in `MENU'." |
|---|
| 768 | (let ((overlay (ac-menu-line-overlay menu line))) |
|---|
| 769 | (overlay-put overlay 'real-string string) |
|---|
| 770 | (funcall (overlay-get overlay 'set-string-function) overlay string face))) |
|---|
| 771 | |
|---|
| 772 | (defun ac-menu-create-line-string (menu string) |
|---|
| 773 | "Adjust `STRING' into `MENU'." |
|---|
| 774 | (let ((length 0) |
|---|
| 775 | (width 0) |
|---|
| 776 | (menu-width (ac-menu-width menu)) |
|---|
| 777 | (chars (append string nil))) |
|---|
| 778 | (while (and |
|---|
| 779 | chars |
|---|
| 780 | (<= (setq width (+ width (char-width (car chars)))) menu-width)) |
|---|
| 781 | (setq length (1+ length)) |
|---|
| 782 | (setq chars (cdr chars))) |
|---|
| 783 | (if (< length (length string)) |
|---|
| 784 | (setq string (substring string 0 length))) |
|---|
| 785 | (let ((string-width (string-width string))) |
|---|
| 786 | (if (< string-width menu-width) |
|---|
| 787 | (setq string (concat string |
|---|
| 788 | (make-string (- menu-width string-width) ? ))))) |
|---|
| 789 | string)) |
|---|
| 790 | |
|---|
| 791 | (defun ac-menu-hide (menu) |
|---|
| 792 | "Hide `MENU'." |
|---|
| 793 | (dotimes (i (ac-menu-height menu)) |
|---|
| 794 | (ac-menu-hide-line menu i))) |
|---|
| 795 | |
|---|
| 796 | (defun ac-menu-last-line-of-buffer () |
|---|
| 797 | (save-excursion |
|---|
| 798 | (not (eq (forward-line) 0)))) |
|---|
| 799 | |
|---|
| 800 | (defun ac-menu-create (line column width height) |
|---|
| 801 | "Create popup menu." |
|---|
| 802 | (save-excursion |
|---|
| 803 | (let ((overlays (make-vector height nil)) |
|---|
| 804 | (window (selected-window))) |
|---|
| 805 | (goto-line line) |
|---|
| 806 | (dotimes (i height) |
|---|
| 807 | (move-to-column column) |
|---|
| 808 | (let (overlay begin w current-column (prefix "") (postfix "")) |
|---|
| 809 | (setq current-column (current-column)) |
|---|
| 810 | (cond |
|---|
| 811 | ((> current-column column) |
|---|
| 812 | (backward-char) |
|---|
| 813 | (setq current-column (current-column)) |
|---|
| 814 | (if (< current-column column) |
|---|
| 815 | (setq prefix (make-string (- column current-column) ? )))) |
|---|
| 816 | ((< current-column column) |
|---|
| 817 | (setq prefix (make-string (- column current-column) ? )))) |
|---|
| 818 | |
|---|
| 819 | (setq begin (point)) |
|---|
| 820 | (setq w (+ width (length prefix))) |
|---|
| 821 | (while (and (not (eolp)) |
|---|
| 822 | (> w 0)) |
|---|
| 823 | (setq w (- w (char-width (char-after)))) |
|---|
| 824 | (forward-char)) |
|---|
| 825 | (if (< w 0) |
|---|
| 826 | (setq postfix (make-string (- w) ? ))) |
|---|
| 827 | (if (ac-menu-last-line-of-buffer) |
|---|
| 828 | (setq postfix (concat postfix "\n"))) |
|---|
| 829 | |
|---|
| 830 | (setq overlay (make-overlay begin (point))) |
|---|
| 831 | (overlay-put overlay 'window window) |
|---|
| 832 | (overlay-put overlay 'prefix prefix) |
|---|
| 833 | (overlay-put overlay 'postfix postfix) |
|---|
| 834 | (overlay-put overlay 'width width) |
|---|
| 835 | (overlay-put overlay 'set-string-function |
|---|
| 836 | (lambda (overlay string &optional face) |
|---|
| 837 | (overlay-put overlay |
|---|
| 838 | 'after-string |
|---|
| 839 | (concat (overlay-get overlay 'prefix) |
|---|
| 840 | (propertize (ac-menu-create-line-string menu string) 'face (or face 'ac-menu-face)) |
|---|
| 841 | (overlay-get overlay 'postfix))))) |
|---|
| 842 | (aset overlays i overlay)) |
|---|
| 843 | (forward-line)) |
|---|
| 844 | (let ((i 100)) |
|---|
| 845 | (mapcar (lambda (overlay) |
|---|
| 846 | (overlay-put overlay 'priority i) |
|---|
| 847 | (setq i (1+ i))) |
|---|
| 848 | (nreverse (append overlays nil)))) |
|---|
| 849 | (list line column width height overlays)))) |
|---|
| 850 | |
|---|
| 851 | (defun ac-menu-delete (menu) |
|---|
| 852 | "Delete `MENU'." |
|---|
| 853 | (mapcar 'delete-overlay (ac-menu-overlays menu))) |
|---|
| 854 | |
|---|
| 855 | (provide 'auto-complete) |
|---|
| 856 | ;;; auto-complete.el ends here |
|---|