diff options
author | David Bremner <bremner@debian.org> | 2019-01-24 07:31:02 -0400 |
---|---|---|
committer | David Bremner <bremner@debian.org> | 2019-01-24 07:31:02 -0400 |
commit | 51d123d5673d63849a45e6f6d18025aab7f9a834 (patch) | |
tree | 00e9211af546362877ede5d965f4f602b6f97381 | |
parent | 1154b1a5b5189d5975b9e3155aa804e116733c74 (diff) | |
parent | ac82e875e144b227e926c09c53def9b0c059115c (diff) |
Merge tag '0.9.9'
-rw-r--r-- | .travis.yml | 37 | ||||
-rw-r--r-- | NEWS.md | 32 | ||||
-rw-r--r-- | company-capf.el | 114 | ||||
-rw-r--r-- | company-clang.el | 14 | ||||
-rw-r--r-- | company-cmake.el | 12 | ||||
-rw-r--r-- | company-css.el | 10 | ||||
-rw-r--r-- | company-gtags.el | 8 | ||||
-rw-r--r-- | company-ispell.el | 2 | ||||
-rw-r--r-- | company-keywords.el | 38 | ||||
-rw-r--r-- | company-nxml.el | 3 | ||||
-rw-r--r-- | company-tng.el | 13 | ||||
-rw-r--r-- | company.el | 108 | ||||
-rw-r--r-- | test/all.el | 4 | ||||
-rw-r--r-- | test/capf-tests.el | 140 | ||||
-rw-r--r-- | test/cmake-tests.el | 44 |
15 files changed, 449 insertions, 130 deletions
diff --git a/.travis.yml b/.travis.yml index b8eb249..03933bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,26 +1,23 @@ -sudo: false - language: generic -matrix: - include: - - env: EMACS=emacs24 - addons: - apt: - sources: [ { sourceline: 'ppa:cassou/emacs' } ] - packages: [ emacs24, emacs24-el ] - - env: EMACS=emacs25 - addons: - apt: - sources: [ { sourceline: 'ppa:kelleyk/emacs' } ] - packages: [ emacs25 ] - - env: EMACS=emacs-snapshot - addons: - apt: - sources: [ { sourceline: 'ppa:ubuntu-elisp/ppa' } ] - packages: [ emacs-snapshot ] +sudo: false + +env: + global: + - CURL="curl -fsSkL --retry 9 --retry-delay 9" + matrix: + - EMACS_VERSION=24.5 + - EMACS_VERSION=25.3 + - EMACS_VERSION=26.1 + - EMACS_VERSION=master + allow_failures: + - env: EMACS_VERSION=master -install: true +install: + - $CURL -O https://github.com/npostavs/emacs-travis/releases/download/bins/emacs-bin-${EMACS_VERSION}.tar.gz + - tar -xaf emacs-bin-${EMACS_VERSION}.tar.gz -C / + - export EMACS=/tmp/emacs/bin/emacs + - $EMACS --version script: make test-batch EMACS=${EMACS} @@ -1,5 +1,37 @@ # History of user-visible changes +## 2018-12-13 (0.9.9) + +* Fix for the changes in the previous release. +* New hook `company-after-completion-hook`. +* `company-clang` removes identity preprocessor #defines from completions + ([#841](https://github.com/company-mode/company-mode/issues/841)). + +## 2018-12-08 (0.9.8) + +* CAPF backend fixed to use the right `:exit-function`. It can now safely be a + closure with lexical context capturing the buffer state at the moment when the + completion table was returned + ([#845](https://github.com/company-mode/company-mode/pull/845)). + +## 2018-11-06 (0.9.7) + +* For more sophisticated highlighting in non-prefix completion, a backend may + now respond to a `match` request with a list of regions. See + `company-backends`. + ([#798](https://github.com/company-mode/company-mode/issues/798), + [#762](https://github.com/company-mode/company-mode/issues/762)) +* The `company-capf` backend will pick up on a `:company-match` metadata element + on the capf function (similar to `:company-location` or `:company-doc-buffer`) + and use it as a response to aforementioned `match` request. +* `company-cmake` supports completion inside string interpolations + ([#714](https://github.com/company-mode/company-mode/pull/714)). +* Workaround for the conflict between `inferior-python-mode`'s completion code + and `company-sort-by-occurrence`. +* In Emacs 26 and newer, `company-css` is removed from `company-backends`. + `company-capf` is used instead. +* Same for `company-nxml`. + ## 2018-02-23 (0.9.6) * Workaround for Emacs' ([bug#23980](https://debbugs.gnu.org/23980)) triggered diff --git a/company-capf.el b/company-capf.el index 06384c7..64b3de9 100644 --- a/company-capf.el +++ b/company-capf.el @@ -1,6 +1,6 @@ ;;; company-capf.el --- company-mode completion-at-point-functions backend -*- lexical-binding: t -*- -;; Copyright (C) 2013-2017 Free Software Foundation, Inc. +;; Copyright (C) 2013-2018 Free Software Foundation, Inc. ;; Author: Stefan Monnier <monnier@iro.umontreal.ca> @@ -24,7 +24,8 @@ ;; ;; The CAPF back-end provides a bridge to the standard ;; completion-at-point-functions facility, and thus can support any major mode -;; that defines a proper completion function, including emacs-lisp-mode. +;; that defines a proper completion function, including emacs-lisp-mode, +;; css-mode and nxml-mode. ;;; Code: @@ -33,6 +34,13 @@ (defvar company--capf-cache nil) +;; FIXME: Provide a way to save this info once in Company itself +;; (https://github.com/company-mode/company-mode/pull/845). +(defvar-local company-capf--current-completion-data nil + "Value last returned by `company-capf' when called with `candidates'. +For most properties/actions, this is just what we need: the exact values +that accompanied the completion table that's currently is use.") + (defun company--capf-data () (let ((cache company--capf-cache)) (if (and (equal (current-buffer) (car cache)) @@ -67,6 +75,14 @@ completion-at-point-functions (remq 'python-completion-complete-at-point completion-at-point-functions))) +(defun company-capf--save-current-data (data) + (setq company-capf--current-completion-data data) + (add-hook 'company-after-completion-hook + #'company-capf--clear-current-data nil t)) + +(defun company-capf--clear-current-data (_ignored) + (setq company-capf--current-completion-data nil)) + (defun company-capf (command &optional arg &rest _args) "`company-mode' backend using `completion-at-point-functions'." (interactive (list 'interactive)) @@ -83,12 +99,13 @@ (t prefix)))))) (`candidates (let ((res (company--capf-data))) + (company-capf--save-current-data res) (when res (let* ((table (nth 3 res)) (pred (plist-get (nthcdr 4 res) :predicate)) (meta (completion-metadata - (buffer-substring (nth 1 res) (nth 2 res)) - table pred)) + (buffer-substring (nth 1 res) (nth 2 res)) + table pred)) (sortfun (cdr (assq 'display-sort-function meta))) (candidates (completion-all-completions arg table pred (length arg))) (last (last candidates)) @@ -104,67 +121,78 @@ candidates)) candidates))))) (`sorted - (let ((res (company--capf-data))) + (let ((res company-capf--current-completion-data)) (when res (let ((meta (completion-metadata (buffer-substring (nth 1 res) (nth 2 res)) (nth 3 res) (plist-get (nthcdr 4 res) :predicate)))) (cdr (assq 'display-sort-function meta)))))) (`match - ;; Can't just use 0 when base-size (see above) is non-zero. - (let ((start (if (get-text-property 0 'face arg) - 0 - (next-single-property-change 0 'face arg)))) - (when start - ;; completions-common-part comes first, but we can't just look for this - ;; value because it can be in a list. - (or - (let ((value (get-text-property start 'face arg))) - (text-property-not-all start (length arg) - 'face value arg)) - (length arg))))) + ;; Ask the for the `:company-match' function. If that doesn't help, + ;; fallback to sniffing for face changes to get a suitable value. + (let ((f (plist-get (nthcdr 4 company-capf--current-completion-data) + :company-match))) + (if f (funcall f arg) + (let* ((match-start nil) (pos -1) + (prop-value nil) (faces nil) + (has-face-p nil) chunks + (limit (length arg))) + (while (< pos limit) + (setq pos + (if (< pos 0) 0 (next-property-change pos arg limit))) + (setq prop-value (or + (get-text-property pos 'face arg) + (get-text-property pos 'font-lock-face arg)) + faces (if (listp prop-value) prop-value (list prop-value)) + has-face-p (memq 'completions-common-part faces)) + (cond ((and (not match-start) has-face-p) + (setq match-start pos)) + ((and match-start (not has-face-p)) + (push (cons match-start pos) chunks) + (setq match-start nil)))) + (nreverse chunks))))) (`duplicates t) (`no-cache t) ;Not much can be done here, as long as we handle ;non-prefix matches. (`meta - (let ((f (plist-get (nthcdr 4 (company--capf-data)) :company-docsig))) + (let ((f (plist-get (nthcdr 4 company-capf--current-completion-data) + :company-docsig))) (when f (funcall f arg)))) (`doc-buffer - (let ((f (plist-get (nthcdr 4 (company--capf-data)) :company-doc-buffer))) + (let ((f (plist-get (nthcdr 4 company-capf--current-completion-data) + :company-doc-buffer))) (when f (funcall f arg)))) (`location - (let ((f (plist-get (nthcdr 4 (company--capf-data)) :company-location))) + (let ((f (plist-get (nthcdr 4 company-capf--current-completion-data) + :company-location))) (when f (funcall f arg)))) (`annotation - (save-excursion - ;; FIXME: `company-begin' sets `company-point' after calling - ;; `company--begin-new'. We shouldn't rely on `company-point' here, - ;; better to cache the capf-data value instead. However: we can't just - ;; save the last capf-data value in `prefix', because that command can - ;; get called more often than `candidates', and at any point in the - ;; buffer (https://github.com/company-mode/company-mode/issues/153). - ;; We could try propertizing the returned prefix string, but it's not - ;; passed to `annotation', and `company-prefix' is set only after - ;; `company--strip-duplicates' is called. - (when company-point - (goto-char company-point)) - (let ((f (plist-get (nthcdr 4 (company--capf-data)) :annotation-function))) - (when f (funcall f arg))))) + (let ((f (plist-get (nthcdr 4 company-capf--current-completion-data) + :annotation-function))) + (when f (funcall f arg)))) (`require-match (plist-get (nthcdr 4 (company--capf-data)) :company-require-match)) (`init nil) ;Don't bother: plenty of other ways to initialize the code. (`post-completion - (let* ((res (company--capf-data)) - (exit-function (plist-get (nthcdr 4 res) :exit-function)) - (table (nth 3 res)) - (pred (plist-get (nthcdr 4 res) :predicate))) - (if exit-function - ;; Follow the example of `completion--done'. - (funcall exit-function arg - (if (eq (try-completion arg table pred) t) - 'finished 'sole))))) + (company--capf-post-completion arg)) )) +(defun company--capf-post-completion (arg) + (let* ((res company-capf--current-completion-data) + (exit-function (plist-get (nthcdr 4 res) :exit-function)) + (table (nth 3 res)) + (pred (plist-get (nthcdr 4 res) :predicate))) + (if exit-function + ;; Follow the example of `completion--done'. + (funcall exit-function arg + ;; FIXME: Should probably use an additional heuristic: + ;; completion-at-point doesn't know when the user picked a + ;; particular candidate explicitly (it only checks whether + ;; futher completions exist). Whereas company user can press + ;; RET (or use implicit completion with company-tng). + (if (eq (try-completion arg table pred) t) + 'finished 'sole))))) + (provide 'company-capf) ;;; company-capf.el ends here diff --git a/company-clang.el b/company-clang.el index 90a372e..d43eebb 100644 --- a/company-clang.el +++ b/company-clang.el @@ -134,11 +134,12 @@ or automatically through a custom `company-clang-prefix-guesser'." (when (string-match ":" match) (setq match (substring match 0 (match-beginning 0))))) (let ((meta (match-string-no-properties 2))) - (when (and meta (not (string= match meta))) - (put-text-property 0 1 'meta - (company-clang--strip-formatting meta) - match))) - (push match lines))) + (unless (equal match meta) + (when meta + (put-text-property 0 1 'meta + (company-clang--strip-formatting meta) + match)) + (push match lines))))) lines)) (defun company-clang--meta (candidate) @@ -183,11 +184,12 @@ or automatically through a custom `company-clang-prefix-guesser'." (let* ((buf (get-buffer-create company-clang--error-buffer-name)) (cmd (concat company-clang-executable " " (mapconcat 'identity args " "))) (pattern (format company-clang--completion-pattern "")) + (message-truncate-lines t) (err (if (re-search-forward pattern nil t) (buffer-substring-no-properties (point-min) (1- (match-beginning 0))) ;; Warn the user more aggressively if no match was found. - (message "clang failed with error %d:\n%s" res cmd) + (message "clang failed with error %d: %s" res cmd) (buffer-string)))) (with-current-buffer buf diff --git a/company-cmake.el b/company-cmake.el index 010df32..1bfb20b 100644 --- a/company-cmake.el +++ b/company-cmake.el @@ -1,6 +1,6 @@ ;;; company-cmake.el --- company-mode completion backend for CMake -;; Copyright (C) 2013-2014 Free Software Foundation, Inc. +;; Copyright (C) 2013-2014, 2017-2018 Free Software Foundation, Inc. ;; Author: Chen Bin <chenbin DOT sh AT gmail> ;; Version: 0.2 @@ -177,6 +177,13 @@ They affect which types of symbols we get completion candidates for.") (buffer-substring-no-properties (line-beginning-position) (point-max)))))) +(defun company-cmake-prefix-dollar-brace-p () + "Test if the current symbol follows ${." + (save-excursion + (skip-syntax-backward "w_") + (and (eq (char-before (point)) ?\{) + (eq (char-before (1- (point))) ?$)))) + (defun company-cmake (command &optional arg &rest ignored) "`company-mode' completion backend for CMake. CMake is a cross-platform, open-source make system." @@ -187,7 +194,8 @@ CMake is a cross-platform, open-source make system." (unless company-cmake-executable (error "Company found no cmake executable")))) (prefix (and (memq major-mode company-cmake-modes) - (not (company-in-string-or-comment)) + (or (not (company-in-string-or-comment)) + (company-cmake-prefix-dollar-brace-p)) (company-grab-symbol))) (candidates (company-cmake--candidates arg)) (meta (company-cmake--meta arg)) diff --git a/company-css.el b/company-css.el index cf8c683..d3ece74 100644 --- a/company-css.el +++ b/company-css.el @@ -1,6 +1,6 @@ ;;; company-css.el --- company-mode completion backend for css-mode -*- lexical-binding: t -*- -;; Copyright (C) 2009, 2011, 2014 Free Software Foundation, Inc. +;; Copyright (C) 2009, 2011, 2014, 2018 Free Software Foundation, Inc. ;; Author: Nikolaj Schumacher @@ -20,6 +20,8 @@ ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: +;; +;; In Emacs >= 26, company-capf is used instead. ;;; Code: @@ -422,7 +424,8 @@ Returns \"\" if no property found, but feasible at this position." (string= (web-mode-language-at-pos) "css"))) (or (company-grab company-css-tag-regexp 1) (company-grab company-css-pseudo-regexp 1) - (company-grab company-css-property-value-regexp 2) + (company-grab company-css-property-value-regexp 2 + (line-beginning-position)) (company-css-grab-property)))) (candidates (cond @@ -430,7 +433,8 @@ Returns \"\" if no property found, but feasible at this position." (all-completions arg company-css-html-tags)) ((company-grab company-css-pseudo-regexp 1) (all-completions arg company-css-pseudo-classes)) - ((company-grab company-css-property-value-regexp 2) + ((company-grab company-css-property-value-regexp 2 + (line-beginning-position)) (all-completions arg (company-css-property-values (company-grab company-css-property-value-regexp 1)))) diff --git a/company-gtags.el b/company-gtags.el index 02513ca..2a85f23 100644 --- a/company-gtags.el +++ b/company-gtags.el @@ -33,15 +33,15 @@ "Completion backend for GNU Global." :group 'company) +(define-obsolete-variable-alias + 'company-gtags-gnu-global-program-name + 'company-gtags-executable "earlier") + (defcustom company-gtags-executable (executable-find "global") "Location of GNU global executable." :type 'string) -(define-obsolete-variable-alias - 'company-gtags-gnu-global-program-name - 'company-gtags-executable "earlier") - (defcustom company-gtags-insert-arguments t "When non-nil, insert function arguments as a template after completion." :type 'boolean diff --git a/company-ispell.el b/company-ispell.el index c275bbe..ed658f2 100644 --- a/company-ispell.el +++ b/company-ispell.el @@ -53,7 +53,7 @@ If nil, use `ispell-complete-word-dict'." (company-ispell--lookup-words "WHATEVER") (setq company-ispell-available t)) (error - (message "Company: ispell-look-command not found") + (message "Company-Ispell: %s" (error-message-string err)) (setq company-ispell-available nil)))) company-ispell-available) diff --git a/company-keywords.el b/company-keywords.el index 414c7b0..b6dfd1d 100644 --- a/company-keywords.el +++ b/company-keywords.el @@ -141,6 +141,16 @@ "sum_suffix" "system_clock" "tan" "tanh" "target" "template" "then" "tiny" "transfer" "transpose" "trim" "true" "type" "ubound" "unpack" "use" "value" "verify" "volatile" "wait" "where" "while" "with" "write")) + (go-mode + ;; 1. Keywords ref: https://golang.org/ref/spec#Keywords + ;; 2. Builtin functions and types ref: https://golang.org/pkg/builtin/ + "append" "bool" "break" "byte" "cap" "case" "chan" "close" "complex" "complex128" + "complex64" "const" "continue" "copy" "default" "defer" "delete" "else" "error" + "fallthrough" "false" "float32" "float64" "for" "func" "go" "goto" "if" "imag" + "import" "int" "int16" "int32" "int64" "int8" "interface" "len" "make" + "map" "new" "nil" "package" "panic" "print" "println" "range" "real" "recover" + "return" "rune" "select" "string" "struct" "switch" "true" "type" "uint" "uint16" + "uint32" "uint64" "uint8" "uintptr" "var") (java-mode "abstract" "assert" "boolean" "break" "byte" "case" "catch" "char" "class" "continue" "default" "do" "double" "else" "enum" "extends" "final" @@ -149,9 +159,12 @@ "return" "short" "static" "strictfp" "super" "switch" "synchronized" "this" "throw" "throws" "transient" "try" "void" "volatile" "while") (javascript-mode - "break" "catch" "const" "continue" "delete" "do" "else" "export" "for" - "function" "if" "import" "in" "instanceOf" "label" "let" "new" "return" - "switch" "this" "throw" "try" "typeof" "var" "void" "while" "with" "yield") + ;; https://tc39.github.io/ecma262/ + async, static and undefined + "async" "await" "break" "case" "catch" "class" "const" "continue" + "debugger" "default" "delete" "do" "else" "enum" "export" "extends" "false" + "finally" "for" "function" "if" "import" "in" "instanceof" "let" "new" + "null" "return" "static" "super" "switch" "this" "throw" "true" "try" + "typeof" "undefined" "var" "void" "while" "with" "yield") (kotlin-mode "abstract" "annotation" "as" "break" "by" "catch" "class" "companion" "const" "constructor" "continue" "data" "do" "else" "enum" "false" "final" @@ -209,9 +222,11 @@ "print" "private" "protected" "public" "require" "require_once" "return" "static" "switch" "this" "throw" "try" "unset" "use" "var" "while" "xor") (python-mode - "and" "assert" "break" "class" "continue" "def" "del" "elif" "else" - "except" "exec" "finally" "for" "from" "global" "if" "import" "in" "is" - "lambda" "not" "or" "pass" "print" "raise" "return" "try" "while" "yield") + ;; https://docs.python.org/3/reference/lexical_analysis.html#keywords + "False" "None" "True" "and" "as" "assert" "break" "class" "continue" "def" + "del" "elif" "else" "except" "exec" "finally" "for" "from" "global" "if" + "import" "in" "is" "lambda" "nonlocal" "not" "or" "pass" "print" "raise" + "return" "try" "while" "with" "yield") (ruby-mode "BEGIN" "END" "alias" "and" "begin" "break" "case" "class" "def" "defined?" "do" "else" "elsif" "end" "ensure" "false" "for" "if" "in" "module" @@ -219,10 +234,6 @@ "then" "true" "undef" "unless" "until" "when" "while" "yield") ;; From https://doc.rust-lang.org/grammar.html#keywords ;; but excluding unused reserved words: https://www.reddit.com/r/rust/comments/34fq0k/is_there_a_good_list_of_rusts_keywords/cqucvnj - (go-mode - "break" "case" "chan" "const" "continue" "default" "defer" "else" "fallthrough" - "for" "func" "go" "goto" "if" "import" "interface" "map" "package" "range" - "return" "select" "struct" "switch" "type" "var") (rust-mode "Self" "as" "box" "break" "const" "continue" "crate" "else" "enum" "extern" @@ -255,12 +266,19 @@ "otherwise" "quote" "return" "switch" "throw" "true" "try" "type" "typealias" "using" "while" ) + ;; From https://github.com/apache/thrift/blob/master/contrib/thrift.el + (thrift-mode + "binary" "bool" "byte" "const" "double" "enum" "exception" "extends" + "i16" "i32" "i64" "include" "list" "map" "oneway" "optional" "required" + "service" "set" "string" "struct" "throws" "typedef" "void" + ) ;; aliases (js2-mode . javascript-mode) (js2-jsx-mode . javascript-mode) (espresso-mode . javascript-mode) (js-mode . javascript-mode) (js-jsx-mode . javascript-mode) + (rjsx-mode . javascript-mode) (cperl-mode . perl-mode) (jde-mode . java-mode) (ess-julia-mode . julia-mode) diff --git a/company-nxml.el b/company-nxml.el index 5afa00e..36ff1ce 100644 --- a/company-nxml.el +++ b/company-nxml.el @@ -1,6 +1,6 @@ ;;; company-nxml.el --- company-mode completion backend for nxml-mode -;; Copyright (C) 2009-2011, 2013 Free Software Foundation, Inc. +;; Copyright (C) 2009-2011, 2013, 2018 Free Software Foundation, Inc. ;; Author: Nikolaj Schumacher @@ -22,6 +22,7 @@ ;;; Commentary: ;; +;; In Emacs >= 26, company-capf is used instead. ;;; Code: diff --git a/company-tng.el b/company-tng.el index 46592da..a1d7173 100644 --- a/company-tng.el +++ b/company-tng.el @@ -102,7 +102,8 @@ confirm the selection and finish the completion." (when (and company-selection-changed (not (company--company-command-p (this-command-keys)))) (company--unread-this-command-keys) - (setq this-command 'company-complete-selection))))) + (setq this-command 'company-complete-selection) + (advice-add 'company-call-backend :before-until 'company-tng--supress-post-completion))))) ;;;###autoload (defun company-tng-configure-default () @@ -159,5 +160,15 @@ made explicitly (i.e. `company-selection-changed' is true)" (setf (nth 3 args) nil)) args) +(defun company-tng--supress-post-completion (command &rest args) + "Installed as a :before-until advice on `company-call-backend' and +prevents the 'post-completion command from being delivered to the backend +for the next iteration. post-completion do things like expand snippets +which are undesirable because completions are implicit in company-tng and +visible side-effects after the completion are surprising." + (when (eq command 'post-completion) + (advice-remove 'company-call-backend 'company-tng--supress-post-completion) + t)) + (provide 'company-tng) ;;; company-tng.el ends here @@ -5,7 +5,7 @@ ;; Author: Nikolaj Schumacher ;; Maintainer: Dmitry Gutov <dgutov@yandex.ru> ;; URL: http://company-mode.github.io/ -;; Version: 0.9.6 +;; Version: 0.9.9 ;; Keywords: abbrev, convenience, matching ;; Package-Requires: ((emacs "24.3")) @@ -44,7 +44,9 @@ ;; Here is a simple example completing "foo": ;; ;; (defun company-my-backend (command &optional arg &rest ignored) +;; (interactive (list 'interactive)) ;; (pcase command +;; (`interactive (company-begin-backend 'company-my-backend)) ;; (`prefix (company-grab-symbol)) ;; (`candidates (list "foobar" "foobaz" "foobarbaz")) ;; (`meta (format "This value is named %s" arg)))) @@ -322,7 +324,10 @@ This doesn't include the margins and the scroll bar." (defcustom company-backends `(,@(unless (version< "24.3.51" emacs-version) (list 'company-elisp)) company-bbdb - company-nxml company-css + ,@(unless (version<= "26" emacs-version) + (list 'company-nxml)) + ,@(unless (version<= "26" emacs-version) + (list 'company-css)) company-eclim company-semantic company-clang company-xcode company-cmake company-capf @@ -398,10 +403,13 @@ be kept if they have different annotations. For that to work properly, backends should store the related information on candidates using text properties. -`match': The second argument is a completion candidate. Return the index -after the end of text matching `prefix' within the candidate string. It -will be used when rendering the popup. This command only makes sense for -backends that provide non-prefix completion. +`match': The second argument is a completion candidate. Return a positive +integer, the index after the end of text matching `prefix' within the +candidate string. Alternatively, return a list of (CHUNK-START +. CHUNK-END) elements, where CHUNK-START and CHUNK-END are indexes within +the candidate string. The corresponding regions are be used when rendering +the popup. This command only makes sense for backends that provide +non-prefix completion. `require-match': If this returns t, the user is not allowed to enter anything not offered as a candidate. Please don't use that value in normal @@ -503,6 +511,11 @@ If you indend to use it to post-process candidates from a specific backend, consider using the `post-completion' command instead." :type 'hook) +(defcustom company-after-completion-hook nil + "Hook run at the end of completion, successful or not. +The hook is called with one argument which is either a string or a symbol." + :type 'hook) + (defcustom company-minimum-prefix-length 3 "The minimum prefix length for idle completion." :type '(integer :tag "prefix length")) @@ -600,7 +613,8 @@ treated as if it was on this list." (defcustom company-continue-commands '(not save-buffer save-some-buffers save-buffers-kill-terminal - save-buffers-kill-emacs) + save-buffers-kill-emacs + completion-at-point) "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', @@ -1358,10 +1372,18 @@ Keywords and function definition names are ignored." noccurs))) (defun company--occurrence-predicate () + (defvar comint-last-prompt) (let ((beg (match-beginning 0)) - (end (match-end 0))) + (end (match-end 0)) + (comint-last-prompt (bound-and-true-p comint-last-prompt))) (save-excursion (goto-char end) + ;; Workaround for python-shell-completion-at-point's behavior: + ;; https://github.com/company-mode/company-mode/issues/759 + ;; https://github.com/company-mode/company-mode/issues/549 + (when (derived-mode-p 'inferior-python-mode) + (let ((lbp (line-beginning-position))) + (setq comint-last-prompt (cons lbp lbp)))) (and (not (memq (get-text-property (1- (point)) 'face) '(font-lock-function-name-face font-lock-keyword-face))) @@ -1413,7 +1435,7 @@ prefix match (same case) will be prioritized." (not company-candidates) (let ((company-idle-delay 'now)) (condition-case-unless-debug err - (progn + (let ((inhibit-quit nil)) (company--perform) ;; Return non-nil if active. company-candidates) @@ -1618,10 +1640,10 @@ prefix match (same case) will be prioritized." ;; `company-completion-finished-hook' in that case, with right argument. (if (stringp result) (let ((company-backend backend)) - (company-call-backend 'pre-completion result) (run-hook-with-args 'company-completion-finished-hook result) (company-call-backend 'post-completion result)) - (run-hook-with-args 'company-completion-cancelled-hook result)))) + (run-hook-with-args 'company-completion-cancelled-hook result)) + (run-hook-with-args 'company-after-completion-hook result))) ;; Make return value explicit. nil) @@ -2378,11 +2400,13 @@ If SHOW-VERSION is non-nil, show the version in the echo area." cc annotations) (when (or (stringp prefix) (consp prefix)) (let ((company-backend backend)) - (setq cc (company-call-backend 'candidates prefix) - annotations - (mapcar - (lambda (c) (cons c (company-call-backend 'annotation c))) - cc)))) + (condition-case nil + (setq cc (company-call-backend 'candidates (company--prefix-str prefix)) + annotations + (mapcar + (lambda (c) (cons c (company-call-backend 'annotation c))) + cc)) + (error (setq annotations 'error))))) (pop-to-buffer (get-buffer-create "*company-diag*")) (setq buffer-read-only nil) (erase-buffer) @@ -2401,11 +2425,13 @@ If SHOW-VERSION is non-nil, show the version in the echo area." (insert "\n") (insert (message "Completions:")) (unless cc (insert " none")) - (save-excursion - (dolist (c annotations) - (insert "\n " (prin1-to-string (car c))) - (when (cdr c) - (insert " " (prin1-to-string (cdr c)))))) + (if (eq annotations 'error) + (insert "(error fetching)") + (save-excursion + (dolist (c annotations) + (insert "\n " (prin1-to-string (car c))) + (when (cdr c) + (insert " " (prin1-to-string (cdr c))))))) (special-mode))) ;;; pseudo-tooltip ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2490,7 +2516,6 @@ If SHOW-VERSION is non-nil, show the version in the echo area." (- width (length annotation))) annotation)) right))) - (setq common (+ (min common width) margin)) (setq width (+ width margin (length right))) (font-lock-append-text-property 0 width 'mouse-face @@ -2502,11 +2527,17 @@ If SHOW-VERSION is non-nil, show the version in the echo area." 'company-tooltip-annotation-selection 'company-tooltip-annotation) line)) - (font-lock-prepend-text-property margin common 'face - (if selected - 'company-tooltip-common-selection - 'company-tooltip-common) - line) + (cl-loop + with width = (- width (length right)) + for (comp-beg . comp-end) in (if (integerp common) `((0 . ,common)) common) + for inline-beg = (+ margin comp-beg) + for inline-end = (min (+ margin comp-end) width) + when (< inline-beg width) + do (font-lock-prepend-text-property inline-beg inline-end 'face + (if selected + 'company-tooltip-common-selection + 'company-tooltip-common) + line)) (when (let ((re (funcall company-search-regexp-function company-search-string))) (and (not (string= re "")) @@ -2802,7 +2833,6 @@ Returns a negative number if the tooltip should be displayed above point." (defun company-pseudo-tooltip-show (row column selection) (company-pseudo-tooltip-hide) - (save-excursion (let* ((height (company--pseudo-tooltip-height)) above) @@ -2811,15 +2841,17 @@ Returns a negative number if the tooltip should be displayed above point." (setq row (+ row height -1) above t)) - (let* ((nl (< (move-to-window-line row) row)) - (beg (point)) - (end (save-excursion - (move-to-window-line (+ row (abs height))) - (point))) - (ov (make-overlay beg end nil t)) - (args (list (mapcar 'company-plainify - (company-buffer-lines beg end)) - column nl above))) + (let (nl beg end ov args) + (save-excursion + (setq nl (< (move-to-window-line row) row) + beg (point) + end (save-excursion + (move-to-window-line (+ row (abs height))) + (point)) + ov (make-overlay beg end nil t) + 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) @@ -2830,7 +2862,7 @@ Returns a negative number if the tooltip should be displayed above point." (overlay-put ov 'company-width (string-width (car lines)))) (overlay-put ov 'company-column column) - (overlay-put ov 'company-height height))))) + (overlay-put ov 'company-height height)))) (defun company-pseudo-tooltip-show-at-point (pos column-offset) (let* ((col-row (company--col-row pos)) diff --git a/test/all.el b/test/all.el index 6d64a62..3d7758f 100644 --- a/test/all.el +++ b/test/all.el @@ -25,4 +25,6 @@ (require 'ert) (dolist (test-file (directory-files company-test-path t "-tests.el$")) - (load test-file nil t)) + (unless (and (= emacs-major-version 24) + (equal (file-name-base test-file) "capf-tests")) + (load test-file nil t))) diff --git a/test/capf-tests.el b/test/capf-tests.el new file mode 100644 index 0000000..c8d4202 --- /dev/null +++ b/test/capf-tests.el @@ -0,0 +1,140 @@ +;;; capf-tests.el --- company tests for the company-capf backend -*- lexical-binding: t; -*- + +;; Copyright (C) 2018 Free Software Foundation, Inc. + +;; Author: João Távora <joaotavora@gmail.com> +;; Keywords: + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; + +;;; Code: + +(require 'company-tests) +(require 'company-capf) + +(defmacro company-capf-with-buffer (contents &rest body) + (declare (indent 0) (debug (sexp &rest form))) + `(with-temp-buffer + (insert ,contents) + (emacs-lisp-mode) + (re-search-backward "|") + (replace-match "") + (let ((completion-at-point-functions '(elisp-completion-at-point)) + (company-backends '(company-capf))) + ,@body))) + +(ert-deftest company-basic-capf () + "Test basic `company-capf' support." + (company-capf-with-buffer + "(with-current-buffer|)" + (company-mode) + (company-complete) + (should company-candidates))) + +(ert-deftest company-non-prefix-capf () + "Test non-prefix `company-capf' in elisp" + (company-capf-with-buffer + "(w-c-b|)" + (company-mode) + (company-complete) + (should company-candidates) + (should (member "with-current-buffer" company-candidates)))) + +(ert-deftest company-basic-capf-highlighting () + "Test basic `company-capf' support, with basic prefix completion." + (company-capf-with-buffer + "(with|)" + (company-mode) + (company-complete) + (should company-candidates) + (let* ((cand (car (member "with-current-buffer" company-candidates))) + (render + (and cand + (company-fill-propertize cand nil (length cand) nil nil nil)))) + ;; remove `font-lock-face' and `mouse-face' text properties that aren't + ;; relevant to our test + (remove-list-of-text-properties + 0 (length cand) '(font-lock-face mouse-face) render) + (should + (ert-equal-including-properties + render + #("with-current-buffer" + 0 4 (face (company-tooltip-common company-tooltip)) ; "with" + 4 19 (face (company-tooltip)))))))) + + + +;; Re. "perfect" highlighting of the non-prefix in company-capf matches, it is +;; only working-out-of-the box (i.e. without the `:company-match' meta) in +;; recent Emacsen containing the following commit. The two tests that follow +;; reflect that. +;; +;; commit 325ef57b0e3977f9509f1049c826999e8b7c226d +;; Author: Stefan Monnier <monnier@iro.umontreal.ca> +;; Date: Tue Nov 7 12:17:34 2017 -0500 + +(ert-deftest company-non-prefix-fancy-capf-highlighting () + "Test highlighting for non-prefix `company-capf' in elisp" + (skip-unless (version<= "27.0" emacs-version)) + (company-capf-with-buffer + "(w-c-b|)" + (company-mode) + (company-complete) + (let* ((cand (car (member "with-current-buffer" company-candidates))) + (render + (and cand + (company-fill-propertize cand nil (length cand) nil nil nil)))) + ;; remove `font-lock-face' and `mouse-face' text properties that aren't + ;; relevant to our test + (remove-list-of-text-properties + 0 (length cand) '(font-lock-face mouse-face) render) + (should + (ert-equal-including-properties + render + #("with-current-buffer" + 0 1 (face (company-tooltip-common company-tooltip)) ; "w" + 1 4 (face (company-tooltip)) ; "ith" + 4 6 (face (company-tooltip-common company-tooltip)) ; "-c" + 6 12 (face (company-tooltip)) ; "urrent" + 12 14 (face (company-tooltip-common company-tooltip)) ; "-b" + 14 19 (face (company-tooltip)))))))) ; "uffer" + +(ert-deftest company-non-prefix-modest-capf-highlighting () + "Test highlighting for non-prefix `company-capf' in elisp" + (skip-unless (version< emacs-version "27.0")) + (company-capf-with-buffer + "(w-c-b|)" + (company-mode) + (company-complete) + (let* ((cand (car (member "with-current-buffer" company-candidates))) + (render + (and cand + (company-fill-propertize cand nil (length cand) nil nil nil)))) + ;; remove `font-lock-face' and `mouse-face' text properties that aren't + ;; relevant to our test + (remove-list-of-text-properties + 0 (length cand) '(font-lock-face mouse-face) render) + (should + (ert-equal-including-properties + render + #("with-current-buffer" + 0 14 (face (company-tooltip-common company-tooltip)); "with-current-b" + 14 19 (face (company-tooltip)))))))) ; "uffer" + +(provide 'capf-tests) +;;; capf-tests.el ends here diff --git a/test/cmake-tests.el b/test/cmake-tests.el new file mode 100644 index 0000000..52467cc --- /dev/null +++ b/test/cmake-tests.el @@ -0,0 +1,44 @@ +;;; cmake-tests.el --- company-mode tests -*- lexical-binding: t -*- + +;; Copyright (C) 2017 Free Software Foundation, Inc. + +;; Author: Zuogong Yue + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. + +(require 'company-tests) +(require 'company-cmake) + +(ert-deftest company-cmake-complete-in-string-prefix-quotes () + (with-temp-buffer + (insert "set(MyFlags \"${CMAKE_CXX_FLAGS_R") + (setq-local major-mode 'cmake-mode) + (should (equal (company-cmake 'prefix) + "CMAKE_CXX_FLAGS_R")))) + +(ert-deftest company-cmake-complete-in-string-more-prefix () + (with-temp-buffer + (insert "set(MyFlags \"${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_R") + (setq-local major-mode 'cmake-mode) + (should (equal (company-cmake 'prefix) + "CMAKE_CXX_FLAGS_R")))) + +(ert-deftest company-cmake-complete-in-string-more-prefix-2 () + (with-temp-buffer + (insert "set(MyFlags \"${CMAKE_CXX_FLAGS} CMAKE_CXX_FLAGS_R") + (setq-local major-mode 'cmake-mode) + (should (equal (company-cmake 'prefix) + nil)))) |