X-Git-Url: https://code.delx.au/gnu-emacs-elpa/blobdiff_plain/7d005c424eb9ee053b36fd686aba8c3e2ac4fa2b..b8f877703a0d50b46254fcfee3c815ae5e386013:/company.el diff --git a/company.el b/company.el index 48f41d49e..28ed56be3 100644 --- a/company.el +++ b/company.el @@ -1,6 +1,6 @@ ;;; company.el --- Modular text completion framework -*- lexical-binding: t -*- -;; Copyright (C) 2009-2015 Free Software Foundation, Inc. +;; Copyright (C) 2009-2016 Free Software Foundation, Inc. ;; Author: Nikolaj Schumacher ;; Maintainer: Dmitry Gutov @@ -45,8 +45,7 @@ ;; ;; (defun company-my-backend (command &optional arg &rest ignored) ;; (pcase command -;; (`prefix (when (looking-back "foo\\>") -;; (match-string 0))) +;; (`prefix (company-grab-symbol)) ;; (`candidates (list "foobar" "foobaz" "foobarbaz")) ;; (`meta (format "This value is named %s" arg)))) ;; @@ -101,8 +100,7 @@ buffer-local wherever it is set." "Face used for the tooltip.") (defface company-tooltip-selection - '((default :inherit company-tooltip) - (((class color) (min-colors 88) (background light)) + '((((class color) (min-colors 88) (background light)) (:background "light blue")) (((class color) (min-colors 88) (background dark)) (:background "orange1")) @@ -118,28 +116,26 @@ buffer-local wherever it is set." "Face used for the tooltip item under the mouse.") (defface company-tooltip-common - '((default :inherit company-tooltip) - (((background light)) + '((((background light)) :foreground "darkred") (((background dark)) :foreground "red")) "Face used for the common completion in the tooltip.") (defface company-tooltip-common-selection - '((default :inherit company-tooltip-selection) - (((background light)) - :foreground "darkred") - (((background dark)) - :foreground "red")) + '((default :inherit company-tooltip-common)) "Face used for the selected common completion in the tooltip.") (defface company-tooltip-annotation - '((default :inherit company-tooltip) - (((background light)) + '((((background light)) :foreground "firebrick4") (((background dark)) :foreground "red4")) - "Face used for the annotation in the tooltip.") + "Face used for the completion annotation in the tooltip.") + +(defface company-tooltip-annotation-selection + '((default :inherit company-tooltip-annotation)) + "Face used for the selected completion annotation in the tooltip.") (defface company-scrollbar-fg '((((background light)) @@ -149,8 +145,7 @@ buffer-local wherever it is set." "Face used for the tooltip scrollbar thumb.") (defface company-scrollbar-bg - '((default :inherit company-tooltip) - (((background light)) + '((((background light)) :background "wheat") (((background dark)) :background "gold")) @@ -158,7 +153,7 @@ buffer-local wherever it is set." (defface company-preview '((((background light)) - :inherit company-tooltip-selection) + :inherit (company-tooltip-selection company-tooltip)) (((background dark)) :background "blue4" :foreground "wheat")) @@ -166,7 +161,7 @@ buffer-local wherever it is set." (defface company-preview-common '((((background light)) - :inherit company-tooltip-selection) + :inherit company-tooltip-common-selection) (((background dark)) :inherit company-preview :foreground "red")) @@ -317,9 +312,10 @@ This doesn't include the margins and the scroll bar." company-eclim company-semantic company-clang company-xcode company-cmake company-capf + company-files (company-dabbrev-code company-gtags company-etags company-keywords) - company-oddmuse company-files company-dabbrev) + company-oddmuse company-dabbrev) "The list of active backends (completion engines). Only one backend is used at a time. The choice depends on the order of @@ -338,10 +334,10 @@ of the following: text immediately before point. Returning nil from this command passes control to the next backend. 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 backend may return a cons where car is the prefix -and cdr is used instead of the actual prefix length in the comparison -against `company-minimum-prefix-length'. It must be either number or t, -and in the latter case the test automatically succeeds. +Instead of a string, the backend may return a cons (PREFIX . LENGTH) +where LENGTH is a number used in place of PREFIX's length when +comparing against `company-minimum-prefix-length'. LENGTH can also +be just 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 match the prefix. @@ -436,7 +432,8 @@ Asynchronous backends The return value of each command can also be a cons (:async . FETCHER) where FETCHER is a function of one argument, CALLBACK. When the data arrives, FETCHER must call CALLBACK and pass it the appropriate return -value, as described above. +value, as described above. That call must happen in the same buffer as +where completion was initiated. True asynchronous operation is only supported for command `candidates', and only during idle completion. Other commands will block the user interface, @@ -468,6 +465,8 @@ without duplicates." (const :tag "Sort by occurrence" (company-sort-by-occurrence)) (const :tag "Sort by backend importance" (company-sort-by-backend-importance)) + (const :tag "Prefer case sensitive prefix" + (company-sort-prefer-same-case-prefix)) (repeat :tag "User defined" (function)))) (defcustom company-completion-started-hook nil @@ -809,7 +808,9 @@ means that `company-mode' is always turned on except in `message-mode' buffers." (let ((col (car (posn-col-row posn))) ;; `posn-col-row' doesn't work well with lines of different height. ;; `posn-actual-col-row' doesn't handle multiple-width characters. - (row (cdr (posn-actual-col-row posn)))) + (row (cdr (or (posn-actual-col-row posn) + ;; When position is non-visible for some reason. + (posn-col-row posn))))) (when (and header-line-format (version< emacs-version "24.3.93.3")) ;; http://debbugs.gnu.org/18384 (cl-decf row)) @@ -834,7 +835,8 @@ means that `company-mode' is always turned on except in `message-mode' buffers." If EXPRESSION is non-nil, return the match string for the respective parenthesized expression in REGEXP. Matching is limited to the current line." - (company-grab regexp expression (point-at-bol))) + (let ((inhibit-field-text-motion t)) + (company-grab regexp expression (point-at-bol)))) (defun company-grab-symbol () "If point is at the end of a symbol, return it. @@ -856,7 +858,7 @@ Otherwise, if point is not inside a symbol, return an empty string." (defun company-grab-symbol-cons (idle-begin-after-re &optional max-len) "Return a string SYMBOL or a cons (SYMBOL . t). -SYMBOL is as returned by `company-grab-symbol'. If the text before poit +SYMBOL is as returned by `company-grab-symbol'. If the text before point matches IDLE-BEGIN-AFTER-RE, return it wrapped in a cons." (let ((symbol (company-grab-symbol))) (when symbol @@ -1096,7 +1098,8 @@ can retrieve meta-data for them." (defun company--group-lighter (candidate base) (let ((backend (or (get-text-property 0 'company-backend candidate) - (car company-backend)))) + (cl-some (lambda (x) (and (not (keywordp x)) x)) + company-backend)))) (when (and backend (symbolp backend)) (let ((name (replace-regexp-in-string "company-\\|-company" "" (symbol-name backend)))) @@ -1166,10 +1169,11 @@ can retrieve meta-data for them." t)))) (defun company--fetch-candidates (prefix) - (let ((c (if company--manual-action - (company-call-backend 'candidates prefix) - (company-call-backend-raw 'candidates prefix))) - res) + (let* ((non-essential (not (company-explicit-action-p))) + (c (if company--manual-action + (company-call-backend 'candidates prefix) + (company-call-backend-raw 'candidates prefix))) + res) (if (not (eq (car c) :async)) c (let ((buf (current-buffer)) @@ -1188,7 +1192,11 @@ can retrieve meta-data for them." company-candidates-cache (list (cons prefix (company--preprocess-candidates candidates)))) - (company-idle-begin buf win tick pt))))) + (unwind-protect + (company-idle-begin buf win tick pt) + (unless company-candidates + (setq company-backend nil + company-candidates-cache nil))))))) ;; FIXME: Relying on the fact that the callers ;; will interpret nil as "do nothing" is shaky. ;; A throw-catch would be one possible improvement. @@ -1196,6 +1204,7 @@ can retrieve meta-data for them." (progn (setq res 'done) nil))))) (defun company--preprocess-candidates (candidates) + (cl-assert (cl-every #'stringp candidates)) (unless (company-call-backend 'sorted) (setq candidates (sort candidates 'string<))) (when (company-call-backend 'duplicates) @@ -1324,6 +1333,16 @@ from the rest of the backends in the group, if any, will be left at the end." (let ((b1 (get-text-property 0 'company-backend c1))) (or (not b1) (not (memq b1 low-priority))))))))))) +(defun company-sort-prefer-same-case-prefix (candidates) + "Prefer CANDIDATES with the same case sensitive prefix. +If a backend returns case insensitive matches, candidates with the an exact +prefix match will be prioritized even if this changes the lexical order." + (cl-loop for candidate in candidates + if (string-prefix-p company-prefix candidate) + collect candidate into same-case + else collect candidate into other-case + finally return (append same-case other-case))) + (defun company-idle-begin (buf win tick pos) (and (eq buf (current-buffer)) (eq win (selected-window)) @@ -1348,6 +1367,7 @@ from the rest of the backends in the group, if any, will be left at the end." (company-cancel)) (quit (company-cancel)))))) +;;;###autoload (defun company-manual-begin () (interactive) (company-assert-enabled) @@ -1517,14 +1537,8 @@ from the rest of the backends in the group, if any, will be left at the end." (company-call-frontends 'update))) (defun company-cancel (&optional result) - (unwind-protect - (when company-prefix - (if (stringp result) - (progn - (company-call-backend 'pre-completion result) - (run-hook-with-args 'company-completion-finished-hook result) - (company-call-backend 'post-completion result)) - (run-hook-with-args 'company-completion-cancelled-hook result))) + (let ((prefix company-prefix) + (backend company-backend)) (setq company-backend nil company-prefix nil company-candidates nil @@ -1543,7 +1557,16 @@ from the rest of the backends in the group, if any, will be left at the end." (company-echo-cancel t) (company-search-mode 0) (company-call-frontends 'hide) - (company-enable-overriding-keymap nil)) + (company-enable-overriding-keymap nil) + (when prefix + ;; FIXME: RESULT can also be e.g. `unique'. We should call + ;; `company-completion-finished-hook' in that case, with right argument. + (if (stringp result) + (let ((company-backend backend)) + (company-call-backend 'pre-completion result) + (run-hook-with-args 'company-completion-finished-hook result) + (company-call-backend 'post-completion result)) + (run-hook-with-args 'company-completion-cancelled-hook result)))) ;; Make return value explicit. nil) @@ -1559,6 +1582,7 @@ from the rest of the backends in the group, if any, will be left at the end." (and (symbolp command) (get command 'company-keep))) (defun company-pre-command () + (company--electric-restore-window-configuration) (unless (company-keep this-command) (condition-case-unless-debug err (when company-candidates @@ -1575,7 +1599,8 @@ from the rest of the backends in the group, if any, will be left at the end." (company-uninstall-map)) (defun company-post-command () - (when (null this-command) + (when (and company-candidates + (null this-command)) ;; Happens when the user presses `C-g' while inside ;; `flyspell-post-command-hook', for example. ;; Or any other `post-command-hook' function that can call `sit-for', @@ -1630,7 +1655,9 @@ each one wraps a part of the input string." (const :tag "Exact match" regexp-quote) (const :tag "Words separated with spaces" company-search-words-regexp) (const :tag "Words separated with spaces, in any order" - company-search-words-in-any-order-regexp))) + company-search-words-in-any-order-regexp) + (const :tag "All characters in given order, with anything in between" + company-search-flex-regexp))) (defvar-local company-search-string "") @@ -1660,6 +1687,15 @@ each one wraps a part of the input string." permutations "\\|"))) +(defun company-search-flex-regexp (input) + (if (zerop (length input)) + "" + (concat (regexp-quote (string (aref input 0))) + (mapconcat (lambda (c) + (concat "[^" (string c) "]*" + (regexp-quote (string c)))) + (substring input 1) "")))) + (defun company--permutations (lst) (if (not lst) '(nil) @@ -2024,6 +2060,7 @@ With ARG, move by that many elements." (eq old-tick (buffer-chars-modified-tick))) (company-complete-common)))))) +;;;###autoload (defun company-complete () "Insert the common part of all candidates or the current selection. The first time this is called, the common part is inserted, the second @@ -2109,28 +2146,30 @@ character, stripping the modifiers. That character must be a digit." (insert string))) (current-buffer))) +(defvar company--electric-saved-window-configuration nil) + (defvar company--electric-commands '(scroll-other-window scroll-other-window-down mwheel-scroll) "List of Commands that won't break out of electric commands.") +(defun company--electric-restore-window-configuration () + "Restore window configuration (after electric commands)." + (when (and company--electric-saved-window-configuration + (not (memq this-command company--electric-commands))) + (set-window-configuration company--electric-saved-window-configuration) + (setq company--electric-saved-window-configuration nil))) + (defmacro company--electric-do (&rest body) (declare (indent 0) (debug t)) `(when (company-manual-begin) - (save-window-excursion - (let ((height (window-height)) - (row (company--row)) - cmd) - ,@body - (and (< (window-height) height) - (< (- (window-height) row 2) company-tooltip-limit) - (recenter (- (window-height) row 2))) - (while (memq (setq cmd (key-binding (read-key-sequence-vector nil))) - company--electric-commands) - (condition-case err - (call-interactively cmd) - ((beginning-of-buffer end-of-buffer) - (message (error-message-string err))))) - (company--unread-last-input))))) + (cl-assert (null company--electric-saved-window-configuration)) + (setq company--electric-saved-window-configuration (current-window-configuration)) + (let ((height (window-height)) + (row (company--row))) + ,@body + (and (< (window-height) height) + (< (- (window-height) row 2) company-tooltip-limit) + (recenter (- (window-height) row 2)))))) (defun company--unread-last-input () (when last-input-event @@ -2337,6 +2376,8 @@ If SHOW-VERSION is non-nil, show the version in the echo area." (if company-common (string-width company-common) 0))) + (_ (setq value (company--pre-render value) + annotation (and annotation (company--pre-render annotation t)))) (ann-ralign company-tooltip-align-annotations) (ann-truncate (< width (+ (length value) (length annotation) @@ -2363,18 +2404,20 @@ If SHOW-VERSION is non-nil, show the version in the echo area." (setq common (+ (min common width) margin)) (setq width (+ width margin (length right))) - (add-text-properties 0 width '(face company-tooltip - mouse-face company-tooltip-mouse) - line) - (add-text-properties margin common - '(face company-tooltip-common - mouse-face company-tooltip-mouse) - line) + (font-lock-append-text-property 0 width 'mouse-face + 'company-tooltip-mouse + line) (when (< ann-start ann-end) - (add-text-properties ann-start ann-end - '(face company-tooltip-annotation - mouse-face company-tooltip-mouse) - line)) + (font-lock-append-text-property ann-start ann-end 'face + (if selected + 'company-tooltip-annotation-selection + 'company-tooltip-annotation) + line)) + (font-lock-prepend-text-property margin common 'face + (if selected + 'company-tooltip-common-selection + 'company-tooltip-common) + line) (when selected (if (let ((re (funcall company-search-regexp-function company-search-string))) @@ -2385,16 +2428,15 @@ If SHOW-VERSION is non-nil, show the version in the echo area." (end (+ margin mend)) (width (- width (length right)))) (when (< beg width) - (add-text-properties beg (min end width) - '(face company-tooltip-search) - line)))) - (add-text-properties 0 width '(face company-tooltip-selection - mouse-face company-tooltip-selection) - line) - (add-text-properties margin common - '(face company-tooltip-common-selection - mouse-face company-tooltip-selection) - line))) + (font-lock-prepend-text-property beg (min end width) + 'face 'company-tooltip-search + line)))) + (font-lock-append-text-property 0 width 'face + 'company-tooltip-selection + line))) + (font-lock-append-text-property 0 width 'face + 'company-tooltip + line) line)) (defun company--search-chunks () @@ -2407,6 +2449,17 @@ If SHOW-VERSION is non-nil, show the version in the echo area." (push (cons (car md) (cadr md)) res)))) res)) +(defun company--pre-render (str &optional annotation-p) + (or (company-call-backend 'pre-render str annotation-p) + (progn + (when (or (text-property-not-all 0 (length str) 'face nil str) + (text-property-not-all 0 (length str) 'mouse-face nil str)) + (setq str (copy-sequence str)) + (remove-text-properties 0 (length str) + '(face nil font-lock-face nil mouse-face nil) + str)) + str))) + (defun company--clean-string (str) (replace-regexp-in-string "\\([^[:graph:] ]\\)\\|\\(\ufeff\\)\\|[[:multibyte:]]" @@ -2785,19 +2838,22 @@ Returns a negative number if the tooltip should be displayed above point." (company-preview-hide) (let ((completion (nth company-selection company-candidates))) - (setq completion (propertize completion 'face 'company-preview)) - (add-text-properties 0 (length company-common) - '(face company-preview-common) completion) + (setq completion (copy-sequence (company--pre-render completion))) + (font-lock-append-text-property 0 (length completion) + 'face 'company-preview + completion) + (font-lock-prepend-text-property 0 (length company-common) + 'face 'company-preview-common + completion) ;; Add search string (and (string-match (funcall company-search-regexp-function company-search-string) completion) (pcase-dolist (`(,mbeg . ,mend) (company--search-chunks)) - (add-text-properties mbeg - mend - '(face company-preview-search) - completion))) + (font-lock-prepend-text-property mbeg mend + 'face 'company-preview-search + completion))) (setq completion (company-strip-prefix completion))