;; Copyright (C) 2009 Nikolaj Schumacher
;;
;; Author: Nikolaj Schumacher <bugs * nschum de>
-;; Version: 0.3.1
+;; Version: 0.4
;; Keywords: abbrev, convenience, matchis
;; URL: http://nschum.de/src/emacs/company/
;; Compatibility: GNU Emacs 22.x, GNU Emacs 23.x
;;
;;; Change Log:
;;
+;; 2009-04-18 (0.4)
+;; Automatic completion is now aborted if the prefix gets too short.
;; Added option `company-dabbrev-time-limit'.
;; `company-backends' now supports merging back-ends.
;; Added back-end `company-dabbrev-code' for generic code.
(company-files . "Files")
(company-gtags . "GNU Global")
(company-ispell . "ispell")
+ (company-keywords . "Programming language keywords")
(company-nxml . "nxml")
(company-oddmuse . "Oddmuse")
(company-semantic . "CEDET Semantic")
(defcustom company-backends '(company-elisp company-nxml company-css
company-semantic company-xcode
- (company-gtags company-etags company-dabbrev-code)
+ (company-gtags company-etags company-dabbrev-code
+ company-keywords)
company-oddmuse company-files company-dabbrev)
"*The list of active back-ends (completion engines).
Each list elements can itself be a list of back-ends. In that case their
;; Emacs calculates the active keymaps before reading the event. That means we
;; cannot change the keymap from a timer. So we send a bogus command.
(defun company-ignore ()
- (interactive))
+ (interactive)
+ (setq this-command last-command))
(global-set-key '[31415926] 'company-ignore)
"Non-nil, if explicit completion took place.")
(make-variable-buffer-local 'company--explicit-action)
+(defvar company--point-max nil)
+(make-variable-buffer-local 'company--point-max)
+
(defvar company--this-command nil)
(defvar company-point nil)
;; Return non-nil if active.
company-candidates)
-(defsubst company-incremental-p (old-prefix new-prefix)
- (and (> (length new-prefix) (length old-prefix))
- (equal old-prefix (substring new-prefix 0 (length old-prefix)))))
-
(defun company-require-match-p ()
(let ((backend-value (company-call-backend 'require-match)))
(or (eq backend-value t)
"Return non-nil, if input starts with punctuation or parentheses."
(memq (char-syntax (string-to-char input)) '(?. ?\( ?\))))
-(defun company-auto-complete-p (beg end)
+(defun company-auto-complete-p (input)
"Return non-nil, if input starts with punctuation or parentheses."
- (and (> end beg)
- (if (functionp company-auto-complete)
+ (and (if (functionp company-auto-complete)
(funcall company-auto-complete)
company-auto-complete)
(if (functionp company-auto-complete-chars)
- (funcall company-auto-complete-chars (buffer-substring beg end))
+ (funcall company-auto-complete-chars input)
(if (consp company-auto-complete-chars)
- (memq (char-syntax (char-after beg)) company-auto-complete-chars)
- (string-match (buffer-substring beg (1+ beg))
- company-auto-complete-chars)))))
+ (memq (char-syntax (string-to-char input))
+ company-auto-complete-chars)
+ (string-match (substring input 0 1) company-auto-complete-chars)))))
+
+(defun company--incremental-p ()
+ (and (> (point) company-point)
+ (> (point-max) company--point-max)
+ (equal (buffer-substring (- company-point (length company-prefix))
+ company-point)
+ company-prefix)))
+
+(defsubst company--string-incremental-p (old-prefix new-prefix)
+ (and (> (length new-prefix) (length old-prefix))
+ (equal old-prefix (substring new-prefix 0 (length old-prefix)))))
-(defun company-continue ()
+(defun company--continue-failed (new-prefix)
+ (when (company--incremental-p)
+ (let ((input (buffer-substring-no-properties (point) company-point)))
+ (cond
+ ((company-auto-complete-p input)
+ ;; auto-complete
+ (save-excursion
+ (goto-char company-point)
+ (company-complete-selection)
+ nil))
+ ((and (company--string-incremental-p company-prefix new-prefix)
+ (company-require-match-p))
+ ;; wrong incremental input, but required match
+ (backward-delete-char (length input))
+ (ding)
+ (message "Matching input is required")
+ company-candidates)
+ ((equal company-prefix (car company-candidates))
+ ;; last input was actually success
+ (company-cancel company-prefix)
+ nil)))))
+
+(defun company--continue ()
(when (company-call-backend 'no-cache company-prefix)
;; Don't complete existing candidates, fetch new ones.
(setq company-candidates-cache nil))
- (let ((new-prefix (company-call-backend 'prefix)))
- (if (= (- (point) (length new-prefix))
- (- company-point (length company-prefix)))
- (unless (or (equal company-prefix new-prefix)
- (let ((c (company-calculate-candidates new-prefix)))
- ;; t means complete/unique.
- (if (eq c t)
- (progn (company-cancel new-prefix) t)
- (when (consp c)
- (setq company-prefix new-prefix)
- (company-update-candidates c)
- t))))
- (if (not (and (company-incremental-p company-prefix new-prefix)
- (company-require-match-p)))
- (progn
- (when (equal company-prefix (car company-candidates))
- ;; cancel, but last input was actually success
- (company-cancel company-prefix))
- (setq company-candidates nil))
- (backward-delete-char (length new-prefix))
- (insert company-prefix)
- (ding)
- (message "Matching input is required")))
- (when (company-auto-complete-p company-point (point))
- (save-excursion
- (goto-char company-point)
- (company-complete-selection)))
- (setq company-candidates nil))
- company-candidates))
+ (let* ((new-prefix (company-call-backend 'prefix))
+ (c (when (and (stringp new-prefix)
+ (or (company-explicit-action-p)
+ (>= (length new-prefix)
+ company-minimum-prefix-length))
+ (= (- (point) (length new-prefix))
+ (- company-point (length company-prefix))))
+ (company-calculate-candidates new-prefix))))
+ (cond
+ ((eq c t)
+ ;; t means complete/unique.
+ (company-cancel new-prefix)
+ nil)
+ ((consp c)
+ ;; incremental match
+ (setq company-prefix new-prefix)
+ (company-update-candidates c)
+ c)
+ (t (company--continue-failed new-prefix)))))
+
+(defun company--begin-new ()
+ (let (prefix c)
+ (dolist (backend (if company-backend
+ ;; prefer manual override
+ (list company-backend)
+ company-backends))
+ (setq prefix
+ (if (or (symbolp backend)
+ (functionp backend))
+ (when (or (not (symbolp backend))
+ (get backend 'company-init))
+ (funcall backend 'prefix))
+ (company--multi-backend-adapter backend 'prefix)))
+ (when prefix
+ (when (and (stringp prefix)
+ (>= (length prefix) company-minimum-prefix-length))
+ (setq company-backend backend
+ company-prefix prefix
+ c (company-calculate-candidates prefix))
+ ;; t means complete/unique. We don't start, so no hooks.
+ (when (consp c)
+ (company-update-candidates c)
+ (run-hook-with-args 'company-completion-started-hook
+ (company-explicit-action-p))
+ (company-call-frontends 'show)))
+ (return c)))))
(defun company-begin ()
- (when (and (not (and company-candidates (company-continue)))
- (company--should-complete))
- (let (prefix)
- (dolist (backend (if company-backend
- ;; prefer manual override
- (list company-backend)
- company-backends))
- (setq prefix
- (if (or (symbolp backend)
- (functionp backend))
- (when (or (not (symbolp backend))
- (get backend 'company-init))
- (funcall backend 'prefix))
- (company--multi-backend-adapter backend 'prefix)))
- (when prefix
- (when (and (stringp prefix)
- (>= (length prefix) company-minimum-prefix-length))
- (setq company-backend backend
- company-prefix prefix)
- (let ((c (company-calculate-candidates prefix)))
- ;; t means complete/unique. We don't start, so no hooks.
- (when (consp c)
- (company-update-candidates c)
- (run-hook-with-args 'company-completion-started-hook
- (company-explicit-action-p))
- (company-call-frontends 'show))))
- (return prefix)))))
+ (setq company-candidates
+ (or (and company-candidates (company--continue))
+ (and (company--should-complete) (company--begin-new))))
(if company-candidates
(progn
(when (and company-end-of-buffer-workaround (eobp))
(save-excursion (insert "\n"))
(setq company-added-newline (buffer-chars-modified-tick)))
- (setq company-point (point))
+ (setq company-point (point)
+ company--point-max (point-max))
(company-enable-overriding-keymap company-active-map)
(company-call-frontends 'update))
(company-cancel)))
company-selection 0
company-selection-changed nil
company--explicit-action nil
+ company--point-max nil
company-point nil)
(when company-timer
(cancel-timer company-timer))
Example:
\(company-begin-with '\(\"foo\" \"foobar\" \"foobarbaz\"\)\)"
+ (setq company-begin-with-marker (copy-marker (point) t))
(company-begin-backend
- (let ((start (- (point) (or prefix-length 0))))
- (setq company-begin-with-marker (copy-marker (point) t))
- `(lambda (command &optional arg &rest ignored)
- (case command
- ('prefix (when (equal (point)
- (marker-position company-begin-with-marker))
- (buffer-substring ,start (point))))
- ('candidates (all-completions arg ',candidates))
- ('require-match ,require-match))))
+ `(lambda (command &optional arg &rest ignored)
+ (cond
+ ((eq command 'prefix)
+ (when (equal (point) (marker-position company-begin-with-marker))
+ (buffer-substring ,(- (point) (or prefix-length 0)) (point))))
+ ((eq command 'candidates)
+ (all-completions arg ',candidates))
+ ((eq command 'require-match)
+ ,require-match)))
callback))
;;; pseudo-tooltip ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;