;;; auto-complete.el --- Inline auto completion ;; Copyright (C) 2008, 2009 MATSUYAMA Tomohiro ;; Author: MATSUYAMA Tomohiro ;; Keywords: convenience ;; Version: 0.2.0 ;; 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 3 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. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; ;; This extension provides a way to complete with popup menu like: ;; ;; def-!- ;; +-----------------+ ;; |defun::::::::::::| ;; |defvar | ;; |defmacro | ;; | ... | ;; +-----------------+ ;; ;; You can complete by typing and selecting menu. ;; Enjoy! ;;; Qualification: ;; ;; This extension can work property on GNU Emacs 22 or higher. ;;; Installation: ;; ;; To use this extension, locate all .el files of this package to your load-path directory. ;; ;; $ cp auto-complete-x.x.x/*.el ~/.emacs.d/ ;; ;; And write following code into your .emacs. ;; ;; (require 'auto-complete) ;; (global-auto-complete-mode t) ;;; Tips: ;; ;; Use C-n/C-p to select candidates ;; -------------------------------- ;; ;; Add following code to your .emacs. ;; ;; (define-key ac-complete-mode-map "\C-n" 'ac-next) ;; (define-key ac-complete-mode-map "\C-p" 'ac-previous) ;; ;; ;; Don't start completion automatically ;; ------------------------------------ ;; ;; Add following code to your .emacs. ;; ;; (setq ac-auto-start nil) ;; (global-set-key "\M-/" 'ac-start) ;; ;; or ;; ;; ;; start completion when entered 3 characters ;; (setq ac-auto-start 3) ;; ;; ;; Stop completion ;; --------------- ;; ;; Add following code to your .emacs. ;; ;; (define-key ac-complete-mode-map "\M-/" 'ac-stop) ;; ;; Now you can stop completion by pressing M-/. ;; ;; ;; Completion by TAB ;; ----------------- ;; ;; Add following code to your .emacs. ;; ;; (define-key ac-complete-mode-map "\t" 'ac-complete) ;; (define-key ac-complete-mode-map "\r" nil) ;; ;; ;; Do What I Mean mode ;; ------------------- ;; ;; If DWIM (Do What I Mean) mode is enabled, ;; the following features is available: ;; ;; a. TAB (ac-expand) behave as completion (ac-complete) ;; when only one candidate is left ;; b. TAB (ac-expand) behave as completion (ac-complete) ;; after you select candidate ;; c. Disapear automatically when you ;; complete a candidate. ;; ;; DWIM mode is enabled by default. ;; You can enable this feature by ;; setting `ac-dwim' to t. ;; ;; (setq ac-dwim t) ;; ;; ;; Change default sources ;; ---------------------- ;; ;; (setq-default ac-sources '(ac-source-abbrev ac-source-words-in-buffer)) ;; ;; ;; Change sources for particular mode ;; ---------------------------------- ;; ;; (add-hook 'emacs-lisp-mode-hook ;; (lambda () ;; (setq ac-sources '(ac-source-words-in-buffer ac-source-symbols)))) ;;; History: ;; ;; 2008-03-18 ;; * auto-complete.el 0.2.0 released ;; ;; 2008-03-04 ;; * fixed menu position bug ;; ;; 2008-03-02 ;; * made a source be able to be just a function which returns candidates ;; * added ac-source-words-in-all-buffer ;; ;; 2008-03-01 ;; * added basic cache facility ;; ;; 2008-02-20 ;; * fixed menu position bug at long line (thanks rubikitch ) ;; * made dictionary source generator (ac-define-dictionary-source) ;; * devided into some files (auto-complete-ruby.el, auto-complete-yasnippet.el, etc) ;; ;; 2008-02-19 ;; * added ac-trigger-commands switch ;; ;; 2008-02-10 ;; * added ac-stop function (suggestion from Andy Stewart) ;; * added ac-override-local-map switch (suggestion from Andy Stewart) ;; ;; 2008-02-03 ;; * omni completion redesign ;; * ac-sources is now buffer local for every buffer ;; * fixed a menu position bug (thanks Andy Stewart) ;; * fixed byte-compile warnings (thanks Andy Stewart) ;; ;; 2008-01-22 ;; * added face/selection-face property for sources ;; * supported menu scroll ;; ;; 2008-01-20 ;; * omni completion ;; ;; 2008-12-24 ;; * suppress errors on command hook ;; ;; 2008-12-03 ;; * changed ac-dwim to nil by default ;; * made menu to be able to adjust width ;; ;; 2008-12-03 ;; * renamed ac-find-function to ac-prefix-function ;; * renamed ac-target to ac-prefix ;; ;; 2008-11-26 ;; * auto-complete.el 0.1.0 released ;; ;; 2008-11-19 ;; * thanks for Taiki SUGAWARA ;; * added source ac-source-abbrev ;; * added source ac-source-symbols ;; * added ac-expand-common to expand common part ;; ;; 2008-11-18 ;; * added ac-auto-start switch ;; * added ac-dwim switch ;; * changed menu popup behavior at end of window ;; * thanks rubikitch , kazu-yamamoto. ;; * fixed canceler bug ;; * changed to use overriding-local-map instead of minor mode map ;; * changed default key bindings ;; ;; 2008-11-16 ;; * supported candidates by using sources ;; * added automatically start swtich ;; * fixed some bug ;; * added source ac-source-files-in-current-dir ;; * added source ac-source-words-in-buffer ;; * added source ac-source-yasnippet ;; * renamed ac-enum-candidates-function to ac-candidate-function ;; * renamed ac-find-target-function to ac-find-function ;; * ac-find-function and ac-candidate-function is not buffer local variable now ;; * made candidates visible when you are end of line ;; ;; 2008-11-11 ;; * by reporting from rubikitch ;; * renamed hook name ;; * registered backward-delete-char as special command ;; * fixed code for creating candidates ;; * made auto-complete disabled when isearch-mode enabled ;; * added some major-mode into ac-modes ;; ;; 2008-11-09 ;; * auto-complete.el 0.0.1 released ;; * fixed double-width character displaying problem ;; * fixed menu position following tab character ;; * made candidates visible when you are end of window ;;; TODO: ;; ;; - test facility ;; - support composed chars ;; - fuzzy match ;; - minibuffer completion ;; - dictionary ;; - documentation ;; - performance issue (cache issue) ;; - fix narrowing bug (reported by Yuto Hayamizu ) ;; - care about undo (buffer-disable-undo) ;; - scroll bar (visual) ;; - show description ;; - semantic ;; - use cl ;; - icon ;; - refactoring (especially menu) ;; - linum.el bug (reported by Andy Stewart) ;; - flymake bug (reported by TiagoCamargo) ;;; Code: (defgroup auto-complete nil "Auto completion with popup menu" :group 'convenience :prefix "auto-complete-") (defcustom ac-candidate-menu-height 10 "Max height of candidate menu." :type 'number :group 'auto-complete) (defcustom ac-candidate-max 10 "Max of number of candidates." :type 'number :group 'auto-complete) (defcustom ac-modes '(emacs-lisp-mode lisp-interaction-mode c-mode cc-mode c++-mode java-mode perl-mode cperl-mode python-mode ruby-mode ecmascript-mode javascript-mode js2-mode php-mode css-mode makefile-mode sh-mode fortran-mode f90-mode ada-mode xml-mode sgml-mode) "Major modes `auto-complete-mode' can run on." :type '(list symbol) :group 'auto-complete) (defcustom ac-trigger-commands '(self-insert-command) "Trigger commands that specify whether `auto-complete' should start or not." :type '(list symbol) :group 'auto-complete) (defcustom ac-auto-start t "Non-nil means completion will be started automatically. Positive integer means if a length of a word you entered is larger than the value, completion will be started automatically. If you specify `nil', never be started automatically." :group 'auto-complete) (defcustom ac-dwim nil "Non-nil means `auto-complete' works based on Do What I Mean." :type 'boolean :group 'auto-complete) (defcustom ac-override-local-map nil "Non-nil mean use `ac-complete-mode-map' override local map. Please set it to non-nil only if you faced to some problem about minor-mode keymap conflicts." :type 'boolean :group 'auto-complete) (defface ac-candidate-face '((t (:background "lightgray" :foreground "black"))) "Face for candidate." :group 'auto-complete) (defface ac-selection-face '((t (:background "blue" :foreground "white"))) "Face for the selected candidate." :group 'auto-complete) (defvar auto-complete-mode-hook nil "Hook for `auto-complete-mode'.") (defvar ac-menu nil "Menu instance.") (defvar ac-menu-direction 1 "Positive integer means `ac-menu' grows forward. Or, `ac-menu' grows backward.") (defvar ac-menu-offset 0 "Offset to contents.") (defvar ac-menu-scroll 0 "Scroll top of `ac-menu'.") (defvar ac-completing nil "Non-nil means `auto-complete-mode' is now working on completion.") (defvar ac-saved-window-start nil "Saved window start value for restore.") (defvar ac-saved-window-hscroll nil "Saved window hscroll value for restore.") (defvar ac-buffer nil "A buffer where auto-complete is started.") (defvar ac-point nil "Start point of prefix.") (defvar ac-old-point nil "Previous start point of prefix.") (defvar ac-prefix nil "Prefix.") (defvaralias 'ac-target 'ac-prefix) (defvar ac-limit 0 "Limit of number of candidates.") (defvar ac-candidates nil "Current candidates.") (defvar ac-selection nil "Current candidate index.") (defvar ac-dwim-enable nil "Non-nil means DWIM completion will be allowed.") (defvar ac-setup-function 'ac-sources-setup "This function will be called when `auto-complete-mode' is enabled.") (defvar ac-prefix-function 'ac-sources-prefix "When `auto-complete-mode' finds it can start completion or update candidates, it will call this function to find a start point of the prefix. If this function returns a point `auto-complete-mode' will set the substring between the point and current point to `ac-prefix'. And also it will start completion or update candidates by using the `ac-prefix'. If this function returns `nil', `auto-complete-mode' ignore starting completion or stop completing.") (defvaralias 'ac-find-function 'ac-prefix-function) (defvar ac-init-function 'ac-sources-init "This function will be called when candidate menu is setupped.") (defvar ac-cleanup-function 'ac-sources-cleanup "This function will be called when candidate menu is cleanupped") (defvar ac-candidate-function 'ac-sources-candidate "This function can return candidates as list by using the `TARGET' that is given as a first argument.") (defvar ac-complete-mode-map (let ((map (make-sparse-keymap))) (define-key map "\t" 'ac-expand) (define-key map "\r" 'ac-complete) (define-key map [down] 'ac-next) (define-key map [up] 'ac-previous) map) "Keymap for completion.") (defvar ac-saved-local-map nil "Old keymap before `auto-complete' activated.") ;;;; Auto completion (defun ac-setup-menu (point width) "Setup popup menu." (when ac-menu ;; reposition (ac-menu-delete ac-menu) (setq ac-menu nil)) (save-excursion (goto-char point) (let ((column (ac-current-physical-column)) (line (line-number-at-pos))) (setq ac-saved-window-start (window-start)) (setq ac-saved-window-hscroll (window-hscroll)) (setq ac-menu-direction (if (and (> line ac-candidate-menu-height) (> ac-candidate-menu-height (- (max 1 (- (window-height) (if mode-line-format 1 0) (if header-line-format 1 0))) (count-lines (window-start) (point))))) -1 1)) (let ((window-width (window-width)) (right (- (+ column width) (window-hscroll)))) (if (and (> right window-width) (>= right width) (>= column width)) (setq column (- column width)))) (if (> ac-menu-direction 0) (progn (forward-line) (if (eq line (line-number-at-pos)) (newline) (forward-line -1)) (setq ac-menu (ac-menu-create (1+ line) column width ac-candidate-menu-height)) (setq ac-point point)) (setq ac-menu (ac-menu-create (- line ac-candidate-menu-height) column width ac-candidate-menu-height)) (setq ac-point point))))) (defun ac-cleanup () "Destroy popup menu." (ac-deactivate-mode-map) (when ac-menu (ac-menu-delete ac-menu) (set-window-start (selected-window) ac-saved-window-start) (set-window-hscroll (selected-window) ac-saved-window-hscroll)) (setq ac-menu nil) (setq ac-menu-scroll 0) (setq ac-completing nil) (setq ac-point nil) (setq ac-candidates nil) (setq ac-selection 0) (setq ac-selection-scroll-top 0) (funcall ac-cleanup-function)) (defun ac-activate-mode-map () "Activate `ac-complete-mode-map'." (if ac-override-local-map (progn (setq ac-saved-local-map overriding-terminal-local-map) (if (eq ac-saved-local-map ac-complete-mode-map) ;; maybe never reach here (setq ac-saved-local-map nil)) (setq overriding-terminal-local-map ac-complete-mode-map)) ;; rearrange ac-mode-map pair first (assq-delete-all 'ac-completing minor-mode-map-alist) (push (cons 'ac-completing ac-complete-mode-map) minor-mode-map-alist))) (defun ac-deactivate-mode-map () "Deactivate `ac-complete-mode-map'." (when (and ac-override-local-map (eq overriding-terminal-local-map ac-complete-mode-map)) (setq overriding-terminal-local-map ac-saved-local-map) (setq ac-saved-local-map nil))) (defun ac-next () "Select next candidate." (interactive) (if (interactive-p) (setq ac-dwim-enable t)) (if ac-candidates (ac-select-candidate (let ((selection (1+ ac-selection))) (if (= selection (+ ac-menu-offset (min ac-candidate-menu-height (length ac-candidates)))) (if (< (+ (- ac-selection ac-menu-offset) ac-menu-scroll) (1- (length ac-candidates))) (prog1 ac-selection (setq ac-menu-scroll (1+ ac-menu-scroll)) (ac-redraw-candidates)) (setq ac-menu-scroll 0) (ac-redraw-candidates) ac-menu-offset) selection))))) (defun ac-previous () "Select previous candidate." (interactive) (if (interactive-p) (setq ac-dwim-enable t)) (if ac-candidates (ac-select-candidate (let ((selection (1- ac-selection))) (if (< selection ac-menu-offset) (if (= ac-menu-scroll 0) (prog1 (1- (+ ac-menu-offset (min ac-candidate-menu-height (length ac-candidates)))) (setq ac-menu-scroll (- (length ac-candidates) (min ac-candidate-menu-height (length ac-candidates)))) (ac-redraw-candidates)) (setq ac-menu-scroll (1- ac-menu-scroll)) (ac-redraw-candidates) ac-selection) selection))))) (defun ac-expand-1 () "Try expansion." (let ((string (overlay-get (ac-menu-line-overlay ac-menu ac-selection) 'real-string))) (delete-region ac-point (point)) (insert string) (setq ac-prefix string))) (defun ac-expand () "Try expansion but select next if expanded twice." (interactive) (if (and ac-dwim ac-dwim-enable) (ac-complete) (let ((target ac-prefix) (string (ac-expand-1))) (when (equal target string) (ac-next) (ac-expand-1))))) (defun ac-expand-common () "Try expansion common part." (interactive) (let ((common (try-completion ac-prefix ac-candidates)) (buffer-undo-list t)) (when (stringp common) (delete-region ac-point (point)) (insert common) (setq ac-prefix common)))) (defun ac-complete () "Try completion." (interactive) (let* ((candidate (ac-get-selected-candidate)) (action (ac-get-candidate-action candidate))) (ac-expand-1) (if action (funcall action)) (ac-abort))) (defun ac-abort () "Abort completion." (ac-cleanup)) (defun ac-stop () "Stop completiong." (interactive) (ac-abort)) (defun ac-redraw-candidates () "Redraw the menu contents." (let ((i ac-menu-offset)) ;; show line and set string to the line (mapc (lambda (candidate) (when (< i ac-candidate-menu-height) (ac-menu-show-line ac-menu i) (ac-menu-set-line-string ac-menu i candidate (if (= i ac-selection) (or (ac-get-candidate-property 'selection-face candidate) 'ac-selection-face) (or (ac-get-candidate-property 'candidate-face candidate) 'ac-candidate-face))) (setq i (1+ i)))) (nthcdr ac-menu-scroll ac-candidates)) ;; ensure lines visible (if (and (> ac-menu-direction 0) (> i (- (max 1 (- (window-height) (if mode-line-format 1 0) (if header-line-format 1 0))) (count-lines (window-start) (point))))) (recenter (- (1+ i)))) (if (> i ac-menu-offset) (let ((window-width (window-width)) (right (- (+ (ac-menu-column ac-menu) (ac-menu-width ac-menu)) (window-hscroll)))) (if (> right window-width) (scroll-left (- right window-width))))) ;; hide remaining lines (if (> ac-menu-direction 0) (while (< i ac-candidate-menu-height) (ac-menu-hide-line ac-menu i) (setq i (1+ i))) (dotimes (i ac-menu-offset) (ac-menu-hide-line ac-menu i))))) (defun ac-update-candidates (candidates) "Update candidates of popup menu." (setq ac-menu-offset (if (> ac-menu-direction 0) 0 (- ac-candidate-menu-height (min ac-candidate-menu-height (length candidates))))) (setq ac-selection ac-menu-offset) (setq ac-candidates candidates) (setq ac-dwim-enable (= (length candidates) 1)) (if candidates (progn (setq ac-completing t) (ac-activate-mode-map)) (setq ac-completing nil) (ac-deactivate-mode-map)) (ac-redraw-candidates)) (defun ac-get-selected-candidate () (overlay-get (ac-menu-line-overlay ac-menu ac-selection) 'real-string)) (defun ac-get-candidate-action (candidate) (ac-get-candidate-property 'action candidate)) (defun ac-propertize-candidate (candidate &rest properties) (apply 'propertize candidate properties)) (defun ac-get-candidate-property (prop candidate) (get-text-property 0 prop candidate)) (defun ac-select-candidate (selection) "Select candidate pointed by `SELECTION'." (when ac-candidates (let ((c1 (nth (+ (- ac-selection ac-menu-offset) ac-menu-scroll) ac-candidates)) (c2 (nth (+ (- selection ac-menu-offset) ac-menu-scroll) ac-candidates))) (ac-menu-set-line-string ac-menu ac-selection c1 (or (ac-get-candidate-property 'candidate-face c1) 'ac-candidate-face)) (ac-menu-set-line-string ac-menu selection c2 (or (ac-get-candidate-property 'selection-face c2) 'ac-selection-face)) (setq ac-selection selection)))) (defun ac-start () "Start completion." (interactive) (let* ((point (save-excursion (funcall ac-prefix-function))) (reposition (not (equal ac-point point)))) (if (null point) (ac-abort) (setq ac-buffer (current-buffer)) (setq ac-point point) (when (not (equal ac-point ac-old-point)) (setq ac-old-point point)) (setq ac-prefix (buffer-substring-no-properties point (point))) (setq ac-limit ac-candidate-max) (if (or reposition (null ac-menu)) (save-excursion (funcall ac-init-function))) (let* ((candidates (if (or ac-completing (not (integerp ac-auto-start)) (>= (length ac-prefix) ac-auto-start)) (save-excursion (funcall ac-candidate-function)))) (current-width (if ac-menu (ac-menu-width ac-menu) 0)) (width (let ((w '(0)) s) (dotimes (i ac-candidate-menu-height) (setq s (nth i candidates)) (if (stringp s) (push (string-width s) w))) (apply 'max w)))) (if (or reposition (null ac-menu) (> width current-width) (< width (- current-width 10))) (ac-setup-menu point (* (ceiling (/ width 10.0)) 10))) (if (and ac-dwim (= (length candidates) 1) (equal (car candidates) ac-prefix) (null (ac-get-candidate-action (car candidates)))) (setq candidates nil)) (ac-update-candidates candidates))))) (defun ac-trigger-command-p () "Return non-nil if `this-command' is a trigger command." (or (memq this-command ac-trigger-commands) (and ac-completing (memq this-command '(delete-backward-char backward-delete-char backward-delete-char-untabify))))) (defun ac-current-physical-column () "Current physical column. (not logical column)" (- (point) (save-excursion (vertical-motion 0) (point)))) (defun ac-on-pre-command () (progn ; ignore-errors (if (and (not (ac-trigger-command-p)) (or (not (symbolp this-command)) (not (string-match "^ac-" (symbol-name this-command))))) (ac-abort)))) (defun ac-on-post-command () (progn ; ignore-errors (if (and (or ac-auto-start ac-completing) (not isearch-mode) (ac-trigger-command-p)) (ac-start)))) (defun auto-complete-mode-maybe () "What buffer `auto-complete-mode' prefers." (if (and (not (minibufferp (current-buffer))) (memq major-mode ac-modes)) (auto-complete-mode 1))) (require 'easy-mmode) (define-minor-mode auto-complete-mode "AutoComplete mode" :lighter " AC" :group 'auto-complete (if auto-complete-mode (progn (funcall ac-setup-function) (add-hook 'post-command-hook 'ac-on-post-command nil t) (add-hook 'pre-command-hook 'ac-on-pre-command nil t) (run-hooks 'auto-complete-mode-hook)) (remove-hook 'post-command-hook 'ac-on-post-command t) (remove-hook 'pre-command-hook 'ac-on-pre-command t) (ac-abort))) (define-global-minor-mode global-auto-complete-mode auto-complete-mode auto-complete-mode-maybe :group 'auto-complete) ;;;; Basic cache facility (defvar ac-clear-variables-after-save nil) (defun ac-clear-variable-after-save (variable) (push variable ac-clear-variables-after-save)) (defun ac-clear-variables-after-save () (dolist (variable ac-clear-variables-after-save) (set variable nil))) ;;;; Sources implementation (defvar ac-sources '(ac-source-words-in-buffer) "Sources for completion. Source takes a form of just function which returns candidates or alist: init INIT-FUNC INIT-FUNC will be called before creating candidate every time. candidates CANDIDATE-FUNC CANDIDATE-FUNC will return a list of string as candidates. CANDIDATE-FUNC should care about `ac-limit' that is specified at limit for performance. action ACTION-FUNC ACTION-FUNC will be called when `ac-complete' is called. limit LIMIT-NUM A limit of candidates. requires REQUIRES-NUM This source will be included when `ac-prefix' length is larger than REQUIRES-NUM.") (make-variable-buffer-local 'ac-sources) (defvar ac-sources-prefix-function 'ac-sources-prefix-default "Default prefix function for sources. You should override this variable instead of ac-prefix-function.") (defvar ac-current-sources nil "Current working sources.") (defvar ac-omni-completion-sources nil "An alist of REGEXP and SOURCES. If matched regexp, switch to omni-completion mode and use SOURCES as `ac-sources'.") (make-variable-buffer-local 'ac-omni-completion-sources) (defvar ac-sources-omni-completion nil "Non-nil means `auto-complete-mode' is now working on omni-completion.") (defun ac-sources-setup () "Implementation for `ac-setup-function' by sources." (make-local-variable 'ac-clear-variables-after-save) (add-hook 'after-save-hook 'ac-clear-variables-after-save nil t)) (defun ac-sources-init () "Implementation for `ac-init-function' by sources." (or ac-current-sources (setq ac-current-sources ac-sources)) (dolist (source ac-current-sources) (let ((init-function (ac-get-source-property 'init source))) (if init-function (funcall init-function))))) (defun ac-sources-cleanup () "Implementation for `ac-cleanup-function' by sources." (setq ac-current-sources nil) (setq ac-sources-omni-completion nil)) (defun ac-sources-prefix () "Implemention for `ac-prefix-function' by sources." (let (point) (dolist (pair ac-omni-completion-sources) (when (looking-back (car pair)) (setq ac-current-sources (cdr pair)) (setq ac-sources-omni-completion t) (setq ac-completing t) (setq point (match-end 0)))) (or point (if (and ac-completing ac-sources-omni-completion) ac-point (setq ac-current-sources ac-sources) (setq ac-sources-omni-completion nil) (funcall ac-sources-prefix-function))))) (defun ac-sources-prefix-default () "Default implementation for `ac-sources-prefix-function'." (require 'thingatpt) (car-safe (bounds-of-thing-at-point 'symbol))) (defun ac-sources-candidate () "Implementation for `ac-cadidates-function' by sources." (let (candidates) (dolist (source ac-current-sources) (let* ((ac-limit (or (ac-get-source-property 'limit source) ac-limit)) (requires (ac-get-source-property 'requires source)) cand) (when (or ac-sources-omni-completion (>= (length ac-prefix) (if (integerp requires) requires 1))) (setq cand (delq nil (mapcar (lambda (candidate) (ac-propertize-candidate candidate 'action (ac-get-source-property 'action source) 'face (ac-get-source-property 'candidate-face source) 'candidate-face (ac-get-source-property 'candidate-face source) 'selection-face (ac-get-source-property 'selection-face source))) (funcall (ac-get-source-property 'candidates source)))))) (if (and (> ac-limit 1) (> (length cand) ac-limit)) (setcdr (nthcdr (1- ac-limit) (copy-sequence cand)) nil)) (setq candidates (append candidates cand)))) (delete-dups candidates))) (defun ac-get-source-property (property source) (if (symbolp source) (setq source (symbol-value source))) (if (and (functionp source) (eq property 'candidates)) source (if (consp source) (assoc-default property source)))) ;;;; Standard sources (defun ac-candidate-words-in-buffer () "Default implemention for `ac-candidate-function'." (if (> (length ac-prefix) 0) (let ((i 0) candidate candidates (regexp (concat "\\b" (regexp-quote ac-prefix) "\\(\\s_\\|\\sw\\)*\\b"))) (save-excursion ;; search backward (goto-char ac-point) (while (and (< i ac-limit) (re-search-backward regexp nil t)) (setq candidate (match-string-no-properties 0)) (unless (member candidate candidates) (push candidate candidates) (setq i (1+ i)))) ;; search backward (goto-char (+ ac-point (length ac-prefix))) (while (and (< i ac-limit) (re-search-forward regexp nil t)) (setq candidate (match-string-no-properties 0)) (unless (member candidate candidates) (push candidate candidates) (setq i (1+ i)))) (nreverse candidates))))) (defvar ac-source-words-in-buffer '((candidates . ac-candidate-words-in-buffer)) "Source for completing words in current buffer.") (defvar ac-word-index nil "Word index for individual buffer.") (ac-clear-variable-after-save 'ac-word-index) (defvar ac-source-words-in-all-buffer '((init . (lambda () (dolist (buffer (buffer-list)) (with-current-buffer buffer (if (not (local-variable-p 'ac-word-index)) (make-local-variable 'ac-word-index)) (if (eq buffer ac-buffer) (setq ac-word-index (ac-candidate-words-in-buffer)) (if (and (null ac-word-index) (< (buffer-size) 102400)) (save-excursion (goto-char (point-min)) (while (re-search-forward "\\b\\(\\s_\\|\\sw\\)+\\b" nil t) (let ((candidate (match-string-no-properties 0))) (if (not (member candidate ac-word-index)) (push candidate ac-word-index)))) (setq ac-word-index (nreverse ac-word-index))))))))) (candidates . (lambda () (let ((candidates) (buffers (buffer-list))) (while (and (< (length candidates) ac-limit) buffers) (setq candidates (append candidates (all-completions ac-prefix (buffer-local-value 'ac-word-index (car buffers))))) (setq buffers (cdr buffers))) candidates)))) "Source for completing words in all buffer.") (defvar ac-source-symbols '((candidates . (lambda () (all-completions ac-prefix obarray)))) "Source for Emacs lisp symbols.") (defvar ac-source-abbrev `((candidates . (lambda () (all-completions ac-prefix local-abbrev-table))) (action . expand-abbrev)) "Source for abbrev.") (defvar ac-source-files-in-current-dir '((candidates . (lambda () (all-completions ac-prefix (directory-files default-directory))))) "Source for listing files in current directory.") (defun ac-filename-candidate () (let ((dir (file-name-directory ac-prefix))) (ignore-errors (delq nil (mapcar (lambda (file) (if (not (member file '("./" "../"))) (concat dir file))) (file-name-all-completions (file-name-nondirectory ac-prefix) dir)))))) (defvar ac-source-filename '((candidates . ac-filename-candidate)) "Source for completing file name.") (defvar ac-imenu-index nil "Imenu index.") (defun ac-imenu-candidate () (require 'imenu) (let ((i 0) (stack ac-imenu-index) candidates node) (while (and stack (< i ac-limit)) (setq node (pop stack)) (when (consp node) (let ((car (car node)) (cdr (cdr node))) (if (consp cdr) (mapc (lambda (child) (push child stack)) cdr) (when (and (stringp car) (string-match (concat "^" (regexp-quote ac-prefix)) car)) (push car candidates) (setq i (1+ i))))))) (nreverse candidates))) (defvar ac-source-imenu '((init . (lambda () (require 'imenu) (setq ac-imenu-index (ignore-errors (imenu--make-index-alist))))) (candidates . ac-imenu-candidate)) "Source for imenu.") (defmacro ac-define-dictionary-source (name list) "Define dictionary source named `NAME'. `LIST' is a list of string. This is useful if you just want to define a dictionary/keywords source." `(defvar ,name '((candidates . (lambda () (all-completions ac-prefix ,list)))))) ;;;; Popup menu (defun ac-menu-line (menu) "Line number of `MENU'." (nth 0 menu)) (defun ac-menu-column (menu) "Column of `MENU'." (nth 1 menu)) (defun ac-menu-width (menu) "Popup menu width of `MENU'." (nth 2 menu)) (defun ac-menu-height (menu) "Popup menu height of `MENU'." (nth 3 menu)) (defun ac-menu-overlays (menu) "Overlays that `MENU' contains." (nth 4 menu)) (defun ac-menu-line-overlay (menu line) "Return a overlay of `MENU' at `LINE'." (aref (ac-menu-overlays menu) line)) (defun ac-menu-hide-line (menu line) "Hide `LINE' in `MENU'." (let ((overlay (ac-menu-line-overlay menu line))) (overlay-put overlay 'invisible nil) (overlay-put overlay 'after-string nil))) (defun ac-menu-show-line (menu line) "Show `LINE' in `MENU'." (let ((overlay (ac-menu-line-overlay menu line))) (overlay-put overlay 'invisible t))) (defun ac-menu-set-line-string (menu line string &optional face) "Set contents of `LINE' in `MENU'." (let ((overlay (ac-menu-line-overlay menu line))) (overlay-put overlay 'real-string string) (funcall (overlay-get overlay 'set-string-function) menu overlay string face))) (defun ac-menu-create-line-string (menu string) "Adjust `STRING' into `MENU'." (let ((length 0) (width 0) (menu-width (ac-menu-width menu)) (chars (append string nil))) (while (and chars (<= (setq width (+ width (char-width (car chars)))) menu-width)) (setq length (1+ length)) (setq chars (cdr chars))) (if (< length (length string)) (setq string (substring string 0 length))) (let ((string-width (string-width string))) (if (< string-width menu-width) (setq string (concat string (make-string (- menu-width string-width) ? ))))) string)) (defun ac-menu-hide (menu) "Hide `MENU'." (dotimes (i (ac-menu-height menu)) (ac-menu-hide-line menu i))) (defun ac-menu-last-line-of-buffer () (save-excursion (not (eq (forward-line) 0)))) (defun ac-menu-create (line column width height) "Create popup menu." (save-excursion (let ((overlays (make-vector height nil)) (window (selected-window))) (goto-line line) (dotimes (i height) (move-to-column column) (let (overlay begin w current-column (prefix "") (postfix "")) (setq current-column (current-column)) (cond ((> current-column column) (backward-char) (setq current-column (current-column)) (if (< current-column column) (setq prefix (make-string (- column current-column) ? )))) ((< current-column column) (setq prefix (make-string (- column current-column) ? )))) (setq begin (point)) (setq w (+ width (length prefix))) (while (and (not (eolp)) (> w 0)) (setq w (- w (char-width (char-after)))) (forward-char)) (if (< w 0) (setq postfix (make-string (- w) ? ))) (if (ac-menu-last-line-of-buffer) (setq postfix (concat postfix "\n"))) (setq overlay (make-overlay begin (point))) (overlay-put overlay 'window window) (overlay-put overlay 'prefix prefix) (overlay-put overlay 'postfix postfix) (overlay-put overlay 'width width) (overlay-put overlay 'set-string-function (lambda (menu overlay string &optional face) (overlay-put overlay 'after-string (concat (overlay-get overlay 'prefix) (propertize (ac-menu-create-line-string menu string) 'face face) (overlay-get overlay 'postfix))))) (aset overlays i overlay)) (forward-line)) (let ((i 100)) (mapc (lambda (overlay) (overlay-put overlay 'priority i) (setq i (1+ i))) (nreverse (append overlays nil)))) (list line column width height overlays)))) (defun ac-menu-delete (menu) "Delete `MENU'." (mapcar 'delete-overlay (ac-menu-overlays menu))) (provide 'auto-complete) ;;; auto-complete.el ends here