(eval-when-compile (require 'cl))
+(defvar company--capf-data nil)
+(make-variable-buffer-local 'company--capf-data)
+
+(defun company--capf-clear-data (&optional _ignore)
+ (setq company--capf-data nil)
+ (remove-hook 'company-completion-cancelled-hook 'company--capf-clear-data t)
+ (remove-hook 'company-completion-finished-hook 'company--capf-clear-data t))
+
(defun company--capf-data ()
;; Ignore tags-completion-at-point-function because it subverts company-etags
;; in the default value of company-backends, where the latter comes later.
(data (run-hook-wrapped 'completion-at-point-functions
;; Ignore misbehaving functions.
#'completion--capf-wrapper 'optimist)))
- (when (and (consp data) (numberp (nth 1 data))) data)))
+ (when (and (consp (cdr data)) (numberp (nth 1 data))) data)))
(defun company-capf (command &optional arg &rest _args)
"`company-mode' back-end using `completion-at-point-functions'.
(when res
(if (> (nth 2 res) (point))
'stop
+ (setq company--capf-data res)
+ (add-hook 'company-completion-cancelled-hook 'company--capf-clear-data nil t)
+ (add-hook 'company-completion-finished-hook 'company--capf-clear-data nil t)
(buffer-substring-no-properties (nth 1 res) (point))))))
(`candidates
- (let ((res (company--capf-data)))
+ (let ((res company--capf-data))
(when res
(let* ((table (nth 3 res))
(pred (plist-get (nthcdr 4 res) :predicate))
candidates))
candidates)))))
(`sorted
- (let ((res (company--capf-data)))
+ (let ((res company--capf-data))
(when res
(let ((meta (completion-metadata
(buffer-substring (nth 1 res) (nth 2 res))
(nth 3 res) (plist-get (nthcdr 4 res) :predicate))))
(cdr (assq 'display-sort-function meta))))))
- (`common-part
+ (`match
;; Can't just use 0 when base-size (see above) is non-zero.
- (let ((start (if (get-text-property 0 'face arg)
+ (let ((start (if (get-text-property 0 'font-lock-face arg)
0
- (next-single-property-change 0 'face arg))))
+ (next-single-property-change 0 'font-lock-face arg))))
(when start
;; completions-common-part comes first, but we can't just look for this
;; value because it can be in a list.
(or
- (let ((value (get-text-property start 'face arg)))
+ (let ((value (get-text-property start 'font-lock-face arg)))
(text-property-not-all start (length arg)
- 'face value arg))
+ 'font-lock-face value arg))
(length arg)))))
(`duplicates t)
(`no-cache t) ;Not much can be done here, as long as we handle
;non-prefix matches.
(`meta
- (let ((f (plist-get (nthcdr 4 (company--capf-data)) :company-docsig)))
+ (let ((f (plist-get (nthcdr 4 company--capf-data) :company-docsig)))
(when f (funcall f arg))))
(`doc-buffer
- (let ((f (plist-get (nthcdr 4 (company--capf-data)) :company-doc-buffer)))
+ (let ((f (plist-get (nthcdr 4 company--capf-data) :company-doc-buffer)))
(when f (funcall f arg))))
(`location
- (let ((f (plist-get (nthcdr 4 (company--capf-data)) :company-location)))
+ (let ((f (plist-get (nthcdr 4 company--capf-data) :company-location)))
(when f (funcall f arg))))
(`annotation
(save-excursion
- (goto-char company-point)
- (let ((f (plist-get (nthcdr 4 (company--capf-data)) :annotation-function)))
+ ;; FIXME: `company-begin' sets `company-point' after calling
+ ;; `company--begin-new'. We shouldn't rely on `company-point' here,
+ ;; better to cache the capf-data value instead.
+ (when company-point
+ (goto-char company-point))
+ (let ((f (plist-get (nthcdr 4 company--capf-data) :annotation-function)))
(when f (funcall f arg)))))
(`require-match
- (plist-get (nthcdr 4 (company--capf-data)) :company-require-match))
+ (plist-get (nthcdr 4 company--capf-data) :company-require-match))
(`init nil) ;Don't bother: plenty of other ways to initialize the code.
(`post-completion
- (let* ((res (company--capf-data))
+ (let* ((res company--capf-data)
(exit-function (plist-get (nthcdr 4 res) :exit-function)))
(if exit-function
(funcall exit-function arg 'finished))))
(should (eq nil company-candidates-length))
(should (eq 4 (point))))))
+(ert-deftest company-should-complete-whitelist ()
+ (with-temp-buffer
+ (insert "ab")
+ (company-mode)
+ (let (company-frontends
+ company-begin-commands
+ (company-backends
+ (list (lambda (command &optional arg)
+ (case command
+ (prefix (buffer-substring (point-min) (point)))
+ (candidates '("abc" "abd")))))))
+ (let ((company-continue-commands nil))
+ (let (this-command)
+ (company-complete))
+ (company-call 'backward-delete-char 1)
+ (should (null company-candidates-length)))
+ (let ((company-continue-commands '(backward-delete-char)))
+ (let (this-command)
+ (company-complete))
+ (company-call 'backward-delete-char 1)
+ (should (eq 2 company-candidates-length))))))
+
+(ert-deftest company-should-complete-blacklist ()
+ (with-temp-buffer
+ (insert "ab")
+ (company-mode)
+ (let (company-frontends
+ company-begin-commands
+ (company-backends
+ (list (lambda (command &optional arg)
+ (case command
+ (prefix (buffer-substring (point-min) (point)))
+ (candidates '("abc" "abd")))))))
+ (let ((company-continue-commands '(not backward-delete-char)))
+ (let (this-command)
+ (company-complete))
+ (company-call 'backward-delete-char 1)
+ (should (null company-candidates-length)))
+ (let ((company-continue-commands '(not backward-delete-char-untabify)))
+ (let (this-command)
+ (company-complete))
+ (company-call 'backward-delete-char 1)
+ (should (eq 2 company-candidates-length))))))
+
(ert-deftest company-auto-complete-explicit ()
(with-temp-buffer
(insert "ab")
(should (equal '(" x 1 " " y 2 " " z 3 ")
(company--create-lines 0 999)))))
+(ert-deftest company-create-lines-truncates-annotations ()
+ (let* ((ww (company--window-width))
+ (data `(("1" . "(123)")
+ ("2" . nil)
+ ("3" . ,(concat "(" (make-string (- ww 2) ?4) ")"))))
+ (company-candidates (mapcar #'car data))
+ (company-candidates-length 3)
+ (company-tooltip-margin 1)
+ (company-backend (lambda (cmd &optional arg)
+ (when (eq cmd 'annotation)
+ (cdr (assoc arg data))))))
+ (should (equal (list (format " 1(123)%s " (company-space-string (- ww 8)))
+ (format " 2%s " (company-space-string (- ww 3)))
+ (format " 3(444%s " (make-string (- ww 7) ?4)))
+ (company--create-lines 0 999)))))
+
(ert-deftest company-column-with-composition ()
(with-temp-buffer
(insert "lambda ()")
(defun company-call (name &rest args)
(let* ((maybe (intern (format "company-%s" name)))
(command (if (fboundp maybe) maybe name)))
+ (let ((this-command command))
+ (run-hooks 'pre-command-hook))
(apply command args)
(let ((this-command command))
(run-hooks 'post-command-hook))))
;; Author: Nikolaj Schumacher
;; Maintainer: Dmitry Gutov <dgutov@yandex.ru>
-;; Version: 0.6.14
+;; Version: 0.7
;; Keywords: abbrev, convenience, matching
;; URL: http://company-mode.github.io/
;; Compatibility: GNU Emacs 22.x, GNU Emacs 23.x, GNU Emacs 24.x
back-end. The function should return `stop' if it should complete but
cannot \(e.g. if it is in the middle of a string\). Instead of a string,
the back-end may return a cons where car is the prefix and cdr is used in
-`company-minimum-prefix-length' test. It's either number or t, in which
-case the test automatically succeeds.
+`company-minimum-prefix-length' test. It must be either number or t, and
+in the latter case the test automatically succeeds.
`candidates': The second argument is the prefix to be completed. The
-return value should be a list of candidates that start with the prefix.
+return value should be a list of candidates that match the prefix.
+
+Non-prefix matches are also supported (candidates that don't start with the
+prefix, but match it in some backend-defined way). Backends that use this
+feature must disable cache (return t to `no-cache') and should also respond
+to `match'.
Optional commands:
of buffer and buffer location, or of file and line number where the
completion candidate was defined.
-`annotation': The second argument is a completion candidate. Returns a
+`annotation': The second argument is a completion candidate. Return a
string to be displayed inline with the candidate in the popup. If
duplicates are removed by company, candidates with equal string values will
be kept if they have different annotations. For that to work properly,
-backends should store the related information with candidates using text
+backends should store the related information on candidates using text
properties.
+`match': The second argument is a completion candidate. Backends that
+provide non-prefix completions should return the position of the end of
+text in the candidate that matches `prefix'. It will be used when
+rendering the popup.
+
`require-match': If this returns t, the user is not allowed to enter
anything not offered as a candidate. Use with care! The default value nil
gives the user that choice with `company-require-match'. Return value
(const :tag "Self insert command" '(self-insert-command))
(repeat :tag "Commands" function)))
+(defcustom company-continue-commands '(not save-buffer save-some-buffers
+ save-buffers-kill-terminal
+ save-buffers-kill-emacs)
+ "A list of commands that are allowed during completion.
+If this is t, or if `company-begin-commands' is t, any command is allowed.
+Otherwise, the value must be a list of symbols. If it starts with `not',
+the cdr is the list of commands that abort completion. Otherwise, all
+commands except those in that list, or in `company-begin-commands', or
+commands in the `company-' namespace, abort completion."
+ :type '(choice (const :tag "Any command" t)
+ (cons :tag "Any except"
+ (const not)
+ (repeat :tag "Commands" function))
+ (repeat :tag "Commands" function)))
+
(defcustom company-show-numbers nil
"If enabled, show quick-access numbers for the first ten candidates."
:type '(choice (const :tag "off" nil)
(and (symbolp this-command) (get this-command 'company-begin)))
(not (and transient-mark-mode mark-active))))
+(defun company--should-continue ()
+ (or (eq t company-begin-commands)
+ (eq t company-continue-commands)
+ (if (eq 'not (car company-continue-commands))
+ (not (memq this-command (cdr company-continue-commands)))
+ (or (memq this-command company-begin-commands)
+ (memq this-command company-continue-commands)
+ (string-match-p "\\`company-" (symbol-name this-command))))))
+
(defun company-call-frontends (command)
(dolist (frontend company-frontends)
(condition-case err
company-prefix)))
(defun company--continue-failed ()
- (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)
- (let ((company--auto-completion t))
- (company-complete-selection))
- nil))
- ((company-require-match-p)
- ;; wrong incremental input, but required match
- (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)))))
+ (let ((input (buffer-substring-no-properties (point) company-point)))
+ (cond
+ ((company-auto-complete-p input)
+ ;; auto-complete
+ (save-excursion
+ (goto-char company-point)
+ (let ((company--auto-completion t))
+ (company-complete-selection))
+ nil))
+ ((company-require-match-p)
+ ;; wrong incremental input, but required match
+ (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))
+ (t (company-cancel)))))
(defun company--good-prefix-p (prefix)
(and (or (company-explicit-action-p)
(- company-point (length company-prefix))))
(setq new-prefix (or (car-safe new-prefix) new-prefix))
(company-calculate-candidates new-prefix))))
- (or (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)))
- (company-cancel))))
+ (cond
+ ((eq c t)
+ ;; t means complete/unique.
+ (company-cancel new-prefix))
+ ((consp c)
+ ;; incremental match
+ (setq company-prefix new-prefix)
+ (company-update-candidates c)
+ c)
+ ((not (company--incremental-p))
+ (company-cancel))
+ (t (company--continue-failed)))))
(defun company--begin-new ()
(let (prefix c)
(or (and company-candidates (company--continue))
(and (company--should-complete) (company--begin-new)))
(when company-candidates
- (when (and company-end-of-buffer-workaround (eobp))
- (save-excursion (insert "\n"))
- (setq company-added-newline (buffer-chars-modified-tick)))
+ (let ((modified (buffer-modified-p)))
+ (when (and company-end-of-buffer-workaround (eobp))
+ (save-excursion (insert "\n"))
+ (setq company-added-newline
+ (or modified (buffer-chars-modified-tick)))))
(setq company-point (point)
company--point-max (point-max))
(company-ensure-emulation-alist)
(let ((tick (buffer-chars-modified-tick)))
(delete-region (1- (point-max)) (point-max))
(equal tick company-added-newline))
- ;; Only set unmodified when tick remained the same since insert.
+ ;; Only set unmodified when tick remained the same since insert,
+ ;; and the buffer wasn't modified before.
(set-buffer-modified-p nil))
(when company-prefix
(if (stringp result)
(cancel-timer company-timer))
(company-search-mode 0)
(company-call-frontends 'hide)
- (company-enable-overriding-keymap nil))
+ (company-enable-overriding-keymap nil)
+ ;; Make return value explicit.
+ nil)
(defun company-abort ()
(interactive)
(unless (company-keep this-command)
(condition-case err
(when company-candidates
- (company-call-frontends 'pre-command))
+ (company-call-frontends 'pre-command)
+ (unless (company--should-continue)
+ (company-abort)))
(error (message "Company: An error occurred in pre-command")
(message "%s" (error-message-string err))
(company-cancel))))
(defun company-fill-propertize (value annotation width selected left right)
(let* ((margin (length left))
- (common (+ (or (company-call-backend 'common-part value)
+ (common (+ (or (company-call-backend 'match value)
(length company-common)) margin))
(ann-start (+ margin (length value)))
(line (concat left
'(face company-tooltip-common
mouse-face company-tooltip-mouse)
line)
- (add-text-properties ann-start (+ ann-start (length annotation))
+ (add-text-properties ann-start (min (+ ann-start (length annotation)) width)
'(face company-tooltip-annotation
mouse-face company-tooltip-mouse)
line)
scrollbar-bounds)
;; Maybe clear old offset.
- (when (<= len (+ company-tooltip-offset limit))
+ (when (< len (+ company-tooltip-offset limit))
(setq company-tooltip-offset 0))
;; Scroll to offset.