X-Git-Url: https://code.delx.au/gnu-emacs-elpa/blobdiff_plain/80c86fabb12f5c90bd93e3a228ce74504fa638d1..ca001a562783538cad2762d90f8026896b4d6985:/packages/company/company.el diff --git a/packages/company/company.el b/packages/company/company.el index 59ddcc6c1..7b4834706 100644 --- a/packages/company/company.el +++ b/packages/company/company.el @@ -1,13 +1,13 @@ -;;; company.el --- Modular in-buffer completion framework +;;; company.el --- Modular text completion framework -*- lexical-binding: t -*- -;; Copyright (C) 2009-2013 Free Software Foundation, Inc. +;; Copyright (C) 2009-2014 Free Software Foundation, Inc. ;; Author: Nikolaj Schumacher ;; Maintainer: Dmitry Gutov -;; Version: 0.6 +;; URL: http://company-mode.github.io/ +;; Version: 0.8.2 ;; Keywords: abbrev, convenience, matching -;; URL: http://company-mode.github.com/ -;; Compatibility: GNU Emacs 22.x, GNU Emacs 23.x, GNU Emacs 24.x +;; Package-Requires: ((emacs "24.1") (cl-lib "0.5")) ;; This file is part of GNU Emacs. @@ -48,11 +48,11 @@ ;; Here is a simple example completing "foo": ;; ;; (defun company-my-backend (command &optional arg &rest ignored) -;; (case command -;; (prefix (when (looking-back "foo\\>") +;; (pcase command +;; (`prefix (when (looking-back "foo\\>") ;; (match-string 0))) -;; (candidates (list "foobar" "foobaz" "foobarbaz")) -;; (meta (format "This value is named %s" arg)))) +;; (`candidates (list "foobar" "foobaz" "foobarbaz")) +;; (`meta (format "This value is named %s" arg)))) ;; ;; Sometimes it is a good idea to mix several back-ends together, for example to ;; enrich gtags with dabbrev-code results (to emulate local variables). @@ -65,98 +65,14 @@ ;; ;;; Change Log: ;; -;; 2013-03-19 (0.6) -;; Switching between tag files now works correctly with `company-etags'. -;; Clang completions now include macros and are case-sensitive. -;; Added `company-capf': completion adapter using -;; `completion-at-point-functions'. (Stefan Monnier) -;; `company-elisp' has some improvements. -;; Instead of `overrriding-terminal-local-map', we're now using -;; `emulation-mode-map-alists' (experimental). This largely means that when -;; the completion keymap is active, other minor modes' keymaps are still -;; used, so, for example, it's not as easy to circumvent `paredit-mode' -;; accidentally when it's enabled. -;; Fixed two old tooltip annoyances. -;; Some performance improvements. -;; `company-clang' now shows meta information, too. -;; Candidates from grouped back-ends are merged more conservatively: only -;; back-ends that return the same prefix at point are used. -;; Loading of `nxml', `semantic', `pymacs' and `ropemacs' is now deferred. -;; `company-pysmell' is not used by default anymore. -;; Across-the-board bugfixing. -;; -;; 2010-02-24 (0.5) -;; `company-ropemacs' now provides location and docs. (Fernando H. Silva) -;; Added `company-with-candidate-inserted' macro. -;; Added `company-clang' back-end. -;; Added new mechanism for non-consecutive insertion. -;; (So far only used by clang for ObjC.) -;; The semantic back-end now shows meta information for local symbols. -;; Added compatibility for CEDET in Emacs 23.2 and from CVS. (Oleg Andreev) -;; -;; 2009-05-07 (0.4.3) -;; Added `company-other-backend'. -;; Idle completion no longer interrupts multi-key command input. -;; Added `company-ropemacs' and `company-pysmell' back-ends. -;; -;; 2009-04-25 (0.4.2) -;; In C modes . and -> now count towards `company-minimum-prefix-length'. -;; Reverted default front-end back to `company-preview-if-just-one-frontend'. -;; The pseudo tooltip will no longer be clipped at the right window edge. -;; Added `company-tooltip-minimum'. -;; Windows compatibility fixes. -;; -;; 2009-04-19 (0.4.1) -;; Added `global-company-mode'. -;; Performance enhancements. -;; Added `company-eclim' back-end. -;; Added safer workaround for Emacs `posn-col-row' bug. -;; -;; 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. -;; Fixed `company-begin-with'. -;; -;; 2009-04-15 (0.3.1) -;; Added 'stop prefix to prevent dabbrev from completing inside of symbols. -;; Fixed issues with tabbar-mode and line-spacing. -;; Performance enhancements. -;; -;; 2009-04-12 (0.3) -;; Added `company-begin-commands' option. -;; Added abbrev, tempo and Xcode back-ends. -;; Back-ends are now interactive. You can start them with M-x backend-name. -;; Added `company-begin-with' for starting company from elisp-code. -;; Added hooks. -;; Added `company-require-match' and `company-auto-complete' options. -;; -;; 2009-04-05 (0.2.1) -;; Improved Emacs Lisp back-end behavior for local variables. -;; Added `company-elisp-detect-function-context' option. -;; The mouse can now be used for selection. -;; -;; 2009-03-22 (0.2) -;; Added `company-show-location'. -;; Added etags back-end. -;; Added work-around for end-of-buffer bug. -;; Added `company-filter-candidates'. -;; More local Lisp variables are now included in the candidates. -;; -;; 2009-03-21 (0.1.5) -;; Fixed elisp documentation buffer always showing the same doc. -;; Added `company-echo-strip-common-frontend'. -;; Added `company-show-numbers' option and M-0 ... M-9 default bindings. -;; Don't hide the echo message if it isn't shown. -;; -;; 2009-03-20 (0.1) -;; Initial release. +;; See NEWS.md in the repository. ;;; Code: -(eval-when-compile (require 'cl)) +(require 'cl-lib) +(require 'newcomment) +;; FIXME: Use `user-error'. (add-to-list 'debug-ignored-errors "^.* frontend cannot be used twice$") (add-to-list 'debug-ignored-errors "^Echo area cannot be used twice$") (add-to-list 'debug-ignored-errors "^No \\(document\\|loc\\)ation available$") @@ -165,6 +81,19 @@ (add-to-list 'debug-ignored-errors "^Cannot complete at point$") (add-to-list 'debug-ignored-errors "^No other back-end$") +;;; Compatibility +(eval-and-compile + ;; `defvar-local' for Emacs 24.2 and below + (unless (fboundp 'defvar-local) + (defmacro defvar-local (var val &optional docstring) + "Define VAR as a buffer-local variable with default value VAL. +Like `defvar' but additionally marks the variable as being automatically +buffer-local wherever it is set." + (declare (debug defvar) (doc-string 3)) + `(progn + (defvar ,var ,val ,docstring) + (make-variable-buffer-local ',var))))) + (defgroup company nil "Extensible inline text completion mechanism" :group 'abbrev @@ -172,81 +101,114 @@ :group 'matching) (defface company-tooltip - '((t :background "yellow" - :foreground "black")) - "Face used for the tool tip." - :group 'company) + '((default :foreground "black") + (((class color) (min-colors 88) (background light)) + (:background "cornsilk")) + (((class color) (min-colors 88) (background dark)) + (:background "yellow"))) + "Face used for the tooltip.") (defface company-tooltip-selection '((default :inherit company-tooltip) - (((class color) (min-colors 88)) (:background "orange1")) + (((class color) (min-colors 88) (background light)) + (:background "light blue")) + (((class color) (min-colors 88) (background dark)) + (:background "orange1")) (t (:background "green"))) - "Face used for the selection in the tool tip." - :group 'company) + "Face used for the selection in the tooltip.") (defface company-tooltip-mouse '((default :inherit highlight)) - "Face used for the tool tip item under the mouse." - :group 'company) + "Face used for the tooltip item under the mouse.") (defface company-tooltip-common - '((t :inherit company-tooltip - :foreground "red")) - "Face used for the common completion in the tool tip." - :group 'company) + '((default :inherit company-tooltip) + (((background light)) + :foreground "darkred") + (((background dark)) + :foreground "red")) + "Face used for the common completion in the tooltip.") (defface company-tooltip-common-selection - '((t :inherit company-tooltip-selection - :foreground "red")) - "Face used for the selected common completion in the tool tip." - :group 'company) + '((default :inherit company-tooltip-selection) + (((background light)) + :foreground "darkred") + (((background dark)) + :foreground "red")) + "Face used for the selected common completion in the tooltip.") + +(defface company-tooltip-annotation + '((default :inherit company-tooltip) + (((background light)) + :foreground "firebrick4") + (((background dark)) + :foreground "red4")) + "Face used for the annotation in the tooltip.") + +(defface company-scrollbar-fg + '((((background light)) + :background "darkred") + (((background dark)) + :background "red")) + "Face used for the tooltip scrollbar thumb.") + +(defface company-scrollbar-bg + '((default :inherit company-tooltip) + (((background light)) + :background "wheat") + (((background dark)) + :background "gold")) + "Face used for the tooltip scrollbar background.") (defface company-preview - '((t :background "blue4" - :foreground "wheat")) - "Face used for the completion preview." - :group 'company) + '((((background light)) + :inherit company-tooltip-selection) + (((background dark)) + :background "blue4" + :foreground "wheat")) + "Face used for the completion preview.") (defface company-preview-common - '((t :inherit company-preview - :foreground "red")) - "Face used for the common part of the completion preview." - :group 'company) + '((((background light)) + :inherit company-tooltip-selection) + (((background dark)) + :inherit company-preview + :foreground "red")) + "Face used for the common part of the completion preview.") (defface company-preview-search - '((t :inherit company-preview - :background "blue1")) - "Face used for the search string in the completion preview." - :group 'company) + '((((background light)) + :inherit company-tooltip-common-selection) + (((background dark)) + :inherit company-preview + :background "blue1")) + "Face used for the search string in the completion preview.") (defface company-echo nil - "Face used for completions in the echo area." - :group 'company) + "Face used for completions in the echo area.") (defface company-echo-common '((((background dark)) (:foreground "firebrick1")) (((background light)) (:background "firebrick4"))) - "Face used for the common part of completions in the echo area." - :group 'company) + "Face used for the common part of completions in the echo area.") (defun company-frontends-set (variable value) - ;; uniquify - (let ((remainder value)) - (setcdr remainder (delq (car remainder) (cdr remainder)))) - (and (memq 'company-pseudo-tooltip-unless-just-one-frontend value) - (memq 'company-pseudo-tooltip-frontend value) - (error "Pseudo tooltip frontend cannot be used twice")) - (and (memq 'company-preview-if-just-one-frontend value) - (memq 'company-preview-frontend value) - (error "Preview frontend cannot be used twice")) - (and (memq 'company-echo value) - (memq 'company-echo-metadata-frontend value) - (error "Echo area cannot be used twice")) - ;; preview must come last - (dolist (f '(company-preview-if-just-one-frontend company-preview-frontend)) - (when (memq f value) - (setq value (append (delq f value) (list f))))) - (set variable value)) + ;; Uniquify. + (let ((value (delete-dups (copy-sequence value)))) + (and (memq 'company-pseudo-tooltip-unless-just-one-frontend value) + (memq 'company-pseudo-tooltip-frontend value) + (error "Pseudo tooltip frontend cannot be used twice")) + (and (memq 'company-preview-if-just-one-frontend value) + (memq 'company-preview-frontend value) + (error "Preview frontend cannot be used twice")) + (and (memq 'company-echo value) + (memq 'company-echo-metadata-frontend value) + (error "Echo area cannot be used twice")) + ;; Preview must come last. + (dolist (f '(company-preview-if-just-one-frontend company-preview-frontend)) + (when (cdr (memq f value)) + (setq value (append (delq f value) (list f))))) + (set variable value))) (defcustom company-frontends '(company-pseudo-tooltip-unless-just-one-frontend company-preview-if-just-one-frontend @@ -255,23 +217,22 @@ Each front-end is a function that takes one argument. It is called with one of the following arguments: -'show: When the visualization should start. +`show': When the visualization should start. -'hide: When the visualization should end. +`hide': When the visualization should end. -'update: When the data has been updated. +`update': When the data has been updated. -'pre-command: Before every command that is executed while the +`pre-command': Before every command that is executed while the visualization is active. -'post-command: After every command that is executed while the +`post-command': After every command that is executed while the visualization is active. The visualized data is stored in `company-prefix', `company-candidates', `company-common', `company-selection', `company-point' and `company-search-string'." :set 'company-frontends-set - :group 'company :type '(repeat (choice (const :tag "echo" company-echo-frontend) (const :tag "echo, strip common" company-echo-strip-common-frontend) @@ -287,78 +248,90 @@ The visualized data is stored in `company-prefix', `company-candidates', (function :tag "custom function" nil)))) (defcustom company-tooltip-limit 10 - "The maximum number of candidates in the tool tip" - :group 'company + "The maximum number of candidates in the tooltip." :type 'integer) (defcustom company-tooltip-minimum 6 - "The minimum height of the tool tip. + "The minimum height of the tooltip. If this many lines are not available, prefer to display the tooltip above." - :group 'company :type 'integer) +(defcustom company-tooltip-minimum-width 0 + "The minimum width of the tooltip's inner area. +This doesn't include the margins and the scroll bar." + :type 'integer + :package-version '(company . "0.8.0")) + +(defcustom company-tooltip-margin 1 + "Width of margin columns to show around the toolip." + :type 'integer) + +(defcustom company-tooltip-offset-display 'scrollbar + "Method using which the tooltip displays scrolling position. +`scrollbar' means draw a scrollbar to the right of the items. +`lines' means wrap items in lines with \"before\" and \"after\" counters." + :type '(choice (const :tag "Scrollbar" scrollbar) + (const :tag "Two lines" lines))) + +(defcustom company-tooltip-align-annotations nil + "When non-nil, align annotations to the right tooltip border." + :type 'boolean + :package-version '(company . "0.7.1")) + +(defcustom company-tooltip-flip-when-above nil + "Whether to flip the tooltip when it's above the current line." + :type 'boolean + :package-version '(company . "0.8.1")) + (defvar company-safe-backends '((company-abbrev . "Abbrev") - (company-clang . "clang") + (company-bbdb . "BBDB") + (company-capf . "completion-at-point-functions") + (company-clang . "Clang") + (company-cmake . "CMake") (company-css . "CSS") (company-dabbrev . "dabbrev for plain text") (company-dabbrev-code . "dabbrev for code") - (company-eclim . "eclim (an Eclipse interace)") + (company-eclim . "Eclim (an Eclipse interface)") (company-elisp . "Emacs Lisp") (company-etags . "etags") (company-files . "Files") (company-gtags . "GNU Global") - (company-ispell . "ispell") + (company-ispell . "Ispell") (company-keywords . "Programming language keywords") (company-nxml . "nxml") (company-oddmuse . "Oddmuse") (company-pysmell . "PySmell") (company-ropemacs . "ropemacs") - (company-semantic . "CEDET Semantic") + (company-semantic . "Semantic") (company-tempo . "Tempo templates") (company-xcode . "Xcode"))) (put 'company-safe-backends 'risky-local-variable t) (defun company-safe-backends-p (backends) (and (consp backends) - (not (dolist (backend backends) + (not (cl-dolist (backend backends) (unless (if (consp backend) (company-safe-backends-p backend) (assq backend company-safe-backends)) - (return t)))))) - -(defun company-capf (command &optional arg &rest args) - "`company-mode' back-end using `completion-at-point-functions'. -Requires Emacs 24.1 or newer." - (interactive (list 'interactive)) - (case command - (interactive (company-begin-backend 'company-capf)) - (prefix - (let ((res (run-hook-wrapped 'completion-at-point-functions - ;; Ignore misbehaving functions. - #'completion--capf-wrapper 'optimist))) - (when (consp res) - (if (> (nth 2 res) (point)) - 'stop - (buffer-substring-no-properties (nth 1 res) (point)))))) - (candidates - (let ((res (run-hook-wrapped 'completion-at-point-functions - ;; Ignore misbehaving functions. - #'completion--capf-wrapper 'optimist))) - (when (consp res) - (all-completions arg (nth 3 res) - (plist-get (nthcdr 4 res) :predicate))))))) - -(defcustom company-backends '(company-elisp company-nxml company-css - company-clang company-semantic company-eclim - company-xcode company-ropemacs - (company-gtags company-etags company-dabbrev-code + (cl-return t)))))) + +(defcustom company-backends `(,@(unless (version< "24.3.50" emacs-version) + (list 'company-elisp)) + company-bbdb + company-nxml company-css + company-eclim company-semantic company-clang + company-xcode company-ropemacs company-cmake + company-capf + (company-dabbrev-code company-gtags company-etags 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 -completions are merged. Otherwise only the first matching back-end returns -results. + +Only one back-end is used at a time. The choice depends on the order of +the items in this list, and on the values they return in response to the +`prefix' command (see below). But a back-end can also be a \"grouped\" +one (see below). `company-begin-backend' can be used to start a specific back-end, `company-other-backend' will skip to the next matching back-end in the list. @@ -368,20 +341,25 @@ The first argument is the command requested from the back-end. It is one of the following: `prefix': The back-end should return the text to be completed. It must be -text immediately before `point'. Returning nil passes control to the next -back-end. The function should return 'stop if it should complete but cannot -\(e.g. if it is in the middle of a string\). If the returned value is only -part of the prefix (e.g. the part after \"->\" in C), the back-end may return a -cons of prefix and prefix length, which is then used in the -`company-minimum-prefix-length' test. +text immediately before point. Returning nil passes control to the next +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 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: -`sorted': The back-end may return t here to indicate that the candidates -are sorted and will not need to be sorted again. +`sorted': Return t here to indicate that the candidates are sorted and will +not need to be sorted again. `duplicates': If non-nil, company will take care of removing duplicates from the list. @@ -390,26 +368,71 @@ from the list. progresses, unless the back-end returns t for this command. The second argument is the latest prefix. -`meta': The second argument is a completion candidate. The back-end should -return a (short) documentation string for it. +`meta': The second argument is a completion candidate. Return a (short) +documentation string for it. + +`doc-buffer': The second argument is a completion candidate. Return a +buffer with documentation for it. Preferably use `company-doc-buffer', -`doc-buffer': The second argument is a completion candidate. -The back-end should create a buffer (preferably with `company-doc-buffer'), -fill it with documentation and return it. +`location': The second argument is a completion candidate. Return the cons +of buffer and buffer location, or of file and line number where the +completion candidate was defined. -`location': The second argument is a completion candidate. The back-end can -return the cons 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. 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 on candidates using text +properties. -`require-match': If this value is t, the user is not allowed to enter anything -not offering as a candidate. Use with care! The default value nil gives the -user that choice with `company-require-match'. Return value 'never overrides -that option the other way around. +`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 +`never' overrides that option the other way around. + +`init': Called once for each buffer. The back-end can check for external +programs and files and load any required libraries. Raising an error here +will show up in message log once, and the back-end will not be used for +completion. + +`post-completion': Called after a completion candidate has been inserted +into the buffer. The second argument is the candidate. Can be used to +modify it, e.g. to expand a snippet. The back-end should return nil for all commands it does not support or does not know about. It should also be callable interactively and use -`company-begin-backend' to start itself in that case." - :group 'company +`company-begin-backend' to start itself in that case. + +Grouped back-ends: + +An element of `company-backends' can also itself be a list of back-ends, +then it's considered to be a \"grouped\" back-end. + +When possible, commands taking a candidate as an argument are dispatched to +the back-end it came from. In other cases, the first non-nil value among +all the back-ends is returned. + +The latter is the case for the `prefix' command. But if the group contains +the keyword `:with', the back-ends after it are ignored for this command. + +The completions from back-ends in a group are merged (but only from those +that return the same `prefix'). + +Asynchronous back-ends: + +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. + +True asynchronous operation is only supported for command `candidates', and +only during idle completion. Other commands will block the user interface, +even if the back-end uses the asynchronous calling convention." :type `(repeat (choice :tag "Back-end" @@ -421,70 +444,86 @@ does not know about. It should also be callable interactively and use ,@(mapcar (lambda (b) `(const :tag ,(cdr b) ,(car b))) company-safe-backends) + (const :tag "With" :with) (symbol :tag "User defined")))))) (put 'company-backends 'safe-local-variable 'company-safe-backends-p) +(defcustom company-transformers nil + "Functions to change the list of candidates received from backends, +after sorting and removal of duplicates (if appropriate). +Each function gets called with the return value of the previous one." + :type '(choice + (const :tag "None" nil) + (const :tag "Sort by occurrence" (company-sort-by-occurrence)) + (const :tag "Sort by back-end importance" + (company-sort-by-backend-importance)) + (repeat :tag "User defined" (function)))) + (defcustom company-completion-started-hook nil "Hook run when company starts completing. The hook is called with one argument that is non-nil if the completion was started manually." - :group 'company :type 'hook) (defcustom company-completion-cancelled-hook nil "Hook run when company cancels completing. The hook is called with one argument that is non-nil if the completion was aborted manually." - :group 'company :type 'hook) (defcustom company-completion-finished-hook nil "Hook run when company successfully completes. -The hook is called with the selected candidate as an argument." - :group 'company +The hook is called with the selected candidate as an argument. + +If you indend to use it to post-process candidates from a specific +back-end, consider using the `post-completion' command instead." :type 'hook) (defcustom company-minimum-prefix-length 3 - "The minimum prefix length for automatic completion." - :group 'company + "The minimum prefix length for idle completion." :type '(integer :tag "prefix length")) +(defcustom company-abort-manual-when-too-short nil + "If enabled, cancel a manually started completion when the prefix gets +shorter than both `company-minimum-prefix-length' and the length of the +prefix it was started from." + :type 'boolean + :package-version '(company . "0.8.0")) + (defcustom company-require-match 'company-explicit-action-p "If enabled, disallow non-matching input. This can be a function do determine if a match is required. -This can be overridden by the back-end, if it returns t or 'never to -'require-match. `company-auto-complete' also takes precedence over this." - :group 'company +This can be overridden by the back-end, if it returns t or `never' to +`require-match'. `company-auto-complete' also takes precedence over this." :type '(choice (const :tag "Off" nil) (function :tag "Predicate function") (const :tag "On, if user interaction took place" 'company-explicit-action-p) (const :tag "On" t))) -(defcustom company-auto-complete 'company-explicit-action-p +(defcustom company-auto-complete nil "Determines when to auto-complete. -If this is enabled, all characters from `company-auto-complete-chars' complete -the selected completion. This can also be a function." - :group 'company +If this is enabled, all characters from `company-auto-complete-chars' +trigger insertion of the selected completion candidate. +This can also be a function." :type '(choice (const :tag "Off" nil) (function :tag "Predicate function") (const :tag "On, if user interaction took place" 'company-explicit-action-p) (const :tag "On" t))) -(defcustom company-auto-complete-chars '(?\ ?\( ?\) ?. ?\" ?$ ?\' ?< ?| ?!) - "Determines which characters trigger an automatic completion. -See `company-auto-complete'. If this is a string, each string character causes -completion. If it is a list of syntax description characters (see +(defcustom company-auto-complete-chars '(?\ ?\) ?.) + "Determines which characters trigger auto-completion. +See `company-auto-complete'. If this is a string, each string character +tiggers auto-completion. If it is a list of syntax description characters (see `modify-syntax-entry'), all characters with that syntax auto-complete. This can also be a function, which is called with the new input and should return non-nil if company should auto-complete. -A character that is part of a valid candidate never starts auto-completion." - :group 'company +A character that is part of a valid candidate never triggers auto-completion." :type '(choice (string :tag "Characters") (set :tag "Syntax" (const :tag "Whitespace" ?\ ) @@ -503,29 +542,47 @@ A character that is part of a valid candidate never starts auto-completion." (const :tag "Generic comment fence." ?!)) (function :tag "Predicate function"))) -(defcustom company-idle-delay .7 - "The idle delay in seconds until automatic completions starts. -A value of nil means never complete automatically, t means complete -immediately when a prefix of `company-minimum-prefix-length' is reached." - :group 'company +(defcustom company-idle-delay .5 + "The idle delay in seconds until completion starts automatically. +The prefix still has to satisfy `company-minimum-prefix-length' before that +happens. The value of nil means no idle completion." :type '(choice (const :tag "never (nil)" nil) - (const :tag "immediate (t)" t) + (const :tag "immediate (0)" 0) (number :tag "seconds"))) -(defcustom company-begin-commands t - "A list of commands following which company will start completing. -If this is t, it will complete after any command. See `company-idle-delay'. +(defcustom company-begin-commands '(self-insert-command org-self-insert-command) + "A list of commands after which idle completion is allowed. +If this is t, it can show completions after any command except a few from a +pre-defined list. See `company-idle-delay'. -Alternatively any command with a non-nil 'company-begin property is treated as -if it was on this list." - :group 'company +Alternatively, any command with a non-nil `company-begin' property is +treated as if it was on this list." :type '(choice (const :tag "Any command" t) (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." - :group 'company + :type '(choice (const :tag "off" nil) + (const :tag "on" t))) + +(defcustom company-selection-wrap-around nil + "If enabled, selecting item before first or after last wraps around." :type '(choice (const :tag "off" nil) (const :tag "on" t))) @@ -533,6 +590,13 @@ if it was on this list." "Work around a visualization bug when completing at the end of the buffer. The work-around consists of adding a newline.") +(defvar company-async-wait 0.03 + "Pause between checks to see if the value's been set when turning an +asynchronous call into synchronous.") + +(defvar company-async-timeout 2 + "Maximum wait time for a value to be set during asynchronous call.") + ;;; mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar company-mode-map (make-sparse-keymap) @@ -544,8 +608,8 @@ The work-around consists of adding a newline.") (define-key keymap "\C-g" 'company-abort) (define-key keymap (kbd "M-n") 'company-select-next) (define-key keymap (kbd "M-p") 'company-select-previous) - (define-key keymap (kbd "") 'company-select-next) - (define-key keymap (kbd "") 'company-select-previous) + (define-key keymap (kbd "") 'company-select-next-or-abort) + (define-key keymap (kbd "") 'company-select-previous-or-abort) (define-key keymap [down-mouse-1] 'ignore) (define-key keymap [down-mouse-3] 'ignore) (define-key keymap [mouse-1] 'company-complete-mouse) @@ -553,14 +617,19 @@ The work-around consists of adding a newline.") (define-key keymap [up-mouse-1] 'ignore) (define-key keymap [up-mouse-3] 'ignore) (define-key keymap [return] 'company-complete-selection) + (define-key keymap (kbd "RET") 'company-complete-selection) (define-key keymap [tab] 'company-complete-common) + (define-key keymap (kbd "TAB") 'company-complete-common) (define-key keymap (kbd "") 'company-show-doc-buffer) + (define-key keymap (kbd "C-h") 'company-show-doc-buffer) (define-key keymap "\C-w" 'company-show-location) (define-key keymap "\C-s" 'company-search-candidates) (define-key keymap "\C-\M-s" 'company-filter-candidates) (dotimes (i 10) (define-key keymap (vector (+ (aref (kbd "M-0") 0) i)) - `(lambda () (interactive) (company-complete-number ,i)))) + `(lambda () + (interactive) + (company-complete-number ,(if (zerop i) 10 i))))) keymap) "Keymap that is enabled during an active completion.") @@ -571,26 +640,29 @@ The work-around consists of adding a newline.") (and (symbolp backend) (not (fboundp backend)) (ignore-errors (require backend nil t))) - - (if (or (symbolp backend) - (functionp backend)) - (condition-case err - (progn - (funcall backend 'init) - (put backend 'company-init t)) - (error - (put backend 'company-init 'failed) - (unless (memq backend company--disabled-backends) - (message "Company back-end '%s' could not be initialized:\n%s" - backend (error-message-string err))) - (pushnew backend company--disabled-backends) - nil)) - (mapc 'company-init-backend backend))) + (cond + ((symbolp backend) + (condition-case err + (progn + (funcall backend 'init) + (put backend 'company-init t)) + (error + (put backend 'company-init 'failed) + (unless (memq backend company--disabled-backends) + (message "Company back-end '%s' could not be initialized:\n%s" + backend (error-message-string err))) + (cl-pushnew backend company--disabled-backends) + nil))) + ;; No initialization for lambdas. + ((functionp backend) t) + (t ;; Must be a list. + (cl-dolist (b backend) + (unless (keywordp b) + (company-init-backend b)))))) (defvar company-default-lighter " company") -(defvar company-lighter company-default-lighter) -(make-variable-buffer-local 'company-lighter) +(defvar-local company-lighter company-default-lighter) ;;;###autoload (define-minor-mode company-mode @@ -607,9 +679,9 @@ Completions can be searched with `company-search-candidates' or `company-filter-candidates'. These can be used while completion is inactive, as well. -The completion data is retrieved using `company-backends' and displayed using -`company-frontends'. If you want to start a specific back-end, call it -interactively or use `company-begin-backend'. +The completion data is retrieved using `company-backends' and displayed +using `company-frontends'. If you want to start a specific back-end, call +it interactively or use `company-begin-backend'. regular keymap (`company-mode-map'): @@ -620,6 +692,9 @@ keymap during active completions (`company-active-map'): nil company-lighter company-mode-map (if company-mode (progn + (when (eq company-idle-delay t) + (setq company-idle-delay 0) + (warn "Setting `company-idle-delay' to t is deprecated. Set it to 0 instead.")) (add-hook 'pre-command-hook 'company-pre-command nil t) (add-hook 'post-command-hook 'company-post-command nil t) (mapc 'company-init-backend company-backends)) @@ -628,9 +703,34 @@ keymap during active completions (`company-active-map'): (company-cancel) (kill-local-variable 'company-point))) -(define-globalized-minor-mode global-company-mode company-mode - (lambda () (unless (or noninteractive (eq (aref (buffer-name) 0) ?\s)) - (company-mode 1)))) +(defcustom company-global-modes t + "Modes for which `company-mode' mode is turned on by `global-company-mode'. +If nil, means no modes. If t, then all major modes have it turned on. +If a list, it should be a list of `major-mode' symbol names for which +`company-mode' should be automatically turned on. The sense of the list is +negated if it begins with `not'. For example: + (c-mode c++-mode) +means that `company-mode' is turned on for buffers in C and C++ modes only. + (not message-mode) +means that `company-mode' is always turned on except in `message-mode' buffers." + :type '(choice (const :tag "none" nil) + (const :tag "all" t) + (set :menu-tag "mode specific" :tag "modes" + :value (not) + (const :tag "Except" not) + (repeat :inline t (symbol :tag "mode"))))) + +;;;###autoload +(define-globalized-minor-mode global-company-mode company-mode company-mode-on) + +(defun company-mode-on () + (when (and (not (or noninteractive (eq (aref (buffer-name) 0) ?\s))) + (cond ((eq company-global-modes t) + t) + ((eq (car-safe company-global-modes) 'not) + (not (memq major-mode (cdr company-global-modes)))) + (t (memq major-mode company-global-modes)))) + (company-mode 1))) (defsubst company-assert-enabled () (unless company-mode @@ -639,8 +739,7 @@ keymap during active completions (`company-active-map'): ;;; keymaps ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar company-my-keymap nil) -(make-variable-buffer-local 'company-my-keymap) +(defvar-local company-my-keymap nil) (defvar company-emulation-alist '((t . nil))) @@ -665,6 +764,7 @@ keymap during active completions (`company-active-map'): ;; Hack: ;; 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. +;; XXX: Seems not to be needed anymore in Emacs 24.4 (defun company-ignore () (interactive) (setq this-command last-command)) @@ -674,20 +774,27 @@ keymap during active completions (`company-active-map'): (defun company-input-noop () (push 31415926 unread-command-events)) -;; Hack: -;; posn-col-row is incorrect in older Emacsen when line-spacing is set -(defun company--col-row (&optional pos) - (let ((posn (posn-at-point pos))) - (cons (car (posn-col-row posn)) (cdr (posn-actual-col-row posn))))) - -(defsubst company--column (&optional pos) - (car (posn-col-row (posn-at-point pos)))) - -(defsubst company--row (&optional pos) - (cdr (posn-actual-col-row (posn-at-point pos)))) +(defun company--column (&optional pos) + (save-excursion + (when pos (goto-char pos)) + (save-restriction + (+ (save-excursion + (vertical-motion 0) + (narrow-to-region (point) (point-max)) + (let ((prefix (get-text-property (point) 'line-prefix))) + (if prefix (length prefix) 0))) + (current-column))))) + +(defun company--row (&optional pos) + (save-excursion + (when pos (goto-char pos)) + (count-screen-lines (window-start) + (progn (vertical-motion 0) (point))))) ;;; backends ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defvar-local company-backend nil) + (defun company-grab (regexp &optional expression limit) (when (looking-back regexp limit) (or (match-string-no-properties (or expression 0)) ""))) @@ -709,93 +816,176 @@ keymap during active completions (`company-active-map'): (unless (and (char-after) (eq (char-syntax (char-after)) ?w)) ""))) +(defun company-grab-symbol-cons (idle-begin-after-re &optional max-len) + (let ((symbol (company-grab-symbol))) + (when symbol + (save-excursion + (forward-char (- (length symbol))) + (if (looking-back idle-begin-after-re (if max-len + (- (point) max-len) + (line-beginning-position))) + (cons symbol t) + symbol))))) + (defun company-in-string-or-comment () (let ((ppss (syntax-ppss))) (or (car (setq ppss (nthcdr 3 ppss))) (car (setq ppss (cdr ppss))) (nth 3 ppss)))) -(if (fboundp 'locate-dominating-file) - (defalias 'company-locate-dominating-file 'locate-dominating-file) - (defun company-locate-dominating-file (file name) - (catch 'root - (let ((dir (file-name-directory file)) - (prev-dir nil)) - (while (not (equal dir prev-dir)) - (when (file-exists-p (expand-file-name name dir)) - (throw 'root dir)) - (setq prev-dir dir - dir (file-name-directory (directory-file-name dir)))))))) - (defun company-call-backend (&rest args) - (if (functionp company-backend) - (apply company-backend args) - (apply 'company--multi-backend-adapter company-backend args))) + (company--force-sync #'company-call-backend-raw args company-backend)) + +(defun company--force-sync (fun args backend) + (let ((value (apply fun args))) + (if (not (eq (car-safe value) :async)) + value + (let ((res 'trash) + (start (time-to-seconds))) + (funcall (cdr value) + (lambda (result) (setq res result))) + (while (eq res 'trash) + (if (> (- (time-to-seconds) start) company-async-timeout) + (error "Company: Back-end %s async timeout with args %s" + backend args) + (sleep-for company-async-wait))) + res)))) + +(defun company-call-backend-raw (&rest args) + (condition-case err + (if (functionp company-backend) + (apply company-backend args) + (apply #'company--multi-backend-adapter company-backend args)) + (error (error "Company: Back-end %s error \"%s\" with args %s" + company-backend (error-message-string err) args)))) (defun company--multi-backend-adapter (backends command &rest args) - (let ((backends (remove-if (lambda (b) (eq 'failed (get b 'company-init))) - backends))) - (case command - (candidates - (loop for backend in backends - when (equal (funcall backend 'prefix) - (car args)) - nconc (apply backend 'candidates args))) - (sorted nil) - (duplicates t) - (otherwise + (let ((backends (cl-loop for b in backends + when (not (and (symbolp b) + (eq 'failed (get b 'company-init)))) + collect b))) + (setq backends + (if (eq command 'prefix) + (butlast backends (length (member :with backends))) + (delq :with backends))) + (pcase command + (`candidates + (company--multi-backend-adapter-candidates backends (car args))) + (`sorted nil) + (`duplicates t) + ((or `prefix `ignore-case `no-cache `require-match) (let (value) - (dolist (backend backends) - (when (setq value (apply backend command args)) - (return value)))))))) + (cl-dolist (backend backends) + (when (setq value (company--force-sync + backend (cons command args) backend)) + (cl-return value))))) + (_ + (let ((arg (car args))) + (when (> (length arg) 0) + (let ((backend (or (get-text-property 0 'company-backend arg) + (car backends)))) + (apply backend command args)))))))) + +(defun company--multi-backend-adapter-candidates (backends prefix) + (let ((pairs (cl-loop for backend in (cdr backends) + when (equal (company--prefix-str + (funcall backend 'prefix)) + prefix) + collect (cons (funcall backend 'candidates prefix) + (let ((b backend)) + (lambda (candidates) + (mapcar + (lambda (str) + (propertize str 'company-backend b)) + candidates))))))) + (when (equal (company--prefix-str (funcall (car backends) 'prefix)) prefix) + ;; Small perf optimization: don't tag the candidates received + ;; from the first backend in the group. + (push (cons (funcall (car backends) 'candidates prefix) + 'identity) + pairs)) + (company--merge-async pairs (lambda (values) (apply #'append values))))) + +(defun company--merge-async (pairs merger) + (let ((async (cl-loop for pair in pairs + thereis + (eq :async (car-safe (car pair)))))) + (if (not async) + (funcall merger (cl-loop for (val . mapper) in pairs + collect (funcall mapper val))) + (cons + :async + (lambda (callback) + (let* (lst pending + (finisher (lambda () + (unless pending + (funcall callback + (funcall merger + (nreverse lst))))))) + (dolist (pair pairs) + (let ((val (car pair)) + (mapper (cdr pair))) + (if (not (eq :async (car-safe val))) + (push (funcall mapper val) lst) + (push nil lst) + (let ((cell lst) + (fetcher (cdr val))) + (push fetcher pending) + (funcall fetcher + (lambda (res) + (setq pending (delq fetcher pending)) + (setcar cell (funcall mapper res)) + (funcall finisher))))))))))))) + +(defun company--prefix-str (prefix) + (or (car-safe prefix) prefix)) ;;; completion mechanism ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar company-backend nil) -(make-variable-buffer-local 'company-backend) +(defvar-local company-prefix nil) + +(defvar-local company-candidates nil) -(defvar company-prefix nil) -(make-variable-buffer-local 'company-prefix) +(defvar-local company-candidates-length nil) -(defvar company-candidates nil) -(make-variable-buffer-local 'company-candidates) +(defvar-local company-candidates-cache nil) -(defvar company-candidates-length nil) -(make-variable-buffer-local 'company-candidates-length) +(defvar-local company-candidates-predicate nil) -(defvar company-candidates-cache nil) -(make-variable-buffer-local 'company-candidates-cache) +(defvar-local company-common nil) -(defvar company-candidates-predicate nil) -(make-variable-buffer-local 'company-candidates-predicate) +(defvar-local company-selection 0) -(defvar company-common nil) -(make-variable-buffer-local 'company-common) +(defvar-local company-selection-changed nil) -(defvar company-selection 0) -(make-variable-buffer-local 'company-selection) +(defvar-local company--manual-action nil + "Non-nil, if manual completion took place.") -(defvar company-selection-changed nil) -(make-variable-buffer-local 'company-selection-changed) +(defvar-local company--manual-prefix nil) -(defvar company--explicit-action nil - "Non-nil, if explicit completion took place.") -(make-variable-buffer-local 'company--explicit-action) +(defvar company--auto-completion nil + "Non-nil when current candidate is being inserted automatically. +Controlled by `company-auto-complete'.") -(defvar company--point-max nil) -(make-variable-buffer-local 'company--point-max) +(defvar-local company--point-max nil) -(defvar company-point nil) -(make-variable-buffer-local 'company-point) +(defvar-local company-point nil) (defvar company-timer nil) -(defvar company-added-newline nil) -(make-variable-buffer-local 'company-added-newline) +(defvar-local company-added-newline nil) (defsubst company-strip-prefix (str) (substring str (length company-prefix))) +(defun company--insert-candidate (candidate) + (setq candidate (substring-no-properties candidate)) + ;; XXX: Return value we check here is subject to change. + (if (eq (company-call-backend 'ignore-case) 'keep-prefix) + (insert (company-strip-prefix candidate)) + (delete-region (- (point) (length company-prefix)) (point)) + (insert candidate))) + (defmacro company-with-candidate-inserted (candidate &rest body) "Evaluate BODY with CANDIDATE temporarily inserted. This is a tool for back-ends that need candidates inserted before they @@ -804,48 +994,70 @@ can retrieve meta-data for them." `(let ((inhibit-modification-hooks t) (inhibit-point-motion-hooks t) (modified-p (buffer-modified-p))) - (insert (company-strip-prefix ,candidate)) + (company--insert-candidate ,candidate) (unwind-protect (progn ,@body) (delete-region company-point (point))))) (defun company-explicit-action-p () "Return whether explicit completion action was taken by the user." - (or company--explicit-action + (or company--manual-action company-selection-changed)) -(defsubst company-reformat (candidate) +(defun company-reformat (candidate) ;; company-ispell needs this, because the results are always lower-case ;; It's mory efficient to fix it only when they are displayed. - (concat company-prefix (substring candidate (length company-prefix)))) + ;; FIXME: Adopt the current text's capitalization instead? + (if (eq (company-call-backend 'ignore-case) 'keep-prefix) + (concat company-prefix (substring candidate (length company-prefix))) + candidate)) (defun company--should-complete () - (and (not (or buffer-read-only overriding-terminal-local-map - overriding-local-map - (minibufferp))) + (and (eq company-idle-delay 'now) + (not (or buffer-read-only overriding-terminal-local-map + overriding-local-map)) ;; Check if in the middle of entering a key combination. (or (equal (this-command-keys-vector) []) (not (keymapp (key-binding (this-command-keys-vector))))) - (eq company-idle-delay t) - (or (eq t company-begin-commands) - (memq this-command company-begin-commands) - (and (symbolp this-command) (get this-command 'company-begin))) (not (and transient-mark-mode mark-active)))) -(defsubst company-call-frontends (command) +(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) + (and (symbolp this-command) + (string-match-p "\\`company-" (symbol-name this-command))))))) + +(defun company-call-frontends (command) (dolist (frontend company-frontends) (condition-case err (funcall frontend command) (error (error "Company: Front-end %s error \"%s\" on command %s" frontend (error-message-string err) command))))) -(defsubst company-set-selection (selection &optional force-update) - (setq selection (max 0 (min (1- company-candidates-length) selection))) +(defun company-set-selection (selection &optional force-update) + (setq selection + (if company-selection-wrap-around + (mod selection company-candidates-length) + (max 0 (min (1- company-candidates-length) selection)))) (when (or force-update (not (equal selection company-selection))) + (company--update-group-lighter (nth selection company-candidates)) (setq company-selection selection company-selection-changed t) (company-call-frontends 'update))) +(defun company--update-group-lighter (candidate) + (when (listp company-backend) + (let ((backend (or (get-text-property 0 'company-backend candidate) + (car company-backend)))) + (when (and backend (symbolp backend)) + (let ((name (replace-regexp-in-string "company-\\|-company" "" + (symbol-name backend)))) + (setq company-lighter (format " company-<%s>" name))))))) + (defun company-apply-predicate (candidates predicate) (let (new) (dolist (c candidates) @@ -862,7 +1074,7 @@ can retrieve meta-data for them." company-candidates candidates) (when selected (while (and candidates (string< (pop candidates) selected)) - (incf company-selection)) + (cl-incf company-selection)) (unless candidates ;; Make sure selection isn't out of bounds. (setq company-selection (min (1- company-candidates-length) @@ -873,9 +1085,19 @@ can retrieve meta-data for them." (push (cons company-prefix company-candidates) company-candidates-cache) ;; Calculate common. (let ((completion-ignore-case (company-call-backend 'ignore-case))) - (setq company-common (try-completion company-prefix company-candidates))) - (when (eq company-common t) - (setq company-candidates nil))) + ;; We want to support non-prefix completion, so filtering is the + ;; responsibility of each respective backend, not ours. + ;; On the other hand, we don't want to replace non-prefix input in + ;; `company-complete-common'. + (setq company-common + (if (cdr company-candidates) + (let ((common (try-completion company-prefix company-candidates))) + (if (eq common t) + ;; Mulple equal strings, probably with different + ;; annotations. + company-prefix + common)) + (car company-candidates))))) (defun company-calculate-candidates (prefix) (let ((candidates (cdr (assoc prefix company-candidates-cache))) @@ -885,94 +1107,235 @@ can retrieve meta-data for them." (let ((len (length prefix)) (completion-ignore-case ignore-case) prev) - (dotimes (i (1+ len)) + (cl-dotimes (i (1+ len)) (when (setq prev (cdr (assoc (substring prefix 0 (- len i)) company-candidates-cache))) (setq candidates (all-completions prefix prev)) - (return t))))) + (cl-return t))))) ;; no cache match, call back-end - (progn - (setq candidates (company-call-backend 'candidates prefix)) - (when company-candidates-predicate - (setq candidates - (company-apply-predicate candidates - company-candidates-predicate))) - (unless (company-call-backend 'sorted) - (setq candidates (sort candidates 'string<))) - (when (company-call-backend 'duplicates) - ;; strip duplicates - (let ((c2 candidates)) - (while c2 - (setcdr c2 (progn (while (equal (pop c2) (car c2))) - c2))))))) - (if (and candidates - (or (cdr candidates) - (not (eq t (compare-strings (car candidates) nil nil - prefix nil nil ignore-case))))) - ;; Don't start when already completed and unique. - candidates - ;; Not the right place? maybe when setting? - (and company-candidates t)))) + (setq candidates + (company--process-candidates + (company--fetch-candidates prefix)))) + (setq candidates (company--transform-candidates candidates)) + (when candidates + (if (or (cdr candidates) + (not (eq t (compare-strings (car candidates) nil nil + prefix nil nil ignore-case)))) + candidates + ;; Already completed and unique; don't start. + t)))) + +(defun company--fetch-candidates (prefix) + (let ((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)) + (win (selected-window)) + (tick (buffer-chars-modified-tick)) + (pt (point)) + (backend company-backend)) + (funcall + (cdr c) + (lambda (candidates) + (if (not (and candidates (eq res 'done))) + ;; Fetcher called us back right away. + (setq res candidates) + (setq company-backend backend + company-candidates-cache + (list (cons prefix + (company--process-candidates + candidates)))) + (company-idle-begin buf win tick pt))))) + ;; FIXME: Relying on the fact that the callers + ;; will interpret nil as "do nothing" is shaky. + ;; A throw-catch would be one possible improvement. + (or res + (progn (setq res 'done) nil))))) + +(defun company--process-candidates (candidates) + (when company-candidates-predicate + (setq candidates + (company-apply-predicate candidates + company-candidates-predicate))) + (unless (company-call-backend 'sorted) + (setq candidates (sort candidates 'string<))) + (when (company-call-backend 'duplicates) + (company--strip-duplicates candidates)) + candidates) + +(defun company--strip-duplicates (candidates) + (let ((c2 candidates)) + (while c2 + (setcdr c2 + (let ((str (car c2)) + (anno 'unk)) + (pop c2) + (while (let ((str2 (car c2))) + (if (not (equal str str2)) + nil + (when (eq anno 'unk) + (setq anno (company-call-backend + 'annotation str))) + (equal anno + (company-call-backend + 'annotation str2)))) + (pop c2)) + c2))))) + +(defun company--transform-candidates (candidates) + (let ((c candidates)) + (dolist (tr company-transformers) + (setq c (funcall tr c))) + c)) + +(defcustom company-occurrence-weight-function + #'company-occurrence-prefer-closest-above + "Function to weigh matches in `company-sort-by-occurrence'. +It's called with three arguments: cursor position, the beginning and the +end of the match." + :type '(choice + (const :tag "First above point, then below point" + company-occurrence-prefer-closest-above) + (const :tag "Prefer closest in any direction" + company-occurrence-prefer-any-closest))) + +(defun company-occurrence-prefer-closest-above (pos match-beg match-end) + "Give priority to the matches above point, then those below point." + (if (< match-beg pos) + (- pos match-end) + (- match-beg (window-start)))) + +(defun company-occurrence-prefer-any-closest (pos _match-beg match-end) + "Give priority to the matches closest to the point." + (abs (- pos match-end))) + +(defun company-sort-by-occurrence (candidates) + "Sort CANDIDATES according to their occurrences. +Searches for each in the currently visible part of the current buffer and +prioritizes the matches according to `company-occurrence-weight-function'. +The rest of the list is appended unchanged. +Keywords and function definition names are ignored." + (let* ((w-start (window-start)) + (w-end (window-end)) + (start-point (point)) + occurs + (noccurs + (save-excursion + (cl-delete-if + (lambda (candidate) + (when (catch 'done + (goto-char w-start) + (while (search-forward candidate w-end t) + (when (and (not (eq (point) start-point)) + (save-match-data + (company--occurrence-predicate))) + (throw 'done t)))) + (push + (cons candidate + (funcall company-occurrence-weight-function + start-point + (match-beginning 0) + (match-end 0))) + occurs) + t)) + candidates)))) + (nconc + (mapcar #'car (sort occurs (lambda (e1 e2) (<= (cdr e1) (cdr e2))))) + noccurs))) + +(defun company--occurrence-predicate () + (let ((beg (match-beginning 0)) + (end (match-end 0))) + (save-excursion + (goto-char end) + (and (not (memq (get-text-property (1- (point)) 'face) + '(font-lock-function-name-face + font-lock-keyword-face))) + (let ((prefix (company--prefix-str + (company-call-backend 'prefix)))) + (and (stringp prefix) + (= (length prefix) (- end beg)))))))) + +(defun company-sort-by-backend-importance (candidates) + "Sort CANDIDATES as two priority groups. +If `company-backend' is a function, do nothing. If it's a list, move +candidates from back-ends before keyword `:with' to the front. Candidates +from the rest of the back-ends in the group, if any, will be left at the end." + (if (functionp company-backend) + candidates + (let ((low-priority (cdr (memq :with company-backend)))) + (if (null low-priority) + candidates + (sort candidates + (lambda (c1 c2) + (and + (let ((b2 (get-text-property 0 'company-backend c2))) + (and b2 (memq b2 low-priority))) + (let ((b1 (get-text-property 0 'company-backend c1))) + (or (not b1) (not (memq b1 low-priority))))))))))) (defun company-idle-begin (buf win tick pos) - (and company-mode - (eq buf (current-buffer)) + (and (eq buf (current-buffer)) (eq win (selected-window)) (eq tick (buffer-chars-modified-tick)) (eq pos (point)) - (not company-candidates) - (not (equal (point) company-point)) - (let ((company-idle-delay t) - (company-begin-commands t)) - (company-begin) - (when company-candidates - (company-input-noop) - (company-post-command))))) + (when (company-auto-begin) + (when (version< emacs-version "24.3.50") + (company-input-noop)) + (company-post-command)))) (defun company-auto-begin () - (company-assert-enabled) (and company-mode (not company-candidates) - (let ((company-idle-delay t) - (company-minimum-prefix-length 0) - (company-begin-commands t)) - (company-begin))) + (let ((company-idle-delay 'now)) + (condition-case-unless-debug err + (company--perform) + (error (message "Company: An error occurred in auto-begin") + (message "%s" (error-message-string err)) + (company-cancel)) + (quit (company-cancel))))) + (unless company-candidates + (setq company-backend nil)) ;; Return non-nil if active. company-candidates) (defun company-manual-begin () (interactive) - (setq company--explicit-action t) - (company-auto-begin)) + (company-assert-enabled) + (setq company--manual-action t) + (unwind-protect + (let ((company-minimum-prefix-length 0)) + (company-auto-begin)) + (unless company-candidates + (setq company--manual-action nil)))) (defun company-other-backend (&optional backward) (interactive (list current-prefix-arg)) (company-assert-enabled) - (if company-backend - (let* ((after (cdr (member company-backend company-backends))) - (before (cdr (member company-backend (reverse company-backends)))) - (next (if backward - (append before (reverse after)) - (append after (reverse before))))) - (company-cancel) - (dolist (backend next) - (when (ignore-errors (company-begin-backend backend)) - (return t)))) - (company-manual-begin)) + (let* ((after (if company-backend + (cdr (member company-backend company-backends)) + company-backends)) + (before (cdr (member company-backend (reverse company-backends)))) + (next (if backward + (append before (reverse after)) + (append after (reverse before))))) + (company-cancel) + (cl-dolist (backend next) + (when (ignore-errors (company-begin-backend backend)) + (cl-return t)))) (unless company-candidates (error "No other back-end"))) (defun company-require-match-p () (let ((backend-value (company-call-backend 'require-match))) (or (eq backend-value t) - (and (if (functionp company-require-match) + (and (not (eq backend-value 'never)) + (if (functionp company-require-match) (funcall company-require-match) - (eq company-require-match t)) - (not (eq backend-value 'never)))))) - -(defun company-punctuation-p (input) - "Return non-nil, if input starts with punctuation or parentheses." - (memq (char-syntax (string-to-char input)) '(?. ?\( ?\)))) + (eq company-require-match t)))))) (defun company-auto-complete-p (input) "Return non-nil, if input starts with punctuation or parentheses." @@ -994,37 +1357,43 @@ can retrieve meta-data for them." 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-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))))) + (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)) + ((and (or (not (company-require-match-p)) + ;; Don't require match if the new prefix + ;; doesn't continue the old one, and the latter was a match. + (<= (length new-prefix) (length company-prefix))) + (member company-prefix company-candidates)) + ;; Last input was a success, + ;; but we're treating it as an abort + input anyway, + ;; like the `unique' case below. + (company-cancel 'non-unique)) + ((company-require-match-p) + ;; Wrong incremental input, but required match. + (delete-char (- (length input))) + (ding) + (message "Matching input is required") + company-candidates) + (t (company-cancel))))) (defun company--good-prefix-p (prefix) - (and (or (company-explicit-action-p) - (>= (or (cdr-safe prefix) (length prefix)) - company-minimum-prefix-length)) - (stringp (or (car-safe prefix) prefix)))) + (and (stringp (company--prefix-str prefix)) ;excludes 'stop + (or (eq (cdr-safe prefix) t) + (let ((len (or (cdr-safe prefix) (length prefix)))) + (if company--manual-prefix + (or (not company-abort-manual-when-too-short) + ;; Must not be less than minimum or initial length. + (>= len (min company-minimum-prefix-length + (length company--manual-prefix)))) + (>= len company-minimum-prefix-length)))))) (defun company--continue () (when (company-call-backend 'no-cache company-prefix) @@ -1032,30 +1401,32 @@ can retrieve meta-data for them." (setq company-candidates-cache nil)) (let* ((new-prefix (company-call-backend 'prefix)) (c (when (and (company--good-prefix-p new-prefix) - (setq new-prefix (or (car-safe new-prefix) new-prefix)) + (setq new-prefix (company--prefix-str new-prefix)) (= (- (point) (length new-prefix)) (- 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 new-prefix))) - (company-cancel)))) + (cond + ((eq c t) + ;; t means complete/unique. + ;; Handle it like completion was aborted, to differentiate from user + ;; calling one of Company's commands to insert the candidate, + ;; not to trigger template expansion, etc. + (company-cancel 'unique)) + ((consp c) + ;; incremental match + (setq company-prefix new-prefix) + (company-update-candidates c) + c) + ((not (company--incremental-p)) + (company-cancel)) + (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)) + (cl-dolist (backend (if company-backend + ;; prefer manual override + (list company-backend) + company-backends)) (setq prefix (if (or (symbolp backend) (functionp backend)) @@ -1067,29 +1438,33 @@ can retrieve meta-data for them." (company--multi-backend-adapter backend 'prefix))) (when prefix (when (company--good-prefix-p prefix) - (setq prefix (or (car-safe prefix) prefix) + (setq company-prefix (company--prefix-str prefix) company-backend backend - c (company-calculate-candidates prefix)) + c (company-calculate-candidates company-prefix)) ;; t means complete/unique. We don't start, so no hooks. (if (not (consp c)) - (when company--explicit-action + (when company--manual-action (message "No completion found")) - (setq company-prefix prefix) - (when (symbolp backend) - (setq company-lighter (concat " " (symbol-name backend)))) + (when company--manual-action + (setq company--manual-prefix prefix)) + (if (symbolp backend) + (setq company-lighter (concat " " (symbol-name backend))) + (company--update-group-lighter (car c))) (company-update-candidates c) (run-hook-with-args 'company-completion-started-hook (company-explicit-action-p)) (company-call-frontends 'show))) - (return c))))) + (cl-return c))))) -(defun company-begin () +(defun company--perform () (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) @@ -1102,46 +1477,47 @@ can retrieve meta-data for them." (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) - (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))) - (setq company-added-newline nil - company-backend nil - company-prefix nil - company-candidates nil - company-candidates-length nil - company-candidates-cache nil - company-candidates-predicate nil - company-common nil - company-selection 0 - company-selection-changed nil - company--explicit-action nil - company-lighter company-default-lighter - company--point-max nil - company-point nil) - (when company-timer - (cancel-timer company-timer)) - (company-search-mode 0) - (company-call-frontends 'hide) - (company-enable-overriding-keymap nil)) + (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))) + (setq company-added-newline nil + company-backend nil + company-prefix nil + company-candidates nil + company-candidates-length nil + company-candidates-cache nil + company-candidates-predicate nil + company-common nil + company-selection 0 + company-selection-changed nil + company--manual-action nil + company--manual-prefix nil + company-lighter company-default-lighter + company--point-max nil + company-point nil) + (when company-timer + (cancel-timer company-timer)) + (company-search-mode 0) + (company-call-frontends 'hide) + (company-enable-overriding-keymap nil)) + ;; Make return value explicit. + nil) (defun company-abort () (interactive) - (company-cancel t) - ;; Don't start again, unless started manually. - (setq company-point (point))) + (company-cancel 'abort)) (defun company-finish (result) - (insert (company-strip-prefix result)) - (company-cancel result) - ;; Don't start again, unless started manually. - (setq company-point (point))) + (company--insert-candidate result) + (company-cancel result)) (defsubst company-keep (command) (and (symbolp command) (get command 'company-keep))) @@ -1150,7 +1526,9 @@ can retrieve meta-data for them." (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)))) @@ -1164,12 +1542,12 @@ can retrieve meta-data for them." (condition-case err (progn (unless (equal (point) company-point) - (company-begin)) + (let (company-idle-delay) ; Against misbehavior while debugging. + (company--perform))) (if company-candidates (company-call-frontends 'post-command) (and (numberp company-idle-delay) - (or (eq t company-begin-commands) - (memq this-command company-begin-commands)) + (company--should-begin) (setq company-timer (run-with-timer company-idle-delay nil 'company-idle-begin @@ -1180,27 +1558,39 @@ can retrieve meta-data for them." (company-cancel)))) (company-install-map)) +(defvar company--begin-inhibit-commands '(company-abort + company-complete-mouse + company-complete + company-complete-common + company-complete-selection + company-complete-number) + "List of commands after which idle completion is (still) disabled when +`company-begin-commands' is t.") + +(defun company--should-begin () + (if (eq t company-begin-commands) + (not (memq this-command company--begin-inhibit-commands)) + (or + (memq this-command company-begin-commands) + (and (symbolp this-command) (get this-command 'company-begin))))) + ;;; search ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar company-search-string nil) -(make-variable-buffer-local 'company-search-string) +(defvar-local company-search-string nil) -(defvar company-search-lighter " Search: \"\"") -(make-variable-buffer-local 'company-search-lighter) +(defvar-local company-search-lighter " Search: \"\"") -(defvar company-search-old-map nil) -(make-variable-buffer-local 'company-search-old-map) +(defvar-local company-search-old-map nil) -(defvar company-search-old-selection 0) -(make-variable-buffer-local 'company-search-old-selection) +(defvar-local company-search-old-selection 0) (defun company-search (text lines) (let ((quoted (regexp-quote text)) (i 0)) - (dolist (line lines) + (cl-dolist (line lines) (when (string-match quoted line (length company-prefix)) - (return i)) - (incf i)))) + (cl-return i)) + (cl-incf i)))) (defun company-search-printing-char () (interactive) @@ -1208,9 +1598,9 @@ can retrieve meta-data for them." (setq company-search-string (concat (or company-search-string "") (string last-command-event)) company-search-lighter (concat " Search: \"" company-search-string - "\"")) + "\"")) (let ((pos (company-search company-search-string - (nthcdr company-selection company-candidates)))) + (nthcdr company-selection company-candidates)))) (if (null pos) (ding) (company-set-selection (+ company-selection pos) t)))) @@ -1220,8 +1610,8 @@ can retrieve meta-data for them." (interactive) (company-search-assert-enabled) (let ((pos (company-search company-search-string - (cdr (nthcdr company-selection - company-candidates))))) + (cdr (nthcdr company-selection + company-candidates))))) (if (null pos) (ding) (company-set-selection (+ company-selection pos 1) t)))) @@ -1231,9 +1621,9 @@ can retrieve meta-data for them." (interactive) (company-search-assert-enabled) (let ((pos (company-search company-search-string - (nthcdr (- company-candidates-length - company-selection) - (reverse company-candidates))))) + (nthcdr (- company-candidates-length + company-selection) + (reverse company-candidates))))) (if (null pos) (ding) (company-set-selection (- company-selection pos 1) t)))) @@ -1277,9 +1667,7 @@ can retrieve meta-data for them." (interactive) (company-search-assert-enabled) (company-search-mode 0) - (when last-input-event - (clear-this-command-keys t) - (setq unread-command-events (list last-input-event)))) + (company--unread-last-input)) (defvar company-search-map (let ((i 0) @@ -1288,7 +1676,7 @@ can retrieve meta-data for them." (set-char-table-range (nth 1 keymap) (cons #x100 (max-char)) 'company-search-printing-char) (with-no-warnings - ;; obselete in Emacs 23 + ;; obsolete in Emacs 23 (let ((l (generic-character-list)) (table (nth 1 keymap))) (while l @@ -1297,16 +1685,17 @@ can retrieve meta-data for them." (define-key keymap [t] 'company-search-other-char) (while (< i ?\s) (define-key keymap (make-string 1 i) 'company-search-other-char) - (incf i)) + (cl-incf i)) (while (< i 256) (define-key keymap (vector i) 'company-search-printing-char) - (incf i)) + (cl-incf i)) (let ((meta-map (make-sparse-keymap))) (define-key keymap (char-to-string meta-prefix-char) meta-map) (define-key keymap [escape] meta-map)) (define-key keymap (vector meta-prefix-char t) 'company-search-other-char) (define-key keymap "\e\e\e" 'company-search-other-char) - (define-key keymap [escape escape escape] 'company-search-other-char) + (define-key keymap [escape escape escape] 'company-search-other-char) + (define-key keymap (kbd "DEL") 'company-search-other-char) (define-key keymap "\C-g" 'company-search-abort) (define-key keymap "\C-s" 'company-search-repeat-forward) @@ -1331,7 +1720,7 @@ Don't start this directly, use `company-search-candidates' or (kill-local-variable 'company-search-old-selection) (company-enable-overriding-keymap company-active-map))) -(defsubst company-search-assert-enabled () +(defun company-search-assert-enabled () (company-assert-enabled) (unless company-search-mode (company-uninstall-map) @@ -1347,8 +1736,8 @@ Don't start this directly, use `company-search-candidates' or Regular characters are appended to the search string. -The command `company-search-kill-others' (\\[company-search-kill-others]) uses - the search string to limit the completion candidates." +The command `company-search-kill-others' (\\[company-search-kill-others]) +uses the search string to limit the completion candidates." (interactive) (company-search-mode 1) (company-enable-overriding-keymap company-search-map)) @@ -1383,40 +1772,106 @@ followed by `company-search-kill-others' after each input." (when (company-manual-begin) (company-set-selection (1- company-selection)))) +(defun company-select-next-or-abort () + "Select the next candidate if more than one, else abort +and invoke the normal binding." + (interactive) + (if (> company-candidates-length 1) + (company-select-next) + (company-abort) + (company--unread-last-input))) + +(defun company-select-previous-or-abort () + "Select the previous candidate if more than one, else abort +and invoke the normal binding." + (interactive) + (if (> company-candidates-length 1) + (company-select-previous) + (company-abort) + (company--unread-last-input))) + +(defvar company-pseudo-tooltip-overlay) + +(defvar company-tooltip-offset) + +(defun company--inside-tooltip-p (event-col-row row height) + (let* ((ovl company-pseudo-tooltip-overlay) + (column (overlay-get ovl 'company-column)) + (width (overlay-get ovl 'company-width)) + (evt-col (car event-col-row)) + (evt-row (cdr event-col-row))) + (and (>= evt-col column) + (< evt-col (+ column width)) + (if (> height 0) + (and (> evt-row row) + (<= evt-row (+ row height) )) + (and (< evt-row row) + (>= evt-row (+ row height))))))) + +(defun company--event-col-row (event) + (let* ((col-row (posn-actual-col-row (event-start event))) + (col (car col-row)) + (row (cdr col-row))) + (cl-incf col (window-hscroll)) + (and header-line-format + (version< "24" emacs-version) + (cl-decf row)) + (cons col row))) + (defun company-select-mouse (event) "Select the candidate picked by the mouse." (interactive "e") - (when (nth 4 (event-start event)) - (company-set-selection (- (cdr (posn-actual-col-row (event-start event))) - (company--row) - 1)) - t)) + (let ((event-col-row (company--event-col-row event)) + (ovl-row (company--row)) + (ovl-height (and company-pseudo-tooltip-overlay + (min (overlay-get company-pseudo-tooltip-overlay + 'company-height) + company-candidates-length)))) + (if (and ovl-height + (company--inside-tooltip-p event-col-row ovl-row ovl-height)) + (progn + (company-set-selection (+ (cdr event-col-row) + (1- company-tooltip-offset) + (if (and (eq company-tooltip-offset-display 'lines) + (not (zerop company-tooltip-offset))) + -1 0) + (- ovl-row) + (if (< ovl-height 0) + (- 1 ovl-height) + 0))) + t) + (company-abort) + (company--unread-last-input) + nil))) (defun company-complete-mouse (event) - "Complete the candidate picked by the mouse." + "Insert the candidate picked by the mouse." (interactive "e") (when (company-select-mouse event) (company-complete-selection))) (defun company-complete-selection () - "Complete the selected candidate." + "Insert the selected candidate." (interactive) (when (company-manual-begin) - (company-finish (nth company-selection company-candidates)))) + (let ((result (nth company-selection company-candidates))) + (company-finish result)))) (defun company-complete-common () - "Complete the common part of all candidates." + "Insert the common part of all candidates." (interactive) (when (company-manual-begin) (if (and (not (cdr company-candidates)) (equal company-common (car company-candidates))) (company-complete-selection) - (insert (company-strip-prefix company-common))))) + (when company-common + (company--insert-candidate company-common))))) (defun company-complete () - "Complete the common part of all candidates or the current selection. -The first time this is called, the common part is completed, the second time, or -when the selection has been changed, the selected candidate is completed." + "Insert the common part of all candidates or the current selection. +The first time this is called, the common part is inserted, the second +time, or when the selection has been changed, the selected candidate is +inserted." (interactive) (when (company-manual-begin) (if (or company-selection-changed @@ -1426,13 +1881,13 @@ when the selection has been changed, the selected candidate is completed." (setq this-command 'company-complete-common)))) (defun company-complete-number (n) - "Complete the Nth candidate. + "Insert the Nth candidate. To show the number next to the candidates in some back-ends, enable `company-show-numbers'." (when (company-manual-begin) - (and (< n 1) (> n company-candidates-length) + (and (or (< n 1) (> n company-candidates-length)) (error "No candidate number %d" n)) - (decf n) + (cl-decf n) (company-finish (nth n company-candidates)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1445,12 +1900,12 @@ To show the number next to the candidates in some back-ends, enable (push (make-string (- company-space-strings-limit 1 i) ?\ ) lst)) (apply 'vector lst))) -(defsubst company-space-string (len) +(defun company-space-string (len) (if (< len company-space-strings-limit) (aref company-space-strings len) (make-string len ?\ ))) -(defsubst company-safe-substring (str from &optional to) +(defun company-safe-substring (str from &optional to) (if (> from (string-width str)) "" (with-temp-buffer @@ -1468,19 +1923,21 @@ To show the number next to the candidates in some back-ends, enable ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar company-last-metadata nil) -(make-variable-buffer-local 'company-last-metadata) +(defvar-local company-last-metadata nil) (defun company-fetch-metadata () (let ((selected (nth company-selection company-candidates))) - (unless (equal selected (car company-last-metadata)) + (unless (eq selected (car company-last-metadata)) (setq company-last-metadata (cons selected (company-call-backend 'meta selected)))) (cdr company-last-metadata))) (defun company-doc-buffer (&optional string) - (with-current-buffer (get-buffer-create "*Company meta-data*") + (with-current-buffer (get-buffer-create "*company-documentation*") (erase-buffer) + (when string + (save-excursion + (insert string))) (current-buffer))) (defvar company--electric-commands @@ -1501,12 +1958,15 @@ To show the number next to the candidates in some back-ends, enable (while (memq (setq cmd (key-binding (vector (list (read-event))))) company--electric-commands) (call-interactively cmd)) - (when last-input-event - (clear-this-command-keys t) - (setq unread-command-events (list last-input-event))))))) + (company--unread-last-input))))) + +(defun company--unread-last-input () + (when last-input-event + (clear-this-command-keys t) + (setq unread-command-events (list last-input-event)))) (defun company-show-doc-buffer () - "Temporarily show a buffer with the complete documentation for the selection." + "Temporarily show the documentation buffer for the selection." (interactive) (company--electric-do (let* ((selected (nth company-selection company-candidates)) @@ -1538,18 +1998,12 @@ To show the number next to the candidates in some back-ends, enable ;;; package functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar company-callback nil) -(make-variable-buffer-local 'company-callback) - -(defvar company-begin-with-marker nil) -(make-variable-buffer-local 'company-begin-with-marker) +(defvar-local company-callback nil) (defun company-remove-callback (&optional ignored) (remove-hook 'company-completion-finished-hook company-callback t) (remove-hook 'company-completion-cancelled-hook 'company-remove-callback t) - (remove-hook 'company-completion-finished-hook 'company-remove-callback t) - (when company-begin-with-marker - (set-marker company-begin-with-marker nil))) + (remove-hook 'company-completion-finished-hook 'company-remove-callback t)) (defun company-begin-backend (backend &optional callback) "Start a completion at point using BACKEND." @@ -1570,62 +2024,82 @@ To show the number next to the candidates in some back-ends, enable (defun company-begin-with (candidates &optional prefix-length require-match callback) "Start a completion at point. -CANDIDATES is the list of candidates to use and PREFIX-LENGTH is the length of -the prefix that already is in the buffer before point. It defaults to 0. - -CALLBACK is a function called with the selected result if the user successfully -completes the input. - -Example: -\(company-begin-with '\(\"foo\" \"foobar\" \"foobarbaz\"\)\)" - (setq company-begin-with-marker (copy-marker (point) t)) - (company-begin-backend - `(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)) +CANDIDATES is the list of candidates to use and PREFIX-LENGTH is the length +of the prefix that already is in the buffer before point. +It defaults to 0. + +CALLBACK is a function called with the selected result if the user +successfully completes the input. + +Example: \(company-begin-with '\(\"foo\" \"foobar\" \"foobarbaz\"\)\)" + (let ((begin-marker (copy-marker (point) t))) + (company-begin-backend + (lambda (command &optional arg &rest ignored) + (pcase command + (`prefix + (when (equal (point) (marker-position begin-marker)) + (buffer-substring (- (point) (or prefix-length 0)) (point)))) + (`candidates + (all-completions arg candidates)) + (`require-match + require-match))) + callback))) + +(defun company-version (&optional show-version) + "Get the Company version as string. + +If SHOW-VERSION is non-nil, show the version in the echo area." + (interactive (list t)) + (with-temp-buffer + (insert-file-contents (find-library-name "company")) + (require 'lisp-mnt) + (if show-version + (message "Company version: %s" (lm-version)) + (lm-version)))) ;;; pseudo-tooltip ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar company-pseudo-tooltip-overlay nil) -(make-variable-buffer-local 'company-pseudo-tooltip-overlay) +(defvar-local company-pseudo-tooltip-overlay nil) -(defvar company-tooltip-offset 0) -(make-variable-buffer-local 'company-tooltip-offset) +(defvar-local company-tooltip-offset 0) -(defun company-pseudo-tooltip-update-offset (selection num-lines limit) - - (decf limit 2) +(defun company-tooltip--lines-update-offset (selection num-lines limit) + (cl-decf limit 2) (setq company-tooltip-offset (max (min selection company-tooltip-offset) (- selection -1 limit))) (when (<= company-tooltip-offset 1) - (incf limit) + (cl-incf limit) (setq company-tooltip-offset 0)) (when (>= company-tooltip-offset (- num-lines limit 1)) - (incf limit) + (cl-incf limit) (when (= selection (1- num-lines)) - (decf company-tooltip-offset) + (cl-decf company-tooltip-offset) (when (<= company-tooltip-offset 1) (setq company-tooltip-offset 0) - (incf limit)))) + (cl-incf limit)))) limit) +(defun company-tooltip--simple-update-offset (selection _num-lines limit) + (setq company-tooltip-offset + (if (< selection company-tooltip-offset) + selection + (max company-tooltip-offset + (- selection limit -1))))) + ;;; propertize (defsubst company-round-tab (arg) (* (/ (+ arg tab-width) tab-width) tab-width)) -(defun company-untabify (str) +(defun company-plainify (str) + (let ((prefix (get-text-property 0 'line-prefix str))) + (when prefix ; Keep the original value unmodified, for no special reason. + (setq str (concat prefix str)) + (remove-text-properties 0 (length str) '(line-prefix) str))) (let* ((pieces (split-string str "\t")) (copy pieces)) (while (cdr copy) @@ -1634,35 +2108,67 @@ Example: (pop copy)) (apply 'concat pieces))) -(defun company-fill-propertize (line width selected) - (setq line (company-safe-substring line 0 width)) - (add-text-properties 0 width '(face company-tooltip - mouse-face company-tooltip-mouse) - line) - (add-text-properties 0 (length company-common) - '(face company-tooltip-common - mouse-face company-tooltip-mouse) - line) - (when selected - (if (and company-search-string - (string-match (regexp-quote company-search-string) line - (length company-prefix))) - (progn - (add-text-properties (match-beginning 0) (match-end 0) - '(face company-tooltip-selection) - line) - (when (< (match-beginning 0) (length company-common)) - (add-text-properties (match-beginning 0) (length company-common) - '(face company-tooltip-common-selection) - line))) - (add-text-properties 0 width '(face company-tooltip-selection - mouse-face company-tooltip-selection) - line) - (add-text-properties 0 (length company-common) - '(face company-tooltip-common-selection - mouse-face company-tooltip-selection) - line))) - line) +(defun company-fill-propertize (value annotation width selected left right) + (let* ((margin (length left)) + (common (+ (or (company-call-backend 'match value) + (length company-common)) margin)) + (ann-ralign company-tooltip-align-annotations) + (ann-truncate (< width + (+ (length value) (length annotation) + (if ann-ralign 1 0)))) + (ann-start (+ margin + (if ann-ralign + (if ann-truncate + (1+ (length value)) + (- width (length annotation))) + (length value)))) + (ann-end (min (+ ann-start (length annotation)) (+ margin width))) + (line (concat left + (if (or ann-truncate (not ann-ralign)) + (company-safe-substring + (concat value + (when (and annotation ann-ralign) " ") + annotation) + 0 width) + (concat + (company-safe-substring value 0 + (- width (length annotation))) + annotation)) + right))) + (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) + (when (< ann-start ann-end) + (add-text-properties ann-start ann-end + '(face company-tooltip-annotation + mouse-face company-tooltip-mouse) + line)) + (when selected + (if (and company-search-string + (string-match (regexp-quote company-search-string) value + (length company-prefix))) + (let ((beg (+ margin (match-beginning 0))) + (end (+ margin (match-end 0)))) + (add-text-properties beg end '(face company-tooltip-selection) + line) + (when (< beg common) + (add-text-properties beg common + '(face company-tooltip-common-selection) + 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))) + line)) ;;; replace @@ -1671,13 +2177,21 @@ Example: (let (lines) (while (and (= 1 (vertical-motion 1)) (<= (point) end)) - (push (buffer-substring beg (min end (1- (point)))) lines) + (let ((bound (min end (1- (point))))) + ;; A visual line can contain several physical lines (e.g. with outline's + ;; folding overlay). Take only the first one. + (push (buffer-substring beg + (save-excursion + (goto-char beg) + (re-search-forward "$" bound 'move) + (point))) + lines)) (setq beg (point))) (unless (eq beg end) (push (buffer-substring beg end) lines)) (nreverse lines))) -(defsubst company-modify-line (old new offset) +(defun company-modify-line (old new offset) (concat (company-safe-substring old 0 offset) new (company-safe-substring old (+ offset (length new))))) @@ -1688,22 +2202,35 @@ Example: (length lst))) (defun company--replacement-string (lines old column nl &optional align-top) + (cl-decf column company-tooltip-margin) - (let ((width (length (car lines)))) - (when (> width (- (window-width) column)) - (setq column (max 0 (- (window-width) width))))) + (when (and align-top company-tooltip-flip-when-above) + (setq lines (reverse lines))) - (let (new) + (let ((width (length (car lines))) + (remaining-cols (- (+ (company--window-width) (window-hscroll)) + column))) + (when (> width remaining-cols) + (cl-decf column (- width remaining-cols)))) + + (let ((offset (and (< column 0) (- column))) + new) + (when offset + (setq column 0)) (when align-top ;; untouched lines first - (dotimes (i (- (length old) (length lines))) + (dotimes (_ (- (length old) (length lines))) (push (pop old) new))) ;; length into old lines. (while old - (push (company-modify-line (pop old) (pop lines) column) new)) + (push (company-modify-line (pop old) + (company--offset-line (pop lines) offset) + column) new)) ;; Append whole new lines. (while lines - (push (concat (company-space-string column) (pop lines)) new)) + (push (concat (company-space-string column) + (company--offset-line (pop lines) offset)) + new)) (let ((str (concat (when nl "\n") (mapconcat 'identity (nreverse new) "\n") @@ -1711,74 +2238,146 @@ Example: (font-lock-append-text-property 0 (length str) 'face 'default str) str))) -(defun company--create-lines (selection limit) +(defun company--offset-line (line offset) + (if (and offset line) + (substring line offset) + line)) +(defun company--create-lines (selection limit) (let ((len company-candidates-length) (numbered 99999) + (window-width (company--window-width)) lines width lines-copy + items previous remainder - new) - - ;; Scroll to offset. - (setq limit (company-pseudo-tooltip-update-offset selection len limit)) - - (when (> company-tooltip-offset 0) - (setq previous (format "...(%d)" company-tooltip-offset))) + scrollbar-bounds) - (setq remainder (- len limit company-tooltip-offset) - remainder (when (> remainder 0) - (setq remainder (format "...(%d)" remainder)))) + ;; Maybe clear old offset. + (when (< len (+ company-tooltip-offset limit)) + (setq company-tooltip-offset 0)) - (decf selection company-tooltip-offset) + ;; Scroll to offset. + (if (eq company-tooltip-offset-display 'lines) + (setq limit (company-tooltip--lines-update-offset selection len limit)) + (company-tooltip--simple-update-offset selection len limit)) + + (cond + ((eq company-tooltip-offset-display 'scrollbar) + (setq scrollbar-bounds (company--scrollbar-bounds company-tooltip-offset + limit len))) + ((eq company-tooltip-offset-display 'lines) + (when (> company-tooltip-offset 0) + (setq previous (format "...(%d)" company-tooltip-offset))) + (setq remainder (- len limit company-tooltip-offset) + remainder (when (> remainder 0) + (setq remainder (format "...(%d)" remainder)))))) + + (cl-decf selection company-tooltip-offset) (setq width (max (length previous) (length remainder)) lines (nthcdr company-tooltip-offset company-candidates) len (min limit len) lines-copy lines) - (dotimes (i len) - (setq width (max (length (pop lines-copy)) width))) - (setq width (min width (window-width))) - - (setq lines-copy lines) + (cl-decf window-width (* 2 company-tooltip-margin)) + (when scrollbar-bounds (cl-decf window-width)) + + (dotimes (_ len) + (let* ((value (pop lines-copy)) + (annotation (company-call-backend 'annotation value))) + (when (and annotation company-tooltip-align-annotations) + ;; `lisp-completion-at-point' adds a space. + (setq annotation (comment-string-strip annotation t nil))) + (push (cons value annotation) items) + (setq width (max (+ (length value) + (if (and annotation company-tooltip-align-annotations) + (1+ (length annotation)) + (length annotation))) + width)))) + + (setq width (min window-width + (max company-tooltip-minimum-width + (if (and company-show-numbers + (< company-tooltip-offset 10)) + (+ 2 width) + width)))) ;; number can make tooltip too long (when company-show-numbers (setq numbered company-tooltip-offset)) - (when previous - (push (propertize (company-safe-substring previous 0 width) - 'face 'company-tooltip) - new)) - - (dotimes (i len) - (push (company-fill-propertize - (if (>= numbered 10) - (company-reformat (pop lines)) - (incf numbered) - (format "%s %d" - (company-safe-substring (company-reformat (pop lines)) - 0 (- width 2)) - (mod numbered 10))) - width (equal i selection)) - new)) - - (when remainder - (push (propertize (company-safe-substring remainder 0 width) - 'face 'company-tooltip) - new)) - - (setq lines (nreverse new)))) + (let ((items (nreverse items)) new) + (when previous + (push (company--scrollpos-line previous width) new)) + + (dotimes (i len) + (let* ((item (pop items)) + (str (company-reformat (car item))) + (annotation (cdr item)) + (right (company-space-string company-tooltip-margin)) + (width width)) + (when (< numbered 10) + (cl-decf width 2) + (cl-incf numbered) + (setq right (concat (format " %d" (mod numbered 10)) right))) + (push (concat + (company-fill-propertize str annotation + width (equal i selection) + (company-space-string + company-tooltip-margin) + right) + (when scrollbar-bounds + (company--scrollbar i scrollbar-bounds))) + new))) + + (when remainder + (push (company--scrollpos-line remainder width) new)) + + (nreverse new)))) + +(defun company--scrollbar-bounds (offset limit length) + (when (> length limit) + (let* ((size (ceiling (* limit (float limit)) length)) + (lower (floor (* limit (float offset)) length)) + (upper (+ lower size -1))) + (cons lower upper)))) + +(defun company--scrollbar (i bounds) + (propertize " " 'face + (if (and (>= i (car bounds)) (<= i (cdr bounds))) + 'company-scrollbar-fg + 'company-scrollbar-bg))) + +(defun company--scrollpos-line (text width) + (propertize (concat (company-space-string company-tooltip-margin) + (company-safe-substring text 0 width) + (company-space-string company-tooltip-margin)) + 'face 'company-tooltip)) ;; show (defsubst company--window-inner-height () - (let ((edges (window-inside-edges (selected-window)))) + (let ((edges (window-inside-edges))) (- (nth 3 edges) (nth 1 edges)))) -(defsubst company--pseudo-tooltip-height () +(defsubst company--window-width () + (let ((ww (window-width))) + ;; Account for the line continuation column. + (when (zerop (cadr (window-fringes))) + (cl-decf ww)) + (unless (or (display-graphic-p) + (version< "24.3.1" emacs-version)) + ;; Emacs 24.3 and earlier included margins + ;; in window-width when in TTY. + (cl-decf ww + (let ((margins (window-margins))) + (+ (or (car margins) 0) + (or (cdr margins) 0))))) + ww)) + +(defun company--pseudo-tooltip-height () "Calculate the appropriate tooltip height. Returns a negative number if the tooltip should be displayed above point." (let* ((lines (company--row)) @@ -1792,8 +2391,6 @@ Returns a negative number if the tooltip should be displayed above point." (company-pseudo-tooltip-hide) (save-excursion - (move-to-column 0) - (let* ((height (company--pseudo-tooltip-height)) above) @@ -1807,33 +2404,35 @@ Returns a negative number if the tooltip should be displayed above point." (move-to-window-line (+ row (abs height))) (point))) (ov (make-overlay beg end)) - (args (list (mapcar 'company-untabify + (args (list (mapcar 'company-plainify (company-buffer-lines beg end)) column nl above))) (setq company-pseudo-tooltip-overlay ov) (overlay-put ov 'company-replacement-args args) - (overlay-put ov 'company-before - (apply 'company--replacement-string - (company--create-lines selection (abs height)) - args)) + + (let ((lines (company--create-lines selection (abs height)))) + (overlay-put ov 'company-after + (apply 'company--replacement-string lines args)) + (overlay-put ov 'company-width (string-width (car lines)))) (overlay-put ov 'company-column column) - (overlay-put ov 'company-height (abs height)) - (overlay-put ov 'window (selected-window)))))) - -(defun company-pseudo-tooltip-show-at-point (pos) - (let ((col-row (company--col-row pos))) - (when col-row - (company-pseudo-tooltip-show (1+ (cdr col-row)) (car col-row) - company-selection)))) - -(defun company-pseudo-tooltip-edit (lines selection) - (let ((column (overlay-get company-pseudo-tooltip-overlay 'company-column)) - (height (overlay-get company-pseudo-tooltip-overlay 'company-height))) - (overlay-put company-pseudo-tooltip-overlay 'company-before + (overlay-put ov 'company-height height))))) + +(defun company-pseudo-tooltip-show-at-point (pos column-offset) + (let ((row (company--row pos)) + (col (- (company--column pos) column-offset))) + (when (< col 0) (setq col 0)) + (company-pseudo-tooltip-show (1+ row) col company-selection))) + +(defun company-pseudo-tooltip-edit (selection) + (let* ((height (overlay-get company-pseudo-tooltip-overlay 'company-height)) + (lines (company--create-lines selection (abs height)))) + (overlay-put company-pseudo-tooltip-overlay 'company-width + (string-width (car lines))) + (overlay-put company-pseudo-tooltip-overlay 'company-after (apply 'company--replacement-string - (company--create-lines selection height) + lines (overlay-get company-pseudo-tooltip-overlay 'company-replacement-args))))) @@ -1845,54 +2444,67 @@ Returns a negative number if the tooltip should be displayed above point." (defun company-pseudo-tooltip-hide-temporarily () (when (overlayp company-pseudo-tooltip-overlay) (overlay-put company-pseudo-tooltip-overlay 'invisible nil) - (overlay-put company-pseudo-tooltip-overlay 'before-string nil))) + (overlay-put company-pseudo-tooltip-overlay 'line-prefix nil) + (overlay-put company-pseudo-tooltip-overlay 'after-string nil))) (defun company-pseudo-tooltip-unhide () (when company-pseudo-tooltip-overlay (overlay-put company-pseudo-tooltip-overlay 'invisible t) - (overlay-put company-pseudo-tooltip-overlay 'before-string - (overlay-get company-pseudo-tooltip-overlay 'company-before)) + ;; Beat outline's folding overlays, at least. + (overlay-put company-pseudo-tooltip-overlay 'priority 1) + ;; No (extra) prefix for the first line. + (overlay-put company-pseudo-tooltip-overlay 'line-prefix "") + (overlay-put company-pseudo-tooltip-overlay 'after-string + (overlay-get company-pseudo-tooltip-overlay 'company-after)) (overlay-put company-pseudo-tooltip-overlay 'window (selected-window)))) +(defun company-pseudo-tooltip-guard () + (list + (save-excursion (beginning-of-visual-line)) + (let ((ov company-pseudo-tooltip-overlay)) + (when (>= (overlay-get ov 'company-height) 0) + (buffer-substring-no-properties (point) (overlay-start ov)))))) + (defun company-pseudo-tooltip-frontend (command) - "A `company-mode' front-end similar to a tool-tip but based on overlays." - (case command + "`company-mode' front-end similar to a tooltip but based on overlays." + (cl-case command (pre-command (company-pseudo-tooltip-hide-temporarily)) (post-command - (let ((old-height (if (overlayp company-pseudo-tooltip-overlay) - (overlay-get company-pseudo-tooltip-overlay - 'company-height) - 0)) - (new-height (company--pseudo-tooltip-height))) - (unless (and (>= (* old-height new-height) 0) - (>= (abs old-height) (abs new-height))) - ;; Redraw needed. - (company-pseudo-tooltip-show-at-point (- (point) - (length company-prefix))))) + (unless (when (overlayp company-pseudo-tooltip-overlay) + (let* ((ov company-pseudo-tooltip-overlay) + (old-height (overlay-get ov 'company-height)) + (new-height (company--pseudo-tooltip-height))) + (and + (>= (* old-height new-height) 0) + (>= (abs old-height) (abs new-height)) + (equal (company-pseudo-tooltip-guard) + (overlay-get ov 'company-guard))))) + ;; Redraw needed. + (company-pseudo-tooltip-show-at-point (point) (length company-prefix)) + (overlay-put company-pseudo-tooltip-overlay + 'company-guard (company-pseudo-tooltip-guard))) (company-pseudo-tooltip-unhide)) (hide (company-pseudo-tooltip-hide) (setq company-tooltip-offset 0)) (update (when (overlayp company-pseudo-tooltip-overlay) - (company-pseudo-tooltip-edit company-candidates - company-selection))))) + (company-pseudo-tooltip-edit company-selection))))) (defun company-pseudo-tooltip-unless-just-one-frontend (command) "`company-pseudo-tooltip-frontend', but not shown for single candidates." (unless (and (eq command 'post-command) - (not (cdr company-candidates))) + (company--show-inline-p)) (company-pseudo-tooltip-frontend command))) ;;; overlay ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar company-preview-overlay nil) -(make-variable-buffer-local 'company-preview-overlay) +(defvar-local company-preview-overlay nil) (defun company-preview-show-at-point (pos) (company-preview-hide) - (setq company-preview-overlay (make-overlay pos pos)) + (setq company-preview-overlay (make-overlay pos (1+ pos))) - (let ((completion(nth company-selection company-candidates))) + (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) @@ -1911,7 +2523,9 @@ Returns a negative number if the tooltip should be displayed above point." (not (equal completion "")) (add-text-properties 0 1 '(cursor t) completion)) - (overlay-put company-preview-overlay 'after-string completion) + (overlay-put company-preview-overlay 'display + (concat completion (unless (eq pos (point-max)) + (buffer-substring pos (1+ pos))))) (overlay-put company-preview-overlay 'window (selected-window)))) (defun company-preview-hide () @@ -1920,22 +2534,27 @@ Returns a negative number if the tooltip should be displayed above point." (setq company-preview-overlay nil))) (defun company-preview-frontend (command) - "A `company-mode' front-end showing the selection as if it had been inserted." - (case command - (pre-command (company-preview-hide)) - (post-command (company-preview-show-at-point (point))) - (hide (company-preview-hide)))) + "`company-mode' front-end showing the selection as if it had been inserted." + (pcase command + (`pre-command (company-preview-hide)) + (`post-command (company-preview-show-at-point (point))) + (`hide (company-preview-hide)))) (defun company-preview-if-just-one-frontend (command) "`company-preview-frontend', but only shown for single candidates." - (unless (and (eq command 'post-command) - (cdr company-candidates)) + (when (or (not (eq command 'post-command)) + (company--show-inline-p)) (company-preview-frontend command))) +(defun company--show-inline-p () + (and (not (cdr company-candidates)) + company-common + (string-prefix-p company-prefix company-common + (company-call-backend 'ignore-case)))) + ;;; echo ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar company-echo-last-msg nil) -(make-variable-buffer-local 'company-echo-last-msg) +(defvar-local company-echo-last-msg nil) (defvar company-echo-timer nil) @@ -1949,17 +2568,12 @@ Returns a negative number if the tooltip should be displayed above point." (message "%s" company-echo-last-msg) (message "")))) -(defsubst company-echo-show-soon (&optional getter) +(defun company-echo-show-soon (&optional getter) (when company-echo-timer (cancel-timer company-echo-timer)) (setq company-echo-timer (run-with-timer 0 nil 'company-echo-show getter))) (defsubst company-echo-show-when-idle (&optional getter) - (when (sit-for .01) - (company-echo-show getter))) - -(defsubst company-echo-show-when-not-busy (&optional getter) - "Run `company-echo-show' with arg GETTER once Emacs isn't busy." (when (sit-for company-echo-delay) (company-echo-show getter))) @@ -1980,8 +2594,8 @@ Returns a negative number if the tooltip should be displayed above point." (progn (setq comp (propertize (format "%d: %s" i comp) 'face 'company-echo)) - (incf len 3) - (incf i) + (cl-incf len 3) + (cl-incf i) (add-text-properties 3 (+ 3 (length company-common)) '(face company-echo-common) comp)) (setq comp (propertize comp 'face 'company-echo)) @@ -2008,8 +2622,8 @@ Returns a negative number if the tooltip should be displayed above point." (when (< i 10) ;; Add number. (setq comp (format "%s (%d)" comp i)) - (incf len 4) - (incf i)) + (cl-incf len 4) + (cl-incf i)) (if (>= len limit) (setq candidates nil) (push (propertize comp 'face 'company-echo) msg))) @@ -2024,26 +2638,22 @@ Returns a negative number if the tooltip should be displayed above point." (company-echo-show))) (defun company-echo-frontend (command) - "A `company-mode' front-end showing the candidates in the echo area." - (case command - (post-command (company-echo-show-soon 'company-echo-format)) - (hide (company-echo-hide)))) + "`company-mode' front-end showing the candidates in the echo area." + (pcase command + (`post-command (company-echo-show-soon 'company-echo-format)) + (`hide (company-echo-hide)))) (defun company-echo-strip-common-frontend (command) - "A `company-mode' front-end showing the candidates in the echo area." - (case command - (post-command (company-echo-show-soon 'company-echo-strip-common-format)) - (hide (company-echo-hide)))) + "`company-mode' front-end showing the candidates in the echo area." + (pcase command + (`post-command (company-echo-show-soon 'company-echo-strip-common-format)) + (`hide (company-echo-hide)))) (defun company-echo-metadata-frontend (command) - "A `company-mode' front-end showing the documentation in the echo area." - (case command - (post-command (company-echo-show-when-idle 'company-fetch-metadata)) - (hide (company-echo-hide)))) - -;; templates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(autoload 'company-template-declare-template "company-template") + "`company-mode' front-end showing the documentation in the echo area." + (pcase command + (`post-command (company-echo-show-when-idle 'company-fetch-metadata)) + (`hide (company-echo-hide)))) (provide 'company) ;;; company.el ends here